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    }