// helper to implement Constraint.SatisfiesConstraint internal static ConstraintSatisfaction SatisfiesContraint(Model model, ValueCombination combination, ParameterInteraction interaction) { Debug.Assert(model != null && combination != null && interaction != null); var parameterMap = combination.ParameterToValueMap; for (int i = 0; i < interaction.Parameters.Count; i++) { if (!parameterMap.ContainsKey(interaction.Parameters[i])) { return(ConstraintSatisfaction.InsufficientData); } } for (int i = 0; i < interaction.Combinations.Count; i++) { if (ParameterInteractionTable.MatchCombination(interaction.Combinations[i], combination)) { if (interaction.Combinations[i].State == ValueCombinationState.Excluded) { return(ConstraintSatisfaction.Unsatisfied); } } } return(ConstraintSatisfaction.Satisfied); }
// add this value to the candidate private static void CreateProposedCandidate(ValueCombination value, int[] baseCandidate, int[] proposed) { baseCandidate.CopyTo(proposed, 0); foreach (var valuePair in value.ParameterToValueMap) { proposed[valuePair.Key] = valuePair.Value; } }
public ParameterInteraction(ParameterInteraction interaction) { this.parameters = new List <int>(interaction.Parameters); foreach (var combination in interaction.Combinations) { var newCombination = new ValueCombination(combination); Combinations.Add(newCombination); } }
// can this value be added to this candidate private static bool IsCompatibleValue(ValueCombination value, int[] candidate) { foreach (int i in value.Keys) { if (candidate[i] != value.ParameterToValueMap[i] && candidate[i] != -1) { return(false); } } return(true); }
// add this value to the candidate private static int[] CreateProposedCandidate(ValueCombination value, int[] baseCandidate) { int[] proposed = new int[baseCandidate.Length]; baseCandidate.CopyTo(proposed, 0); foreach (var valuePair in value.ParameterToVaueMap) { proposed[valuePair.Key] = valuePair.Value; } return(proposed); }
// do all the values in subset match with the whole public static bool MatchCombination(ValueCombination subSet, ValueCombination whole) { var wholeParameterMap = whole.ParameterToValueMap; var subSetParameterMap = subSet.ParameterToValueMap; foreach (var key in subSet.Keys) { if (!wholeParameterMap.ContainsKey(key) || subSetParameterMap[key] != wholeParameterMap[key]) { return(false); } } return(true); }
// create all the values for each combination private void GenerateValueCombinations(Model model) { for (int i = 0; i < Interactions.Count; i++) { var interaction = Interactions[i]; var valueTable = GenerateValueTable(model.Parameters, interaction); for (int j = 0; j < valueTable.Count; j++) { var values = valueTable[j]; var combination = new ValueCombination(values, interaction); var tag = model.DefaultVariationTag; double weight = 1.0; interaction.Combinations.Add(combination); for (int k = 0; k < values.Length; k++) { var value = model.Parameters[interaction.Parameters[k]][values[k]]; if (value is ParameterValue) { var parameterValue = (ParameterValue)value; if (parameterValue.Weight != 1.0) { weight = weight == 1.0 ? parameterValue.Weight : Math.Max(weight, parameterValue.Weight); } if (parameterValue.Tag != null && parameterValue.Tag != model.DefaultVariationTag) { if (tag != model.DefaultVariationTag) { combination.State = ValueCombinationState.Excluded; } else { tag = parameterValue.Tag; } } } } combination.Weight = weight; combination.Tag = tag; } } }
// helper to implement Constraint.SatisfiesConstraint internal static ConstraintSatisfaction SatisfiesContraint(Model model, ValueCombination combination, ParameterInteraction interaction) { Debug.Assert(model != null && combination != null && interaction != null); if (!interaction.Parameters.All((i) => combination.ParameterToVaueMap.ContainsKey(i))) { return(ConstraintSatisfaction.InsufficientData); } var matches = interaction.Combinations.Where((c) => ParameterInteractionTable.MatchCombination(c, combination)); if (matches.Any((c) => c.State == ValueCombinationState.Excluded)) { return(ConstraintSatisfaction.Unsatisfied); } return(ConstraintSatisfaction.Satisfied); }
public ValueCombination(ValueCombination combination) { this.parameterToValueMap = new Dictionary <int, int>(combination.ParameterToVaueMap); this.State = combination.State; }
// this is the actual generation function // returns a list of indices that allow lookup of the actual value in the model private static IList <VariationIndexTagPair> GenerateVariationIndices <T>(ParameterInteractionTable <T> interactions, int variationSize, int seed, long maxVariations, object defaultTag) where T : new() { Random random = new Random(seed); List <VariationIndexTagPair> variations = new List <VariationIndexTagPair>(); // while there a uncovered values while (!interactions.IsCovered()) { int[] candidate = new int[variationSize]; object variationTag = defaultTag; // this is a scatch variable so new arrays won't be allocated for every candidate int[] proposedCandidate = new int[variationSize]; for (int i = 0; i < candidate.Length; i++) { // -1 indicates an empty slot candidate[i] = -1; } IEnumerable <ParameterInteraction> candidateInteractions = interactions.Interactions; // while there are empty slots while (candidate.Any((i) => i == -1)) { // if all the slots are empty if (candidate.All((i) => i == -1)) { // then pick the first uncovered combination from the most uncovered parameter interaction int mostUncovered = interactions.Interactions.Max((i) => i.GetUncoveredCombinationsCount()); var interaction = interactions.Interactions.First((i) => i.GetUncoveredCombinationsCount() == mostUncovered); var combination = interaction.Combinations.First((c) => c.State == ValueCombinationState.Uncovered); foreach (var valuePair in combination.ParameterToValueMap) { candidate[valuePair.Key] = valuePair.Value; } variationTag = combination.Tag == null || combination.Tag == defaultTag ? variationTag : combination.Tag; combination.State = ValueCombinationState.Covered; } else { // find interactions that aren't covered by the current candidate variation var incompletelyCoveredInteractions = from interaction in candidateInteractions where interaction.Parameters.Any((i) => candidate[i] == -1) select interaction; candidateInteractions = incompletelyCoveredInteractions; // find values that can be added to the current candidate var compatibleValues = new List <ValueCombination>(); foreach (var interaction in incompletelyCoveredInteractions) { foreach (var combination in interaction.Combinations) { if (IsCompatibleValue(combination, candidate)) { compatibleValues.Add(combination); } } } // get the uncovered values var uncoveredValues = compatibleValues.Where((v) => v.State == ValueCombinationState.Uncovered).ToList(); // calculate what the candidate will look like if add an uncovered value var proposedCandidates = new List <CandidateCoverage>(); foreach (var uncoveredValue in uncoveredValues) { CreateProposedCandidate(uncoveredValue, candidate, proposedCandidate); if (!IsExcluded(interactions.ExcludedCombinations, proposedCandidate)) { var coverage = new CandidateCoverage { Value = uncoveredValue, CoverageCount = uncoveredValues.Count((v) => IsCovered(v, proposedCandidate)), }; proposedCandidates.Add(coverage); } } // if any of the proposed candidates isn't exclude if (proposedCandidates.Count > 0) { // find the value that will cover the most combinations int maxCovered = proposedCandidates.Max((c) => c.CoverageCount); double maxWeight = proposedCandidates.Where((c) => c.CoverageCount == maxCovered).Max((c) => c.Value.Weight); ValueCombination proposedValue = proposedCandidates.First((c) => c.CoverageCount == maxCovered && c.Value.Weight == maxWeight).Value; // add this value to candidate and mark all values as such foreach (var valuePair in proposedValue.ParameterToValueMap) { candidate[valuePair.Key] = valuePair.Value; } variationTag = proposedValue.Tag == null || proposedValue.Tag == defaultTag ? variationTag : proposedValue.Tag; // get the newly covered values so they can be marked var newlyCoveredValue = uncoveredValues.Where((v) => IsCovered(v, candidate)).ToList(); foreach (var value in newlyCoveredValue) { value.State = ValueCombinationState.Covered; } } else { // no uncovered values can be added with violating a constraint, add a random covered value var compatibleWeightBuckets = compatibleValues.GroupBy((v) => v.Weight).OrderByDescending((v) => v.Key); ValueCombination value = null; bool combinationFound = false; foreach (var bucket in compatibleWeightBuckets) { int count = bucket.Count(); int attempts = 0; do { value = bucket.ElementAt(random.Next(count - 1)); CreateProposedCandidate(value, candidate, proposedCandidate); if (!interactions.ExcludedCombinations.Any((c) => IsCovered(c, proposedCandidate))) { combinationFound = true; } attempts++; // this is a heuristic, since we're pulling random values just going to count probably // means we've attempted duplicates, going to 2 * count means we've probably tried // everything at least once if (attempts > count * 2) { break; } }while (!combinationFound); if (combinationFound) { break; } } if (!combinationFound) { throw new InternalVariationGenerationException("Unable to find candidate with no exclusions."); } // add this value to candidate and mark all values as such foreach (var valuePair in value.ParameterToValueMap) { candidate[valuePair.Key] = valuePair.Value; } variationTag = value.Tag == null || value.Tag == defaultTag ? variationTag : value.Tag; } } } variations.Add(new VariationIndexTagPair { Indices = candidate, Tag = variationTag }); // more variations than are need to exhaustively test the model have been adde if (variations.Count > maxVariations) { throw new InternalVariationGenerationException("More variations than an exhaustive suite produced."); } } return(variations); }
public ValueCombination(ValueCombination combination) { this.parameterToValueMap = new Dictionary <int, int>(combination.ParameterToValueMap); this.State = combination.State; keys = new KeyCollection(parameterToValueMap.Keys); }
/// <summary> /// Calcultes whether the specified value satisfies the constraint or has insufficient data to do so. /// </summary> /// <param name="model">The model.</param> /// <param name="combination">The value.</param> /// <returns>The calculated result.</returns> internal abstract ConstraintSatisfaction SatisfiesContraint(Model <T> model, ValueCombination combination);
// this is the actual generation function // returns a list of indices that allow lookup of the actual value in the model private static IList <int[]> GenerateVariationIndices(ParameterInteractionTable interactions, int variationSize, int seed, int maxVariations) { Random random = new Random(seed); List <int[]> variations = new List <int[]>(); // while there a uncovered values while (!interactions.IsCovered()) { int[] candidate = new int[variationSize]; for (int i = 0; i < candidate.Length; i++) { // -1 indicates an empty slot candidate[i] = -1; } // while there are empty slots while (candidate.Any((i) => i == -1)) { // if all the slots are empty if (candidate.All((i) => i == -1)) { // then pick the first uncovered combination from the most uncovered parameter interaction int mostUncovered = interactions.Interactions.Max((i) => i.GetUncoveredCombinationsCount()); var interaction = interactions.Interactions.First((i) => i.GetUncoveredCombinationsCount() == mostUncovered); var combination = interaction.Combinations.First((c) => c.State == ValueCombinationState.Uncovered); foreach (var valuePair in combination.ParameterToVaueMap) { candidate[valuePair.Key] = valuePair.Value; } combination.State = ValueCombinationState.Covered; } else { // find interactions that aren't covered by the current candidate variation var incompletelyCoveredInteractions = from interaction in interactions.Interactions where interaction.Parameters.Any((i) => candidate[i] == -1) select interaction; // find values that can be added to the current candidate var compatibleValues = from interaction in incompletelyCoveredInteractions from combination in interaction.Combinations where IsCompatibleValue(combination, candidate) select combination; // get the uncovered values var uncoveredValues = compatibleValues.Where((v) => v.State == ValueCombinationState.Uncovered).ToList(); // calculate what the candidate will look like if add an uncovered value var proposedCandidates = from value in uncoveredValues select new { Value = value, Candidate = CreateProposedCandidate(value, candidate) }; // if any of the proposed candidates isn't exclude if (proposedCandidates.Any((a) => !IsExcluded(interactions.ExcludedCombinations(), a.Candidate))) { // find the value that will cover the most combinations int maxCovered = proposedCandidates.Max( (a) => uncoveredValues.Count( (v) => IsCovered(v, a.Candidate) && !IsExcluded(interactions.ExcludedCombinations(), a.Candidate))); ValueCombination proposedValue = proposedCandidates.First( (a) => uncoveredValues.Count( (v) => IsCovered(v, a.Candidate) && !IsExcluded(interactions.ExcludedCombinations(), a.Candidate)) == maxCovered).Value; // add this value to candidate and mark all values as such foreach (var valuePair in proposedValue.ParameterToVaueMap) { candidate[valuePair.Key] = valuePair.Value; } // get the newly covered values so they can be marked var newlyCoveredValue = uncoveredValues.Where((v) => IsCovered(v, candidate)).ToList(); foreach (var value in newlyCoveredValue) { value.State = ValueCombinationState.Covered; } } else { // no uncovered values can be added with violating a constraint, add a random covered value int count = compatibleValues.Count(); int attempts = 0; ValueCombination value; int[] proposedCandidate; do { value = compatibleValues.ElementAt(random.Next(count - 1)); proposedCandidate = CreateProposedCandidate(value, candidate); // this is a heuristic, since we're pulling random values just going to count probably // means we've attempted duplicates, going to 2 * count means we've probably tried // everything at least once if (attempts > count * 2) { throw new InternalVariationGenerationException("Unable to find candidate with no exclusions."); } attempts++; }while (interactions.ExcludedCombinations().Any((c) => IsCovered(c, CreateProposedCandidate(value, candidate)))); // add this value to candidate and mark all values as such foreach (var valuePair in value.ParameterToVaueMap) { candidate[valuePair.Key] = valuePair.Value; } } } } variations.Add(candidate); // more variations than are need to exhaustively test the model have been adde if (variations.Count > maxVariations) { throw new InternalVariationGenerationException("More variations than an exhaustive suite produced."); } } return(variations); }
// can this value be added to this candidate private static bool IsCompatibleValue(ValueCombination value, int[] candidate) { return(value.ParameterToVaueMap.Keys .All((i) => candidate[i] == value.ParameterToVaueMap[i] || candidate[i] == -1)); }
// do all the values in subset match with the whole public static bool MatchCombination(ValueCombination subSet, ValueCombination whole) { return(subSet.ParameterToVaueMap.Keys .All((i) => whole.ParameterToVaueMap.ContainsKey(i) && subSet.ParameterToVaueMap[i] == whole.ParameterToVaueMap[i])); }