001 package aima.logic.fol.inference; 002 003 import java.util.ArrayList; 004 import java.util.LinkedHashSet; 005 import java.util.List; 006 import java.util.Map; 007 import java.util.Set; 008 009 import aima.logic.fol.StandardizeApart; 010 import aima.logic.fol.StandardizeApartIndexical; 011 import aima.logic.fol.StandardizeApartIndexicalFactory; 012 import aima.logic.fol.inference.proof.ProofStepClauseParamodulation; 013 import aima.logic.fol.kb.data.Clause; 014 import aima.logic.fol.kb.data.Literal; 015 import aima.logic.fol.parsing.ast.AtomicSentence; 016 import aima.logic.fol.parsing.ast.Term; 017 import aima.logic.fol.parsing.ast.TermEquality; 018 import aima.logic.fol.parsing.ast.Variable; 019 020 /** 021 * Artificial Intelligence A Modern Approach (2nd Edition): page 304.<br> 022 * Paramodulation: For any terms x, y, z, where UNIFY(x,z) = theta,<br> 023 * <pre> 024 * l1 OR ... l<sub>k</sub> OR x=y, m1 OR ... OR m<sub>n</sub>[z] 025 * ----------------------------------------------------------------- 026 * SUBST(theta, l1 OR ... l<sub>k</sub> OR m1 OR ... OR m<sub>n</sub>[y]) 027 * </pre> 028 * Unlike demodulation, paramodulation yields a complete inference procedure for first-order 029 * logic with equality. 030 */ 031 032 /** 033 * @author Ciaran O'Reilly 034 * 035 */ 036 public class Paramodulation extends AbstractModulation { 037 private static StandardizeApartIndexical _saIndexical = StandardizeApartIndexicalFactory 038 .newStandardizeApartIndexical('p'); 039 private static List<Literal> _emptyLiteralList = new ArrayList<Literal>(); 040 // 041 private StandardizeApart sApart = new StandardizeApart(); 042 043 public Paramodulation() { 044 } 045 046 public Set<Clause> apply(Clause c1, Clause c2) { 047 return apply(c1, c2, false); 048 } 049 050 public Set<Clause> apply(Clause c1, Clause c2, boolean standardizeApart) { 051 Set<Clause> paraExpressions = new LinkedHashSet<Clause>(); 052 053 for (int i = 0; i < 2; i++) { 054 Clause topClause, equalityClause; 055 if (i == 0) { 056 topClause = c1; 057 equalityClause = c2; 058 } else { 059 topClause = c2; 060 equalityClause = c1; 061 } 062 063 for (Literal possEqLit : equalityClause.getLiterals()) { 064 // Must be a positive term equality to be used 065 // for paramodulation. 066 if (possEqLit.isPositiveLiteral() 067 && possEqLit.getAtomicSentence() instanceof TermEquality) { 068 TermEquality assertion = (TermEquality) possEqLit 069 .getAtomicSentence(); 070 071 // Test matching for both sides of the equality 072 for (int x = 0; x < 2; x++) { 073 Term toMatch, toReplaceWith; 074 if (x == 0) { 075 toMatch = assertion.getTerm1(); 076 toReplaceWith = assertion.getTerm2(); 077 } else { 078 toMatch = assertion.getTerm2(); 079 toReplaceWith = assertion.getTerm1(); 080 } 081 082 for (Literal l1 : topClause.getLiterals()) { 083 IdentifyCandidateMatchingTerm icm = getMatchingSubstitution( 084 toMatch, l1.getAtomicSentence()); 085 086 if (null != icm) { 087 Term replaceWith = substVisitor.subst(icm 088 .getMatchingSubstitution(), 089 toReplaceWith); 090 091 // Want to ignore reflexivity axiom situation, 092 // i.e. x = x 093 if (icm.getMatchingTerm().equals(replaceWith)) { 094 continue; 095 } 096 097 ReplaceMatchingTerm rmt = new ReplaceMatchingTerm(); 098 099 AtomicSentence altExpression = rmt.replace(l1 100 .getAtomicSentence(), icm 101 .getMatchingTerm(), replaceWith); 102 103 // I have an alternative, create a new clause 104 // with the alternative and the substitution 105 // applied to all the literals before returning 106 List<Literal> newLits = new ArrayList<Literal>(); 107 for (Literal l2 : topClause.getLiterals()) { 108 if (l1.equals(l2)) { 109 newLits 110 .add(l1 111 .newInstance((AtomicSentence) substVisitor 112 .subst( 113 icm 114 .getMatchingSubstitution(), 115 altExpression))); 116 } else { 117 newLits 118 .add(substVisitor 119 .subst( 120 icm 121 .getMatchingSubstitution(), 122 l2)); 123 } 124 } 125 // Assign the equality clause literals, 126 // excluding 127 // the term equality used. 128 for (Literal l2 : equalityClause.getLiterals()) { 129 if (possEqLit.equals(l2)) { 130 continue; 131 } 132 newLits.add(substVisitor.subst(icm 133 .getMatchingSubstitution(), l2)); 134 } 135 136 // Only apply paramodulation at most once 137 // for each term equality. 138 Clause nc = null; 139 if (standardizeApart) { 140 sApart.standardizeApart(newLits, 141 _emptyLiteralList, _saIndexical); 142 nc = new Clause(newLits); 143 144 } else { 145 nc = new Clause(newLits); 146 } 147 nc 148 .setProofStep(new ProofStepClauseParamodulation( 149 nc, topClause, equalityClause, 150 assertion)); 151 if (c1.isImmutable()) { 152 nc.setImmutable(); 153 } 154 if (!c1.isStandardizedApartCheckRequired()) { 155 c1.setStandardizedApartCheckNotRequired(); 156 } 157 paraExpressions.add(nc); 158 break; 159 } 160 } 161 } 162 } 163 } 164 } 165 166 return paraExpressions; 167 } 168 169 // 170 // PROTECTED METHODS 171 // 172 protected boolean isValidMatch(Term toMatch, 173 Set<Variable> toMatchVariables, Term possibleMatch, 174 Map<Variable, Term> substitution) { 175 176 if (possibleMatch != null && substitution != null) { 177 // Note: 178 // [Brand 1975] showed that paramodulation into 179 // variables is unnecessary. 180 if (!(possibleMatch instanceof Variable)) { 181 // TODO: Find out whether the following statement from: 182 // http://www.cs.miami.edu/~geoff/Courses/CSC648-07F/Content/Paramodulation.shtml 183 // is actually the case, as it was not positive but 184 // intuitively makes sense: 185 // "Similarly, depending on how paramodulation is used, it is 186 // often unnecessary to paramodulate from variables." 187 // if (!(toMatch instanceof Variable)) { 188 return true; 189 // } 190 } 191 } 192 return false; 193 } 194 }