// END-InferenceProcedure // // // PRIVATE METHODS // /** * <code> * function FOL-BC-ASK(KB, goals, theta) returns a set of substitutions * input: KB, a knowledge base * goals, a list of conjuncts forming a query (theta already applied) * theta, the current substitution, initially the empty substitution {} * </code> */ private List<List<ProofStepBwChGoal>> folbcask(FOLKnowledgeBase KB, BCAskAnswerHandler ansHandler, List<Literal> goals, Dictionary<Variable, Term> theta) { List<List<ProofStepBwChGoal>> thisLevelProofSteps = new List<List<ProofStepBwChGoal>>(); // local variables: answers, a set of substitutions, initially empty // if goals is empty then return {theta} if (goals.Count==0) { thisLevelProofSteps.Add(new List<ProofStepBwChGoal>()); return thisLevelProofSteps; } // qDelta <- SUBST(theta, FIRST(goals)) Literal qDelta = KB.subst(theta, goals[0]); // for each sentence r in KB where // STANDARDIZE-APART(r) = (p1 ^ ... ^ pn => q) foreach (Clause r in KB.getAllDefiniteClauses()) { Clause r2 = KB.standardizeApart(r); // and thetaDelta <- UNIFY(q, qDelta) succeeds Dictionary<Variable, Term> thetaDelta = KB.unify(r2.getPositiveLiterals() [0].getAtomicSentence(), qDelta.getAtomicSentence()); if (null != thetaDelta) { // new_goals <- [p1,...,pn|REST(goals)] List<Literal> newGoals = new List<Literal>(r2 .getNegativeLiterals()); newGoals.AddRange(goals.Skip(1)); // answers <- FOL-BC-ASK(KB, new_goals, COMPOSE(thetaDelta, // theta)) U answers Dictionary<Variable, Term> composed = compose(KB, thetaDelta, theta); List<List<ProofStepBwChGoal>> lowerLevelProofSteps = folbcask( KB, ansHandler, newGoals, composed); ansHandler.addProofStep(lowerLevelProofSteps, r2, qDelta, composed); thisLevelProofSteps.AddRange(lowerLevelProofSteps); } } // return answers return thisLevelProofSteps; }
// Artificial Intelligence A Modern Approach (2nd Edition): page 288. // COMPOSE(delta, tau) is the substitution whose effect is identical to // the effect of applying each substitution in turn. That is, // SUBST(COMPOSE(theta1, theta2), p) = SUBST(theta2, SUBST(theta1, p)) private Dictionary<Variable, Term> compose(FOLKnowledgeBase KB, Dictionary<Variable, Term> theta1, Dictionary<Variable, Term> theta2) { Dictionary<Variable, Term> composed = new Dictionary<Variable, Term>(); // So that it behaves like: // SUBST(theta2, SUBST(theta1, p)) // There are two steps involved here. // See: http://logic.stanford.edu/classes/cs157/2008/notes/chap09.pdf // for a detailed discussion: // 1. Apply theta2 to the range of theta1. foreach (Variable v in theta1.Keys) { composed.Add(v, KB.subst(theta2, theta1[v])); } // 2. Adjoin to delta all pairs from tau with different // domain variables. foreach (Variable v in theta2.Keys) { if (!theta1.ContainsKey(v)) { composed.Add(v, theta2[v]); } } return cascadeSubstitutions(KB, composed); }
// See: // http://logic.stanford.edu/classes/cs157/2008/miscellaneous/faq.html#jump165 // for need for this. private Dictionary<Variable, Term> cascadeSubstitutions(FOLKnowledgeBase KB, Dictionary<Variable, Term> theta) { foreach (Variable v in theta.Keys) { Term t = theta[v]; theta.Add(v, KB.subst(theta, t)); } return theta; }
// // START-InferenceProcedure /** * <code> * function FOL-FC-ASK(KB, alpha) returns a substitution or false * inputs: KB, the knowledge base, a set of first order definite clauses * alpha, the query, an atomic sentence * </code> */ public InferenceResult ask(FOLKnowledgeBase KB, Sentence query) { // Assertions on the type of queries this Inference procedure // supports if (!(query is AtomicSentence)) { throw new ArgumentException( "Only Atomic Queries are supported."); } FCAskAnswerHandler ansHandler = new FCAskAnswerHandler(); Literal alpha = new Literal((AtomicSentence)query); // local variables: new, the new sentences inferred on each iteration List<Literal> newSentences = new List<Literal>(); // Ensure query is not already a know fact before // attempting forward chaining. List<Dictionary<Variable, Term>> answers = KB.fetch(alpha); if (answers.Count > 0) { ansHandler.addProofStep(new ProofStepFoChAlreadyAFact(alpha)); ansHandler.setAnswers(answers); return ansHandler; } // repeat until new is empty do { // new <- {} newSentences.Clear(); // for each rule in KB do // (p1 ^ ... ^ pn => q) <-STANDARDIZE-VARIABLES(rule) foreach (Clause impl in KB.getAllDefiniteClauseImplications()) { Clause impl2 = KB.standardizeApart(impl); // for each theta such that SUBST(theta, p1 ^ ... ^ pn) = // SUBST(theta, p'1 ^ ... ^ p'n) // --- for some p'1,...,p'n in KB foreach (Dictionary<Variable, Term> theta in KB.fetch(invert(new List<Literal>(impl2 .getNegativeLiterals())))) { // q' <- SUBST(theta, q) Literal qPrime = KB.subst(theta, impl.getPositiveLiterals() [0]); // if q' does not unify with some sentence already in KB or // new then do if (!KB.isRenaming(qPrime) && !KB.isRenaming(qPrime, newSentences)) { // add q' to new newSentences.Add(qPrime); ansHandler.addProofStep(impl, qPrime, theta); // theta <- UNIFY(q', alpha) Dictionary<Variable, Term> theta2 = KB.unify(qPrime.getAtomicSentence(), alpha .getAtomicSentence()); // if theta is not fail then return theta if (null != theta2) { foreach (Literal l in newSentences) { Sentence s = null; if (l.isPositiveLiteral()) { s = l.getAtomicSentence(); } else { s = new NotSentence(l.getAtomicSentence()); } KB.tell(s); } ansHandler.setAnswers(KB.fetch(alpha)); return ansHandler; } } } } // add new to KB foreach (Literal l in newSentences) { Sentence s = null; if (l.isPositiveLiteral()) { s = l.getAtomicSentence(); } else { s = new NotSentence(l.getAtomicSentence()); } KB.tell(s); } } while (newSentences.Count > 0); // return false return ansHandler; }