123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716 |
- # Natural Language Toolkit: First-Order Tableau Theorem Prover
- #
- # Copyright (C) 2001-2019 NLTK Project
- # Author: Dan Garrette <dhgarrette@gmail.com>
- #
- # URL: <http://nltk.org/>
- # For license information, see LICENSE.TXT
- """
- Module for a tableau-based First Order theorem prover.
- """
- from __future__ import print_function, unicode_literals
- from nltk.internals import Counter
- from nltk.sem.logic import (
- VariableExpression,
- EqualityExpression,
- ApplicationExpression,
- Expression,
- AbstractVariableExpression,
- AllExpression,
- NegatedExpression,
- ExistsExpression,
- Variable,
- ImpExpression,
- AndExpression,
- unique_variable,
- LambdaExpression,
- IffExpression,
- OrExpression,
- FunctionVariableExpression,
- )
- from nltk.inference.api import Prover, BaseProverCommand
- _counter = Counter()
- class ProverParseError(Exception):
- pass
- class TableauProver(Prover):
- _assume_false = False
- def _prove(self, goal=None, assumptions=None, verbose=False):
- if not assumptions:
- assumptions = []
- result = None
- try:
- agenda = Agenda()
- if goal:
- agenda.put(-goal)
- agenda.put_all(assumptions)
- debugger = Debug(verbose)
- result = self._attempt_proof(agenda, set(), set(), debugger)
- except RuntimeError as e:
- if self._assume_false and str(e).startswith(
- 'maximum recursion depth exceeded'
- ):
- result = False
- else:
- if verbose:
- print(e)
- else:
- raise e
- return (result, '\n'.join(debugger.lines))
- def _attempt_proof(self, agenda, accessible_vars, atoms, debug):
- (current, context), category = agenda.pop_first()
- # if there's nothing left in the agenda, and we haven't closed the path
- if not current:
- debug.line('AGENDA EMPTY')
- return False
- proof_method = {
- Categories.ATOM: self._attempt_proof_atom,
- Categories.PROP: self._attempt_proof_prop,
- Categories.N_ATOM: self._attempt_proof_n_atom,
- Categories.N_PROP: self._attempt_proof_n_prop,
- Categories.APP: self._attempt_proof_app,
- Categories.N_APP: self._attempt_proof_n_app,
- Categories.N_EQ: self._attempt_proof_n_eq,
- Categories.D_NEG: self._attempt_proof_d_neg,
- Categories.N_ALL: self._attempt_proof_n_all,
- Categories.N_EXISTS: self._attempt_proof_n_some,
- Categories.AND: self._attempt_proof_and,
- Categories.N_OR: self._attempt_proof_n_or,
- Categories.N_IMP: self._attempt_proof_n_imp,
- Categories.OR: self._attempt_proof_or,
- Categories.IMP: self._attempt_proof_imp,
- Categories.N_AND: self._attempt_proof_n_and,
- Categories.IFF: self._attempt_proof_iff,
- Categories.N_IFF: self._attempt_proof_n_iff,
- Categories.EQ: self._attempt_proof_eq,
- Categories.EXISTS: self._attempt_proof_some,
- Categories.ALL: self._attempt_proof_all,
- }[category]
- debug.line((current, context))
- return proof_method(current, context, agenda, accessible_vars, atoms, debug)
- def _attempt_proof_atom(
- self, current, context, agenda, accessible_vars, atoms, debug
- ):
- # Check if the branch is closed. Return 'True' if it is
- if (current, True) in atoms:
- debug.line('CLOSED', 1)
- return True
- if context:
- if isinstance(context.term, NegatedExpression):
- current = current.negate()
- agenda.put(context(current).simplify())
- return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1)
- else:
- # mark all AllExpressions as 'not exhausted' into the agenda since we are (potentially) adding new accessible vars
- agenda.mark_alls_fresh()
- return self._attempt_proof(
- agenda,
- accessible_vars | set(current.args),
- atoms | set([(current, False)]),
- debug + 1,
- )
- def _attempt_proof_n_atom(
- self, current, context, agenda, accessible_vars, atoms, debug
- ):
- # Check if the branch is closed. Return 'True' if it is
- if (current.term, False) in atoms:
- debug.line('CLOSED', 1)
- return True
- if context:
- if isinstance(context.term, NegatedExpression):
- current = current.negate()
- agenda.put(context(current).simplify())
- return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1)
- else:
- # mark all AllExpressions as 'not exhausted' into the agenda since we are (potentially) adding new accessible vars
- agenda.mark_alls_fresh()
- return self._attempt_proof(
- agenda,
- accessible_vars | set(current.term.args),
- atoms | set([(current.term, True)]),
- debug + 1,
- )
- def _attempt_proof_prop(
- self, current, context, agenda, accessible_vars, atoms, debug
- ):
- # Check if the branch is closed. Return 'True' if it is
- if (current, True) in atoms:
- debug.line('CLOSED', 1)
- return True
- # mark all AllExpressions as 'not exhausted' into the agenda since we are (potentially) adding new accessible vars
- agenda.mark_alls_fresh()
- return self._attempt_proof(
- agenda, accessible_vars, atoms | set([(current, False)]), debug + 1
- )
- def _attempt_proof_n_prop(
- self, current, context, agenda, accessible_vars, atoms, debug
- ):
- # Check if the branch is closed. Return 'True' if it is
- if (current.term, False) in atoms:
- debug.line('CLOSED', 1)
- return True
- # mark all AllExpressions as 'not exhausted' into the agenda since we are (potentially) adding new accessible vars
- agenda.mark_alls_fresh()
- return self._attempt_proof(
- agenda, accessible_vars, atoms | set([(current.term, True)]), debug + 1
- )
- def _attempt_proof_app(
- self, current, context, agenda, accessible_vars, atoms, debug
- ):
- f, args = current.uncurry()
- for i, arg in enumerate(args):
- if not TableauProver.is_atom(arg):
- ctx = f
- nv = Variable('X%s' % _counter.get())
- for j, a in enumerate(args):
- ctx = ctx(VariableExpression(nv)) if i == j else ctx(a)
- if context:
- ctx = context(ctx).simplify()
- ctx = LambdaExpression(nv, ctx)
- agenda.put(arg, ctx)
- return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1)
- raise Exception('If this method is called, there must be a non-atomic argument')
- def _attempt_proof_n_app(
- self, current, context, agenda, accessible_vars, atoms, debug
- ):
- f, args = current.term.uncurry()
- for i, arg in enumerate(args):
- if not TableauProver.is_atom(arg):
- ctx = f
- nv = Variable('X%s' % _counter.get())
- for j, a in enumerate(args):
- ctx = ctx(VariableExpression(nv)) if i == j else ctx(a)
- if context:
- # combine new context with existing
- ctx = context(ctx).simplify()
- ctx = LambdaExpression(nv, -ctx)
- agenda.put(-arg, ctx)
- return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1)
- raise Exception('If this method is called, there must be a non-atomic argument')
- def _attempt_proof_n_eq(
- self, current, context, agenda, accessible_vars, atoms, debug
- ):
- ###########################################################################
- # Since 'current' is of type '~(a=b)', the path is closed if 'a' == 'b'
- ###########################################################################
- if current.term.first == current.term.second:
- debug.line('CLOSED', 1)
- return True
- agenda[Categories.N_EQ].add((current, context))
- current._exhausted = True
- return self._attempt_proof(
- agenda,
- accessible_vars | set([current.term.first, current.term.second]),
- atoms,
- debug + 1,
- )
- def _attempt_proof_d_neg(
- self, current, context, agenda, accessible_vars, atoms, debug
- ):
- agenda.put(current.term.term, context)
- return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1)
- def _attempt_proof_n_all(
- self, current, context, agenda, accessible_vars, atoms, debug
- ):
- agenda[Categories.EXISTS].add(
- (ExistsExpression(current.term.variable, -current.term.term), context)
- )
- return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1)
- def _attempt_proof_n_some(
- self, current, context, agenda, accessible_vars, atoms, debug
- ):
- agenda[Categories.ALL].add(
- (AllExpression(current.term.variable, -current.term.term), context)
- )
- return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1)
- def _attempt_proof_and(
- self, current, context, agenda, accessible_vars, atoms, debug
- ):
- agenda.put(current.first, context)
- agenda.put(current.second, context)
- return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1)
- def _attempt_proof_n_or(
- self, current, context, agenda, accessible_vars, atoms, debug
- ):
- agenda.put(-current.term.first, context)
- agenda.put(-current.term.second, context)
- return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1)
- def _attempt_proof_n_imp(
- self, current, context, agenda, accessible_vars, atoms, debug
- ):
- agenda.put(current.term.first, context)
- agenda.put(-current.term.second, context)
- return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1)
- def _attempt_proof_or(
- self, current, context, agenda, accessible_vars, atoms, debug
- ):
- new_agenda = agenda.clone()
- agenda.put(current.first, context)
- new_agenda.put(current.second, context)
- return self._attempt_proof(
- agenda, accessible_vars, atoms, debug + 1
- ) and self._attempt_proof(new_agenda, accessible_vars, atoms, debug + 1)
- def _attempt_proof_imp(
- self, current, context, agenda, accessible_vars, atoms, debug
- ):
- new_agenda = agenda.clone()
- agenda.put(-current.first, context)
- new_agenda.put(current.second, context)
- return self._attempt_proof(
- agenda, accessible_vars, atoms, debug + 1
- ) and self._attempt_proof(new_agenda, accessible_vars, atoms, debug + 1)
- def _attempt_proof_n_and(
- self, current, context, agenda, accessible_vars, atoms, debug
- ):
- new_agenda = agenda.clone()
- agenda.put(-current.term.first, context)
- new_agenda.put(-current.term.second, context)
- return self._attempt_proof(
- agenda, accessible_vars, atoms, debug + 1
- ) and self._attempt_proof(new_agenda, accessible_vars, atoms, debug + 1)
- def _attempt_proof_iff(
- self, current, context, agenda, accessible_vars, atoms, debug
- ):
- new_agenda = agenda.clone()
- agenda.put(current.first, context)
- agenda.put(current.second, context)
- new_agenda.put(-current.first, context)
- new_agenda.put(-current.second, context)
- return self._attempt_proof(
- agenda, accessible_vars, atoms, debug + 1
- ) and self._attempt_proof(new_agenda, accessible_vars, atoms, debug + 1)
- def _attempt_proof_n_iff(
- self, current, context, agenda, accessible_vars, atoms, debug
- ):
- new_agenda = agenda.clone()
- agenda.put(current.term.first, context)
- agenda.put(-current.term.second, context)
- new_agenda.put(-current.term.first, context)
- new_agenda.put(current.term.second, context)
- return self._attempt_proof(
- agenda, accessible_vars, atoms, debug + 1
- ) and self._attempt_proof(new_agenda, accessible_vars, atoms, debug + 1)
- def _attempt_proof_eq(
- self, current, context, agenda, accessible_vars, atoms, debug
- ):
- #########################################################################
- # Since 'current' is of the form '(a = b)', replace ALL free instances
- # of 'a' with 'b'
- #########################################################################
- agenda.put_atoms(atoms)
- agenda.replace_all(current.first, current.second)
- accessible_vars.discard(current.first)
- agenda.mark_neqs_fresh()
- return self._attempt_proof(agenda, accessible_vars, set(), debug + 1)
- def _attempt_proof_some(
- self, current, context, agenda, accessible_vars, atoms, debug
- ):
- new_unique_variable = VariableExpression(unique_variable())
- agenda.put(current.term.replace(current.variable, new_unique_variable), context)
- agenda.mark_alls_fresh()
- return self._attempt_proof(
- agenda, accessible_vars | set([new_unique_variable]), atoms, debug + 1
- )
- def _attempt_proof_all(
- self, current, context, agenda, accessible_vars, atoms, debug
- ):
- try:
- current._used_vars
- except AttributeError:
- current._used_vars = set()
- # if there are accessible_vars on the path
- if accessible_vars:
- # get the set of bound variables that have not be used by this AllExpression
- bv_available = accessible_vars - current._used_vars
- if bv_available:
- variable_to_use = list(bv_available)[0]
- debug.line('--> Using \'%s\'' % variable_to_use, 2)
- current._used_vars |= set([variable_to_use])
- agenda.put(
- current.term.replace(current.variable, variable_to_use), context
- )
- agenda[Categories.ALL].add((current, context))
- return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1)
- else:
- # no more available variables to substitute
- debug.line('--> Variables Exhausted', 2)
- current._exhausted = True
- agenda[Categories.ALL].add((current, context))
- return self._attempt_proof(agenda, accessible_vars, atoms, debug + 1)
- else:
- new_unique_variable = VariableExpression(unique_variable())
- debug.line('--> Using \'%s\'' % new_unique_variable, 2)
- current._used_vars |= set([new_unique_variable])
- agenda.put(
- current.term.replace(current.variable, new_unique_variable), context
- )
- agenda[Categories.ALL].add((current, context))
- agenda.mark_alls_fresh()
- return self._attempt_proof(
- agenda, accessible_vars | set([new_unique_variable]), atoms, debug + 1
- )
- @staticmethod
- def is_atom(e):
- if isinstance(e, NegatedExpression):
- e = e.term
- if isinstance(e, ApplicationExpression):
- for arg in e.args:
- if not TableauProver.is_atom(arg):
- return False
- return True
- elif isinstance(e, AbstractVariableExpression) or isinstance(
- e, LambdaExpression
- ):
- return True
- else:
- return False
- class TableauProverCommand(BaseProverCommand):
- def __init__(self, goal=None, assumptions=None, prover=None):
- """
- :param goal: Input expression to prove
- :type goal: sem.Expression
- :param assumptions: Input expressions to use as assumptions in
- the proof.
- :type assumptions: list(sem.Expression)
- """
- if prover is not None:
- assert isinstance(prover, TableauProver)
- else:
- prover = TableauProver()
- BaseProverCommand.__init__(self, prover, goal, assumptions)
- class Agenda(object):
- def __init__(self):
- self.sets = tuple(set() for i in range(21))
- def clone(self):
- new_agenda = Agenda()
- set_list = [s.copy() for s in self.sets]
- new_allExs = set()
- for allEx, _ in set_list[Categories.ALL]:
- new_allEx = AllExpression(allEx.variable, allEx.term)
- try:
- new_allEx._used_vars = set(used for used in allEx._used_vars)
- except AttributeError:
- new_allEx._used_vars = set()
- new_allExs.add((new_allEx, None))
- set_list[Categories.ALL] = new_allExs
- set_list[Categories.N_EQ] = set(
- (NegatedExpression(n_eq.term), ctx)
- for (n_eq, ctx) in set_list[Categories.N_EQ]
- )
- new_agenda.sets = tuple(set_list)
- return new_agenda
- def __getitem__(self, index):
- return self.sets[index]
- def put(self, expression, context=None):
- if isinstance(expression, AllExpression):
- ex_to_add = AllExpression(expression.variable, expression.term)
- try:
- ex_to_add._used_vars = set(used for used in expression._used_vars)
- except AttributeError:
- ex_to_add._used_vars = set()
- else:
- ex_to_add = expression
- self.sets[self._categorize_expression(ex_to_add)].add((ex_to_add, context))
- def put_all(self, expressions):
- for expression in expressions:
- self.put(expression)
- def put_atoms(self, atoms):
- for atom, neg in atoms:
- if neg:
- self[Categories.N_ATOM].add((-atom, None))
- else:
- self[Categories.ATOM].add((atom, None))
- def pop_first(self):
- """ Pop the first expression that appears in the agenda """
- for i, s in enumerate(self.sets):
- if s:
- if i in [Categories.N_EQ, Categories.ALL]:
- for ex in s:
- try:
- if not ex[0]._exhausted:
- s.remove(ex)
- return (ex, i)
- except AttributeError:
- s.remove(ex)
- return (ex, i)
- else:
- return (s.pop(), i)
- return ((None, None), None)
- def replace_all(self, old, new):
- for s in self.sets:
- for ex, ctx in s:
- ex.replace(old.variable, new)
- if ctx is not None:
- ctx.replace(old.variable, new)
- def mark_alls_fresh(self):
- for u, _ in self.sets[Categories.ALL]:
- u._exhausted = False
- def mark_neqs_fresh(self):
- for neq, _ in self.sets[Categories.N_EQ]:
- neq._exhausted = False
- def _categorize_expression(self, current):
- if isinstance(current, NegatedExpression):
- return self._categorize_NegatedExpression(current)
- elif isinstance(current, FunctionVariableExpression):
- return Categories.PROP
- elif TableauProver.is_atom(current):
- return Categories.ATOM
- elif isinstance(current, AllExpression):
- return Categories.ALL
- elif isinstance(current, AndExpression):
- return Categories.AND
- elif isinstance(current, OrExpression):
- return Categories.OR
- elif isinstance(current, ImpExpression):
- return Categories.IMP
- elif isinstance(current, IffExpression):
- return Categories.IFF
- elif isinstance(current, EqualityExpression):
- return Categories.EQ
- elif isinstance(current, ExistsExpression):
- return Categories.EXISTS
- elif isinstance(current, ApplicationExpression):
- return Categories.APP
- else:
- raise ProverParseError("cannot categorize %s" % current.__class__.__name__)
- def _categorize_NegatedExpression(self, current):
- negated = current.term
- if isinstance(negated, NegatedExpression):
- return Categories.D_NEG
- elif isinstance(negated, FunctionVariableExpression):
- return Categories.N_PROP
- elif TableauProver.is_atom(negated):
- return Categories.N_ATOM
- elif isinstance(negated, AllExpression):
- return Categories.N_ALL
- elif isinstance(negated, AndExpression):
- return Categories.N_AND
- elif isinstance(negated, OrExpression):
- return Categories.N_OR
- elif isinstance(negated, ImpExpression):
- return Categories.N_IMP
- elif isinstance(negated, IffExpression):
- return Categories.N_IFF
- elif isinstance(negated, EqualityExpression):
- return Categories.N_EQ
- elif isinstance(negated, ExistsExpression):
- return Categories.N_EXISTS
- elif isinstance(negated, ApplicationExpression):
- return Categories.N_APP
- else:
- raise ProverParseError("cannot categorize %s" % negated.__class__.__name__)
- class Debug(object):
- def __init__(self, verbose, indent=0, lines=None):
- self.verbose = verbose
- self.indent = indent
- if not lines:
- lines = []
- self.lines = lines
- def __add__(self, increment):
- return Debug(self.verbose, self.indent + 1, self.lines)
- def line(self, data, indent=0):
- if isinstance(data, tuple):
- ex, ctx = data
- if ctx:
- data = '%s, %s' % (ex, ctx)
- else:
- data = '%s' % ex
- if isinstance(ex, AllExpression):
- try:
- used_vars = "[%s]" % (
- ",".join("%s" % ve.variable.name for ve in ex._used_vars)
- )
- data += ': %s' % used_vars
- except AttributeError:
- data += ': []'
- newline = '%s%s' % (' ' * (self.indent + indent), data)
- self.lines.append(newline)
- if self.verbose:
- print(newline)
- class Categories(object):
- ATOM = 0
- PROP = 1
- N_ATOM = 2
- N_PROP = 3
- APP = 4
- N_APP = 5
- N_EQ = 6
- D_NEG = 7
- N_ALL = 8
- N_EXISTS = 9
- AND = 10
- N_OR = 11
- N_IMP = 12
- OR = 13
- IMP = 14
- N_AND = 15
- IFF = 16
- N_IFF = 17
- EQ = 18
- EXISTS = 19
- ALL = 20
- def testTableauProver():
- tableau_test('P | -P')
- tableau_test('P & -P')
- tableau_test('Q', ['P', '(P -> Q)'])
- tableau_test('man(x)')
- tableau_test('(man(x) -> man(x))')
- tableau_test('(man(x) -> --man(x))')
- tableau_test('-(man(x) and -man(x))')
- tableau_test('(man(x) or -man(x))')
- tableau_test('(man(x) -> man(x))')
- tableau_test('-(man(x) and -man(x))')
- tableau_test('(man(x) or -man(x))')
- tableau_test('(man(x) -> man(x))')
- tableau_test('(man(x) iff man(x))')
- tableau_test('-(man(x) iff -man(x))')
- tableau_test('all x.man(x)')
- tableau_test('all x.all y.((x = y) -> (y = x))')
- tableau_test('all x.all y.all z.(((x = y) & (y = z)) -> (x = z))')
- # tableau_test('-all x.some y.F(x,y) & some x.all y.(-F(x,y))')
- # tableau_test('some x.all y.sees(x,y)')
- p1 = 'all x.(man(x) -> mortal(x))'
- p2 = 'man(Socrates)'
- c = 'mortal(Socrates)'
- tableau_test(c, [p1, p2])
- p1 = 'all x.(man(x) -> walks(x))'
- p2 = 'man(John)'
- c = 'some y.walks(y)'
- tableau_test(c, [p1, p2])
- p = '((x = y) & walks(y))'
- c = 'walks(x)'
- tableau_test(c, [p])
- p = '((x = y) & ((y = z) & (z = w)))'
- c = '(x = w)'
- tableau_test(c, [p])
- p = 'some e1.some e2.(believe(e1,john,e2) & walk(e2,mary))'
- c = 'some e0.walk(e0,mary)'
- tableau_test(c, [p])
- c = '(exists x.exists z3.((x = Mary) & ((z3 = John) & sees(z3,x))) <-> exists x.exists z4.((x = John) & ((z4 = Mary) & sees(x,z4))))'
- tableau_test(c)
- # p = 'some e1.some e2.((believe e1 john e2) and (walk e2 mary))'
- # c = 'some x.some e3.some e4.((believe e3 x e4) and (walk e4 mary))'
- # tableau_test(c, [p])
- def testHigherOrderTableauProver():
- tableau_test('believe(j, -lie(b))', ['believe(j, -lie(b) & -cheat(b))'])
- tableau_test('believe(j, lie(b) & cheat(b))', ['believe(j, lie(b))'])
- tableau_test(
- 'believe(j, lie(b))', ['lie(b)']
- ) # how do we capture that John believes all things that are true
- tableau_test(
- 'believe(j, know(b, cheat(b)))',
- ['believe(j, know(b, lie(b)) & know(b, steals(b) & cheat(b)))'],
- )
- tableau_test('P(Q(y), R(y) & R(z))', ['P(Q(x) & Q(y), R(y) & R(z))'])
- tableau_test('believe(j, cheat(b) & lie(b))', ['believe(j, lie(b) & cheat(b))'])
- tableau_test('believe(j, -cheat(b) & -lie(b))', ['believe(j, -lie(b) & -cheat(b))'])
- def tableau_test(c, ps=None, verbose=False):
- pc = Expression.fromstring(c)
- pps = [Expression.fromstring(p) for p in ps] if ps else []
- if not ps:
- ps = []
- print(
- '%s |- %s: %s'
- % (', '.join(ps), pc, TableauProver().prove(pc, pps, verbose=verbose))
- )
- def demo():
- testTableauProver()
- testHigherOrderTableauProver()
- if __name__ == '__main__':
- demo()
|