/// <summary> /// Combines a list of unifiers for the term product /// </summary> private UnificationResult ComposeUnifiers(IEnumerable <UnificationResult> unifierList) { if (unifierList.Count() == 1) { return(unifierList.First()); } UnificationResult retVal = new UnificationResult(); var composedUnifier = new Dictionary <TrsVariable, TrsTermBase>(); foreach (var unifier in unifierList) { foreach (var substitution in unifier.Unifier) { TrsTermBase subValue = null; if (!composedUnifier.TryGetValue(substitution.Variable, out subValue)) { composedUnifier.Add(substitution.Variable, substitution.SubstitutionTerm); } else if (!subValue.Equals(substitution.SubstitutionTerm)) { // Conflicting mapping found. retVal.Succeed = false; return(retVal); } } } retVal.Succeed = true; retVal.Unifier = new List <Substitution>(); retVal.Unifier.AddRange(composedUnifier.Select(pair => new Substitution { Variable = pair.Key, SubstitutionTerm = pair.Value })); return(retVal); }
public InterpreterEvaluationTerm(TrsTermBase root, TrsTermBase subterm, InterpreterTerm cacheSourceTerm, UnificationResult currentUnifier) : base(root) { if (subterm == null) throw new ArgumentException("subterm"); if (cacheSourceTerm == null) throw new ArgumentException("cacheSourceTerm"); if (currentUnifier == null) throw new ArgumentException("currentUnifier"); CurrentSubTerm = subterm; CacheSourceTerm = cacheSourceTerm; Unifier = currentUnifier; }
public override bool Equals(object obj) { UnificationResult otherUnifier = obj as UnificationResult; if (otherUnifier == null) { return(false); } var substitutionSetOther = new HashSet <Substitution>(otherUnifier.Unifier); var subtitutionSetThis = new HashSet <Substitution>(Unifier); substitutionSetOther.IntersectWith(subtitutionSetThis); return(Succeed == otherUnifier.Succeed && substitutionSetOther.Count == subtitutionSetThis.Count); }
public InterpreterEvaluationTerm(TrsTermBase root, TrsTermBase subterm, InterpreterTerm cacheSourceTerm, UnificationResult currentUnifier) : base(root) { if (subterm == null) { throw new ArgumentException("subterm"); } if (cacheSourceTerm == null) { throw new ArgumentException("cacheSourceTerm"); } if (currentUnifier == null) { throw new ArgumentException("currentUnifier"); } CurrentSubTerm = subterm; CacheSourceTerm = cacheSourceTerm; Unifier = currentUnifier; }
/// <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); }
private void ApplyReductionRuleToCache(TrsReductionRule rule) { // This data structure stores the Cartesian product used for term products // The length of each sub-List is the size of an alphabet for calculating a "Godel string" of terms. // This avoids the need for seperate sub-enumerators. var rewriteCandidates = new List <List <InterpreterEvaluationTerm> >(); // Populate the rewriteCandidates TrsTermProduct ruleHead = (TrsTermProduct)rule.Head; int productLength = ruleHead.TermList.Count; for (int termProductIndex = 0; termProductIndex < productLength; termProductIndex++) { rewriteCandidates.Add(new List <InterpreterEvaluationTerm>()); foreach (var termInCache in executionCache) { // Do not rewrite new terms, we are doing one rewrite step at a time if (termInCache.IsNewTerm) { continue; } // Test all sub-terms var expantionStack = new Stack <TrsTermBase>(); expantionStack.Push(termInCache.RootTerm); while (expantionStack.Count > 0) { var current = expantionStack.Pop(); // Ignore the "variable only" case to avoid matching all rewrite rules to a sub-term. if (current is TrsVariable) { continue; } // Type rules applied here ... cater for multiple unification results by duplicating candidates foreach (var unifier in mguCalculation.GetUnifier(ruleHead.TermList[termProductIndex], current)) { if (IsUnifierValid(unifier, ruleHead.TermList[termProductIndex])) { rewriteCandidates[termProductIndex].Add(new InterpreterEvaluationTerm(termInCache.RootTerm, current, termInCache, unifier)); } } // Apply rewrite rule to subterms if (current is TrsTerm) { foreach (var subTerm in ((TrsTerm)current).Arguments) { expantionStack.Push(subTerm); } } else if (current is TrsAcTerm) { foreach (var subTerm in ((TrsAcTerm)current).OnfArguments) { expantionStack.Push(subTerm.Term); } } } } } // Execute rewite step ... iterate over cartesian term product // This iterationCount variable will prevent rewriting in the case where any of the lists are empty int iterationCount = rewriteCandidates.First().Count; foreach (var termList in rewriteCandidates.Skip(1)) { iterationCount *= termList.Count; } var matchTupple = new List <InterpreterEvaluationTerm>(rewriteCandidates.Count); for (int tuppleNumber = 0; tuppleNumber < iterationCount; tuppleNumber++) { var currDiv = tuppleNumber; UnificationResult currentUnifier = null; UnificationResult testUnifier = null; matchTupple.Clear(); // In order to do a substitution, all variables must bind to the same values across the tupple members for (int termColumn = 0; termColumn < rewriteCandidates.Count; termColumn++) { var currMod = currDiv % rewriteCandidates[termColumn].Count; currDiv = currDiv / rewriteCandidates[termColumn].Count; var targetTerm = rewriteCandidates[termColumn][currMod]; currentUnifier = targetTerm.Unifier; if (testUnifier == null) { testUnifier = currentUnifier; } matchTupple.Add(targetTerm); } var termProductUnifier = ComposeUnifiers(matchTupple.Select(term => term.Unifier)); if (termProductUnifier.Succeed) { foreach (var term in matchTupple) { ExecuteRewriteForRule(term, rule, termProductUnifier); } } } }
private void ExecuteRewriteForRule(InterpreterEvaluationTerm termToRewrite, TrsReductionRule rule, UnificationResult composedUnifier) { bool rewritingTookPlaceLocal = false; if (composedUnifier.Succeed) { if (rule.Tail.GetType() == typeof(TrsNativeKeyword)) { // Replacing term value with a native function generated value var nativeTermInHead = ((TrsTermBase)termToRewrite.CurrentSubTerm.CreateCopy()).ApplySubstitutions(composedUnifier.Unifier); foreach (var native in nativeFunctions) { var processedTerm = native.Evaluate(nativeTermInHead); if (processedTerm == null) { throw new Exception(string.Format("Native function in type {0} returned null", native.GetType().FullName)); } // If the rewrite result is the same, no rewriting took place ... if (!nativeTermInHead.Equals(processedTerm)) { var newTerm = termToRewrite.RootTerm.CreateCopyAndReplaceSubTerm(termToRewrite.CurrentSubTerm, processedTerm); rewritingTookPlaceLocal = true; var newTermWrapper = new InterpreterTerm(newTerm); newTermWrapper.IsNewTerm = true; executionCache.Add(newTermWrapper); } } } else { // Normal rewriting without native eval functions var replacementTerm = ((TrsTermBase)rule.Tail.CreateCopy()).ApplySubstitutions(composedUnifier.Unifier); if (!termToRewrite.CurrentSubTerm.Equals(replacementTerm)) { var newTerm = termToRewrite.RootTerm.CreateCopyAndReplaceSubTerm(termToRewrite.CurrentSubTerm, replacementTerm); rewritingTookPlaceLocal = true; var newTermWrapper = new InterpreterTerm(newTerm); newTermWrapper.IsNewTerm = true; executionCache.Add(newTermWrapper); } } } if (rewritingTookPlaceLocal) { rewritingTookPlace = true; // stops rewriting on next rewrite step termToRewrite.CacheSourceTerm.MustDeletFromCache = true; } }
/// <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; }
private void ExecuteRewriteForRule(InterpreterEvaluationTerm termToRewrite, TrsReductionRule rule, UnificationResult composedUnifier) { bool rewritingTookPlaceLocal = false; if (composedUnifier.Succeed) { if (rule.Tail.GetType() == typeof(TrsNativeKeyword)) { // Replacing term value with a native function generated value var nativeTermInHead = ((TrsTermBase)termToRewrite.CurrentSubTerm.CreateCopy()).ApplySubstitutions(composedUnifier.Unifier); foreach (var native in nativeFunctions) { var processedTerm = native.Evaluate(nativeTermInHead); if (processedTerm == null) throw new Exception(string.Format("Native function in type {0} returned null", native.GetType().FullName)); // If the rewrite result is the same, no rewriting took place ... if (!nativeTermInHead.Equals(processedTerm)) { var newTerm = termToRewrite.RootTerm.CreateCopyAndReplaceSubTerm(termToRewrite.CurrentSubTerm, processedTerm); rewritingTookPlaceLocal = true; var newTermWrapper = new InterpreterTerm(newTerm); newTermWrapper.IsNewTerm = true; executionCache.Add(newTermWrapper); } } } else { // Normal rewriting without native eval functions var replacementTerm = ((TrsTermBase)rule.Tail.CreateCopy()).ApplySubstitutions(composedUnifier.Unifier); if (!termToRewrite.CurrentSubTerm.Equals(replacementTerm)) { var newTerm = termToRewrite.RootTerm.CreateCopyAndReplaceSubTerm(termToRewrite.CurrentSubTerm, replacementTerm); rewritingTookPlaceLocal = true; var newTermWrapper = new InterpreterTerm(newTerm); newTermWrapper.IsNewTerm = true; executionCache.Add(newTermWrapper); } } } if (rewritingTookPlaceLocal) { rewritingTookPlace = true; // stops rewriting on next rewrite step termToRewrite.CacheSourceTerm.MustDeletFromCache = true; } }
/// <summary> /// Combines a list of unifiers for the term product /// </summary> private UnificationResult ComposeUnifiers(IEnumerable<UnificationResult> unifierList) { if (unifierList.Count() == 1) return unifierList.First(); UnificationResult retVal = new UnificationResult(); var composedUnifier = new Dictionary<TrsVariable, TrsTermBase>(); foreach (var unifier in unifierList) { foreach (var substitution in unifier.Unifier) { TrsTermBase subValue = null; if (!composedUnifier.TryGetValue(substitution.Variable, out subValue)) { composedUnifier.Add(substitution.Variable, substitution.SubstitutionTerm); } else if (!subValue.Equals(substitution.SubstitutionTerm)) { // Conflicting mapping found. retVal.Succeed = false; return retVal; } } } retVal.Succeed = true; retVal.Unifier = new List<Substitution>(); retVal.Unifier.AddRange(composedUnifier.Select(pair => new Substitution { Variable = pair.Key, SubstitutionTerm = pair.Value })); return retVal; }
/// <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 }; }