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 }