001 package aima.test.logictest.foltest; 002 003 import junit.framework.TestCase; 004 import aima.logic.fol.CNFConverter; 005 import aima.logic.fol.domain.DomainFactory; 006 import aima.logic.fol.domain.FOLDomain; 007 import aima.logic.fol.kb.data.CNF; 008 import aima.logic.fol.parsing.FOLParser; 009 import aima.logic.fol.parsing.ast.Sentence; 010 011 /** 012 * @author Ciaran O'Reilly 013 * 014 */ 015 public class CNFConverterTest extends TestCase { 016 017 public void testExamplePg295() { 018 FOLDomain domain = DomainFactory.weaponsDomain(); 019 FOLParser parser = new FOLParser(domain); 020 021 Sentence origSentence = parser 022 .parse("FORALL x ((((American(x) AND Weapon(y)) AND Sells(x, y, z)) AND Hostile(z)) => Criminal(x))"); 023 024 CNFConverter cnfConv = new CNFConverter(parser); 025 026 CNF cnf = cnfConv.convertToCNF(origSentence); 027 028 assertEquals( 029 "[~American(x), ~Hostile(z), ~Sells(x,y,z), ~Weapon(y), Criminal(x)]", 030 cnf.toString()); 031 } 032 033 public void testExamplePg296() { 034 FOLDomain domain = DomainFactory.lovesAnimalDomain(); 035 FOLParser parser = new FOLParser(domain); 036 037 Sentence origSentence = parser 038 .parse("FORALL x (FORALL y (Animal(y) => Loves(x, y)) => EXISTS y Loves(y, x))"); 039 040 CNFConverter cnfConv = new CNFConverter(parser); 041 042 CNF cnf = cnfConv.convertToCNF(origSentence); 043 044 assertEquals( 045 "[Animal(SF0(x)), Loves(SF1(x),x)],[~Loves(x,SF0(x)), Loves(SF1(x),x)]", 046 cnf.toString()); 047 } 048 049 public void testExamplesPg299() { 050 FOLDomain domain = DomainFactory.lovesAnimalDomain(); 051 FOLParser parser = new FOLParser(domain); 052 053 // FOL A. 054 Sentence origSentence = parser 055 .parse("FORALL x (FORALL y (Animal(y) => Loves(x, y)) => EXISTS y Loves(y, x))"); 056 057 CNFConverter cnfConv = new CNFConverter(parser); 058 059 CNF cnf = cnfConv.convertToCNF(origSentence); 060 061 // CNF A1. and A2. 062 assertEquals( 063 "[Animal(SF0(x)), Loves(SF1(x),x)],[~Loves(x,SF0(x)), Loves(SF1(x),x)]", 064 cnf.toString()); 065 066 // FOL B. 067 origSentence = parser 068 .parse("FORALL x (EXISTS y (Animal(y) AND Kills(x, y)) => FORALL z NOT(Loves(z, x)))"); 069 070 cnf = cnfConv.convertToCNF(origSentence); 071 072 // CNF B. 073 assertEquals( 074 "[~Animal(y), ~Kills(x,y), ~Loves(z,x)]", 075 cnf.toString()); 076 077 // FOL C. 078 origSentence = parser.parse("FORALL x (Animal(x) => Loves(Jack, x))"); 079 080 cnf = cnfConv.convertToCNF(origSentence); 081 082 // CNF C. 083 assertEquals("[~Animal(x), Loves(Jack,x)]", cnf 084 .toString()); 085 086 // FOL D. 087 origSentence = parser 088 .parse("(Kills(Jack, Tuna) OR Kills(Curiosity, Tuna))"); 089 090 cnf = cnfConv.convertToCNF(origSentence); 091 092 // CNF D. 093 assertEquals("[Kills(Curiosity,Tuna), Kills(Jack,Tuna)]", cnf 094 .toString()); 095 096 // FOL E. 097 origSentence = parser.parse("Cat(Tuna)"); 098 099 cnf = cnfConv.convertToCNF(origSentence); 100 101 // CNF E. 102 assertEquals("[Cat(Tuna)]", cnf.toString()); 103 104 // FOL F. 105 origSentence = parser.parse("FORALL x (Cat(x) => Animal(x))"); 106 107 cnf = cnfConv.convertToCNF(origSentence); 108 109 // CNF F. 110 assertEquals("[~Cat(x), Animal(x)]", cnf.toString()); 111 112 // FOL G. 113 origSentence = parser.parse("NOT(Kills(Curiosity, Tuna))"); 114 115 cnf = cnfConv.convertToCNF(origSentence); 116 117 // CNF G. 118 assertEquals("[~Kills(Curiosity,Tuna)]", cnf.toString()); 119 } 120 121 public void testNestedExistsAndOrs() { 122 FOLDomain domain = new FOLDomain(); 123 domain.addPredicate("P"); 124 domain.addPredicate("R"); 125 domain.addPredicate("Q"); 126 127 FOLParser parser = new FOLParser(domain); 128 129 Sentence origSentence = parser 130 .parse("EXISTS w (FORALL x ( (EXISTS z (Q(w, z))) => (EXISTS y (NOT(P(x, y)) AND R(y))) ) )"); 131 132 CNFConverter cnfConv = new CNFConverter(parser); 133 134 CNF cnf = cnfConv.convertToCNF(origSentence); 135 136 assertEquals("[~P(x,SF0(x)), ~Q(SC0,z)],[~Q(SC0,z), R(SF0(x))]", cnf 137 .toString()); 138 139 // Ax.Ay.(p(x,y) => Ez.(q(x,y,z))) 140 origSentence = parser 141 .parse("FORALL x1 (FORALL y1 (P(x1, y1) => EXISTS z1 (Q(x1, y1, z1))))"); 142 143 cnf = cnfConv.convertToCNF(origSentence); 144 145 assertEquals("[~P(x1,y1), Q(x1,y1,SF1(x1,y1))]", cnf.toString()); 146 147 // Ex.Ay.Az.(r(y,z) <=> q(x,y,z)) 148 origSentence = parser 149 .parse("EXISTS x2 (FORALL y2 (FORALL z2 (R(y2, z2) <=> Q(x2, y2, z2))))"); 150 151 cnf = cnfConv.convertToCNF(origSentence); 152 153 assertEquals("[~R(y2,z2), Q(SC1,y2,z2)],[~Q(SC1,y2,z2), R(y2,z2)]", cnf 154 .toString()); 155 156 // Ax.Ey.(~p(x,y) => Az.(q(x,y,z))) 157 origSentence = parser 158 .parse("FORALL x3 (EXISTS y3 (NOT(P(x3, y3)) => FORALL z3 (Q(x3, y3, z3))))"); 159 160 cnf = cnfConv.convertToCNF(origSentence); 161 162 assertEquals("[P(x3,SF2(x3)), Q(x3,SF2(x3),z3)]", cnf.toString()); 163 164 // Ew.Ex.Ey.Ez.(r(x,y) & q(x,w,z)) 165 origSentence = parser 166 .parse("NOT(EXISTS w4 (EXISTS x4 (EXISTS y4 ( EXISTS z4 (R(x4, y4) AND Q(x4, w4, z4))))))"); 167 168 cnf = cnfConv.convertToCNF(origSentence); 169 170 assertEquals("[~Q(x4,w4,z4), ~R(x4,y4)]", cnf.toString()); 171 } 172 173 public void testImplicationsAndExtendedAndsOrs() { 174 FOLDomain domain = new FOLDomain(); 175 domain.addPredicate("Cheat"); 176 domain.addPredicate("Extra"); 177 domain.addPredicate("Knows"); 178 domain.addPredicate("Diff"); 179 domain.addPredicate("F"); 180 domain.addPredicate("A"); 181 domain.addPredicate("Probation"); 182 domain.addPredicate("Award"); 183 184 FOLParser parser = new FOLParser(domain); 185 CNFConverter cnfConv = new CNFConverter(parser); 186 187 // cheat(x,y) => f(x,y) 188 Sentence def1 = parser.parse("(Cheat(x,y) => F(x,y))"); 189 CNF cnfDef1 = cnfConv.convertToCNF(def1); 190 191 assertEquals("[~Cheat(x,y), F(x,y)]", cnfDef1.toString()); 192 193 // extra(x,y) | knows(x) => a(x,y) 194 Sentence def2 = parser.parse("((Extra(x,y) OR Knows(x)) => A(x,y))"); 195 CNF cnfDef2 = cnfConv.convertToCNF(def2); 196 197 assertEquals("[~Extra(x,y), A(x,y)],[~Knows(x), A(x,y)]", cnfDef2 198 .toString()); 199 200 // f(x,y) & f(x,z) & diff(y,z) <=> probation(x) 201 Sentence def3 = parser 202 .parse("(((NOT(((F(x,y) AND F(x,z)) AND Diff(y,z)))) OR Probation(x)) AND (((F(x,y) AND F(x,z)) AND Diff(y,z)) OR NOT(Probation(x))))"); 203 CNF cnfDef3 = cnfConv.convertToCNF(def3); 204 205 assertEquals( 206 "[~Diff(y,z), ~F(x,y), ~F(x,z), Probation(x)],[~Probation(x), F(x,y)],[~Probation(x), F(x,z)],[~Probation(x), Diff(y,z)]", 207 cnfDef3.toString()); 208 209 // a(x,y) & a(x,z) & diff(y,z) <=> award(x) 210 Sentence def4 = parser 211 .parse("(((NOT(((A(x,y) AND A(x,z)) AND Diff(y,z)))) OR Award(x)) AND (((A(x,y) AND A(x,z)) AND Diff(y,z)) OR NOT(Award(x))))"); 212 CNF cnfDef4 = cnfConv.convertToCNF(def4); 213 214 assertEquals( 215 "[~A(x,y), ~A(x,z), ~Diff(y,z), Award(x)],[~Award(x), A(x,y)],[~Award(x), A(x,z)],[~Award(x), Diff(y,z)]", 216 cnfDef4.toString()); 217 218 // f(x,y) <=> ~a(x,y) 219 Sentence def5 = parser 220 .parse("( ( NOT(F(x,y)) OR NOT(A(x,y))) AND ( F(x,y) OR NOT(NOT(A(x,y))) ) )"); 221 CNF cnfDef5 = cnfConv.convertToCNF(def5); 222 223 assertEquals("[~A(x,y), ~F(x,y)],[A(x,y), F(x,y)]", cnfDef5.toString()); 224 } 225 226 public void testNegationsAndNestedImplications() { 227 FOLDomain domain = new FOLDomain(); 228 domain.addPredicate("P"); 229 domain.addPredicate("Q"); 230 domain.addPredicate("R"); 231 domain.addConstant("A"); 232 233 FOLParser parser = new FOLParser(domain); 234 CNFConverter cnfConv = new CNFConverter(parser); 235 236 // ~(((~p or ~q) => ~(p or q)) => r) 237 Sentence sent = parser.parse("NOT(((((NOT(P(A)) OR NOT(Q(A)))) => NOT((P(A) OR Q(A)))) => R(A)))"); 238 CNF cnf = cnfConv.convertToCNF(sent); 239 240 assertEquals( 241 "[~P(A), P(A)],[~P(A), Q(A)],[~Q(A), P(A)],[~Q(A), Q(A)],[~R(A)]", 242 cnf.toString()); 243 } 244 245 public void testInductionAxiomSchema() { 246 FOLDomain domain = new FOLDomain(); 247 domain.addPredicate("Equal"); 248 domain.addFunction("Plus"); 249 domain.addConstant("A"); 250 domain.addConstant("B"); 251 domain.addConstant("N"); 252 domain.addConstant("ONE"); 253 domain.addConstant("ZERO"); 254 255 FOLParser parser = new FOLParser(domain); 256 CNFConverter cnfConv = new CNFConverter(parser); 257 258 // Base Case: 259 Sentence sent = parser 260 .parse("NOT(FORALL x (FORALL y (Equal(Plus(Plus(x,y),ZERO), Plus(x,Plus(y,ZERO))))))"); 261 CNF cnf = cnfConv.convertToCNF(sent); 262 assertEquals( 263 "[~Equal(Plus(Plus(SC0,SC1),ZERO),Plus(SC0,Plus(SC1,ZERO)))]", 264 cnf.toString()); 265 266 // Instance of Induction Axion Scmema 267 sent = parser 268 .parse("((" 269 + "Equal(Plus(Plus(A,B),ZERO), Plus(A,Plus(B,ZERO)))" 270 + " AND " 271 + "(FORALL x (FORALL y (FORALL z(" 272 + "Equal(Plus(Plus(x,y),z), Plus(x,Plus(y,z)))" 273 + " => " 274 + "Equal(Plus(Plus(x,y),Plus(z,ONE)), Plus(x,Plus(y,Plus(z,ONE))))" 275 + "))))" + ")" 276 + " => " 277 + "FORALL x (FORALL y (FORALL z(" 278 + "Equal(Plus(Plus(x,y),z), Plus(x,Plus(y,z)))" 279 + "))))" 280 ); 281 cnf = cnfConv.convertToCNF(sent); 282 assertEquals( 283 "[~Equal(Plus(Plus(A,B),ZERO),Plus(A,Plus(B,ZERO))), Equal(Plus(Plus(q0,q1),q2),Plus(q0,Plus(q1,q2))), Equal(Plus(Plus(SC2,SC3),SC4),Plus(SC2,Plus(SC3,SC4)))],[~Equal(Plus(Plus(A,B),ZERO),Plus(A,Plus(B,ZERO))), ~Equal(Plus(Plus(SC2,SC3),Plus(SC4,ONE)),Plus(SC2,Plus(SC3,Plus(SC4,ONE)))), Equal(Plus(Plus(q0,q1),q2),Plus(q0,Plus(q1,q2)))]", 284 cnf.toString()); 285 286 // Goal 287 sent = parser 288 .parse("NOT(FORALL x (FORALL y (FORALL z (Equal(Plus(Plus(x,y),z), Plus(x,Plus(y,z)))))))"); 289 cnf = cnfConv.convertToCNF(sent); 290 assertEquals( 291 "[~Equal(Plus(Plus(SC5,SC6),SC7),Plus(SC5,Plus(SC6,SC7)))]", 292 cnf.toString()); 293 } 294 295 public void testTermEquality() { 296 FOLDomain domain = new FOLDomain(); 297 domain.addPredicate("P"); 298 domain.addPredicate("Q"); 299 domain.addPredicate("R"); 300 domain.addConstant("A"); 301 domain.addConstant("B"); 302 domain.addConstant("C"); 303 domain.addConstant("D"); 304 domain.addFunction("Plus"); 305 domain.addConstant("ONE"); 306 domain.addConstant("ZERO"); 307 308 FOLParser parser = new FOLParser(domain); 309 CNFConverter cnfConv = new CNFConverter(parser); 310 311 // x=y 312 Sentence sent = parser.parse("x = y"); 313 CNF cnf = cnfConv.convertToCNF(sent); 314 315 assertEquals("[x = y]", cnf.toString()); 316 317 // x!=y 318 sent = parser.parse("NOT(x = y)"); 319 cnf = cnfConv.convertToCNF(sent); 320 321 assertEquals("[~x = y]", cnf.toString()); 322 323 // A=B 324 sent = parser.parse("A = B"); 325 cnf = cnfConv.convertToCNF(sent); 326 327 assertEquals("[A = B]", cnf.toString()); 328 329 // A!=B 330 sent = parser.parse("NOT(A = B)"); 331 cnf = cnfConv.convertToCNF(sent); 332 333 assertEquals("[~A = B]", cnf.toString()); 334 335 // ~(((~A=B or ~D=C) => ~(A=B or D=C)) => A=D) 336 sent = parser 337 .parse("NOT(((((NOT(A = B) OR NOT(D = C))) => NOT((A = B OR D = C))) => A = D))"); 338 cnf = cnfConv.convertToCNF(sent); 339 340 assertEquals( 341 "[~A = B, A = B],[~A = B, D = C],[~D = C, A = B],[~D = C, D = C],[~A = D]", 342 cnf.toString()); 343 344 // 345 // Induction Axiom Schema using Term Equality 346 347 // Base Case: 348 sent = parser 349 .parse("NOT(FORALL x (FORALL y (Plus(Plus(x,y),ZERO) = Plus(x,Plus(y,ZERO)))))"); 350 cnf = cnfConv.convertToCNF(sent); 351 assertEquals("[~Plus(Plus(SC0,SC1),ZERO) = Plus(SC0,Plus(SC1,ZERO))]", 352 cnf.toString()); 353 354 // Instance of Induction Axion Scmema 355 sent = parser 356 .parse("((" 357 + "Plus(Plus(A,B),ZERO) = Plus(A,Plus(B,ZERO))" 358 + " AND " 359 + "(FORALL x (FORALL y (FORALL z(" 360 + "Plus(Plus(x,y),z) = Plus(x,Plus(y,z))" 361 + " => " 362 + "Plus(Plus(x,y),Plus(z,ONE)) = Plus(x,Plus(y,Plus(z,ONE)))" 363 + "))))" + ")" + " => " 364 + "FORALL x (FORALL y (FORALL z(" 365 + "Plus(Plus(x,y),z) = Plus(x,Plus(y,z))" 366 + "))))"); 367 cnf = cnfConv.convertToCNF(sent); 368 assertEquals( 369 "[~Plus(Plus(A,B),ZERO) = Plus(A,Plus(B,ZERO)), Plus(Plus(q0,q1),q2) = Plus(q0,Plus(q1,q2)), Plus(Plus(SC2,SC3),SC4) = Plus(SC2,Plus(SC3,SC4))],[~Plus(Plus(A,B),ZERO) = Plus(A,Plus(B,ZERO)), ~Plus(Plus(SC2,SC3),Plus(SC4,ONE)) = Plus(SC2,Plus(SC3,Plus(SC4,ONE))), Plus(Plus(q0,q1),q2) = Plus(q0,Plus(q1,q2))]", 370 cnf.toString()); 371 372 // Goal 373 sent = parser 374 .parse("NOT(FORALL x (FORALL y (FORALL z (Plus(Plus(x,y),z) = Plus(x,Plus(y,z))))))"); 375 cnf = cnfConv.convertToCNF(sent); 376 assertEquals( 377 "[~Plus(Plus(SC5,SC6),SC7) = Plus(SC5,Plus(SC6,SC7))]", 378 cnf.toString()); 379 380 } 381 }