private bool SelectNextValuesForCurrentDimensionCombination(DimensionValueIndexCombination currentCombination, ReadOnlyCollection <int> currentDimensionsToCover) { int currentIndex = currentDimensionsToCover.Count - 1; bool selectNext = true; // Continue enumerating pairwise indices until a value is selected (!selectNext) // or we exhaust all possibilities for the current pairwise combination // (selectNext) && (currentIndex == -1) while ((selectNext) && (currentIndex >= 0)) { Debug.Assert(currentCombination[currentDimensionsToCover[currentIndex]] < currentCombination.GetDimensionSize(currentDimensionsToCover[currentIndex])); if (currentCombination[currentDimensionsToCover[currentIndex]] == currentCombination.GetDimensionSize(currentDimensionsToCover[currentIndex]) - 1) { currentCombination[currentDimensionsToCover[currentIndex]] = 0; selectNext = true; currentIndex--; } else { currentCombination[currentDimensionsToCover[currentIndex]]++; selectNext = false; } } return(!selectNext || (currentIndex != -1)); }
/// <summary> /// Gets the allowed values for a given target dimension, given the values that have been set so far for other dimensions. /// </summary> /// <param name="targetDimension">The index of the target dimension (index is relative to the dimensionValues collection passed in at construction).</param> /// <param name="setDimensionIndexes">The indexes of the dimensions where we have chosen values.</param> /// <param name="currentCombination">The chosen values for the dimensions.</param> /// <returns>A list of indexes refering to values from the dimensionValues collection passed in at construction that are valid according to constraints.</returns> public ReadOnlyCollection<int> GetAllowedValueIndexesForDimension(int targetDimension, IEnumerable<int> setDimensionIndexes, DimensionValueIndexCombination currentCombination) { List<int> ret = new List<int>(Enumerable.Range(0, _dimensionValues[targetDimension].Values.Count)); foreach (PossiblyExtendedConstraint constraint in _extendedConstraints.Where(c => c.IsRelevantConstraint(targetDimension, setDimensionIndexes))) { ret.RemoveAll(vi => !constraint.IsValid(currentCombination, targetDimension, vi)); } return ret.AsReadOnly(); }
/// <summary> /// Adds all the combinations of the given order as covered. /// </summary> /// <param name="combination">The combination to cover.</param> /// <param name="dimensionIndexGenerator">The dimension index generator to use for getting all dimension index combinations we care about.</param> public void AddCoveredPairwiseCombinations(DimensionValueIndexCombination combination, DimensionIndexCombinationGenerator dimensionIndexGenerator) { foreach (ReadOnlyCollection <int> dimensionsOfInterest in dimensionIndexGenerator.Generate()) { if (!IsCombinationCovered(dimensionsOfInterest, combination)) { _coveredCombinations.Add(EncodeCombination(dimensionsOfInterest, combination)); } } }
/// <summary> /// Gets the allowed values for a given target dimension, given the values that have been set so far for other dimensions. /// </summary> /// <param name="targetDimension">The index of the target dimension (index is relative to the dimensionValues collection passed in at construction).</param> /// <param name="setDimensionIndexes">The indexes of the dimensions where we have chosen values.</param> /// <param name="currentCombination">The chosen values for the dimensions.</param> /// <returns>A list of indexes refering to values from the dimensionValues collection passed in at construction that are valid according to constraints.</returns> public ReadOnlyCollection <int> GetAllowedValueIndexesForDimension(int targetDimension, IEnumerable <int> setDimensionIndexes, DimensionValueIndexCombination currentCombination) { List <int> ret = new List <int>(Enumerable.Range(0, _dimensionValues[targetDimension].Values.Count)); foreach (PossiblyExtendedConstraint constraint in _extendedConstraints.Where(c => c.IsRelevantConstraint(targetDimension, setDimensionIndexes))) { ret.RemoveAll(vi => !constraint.IsValid(currentCombination, targetDimension, vi)); } return(ret.AsReadOnly()); }
/// <summary> /// Determines whether the given combination has already been covered. /// </summary> /// <param name="dimensionsOfInterest">The dimensions of interest.</param> /// <param name="combination">The combination.</param> /// <returns> /// <c>true</c> if the given combination is already covered; otherwise, <c>false</c>. /// </returns> public bool IsCombinationCovered(ICollection <int> dimensionsOfInterest, DimensionValueIndexCombination combination) { List <int> encodedCombination = EncodeCombination(dimensionsOfInterest, combination); foreach (List <int> covered in _coveredCombinations) { if (AreEqual(covered, encodedCombination)) { return(true); } } return(false); }
public bool IsValid(DimensionValueIndexCombination combination) { return IsValid(GetValueIndexes(combination)); }
public bool IsValid(DimensionValueIndexCombination combination, int targetDimensionIndex, int targetDimensionIndexValue) { return IsValid(GetValueIndexes(combination, targetDimensionIndex, targetDimensionIndexValue)); }
private bool SelectNextValuesForCurrentDimensionCombination(DimensionValueIndexCombination currentCombination, ReadOnlyCollection<int> currentDimensionsToCover) { int currentIndex = currentDimensionsToCover.Count - 1; bool selectNext = true; // Continue enumerating pairwise indices until a value is selected (!selectNext) // or we exhaust all possibilities for the current pairwise combination // (selectNext) && (currentIndex == -1) while ((selectNext) && (currentIndex >= 0)) { Debug.Assert(currentCombination[currentDimensionsToCover[currentIndex]] < currentCombination.GetDimensionSize(currentDimensionsToCover[currentIndex])); if (currentCombination[currentDimensionsToCover[currentIndex]] == currentCombination.GetDimensionSize(currentDimensionsToCover[currentIndex]) - 1) { currentCombination[currentDimensionsToCover[currentIndex]] = 0; selectNext = true; currentIndex--; } else { currentCombination[currentDimensionsToCover[currentIndex]]++; selectNext = false; } } return !selectNext || (currentIndex != -1); }
public bool IsValid(DimensionValueIndexCombination combination, int targetDimensionIndex, int targetDimensionIndexValue) { return(IsValid(GetValueIndexes(combination, targetDimensionIndex, targetDimensionIndexValue))); }
private IEnumerable <int> GetValueIndexes(DimensionValueIndexCombination combination, int targetDimensionIndex, int targetDimensionIndexValue) { return(Enumerable.Range(0, _dimensionIndexes.Count).Select(i => GetValueIndex(i, combination, targetDimensionIndex, targetDimensionIndexValue))); }
/// <summary> /// Explores the input (sub-)space. Each invocation may return a different set. /// </summary> /// <returns> /// A (reasonably-sized) stream of vectors/values from the (sub-)space. /// </returns> public override IEnumerable <Vector> Explore() { if (TargetMatrix.Dimensions.Count == 0) { // No dimensions to explore, then yield one empty vector. yield return(new Vector()); yield break; } ReadOnlyCollection <DimensionWithValues> dimensionValues = GetAllDimensionValues(); if (dimensionValues.Any(dim => dim.Values.Count == 0)) { yield break; // One of the dimensions is empty } if (dimensionValues.Count < _order) { // This is really non-sensical to ask for an n-wise strategy for a matrix with less than n dimensions // but I'd rather return a good default than throw. So I'll just cheat and do an exhaustive strategy instead ExhaustiveCombinatorialStrategy exhaustiveStrategy = new ExhaustiveCombinatorialStrategy(this); foreach (Vector v in exhaustiveStrategy.Explore()) { yield return(v); } yield break; } CoveredPairwiseCombinations coveredCombinations = new CoveredPairwiseCombinations(); DimensionValueIndexCombination currentCombination = new DimensionValueIndexCombination(dimensionValues); DimensionIndexCombinationGenerator dimensionIndexGenerator = CreateDimensionIndexCombinationGenerator(dimensionValues); Vector currentVector = currentCombination.GenerateVector(); if (IsValidVector(currentVector)) { coveredCombinations.AddCoveredPairwiseCombinations(currentCombination, dimensionIndexGenerator); yield return(currentVector); } PartialVectorConstraintChecker partialConstraintChecker = UsePartialConstraintChecker ? new PartialVectorConstraintChecker(dimensionValues, Constraints) : null; foreach (ReadOnlyCollection <int> currentDimensionsToCover in dimensionIndexGenerator.Generate()) { // Initialize the selection of values in the newly selected pairwise combination for (int i = 0; i < currentDimensionsToCover.Count; i++) { currentCombination[currentDimensionsToCover[i]] = 0; } while (true) { bool selectionSucceeded = SelectNextValuesForCurrentDimensionCombination(currentCombination, currentDimensionsToCover); if (!selectionSucceeded) { // We exhausted all combinations of values for the current pairwise combination // select the next pairwise combination break; } if (coveredCombinations.IsCombinationCovered(currentDimensionsToCover, currentCombination)) { continue; } if (partialConstraintChecker != null && !partialConstraintChecker.IsValidForApplicableConstraints(currentCombination, currentDimensionsToCover)) { // This combination is forbidden by constraints - carry on continue; } // Randomize other values, try at most MAXTRIES since we might hit invalid combinations for (int i = 0; i < MAXTRIES; i++) { List <int> remainingDimensionIndexes = Enumerable.Range(0, dimensionValues.Count) .Where(di => !currentDimensionsToCover.Contains(di)) // Don't randomize the pair currently worked on .ToList(); RandomUtilities.ShuffleList(remainingDimensionIndexes, _nextInt); List <int> setDimensionIndexes = new List <int>(currentDimensionsToCover); foreach (int dimensionIndex in remainingDimensionIndexes) { ReadOnlyCollection <int> allowedValueIndexes = UsePartialConstraintChecker ? partialConstraintChecker.GetAllowedValueIndexesForDimension(dimensionIndex, setDimensionIndexes, currentCombination) : Enumerable.Range(0, currentCombination.GetDimensionSize(dimensionIndex)).ToList().AsReadOnly(); if (allowedValueIndexes.Count == 0) { break; } currentCombination[dimensionIndex] = allowedValueIndexes[_nextInt(allowedValueIndexes.Count)]; setDimensionIndexes.Add(dimensionIndex); } if (setDimensionIndexes.Count < dimensionValues.Count) { // Couldn't set all dimension values, try again (or quit) continue; } currentVector = currentCombination.GenerateVector(); if (partialConstraintChecker != null || IsValidVector(currentVector)) { coveredCombinations.AddCoveredPairwiseCombinations(currentCombination, dimensionIndexGenerator); yield return(currentVector); break; // Out of the MAXTRIES loop } } } } }
/// <summary> /// Encodes the given combination as a list of integers /// </summary> /// <param name="dimensionsOfInterest"></param> /// <param name="combination"></param> /// <returns></returns> /// <remarks> /// I'll just encode a covered combination like: {D4:2,D1:10} as {1,10,4,2} (sort on dimension index first). /// </remarks> private static List<int> EncodeCombination(ICollection<int> dimensionsOfInterest, DimensionValueIndexCombination combination) { List<int> sortedDimensions = new List<int>(dimensionsOfInterest); sortedDimensions.Sort(); List<int> ret = new List<int>(dimensionsOfInterest.Count * 2); foreach (int dimension in sortedDimensions) { ret.Add(dimension); ret.Add(combination[dimension]); } return ret; }
/// <summary> /// Adds all the combinations of the given order as covered. /// </summary> /// <param name="combination">The combination to cover.</param> /// <param name="dimensionIndexGenerator">The dimension index generator to use for getting all dimension index combinations we care about.</param> public void AddCoveredPairwiseCombinations(DimensionValueIndexCombination combination, DimensionIndexCombinationGenerator dimensionIndexGenerator) { foreach (ReadOnlyCollection<int> dimensionsOfInterest in dimensionIndexGenerator.Generate()) { if (!IsCombinationCovered(dimensionsOfInterest, combination)) _coveredCombinations.Add(EncodeCombination(dimensionsOfInterest, combination)); } }
/// <summary> /// Determines whether the given combination has already been covered. /// </summary> /// <param name="dimensionsOfInterest">The dimensions of interest.</param> /// <param name="combination">The combination.</param> /// <returns> /// <c>true</c> if the given combination is already covered; otherwise, <c>false</c>. /// </returns> public bool IsCombinationCovered(ICollection<int> dimensionsOfInterest, DimensionValueIndexCombination combination) { List<int> encodedCombination = EncodeCombination(dimensionsOfInterest, combination); foreach (List<int> covered in _coveredCombinations) { if (AreEqual(covered, encodedCombination)) return true; } return false; }
/// <summary> /// Checks if the vector so far is valid according to constraints. /// </summary> /// <param name="combination">The combination with the values chosen so far.</param> /// <param name="setDimensionIndexes">The indexes of the dimensions where we have chosen values.</param> /// <returns><c>true</c></returns> public bool IsValidForApplicableConstraints(DimensionValueIndexCombination combination, IEnumerable<int> setDimensionIndexes) { return _extendedConstraints.Where(c => c.IsRelevantConstraint(setDimensionIndexes)).All(c => c.IsValid(combination)); }
/// <summary> /// Encodes the given combination as a list of integers /// </summary> /// <param name="dimensionsOfInterest"></param> /// <param name="combination"></param> /// <returns></returns> /// <remarks> /// I'll just encode a covered combination like: {D4:2,D1:10} as {1,10,4,2} (sort on dimension index first). /// </remarks> private static List <int> EncodeCombination(ICollection <int> dimensionsOfInterest, DimensionValueIndexCombination combination) { List <int> sortedDimensions = new List <int>(dimensionsOfInterest); sortedDimensions.Sort(); List <int> ret = new List <int>(dimensionsOfInterest.Count * 2); foreach (int dimension in sortedDimensions) { ret.Add(dimension); ret.Add(combination[dimension]); } return(ret); }
private int GetValueIndex(int dimensionIndexIndex, DimensionValueIndexCombination combination, int targetDimensionIndex, int targetDimensionIndexValue) { return(_dimensionIndexes[dimensionIndexIndex] == targetDimensionIndex ? targetDimensionIndexValue : combination[_dimensionIndexes[dimensionIndexIndex]]); }
private int GetValueIndex(int dimensionIndexIndex, DimensionValueIndexCombination combination, int targetDimensionIndex, int targetDimensionIndexValue) { return _dimensionIndexes[dimensionIndexIndex] == targetDimensionIndex ? targetDimensionIndexValue : combination[_dimensionIndexes[dimensionIndexIndex]]; }
private IEnumerable <int> GetValueIndexes(DimensionValueIndexCombination combination) { return(_dimensionIndexes.Select(d => combination[d])); }
private IEnumerable<int> GetValueIndexes(DimensionValueIndexCombination combination, int targetDimensionIndex, int targetDimensionIndexValue) { return Enumerable.Range(0, _dimensionIndexes.Count).Select(i => GetValueIndex(i, combination, targetDimensionIndex, targetDimensionIndexValue)); }
public bool IsValid(DimensionValueIndexCombination combination) { return(IsValid(GetValueIndexes(combination))); }
private IEnumerable<int> GetValueIndexes(DimensionValueIndexCombination combination) { return _dimensionIndexes.Select(d => combination[d]); }
/// <summary> /// Checks if the vector so far is valid according to constraints. /// </summary> /// <param name="combination">The combination with the values chosen so far.</param> /// <param name="setDimensionIndexes">The indexes of the dimensions where we have chosen values.</param> /// <returns><c>true</c></returns> public bool IsValidForApplicableConstraints(DimensionValueIndexCombination combination, IEnumerable <int> setDimensionIndexes) { return(_extendedConstraints.Where(c => c.IsRelevantConstraint(setDimensionIndexes)).All(c => c.IsValid(combination))); }
/// <summary> /// Explores the input (sub-)space. Each invocation may return a different set. /// </summary> /// <returns> /// A (reasonably-sized) stream of vectors/values from the (sub-)space. /// </returns> public override IEnumerable<Vector> Explore() { if (TargetMatrix.Dimensions.Count == 0) { // No dimensions to explore, then yield one empty vector. yield return new Vector(); yield break; } ReadOnlyCollection<DimensionWithValues> dimensionValues = GetAllDimensionValues(); if (dimensionValues.Any(dim => dim.Values.Count == 0)) { yield break; // One of the dimensions is empty } if (dimensionValues.Count < _order) { // This is really non-sensical to ask for an n-wise strategy for a matrix with less than n dimensions // but I'd rather return a good default than throw. So I'll just cheat and do an exhaustive strategy instead ExhaustiveCombinatorialStrategy exhaustiveStrategy = new ExhaustiveCombinatorialStrategy(this); foreach (Vector v in exhaustiveStrategy.Explore()) yield return v; yield break; } CoveredPairwiseCombinations coveredCombinations = new CoveredPairwiseCombinations(); DimensionValueIndexCombination currentCombination = new DimensionValueIndexCombination(dimensionValues); DimensionIndexCombinationGenerator dimensionIndexGenerator = CreateDimensionIndexCombinationGenerator(dimensionValues); Vector currentVector = currentCombination.GenerateVector(); if (IsValidVector(currentVector)) { coveredCombinations.AddCoveredPairwiseCombinations(currentCombination, dimensionIndexGenerator); yield return currentVector; } PartialVectorConstraintChecker partialConstraintChecker = UsePartialConstraintChecker ? new PartialVectorConstraintChecker(dimensionValues, Constraints) : null; foreach (ReadOnlyCollection<int> currentDimensionsToCover in dimensionIndexGenerator.Generate()) { // Initialize the selection of values in the newly selected pairwise combination for (int i = 0; i < currentDimensionsToCover.Count; i++) { currentCombination[currentDimensionsToCover[i]] = 0; } while (true) { bool selectionSucceeded = SelectNextValuesForCurrentDimensionCombination(currentCombination, currentDimensionsToCover); if (!selectionSucceeded) { // We exhausted all combinations of values for the current pairwise combination // select the next pairwise combination break; } if (coveredCombinations.IsCombinationCovered(currentDimensionsToCover, currentCombination)) { continue; } if (partialConstraintChecker != null && !partialConstraintChecker.IsValidForApplicableConstraints(currentCombination, currentDimensionsToCover)) { // This combination is forbidden by constraints - carry on continue; } // Randomize other values, try at most MAXTRIES since we might hit invalid combinations for (int i = 0; i < MAXTRIES; i++) { List<int> remainingDimensionIndexes = Enumerable.Range(0, dimensionValues.Count) .Where(di => !currentDimensionsToCover.Contains(di)) // Don't randomize the pair currently worked on .ToList(); RandomUtilities.ShuffleList(remainingDimensionIndexes, _nextInt); List<int> setDimensionIndexes = new List<int>(currentDimensionsToCover); foreach (int dimensionIndex in remainingDimensionIndexes) { ReadOnlyCollection<int> allowedValueIndexes = UsePartialConstraintChecker ? partialConstraintChecker.GetAllowedValueIndexesForDimension(dimensionIndex, setDimensionIndexes, currentCombination) : Enumerable.Range(0, currentCombination.GetDimensionSize(dimensionIndex)).ToList().AsReadOnly(); if (allowedValueIndexes.Count == 0) { break; } currentCombination[dimensionIndex] = allowedValueIndexes[_nextInt(allowedValueIndexes.Count)]; setDimensionIndexes.Add(dimensionIndex); } if (setDimensionIndexes.Count < dimensionValues.Count) { // Couldn't set all dimension values, try again (or quit) continue; } currentVector = currentCombination.GenerateVector(); if (partialConstraintChecker != null || IsValidVector(currentVector)) { coveredCombinations.AddCoveredPairwiseCombinations(currentCombination, dimensionIndexGenerator); yield return currentVector; break; // Out of the MAXTRIES loop } } } } }