public override bool Equals(object obj) { Substitution other = obj as Substitution; return(other != null && Variable.Equals(other.Variable) && SubstitutionTerm.Equals(other.SubstitutionTerm)); }
/// <summary> /// Adds the given substitution to the existing oners and apply the new composition /// to the existing problem. /// </summary> public void ComposeAndApplySubstitutions(Substitution newSubstitution) { // Add the new substitution to the existing ones List<Substitution> removeList = new List<Substitution>(); bool found = false; foreach (var sub in CurrentSubstitutions) { sub.SubstitutionTerm = sub.SubstitutionTerm.ApplySubstitutions(new[] { newSubstitution }); if (sub.Variable.Equals(sub.SubstitutionTerm)) removeList.Add(sub); else found = sub.Variable.Equals(newSubstitution.Variable); } CurrentSubstitutions.RemoveAll(r => removeList.Contains(r)); if (!found) CurrentSubstitutions.Add(newSubstitution); // Apply the modified substitutions to the existing problem foreach (var eq in CurrentEquations) { eq.Lhs = eq.Lhs.ApplySubstitutions(CurrentSubstitutions); eq.Rhs = eq.Rhs.ApplySubstitutions(CurrentSubstitutions); } }
/// <summary> /// Uses the input type definitions as a bottom up tree automaton to /// test the input mapping of a variable to a term. /// /// Note: /// * All final states must be matched for a mapping to be valid. /// * Types are "merged" therefore the same type definition name can have multiple definitions associated. /// </summary> public bool IsSubstitutionValid(Substitution substitution) { HashSet <TrsTypeDefinitionTypeName> endStates = null; // If variable not bound, it is valid by default if (!typeMappings.TryGetValue(substitution.Variable, out endStates)) { return(true); } // Initial and final states var termIn = substitution.SubstitutionTerm.Convert(); AddDynamicStates(termIn); InterpreterType testType = new InterpreterType(termIn); var retVal = testType.IsTermValid(transitionFunction, endStates); // Undo dynamic changes to state machine to cater for $TrsNumber, $TrsConstant, $TrsString and $TrsVariable RemoveDynamicStates(); return(retVal); }
private void ProcessSubstitutionStep(UnificationContinuation currentProblem, Equation currEq, List <TrsVariable> variableNamesToPreserve) { // LHS will always be variable here ... preserve matched term variables Substitution newSub = null; if (currEq.Rhs is TrsVariable && variableNamesToPreserve.Contains(currEq.Lhs)) { newSub = new Substitution { Variable = (TrsVariable)currEq.Rhs, SubstitutionTerm = currEq.Lhs }; } else { newSub = new Substitution { Variable = (TrsVariable)currEq.Lhs, SubstitutionTerm = currEq.Rhs }; } currentProblem.ComposeAndApplySubstitutions(newSub); }
/// <summary> /// Checks that the given unifier is valid in terms of the type definitions. /// </summary> private bool IsUnifierValid(UnificationResult unifier, TrsTermBase matchedRuleHead) { // Variables at this level must be validated in a way that is sensitive to AC term type definitions // to account for semantics. If it is not add[:x,:y] will not match add[1,2,3] with limit :x,:y to $TrsNumber if (!unifier.Succeed) { return(false); } if (unifier.Unifier.Count == 0) { return(true); // equal terms, nothing to validate } // Keep track of variable parent cases Stack <TrsTermBase> evalStack = new Stack <TrsTermBase>(); Dictionary <TrsVariable, bool> isAcParent = new Dictionary <TrsVariable, bool>(); Dictionary <TrsVariable, bool> isNonAcParent = new Dictionary <TrsVariable, bool>(); Dictionary <TrsVariable, HashSet <string> > variableTermNames = new Dictionary <TrsVariable, HashSet <string> >(); evalStack.Push(matchedRuleHead); Action <TrsVariable, bool, Dictionary <TrsVariable, bool> > updateLookups = delegate(TrsVariable v, bool b, Dictionary <TrsVariable, bool> target) { if (target.ContainsKey(v)) { target[v] = b; } else { target.Add(v, b); } }; while (evalStack.Count > 0) { var current = evalStack.Pop(); // Variable only case ... this should not happen unless variable only reduction rule heads are allowed in the future var currentVariable = current as TrsVariable; if (currentVariable != null) { updateLookups(currentVariable, false, isAcParent); updateLookups(currentVariable, false, isNonAcParent); } else { // Check arguments var curTerm = current as TrsTerm; var curAcTerm = current as TrsAcTerm; foreach (var variable in Enumerable.Concat(curTerm == null ? new TrsVariable[0] : curTerm.Arguments.Where(arg => arg is TrsVariable).Cast <TrsVariable>(), curAcTerm == null ? new TrsVariable[0] : curAcTerm.OnfArguments.Where(arg => arg.Term is TrsVariable).Select(arg => arg.Term).Cast <TrsVariable>())) { updateLookups(variable, curTerm != null, isNonAcParent); updateLookups(variable, curAcTerm != null, isAcParent); if (curAcTerm != null) { HashSet <string> termNames; if (!variableTermNames.TryGetValue(variable, out termNames)) { variableTermNames.Add(variable, termNames = new HashSet <string>()); } termNames.Add(curAcTerm.Name); } } } } bool isValid = true; foreach (var substitution in unifier.Unifier) { // It is possible that the variable being tested does not occur in the term head ... if (!isNonAcParent.ContainsKey(substitution.Variable) && !isAcParent.ContainsKey(substitution.Variable)) { isValid = isValid && typeChecker.IsSubstitutionValid(substitution); continue; } // AC term case if (isNonAcParent.ContainsKey(substitution.Variable) && isNonAcParent[substitution.Variable]) { isValid = isValid && typeChecker.IsSubstitutionValid(substitution); } // Non-AC term case if (isAcParent.ContainsKey(substitution.Variable) && isAcParent[substitution.Variable]) { var acSubstitutionTerm = substitution.SubstitutionTerm as TrsAcTerm; if (acSubstitutionTerm == null || !variableTermNames[substitution.Variable].Contains(acSubstitutionTerm.Name)) { isValid = isValid && typeChecker.IsSubstitutionValid(substitution); } else { // In this case, test each nested argument of the AC substitution term to match the term head variable. // This is due to the ONF convertion for AC terms. It keeps type checking in line with AC semantics. foreach (var argTerm in acSubstitutionTerm.OnfArguments.Select(arg => arg.Term)) { var testSubstitution = new Substitution { Variable = substitution.Variable, SubstitutionTerm = argTerm }; isValid = isValid && typeChecker.IsSubstitutionValid(testSubstitution); } } } } return(isValid); }
/// <summary> /// Applies the given substituion to this term, creating a new term. This method is used by the public /// version to apply a collection of substitutions. /// </summary> protected abstract TrsTermBase ApplySubstitution(Substitution substitution);
/// <summary> /// Checks that the given unifier is valid in terms of the type definitions. /// </summary> private bool IsUnifierValid(UnificationResult unifier, TrsTermBase matchedRuleHead) { // Variables at this level must be validated in a way that is sensitive to AC term type definitions // to account for semantics. If it is not add[:x,:y] will not match add[1,2,3] with limit :x,:y to $TrsNumber if (!unifier.Succeed) return false; if (unifier.Unifier.Count == 0) return true; // equal terms, nothing to validate // Keep track of variable parent cases Stack<TrsTermBase> evalStack = new Stack<TrsTermBase>(); Dictionary<TrsVariable, bool> isAcParent = new Dictionary<TrsVariable, bool>(); Dictionary<TrsVariable, bool> isNonAcParent = new Dictionary<TrsVariable, bool>(); Dictionary<TrsVariable, HashSet<string>> variableTermNames = new Dictionary<TrsVariable, HashSet<string>>(); evalStack.Push(matchedRuleHead); Action<TrsVariable, bool, Dictionary<TrsVariable, bool>> updateLookups = delegate(TrsVariable v, bool b, Dictionary<TrsVariable, bool> target) { if (target.ContainsKey(v)) target[v] = b; else target.Add(v, b); }; while (evalStack.Count > 0) { var current = evalStack.Pop(); // Variable only case ... this should not happen unless variable only reduction rule heads are allowed in the future var currentVariable = current as TrsVariable; if (currentVariable != null) { updateLookups(currentVariable, false, isAcParent); updateLookups(currentVariable, false, isNonAcParent); } else { // Check arguments var curTerm = current as TrsTerm; var curAcTerm = current as TrsAcTerm; foreach (var variable in Enumerable.Concat(curTerm == null ? new TrsVariable[0] : curTerm.Arguments.Where(arg => arg is TrsVariable).Cast<TrsVariable>(), curAcTerm == null ? new TrsVariable[0] : curAcTerm.OnfArguments.Where(arg => arg.Term is TrsVariable).Select(arg => arg.Term).Cast<TrsVariable>())) { updateLookups(variable, curTerm != null, isNonAcParent); updateLookups(variable, curAcTerm != null, isAcParent); if (curAcTerm != null) { HashSet<string> termNames; if (!variableTermNames.TryGetValue(variable, out termNames)) variableTermNames.Add(variable, termNames = new HashSet<string>()); termNames.Add(curAcTerm.Name); } } } } bool isValid = true; foreach (var substitution in unifier.Unifier) { // It is possible that the variable being tested does not occur in the term head ... if (!isNonAcParent.ContainsKey(substitution.Variable) && !isAcParent.ContainsKey(substitution.Variable)) { isValid = isValid && typeChecker.IsSubstitutionValid(substitution); continue; } // AC term case if (isNonAcParent.ContainsKey(substitution.Variable) && isNonAcParent[substitution.Variable]) isValid = isValid && typeChecker.IsSubstitutionValid(substitution); // Non-AC term case if (isAcParent.ContainsKey(substitution.Variable) && isAcParent[substitution.Variable]) { var acSubstitutionTerm = substitution.SubstitutionTerm as TrsAcTerm; if (acSubstitutionTerm == null || !variableTermNames[substitution.Variable].Contains(acSubstitutionTerm.Name)) { isValid = isValid && typeChecker.IsSubstitutionValid(substitution); } else { // In this case, test each nested argument of the AC substitution term to match the term head variable. // This is due to the ONF convertion for AC terms. It keeps type checking in line with AC semantics. foreach (var argTerm in acSubstitutionTerm.OnfArguments.Select(arg => arg.Term)) { var testSubstitution = new Substitution { Variable = substitution.Variable, SubstitutionTerm = argTerm }; isValid = isValid && typeChecker.IsSubstitutionValid(testSubstitution); } } } } return isValid; }
/// <summary> /// NB: this is not a general solution, only for c = b + x where + can be -, / or * and, b and x can be swapped arround. /// c is the match term. The rest is the head term. /// </summary> public List<UnificationResult> GetUnifier(TrsTermBase termHead, TrsTermBase matchTerm) { UnificationResult result = new UnificationResult(); result.Succeed = false; // Check input Substitution sRhs = new Substitution { Variable = new TrsVariable("exp_r"), SubstitutionTerm = termHead }; Substitution sLhs = new Substitution { Variable = new TrsVariable("exp_l"), SubstitutionTerm = matchTerm }; var headTerm = termHead as TrsTerm; if (!interpreter.TypeChecker.IsSubstitutionValid(sLhs) || !interpreter.TypeChecker.IsSubstitutionValid(sRhs)) { return new List<UnificationResult> { result }; } // Load problem interpreter.ClearExecutionCache(); interpreter.LoadTerms(new [] { new TrsTerm("rhs",new [] { termHead }), new TrsTerm("lhs",new [] { matchTerm }) }); // Solve while (interpreter.ExecuteRewriteStep()) { }; // Extract answer var runResults = interpreter.GetCurrentRewriteResult(); foreach (var stm in runResults.ProgramOut.Statements) { var resEq = stm as TrsTerm; if (resEq != null && resEq.Name == "eq" && resEq.Arguments.Count == 2 && resEq.Arguments[0] is TrsNumber && resEq.Arguments[1] is TrsVariable) { result.Succeed = true; result.Unifier = new List<Substitution>(); result.Unifier.Add(new Substitution() { Variable = resEq.Arguments[1] as TrsVariable, SubstitutionTerm = resEq.Arguments[0] }); } } return new List<UnificationResult> { result }; }
private void ProcessSubstitutionStep(UnificationContinuation currentProblem, Equation currEq, List<TrsVariable> variableNamesToPreserve) { // LHS will always be variable here ... preserve matched term variables Substitution newSub = null; if (currEq.Rhs is TrsVariable && variableNamesToPreserve.Contains(currEq.Lhs)) { newSub = new Substitution { Variable = (TrsVariable)currEq.Rhs, SubstitutionTerm = currEq.Lhs }; } else { newSub = new Substitution { Variable = (TrsVariable)currEq.Lhs, SubstitutionTerm = currEq.Rhs }; } currentProblem.ComposeAndApplySubstitutions(newSub); }
protected override TrsTermBase ApplySubstitution(Substitution substitution) { TrsTermProduct product = new TrsTermProduct(new List<TrsTermBase>()); foreach (var term in TermList) { if (term.Equals(substitution.Variable)) product.TermList.Add(substitution.SubstitutionTerm); else product.TermList.Add(term.ApplySubstitutions(new [] { substitution })); } return product; }
protected override TrsTermBase ApplySubstitution(Substitution substitution) { return this; }
/// <summary> /// Uses the input type definitions as a bottom up tree automaton to /// test the input mapping of a variable to a term. /// /// Note: /// * All final states must be matched for a mapping to be valid. /// * Types are "merged" therefore the same type definition name can have multiple definitions associated. /// </summary> public bool IsSubstitutionValid(Substitution substitution) { HashSet<TrsTypeDefinitionTypeName> endStates = null; // If variable not bound, it is valid by default if (!typeMappings.TryGetValue(substitution.Variable, out endStates)) return true; // Initial and final states var termIn = substitution.SubstitutionTerm.Convert(); AddDynamicStates(termIn); InterpreterType testType = new InterpreterType(termIn); var retVal = testType.IsTermValid(transitionFunction, endStates); // Undo dynamic changes to state machine to cater for $TrsNumber, $TrsConstant, $TrsString and $TrsVariable RemoveDynamicStates(); return retVal; }