tableau.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714
  1. # Natural Language Toolkit: First-Order Tableau Theorem Prover
  2. #
  3. # Copyright (C) 2001-2020 NLTK Project
  4. # Author: Dan Garrette <dhgarrette@gmail.com>
  5. #
  6. # URL: <http://nltk.org/>
  7. # For license information, see LICENSE.TXT
  8. """
  9. Module for a tableau-based First Order theorem prover.
  10. """
  11. from nltk.internals import Counter
  12. from nltk.sem.logic import (
  13. VariableExpression,
  14. EqualityExpression,
  15. ApplicationExpression,
  16. Expression,
  17. AbstractVariableExpression,
  18. AllExpression,
  19. NegatedExpression,
  20. ExistsExpression,
  21. Variable,
  22. ImpExpression,
  23. AndExpression,
  24. unique_variable,
  25. LambdaExpression,
  26. IffExpression,
  27. OrExpression,
  28. FunctionVariableExpression,
  29. )
  30. from nltk.inference.api import Prover, BaseProverCommand
  31. _counter = Counter()
  32. class ProverParseError(Exception):
  33. pass
  34. class TableauProver(Prover):
  35. _assume_false = False
  36. def _prove(self, goal=None, assumptions=None, verbose=False):
  37. if not assumptions:
  38. assumptions = []
  39. result = None
  40. try:
  41. agenda = Agenda()
  42. if goal:
  43. agenda.put(-goal)
  44. agenda.put_all(assumptions)
  45. debugger = Debug(verbose)
  46. result = self._attempt_proof(agenda, set(), set(), debugger)
  47. except RuntimeError as e:
  48. if self._assume_false and str(e).startswith(
  49. "maximum recursion depth exceeded"
  50. ):
  51. result = False
  52. else:
  53. if verbose:
  54. print(e)
  55. else:
  56. raise e
  57. return (result, "\n".join(debugger.lines))
  58. def _attempt_proof(self, agenda, accessible_vars, atoms, debug):
  59. (current, context), category = agenda.pop_first()
  60. # if there's nothing left in the agenda, and we haven't closed the path
  61. if not current:
  62. debug.line("AGENDA EMPTY")
  63. return False
  64. proof_method = {
  65. Categories.ATOM: self._attempt_proof_atom,
  66. Categories.PROP: self._attempt_proof_prop,
  67. Categories.N_ATOM: self._attempt_proof_n_atom,
  68. Categories.N_PROP: self._attempt_proof_n_prop,
  69. Categories.APP: self._attempt_proof_app,
  70. Categories.N_APP: self._attempt_proof_n_app,
  71. Categories.N_EQ: self._attempt_proof_n_eq,
  72. Categories.D_NEG: self._attempt_proof_d_neg,
  73. Categories.N_ALL: self._attempt_proof_n_all,
  74. Categories.N_EXISTS: self._attempt_proof_n_some,
  75. Categories.AND: self._attempt_proof_and,
  76. Categories.N_OR: self._attempt_proof_n_or,
  77. Categories.N_IMP: self._attempt_proof_n_imp,
  78. Categories.OR: self._attempt_proof_or,
  79. Categories.IMP: self._attempt_proof_imp,
  80. Categories.N_AND: self._attempt_proof_n_and,
  81. Categories.IFF: self._attempt_proof_iff,
  82. Categories.N_IFF: self._attempt_proof_n_iff,
  83. Categories.EQ: self._attempt_proof_eq,
  84. Categories.EXISTS: self._attempt_proof_some,
  85. Categories.ALL: self._attempt_proof_all,
  86. }[category]
  87. debug.line((current, context))
  88. return proof_method(current, context, agenda, accessible_vars, atoms, debug)
  89. def _attempt_proof_atom(
  90. self, current, context, agenda, accessible_vars, atoms, debug
  91. ):
  92. # Check if the branch is closed. Return 'True' if it is
  93. if (current, True) in atoms:
  94. debug.line("CLOSED", 1)
  95. return True
  96. if context:
  97. if isinstance(context.term, NegatedExpression):
  98. current = current.negate()
  99. agenda.put(context(current).simplify())
  100. return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1)
  101. else:
  102. # mark all AllExpressions as 'not exhausted' into the agenda since we are (potentially) adding new accessible vars
  103. agenda.mark_alls_fresh()
  104. return self._attempt_proof(
  105. agenda,
  106. accessible_vars | set(current.args),
  107. atoms | set([(current, False)]),
  108. debug + 1,
  109. )
  110. def _attempt_proof_n_atom(
  111. self, current, context, agenda, accessible_vars, atoms, debug
  112. ):
  113. # Check if the branch is closed. Return 'True' if it is
  114. if (current.term, False) in atoms:
  115. debug.line("CLOSED", 1)
  116. return True
  117. if context:
  118. if isinstance(context.term, NegatedExpression):
  119. current = current.negate()
  120. agenda.put(context(current).simplify())
  121. return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1)
  122. else:
  123. # mark all AllExpressions as 'not exhausted' into the agenda since we are (potentially) adding new accessible vars
  124. agenda.mark_alls_fresh()
  125. return self._attempt_proof(
  126. agenda,
  127. accessible_vars | set(current.term.args),
  128. atoms | set([(current.term, True)]),
  129. debug + 1,
  130. )
  131. def _attempt_proof_prop(
  132. self, current, context, agenda, accessible_vars, atoms, debug
  133. ):
  134. # Check if the branch is closed. Return 'True' if it is
  135. if (current, True) in atoms:
  136. debug.line("CLOSED", 1)
  137. return True
  138. # mark all AllExpressions as 'not exhausted' into the agenda since we are (potentially) adding new accessible vars
  139. agenda.mark_alls_fresh()
  140. return self._attempt_proof(
  141. agenda, accessible_vars, atoms | set([(current, False)]), debug + 1
  142. )
  143. def _attempt_proof_n_prop(
  144. self, current, context, agenda, accessible_vars, atoms, debug
  145. ):
  146. # Check if the branch is closed. Return 'True' if it is
  147. if (current.term, False) in atoms:
  148. debug.line("CLOSED", 1)
  149. return True
  150. # mark all AllExpressions as 'not exhausted' into the agenda since we are (potentially) adding new accessible vars
  151. agenda.mark_alls_fresh()
  152. return self._attempt_proof(
  153. agenda, accessible_vars, atoms | set([(current.term, True)]), debug + 1
  154. )
  155. def _attempt_proof_app(
  156. self, current, context, agenda, accessible_vars, atoms, debug
  157. ):
  158. f, args = current.uncurry()
  159. for i, arg in enumerate(args):
  160. if not TableauProver.is_atom(arg):
  161. ctx = f
  162. nv = Variable("X%s" % _counter.get())
  163. for j, a in enumerate(args):
  164. ctx = ctx(VariableExpression(nv)) if i == j else ctx(a)
  165. if context:
  166. ctx = context(ctx).simplify()
  167. ctx = LambdaExpression(nv, ctx)
  168. agenda.put(arg, ctx)
  169. return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1)
  170. raise Exception("If this method is called, there must be a non-atomic argument")
  171. def _attempt_proof_n_app(
  172. self, current, context, agenda, accessible_vars, atoms, debug
  173. ):
  174. f, args = current.term.uncurry()
  175. for i, arg in enumerate(args):
  176. if not TableauProver.is_atom(arg):
  177. ctx = f
  178. nv = Variable("X%s" % _counter.get())
  179. for j, a in enumerate(args):
  180. ctx = ctx(VariableExpression(nv)) if i == j else ctx(a)
  181. if context:
  182. # combine new context with existing
  183. ctx = context(ctx).simplify()
  184. ctx = LambdaExpression(nv, -ctx)
  185. agenda.put(-arg, ctx)
  186. return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1)
  187. raise Exception("If this method is called, there must be a non-atomic argument")
  188. def _attempt_proof_n_eq(
  189. self, current, context, agenda, accessible_vars, atoms, debug
  190. ):
  191. ###########################################################################
  192. # Since 'current' is of type '~(a=b)', the path is closed if 'a' == 'b'
  193. ###########################################################################
  194. if current.term.first == current.term.second:
  195. debug.line("CLOSED", 1)
  196. return True
  197. agenda[Categories.N_EQ].add((current, context))
  198. current._exhausted = True
  199. return self._attempt_proof(
  200. agenda,
  201. accessible_vars | set([current.term.first, current.term.second]),
  202. atoms,
  203. debug + 1,
  204. )
  205. def _attempt_proof_d_neg(
  206. self, current, context, agenda, accessible_vars, atoms, debug
  207. ):
  208. agenda.put(current.term.term, context)
  209. return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1)
  210. def _attempt_proof_n_all(
  211. self, current, context, agenda, accessible_vars, atoms, debug
  212. ):
  213. agenda[Categories.EXISTS].add(
  214. (ExistsExpression(current.term.variable, -current.term.term), context)
  215. )
  216. return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1)
  217. def _attempt_proof_n_some(
  218. self, current, context, agenda, accessible_vars, atoms, debug
  219. ):
  220. agenda[Categories.ALL].add(
  221. (AllExpression(current.term.variable, -current.term.term), context)
  222. )
  223. return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1)
  224. def _attempt_proof_and(
  225. self, current, context, agenda, accessible_vars, atoms, debug
  226. ):
  227. agenda.put(current.first, context)
  228. agenda.put(current.second, context)
  229. return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1)
  230. def _attempt_proof_n_or(
  231. self, current, context, agenda, accessible_vars, atoms, debug
  232. ):
  233. agenda.put(-current.term.first, context)
  234. agenda.put(-current.term.second, context)
  235. return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1)
  236. def _attempt_proof_n_imp(
  237. self, current, context, agenda, accessible_vars, atoms, debug
  238. ):
  239. agenda.put(current.term.first, context)
  240. agenda.put(-current.term.second, context)
  241. return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1)
  242. def _attempt_proof_or(
  243. self, current, context, agenda, accessible_vars, atoms, debug
  244. ):
  245. new_agenda = agenda.clone()
  246. agenda.put(current.first, context)
  247. new_agenda.put(current.second, context)
  248. return self._attempt_proof(
  249. agenda, accessible_vars, atoms, debug + 1
  250. ) and self._attempt_proof(new_agenda, accessible_vars, atoms, debug + 1)
  251. def _attempt_proof_imp(
  252. self, current, context, agenda, accessible_vars, atoms, debug
  253. ):
  254. new_agenda = agenda.clone()
  255. agenda.put(-current.first, context)
  256. new_agenda.put(current.second, context)
  257. return self._attempt_proof(
  258. agenda, accessible_vars, atoms, debug + 1
  259. ) and self._attempt_proof(new_agenda, accessible_vars, atoms, debug + 1)
  260. def _attempt_proof_n_and(
  261. self, current, context, agenda, accessible_vars, atoms, debug
  262. ):
  263. new_agenda = agenda.clone()
  264. agenda.put(-current.term.first, context)
  265. new_agenda.put(-current.term.second, context)
  266. return self._attempt_proof(
  267. agenda, accessible_vars, atoms, debug + 1
  268. ) and self._attempt_proof(new_agenda, accessible_vars, atoms, debug + 1)
  269. def _attempt_proof_iff(
  270. self, current, context, agenda, accessible_vars, atoms, debug
  271. ):
  272. new_agenda = agenda.clone()
  273. agenda.put(current.first, context)
  274. agenda.put(current.second, context)
  275. new_agenda.put(-current.first, context)
  276. new_agenda.put(-current.second, context)
  277. return self._attempt_proof(
  278. agenda, accessible_vars, atoms, debug + 1
  279. ) and self._attempt_proof(new_agenda, accessible_vars, atoms, debug + 1)
  280. def _attempt_proof_n_iff(
  281. self, current, context, agenda, accessible_vars, atoms, debug
  282. ):
  283. new_agenda = agenda.clone()
  284. agenda.put(current.term.first, context)
  285. agenda.put(-current.term.second, context)
  286. new_agenda.put(-current.term.first, context)
  287. new_agenda.put(current.term.second, context)
  288. return self._attempt_proof(
  289. agenda, accessible_vars, atoms, debug + 1
  290. ) and self._attempt_proof(new_agenda, accessible_vars, atoms, debug + 1)
  291. def _attempt_proof_eq(
  292. self, current, context, agenda, accessible_vars, atoms, debug
  293. ):
  294. #########################################################################
  295. # Since 'current' is of the form '(a = b)', replace ALL free instances
  296. # of 'a' with 'b'
  297. #########################################################################
  298. agenda.put_atoms(atoms)
  299. agenda.replace_all(current.first, current.second)
  300. accessible_vars.discard(current.first)
  301. agenda.mark_neqs_fresh()
  302. return self._attempt_proof(agenda, accessible_vars, set(), debug + 1)
  303. def _attempt_proof_some(
  304. self, current, context, agenda, accessible_vars, atoms, debug
  305. ):
  306. new_unique_variable = VariableExpression(unique_variable())
  307. agenda.put(current.term.replace(current.variable, new_unique_variable), context)
  308. agenda.mark_alls_fresh()
  309. return self._attempt_proof(
  310. agenda, accessible_vars | set([new_unique_variable]), atoms, debug + 1
  311. )
  312. def _attempt_proof_all(
  313. self, current, context, agenda, accessible_vars, atoms, debug
  314. ):
  315. try:
  316. current._used_vars
  317. except AttributeError:
  318. current._used_vars = set()
  319. # if there are accessible_vars on the path
  320. if accessible_vars:
  321. # get the set of bound variables that have not be used by this AllExpression
  322. bv_available = accessible_vars - current._used_vars
  323. if bv_available:
  324. variable_to_use = list(bv_available)[0]
  325. debug.line("--> Using '%s'" % variable_to_use, 2)
  326. current._used_vars |= set([variable_to_use])
  327. agenda.put(
  328. current.term.replace(current.variable, variable_to_use), context
  329. )
  330. agenda[Categories.ALL].add((current, context))
  331. return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1)
  332. else:
  333. # no more available variables to substitute
  334. debug.line("--> Variables Exhausted", 2)
  335. current._exhausted = True
  336. agenda[Categories.ALL].add((current, context))
  337. return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1)
  338. else:
  339. new_unique_variable = VariableExpression(unique_variable())
  340. debug.line("--> Using '%s'" % new_unique_variable, 2)
  341. current._used_vars |= set([new_unique_variable])
  342. agenda.put(
  343. current.term.replace(current.variable, new_unique_variable), context
  344. )
  345. agenda[Categories.ALL].add((current, context))
  346. agenda.mark_alls_fresh()
  347. return self._attempt_proof(
  348. agenda, accessible_vars | set([new_unique_variable]), atoms, debug + 1
  349. )
  350. @staticmethod
  351. def is_atom(e):
  352. if isinstance(e, NegatedExpression):
  353. e = e.term
  354. if isinstance(e, ApplicationExpression):
  355. for arg in e.args:
  356. if not TableauProver.is_atom(arg):
  357. return False
  358. return True
  359. elif isinstance(e, AbstractVariableExpression) or isinstance(
  360. e, LambdaExpression
  361. ):
  362. return True
  363. else:
  364. return False
  365. class TableauProverCommand(BaseProverCommand):
  366. def __init__(self, goal=None, assumptions=None, prover=None):
  367. """
  368. :param goal: Input expression to prove
  369. :type goal: sem.Expression
  370. :param assumptions: Input expressions to use as assumptions in
  371. the proof.
  372. :type assumptions: list(sem.Expression)
  373. """
  374. if prover is not None:
  375. assert isinstance(prover, TableauProver)
  376. else:
  377. prover = TableauProver()
  378. BaseProverCommand.__init__(self, prover, goal, assumptions)
  379. class Agenda(object):
  380. def __init__(self):
  381. self.sets = tuple(set() for i in range(21))
  382. def clone(self):
  383. new_agenda = Agenda()
  384. set_list = [s.copy() for s in self.sets]
  385. new_allExs = set()
  386. for allEx, _ in set_list[Categories.ALL]:
  387. new_allEx = AllExpression(allEx.variable, allEx.term)
  388. try:
  389. new_allEx._used_vars = set(used for used in allEx._used_vars)
  390. except AttributeError:
  391. new_allEx._used_vars = set()
  392. new_allExs.add((new_allEx, None))
  393. set_list[Categories.ALL] = new_allExs
  394. set_list[Categories.N_EQ] = set(
  395. (NegatedExpression(n_eq.term), ctx)
  396. for (n_eq, ctx) in set_list[Categories.N_EQ]
  397. )
  398. new_agenda.sets = tuple(set_list)
  399. return new_agenda
  400. def __getitem__(self, index):
  401. return self.sets[index]
  402. def put(self, expression, context=None):
  403. if isinstance(expression, AllExpression):
  404. ex_to_add = AllExpression(expression.variable, expression.term)
  405. try:
  406. ex_to_add._used_vars = set(used for used in expression._used_vars)
  407. except AttributeError:
  408. ex_to_add._used_vars = set()
  409. else:
  410. ex_to_add = expression
  411. self.sets[self._categorize_expression(ex_to_add)].add((ex_to_add, context))
  412. def put_all(self, expressions):
  413. for expression in expressions:
  414. self.put(expression)
  415. def put_atoms(self, atoms):
  416. for atom, neg in atoms:
  417. if neg:
  418. self[Categories.N_ATOM].add((-atom, None))
  419. else:
  420. self[Categories.ATOM].add((atom, None))
  421. def pop_first(self):
  422. """ Pop the first expression that appears in the agenda """
  423. for i, s in enumerate(self.sets):
  424. if s:
  425. if i in [Categories.N_EQ, Categories.ALL]:
  426. for ex in s:
  427. try:
  428. if not ex[0]._exhausted:
  429. s.remove(ex)
  430. return (ex, i)
  431. except AttributeError:
  432. s.remove(ex)
  433. return (ex, i)
  434. else:
  435. return (s.pop(), i)
  436. return ((None, None), None)
  437. def replace_all(self, old, new):
  438. for s in self.sets:
  439. for ex, ctx in s:
  440. ex.replace(old.variable, new)
  441. if ctx is not None:
  442. ctx.replace(old.variable, new)
  443. def mark_alls_fresh(self):
  444. for u, _ in self.sets[Categories.ALL]:
  445. u._exhausted = False
  446. def mark_neqs_fresh(self):
  447. for neq, _ in self.sets[Categories.N_EQ]:
  448. neq._exhausted = False
  449. def _categorize_expression(self, current):
  450. if isinstance(current, NegatedExpression):
  451. return self._categorize_NegatedExpression(current)
  452. elif isinstance(current, FunctionVariableExpression):
  453. return Categories.PROP
  454. elif TableauProver.is_atom(current):
  455. return Categories.ATOM
  456. elif isinstance(current, AllExpression):
  457. return Categories.ALL
  458. elif isinstance(current, AndExpression):
  459. return Categories.AND
  460. elif isinstance(current, OrExpression):
  461. return Categories.OR
  462. elif isinstance(current, ImpExpression):
  463. return Categories.IMP
  464. elif isinstance(current, IffExpression):
  465. return Categories.IFF
  466. elif isinstance(current, EqualityExpression):
  467. return Categories.EQ
  468. elif isinstance(current, ExistsExpression):
  469. return Categories.EXISTS
  470. elif isinstance(current, ApplicationExpression):
  471. return Categories.APP
  472. else:
  473. raise ProverParseError("cannot categorize %s" % current.__class__.__name__)
  474. def _categorize_NegatedExpression(self, current):
  475. negated = current.term
  476. if isinstance(negated, NegatedExpression):
  477. return Categories.D_NEG
  478. elif isinstance(negated, FunctionVariableExpression):
  479. return Categories.N_PROP
  480. elif TableauProver.is_atom(negated):
  481. return Categories.N_ATOM
  482. elif isinstance(negated, AllExpression):
  483. return Categories.N_ALL
  484. elif isinstance(negated, AndExpression):
  485. return Categories.N_AND
  486. elif isinstance(negated, OrExpression):
  487. return Categories.N_OR
  488. elif isinstance(negated, ImpExpression):
  489. return Categories.N_IMP
  490. elif isinstance(negated, IffExpression):
  491. return Categories.N_IFF
  492. elif isinstance(negated, EqualityExpression):
  493. return Categories.N_EQ
  494. elif isinstance(negated, ExistsExpression):
  495. return Categories.N_EXISTS
  496. elif isinstance(negated, ApplicationExpression):
  497. return Categories.N_APP
  498. else:
  499. raise ProverParseError("cannot categorize %s" % negated.__class__.__name__)
  500. class Debug(object):
  501. def __init__(self, verbose, indent=0, lines=None):
  502. self.verbose = verbose
  503. self.indent = indent
  504. if not lines:
  505. lines = []
  506. self.lines = lines
  507. def __add__(self, increment):
  508. return Debug(self.verbose, self.indent + 1, self.lines)
  509. def line(self, data, indent=0):
  510. if isinstance(data, tuple):
  511. ex, ctx = data
  512. if ctx:
  513. data = "%s, %s" % (ex, ctx)
  514. else:
  515. data = "%s" % ex
  516. if isinstance(ex, AllExpression):
  517. try:
  518. used_vars = "[%s]" % (
  519. ",".join("%s" % ve.variable.name for ve in ex._used_vars)
  520. )
  521. data += ": %s" % used_vars
  522. except AttributeError:
  523. data += ": []"
  524. newline = "%s%s" % (" " * (self.indent + indent), data)
  525. self.lines.append(newline)
  526. if self.verbose:
  527. print(newline)
  528. class Categories(object):
  529. ATOM = 0
  530. PROP = 1
  531. N_ATOM = 2
  532. N_PROP = 3
  533. APP = 4
  534. N_APP = 5
  535. N_EQ = 6
  536. D_NEG = 7
  537. N_ALL = 8
  538. N_EXISTS = 9
  539. AND = 10
  540. N_OR = 11
  541. N_IMP = 12
  542. OR = 13
  543. IMP = 14
  544. N_AND = 15
  545. IFF = 16
  546. N_IFF = 17
  547. EQ = 18
  548. EXISTS = 19
  549. ALL = 20
  550. def testTableauProver():
  551. tableau_test("P | -P")
  552. tableau_test("P & -P")
  553. tableau_test("Q", ["P", "(P -> Q)"])
  554. tableau_test("man(x)")
  555. tableau_test("(man(x) -> man(x))")
  556. tableau_test("(man(x) -> --man(x))")
  557. tableau_test("-(man(x) and -man(x))")
  558. tableau_test("(man(x) or -man(x))")
  559. tableau_test("(man(x) -> man(x))")
  560. tableau_test("-(man(x) and -man(x))")
  561. tableau_test("(man(x) or -man(x))")
  562. tableau_test("(man(x) -> man(x))")
  563. tableau_test("(man(x) iff man(x))")
  564. tableau_test("-(man(x) iff -man(x))")
  565. tableau_test("all x.man(x)")
  566. tableau_test("all x.all y.((x = y) -> (y = x))")
  567. tableau_test("all x.all y.all z.(((x = y) & (y = z)) -> (x = z))")
  568. # tableau_test('-all x.some y.F(x,y) & some x.all y.(-F(x,y))')
  569. # tableau_test('some x.all y.sees(x,y)')
  570. p1 = "all x.(man(x) -> mortal(x))"
  571. p2 = "man(Socrates)"
  572. c = "mortal(Socrates)"
  573. tableau_test(c, [p1, p2])
  574. p1 = "all x.(man(x) -> walks(x))"
  575. p2 = "man(John)"
  576. c = "some y.walks(y)"
  577. tableau_test(c, [p1, p2])
  578. p = "((x = y) & walks(y))"
  579. c = "walks(x)"
  580. tableau_test(c, [p])
  581. p = "((x = y) & ((y = z) & (z = w)))"
  582. c = "(x = w)"
  583. tableau_test(c, [p])
  584. p = "some e1.some e2.(believe(e1,john,e2) & walk(e2,mary))"
  585. c = "some e0.walk(e0,mary)"
  586. tableau_test(c, [p])
  587. c = "(exists x.exists z3.((x = Mary) & ((z3 = John) & sees(z3,x))) <-> exists x.exists z4.((x = John) & ((z4 = Mary) & sees(x,z4))))"
  588. tableau_test(c)
  589. # p = 'some e1.some e2.((believe e1 john e2) and (walk e2 mary))'
  590. # c = 'some x.some e3.some e4.((believe e3 x e4) and (walk e4 mary))'
  591. # tableau_test(c, [p])
  592. def testHigherOrderTableauProver():
  593. tableau_test("believe(j, -lie(b))", ["believe(j, -lie(b) & -cheat(b))"])
  594. tableau_test("believe(j, lie(b) & cheat(b))", ["believe(j, lie(b))"])
  595. tableau_test(
  596. "believe(j, lie(b))", ["lie(b)"]
  597. ) # how do we capture that John believes all things that are true
  598. tableau_test(
  599. "believe(j, know(b, cheat(b)))",
  600. ["believe(j, know(b, lie(b)) & know(b, steals(b) & cheat(b)))"],
  601. )
  602. tableau_test("P(Q(y), R(y) & R(z))", ["P(Q(x) & Q(y), R(y) & R(z))"])
  603. tableau_test("believe(j, cheat(b) & lie(b))", ["believe(j, lie(b) & cheat(b))"])
  604. tableau_test("believe(j, -cheat(b) & -lie(b))", ["believe(j, -lie(b) & -cheat(b))"])
  605. def tableau_test(c, ps=None, verbose=False):
  606. pc = Expression.fromstring(c)
  607. pps = [Expression.fromstring(p) for p in ps] if ps else []
  608. if not ps:
  609. ps = []
  610. print(
  611. "%s |- %s: %s"
  612. % (", ".join(ps), pc, TableauProver().prove(pc, pps, verbose=verbose))
  613. )
  614. def demo():
  615. testTableauProver()
  616. testHigherOrderTableauProver()
  617. if __name__ == "__main__":
  618. demo()