/// <summary> /// Returns a new table that is the union of the input tables. /// </summary> /// <param name="parameters">The model's parameters</param> /// <param name="first">First table</param> /// <param name="second">Second table</param> /// <param name="newState">Function to calculate state of merged value combinations</param> /// <returns>The new table</returns> public static ParameterInteraction Merge <T>(IList <ParameterBase> parameters, ParameterInteraction first, ParameterInteraction second, Func <ValueCombinationState, ValueCombinationState, ValueCombinationState> newState) where T : new() { List <int> parameterIndices = first.Parameters.Union(second.Parameters).ToList(); parameterIndices.Sort(); var mergedInteraction = new ParameterInteraction(parameterIndices); var valueTable = ParameterInteractionTable <T> .GenerateValueTable(parameters, mergedInteraction); foreach (var value in valueTable) { mergedInteraction.Combinations.Add(new ValueCombination(value, mergedInteraction)); } foreach (var combination in mergedInteraction.Combinations) { var firstMatch = first.Combinations.First((c) => ParameterInteractionTable <T> .MatchCombination(c, combination)); var secondMatch = second.Combinations.First((c) => ParameterInteractionTable <T> .MatchCombination(c, combination)); combination.State = newState(firstMatch.State, secondMatch.State); } return(mergedInteraction); }
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); } }
public ValueCombination(IList <int> values, ParameterInteraction interaction) { if (values.Count != interaction.Parameters.Count) { throw new ArgumentOutOfRangeException("values", "values and interaction must be the same length."); } for (int i = 0; i < values.Count; i++) { parameterToValueMap[interaction.Parameters[i]] = values[i]; } State = ValueCombinationState.Uncovered; }
public ValueCombination(IList <int> values, ParameterInteraction interaction) { if (values.Count != interaction.Parameters.Count) { throw new ArgumentOutOfRangeException("values", "values and interaction must be the same length."); } this.parameterToValueMap = new Dictionary <int, int>(interaction.Parameters.Count); for (int i = 0; i < values.Count; i++) { parameterToValueMap[interaction.Parameters[i]] = values[i]; } State = ValueCombinationState.Uncovered; keys = new KeyCollection(parameterToValueMap.Keys); }
// adds an interaction generated from a constraint to the table private void MergeConstraintInteraction(int order, ParameterInteraction constraintInteraction) { // is the constraint already in the table if (!Interactions.Contains(constraintInteraction)) { // if the interaction has more parameters than the order we can't mark any of the existing combinations excluded // if the interaction is shorter we need to mark the existing combinations excluded if (constraintInteraction.Parameters.Count > order) { Interactions.Add(constraintInteraction); } else { var excludedValues = constraintInteraction.Combinations.Where((c) => c.State == ValueCombinationState.Excluded); var candidateInteractions = from interaction in Interactions where constraintInteraction.Parameters.All((i) => interaction.Parameters.Contains(i)) select interaction; var excludedCombinations = from interaction in candidateInteractions from combination in interaction.Combinations where excludedValues.Any((c) => MatchCombination(c, combination)) select combination; foreach (var combination in excludedCombinations) { combination.State = ValueCombinationState.Excluded; } } } else { // mark combinations excluded by the constraint excluded in the table var interaction = Interactions.First((i) => i.Equals(constraintInteraction)); var excludedValues = constraintInteraction.Combinations.Where((c) => c.State == ValueCombinationState.Excluded); var combinations = interaction.Combinations.Where((c) => excludedValues.Any((excluded) => MatchCombination(excluded, c))); foreach (var combination in combinations) { combination.State = ValueCombinationState.Excluded; } } }
private void GenerateHigherOrderDependentExclusions(Model <T> model, int order, List <int> parameterInteractionCounts, ParameterInteraction completeInteraction, IEnumerable <ValueCombination> allowedCombinations) { // generate the combinations for orders between order and completerInteraction.Parameters.Count foreach (var count in parameterInteractionCounts) { IList <int[]> parameterCombinations = GenerateCombinations(completeInteraction.Parameters.Count, count); foreach (var combinations in parameterCombinations) { var interaction = new ParameterInteraction(combinations.Select((i) => completeInteraction.Parameters[i])); var possibleValues = ParameterInteractionTable <T> .GenerateValueTable(model.Parameters, interaction); foreach (var value in possibleValues) { interaction.Combinations .Add(new ValueCombination(value, interaction) { State = ValueCombinationState.Covered }); } // find combinations that should be excluded var excludedCombinations = new List <ValueCombination>(); foreach (var combination in interaction.Combinations) { if (!combination.ParameterToValueMap.Any((p) => !completeInteraction.Parameters.Contains(p.Key)) && !allowedCombinations.Any((c) => MatchCombination(combination, c))) { excludedCombinations.Add(combination); } } if (excludedCombinations.Count() > 0) { foreach (var combination in excludedCombinations) { combination.State = ValueCombinationState.Excluded; } MergeConstraintInteraction(order, interaction); } } } }
// helper to implement Constraint.GetExcludeCombinations internal static ParameterInteraction GetExcludedCombinations <T>(Model model, Constraint constraint, Parameter first, Parameter second, T value, Func <T, T, bool> comparison) { Debug.Assert(first != null && model != null && constraint != null && comparison != null); List <int> parameterIndices = new List <int>(); parameterIndices.Add(model.Parameters.IndexOf(first)); if (second != null) { parameterIndices.Add(model.Parameters.IndexOf(second)); } parameterIndices.Sort(); ParameterInteraction interaction = new ParameterInteraction(parameterIndices); List <int[]> valueTable = ParameterInteractionTable.GenerateValueTable(model.Parameters, interaction); foreach (var valueIndices in valueTable) { T value1, value2; value1 = (T)first[valueIndices[0]]; if (second == null) { value2 = value; } else { value2 = (T)second[valueIndices[1]]; } ValueCombinationState comboState = comparison(value1, value2) ? ValueCombinationState.Covered : ValueCombinationState.Excluded; interaction.Combinations.Add(new ValueCombination(valueIndices, interaction) { State = comboState }); } return(interaction); }
// returns a table with all possible value combinations for the given interaction internal static List <int[]> GenerateValueTable(IList <Parameter> parameters, ParameterInteraction interaction) { List <int[]> possibleValueTable = new List <int[]>(); for (int i = 0; i < interaction.Parameters.Count; i++) { possibleValueTable.Add(GenerateOrderedArray(parameters[interaction.Parameters[i]].Count)); } int valueTableCount = possibleValueTable.Aggregate(1, (seed, array) => seed * array.Length); var table = new List <int[]>(valueTableCount); for (int i = 0; i < valueTableCount; i++) { table.Add(new int[possibleValueTable.Count]); } int[] repeats = new int[possibleValueTable.Count]; for (int i = possibleValueTable.Count - 1; i >= 0; i--) { int repeat = 1; if (i < possibleValueTable.Count - 1) { repeat = possibleValueTable[i + 1].Length * repeats[i + 1]; } repeats[i] = repeat; } for (int i = 0; i < table.Count; i++) { for (int j = 0; j < table[i].Length; j++) { table[i][j] = possibleValueTable[j][(i / repeats[j]) % possibleValueTable[j].Length]; } } return(table); }
// for the following system: // if A == 0 then B == 0 // if B == 0 then C == 0 // if C == 0 then A == 1 // all combinations with A == 0 need to be excluded, but this is impossible to determine when looking at individual constraints // to do this: // - create groups of constraints where all members have a parameter that overlaps with another constraint // - create ParameterInteraction for each group of constraints // - for the existing uncovered combinations, if they excluded in every matching combination in the group interaction, mark excluded // - higher order combinations that match the order of a constraint's interaction can also need to be exculded private void GenerateDependentConstraintInteractions(Model model, int order, List <ParameterInteraction> constraintInteractions) { // group all the constraint parameter interactions that share parameters var dependentConstraintSets = new List <List <ParameterInteraction> >(); while (constraintInteractions.Count > 0) { var dependentConstraintSet = new List <ParameterInteraction>(); var interactionsToExplore = new List <ParameterInteraction>(); interactionsToExplore.Add(constraintInteractions[0]); constraintInteractions.RemoveAt(0); while (interactionsToExplore.Count > 0) { ParameterInteraction current = interactionsToExplore[0]; interactionsToExplore.RemoveAt(0); dependentConstraintSet.Add(current); var dependentInteractions = (from constraint in constraintInteractions where constraint.Parameters.Any((i) => current.Parameters.Contains(i)) select constraint).ToList(); foreach (var dependentInteraction in dependentInteractions) { constraintInteractions.Remove(dependentInteraction); } interactionsToExplore.AddRange(dependentInteractions); } dependentConstraintSets.Add(dependentConstraintSet); } // walk over the groups of constraints foreach (var dependentConstraintSet in dependentConstraintSets) { // if there's only one constraint no more processing is necessary if (dependentConstraintSet.Count <= 1) { continue; } // merge the interactions of all the constraints var uniqueParameters = new Dictionary <int, bool>(); var parameterInteractionCounts = new List <int>(); for (int i = 0; i < dependentConstraintSet.Count; i++) { foreach (var parameter in dependentConstraintSet[i].Parameters) { uniqueParameters[parameter] = true; } if (dependentConstraintSet[i].Parameters.Count > order) { parameterInteractionCounts.Add(dependentConstraintSet[i].Parameters.Count); } } var sortedParameters = uniqueParameters.Keys.ToList(); sortedParameters.Sort(); ParameterInteraction completeInteraction = new ParameterInteraction(sortedParameters); var valueTable = ParameterInteractionTable.GenerateValueTable(model.Parameters, completeInteraction); foreach (var value in valueTable) { completeInteraction.Combinations .Add(new ValueCombination(value, completeInteraction) { State = ValueCombinationState.Covered }); } // calculate the excluded combinations in the new uber interaction var completeInteractionExcludedCombinations = new List <ValueCombination>(); // find the combinations from the uber interaction that aren't excluded // if a combination is a subset of any of these it is not excluded var allowedCombinations = new List <ValueCombination>(); foreach (var combination in completeInteraction.Combinations) { bool exclude = false; foreach (var constraint in completeConstraints) { if (constraint.SatisfiesContraint(model, combination) == ConstraintSatisfaction.Unsatisfied) { exclude = true; break; } } if (exclude) { combination.State = ValueCombinationState.Excluded; completeInteractionExcludedCombinations.Add(combination); } else { allowedCombinations.Add(combination); } } // find the existing combinations that are never allowed in the uber interaction var individualInteractionExcludedCombinations = from interaction in Interactions from combination in interaction.Combinations where !combination.ParameterToValueMap.Any((p) => !completeInteraction.Parameters.Contains(p.Key)) && !allowedCombinations.Any((c) => MatchCombination(combination, c)) select combination; // mark the combinations in the table as excluded foreach (var combination in individualInteractionExcludedCombinations) { combination.State = ValueCombinationState.Excluded; } GenerateHigherOrderDependentExclusions(model, order, parameterInteractionCounts, completeInteraction, allowedCombinations); } }
// 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); }
// helper to implement Constraint.SatisfiesConstraint internal static ConstraintSatisfaction SatisfiesContraint <T>(Model <T> model, ValueCombination combination, ParameterInteraction interaction) where T : new() { 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 <T> .MatchCombination(interaction.Combinations[i], combination)) { if (interaction.Combinations[i].State == ValueCombinationState.Excluded) { return(ConstraintSatisfaction.Unsatisfied); } } } return(ConstraintSatisfaction.Satisfied); }