tableau.py 25 KB

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