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.ProofStepClauseDemodulation;
009    import aima.logic.fol.kb.data.Clause;
010    import aima.logic.fol.kb.data.Literal;
011    import aima.logic.fol.parsing.ast.AtomicSentence;
012    import aima.logic.fol.parsing.ast.Term;
013    import aima.logic.fol.parsing.ast.TermEquality;
014    import aima.logic.fol.parsing.ast.Variable;
015    
016    /**
017     * Artificial Intelligence A Modern Approach (2nd Edition): page 304.<br>
018     * Demodulation: For any terms x, y, z, where UNIFY(x,z) = theta and m<sub>n</sub>[z] is a
019     * literal containing z:<br>
020     * <pre>
021     *     x=y, m1 OR ... OR m<sub>n</sub>[z]
022     *     ----------------------------------
023     *     m1 OR ... m<sub>n</sub>[SUBST(theta,y)]
024     * </pre>
025     * Demodulation is typically used for simplifying expressions using collections of assertions
026     * such as x + 0 = x, x<sup>1</sup> = x, and so on.<br>
027     * <br>
028     * Some additional restrictions/clarifications highlighted in:<br>
029     * http://logic.stanford.edu/classes/cs157/2008/lectures/lecture15.pdf<br>
030     * 1. Unit Equations Only.<br>
031     * 2. Variables substituted in Equation Only.<br>
032     */
033    
034    /**
035     * @author Ciaran O'Reilly
036     * 
037     */
038    public class Demodulation extends AbstractModulation {
039            public Demodulation() {
040            }
041    
042            public Clause apply(TermEquality assertion, Clause clExpression) {
043                    Clause altClExpression = null;
044    
045                    for (Literal l1 : clExpression.getLiterals()) {
046                            AtomicSentence altExpression = apply(assertion, l1
047                                            .getAtomicSentence());
048                            if (null != altExpression) {
049                                    // I have an alternative, create a new clause
050                                    // with the alternative and return
051                                    List<Literal> newLits = new ArrayList<Literal>();
052                                    for (Literal l2 : clExpression.getLiterals()) {
053                                            if (l1.equals(l2)) {
054                                                    newLits.add(l1.newInstance(altExpression));
055                                            } else {
056                                                    newLits.add(l2);
057                                            }
058                                    }
059                                    // Only apply demodulation at most once on
060                                    // each call.
061                                    altClExpression = new Clause(newLits);
062                                    altClExpression.setProofStep(new ProofStepClauseDemodulation(
063                                                    altClExpression, clExpression, assertion));
064                                    if (clExpression.isImmutable()) {
065                                            altClExpression.setImmutable();
066                                    }
067                                    if (!clExpression.isStandardizedApartCheckRequired()) {
068                                            altClExpression.setStandardizedApartCheckNotRequired();
069                                    }
070                                    break;
071                            }
072                    }
073    
074                    return altClExpression;
075            }
076    
077            public AtomicSentence apply(TermEquality assertion,
078                            AtomicSentence expression) {
079                    AtomicSentence altExpression = null;
080    
081                    IdentifyCandidateMatchingTerm icm = getMatchingSubstitution(assertion
082                                    .getTerm1(),
083                                    expression);
084    
085                    if (null != icm) {
086                            Term replaceWith = substVisitor.subst(
087                                            icm.getMatchingSubstitution(), assertion.getTerm2());
088                            // Want to ignore reflexivity axiom situation, i.e. x = x
089                            if (!icm.getMatchingTerm().equals(replaceWith)) {
090                                    ReplaceMatchingTerm rmt = new ReplaceMatchingTerm();
091    
092                                    // Only apply demodulation at most once on each call.
093                                    altExpression = rmt.replace(expression, icm.getMatchingTerm(),
094                                                    replaceWith);
095                            }
096                    }
097    
098                    return altExpression;
099            }
100    
101            //
102            // PROTECTED METHODS
103            //
104            protected boolean isValidMatch(Term toMatch,
105                            Set<Variable> toMatchVariables, Term possibleMatch,
106                            Map<Variable, Term> substitution) {
107                    // Demodulation only allows substitution in the equation only,
108                    // if the substitution contains variables not in the toMatch
109                    // side of the equation (i.e. left hand side), then
110                    // it is not a legal demodulation match.
111                    // Note: see:
112                    // http://logic.stanford.edu/classes/cs157/2008/lectures/lecture15.pdf
113                    // slide 23 for an example.
114                    if (toMatchVariables.containsAll(substitution.keySet())) {
115                            return true;
116                    }
117    
118                    return false;
119            }
120    }