001    package aima.test.logictest.foltest;
002    
003    import java.util.ArrayList;
004    import java.util.Hashtable;
005    import java.util.List;
006    import java.util.Map;
007    
008    import junit.framework.TestCase;
009    import aima.logic.fol.Unifier;
010    import aima.logic.fol.domain.DomainFactory;
011    import aima.logic.fol.domain.FOLDomain;
012    import aima.logic.fol.parsing.FOLParser;
013    import aima.logic.fol.parsing.ast.Constant;
014    import aima.logic.fol.parsing.ast.Function;
015    import aima.logic.fol.parsing.ast.Predicate;
016    import aima.logic.fol.parsing.ast.Sentence;
017    import aima.logic.fol.parsing.ast.Term;
018    import aima.logic.fol.parsing.ast.TermEquality;
019    import aima.logic.fol.parsing.ast.Variable;
020    
021    /**
022     * @author Ravi Mohan
023     * @author Ciaran O'Reilly
024     */
025    public class UnifierTest extends TestCase {
026    
027            private FOLParser parser;
028            private Unifier unifier;
029            private Map<Variable, Term> theta;
030    
031            @Override
032            public void setUp() {
033                    parser = new FOLParser(DomainFactory.knowsDomain());
034                    unifier = new Unifier();
035                    theta = new Hashtable<Variable, Term>();
036            }
037    
038            public void testFailureIfThetaisNull() {
039                    Variable var = new Variable("x");
040                    Sentence sentence = parser.parse("Knows(x)");
041                    theta = null;
042                    Map<Variable, Term> result = unifier.unify(var, sentence, theta);
043                    assertNull(result);
044            }
045    
046            public void testUnificationFailure() {
047                    Variable var = new Variable("x");
048                    Sentence sentence = parser.parse("Knows(y)");
049                    theta = null;
050                    Map<Variable, Term> result = unifier.unify(var, sentence, theta);
051                    assertNull(result);
052            }
053    
054            public void testThetaPassedBackIfXEqualsYBothVariables() {
055                    Variable var1 = new Variable("x");
056                    Variable var2 = new Variable("x");
057    
058                    theta.put(new Variable("dummy"), new Variable("dummy"));
059                    Map<Variable, Term> result = unifier.unify(var1, var2, theta);
060                    assertEquals(theta, result);
061                    assertEquals(1, theta.keySet().size());
062                    assertTrue(theta.containsKey(new Variable("dummy")));
063            }
064    
065            public void testVariableEqualsConstant() {
066                    Variable var1 = new Variable("x");
067                    Constant constant = new Constant("John");
068    
069                    Map<Variable, Term> result = unifier.unify(var1, constant, theta);
070                    assertEquals(theta, result);
071                    assertEquals(1, theta.keySet().size());
072                    assertTrue(theta.keySet().contains(var1));
073                    assertEquals(constant, theta.get(var1));
074            }
075    
076            public void testSimpleVariableUnification() {
077                    Variable var1 = new Variable("x");
078                    List<Term> terms1 = new ArrayList<Term>();
079                    terms1.add(var1);
080                    Predicate p1 = new Predicate("King", terms1); // King(x)
081    
082                    List<Term> terms2 = new ArrayList<Term>();
083                    terms2.add(new Constant("John"));
084                    Predicate p2 = new Predicate("King", terms2); // King(John)
085    
086                    Map<Variable, Term> result = unifier.unify(p1, p2, theta);
087                    assertEquals(theta, result);
088                    assertEquals(1, theta.keySet().size());
089                    assertTrue(theta.keySet().contains(new Variable("x"))); // x =
090                    assertEquals(new Constant("John"), theta.get(var1)); // John
091            }
092    
093            public void testKnows1() {
094                    Sentence query = parser.parse("Knows(John,x)");
095                    Sentence johnKnowsJane = parser.parse("Knows(John,Jane)");
096                    Map<Variable, Term> result = unifier.unify(query, johnKnowsJane, theta);
097                    assertEquals(theta, result);
098                    assertTrue(theta.keySet().contains(new Variable("x"))); // x =
099                    assertEquals(new Constant("Jane"), theta.get(new Variable("x"))); // Jane
100    
101            }
102    
103            public void testKnows2() {
104                    Sentence query = parser.parse("Knows(John,x)");
105                    Sentence johnKnowsJane = parser.parse("Knows(y,Bill)");
106                    Map<Variable, Term> result = unifier.unify(query, johnKnowsJane, theta);
107    
108                    assertEquals(2, result.size());
109    
110                    assertEquals(new Constant("Bill"), theta.get(new Variable("x"))); // x =
111                    // Bill
112                    assertEquals(new Constant("John"), theta.get(new Variable("y"))); // y =
113                    // John
114            }
115    
116            public void testKnows3() {
117                    Sentence query = parser.parse("Knows(John,x)");
118                    Sentence johnKnowsJane = parser.parse("Knows(y,Mother(y))");
119                    Map<Variable, Term> result = unifier.unify(query, johnKnowsJane, theta);
120    
121                    assertEquals(2, result.size());
122    
123                    List<Term> terms = new ArrayList<Term>();
124                    terms.add(new Constant("John"));
125                    Function mother = new Function("Mother", terms);
126                    assertEquals(mother, theta.get(new Variable("x")));
127                    assertEquals(new Constant("John"), theta.get(new Variable("y")));
128            }
129    
130            public void testKnows5() {
131                    Sentence query = parser.parse("Knows(John,x)");
132                    Sentence johnKnowsJane = parser.parse("Knows(y,z)");
133                    Map<Variable, Term> result = unifier.unify(query, johnKnowsJane, theta);
134    
135                    assertEquals(2, result.size());
136    
137                    assertEquals(new Variable("z"), theta.get(new Variable("x"))); // x = z
138                    assertEquals(new Constant("John"), theta.get(new Variable("y"))); // y =
139                    // John
140            }
141    
142            public void testCascadedOccursCheck() {
143                    FOLDomain domain = new FOLDomain();
144                    domain.addPredicate("P");
145                    domain.addFunction("F");
146                    domain.addFunction("SF0");
147                    domain.addFunction("SF1");
148                    FOLParser parser = new FOLParser(domain);
149    
150                    Sentence s1 = parser.parse("P(SF1(v2),v2)");
151                    Sentence s2 = parser.parse("P(v3,SF0(v3))");
152                    Map<Variable, Term> result = unifier.unify(s1, s2);
153    
154                    assertNull(result);
155                    
156                    s1 = parser.parse("P(v1,SF0(v1),SF0(v1),SF0(v1),SF0(v1))");
157                    s2 = parser.parse("P(v2,SF0(v2),v2,     v3,     v2)");
158                    result = unifier.unify(s1, s2);
159    
160                    assertNull(result);
161                    
162                    s1 = parser.parse("P(v1,   F(v2),F(v2),F(v2),v1,      F(F(v1)),F(F(F(v1))),v2)");
163                    s2 = parser.parse("P(F(v3),v4,   v5,   v6,   F(F(v5)),v4,      F(v3),      F(F(v5)))");
164                    result = unifier.unify(s1, s2);
165    
166                    assertNull(result);
167            }
168    
169            public void testAdditionalVariableMixtures() {
170                    FOLDomain domain = new FOLDomain();
171                    domain.addConstant("A");
172                    domain.addConstant("B");
173                    domain.addFunction("F");
174                    domain.addFunction("G");
175                    domain.addFunction("H");
176                    domain.addPredicate("P");
177    
178                    FOLParser parser = new FOLParser(domain);
179    
180                    // Test Cascade Substitutions handled correctly
181                    Sentence s1 = parser.parse("P(z, x)");
182                    Sentence s2 = parser.parse("P(x, a)");
183                    Map<Variable, Term> result = unifier.unify(s1, s2);
184    
185                    assertEquals("{z=a, x=a}", result.toString());
186    
187                    s1 = parser.parse("P(x, z)");
188                    s2 = parser.parse("P(a, x)");
189                    result = unifier.unify(s1, s2);
190    
191                    assertEquals("{x=a, z=a}", result.toString());
192    
193                    s1 = parser.parse("P(w, w, w)");
194                    s2 = parser.parse("P(x, y, z)");
195                    result = unifier.unify(s1, s2);
196    
197                    assertEquals("{w=z, x=z, y=z}", result.toString());
198    
199                    s1 = parser.parse("P(x, y, z)");
200                    s2 = parser.parse("P(w, w, w)");
201                    result = unifier.unify(s1, s2);
202    
203                    assertEquals("{x=w, y=w, z=w}", result.toString());
204    
205                    s1 = parser.parse("P(x, B, F(y))");
206                    s2 = parser.parse("P(A, y, F(z))");
207                    result = unifier.unify(s1, s2);
208    
209                    assertEquals("{x=A, y=B, z=B}", result.toString());
210    
211                    s1 = parser.parse("P(F(x,B), G(y),         F(z,A))");
212                    s2 = parser.parse("P(y,      G(F(G(w),w)), F(w,z))");
213                    result = unifier.unify(s1, s2);
214    
215                    assertNull(result);
216    
217                    s1 = parser.parse("P(F(G(A)), x,    F(H(z,z)), H(y,    G(w)))");
218                    s2 = parser.parse("P(y,       G(z), F(v     ), H(F(w), x   ))");
219                    result = unifier.unify(s1, s2);
220    
221                    assertEquals("{y=F(G(A)), x=G(G(A)), v=H(G(A),G(A)), w=G(A), z=G(A)}",
222                                    result.toString());
223            }
224    
225            public void testTermEquality() {
226                    FOLDomain domain = new FOLDomain();
227                    domain.addConstant("A");
228                    domain.addConstant("B");
229                    domain.addFunction("Plus");
230    
231                    FOLParser parser = new FOLParser(domain);
232    
233                    TermEquality te1 = (TermEquality) parser.parse("x = x");
234                    TermEquality te2 = (TermEquality) parser.parse("x = x");
235    
236                    // Both term equalities the same,
237                    // should unify but no substitutions.
238                    Map<Variable, Term> result = unifier.unify(te1, te2);
239    
240                    assertNotNull(result);
241                    assertEquals(0, result.size());
242    
243                    // Different variable names but should unify.
244                    te1 = (TermEquality) parser.parse("x1 = x1");
245                    te2 = (TermEquality) parser.parse("x2 = x2");
246    
247                    result = unifier.unify(te1, te2);
248    
249                    assertNotNull(result);
250                    assertEquals(1, result.size());
251                    assertEquals("{x1=x2}", result.toString());
252    
253                    // Test simple unification with reflexivity axiom
254                    te1 = (TermEquality) parser.parse("x1 = x1");
255                    te2 = (TermEquality) parser.parse("Plus(A,B) = Plus(A,B)");
256    
257                    result = unifier.unify(te1, te2);
258    
259                    assertNotNull(result);
260    
261                    assertEquals(1, result.size());
262                    assertEquals("{x1=Plus(A,B)}", result.toString());
263    
264                    // Test more complex unification with reflexivity axiom
265                    te1 = (TermEquality) parser.parse("x1 = x1");
266                    te2 = (TermEquality) parser.parse("Plus(A,B) = Plus(A,z1)");
267    
268                    result = unifier.unify(te1, te2);
269    
270                    assertNotNull(result);
271    
272                    assertEquals(2, result.size());
273                    assertEquals("{x1=Plus(A,B), z1=B}", result.toString());
274    
275                    // Test reverse of previous unification with reflexivity axiom
276                    // Should still be the same.
277                    te1 = (TermEquality) parser.parse("x1 = x1");
278                    te2 = (TermEquality) parser.parse("Plus(A,z1) = Plus(A,B)");
279    
280                    result = unifier.unify(te1, te2);
281    
282                    assertNotNull(result);
283    
284                    assertEquals(2, result.size());
285                    assertEquals("{x1=Plus(A,B), z1=B}", result.toString());
286    
287                    // Test with nested terms
288                    te1 = (TermEquality) parser
289                                    .parse("Plus(Plus(Plus(A,B),B, A)) = Plus(Plus(Plus(A,B),B, A))");
290                    te2 = (TermEquality) parser
291                                    .parse("Plus(Plus(Plus(A,B),B, A)) = Plus(Plus(Plus(A,B),B, A))");
292    
293                    result = unifier.unify(te1, te2);
294    
295                    assertNotNull(result);
296    
297                    assertEquals(0, result.size());
298    
299                    // Simple term equality unification fails
300                    te1 = (TermEquality) parser.parse("Plus(A,B) = Plus(B,A)");
301                    te2 = (TermEquality) parser.parse("Plus(A,B) = Plus(A,B)");
302    
303                    result = unifier.unify(te1, te2);
304    
305                    assertNull(result);
306            }
307    
308            public void testNOTSentence() {
309                    FOLDomain domain = new FOLDomain();
310                    domain.addConstant("A");
311                    domain.addConstant("B");
312                    domain.addConstant("C");
313                    domain.addFunction("Plus");
314                    domain.addPredicate("P");
315    
316                    FOLParser parser = new FOLParser(domain);
317    
318                    Sentence s1 = parser.parse("NOT(P(A))");
319                    Sentence s2 = parser.parse("NOT(P(A))");
320    
321                    Map<Variable, Term> result = unifier.unify(s1, s2);
322    
323                    assertNotNull(result);
324                    assertEquals(0, result.size());
325    
326                    s1 = parser.parse("NOT(P(A))");
327                    s2 = parser.parse("NOT(P(B))");
328    
329                    result = unifier.unify(s1, s2);
330    
331                    assertNull(result);
332    
333                    s1 = parser.parse("NOT(P(A))");
334                    s2 = parser.parse("NOT(P(x))");
335    
336                    result = unifier.unify(s1, s2);
337    
338                    assertNotNull(result);
339                    assertEquals(1, result.size());
340                    assertEquals(new Constant("A"), result.get(new Variable("x")));
341            }
342    
343            public void testConnectedSentence() {
344                    FOLDomain domain = new FOLDomain();
345                    domain.addConstant("A");
346                    domain.addConstant("B");
347                    domain.addConstant("C");
348                    domain.addFunction("Plus");
349                    domain.addPredicate("P");
350    
351                    FOLParser parser = new FOLParser(domain);
352    
353                    Sentence s1 = parser.parse("(P(A) AND P(B))");
354                    Sentence s2 = parser.parse("(P(A) AND P(B))");
355    
356                    Map<Variable, Term> result = unifier.unify(s1, s2);
357    
358                    assertNotNull(result);
359                    assertEquals(0, result.size());
360    
361                    s1 = parser.parse("(P(A) AND P(B))");
362                    s2 = parser.parse("(P(A) AND P(C))");
363    
364                    result = unifier.unify(s1, s2);
365    
366                    assertNull(result);
367    
368                    s1 = parser.parse("(P(A) AND P(B))");
369                    s2 = parser.parse("(P(A) AND P(x))");
370    
371                    result = unifier.unify(s1, s2);
372    
373                    assertNotNull(result);
374                    assertEquals(1, result.size());
375                    assertEquals(new Constant("B"), result.get(new Variable("x")));
376    
377                    s1 = parser.parse("(P(A) OR P(B))");
378                    s2 = parser.parse("(P(A) OR P(B))");
379    
380                    result = unifier.unify(s1, s2);
381    
382                    assertNotNull(result);
383                    assertEquals(0, result.size());
384    
385                    s1 = parser.parse("(P(A) OR P(B))");
386                    s2 = parser.parse("(P(A) OR P(C))");
387    
388                    result = unifier.unify(s1, s2);
389    
390                    assertNull(result);
391    
392                    s1 = parser.parse("(P(A) OR P(B))");
393                    s2 = parser.parse("(P(A) OR P(x))");
394    
395                    result = unifier.unify(s1, s2);
396    
397                    assertNotNull(result);
398                    assertEquals(1, result.size());
399                    assertEquals(new Constant("B"), result.get(new Variable("x")));
400    
401                    s1 = parser.parse("(P(A) => P(B))");
402                    s2 = parser.parse("(P(A) => P(B))");
403    
404                    result = unifier.unify(s1, s2);
405    
406                    assertNotNull(result);
407                    assertEquals(0, result.size());
408    
409                    s1 = parser.parse("(P(A) => P(B))");
410                    s2 = parser.parse("(P(A) => P(C))");
411    
412                    result = unifier.unify(s1, s2);
413    
414                    assertNull(result);
415    
416                    s1 = parser.parse("(P(A) => P(B))");
417                    s2 = parser.parse("(P(A) => P(x))");
418    
419                    result = unifier.unify(s1, s2);
420    
421                    assertNotNull(result);
422                    assertEquals(1, result.size());
423                    assertEquals(new Constant("B"), result.get(new Variable("x")));
424    
425                    s1 = parser.parse("(P(A) <=> P(B))");
426                    s2 = parser.parse("(P(A) <=> P(B))");
427    
428                    result = unifier.unify(s1, s2);
429    
430                    assertNotNull(result);
431                    assertEquals(0, result.size());
432    
433                    s1 = parser.parse("(P(A) <=> P(B))");
434                    s2 = parser.parse("(P(A) <=> P(C))");
435    
436                    result = unifier.unify(s1, s2);
437    
438                    assertNull(result);
439    
440                    s1 = parser.parse("(P(A) <=> P(B))");
441                    s2 = parser.parse("(P(A) <=> P(x))");
442    
443                    result = unifier.unify(s1, s2);
444    
445                    assertNotNull(result);
446                    assertEquals(1, result.size());
447                    assertEquals(new Constant("B"), result.get(new Variable("x")));
448    
449                    s1 = parser.parse("((P(A) AND P(B)) OR (P(C) => (P(A) <=> P(C))))");
450                    s2 = parser.parse("((P(A) AND P(B)) OR (P(C) => (P(A) <=> P(C))))");
451    
452                    result = unifier.unify(s1, s2);
453    
454                    assertNotNull(result);
455                    assertEquals(0, result.size());
456    
457                    s1 = parser.parse("((P(A) AND P(B)) OR (P(C) => (P(A) <=> P(C))))");
458                    s2 = parser.parse("((P(A) AND P(B)) OR (P(C) => (P(A) <=> P(A))))");
459    
460                    result = unifier.unify(s1, s2);
461    
462                    assertNull(result);
463    
464                    s1 = parser.parse("((P(A) AND P(B)) OR (P(C) => (P(A) <=> P(C))))");
465                    s2 = parser.parse("((P(A) AND P(B)) OR (P(C) => (P(A) <=> P(x))))");
466    
467                    result = unifier.unify(s1, s2);
468    
469                    assertNotNull(result);
470                    assertEquals(1, result.size());
471                    assertEquals(new Constant("C"), result.get(new Variable("x")));
472            }
473    
474            public void testQuantifiedSentence() {
475                    FOLDomain domain = new FOLDomain();
476                    domain.addConstant("A");
477                    domain.addConstant("B");
478                    domain.addConstant("C");
479                    domain.addFunction("Plus");
480                    domain.addPredicate("P");
481    
482                    FOLParser parser = new FOLParser(domain);
483    
484                    Sentence s1 = parser
485                                    .parse("FORALL x,y ((P(x) AND P(A)) OR (P(A) => P(y)))");
486                    Sentence s2 = parser
487                                    .parse("FORALL x,y ((P(x) AND P(A)) OR (P(A) => P(y)))");
488    
489                    Map<Variable, Term> result = unifier.unify(s1, s2);
490    
491                    assertNotNull(result);
492                    assertEquals(0, result.size());
493    
494                    s1 = parser.parse("FORALL x,y ((P(x) AND P(A)) OR (P(A) => P(y)))");
495                    s2 = parser.parse("FORALL x   ((P(x) AND P(A)) OR (P(A) => P(y)))");
496    
497                    result = unifier.unify(s1, s2);
498    
499                    assertNull(result);
500    
501                    s1 = parser.parse("FORALL x,y ((P(x) AND P(A)) OR (P(A) => P(y)))");
502                    s2 = parser.parse("FORALL x,y ((P(x) AND P(A)) OR (P(B) => P(y)))");
503    
504                    result = unifier.unify(s1, s2);
505    
506                    assertNull(result);
507    
508                    s1 = parser.parse("FORALL x,y ((P(x) AND P(A)) OR (P(A) => P(y)))");
509                    s2 = parser.parse("FORALL x,y ((P(A) AND P(A)) OR (P(A) => P(y)))");
510    
511                    result = unifier.unify(s1, s2);
512    
513                    assertNotNull(result);
514                    assertEquals(1, result.size());
515                    assertEquals(new Constant("A"), result.get(new Variable("x")));
516    
517                    //
518                    s1 = parser.parse("EXISTS x,y ((P(x) AND P(A)) OR (P(A) => P(y)))");
519                    s2 = parser.parse("EXISTS x,y ((P(x) AND P(A)) OR (P(A) => P(y)))");
520    
521                    result = unifier.unify(s1, s2);
522    
523                    assertNotNull(result);
524                    assertEquals(0, result.size());
525    
526                    s1 = parser.parse("EXISTS x,y ((P(x) AND P(A)) OR (P(A) => P(y)))");
527                    s2 = parser.parse("EXISTS x   ((P(x) AND P(A)) OR (P(A) => P(y)))");
528    
529                    result = unifier.unify(s1, s2);
530    
531                    assertNull(result);
532    
533                    s1 = parser.parse("EXISTS x,y ((P(x) AND P(A)) OR (P(A) => P(y)))");
534                    s2 = parser.parse("EXISTS x,y ((P(x) AND P(A)) OR (P(B) => P(y)))");
535    
536                    result = unifier.unify(s1, s2);
537    
538                    assertNull(result);
539    
540                    s1 = parser.parse("EXISTS x,y ((P(x) AND P(A)) OR (P(A) => P(y)))");
541                    s2 = parser.parse("EXISTS x,y ((P(A) AND P(A)) OR (P(A) => P(y)))");
542    
543                    result = unifier.unify(s1, s2);
544    
545                    assertNotNull(result);
546                    assertEquals(1, result.size());
547                    assertEquals(new Constant("A"), result.get(new Variable("x")));
548            }
549    }