001 package aima.logic.fol.inference; 002 003 import java.util.ArrayList; 004 import java.util.List; 005 import java.util.Map; 006 import java.util.Set; 007 008 import aima.logic.fol.inference.proof.Proof; 009 import aima.logic.fol.inference.proof.ProofFinal; 010 import aima.logic.fol.inference.proof.ProofStep; 011 import aima.logic.fol.inference.proof.ProofStepFoChAlreadyAFact; 012 import aima.logic.fol.inference.proof.ProofStepFoChAssertFact; 013 import aima.logic.fol.kb.FOLKnowledgeBase; 014 import aima.logic.fol.kb.data.Clause; 015 import aima.logic.fol.kb.data.Literal; 016 import aima.logic.fol.parsing.ast.AtomicSentence; 017 import aima.logic.fol.parsing.ast.NotSentence; 018 import aima.logic.fol.parsing.ast.Sentence; 019 import aima.logic.fol.parsing.ast.Term; 020 import aima.logic.fol.parsing.ast.Variable; 021 022 /** 023 * Artificial Intelligence A Modern Approach (2nd Edition): Figure 9.3, page 282. 024 * 025 * <pre> 026 * function FOL-FC-ASK(KB, alpha) returns a substitution or false 027 * inputs: KB, the knowledge base, a set of first order definite clauses 028 * alpha, the query, an atomic sentence 029 * local variables: new, the new sentences inferred on each iteration 030 * 031 * repeat until new is empty 032 * new <- {} 033 * for each sentence r in KB do 034 * (p1 ^ ... ^ pn => q) <- STANDARDIZE-APART(r) 035 * for each theta such that SUBST(theta, p1 ^ ... ^ pn) = SUBST(theta, p'1 ^ ... ^ p'n) 036 * for some p'1,...,p'n in KB 037 * q' <- SUBST(theta, q) 038 * if q' is not a renaming of some sentence already in KB or new then do 039 * add q' to new 040 * theta <- UNIFY(q', alpha) 041 * if theta is not fail then return theta 042 * add new to KB 043 * return false 044 * </pre> 045 * 046 * Figure 9.3 A conceptually straightforward, but very inefficient forward-chaining algo- 047 * rithm. On each iteration, it adds to KB all the atomic sentences that can be inferred in one 048 * step from the implication sentences and the atomic sentences already in KB. 049 */ 050 051 /** 052 * @author Ciaran O'Reilly 053 * 054 */ 055 public class FOLFCAsk implements InferenceProcedure { 056 057 public FOLFCAsk() { 058 } 059 060 // 061 // START-InferenceProcedure 062 063 /** 064 * <code> 065 * function FOL-FC-ASK(KB, alpha) returns a substitution or false 066 * inputs: KB, the knowledge base, a set of first order definite clauses 067 * alpha, the query, an atomic sentence 068 * </code> 069 */ 070 public InferenceResult ask(FOLKnowledgeBase KB, Sentence query) { 071 // Assertions on the type of queries this Inference procedure 072 // supports 073 if (!(query instanceof AtomicSentence)) { 074 throw new IllegalArgumentException( 075 "Only Atomic Queries are supported."); 076 } 077 078 FCAskAnswerHandler ansHandler = new FCAskAnswerHandler(); 079 080 Literal alpha = new Literal((AtomicSentence) query); 081 082 // local variables: new, the new sentences inferred on each iteration 083 List<Literal> newSentences = new ArrayList<Literal>(); 084 085 // Ensure query is not already a know fact before 086 // attempting forward chaining. 087 Set<Map<Variable, Term>> answers = KB.fetch(alpha); 088 if (answers.size() > 0) { 089 ansHandler.addProofStep(new ProofStepFoChAlreadyAFact(alpha)); 090 ansHandler.setAnswers(answers); 091 return ansHandler; 092 } 093 094 // repeat until new is empty 095 do { 096 097 // new <- {} 098 newSentences.clear(); 099 // for each sentence r in KB do 100 // (p1 ^ ... ^ pn => q) <-STANDARDIZE-APART(r) 101 for (Clause impl : KB.getAllDefiniteClauseImplications()) { 102 impl = KB.standardizeApart(impl); 103 // for each theta such that SUBST(theta, p1 ^ ... ^ pn) = 104 // SUBST(theta, p'1 ^ ... ^ p'n) 105 // --- for some p'1,...,p'n in KB 106 for (Map<Variable, Term> theta : KB.fetch(invert(impl 107 .getNegativeLiterals()))) { 108 // q' <- SUBST(theta, q) 109 Literal qDelta = KB.subst(theta, impl.getPositiveLiterals() 110 .get(0)); 111 // if q' is not a renaming of some sentence already in KB or 112 // new then do 113 if (!KB.isRenaming(qDelta) 114 && !KB.isRenaming(qDelta, newSentences)) { 115 // add q' to new 116 newSentences.add(qDelta); 117 ansHandler.addProofStep(impl, qDelta, theta); 118 // theta <- UNIFY(q', alpha) 119 theta = KB.unify(qDelta.getAtomicSentence(), alpha 120 .getAtomicSentence()); 121 // if theta is not fail then return theta 122 if (null != theta) { 123 for (Literal l : newSentences) { 124 Sentence s = null; 125 if (l.isPositiveLiteral()) { 126 s = l.getAtomicSentence(); 127 } else { 128 s = new NotSentence(l.getAtomicSentence()); 129 } 130 KB.tell(s); 131 } 132 ansHandler.setAnswers(KB.fetch(alpha)); 133 return ansHandler; 134 } 135 } 136 } 137 } 138 // add new to KB 139 for (Literal l : newSentences) { 140 Sentence s = null; 141 if (l.isPositiveLiteral()) { 142 s = l.getAtomicSentence(); 143 } else { 144 s = new NotSentence(l.getAtomicSentence()); 145 } 146 KB.tell(s); 147 } 148 } while (newSentences.size() > 0); 149 150 // return false 151 return ansHandler; 152 } 153 154 // END-InferenceProcedure 155 // 156 157 // 158 // PRIVATE METHODS 159 // 160 private List<Literal> invert(List<Literal> lits) { 161 List<Literal> invLits = new ArrayList<Literal>(); 162 for (Literal l : lits) { 163 invLits.add(new Literal(l.getAtomicSentence(), (l 164 .isPositiveLiteral() ? true : false))); 165 } 166 return invLits; 167 } 168 169 class FCAskAnswerHandler implements InferenceResult { 170 171 private ProofStep stepFinal = null; 172 private List<Proof> proofs = new ArrayList<Proof>(); 173 174 public FCAskAnswerHandler() { 175 176 } 177 178 // 179 // START-InferenceResult 180 public boolean isPossiblyFalse() { 181 return proofs.size() == 0; 182 } 183 184 public boolean isTrue() { 185 return proofs.size() > 0; 186 } 187 188 public boolean isUnknownDueToTimeout() { 189 return false; 190 } 191 192 public boolean isPartialResultDueToTimeout() { 193 return false; 194 } 195 196 public List<Proof> getProofs() { 197 return proofs; 198 } 199 200 // END-InferenceResult 201 // 202 203 public void addProofStep(Clause implication, Literal fact, 204 Map<Variable, Term> bindings) { 205 stepFinal = new ProofStepFoChAssertFact(implication, fact, 206 bindings, stepFinal); 207 } 208 209 public void addProofStep(ProofStep step) { 210 stepFinal = step; 211 } 212 213 public void setAnswers(Set<Map<Variable, Term>> answers) { 214 for (Map<Variable, Term> ans : answers) { 215 proofs.add(new ProofFinal(stepFinal, ans)); 216 } 217 } 218 } 219 }