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    }