123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621 |
- # Natural Language Toolkit: Classifier Interface
- #
- # Author: Ewan Klein <ewan@inf.ed.ac.uk>
- # Dan Garrette <dhgarrette@gmail.com>
- #
- # URL: <http://nltk.org/>
- # For license information, see LICENSE.TXT
- """
- Interfaces and base classes for theorem provers and model builders.
- ``Prover`` is a standard interface for a theorem prover which tries to prove a goal from a
- list of assumptions.
- ``ModelBuilder`` is a standard interface for a model builder. Given just a set of assumptions.
- the model builder tries to build a model for the assumptions. Given a set of assumptions and a
- goal *G*, the model builder tries to find a counter-model, in the sense of a model that will satisfy
- the assumptions plus the negation of *G*.
- """
- from __future__ import print_function
- from abc import ABCMeta, abstractmethod
- import threading
- import time
- from six import add_metaclass
- @add_metaclass(ABCMeta)
- class Prover(object):
- """
- Interface for trying to prove a goal from assumptions. Both the goal and
- the assumptions are constrained to be formulas of ``logic.Expression``.
- """
- def prove(self, goal=None, assumptions=None, verbose=False):
- """
- :return: Whether the proof was successful or not.
- :rtype: bool
- """
- return self._prove(goal, assumptions, verbose)[0]
- @abstractmethod
- def _prove(self, goal=None, assumptions=None, verbose=False):
- """
- :return: Whether the proof was successful or not, along with the proof
- :rtype: tuple: (bool, str)
- """
- @add_metaclass(ABCMeta)
- class ModelBuilder(object):
- """
- Interface for trying to build a model of set of formulas.
- Open formulas are assumed to be universally quantified.
- Both the goal and the assumptions are constrained to be formulas
- of ``logic.Expression``.
- """
- def build_model(self, goal=None, assumptions=None, verbose=False):
- """
- Perform the actual model building.
- :return: Whether a model was generated
- :rtype: bool
- """
- return self._build_model(goal, assumptions, verbose)[0]
- @abstractmethod
- def _build_model(self, goal=None, assumptions=None, verbose=False):
- """
- Perform the actual model building.
- :return: Whether a model was generated, and the model itself
- :rtype: tuple(bool, sem.Valuation)
- """
- @add_metaclass(ABCMeta)
- class TheoremToolCommand(object):
- """
- This class holds a goal and a list of assumptions to be used in proving
- or model building.
- """
- @abstractmethod
- def add_assumptions(self, new_assumptions):
- """
- Add new assumptions to the assumption list.
- :param new_assumptions: new assumptions
- :type new_assumptions: list(sem.Expression)
- """
- @abstractmethod
- def retract_assumptions(self, retracted, debug=False):
- """
- Retract assumptions from the assumption list.
- :param debug: If True, give warning when ``retracted`` is not present on
- assumptions list.
- :type debug: bool
- :param retracted: assumptions to be retracted
- :type retracted: list(sem.Expression)
- """
- @abstractmethod
- def assumptions(self):
- """
- List the current assumptions.
- :return: list of ``Expression``
- """
- @abstractmethod
- def goal(self):
- """
- Return the goal
- :return: ``Expression``
- """
- @abstractmethod
- def print_assumptions(self):
- """
- Print the list of the current assumptions.
- """
- class ProverCommand(TheoremToolCommand):
- """
- This class holds a ``Prover``, a goal, and a list of assumptions. When
- prove() is called, the ``Prover`` is executed with the goal and assumptions.
- """
- @abstractmethod
- def prove(self, verbose=False):
- """
- Perform the actual proof.
- """
- @abstractmethod
- def proof(self, simplify=True):
- """
- Return the proof string
- :param simplify: bool simplify the proof?
- :return: str
- """
- @abstractmethod
- def get_prover(self):
- """
- Return the prover object
- :return: ``Prover``
- """
- class ModelBuilderCommand(TheoremToolCommand):
- """
- This class holds a ``ModelBuilder``, a goal, and a list of assumptions.
- When build_model() is called, the ``ModelBuilder`` is executed with the goal
- and assumptions.
- """
- @abstractmethod
- def build_model(self, verbose=False):
- """
- Perform the actual model building.
- :return: A model if one is generated; None otherwise.
- :rtype: sem.Valuation
- """
- @abstractmethod
- def model(self, format=None):
- """
- Return a string representation of the model
- :param simplify: bool simplify the proof?
- :return: str
- """
- @abstractmethod
- def get_model_builder(self):
- """
- Return the model builder object
- :return: ``ModelBuilder``
- """
- class BaseTheoremToolCommand(TheoremToolCommand):
- """
- This class holds a goal and a list of assumptions to be used in proving
- or model building.
- """
- def __init__(self, goal=None, assumptions=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)
- """
- self._goal = goal
- if not assumptions:
- self._assumptions = []
- else:
- self._assumptions = list(assumptions)
- self._result = None
- """A holder for the result, to prevent unnecessary re-proving"""
- def add_assumptions(self, new_assumptions):
- """
- Add new assumptions to the assumption list.
- :param new_assumptions: new assumptions
- :type new_assumptions: list(sem.Expression)
- """
- self._assumptions.extend(new_assumptions)
- self._result = None
- def retract_assumptions(self, retracted, debug=False):
- """
- Retract assumptions from the assumption list.
- :param debug: If True, give warning when ``retracted`` is not present on
- assumptions list.
- :type debug: bool
- :param retracted: assumptions to be retracted
- :type retracted: list(sem.Expression)
- """
- retracted = set(retracted)
- result_list = list(filter(lambda a: a not in retracted, self._assumptions))
- if debug and result_list == self._assumptions:
- print(Warning("Assumptions list has not been changed:"))
- self.print_assumptions()
- self._assumptions = result_list
- self._result = None
- def assumptions(self):
- """
- List the current assumptions.
- :return: list of ``Expression``
- """
- return self._assumptions
- def goal(self):
- """
- Return the goal
- :return: ``Expression``
- """
- return self._goal
- def print_assumptions(self):
- """
- Print the list of the current assumptions.
- """
- for a in self.assumptions():
- print(a)
- class BaseProverCommand(BaseTheoremToolCommand, ProverCommand):
- """
- This class holds a ``Prover``, a goal, and a list of assumptions. When
- prove() is called, the ``Prover`` is executed with the goal and assumptions.
- """
- def __init__(self, prover, goal=None, assumptions=None):
- """
- :param prover: The theorem tool to execute with the assumptions
- :type prover: Prover
- :see: ``BaseTheoremToolCommand``
- """
- self._prover = prover
- """The theorem tool to execute with the assumptions"""
- BaseTheoremToolCommand.__init__(self, goal, assumptions)
- self._proof = None
- def prove(self, verbose=False):
- """
- Perform the actual proof. Store the result to prevent unnecessary
- re-proving.
- """
- if self._result is None:
- self._result, self._proof = self._prover._prove(
- self.goal(), self.assumptions(), verbose
- )
- return self._result
- def proof(self, simplify=True):
- """
- Return the proof string
- :param simplify: bool simplify the proof?
- :return: str
- """
- if self._result is None:
- raise LookupError("You have to call prove() first to get a proof!")
- else:
- return self.decorate_proof(self._proof, simplify)
- def decorate_proof(self, proof_string, simplify=True):
- """
- Modify and return the proof string
- :param proof_string: str the proof to decorate
- :param simplify: bool simplify the proof?
- :return: str
- """
- return proof_string
- def get_prover(self):
- return self._prover
- class BaseModelBuilderCommand(BaseTheoremToolCommand, ModelBuilderCommand):
- """
- This class holds a ``ModelBuilder``, a goal, and a list of assumptions. When
- build_model() is called, the ``ModelBuilder`` is executed with the goal and
- assumptions.
- """
- def __init__(self, modelbuilder, goal=None, assumptions=None):
- """
- :param modelbuilder: The theorem tool to execute with the assumptions
- :type modelbuilder: ModelBuilder
- :see: ``BaseTheoremToolCommand``
- """
- self._modelbuilder = modelbuilder
- """The theorem tool to execute with the assumptions"""
- BaseTheoremToolCommand.__init__(self, goal, assumptions)
- self._model = None
- def build_model(self, verbose=False):
- """
- Attempt to build a model. Store the result to prevent unnecessary
- re-building.
- """
- if self._result is None:
- self._result, self._model = self._modelbuilder._build_model(
- self.goal(), self.assumptions(), verbose
- )
- return self._result
- def model(self, format=None):
- """
- Return a string representation of the model
- :param simplify: bool simplify the proof?
- :return: str
- """
- if self._result is None:
- raise LookupError('You have to call build_model() first to ' 'get a model!')
- else:
- return self._decorate_model(self._model, format)
- def _decorate_model(self, valuation_str, format=None):
- """
- :param valuation_str: str with the model builder's output
- :param format: str indicating the format for displaying
- :return: str
- """
- return valuation_str
- def get_model_builder(self):
- return self._modelbuilder
- class TheoremToolCommandDecorator(TheoremToolCommand):
- """
- A base decorator for the ``ProverCommandDecorator`` and
- ``ModelBuilderCommandDecorator`` classes from which decorators can extend.
- """
- def __init__(self, command):
- """
- :param command: ``TheoremToolCommand`` to decorate
- """
- self._command = command
- # The decorator has its own versions of 'result' different from the
- # underlying command
- self._result = None
- def assumptions(self):
- return self._command.assumptions()
- def goal(self):
- return self._command.goal()
- def add_assumptions(self, new_assumptions):
- self._command.add_assumptions(new_assumptions)
- self._result = None
- def retract_assumptions(self, retracted, debug=False):
- self._command.retract_assumptions(retracted, debug)
- self._result = None
- def print_assumptions(self):
- self._command.print_assumptions()
- class ProverCommandDecorator(TheoremToolCommandDecorator, ProverCommand):
- """
- A base decorator for the ``ProverCommand`` class from which other
- prover command decorators can extend.
- """
- def __init__(self, proverCommand):
- """
- :param proverCommand: ``ProverCommand`` to decorate
- """
- TheoremToolCommandDecorator.__init__(self, proverCommand)
- # The decorator has its own versions of 'result' and 'proof'
- # because they may be different from the underlying command
- self._proof = None
- def prove(self, verbose=False):
- if self._result is None:
- prover = self.get_prover()
- self._result, self._proof = prover._prove(
- self.goal(), self.assumptions(), verbose
- )
- return self._result
- def proof(self, simplify=True):
- """
- Return the proof string
- :param simplify: bool simplify the proof?
- :return: str
- """
- if self._result is None:
- raise LookupError("You have to call prove() first to get a proof!")
- else:
- return self.decorate_proof(self._proof, simplify)
- def decorate_proof(self, proof_string, simplify=True):
- """
- Modify and return the proof string
- :param proof_string: str the proof to decorate
- :param simplify: bool simplify the proof?
- :return: str
- """
- return self._command.decorate_proof(proof_string, simplify)
- def get_prover(self):
- return self._command.get_prover()
- class ModelBuilderCommandDecorator(TheoremToolCommandDecorator, ModelBuilderCommand):
- """
- A base decorator for the ``ModelBuilderCommand`` class from which other
- prover command decorators can extend.
- """
- def __init__(self, modelBuilderCommand):
- """
- :param modelBuilderCommand: ``ModelBuilderCommand`` to decorate
- """
- TheoremToolCommandDecorator.__init__(self, modelBuilderCommand)
- # The decorator has its own versions of 'result' and 'valuation'
- # because they may be different from the underlying command
- self._model = None
- def build_model(self, verbose=False):
- """
- Attempt to build a model. Store the result to prevent unnecessary
- re-building.
- """
- if self._result is None:
- modelbuilder = self.get_model_builder()
- self._result, self._model = modelbuilder._build_model(
- self.goal(), self.assumptions(), verbose
- )
- return self._result
- def model(self, format=None):
- """
- Return a string representation of the model
- :param simplify: bool simplify the proof?
- :return: str
- """
- if self._result is None:
- raise LookupError('You have to call build_model() first to ' 'get a model!')
- else:
- return self._decorate_model(self._model, format)
- def _decorate_model(self, valuation_str, format=None):
- """
- Modify and return the proof string
- :param valuation_str: str with the model builder's output
- :param format: str indicating the format for displaying
- :return: str
- """
- return self._command._decorate_model(valuation_str, format)
- def get_model_builder(self):
- return self._command.get_prover()
- class ParallelProverBuilder(Prover, ModelBuilder):
- """
- This class stores both a prover and a model builder and when either
- prove() or build_model() is called, then both theorem tools are run in
- parallel. Whichever finishes first, the prover or the model builder, is the
- result that will be used.
- """
- def __init__(self, prover, modelbuilder):
- self._prover = prover
- self._modelbuilder = modelbuilder
- def _prove(self, goal=None, assumptions=None, verbose=False):
- return self._run(goal, assumptions, verbose), ''
- def _build_model(self, goal=None, assumptions=None, verbose=False):
- return not self._run(goal, assumptions, verbose), ''
- def _run(self, goal, assumptions, verbose):
- # Set up two thread, Prover and ModelBuilder to run in parallel
- tp_thread = TheoremToolThread(
- lambda: self._prover.prove(goal, assumptions, verbose), verbose, 'TP'
- )
- mb_thread = TheoremToolThread(
- lambda: self._modelbuilder.build_model(goal, assumptions, verbose),
- verbose,
- 'MB',
- )
- tp_thread.start()
- mb_thread.start()
- while tp_thread.isAlive() and mb_thread.isAlive():
- # wait until either the prover or the model builder is done
- pass
- if tp_thread.result is not None:
- return tp_thread.result
- elif mb_thread.result is not None:
- return not mb_thread.result
- else:
- return None
- class ParallelProverBuilderCommand(BaseProverCommand, BaseModelBuilderCommand):
- """
- This command stores both a prover and a model builder and when either
- prove() or build_model() is called, then both theorem tools are run in
- parallel. Whichever finishes first, the prover or the model builder, is the
- result that will be used.
- Because the theorem prover result is the opposite of the model builder
- result, we will treat self._result as meaning "proof found/no model found".
- """
- def __init__(self, prover, modelbuilder, goal=None, assumptions=None):
- BaseProverCommand.__init__(self, prover, goal, assumptions)
- BaseModelBuilderCommand.__init__(self, modelbuilder, goal, assumptions)
- def prove(self, verbose=False):
- return self._run(verbose)
- def build_model(self, verbose=False):
- return not self._run(verbose)
- def _run(self, verbose):
- # Set up two thread, Prover and ModelBuilder to run in parallel
- tp_thread = TheoremToolThread(
- lambda: BaseProverCommand.prove(self, verbose), verbose, 'TP'
- )
- mb_thread = TheoremToolThread(
- lambda: BaseModelBuilderCommand.build_model(self, verbose), verbose, 'MB'
- )
- tp_thread.start()
- mb_thread.start()
- while tp_thread.isAlive() and mb_thread.isAlive():
- # wait until either the prover or the model builder is done
- pass
- if tp_thread.result is not None:
- self._result = tp_thread.result
- elif mb_thread.result is not None:
- self._result = not mb_thread.result
- return self._result
- class TheoremToolThread(threading.Thread):
- def __init__(self, command, verbose, name=None):
- threading.Thread.__init__(self)
- self._command = command
- self._result = None
- self._verbose = verbose
- self._name = name
- def run(self):
- try:
- self._result = self._command()
- if self._verbose:
- print(
- 'Thread %s finished with result %s at %s'
- % (self._name, self._result, time.localtime(time.time()))
- )
- except Exception as e:
- print(e)
- print('Thread %s completed abnormally' % (self._name))
- @property
- def result(self):
- return self._result
|