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.
Files changed (237) hide show
  1. unicode_logic_kit/__init__.py +385 -0
  2. unicode_logic_kit/__main__.py +520 -0
  3. unicode_logic_kit/_deadline.py +219 -0
  4. unicode_logic_kit/ace/__init__.py +126 -0
  5. unicode_logic_kit/ace/_align.py +135 -0
  6. unicode_logic_kit/ace/chem_lexicon.py +128 -0
  7. unicode_logic_kit/ace/drs_reader.py +570 -0
  8. unicode_logic_kit/ace/mapping.py +666 -0
  9. unicode_logic_kit/ace/reverse_modal.py +138 -0
  10. unicode_logic_kit/ace/runner.py +551 -0
  11. unicode_logic_kit/ace/translate.py +452 -0
  12. unicode_logic_kit/ace/verbalize.py +1070 -0
  13. unicode_logic_kit/api.py +1284 -0
  14. unicode_logic_kit/atp/__init__.py +177 -0
  15. unicode_logic_kit/atp/_ascii_names.py +113 -0
  16. unicode_logic_kit/atp/_html.py +72 -0
  17. unicode_logic_kit/atp/_substructural_input.py +228 -0
  18. unicode_logic_kit/atp/_tff_problem.py +715 -0
  19. unicode_logic_kit/atp/_tptp_problem.py +1111 -0
  20. unicode_logic_kit/atp/_writer_support.py +289 -0
  21. unicode_logic_kit/atp/clingo_backend.py +1180 -0
  22. unicode_logic_kit/atp/cvc5_backend.py +1385 -0
  23. unicode_logic_kit/atp/eprover_backend.py +732 -0
  24. unicode_logic_kit/atp/finite_domain.py +1055 -0
  25. unicode_logic_kit/atp/fitch.py +1547 -0
  26. unicode_logic_kit/atp/fitch_search.py +551 -0
  27. unicode_logic_kit/atp/hets_backend.py +339 -0
  28. unicode_logic_kit/atp/hybrid_down.py +120 -0
  29. unicode_logic_kit/atp/incremental.py +250 -0
  30. unicode_logic_kit/atp/kripke_enum.py +741 -0
  31. unicode_logic_kit/atp/lambek.py +436 -0
  32. unicode_logic_kit/atp/leo3_backend.py +332 -0
  33. unicode_logic_kit/atp/linear.py +738 -0
  34. unicode_logic_kit/atp/lj.py +705 -0
  35. unicode_logic_kit/atp/logic_backends.py +566 -0
  36. unicode_logic_kit/atp/ltl_tableau.py +1084 -0
  37. unicode_logic_kit/atp/minizinc_backend.py +1402 -0
  38. unicode_logic_kit/atp/modal_tableau.py +1382 -0
  39. unicode_logic_kit/atp/nanocop_backend.py +410 -0
  40. unicode_logic_kit/atp/portfolio.py +489 -0
  41. unicode_logic_kit/atp/protocol.py +1803 -0
  42. unicode_logic_kit/atp/prover9_entailment.py +1153 -0
  43. unicode_logic_kit/atp/resolution.py +1376 -0
  44. unicode_logic_kit/atp/resolution_check.py +1114 -0
  45. unicode_logic_kit/atp/sequent.py +1050 -0
  46. unicode_logic_kit/atp/tableau.py +921 -0
  47. unicode_logic_kit/atp/tableau_check.py +543 -0
  48. unicode_logic_kit/atp/tptp_ncl.py +811 -0
  49. unicode_logic_kit/atp/tptp_tff.py +1546 -0
  50. unicode_logic_kit/atp/tstp.py +1333 -0
  51. unicode_logic_kit/atp/tstp_check.py +1096 -0
  52. unicode_logic_kit/atp/twee_backend.py +236 -0
  53. unicode_logic_kit/atp/twee_check.py +711 -0
  54. unicode_logic_kit/atp/twee_entailment.py +953 -0
  55. unicode_logic_kit/atp/vampire_entailment.py +540 -0
  56. unicode_logic_kit/atp/z3_arith.py +470 -0
  57. unicode_logic_kit/atp/z3_equivalence.py +36 -0
  58. unicode_logic_kit/atp/z3_fuzzy.py +362 -0
  59. unicode_logic_kit/atp/z3_input.py +500 -0
  60. unicode_logic_kit/atp/z3_models.py +208 -0
  61. unicode_logic_kit/chem/__init__.py +88 -0
  62. unicode_logic_kit/chem/_naming.py +284 -0
  63. unicode_logic_kit/chem/cache.py +185 -0
  64. unicode_logic_kit/chem/interop.py +244 -0
  65. unicode_logic_kit/chem/mol.py +525 -0
  66. unicode_logic_kit/chem/signature.py +112 -0
  67. unicode_logic_kit/comorphism.py +497 -0
  68. unicode_logic_kit/dl/__init__.py +384 -0
  69. unicode_logic_kit/dl/classification.py +227 -0
  70. unicode_logic_kit/dl/concepts.py +632 -0
  71. unicode_logic_kit/dl/datatypes.py +818 -0
  72. unicode_logic_kit/dl/owl_functional.py +2433 -0
  73. unicode_logic_kit/dl/owl_manchester.py +1637 -0
  74. unicode_logic_kit/dl/owl_reasoner.py +790 -0
  75. unicode_logic_kit/dl/parser.py +391 -0
  76. unicode_logic_kit/dl/tableau.py +4048 -0
  77. unicode_logic_kit/dl/translate.py +2704 -0
  78. unicode_logic_kit/drt/__init__.py +94 -0
  79. unicode_logic_kit/drt/export.py +179 -0
  80. unicode_logic_kit/drt/nodes.py +506 -0
  81. unicode_logic_kit/drt/parser.py +965 -0
  82. unicode_logic_kit/drt/resolve.py +195 -0
  83. unicode_logic_kit/drt/reverse.py +175 -0
  84. unicode_logic_kit/eval/__init__.py +106 -0
  85. unicode_logic_kit/eval/batch.py +382 -0
  86. unicode_logic_kit/eval/canonical.py +663 -0
  87. unicode_logic_kit/eval/chem_batch.py +606 -0
  88. unicode_logic_kit/eval/converses.py +200 -0
  89. unicode_logic_kit/eval/datasets/__init__.py +136 -0
  90. unicode_logic_kit/eval/datasets/_base.py +263 -0
  91. unicode_logic_kit/eval/datasets/_proofwriter_proof.py +422 -0
  92. unicode_logic_kit/eval/datasets/c3po.py +678 -0
  93. unicode_logic_kit/eval/datasets/folio.py +158 -0
  94. unicode_logic_kit/eval/datasets/fracas.py +418 -0
  95. unicode_logic_kit/eval/datasets/groves.py +191 -0
  96. unicode_logic_kit/eval/datasets/logicbench.py +467 -0
  97. unicode_logic_kit/eval/datasets/logicnli.py +303 -0
  98. unicode_logic_kit/eval/datasets/malls.py +133 -0
  99. unicode_logic_kit/eval/datasets/pfolio.py +594 -0
  100. unicode_logic_kit/eval/datasets/pmb.py +242 -0
  101. unicode_logic_kit/eval/datasets/prontoqa.py +611 -0
  102. unicode_logic_kit/eval/datasets/proofwriter.py +1431 -0
  103. unicode_logic_kit/eval/datasets/proverqa.py +674 -0
  104. unicode_logic_kit/eval/datasets/willow.py +478 -0
  105. unicode_logic_kit/eval/equivalence.py +466 -0
  106. unicode_logic_kit/eval/exercise_gen.py +533 -0
  107. unicode_logic_kit/eval/explain.py +791 -0
  108. unicode_logic_kit/eval/generality.py +750 -0
  109. unicode_logic_kit/eval/metric_hf.py +458 -0
  110. unicode_logic_kit/eval/predicate_match.py +343 -0
  111. unicode_logic_kit/eval/theory_check.py +1170 -0
  112. unicode_logic_kit/eval/validate.py +306 -0
  113. unicode_logic_kit/fol/__init__.py +177 -0
  114. unicode_logic_kit/fol/_atom_keys.py +510 -0
  115. unicode_logic_kit/fol/_fol_nodes.py +3586 -0
  116. unicode_logic_kit/fol/_free_parameters.py +105 -0
  117. unicode_logic_kit/fol/_ho_nodes.py +448 -0
  118. unicode_logic_kit/fol/_hybrid_nodes.py +308 -0
  119. unicode_logic_kit/fol/_identifiers.py +1091 -0
  120. unicode_logic_kit/fol/_lambek_nodes.py +112 -0
  121. unicode_logic_kit/fol/_linear_nodes.py +352 -0
  122. unicode_logic_kit/fol/_modal_nodes.py +1467 -0
  123. unicode_logic_kit/fol/_msfl_nodes.py +2196 -0
  124. unicode_logic_kit/fol/_numeral_symbols.py +231 -0
  125. unicode_logic_kit/fol/_so_nodes.py +200 -0
  126. unicode_logic_kit/fol/_symbol_names.py +81 -0
  127. unicode_logic_kit/fol/_team_nodes.py +181 -0
  128. unicode_logic_kit/fol/_tptp_symbols.py +551 -0
  129. unicode_logic_kit/fol/_truth_constants.py +117 -0
  130. unicode_logic_kit/fol/casl_export.py +1135 -0
  131. unicode_logic_kit/fol/casl_import.py +929 -0
  132. unicode_logic_kit/fol/derivation.py +367 -0
  133. unicode_logic_kit/fol/dialect_detect.py +70 -0
  134. unicode_logic_kit/fol/dialect_repair.py +537 -0
  135. unicode_logic_kit/fol/frames.py +637 -0
  136. unicode_logic_kit/fol/grammars/terminals.lark +31 -0
  137. unicode_logic_kit/fol/lambda_tools.py +297 -0
  138. unicode_logic_kit/fol/latex_input.py +429 -0
  139. unicode_logic_kit/fol/modal_translation.py +944 -0
  140. unicode_logic_kit/fol/msflparser.py +1033 -0
  141. unicode_logic_kit/fol/naming.py +422 -0
  142. unicode_logic_kit/fol/nodes.py +241 -0
  143. unicode_logic_kit/fol/normalforms.py +492 -0
  144. unicode_logic_kit/fol/pal.py +287 -0
  145. unicode_logic_kit/fol/prolog_export.py +566 -0
  146. unicode_logic_kit/fol/prolog_input.py +505 -0
  147. unicode_logic_kit/fol/prover9_input.py +1325 -0
  148. unicode_logic_kit/fol/qml.py +1760 -0
  149. unicode_logic_kit/fol/qmltp_input.py +525 -0
  150. unicode_logic_kit/fol/sanitize.py +221 -0
  151. unicode_logic_kit/fol/serialize.py +79 -0
  152. unicode_logic_kit/fol/signature.py +1290 -0
  153. unicode_logic_kit/fol/simplify_check.py +544 -0
  154. unicode_logic_kit/fol/spans.py +594 -0
  155. unicode_logic_kit/fol/tptp_input.py +1503 -0
  156. unicode_logic_kit/fol/tptp_repair.py +941 -0
  157. unicode_logic_kit/fol/unification.py +157 -0
  158. unicode_logic_kit/fol/verbalize.py +263 -0
  159. unicode_logic_kit/hets/__init__.py +163 -0
  160. unicode_logic_kit/hets/bridge.py +142 -0
  161. unicode_logic_kit/hets/client.py +748 -0
  162. unicode_logic_kit/hets/docker.py +420 -0
  163. unicode_logic_kit/hets/dol.py +712 -0
  164. unicode_logic_kit/hets/haskell_json.py +355 -0
  165. unicode_logic_kit/hets/owl_backend.py +794 -0
  166. unicode_logic_kit/hets/owl_cli.py +598 -0
  167. unicode_logic_kit/hets/symbols.py +512 -0
  168. unicode_logic_kit/hol/__init__.py +140 -0
  169. unicode_logic_kit/hol/_ho_common.py +323 -0
  170. unicode_logic_kit/hol/_isabelle_binders.py +125 -0
  171. unicode_logic_kit/hol/classical.py +812 -0
  172. unicode_logic_kit/hol/deepshallow/__init__.py +45 -0
  173. unicode_logic_kit/hol/deepshallow/_common.py +177 -0
  174. unicode_logic_kit/hol/deepshallow/conditional.py +225 -0
  175. unicode_logic_kit/hol/deepshallow/intuitionistic.py +181 -0
  176. unicode_logic_kit/hol/deepshallow/modal.py +217 -0
  177. unicode_logic_kit/hol/deepshallow/qml.py +406 -0
  178. unicode_logic_kit/hol/deepshallow/relevant.py +206 -0
  179. unicode_logic_kit/hol/free.py +753 -0
  180. unicode_logic_kit/hol/goedel.py +336 -0
  181. unicode_logic_kit/hol/ho_modal.py +1743 -0
  182. unicode_logic_kit/hol/intuitionistic.py +403 -0
  183. unicode_logic_kit/hol/isabelle_conditional.py +593 -0
  184. unicode_logic_kit/hol/isabelle_modal.py +1908 -0
  185. unicode_logic_kit/hol/isabelle_relevant.py +412 -0
  186. unicode_logic_kit/hol/isabelle_runner.py +1147 -0
  187. unicode_logic_kit/hol/isabelle_substructural.py +884 -0
  188. unicode_logic_kit/hol/lean.py +1018 -0
  189. unicode_logic_kit/hol/manyvalued.py +921 -0
  190. unicode_logic_kit/hol/secondorder.py +687 -0
  191. unicode_logic_kit/hol/thf_modal.py +941 -0
  192. unicode_logic_kit/hol/thirdorder.py +397 -0
  193. unicode_logic_kit/ilp/__init__.py +89 -0
  194. unicode_logic_kit/ilp/readback.py +389 -0
  195. unicode_logic_kit/ilp/separation.py +153 -0
  196. unicode_logic_kit/ilp/task.py +730 -0
  197. unicode_logic_kit/logic.py +163 -0
  198. unicode_logic_kit/mcp/__init__.py +28 -0
  199. unicode_logic_kit/mcp/__main__.py +5 -0
  200. unicode_logic_kit/mcp/chem_tools.py +1031 -0
  201. unicode_logic_kit/mcp/server.py +2453 -0
  202. unicode_logic_kit/mcp/syntax_spec.py +681 -0
  203. unicode_logic_kit/prob/__init__.py +53 -0
  204. unicode_logic_kit/prob/_bdd.py +225 -0
  205. unicode_logic_kit/prob/_column_gen.py +668 -0
  206. unicode_logic_kit/prob/distribution.py +686 -0
  207. unicode_logic_kit/prob/nilsson.py +470 -0
  208. unicode_logic_kit/py.typed +0 -0
  209. unicode_logic_kit/semantics/__init__.py +137 -0
  210. unicode_logic_kit/semantics/_modal_reject.py +156 -0
  211. unicode_logic_kit/semantics/action_models.py +466 -0
  212. unicode_logic_kit/semantics/asp_models.py +1200 -0
  213. unicode_logic_kit/semantics/conditional.py +580 -0
  214. unicode_logic_kit/semantics/dynamic_epistemic.py +95 -0
  215. unicode_logic_kit/semantics/free_logic.py +913 -0
  216. unicode_logic_kit/semantics/fuzzy.py +384 -0
  217. unicode_logic_kit/semantics/fuzzy_kripke.py +442 -0
  218. unicode_logic_kit/semantics/intuitionistic.py +581 -0
  219. unicode_logic_kit/semantics/kripke.py +1139 -0
  220. unicode_logic_kit/semantics/manyvalued.py +580 -0
  221. unicode_logic_kit/semantics/matrix.py +342 -0
  222. unicode_logic_kit/semantics/model_eval.py +1135 -0
  223. unicode_logic_kit/semantics/modelfinder.py +1036 -0
  224. unicode_logic_kit/semantics/nonmonotonic.py +372 -0
  225. unicode_logic_kit/semantics/relevant.py +331 -0
  226. unicode_logic_kit/semantics/secondorder.py +657 -0
  227. unicode_logic_kit/semantics/structures.py +352 -0
  228. unicode_logic_kit/semantics/tarski.py +975 -0
  229. unicode_logic_kit/semantics/team.py +315 -0
  230. unicode_logic_kit/semantics/team_translation.py +416 -0
  231. unicode_logic_kit/semantics/thirdorder.py +358 -0
  232. unicode_logic_kit/semantics/tnorm.py +85 -0
  233. unicode_logic_kit/semantics/truthtable.py +201 -0
  234. unicode_logic_kit-0.31.0.dist-info/METADATA +333 -0
  235. unicode_logic_kit-0.31.0.dist-info/RECORD +237 -0
  236. unicode_logic_kit-0.31.0.dist-info/WHEEL +4 -0
  237. 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)