unicode-logic-kit 0.31.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- unicode_logic_kit/__init__.py +385 -0
- unicode_logic_kit/__main__.py +520 -0
- unicode_logic_kit/_deadline.py +219 -0
- unicode_logic_kit/ace/__init__.py +126 -0
- unicode_logic_kit/ace/_align.py +135 -0
- unicode_logic_kit/ace/chem_lexicon.py +128 -0
- unicode_logic_kit/ace/drs_reader.py +570 -0
- unicode_logic_kit/ace/mapping.py +666 -0
- unicode_logic_kit/ace/reverse_modal.py +138 -0
- unicode_logic_kit/ace/runner.py +551 -0
- unicode_logic_kit/ace/translate.py +452 -0
- unicode_logic_kit/ace/verbalize.py +1070 -0
- unicode_logic_kit/api.py +1284 -0
- unicode_logic_kit/atp/__init__.py +177 -0
- unicode_logic_kit/atp/_ascii_names.py +113 -0
- unicode_logic_kit/atp/_html.py +72 -0
- unicode_logic_kit/atp/_substructural_input.py +228 -0
- unicode_logic_kit/atp/_tff_problem.py +715 -0
- unicode_logic_kit/atp/_tptp_problem.py +1111 -0
- unicode_logic_kit/atp/_writer_support.py +289 -0
- unicode_logic_kit/atp/clingo_backend.py +1180 -0
- unicode_logic_kit/atp/cvc5_backend.py +1385 -0
- unicode_logic_kit/atp/eprover_backend.py +732 -0
- unicode_logic_kit/atp/finite_domain.py +1055 -0
- unicode_logic_kit/atp/fitch.py +1547 -0
- unicode_logic_kit/atp/fitch_search.py +551 -0
- unicode_logic_kit/atp/hets_backend.py +339 -0
- unicode_logic_kit/atp/hybrid_down.py +120 -0
- unicode_logic_kit/atp/incremental.py +250 -0
- unicode_logic_kit/atp/kripke_enum.py +741 -0
- unicode_logic_kit/atp/lambek.py +436 -0
- unicode_logic_kit/atp/leo3_backend.py +332 -0
- unicode_logic_kit/atp/linear.py +738 -0
- unicode_logic_kit/atp/lj.py +705 -0
- unicode_logic_kit/atp/logic_backends.py +566 -0
- unicode_logic_kit/atp/ltl_tableau.py +1084 -0
- unicode_logic_kit/atp/minizinc_backend.py +1402 -0
- unicode_logic_kit/atp/modal_tableau.py +1382 -0
- unicode_logic_kit/atp/nanocop_backend.py +410 -0
- unicode_logic_kit/atp/portfolio.py +489 -0
- unicode_logic_kit/atp/protocol.py +1803 -0
- unicode_logic_kit/atp/prover9_entailment.py +1153 -0
- unicode_logic_kit/atp/resolution.py +1376 -0
- unicode_logic_kit/atp/resolution_check.py +1114 -0
- unicode_logic_kit/atp/sequent.py +1050 -0
- unicode_logic_kit/atp/tableau.py +921 -0
- unicode_logic_kit/atp/tableau_check.py +543 -0
- unicode_logic_kit/atp/tptp_ncl.py +811 -0
- unicode_logic_kit/atp/tptp_tff.py +1546 -0
- unicode_logic_kit/atp/tstp.py +1333 -0
- unicode_logic_kit/atp/tstp_check.py +1096 -0
- unicode_logic_kit/atp/twee_backend.py +236 -0
- unicode_logic_kit/atp/twee_check.py +711 -0
- unicode_logic_kit/atp/twee_entailment.py +953 -0
- unicode_logic_kit/atp/vampire_entailment.py +540 -0
- unicode_logic_kit/atp/z3_arith.py +470 -0
- unicode_logic_kit/atp/z3_equivalence.py +36 -0
- unicode_logic_kit/atp/z3_fuzzy.py +362 -0
- unicode_logic_kit/atp/z3_input.py +500 -0
- unicode_logic_kit/atp/z3_models.py +208 -0
- unicode_logic_kit/chem/__init__.py +88 -0
- unicode_logic_kit/chem/_naming.py +284 -0
- unicode_logic_kit/chem/cache.py +185 -0
- unicode_logic_kit/chem/interop.py +244 -0
- unicode_logic_kit/chem/mol.py +525 -0
- unicode_logic_kit/chem/signature.py +112 -0
- unicode_logic_kit/comorphism.py +497 -0
- unicode_logic_kit/dl/__init__.py +384 -0
- unicode_logic_kit/dl/classification.py +227 -0
- unicode_logic_kit/dl/concepts.py +632 -0
- unicode_logic_kit/dl/datatypes.py +818 -0
- unicode_logic_kit/dl/owl_functional.py +2433 -0
- unicode_logic_kit/dl/owl_manchester.py +1637 -0
- unicode_logic_kit/dl/owl_reasoner.py +790 -0
- unicode_logic_kit/dl/parser.py +391 -0
- unicode_logic_kit/dl/tableau.py +4048 -0
- unicode_logic_kit/dl/translate.py +2704 -0
- unicode_logic_kit/drt/__init__.py +94 -0
- unicode_logic_kit/drt/export.py +179 -0
- unicode_logic_kit/drt/nodes.py +506 -0
- unicode_logic_kit/drt/parser.py +965 -0
- unicode_logic_kit/drt/resolve.py +195 -0
- unicode_logic_kit/drt/reverse.py +175 -0
- unicode_logic_kit/eval/__init__.py +106 -0
- unicode_logic_kit/eval/batch.py +382 -0
- unicode_logic_kit/eval/canonical.py +663 -0
- unicode_logic_kit/eval/chem_batch.py +606 -0
- unicode_logic_kit/eval/converses.py +200 -0
- unicode_logic_kit/eval/datasets/__init__.py +136 -0
- unicode_logic_kit/eval/datasets/_base.py +263 -0
- unicode_logic_kit/eval/datasets/_proofwriter_proof.py +422 -0
- unicode_logic_kit/eval/datasets/c3po.py +678 -0
- unicode_logic_kit/eval/datasets/folio.py +158 -0
- unicode_logic_kit/eval/datasets/fracas.py +418 -0
- unicode_logic_kit/eval/datasets/groves.py +191 -0
- unicode_logic_kit/eval/datasets/logicbench.py +467 -0
- unicode_logic_kit/eval/datasets/logicnli.py +303 -0
- unicode_logic_kit/eval/datasets/malls.py +133 -0
- unicode_logic_kit/eval/datasets/pfolio.py +594 -0
- unicode_logic_kit/eval/datasets/pmb.py +242 -0
- unicode_logic_kit/eval/datasets/prontoqa.py +611 -0
- unicode_logic_kit/eval/datasets/proofwriter.py +1431 -0
- unicode_logic_kit/eval/datasets/proverqa.py +674 -0
- unicode_logic_kit/eval/datasets/willow.py +478 -0
- unicode_logic_kit/eval/equivalence.py +466 -0
- unicode_logic_kit/eval/exercise_gen.py +533 -0
- unicode_logic_kit/eval/explain.py +791 -0
- unicode_logic_kit/eval/generality.py +750 -0
- unicode_logic_kit/eval/metric_hf.py +458 -0
- unicode_logic_kit/eval/predicate_match.py +343 -0
- unicode_logic_kit/eval/theory_check.py +1170 -0
- unicode_logic_kit/eval/validate.py +306 -0
- unicode_logic_kit/fol/__init__.py +177 -0
- unicode_logic_kit/fol/_atom_keys.py +510 -0
- unicode_logic_kit/fol/_fol_nodes.py +3586 -0
- unicode_logic_kit/fol/_free_parameters.py +105 -0
- unicode_logic_kit/fol/_ho_nodes.py +448 -0
- unicode_logic_kit/fol/_hybrid_nodes.py +308 -0
- unicode_logic_kit/fol/_identifiers.py +1091 -0
- unicode_logic_kit/fol/_lambek_nodes.py +112 -0
- unicode_logic_kit/fol/_linear_nodes.py +352 -0
- unicode_logic_kit/fol/_modal_nodes.py +1467 -0
- unicode_logic_kit/fol/_msfl_nodes.py +2196 -0
- unicode_logic_kit/fol/_numeral_symbols.py +231 -0
- unicode_logic_kit/fol/_so_nodes.py +200 -0
- unicode_logic_kit/fol/_symbol_names.py +81 -0
- unicode_logic_kit/fol/_team_nodes.py +181 -0
- unicode_logic_kit/fol/_tptp_symbols.py +551 -0
- unicode_logic_kit/fol/_truth_constants.py +117 -0
- unicode_logic_kit/fol/casl_export.py +1135 -0
- unicode_logic_kit/fol/casl_import.py +929 -0
- unicode_logic_kit/fol/derivation.py +367 -0
- unicode_logic_kit/fol/dialect_detect.py +70 -0
- unicode_logic_kit/fol/dialect_repair.py +537 -0
- unicode_logic_kit/fol/frames.py +637 -0
- unicode_logic_kit/fol/grammars/terminals.lark +31 -0
- unicode_logic_kit/fol/lambda_tools.py +297 -0
- unicode_logic_kit/fol/latex_input.py +429 -0
- unicode_logic_kit/fol/modal_translation.py +944 -0
- unicode_logic_kit/fol/msflparser.py +1033 -0
- unicode_logic_kit/fol/naming.py +422 -0
- unicode_logic_kit/fol/nodes.py +241 -0
- unicode_logic_kit/fol/normalforms.py +492 -0
- unicode_logic_kit/fol/pal.py +287 -0
- unicode_logic_kit/fol/prolog_export.py +566 -0
- unicode_logic_kit/fol/prolog_input.py +505 -0
- unicode_logic_kit/fol/prover9_input.py +1325 -0
- unicode_logic_kit/fol/qml.py +1760 -0
- unicode_logic_kit/fol/qmltp_input.py +525 -0
- unicode_logic_kit/fol/sanitize.py +221 -0
- unicode_logic_kit/fol/serialize.py +79 -0
- unicode_logic_kit/fol/signature.py +1290 -0
- unicode_logic_kit/fol/simplify_check.py +544 -0
- unicode_logic_kit/fol/spans.py +594 -0
- unicode_logic_kit/fol/tptp_input.py +1503 -0
- unicode_logic_kit/fol/tptp_repair.py +941 -0
- unicode_logic_kit/fol/unification.py +157 -0
- unicode_logic_kit/fol/verbalize.py +263 -0
- unicode_logic_kit/hets/__init__.py +163 -0
- unicode_logic_kit/hets/bridge.py +142 -0
- unicode_logic_kit/hets/client.py +748 -0
- unicode_logic_kit/hets/docker.py +420 -0
- unicode_logic_kit/hets/dol.py +712 -0
- unicode_logic_kit/hets/haskell_json.py +355 -0
- unicode_logic_kit/hets/owl_backend.py +794 -0
- unicode_logic_kit/hets/owl_cli.py +598 -0
- unicode_logic_kit/hets/symbols.py +512 -0
- unicode_logic_kit/hol/__init__.py +140 -0
- unicode_logic_kit/hol/_ho_common.py +323 -0
- unicode_logic_kit/hol/_isabelle_binders.py +125 -0
- unicode_logic_kit/hol/classical.py +812 -0
- unicode_logic_kit/hol/deepshallow/__init__.py +45 -0
- unicode_logic_kit/hol/deepshallow/_common.py +177 -0
- unicode_logic_kit/hol/deepshallow/conditional.py +225 -0
- unicode_logic_kit/hol/deepshallow/intuitionistic.py +181 -0
- unicode_logic_kit/hol/deepshallow/modal.py +217 -0
- unicode_logic_kit/hol/deepshallow/qml.py +406 -0
- unicode_logic_kit/hol/deepshallow/relevant.py +206 -0
- unicode_logic_kit/hol/free.py +753 -0
- unicode_logic_kit/hol/goedel.py +336 -0
- unicode_logic_kit/hol/ho_modal.py +1743 -0
- unicode_logic_kit/hol/intuitionistic.py +403 -0
- unicode_logic_kit/hol/isabelle_conditional.py +593 -0
- unicode_logic_kit/hol/isabelle_modal.py +1908 -0
- unicode_logic_kit/hol/isabelle_relevant.py +412 -0
- unicode_logic_kit/hol/isabelle_runner.py +1147 -0
- unicode_logic_kit/hol/isabelle_substructural.py +884 -0
- unicode_logic_kit/hol/lean.py +1018 -0
- unicode_logic_kit/hol/manyvalued.py +921 -0
- unicode_logic_kit/hol/secondorder.py +687 -0
- unicode_logic_kit/hol/thf_modal.py +941 -0
- unicode_logic_kit/hol/thirdorder.py +397 -0
- unicode_logic_kit/ilp/__init__.py +89 -0
- unicode_logic_kit/ilp/readback.py +389 -0
- unicode_logic_kit/ilp/separation.py +153 -0
- unicode_logic_kit/ilp/task.py +730 -0
- unicode_logic_kit/logic.py +163 -0
- unicode_logic_kit/mcp/__init__.py +28 -0
- unicode_logic_kit/mcp/__main__.py +5 -0
- unicode_logic_kit/mcp/chem_tools.py +1031 -0
- unicode_logic_kit/mcp/server.py +2453 -0
- unicode_logic_kit/mcp/syntax_spec.py +681 -0
- unicode_logic_kit/prob/__init__.py +53 -0
- unicode_logic_kit/prob/_bdd.py +225 -0
- unicode_logic_kit/prob/_column_gen.py +668 -0
- unicode_logic_kit/prob/distribution.py +686 -0
- unicode_logic_kit/prob/nilsson.py +470 -0
- unicode_logic_kit/py.typed +0 -0
- unicode_logic_kit/semantics/__init__.py +137 -0
- unicode_logic_kit/semantics/_modal_reject.py +156 -0
- unicode_logic_kit/semantics/action_models.py +466 -0
- unicode_logic_kit/semantics/asp_models.py +1200 -0
- unicode_logic_kit/semantics/conditional.py +580 -0
- unicode_logic_kit/semantics/dynamic_epistemic.py +95 -0
- unicode_logic_kit/semantics/free_logic.py +913 -0
- unicode_logic_kit/semantics/fuzzy.py +384 -0
- unicode_logic_kit/semantics/fuzzy_kripke.py +442 -0
- unicode_logic_kit/semantics/intuitionistic.py +581 -0
- unicode_logic_kit/semantics/kripke.py +1139 -0
- unicode_logic_kit/semantics/manyvalued.py +580 -0
- unicode_logic_kit/semantics/matrix.py +342 -0
- unicode_logic_kit/semantics/model_eval.py +1135 -0
- unicode_logic_kit/semantics/modelfinder.py +1036 -0
- unicode_logic_kit/semantics/nonmonotonic.py +372 -0
- unicode_logic_kit/semantics/relevant.py +331 -0
- unicode_logic_kit/semantics/secondorder.py +657 -0
- unicode_logic_kit/semantics/structures.py +352 -0
- unicode_logic_kit/semantics/tarski.py +975 -0
- unicode_logic_kit/semantics/team.py +315 -0
- unicode_logic_kit/semantics/team_translation.py +416 -0
- unicode_logic_kit/semantics/thirdorder.py +358 -0
- unicode_logic_kit/semantics/tnorm.py +85 -0
- unicode_logic_kit/semantics/truthtable.py +201 -0
- unicode_logic_kit-0.31.0.dist-info/METADATA +333 -0
- unicode_logic_kit-0.31.0.dist-info/RECORD +237 -0
- unicode_logic_kit-0.31.0.dist-info/WHEEL +4 -0
- unicode_logic_kit-0.31.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
"""A persistent, push/pop Z3 session for many goals against one premise set.
|
|
2
|
+
|
|
3
|
+
Every OTHER Z3 route in this kit (:mod:`unicode_logic_kit.atp.z3_models`,
|
|
4
|
+
:class:`unicode_logic_kit.atp.protocol.Z3Backend`) builds a brand-new
|
|
5
|
+
``z3.Solver()`` per call: correct, but wasteful when the SAME premise set is
|
|
6
|
+
checked against many different goals in a row — the real workload of
|
|
7
|
+
``eval.datasets.fracas``/``prontoqa``/``proverqa`` (a paired verdict/negated
|
|
8
|
+
``api.prove()`` call per example, same premises each time) and
|
|
9
|
+
``eval.datasets.proofwriter``'s ``atom_oracle`` loop (many single-atom goals
|
|
10
|
+
against one fixed theory). :class:`IncrementalSession` keeps ONE ``z3.Solver``
|
|
11
|
+
alive across those calls and uses Z3's native push/pop scope stack so the
|
|
12
|
+
premise set only has to be re-asserted when it actually changes.
|
|
13
|
+
|
|
14
|
+
**Not a decision route.** This is a standalone utility, deliberately NOT a
|
|
15
|
+
:class:`~unicode_logic_kit.atp.protocol.ProverBackend` and NOT registered or
|
|
16
|
+
reachable through :func:`~unicode_logic_kit.api.prove`'s backend chain — it
|
|
17
|
+
answers a narrower question (repeated queries against a mutable-by-push/pop
|
|
18
|
+
premise stack) that the uniform one-shot ``decide(formula, premises)``
|
|
19
|
+
contract does not express. Wiring a caller like ``atom_oracle`` to use this
|
|
20
|
+
instead of repeated stateless ``api.prove()`` calls is a reasonable follow-up,
|
|
21
|
+
kept out of this module on purpose.
|
|
22
|
+
|
|
23
|
+
**LIFO only.** Z3's ``Solver.push()``/``pop()`` is a stack: you can only
|
|
24
|
+
retract the MOST RECENTLY asserted premise, never an arbitrary earlier one
|
|
25
|
+
while keeping the rest — see :meth:`IncrementalSession.retract`. Arbitrary
|
|
26
|
+
(non-LIFO) premise editing needs Z3's assumption-literal pattern
|
|
27
|
+
(``solver.add(Implies(a_i, premise_i)); solver.check(a_1, ..., a_n)``)
|
|
28
|
+
instead of push/pop; that is a different API and out of scope here.
|
|
29
|
+
|
|
30
|
+
**Many-sorted (MSFOL) soundness.** Exactly like :mod:`z3_models` and
|
|
31
|
+
:class:`~unicode_logic_kit.atp.protocol.Z3Backend` (see either module's
|
|
32
|
+
docstring, and the classical-reasoning guide's many-sorted section):
|
|
33
|
+
``Node.to_z3()`` relativises a sorted quantifier/constant/count to plain
|
|
34
|
+
classical FOL but never asserts that a sort's universe is non-empty — MSFOL,
|
|
35
|
+
by convention, never gives a sort an empty one, and a sorted constant ``c:S``
|
|
36
|
+
denotes an element of ``S``, which the relativisation forgets.
|
|
37
|
+
:meth:`IncrementalSession.decide` closes both gaps the same way
|
|
38
|
+
``Z3Backend.decide`` does: at EVERY call it recomputes
|
|
39
|
+
``sort_axioms(goal, *premises)`` (a ``∃x S(x)`` per sort, an ``S(c)`` per
|
|
40
|
+
sorted constant) over the goal and the CURRENT premise set (base premises plus
|
|
41
|
+
whatever :meth:`assert_premise` has added and :meth:`retract` has not yet
|
|
42
|
+
undone) and asserts them, unnegated, inside that same call's own push/pop
|
|
43
|
+
scope — never folded into ``to_z3()`` itself (polarity-blind, shared with
|
|
44
|
+
every other caller), and never asserted on a scope :meth:`retract` could later
|
|
45
|
+
pop away with an unrelated premise: each ``decide()`` call adds and removes
|
|
46
|
+
its own copy, so the axioms are always exactly the ones the CURRENT premise
|
|
47
|
+
set and goal need, no more and no less — the same set :class:`Z3Backend`
|
|
48
|
+
would compute from scratch for an equivalent one-shot call, so the two always
|
|
49
|
+
agree. In particular, retracting the only premise that mentioned ``c:S``
|
|
50
|
+
takes ``S(c)`` away with it.
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
import time
|
|
54
|
+
from typing import Dict, List, Optional, Sequence, Tuple
|
|
55
|
+
|
|
56
|
+
from ..fol._msfl_nodes import sort_axioms
|
|
57
|
+
from ..fol.nodes import Node, Z3Env
|
|
58
|
+
from .protocol import PROVED, REFUTED, UNKNOWN, Verdict
|
|
59
|
+
from .z3_models import model_assignment
|
|
60
|
+
|
|
61
|
+
__all__ = ["IncrementalSession"]
|
|
62
|
+
|
|
63
|
+
#: The backend tag on every Verdict this session returns — distinct from the
|
|
64
|
+
#: stateless ``"z3"`` backend name (:class:`~unicode_logic_kit.atp.protocol
|
|
65
|
+
#: .Z3Backend`) so logs/caches never conflate a stateful-session answer with
|
|
66
|
+
#: a fresh-Solver-per-call one, even though both use Z3 underneath.
|
|
67
|
+
_BACKEND_NAME = "z3-incremental"
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _model_assignment(model) -> Dict[str, str]:
|
|
71
|
+
"""Read a satisfying ``z3.ModelRef`` back into a ``{name: value}`` dict.
|
|
72
|
+
|
|
73
|
+
Same shape as :func:`unicode_logic_kit.atp.z3_models.get_model`'s return
|
|
74
|
+
value. Unlike :class:`~unicode_logic_kit.atp.protocol.Z3Backend`'s own
|
|
75
|
+
model reader, no tracking-tag filtering is needed here: this session
|
|
76
|
+
never uses ``assert_and_track`` (see the module docstring — it has no
|
|
77
|
+
unsat-core proof to build), so no ``"goal"``/``"p<i>"`` Bool declaration
|
|
78
|
+
is ever present in ``model.decls()`` to begin with. A name declared more
|
|
79
|
+
than once (one name at two arities, a function and a predicate of one name)
|
|
80
|
+
is two symbols and is reported under ``"P/1"`` / ``"P/2"`` keys, as
|
|
81
|
+
:func:`unicode_logic_kit.atp.z3_models.model_assignment` does.
|
|
82
|
+
"""
|
|
83
|
+
return model_assignment(model)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class IncrementalSession:
|
|
87
|
+
"""One persistent ``z3.Solver`` for many goals against a growing/shrinking
|
|
88
|
+
premise set, using Z3's native push/pop scope stack.
|
|
89
|
+
|
|
90
|
+
Construct with the starting ("base") premises, grow the set with
|
|
91
|
+
:meth:`assert_premise`, shrink it with :meth:`retract` (LIFO — see the
|
|
92
|
+
module docstring), and decide any number of goals against whatever the
|
|
93
|
+
current premise set is with :meth:`decide`. The base premises supplied to
|
|
94
|
+
the constructor can never be retracted: they are asserted directly on the
|
|
95
|
+
solver, not on a push scope, so there is nothing for :meth:`retract` to
|
|
96
|
+
pop back to below them — calling it with nothing else on the stack raises
|
|
97
|
+
rather than reaching into the base set.
|
|
98
|
+
|
|
99
|
+
Timeout and random seed are set ONCE, at construction, exactly like every
|
|
100
|
+
other Z3 route in this kit (:mod:`unicode_logic_kit.atp.z3_models`,
|
|
101
|
+
:class:`~unicode_logic_kit.atp.protocol.Z3Backend`) — ``random_seed`` makes
|
|
102
|
+
a REFUTED verdict's countermodel reproducible across runs of the same
|
|
103
|
+
session, since Z3's model search is otherwise free to return any
|
|
104
|
+
satisfying structure it finds first.
|
|
105
|
+
"""
|
|
106
|
+
|
|
107
|
+
def __init__(self, premises: Sequence[Node] = (), *,
|
|
108
|
+
timeout: int = 10000, random_seed: int = 42) -> None:
|
|
109
|
+
from z3 import Solver
|
|
110
|
+
|
|
111
|
+
self._solver = Solver()
|
|
112
|
+
self._solver.set("timeout", timeout)
|
|
113
|
+
self._solver.set("random_seed", random_seed)
|
|
114
|
+
self._timeout = timeout
|
|
115
|
+
|
|
116
|
+
self._base_premises: Tuple[Node, ...] = tuple(premises)
|
|
117
|
+
# Translate every base premise BEFORE touching the solver, so an
|
|
118
|
+
# unsupported fragment (Node.to_z3 raising NotImplementedError, e.g.
|
|
119
|
+
# a substructural node) is refused loudly with no partial state
|
|
120
|
+
# asserted — never approximated, per the kit's fragment-refusal
|
|
121
|
+
# policy. Each premise gets its OWN solver.add call (never folded
|
|
122
|
+
# into one conjunction), so push/pop granularity in assert_premise
|
|
123
|
+
# can later align 1:1 with individual premises the same way.
|
|
124
|
+
#
|
|
125
|
+
# One Z3Env for the whole premise set (and one copy per push scope,
|
|
126
|
+
# below), so that a numeral and a constant of the same text are refused
|
|
127
|
+
# wherever they meet — see Z3Env — and retracting a premise takes its
|
|
128
|
+
# symbols' claims away with it.
|
|
129
|
+
base_env = Z3Env()
|
|
130
|
+
z3_base = [p.to_z3(base_env) for p in self._base_premises]
|
|
131
|
+
for z3_p in z3_base:
|
|
132
|
+
self._solver.add(z3_p)
|
|
133
|
+
|
|
134
|
+
# LIFO stack of premises asserted via assert_premise, each on its
|
|
135
|
+
# own push scope; retract() pops exactly one. Never includes a base
|
|
136
|
+
# premise (those have no push scope of their own — see the class
|
|
137
|
+
# docstring).
|
|
138
|
+
self._stack: List[Node] = []
|
|
139
|
+
# _envs[k] knows the base premises and the first k asserted ones.
|
|
140
|
+
self._envs: List[Z3Env] = [base_env]
|
|
141
|
+
|
|
142
|
+
@property
|
|
143
|
+
def premises(self) -> Tuple[Node, ...]:
|
|
144
|
+
"""The current premise set, base premises first, in assertion order."""
|
|
145
|
+
return self._base_premises + tuple(self._stack)
|
|
146
|
+
|
|
147
|
+
@property
|
|
148
|
+
def scope_depth(self) -> int:
|
|
149
|
+
"""The underlying solver's current push/pop scope depth.
|
|
150
|
+
|
|
151
|
+
Grows by one per live :meth:`assert_premise` call and shrinks by one
|
|
152
|
+
per :meth:`retract`; :meth:`decide` pushes and pops its own
|
|
153
|
+
transient scope internally and always leaves this unchanged before
|
|
154
|
+
vs. after — see :meth:`decide`'s docstring.
|
|
155
|
+
"""
|
|
156
|
+
return self._solver.num_scopes()
|
|
157
|
+
|
|
158
|
+
def assert_premise(self, p: Node) -> None:
|
|
159
|
+
"""Grow the premise set by ``p``, on its own new push scope.
|
|
160
|
+
|
|
161
|
+
Translates ``p`` first and only then pushes/asserts, so a fragment
|
|
162
|
+
``Node.to_z3`` cannot translate (``NotImplementedError``) is refused
|
|
163
|
+
loudly with the scope stack left exactly as it was — never a
|
|
164
|
+
half-pushed scope.
|
|
165
|
+
"""
|
|
166
|
+
env = self._envs[-1].copy()
|
|
167
|
+
z3_p = p.to_z3(env)
|
|
168
|
+
self._solver.push()
|
|
169
|
+
self._solver.add(z3_p)
|
|
170
|
+
self._stack.append(p)
|
|
171
|
+
self._envs.append(env)
|
|
172
|
+
|
|
173
|
+
def retract(self) -> Node:
|
|
174
|
+
"""Undo the most recent :meth:`assert_premise` call (LIFO) and
|
|
175
|
+
return the premise that was removed.
|
|
176
|
+
|
|
177
|
+
Z3 scopes are a stack: this can only retract the MOST RECENTLY
|
|
178
|
+
asserted premise, never an arbitrary earlier one while keeping the
|
|
179
|
+
rest — see the module docstring for the assumption-literal pattern
|
|
180
|
+
an arbitrary-retraction API would need instead. Retracting past the
|
|
181
|
+
constructor-supplied base premises raises ``ValueError`` — never a
|
|
182
|
+
silent no-op, and never an under/over ``pop()`` of the solver itself.
|
|
183
|
+
"""
|
|
184
|
+
if not self._stack:
|
|
185
|
+
raise ValueError(
|
|
186
|
+
"IncrementalSession.retract: nothing left to retract — the "
|
|
187
|
+
"constructor's base premises are never retractable (LIFO "
|
|
188
|
+
"push/pop only reaches premises added by assert_premise)")
|
|
189
|
+
self._solver.pop()
|
|
190
|
+
self._envs.pop()
|
|
191
|
+
return self._stack.pop()
|
|
192
|
+
|
|
193
|
+
def decide(self, goal: Node, *, timeout: Optional[int] = None) -> Verdict:
|
|
194
|
+
"""Decide ``self.premises ⊨ goal`` against the CURRENT premise set.
|
|
195
|
+
|
|
196
|
+
Mirrors :meth:`~unicode_logic_kit.atp.protocol.Z3Backend.decide`'s
|
|
197
|
+
body on the session's persistent solver instead of a fresh one:
|
|
198
|
+
pushes a new scope, asserts ``Not(goal)`` plus the many-sorted
|
|
199
|
+
axioms the current premises/goal need (non-emptiness and membership;
|
|
200
|
+
see the module docstring), checks, builds the same three-way PROVED/REFUTED/UNKNOWN
|
|
201
|
+
verdict :class:`~unicode_logic_kit.atp.protocol.Z3Backend` would, then
|
|
202
|
+
pops — leaving :attr:`scope_depth` exactly as it was before this
|
|
203
|
+
call, whichever branch is taken (the push/pop live in a ``finally``).
|
|
204
|
+
|
|
205
|
+
``timeout``, if given, overrides the constructor's timeout for THIS
|
|
206
|
+
call only and is restored immediately after — the constructor's
|
|
207
|
+
original value is otherwise untouched by any number of ``decide()``
|
|
208
|
+
calls in between.
|
|
209
|
+
|
|
210
|
+
An unsupported fragment (``goal.to_z3()`` or a current premise's
|
|
211
|
+
``to_z3()`` raising ``NotImplementedError``) is reported as
|
|
212
|
+
``UNKNOWN``/``"unsupported"``, exactly like ``Z3Backend.decide`` —
|
|
213
|
+
never guessed at, and never leaves a scope pushed.
|
|
214
|
+
"""
|
|
215
|
+
from z3 import Not as _z3_not, sat, unsat
|
|
216
|
+
|
|
217
|
+
current_premises = self.premises
|
|
218
|
+
try:
|
|
219
|
+
env = self._envs[-1].copy()
|
|
220
|
+
z3_goal = goal.to_z3(env)
|
|
221
|
+
axioms = [ax.to_z3(env) for ax in
|
|
222
|
+
sort_axioms(goal, *current_premises)]
|
|
223
|
+
except NotImplementedError as exc:
|
|
224
|
+
return Verdict(UNKNOWN, _BACKEND_NAME, reason="unsupported", detail=str(exc))
|
|
225
|
+
|
|
226
|
+
self._solver.push()
|
|
227
|
+
try:
|
|
228
|
+
if timeout is not None:
|
|
229
|
+
self._solver.set("timeout", timeout)
|
|
230
|
+
self._solver.add(_z3_not(z3_goal))
|
|
231
|
+
for axiom in axioms:
|
|
232
|
+
self._solver.add(axiom)
|
|
233
|
+
|
|
234
|
+
start = time.perf_counter()
|
|
235
|
+
res = self._solver.check()
|
|
236
|
+
elapsed = time.perf_counter() - start
|
|
237
|
+
|
|
238
|
+
if res == unsat:
|
|
239
|
+
return Verdict(PROVED, _BACKEND_NAME, wall_time=elapsed)
|
|
240
|
+
if res == sat:
|
|
241
|
+
assignment = _model_assignment(self._solver.model())
|
|
242
|
+
return Verdict(REFUTED, _BACKEND_NAME, wall_time=elapsed,
|
|
243
|
+
countermodel={"kind": "z3_model", "assignment": assignment})
|
|
244
|
+
why = self._solver.reason_unknown()
|
|
245
|
+
reason = "timeout" if ("timeout" in why or "cancel" in why) else "incomplete"
|
|
246
|
+
return Verdict(UNKNOWN, _BACKEND_NAME, reason=reason, wall_time=elapsed, detail=why)
|
|
247
|
+
finally:
|
|
248
|
+
self._solver.pop()
|
|
249
|
+
if timeout is not None:
|
|
250
|
+
self._solver.set("timeout", self._timeout)
|