package aima.test.core.unit.logic.fol; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import java.util.Map; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import aima.core.logic.fol.Unifier; import aima.core.logic.fol.domain.DomainFactory; import aima.core.logic.fol.domain.FOLDomain; import aima.core.logic.fol.parsing.FOLParser; import aima.core.logic.fol.parsing.ast.Constant; import aima.core.logic.fol.parsing.ast.Function; import aima.core.logic.fol.parsing.ast.Predicate; import aima.core.logic.fol.parsing.ast.Sentence; import aima.core.logic.fol.parsing.ast.Term; import aima.core.logic.fol.parsing.ast.TermEquality; import aima.core.logic.fol.parsing.ast.Variable; /** * @author Ravi Mohan * @author Ciaran O'Reilly */ public class UnifierTest { private FOLParser parser; private Unifier unifier; private Map<Variable, Term> theta; @Before public void setUp() { parser = new FOLParser(DomainFactory.knowsDomain()); unifier = new Unifier(); theta = new Hashtable<Variable, Term>(); } @Test public void testFailureIfThetaisNull() { Variable var = new Variable("x"); Sentence sentence = parser.parse("Knows(x)"); theta = null; Map<Variable, Term> result = unifier.unify(var, sentence, theta); Assert.assertNull(result); } @Test public void testUnificationFailure() { Variable var = new Variable("x"); Sentence sentence = parser.parse("Knows(y)"); theta = null; Map<Variable, Term> result = unifier.unify(var, sentence, theta); Assert.assertNull(result); } @Test public void testThetaPassedBackIfXEqualsYBothVariables() { Variable var1 = new Variable("x"); Variable var2 = new Variable("x"); theta.put(new Variable("dummy"), new Variable("dummy")); Map<Variable, Term> result = unifier.unify(var1, var2, theta); Assert.assertEquals(theta, result); Assert.assertEquals(1, theta.keySet().size()); Assert.assertTrue(theta.containsKey(new Variable("dummy"))); } @Test public void testVariableEqualsConstant() { Variable var1 = new Variable("x"); Constant constant = new Constant("John"); Map<Variable, Term> result = unifier.unify(var1, constant, theta); Assert.assertEquals(theta, result); Assert.assertEquals(1, theta.keySet().size()); Assert.assertTrue(theta.keySet().contains(var1)); Assert.assertEquals(constant, theta.get(var1)); } @Test public void testSimpleVariableUnification() { Variable var1 = new Variable("x"); List<Term> terms1 = new ArrayList<Term>(); terms1.add(var1); Predicate p1 = new Predicate("King", terms1); // King(x) List<Term> terms2 = new ArrayList<Term>(); terms2.add(new Constant("John")); Predicate p2 = new Predicate("King", terms2); // King(John) Map<Variable, Term> result = unifier.unify(p1, p2, theta); Assert.assertEquals(theta, result); Assert.assertEquals(1, theta.keySet().size()); Assert.assertTrue(theta.keySet().contains(new Variable("x"))); // x = Assert.assertEquals(new Constant("John"), theta.get(var1)); // John } @Test public void testKnows1() { Sentence query = parser.parse("Knows(John,x)"); Sentence johnKnowsJane = parser.parse("Knows(John,Jane)"); Map<Variable, Term> result = unifier.unify(query, johnKnowsJane, theta); Assert.assertEquals(theta, result); Assert.assertTrue(theta.keySet().contains(new Variable("x"))); // x = Assert.assertEquals(new Constant("Jane"), theta.get(new Variable("x"))); // Jane } @Test public void testKnows2() { Sentence query = parser.parse("Knows(John,x)"); Sentence johnKnowsJane = parser.parse("Knows(y,Bill)"); Map<Variable, Term> result = unifier.unify(query, johnKnowsJane, theta); Assert.assertEquals(2, result.size()); Assert.assertEquals(new Constant("Bill"), theta.get(new Variable("x"))); // x // = // Bill Assert.assertEquals(new Constant("John"), theta.get(new Variable("y"))); // y // = // John } @Test public void testKnows3() { Sentence query = parser.parse("Knows(John,x)"); Sentence johnKnowsJane = parser.parse("Knows(y,Mother(y))"); Map<Variable, Term> result = unifier.unify(query, johnKnowsJane, theta); Assert.assertEquals(2, result.size()); List<Term> terms = new ArrayList<Term>(); terms.add(new Constant("John")); Function mother = new Function("Mother", terms); Assert.assertEquals(mother, theta.get(new Variable("x"))); Assert.assertEquals(new Constant("John"), theta.get(new Variable("y"))); } @Test public void testKnows5() { Sentence query = parser.parse("Knows(John,x)"); Sentence johnKnowsJane = parser.parse("Knows(y,z)"); Map<Variable, Term> result = unifier.unify(query, johnKnowsJane, theta); Assert.assertEquals(2, result.size()); Assert.assertEquals(new Variable("z"), theta.get(new Variable("x"))); // x // = // z Assert.assertEquals(new Constant("John"), theta.get(new Variable("y"))); // y // = // John } @Test public void testCascadedOccursCheck() { FOLDomain domain = new FOLDomain(); domain.addPredicate("P"); domain.addFunction("F"); domain.addFunction("SF0"); domain.addFunction("SF1"); FOLParser parser = new FOLParser(domain); Sentence s1 = parser.parse("P(SF1(v2),v2)"); Sentence s2 = parser.parse("P(v3,SF0(v3))"); Map<Variable, Term> result = unifier.unify(s1, s2); Assert.assertNull(result); s1 = parser.parse("P(v1,SF0(v1),SF0(v1),SF0(v1),SF0(v1))"); s2 = parser.parse("P(v2,SF0(v2),v2, v3, v2)"); result = unifier.unify(s1, s2); Assert.assertNull(result); s1 = parser .parse("P(v1, F(v2),F(v2),F(v2),v1, F(F(v1)),F(F(F(v1))),v2)"); s2 = parser .parse("P(F(v3),v4, v5, v6, F(F(v5)),v4, F(v3), F(F(v5)))"); result = unifier.unify(s1, s2); Assert.assertNull(result); } /** * From: TPTP:LCL418-1 Am performing an incorrect unification for: * [is_a_theorem * (equivalent(equivalent(c1744,c1743),equivalent(c1742,c1743))), * is_a_theorem(equivalent(equivalent(c1752,c1751),c1752))] * * which is giving the following substitution: * * subst={c1744=equivalent(c1742,c1743), c1743=c1751, * c1752=equivalent(c1742,c1751)} * * which is incorrect as c1743 in the first function term needs to be c1751 * as this is the second substitution. */ @Test public void testBadCascadeSubstitution_LCL418_1() { FOLDomain domain = new FOLDomain(); domain.addPredicate("ISATHEOREM"); domain.addFunction("EQUIVALENT"); FOLParser parser = new FOLParser(domain); Sentence s1 = parser .parse("ISATHEOREM(EQUIVALENT(EQUIVALENT(c1744,c1743),EQUIVALENT(c1742,c1743)))"); Sentence s2 = parser .parse("ISATHEOREM(EQUIVALENT(EQUIVALENT(c1752,c1751),c1752))"); Map<Variable, Term> result = unifier.unify(s1, s2); Assert.assertEquals( "{c1744=EQUIVALENT(c1742,c1751), c1743=c1751, c1752=EQUIVALENT(c1742,c1751)}", result.toString()); } @Test public void testAdditionalVariableMixtures() { FOLDomain domain = new FOLDomain(); domain.addConstant("A"); domain.addConstant("B"); domain.addFunction("F"); domain.addFunction("G"); domain.addFunction("H"); domain.addPredicate("P"); FOLParser parser = new FOLParser(domain); // Test Cascade Substitutions handled correctly Sentence s1 = parser.parse("P(z, x)"); Sentence s2 = parser.parse("P(x, a)"); Map<Variable, Term> result = unifier.unify(s1, s2); Assert.assertEquals("{z=a, x=a}", result.toString()); s1 = parser.parse("P(x, z)"); s2 = parser.parse("P(a, x)"); result = unifier.unify(s1, s2); Assert.assertEquals("{x=a, z=a}", result.toString()); s1 = parser.parse("P(w, w, w)"); s2 = parser.parse("P(x, y, z)"); result = unifier.unify(s1, s2); Assert.assertEquals("{w=z, x=z, y=z}", result.toString()); s1 = parser.parse("P(x, y, z)"); s2 = parser.parse("P(w, w, w)"); result = unifier.unify(s1, s2); Assert.assertEquals("{x=w, y=w, z=w}", result.toString()); s1 = parser.parse("P(x, B, F(y))"); s2 = parser.parse("P(A, y, F(z))"); result = unifier.unify(s1, s2); Assert.assertEquals("{x=A, y=B, z=B}", result.toString()); s1 = parser.parse("P(F(x,B), G(y), F(z,A))"); s2 = parser.parse("P(y, G(F(G(w),w)), F(w,z))"); result = unifier.unify(s1, s2); Assert.assertNull(result); s1 = parser.parse("P(F(G(A)), x, F(H(z,z)), H(y, G(w)))"); s2 = parser.parse("P(y, G(z), F(v ), H(F(w), x ))"); result = unifier.unify(s1, s2); Assert.assertEquals( "{y=F(G(A)), x=G(G(A)), v=H(G(A),G(A)), w=G(A), z=G(A)}", result.toString()); } @Test public void testTermEquality() { FOLDomain domain = new FOLDomain(); domain.addConstant("A"); domain.addConstant("B"); domain.addFunction("Plus"); FOLParser parser = new FOLParser(domain); TermEquality te1 = (TermEquality) parser.parse("x = x"); TermEquality te2 = (TermEquality) parser.parse("x = x"); // Both term equalities the same, // should unify but no substitutions. Map<Variable, Term> result = unifier.unify(te1, te2); Assert.assertNotNull(result); Assert.assertEquals(0, result.size()); // Different variable names but should unify. te1 = (TermEquality) parser.parse("x1 = x1"); te2 = (TermEquality) parser.parse("x2 = x2"); result = unifier.unify(te1, te2); Assert.assertNotNull(result); Assert.assertEquals(1, result.size()); Assert.assertEquals("{x1=x2}", result.toString()); // Test simple unification with reflexivity axiom te1 = (TermEquality) parser.parse("x1 = x1"); te2 = (TermEquality) parser.parse("Plus(A,B) = Plus(A,B)"); result = unifier.unify(te1, te2); Assert.assertNotNull(result); Assert.assertEquals(1, result.size()); Assert.assertEquals("{x1=Plus(A,B)}", result.toString()); // Test more complex unification with reflexivity axiom te1 = (TermEquality) parser.parse("x1 = x1"); te2 = (TermEquality) parser.parse("Plus(A,B) = Plus(A,z1)"); result = unifier.unify(te1, te2); Assert.assertNotNull(result); Assert.assertEquals(2, result.size()); Assert.assertEquals("{x1=Plus(A,B), z1=B}", result.toString()); // Test reverse of previous unification with reflexivity axiom // Should still be the same. te1 = (TermEquality) parser.parse("x1 = x1"); te2 = (TermEquality) parser.parse("Plus(A,z1) = Plus(A,B)"); result = unifier.unify(te1, te2); Assert.assertNotNull(result); Assert.assertEquals(2, result.size()); Assert.assertEquals("{x1=Plus(A,B), z1=B}", result.toString()); // Test with nested terms te1 = (TermEquality) parser .parse("Plus(Plus(Plus(A,B),B, A)) = Plus(Plus(Plus(A,B),B, A))"); te2 = (TermEquality) parser .parse("Plus(Plus(Plus(A,B),B, A)) = Plus(Plus(Plus(A,B),B, A))"); result = unifier.unify(te1, te2); Assert.assertNotNull(result); Assert.assertEquals(0, result.size()); // Simple term equality unification fails te1 = (TermEquality) parser.parse("Plus(A,B) = Plus(B,A)"); te2 = (TermEquality) parser.parse("Plus(A,B) = Plus(A,B)"); result = unifier.unify(te1, te2); Assert.assertNull(result); } @Test public void testNOTSentence() { FOLDomain domain = new FOLDomain(); domain.addConstant("A"); domain.addConstant("B"); domain.addConstant("C"); domain.addFunction("Plus"); domain.addPredicate("P"); FOLParser parser = new FOLParser(domain); Sentence s1 = parser.parse("NOT(P(A))"); Sentence s2 = parser.parse("NOT(P(A))"); Map<Variable, Term> result = unifier.unify(s1, s2); Assert.assertNotNull(result); Assert.assertEquals(0, result.size()); s1 = parser.parse("NOT(P(A))"); s2 = parser.parse("NOT(P(B))"); result = unifier.unify(s1, s2); Assert.assertNull(result); s1 = parser.parse("NOT(P(A))"); s2 = parser.parse("NOT(P(x))"); result = unifier.unify(s1, s2); Assert.assertNotNull(result); Assert.assertEquals(1, result.size()); Assert.assertEquals(new Constant("A"), result.get(new Variable("x"))); } @Test public void testConnectedSentence() { FOLDomain domain = new FOLDomain(); domain.addConstant("A"); domain.addConstant("B"); domain.addConstant("C"); domain.addFunction("Plus"); domain.addPredicate("P"); FOLParser parser = new FOLParser(domain); Sentence s1 = parser.parse("(P(A) AND P(B))"); Sentence s2 = parser.parse("(P(A) AND P(B))"); Map<Variable, Term> result = unifier.unify(s1, s2); Assert.assertNotNull(result); Assert.assertEquals(0, result.size()); s1 = parser.parse("(P(A) AND P(B))"); s2 = parser.parse("(P(A) AND P(C))"); result = unifier.unify(s1, s2); Assert.assertNull(result); s1 = parser.parse("(P(A) AND P(B))"); s2 = parser.parse("(P(A) AND P(x))"); result = unifier.unify(s1, s2); Assert.assertNotNull(result); Assert.assertEquals(1, result.size()); Assert.assertEquals(new Constant("B"), result.get(new Variable("x"))); s1 = parser.parse("(P(A) OR P(B))"); s2 = parser.parse("(P(A) OR P(B))"); result = unifier.unify(s1, s2); Assert.assertNotNull(result); Assert.assertEquals(0, result.size()); s1 = parser.parse("(P(A) OR P(B))"); s2 = parser.parse("(P(A) OR P(C))"); result = unifier.unify(s1, s2); Assert.assertNull(result); s1 = parser.parse("(P(A) OR P(B))"); s2 = parser.parse("(P(A) OR P(x))"); result = unifier.unify(s1, s2); Assert.assertNotNull(result); Assert.assertEquals(1, result.size()); Assert.assertEquals(new Constant("B"), result.get(new Variable("x"))); s1 = parser.parse("(P(A) => P(B))"); s2 = parser.parse("(P(A) => P(B))"); result = unifier.unify(s1, s2); Assert.assertNotNull(result); Assert.assertEquals(0, result.size()); s1 = parser.parse("(P(A) => P(B))"); s2 = parser.parse("(P(A) => P(C))"); result = unifier.unify(s1, s2); Assert.assertNull(result); s1 = parser.parse("(P(A) => P(B))"); s2 = parser.parse("(P(A) => P(x))"); result = unifier.unify(s1, s2); Assert.assertNotNull(result); Assert.assertEquals(1, result.size()); Assert.assertEquals(new Constant("B"), result.get(new Variable("x"))); s1 = parser.parse("(P(A) <=> P(B))"); s2 = parser.parse("(P(A) <=> P(B))"); result = unifier.unify(s1, s2); Assert.assertNotNull(result); Assert.assertEquals(0, result.size()); s1 = parser.parse("(P(A) <=> P(B))"); s2 = parser.parse("(P(A) <=> P(C))"); result = unifier.unify(s1, s2); Assert.assertNull(result); s1 = parser.parse("(P(A) <=> P(B))"); s2 = parser.parse("(P(A) <=> P(x))"); result = unifier.unify(s1, s2); Assert.assertNotNull(result); Assert.assertEquals(1, result.size()); Assert.assertEquals(new Constant("B"), result.get(new Variable("x"))); s1 = parser.parse("((P(A) AND P(B)) OR (P(C) => (P(A) <=> P(C))))"); s2 = parser.parse("((P(A) AND P(B)) OR (P(C) => (P(A) <=> P(C))))"); result = unifier.unify(s1, s2); Assert.assertNotNull(result); Assert.assertEquals(0, result.size()); s1 = parser.parse("((P(A) AND P(B)) OR (P(C) => (P(A) <=> P(C))))"); s2 = parser.parse("((P(A) AND P(B)) OR (P(C) => (P(A) <=> P(A))))"); result = unifier.unify(s1, s2); Assert.assertNull(result); s1 = parser.parse("((P(A) AND P(B)) OR (P(C) => (P(A) <=> P(C))))"); s2 = parser.parse("((P(A) AND P(B)) OR (P(C) => (P(A) <=> P(x))))"); result = unifier.unify(s1, s2); Assert.assertNotNull(result); Assert.assertEquals(1, result.size()); Assert.assertEquals(new Constant("C"), result.get(new Variable("x"))); } @Test public void testQuantifiedSentence() { FOLDomain domain = new FOLDomain(); domain.addConstant("A"); domain.addConstant("B"); domain.addConstant("C"); domain.addFunction("Plus"); domain.addPredicate("P"); FOLParser parser = new FOLParser(domain); Sentence s1 = parser .parse("FORALL x,y ((P(x) AND P(A)) OR (P(A) => P(y)))"); Sentence s2 = parser .parse("FORALL x,y ((P(x) AND P(A)) OR (P(A) => P(y)))"); Map<Variable, Term> result = unifier.unify(s1, s2); Assert.assertNotNull(result); Assert.assertEquals(0, result.size()); s1 = parser.parse("FORALL x,y ((P(x) AND P(A)) OR (P(A) => P(y)))"); s2 = parser.parse("FORALL x ((P(x) AND P(A)) OR (P(A) => P(y)))"); result = unifier.unify(s1, s2); Assert.assertNull(result); s1 = parser.parse("FORALL x,y ((P(x) AND P(A)) OR (P(A) => P(y)))"); s2 = parser.parse("FORALL x,y ((P(x) AND P(A)) OR (P(B) => P(y)))"); result = unifier.unify(s1, s2); Assert.assertNull(result); s1 = parser.parse("FORALL x,y ((P(x) AND P(A)) OR (P(A) => P(y)))"); s2 = parser.parse("FORALL x,y ((P(A) AND P(A)) OR (P(A) => P(y)))"); result = unifier.unify(s1, s2); Assert.assertNotNull(result); Assert.assertEquals(1, result.size()); Assert.assertEquals(new Constant("A"), result.get(new Variable("x"))); // s1 = parser.parse("EXISTS x,y ((P(x) AND P(A)) OR (P(A) => P(y)))"); s2 = parser.parse("EXISTS x,y ((P(x) AND P(A)) OR (P(A) => P(y)))"); result = unifier.unify(s1, s2); Assert.assertNotNull(result); Assert.assertEquals(0, result.size()); s1 = parser.parse("EXISTS x,y ((P(x) AND P(A)) OR (P(A) => P(y)))"); s2 = parser.parse("EXISTS x ((P(x) AND P(A)) OR (P(A) => P(y)))"); result = unifier.unify(s1, s2); Assert.assertNull(result); s1 = parser.parse("EXISTS x,y ((P(x) AND P(A)) OR (P(A) => P(y)))"); s2 = parser.parse("EXISTS x,y ((P(x) AND P(A)) OR (P(B) => P(y)))"); result = unifier.unify(s1, s2); Assert.assertNull(result); s1 = parser.parse("EXISTS x,y ((P(x) AND P(A)) OR (P(A) => P(y)))"); s2 = parser.parse("EXISTS x,y ((P(A) AND P(A)) OR (P(A) => P(y)))"); result = unifier.unify(s1, s2); Assert.assertNotNull(result); Assert.assertEquals(1, result.size()); Assert.assertEquals(new Constant("A"), result.get(new Variable("x"))); } }