/// <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 static TrsTypeDefinitionTermBase Convert(this TrsTermBase termIn) { TrsAtom atom = termIn as TrsAtom; TrsVariable variable = termIn as TrsVariable; TrsTerm term = termIn as TrsTerm; TrsAcTerm acTerm = termIn as TrsAcTerm; if (atom != null) { return(atom.Convert()); } else if (variable != null) { return(variable.Convert()); } else if (term != null) { return(term.Convert()); } else if (acTerm != null) { return(acTerm.Convert()); } else { throw new Exception("Unexpected type: " + termIn.GetType().FullName); } }
public InterpreterTerm(TrsTermBase root) { if (root == null) { throw new ArgumentException("Root may not be null", "root"); } RootTerm = root; }
public List <UnificationResult> GetUnifier(TrsTermBase ruleHead, TrsTermBase matchTerm) { List <UnificationResult> unifiers = new List <UnificationResult>(); for (int i = 0; i < 1; i++) { var tempResult = new UnificationResult(); tempResult.Unifier = new List <Substitution>(); foreach (var variable in ruleHead.GetVariables()) { tempResult.Unifier.Add(new Substitution { SubstitutionTerm = new TrsNumber(i.ToString()), Variable = variable }); } tempResult.Succeed = true; unifiers.Add(tempResult); } return(unifiers); }
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> /// 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 }); }
/// <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); }
public TrsTermBase Evaluate(TrsTermBase termIn) { var tIn = termIn as TrsTerm; var tInAc = termIn as TrsAcTerm; if (tIn != null) { if (tIn.Arguments.Count != 2) { return(termIn); } if (!(tIn.Arguments[0] is TrsNumber) || !(tIn.Arguments[1] is TrsNumber)) { return(termIn); } var numLhs = Convert.ToDouble(((TrsNumber)tIn.Arguments[0]).Value); var numRhs = Convert.ToDouble(((TrsNumber)tIn.Arguments[1]).Value); double retVal; switch (tIn.Name.ToLower()) { case Subtraction: retVal = numLhs - numRhs; break; case Divide: if (numRhs == 0) { return(tIn); } else { retVal = numLhs / numRhs; } break; default: return(tIn); } return(new TrsNumber(retVal.ToString())); } else if (tInAc != null) { if (tInAc.TotalCardinality < 2) { throw new InvalidProgramException("AC term with less than 2 arguments"); } if (tInAc.OnfArguments.Where(arg => arg.Term is TrsNumber).Select(arg => arg.Cardinality).Count() != tInAc.TotalCardinality) { return(tInAc); } if (tInAc.Name == Addition) { return(new TrsNumber(tInAc.ExpandedArguments.Select(arg => Convert.ToDouble(((TrsNumber)arg).Value)).Sum().ToString())); } else if (tInAc.Name == Multiply) { double retVal = 1.0; foreach (var number in tInAc.ExpandedArguments.Select(arg => Convert.ToDouble(((TrsNumber)arg).Value))) { retVal *= number; } return(new TrsNumber(retVal.ToString())); } else { return(tInAc); } } else { return(termIn); } }
public override TrsTermBase CreateCopyAndReplaceSubTerm(TrsTermBase termToReplace, TrsTermBase replacementTerm) { return(this); }
public List <UnificationResult> GetUnifier(TrsTermBase termHead, TrsTermBase matchTerm) { return(GetMgu(new Equation { Lhs = termHead, Rhs = matchTerm }, matchTerm.GetVariables())); }
/// <summary> /// Returns empty list if the terms in the equation cannot be unified or if Lhs = Rhs with no variables, /// otherwise a list of substitutions defining the MGU /// /// Based on Unification chapter from Handbook of Automated Reasoning. /// </summary> private List <UnificationResult> GetMgu(Equation unificationProblem, List <TrsVariable> variableNamesToPreserve) { if (unificationProblem == null) { throw new ArgumentException(); } if (unificationProblem.Lhs == null) { throw new ArgumentException(); } if (unificationProblem.Rhs == null) { throw new ArgumentException(); } if (variableNamesToPreserve == null) { throw new ArgumentException(); } Equation initialEquation = unificationProblem.CreateCopy(); UnificationContinuation currentProblem = new UnificationContinuation { CurrentEquations = new List <Equation>(), CurrentSubstitutions = new List <Substitution>() }; currentProblem.CurrentEquations.Add(initialEquation); Queue <UnificationContinuation> currentContinuations = new Queue <UnificationContinuation>(); currentContinuations.Enqueue(currentProblem); HashSet <UnificationResult> results = new HashSet <UnificationResult>(); Func <UnificationContinuation, Equation, bool> processFail = delegate(UnificationContinuation curr, Equation currEq) { var failResult = CustomFail(currentProblem, currEq, variableNamesToPreserve); var succeed = true; if (failResult.Count == 0) { succeed = false; } else if (failResult.Count == 1) { currentProblem = failResult.First(); } else { foreach (var continuation in failResult) { currentContinuations.Enqueue(continuation); } } return(succeed); }; while (currentContinuations.Count > 0) { currentProblem = currentContinuations.Dequeue(); bool fail = false; while (currentProblem.CurrentEquations.Count > 0 && !fail) { var currEq = currentProblem.CurrentEquations[currentProblem.CurrentEquations.Count - 1]; currentProblem.CurrentEquations.RemoveAt(currentProblem.CurrentEquations.Count - 1); if (currEq.Lhs.Equals(currEq.Rhs)) { // Elimination by omission (this is a "succeed" case) } else if (currEq.Lhs is TrsAtom && currEq.Rhs is TrsAtom) { if (!currEq.Lhs.Equals(currEq.Rhs)) { fail = !processFail(currentProblem, currEq); } } else if (currEq.Lhs is TrsAtom && currEq.Rhs is TrsTerm || currEq.Lhs is TrsTerm && currEq.Rhs is TrsAtom || currEq.Lhs is TrsAtom && currEq.Rhs is TrsAcTerm || currEq.Lhs is TrsAcTerm && currEq.Rhs is TrsAtom || currEq.Lhs is TrsTerm && currEq.Rhs is TrsAcTerm || currEq.Lhs is TrsAcTerm && currEq.Rhs is TrsTerm) { fail = !processFail(currentProblem, currEq); } else if (currEq.Lhs is TrsTerm && currEq.Rhs is TrsTerm) { TrsTerm lhs = currEq.Lhs as TrsTerm; TrsTerm rhs = currEq.Rhs as TrsTerm; if (lhs.Name != rhs.Name || lhs.Arguments.Count != rhs.Arguments.Count) { fail = !processFail(currentProblem, currEq); } else { currentProblem.CurrentEquations.AddRange(Enumerable.Range(0, lhs.Arguments.Count). Select(i => new Equation { Lhs = lhs.Arguments[i], Rhs = rhs.Arguments[i] })); } } else if (currEq.Lhs is TrsAcTerm && currEq.Rhs is TrsAcTerm) { // Note: Failure is already processed internally in the next function call (ie. custom unifiers are called) fail = ProcessAcUnificationStep(currentProblem, currentContinuations, processFail, currEq); } else if (!(currEq.Lhs is TrsVariable) && (currEq.Rhs is TrsVariable)) { TrsTermBase lhsSwap = currEq.Lhs; currEq.Lhs = currEq.Rhs; currEq.Rhs = lhsSwap; currentProblem.CurrentEquations.Add(currEq); } else if (currEq.Lhs is TrsVariable) { // Occurs check if (currEq.Rhs.ContainsVariable((TrsVariable)currEq.Lhs)) { fail = !processFail(currentProblem, currEq); } else { ProcessSubstitutionStep(currentProblem, currEq, variableNamesToPreserve); } } else { throw new Exception("Invalid program state"); } } if (!fail) { results.Add(new UnificationResult { Succeed = true, Unifier = currentProblem.CurrentSubstitutions }); } } return(results.ToList()); }