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,540 @@
1
+ """Entailment checking via the Vampire theorem prover (TPTP backend).
2
+
3
+ The companion to :func:`prover9_entailment.check_logical_entailment`, but driving
4
+ `Vampire <https://vprover.github.io/>`_ instead of Prover9. The problem is emitted
5
+ in TPTP ``fof`` syntax — every premise as an ``axiom`` and the conclusion as a
6
+ ``conjecture`` — and handed to a Vampire binary whose path the caller supplies.
7
+ Vampire negates the conjecture internally and reports ``SZS status Theorem`` when
8
+ the premises entail the conclusion.
9
+
10
+ Only the classical FOL fragment is supported, exactly as far as ``Node.to_tptp``
11
+ reaches: a modal, second-order, Łukasiewicz, or lambda node raises
12
+ ``NotImplementedError`` from ``to_tptp`` and that error propagates here.
13
+
14
+ A Windows host can drive a Linux Vampire installed in WSL by passing
15
+ ``use_wsl=True``: Vampire is then launched through ``wsl.exe`` and the temporary
16
+ problem file's path is translated to its ``/mnt/...`` form with ``wslpath``.
17
+
18
+ :func:`check_logical_entailment_vampire` is the original bool-returning entry
19
+ point and its behaviour is unchanged below. :func:`check_entailment_vampire_detailed`
20
+ is an additive alternative that reads Vampire's SZS status line and TSTP
21
+ derivation (via :mod:`atp.tstp`) instead of the old substring heuristic, for
22
+ callers that want the full ``atp.protocol`` status/reason vocabulary and a proof
23
+ DAG rather than a bare bool.
24
+ """
25
+
26
+ import os
27
+ import re
28
+ import subprocess
29
+ import tempfile
30
+ from typing import List, Optional, Sequence, Tuple
31
+
32
+ from ..fol.nodes import Node
33
+ from ._ascii_names import reverse_map_text
34
+ from ._tff_problem import generate_tff_arith_problem
35
+ from ._tptp_problem import generate_tptp_problem_for_prover, generate_tptp_problem_with_mapping
36
+ from ._writer_support import names_kwargs
37
+
38
+
39
+ def _generate_vampire_input(premises: List[Node], conclusion: Node,
40
+ *, tff: Optional[bool] = None,
41
+ sort: Optional[str] = None,
42
+ premise_names: Optional[Sequence[str]] = None) -> str:
43
+ """Build a TPTP problem string from premises and a conclusion.
44
+
45
+ Each premise becomes an ``axiom`` and the conclusion the single
46
+ ``conjecture``. Vampire treats the single conjecture as the goal to
47
+ prove from the axioms.
48
+
49
+ ``sort`` (``None`` by default) selects the single-numeric-sort typed
50
+ arithmetic route (:func:`atp._tff_problem.generate_tff_arith_problem`,
51
+ ``'real'`` or ``'int'`` — see that module's docstring) UNCONDITIONALLY
52
+ when given, taking priority over ``tff``: an opt-in alternative, so a
53
+ caller must ask for it explicitly (every existing caller that never
54
+ passes ``sort`` keeps its current ``tff``-selected/auto-selected
55
+ behaviour byte-for-byte). With ``sort=None``, ``tff`` selects between
56
+ the two dialects: ``None`` (the default) tries the native, genuinely-typed
57
+ ``tff`` route (:func:`atp.tptp_tff.generate_tff_problem`) when
58
+ ``premises``/``conclusion`` contain a SortedQuantifier/SortedConstant/
59
+ SortedCount node (see :func:`atp.tptp_tff.problem_needs_tff`), and falls back
60
+ to the classical guard-predicate ``fof`` route when the typed writer refuses
61
+ the problem (a :class:`~unicode_logic_kit.atp.tptp_tff.Tf0Refusal`: its text would
62
+ ask another question than the kit's; or a node it does not cover that the ``fof``
63
+ writer does: a counting quantifier, ``Contrast``, ``Measure`` — see
64
+ :func:`atp._tptp_problem.generate_tptp_problem_for_prover`); a problem without a sorted node goes to
65
+ the ``fof`` route (:func:`atp._tptp_problem.generate_tptp_problem` — also
66
+ used by :mod:`atp.eprover_backend` and :mod:`atp.twee_entailment`, which build
67
+ the identical ``fof`` problem shape). ``tff=True``/``False`` force one route
68
+ explicitly, and ``True`` raises the typed writer's refusal. Every route's own
69
+ cross-formula symbol-collision guard can raise ``NotImplementedError`` before
70
+ any TPTP text is produced. ``premise_names`` names the premises' ``axiom`` lines
71
+ in whichever route is written (see :func:`atp._tptp_problem
72
+ .generate_tptp_problem_with_mapping`), ``premise_<i>`` when it is ``None``.
73
+ """
74
+ if sort is not None:
75
+ problem, _name_map = generate_tff_arith_problem(
76
+ premises, conclusion, sort=sort, **names_kwargs(premise_names))
77
+ return problem
78
+ return generate_tptp_problem_for_prover(
79
+ premises, conclusion, tff=tff, fof_writer=generate_tptp_problem_with_mapping,
80
+ premise_names=premise_names).text
81
+
82
+
83
+ def _is_entailed_output(stdout: str) -> bool:
84
+ """Decide entailment from Vampire's stdout.
85
+
86
+ Vampire reports ``SZS status Theorem`` when it proves the conjecture from the
87
+ axioms; ``Refutation found`` is the equivalent message in its default proof
88
+ output (and also covers the vacuous case of inconsistent premises, which
89
+ entail anything). Either signal means the entailment holds. A
90
+ ``CounterSatisfiable`` / ``Satisfiable`` / ``Timeout`` status — or no proof at
91
+ all — means it does not.
92
+ """
93
+ return ("SZS status Theorem" in stdout) or ("Refutation found" in stdout)
94
+
95
+
96
+ #: Text that says Vampire FAILED rather than ended a search: its own
97
+ #: ``User error: ...``, a parse error or exception, a crash. Seen in an output
98
+ #: it rules out the "ended by itself" reading below, whatever else is printed.
99
+ _FAILURE_TEXT = re.compile(
100
+ r"\berror\b|exception|assertion|segmentation|core dumped|abort", re.IGNORECASE)
101
+
102
+ #: The line Vampire's statistics block ends a search with:
103
+ #: ``% Termination reason: Refutation not found, incomplete strategy``.
104
+ _TERMINATION_REASON = re.compile(r"^\s*%?\s*Termination reason:\s*(?P<why>.*?)\s*$",
105
+ re.MULTILINE)
106
+
107
+
108
+ def _ended_by_itself(stdout: str) -> Optional[Tuple[str, str]]:
109
+ """Whether a run WITHOUT an SZS status line is a search Vampire ended itself.
110
+
111
+ Vampire does not always print an SZS line when it gives up. Measured on
112
+ Vampire 5.0.1: a non-theorem of a typed-arithmetic (``$int``) problem ends
113
+ ``% Refutation not found, incomplete strategy`` ... ``% Termination reason:
114
+ Refutation not found, incomplete strategy`` (exit code 1, no SZS line), and
115
+ a run stopped by its own ``--time_limit`` / ``--activation_limit`` ends
116
+ ``% Termination reason: Time limit`` / ``Activation limit`` the same way.
117
+ Those are honest answers, not refusals: the problem WAS read and searched.
118
+
119
+ Returns ``(reason, why)`` -- the :mod:`atp.protocol` reason axis value and
120
+ the termination reason as Vampire worded it -- or ``None`` when the output
121
+ does not show a search that ended: no ``Termination reason`` line and no
122
+ ``Refutation not found``, or any text of a failure (``User error``, a parse
123
+ error, an exception, a crash), in which case the caller reports an ERROR.
124
+ A reason naming a ``limit`` is a budget Vampire was given: ``Time limit`` is
125
+ ``"timeout"``, any other limit ``"bound_hit"``; every other reason is the
126
+ prover giving up, ``"incomplete"``.
127
+ """
128
+ if _FAILURE_TEXT.search(stdout):
129
+ return None
130
+ match = _TERMINATION_REASON.search(stdout)
131
+ if match is not None:
132
+ why = match.group("why")
133
+ elif "Refutation not found" in stdout:
134
+ why = "Refutation not found"
135
+ else:
136
+ return None
137
+ if re.search(r"\blimit\b", why, re.IGNORECASE):
138
+ return ("timeout" if re.match(r"time\b", why, re.IGNORECASE) else "bound_hit"), why
139
+ return "incomplete", why
140
+
141
+
142
+ def _to_wsl_path(windows_path: str) -> str:
143
+ """Translate a Windows path to its WSL ``/mnt/...`` form via ``wslpath``.
144
+
145
+ Backslashes are turned into forward slashes first: the WSL interop layer
146
+ swallows backslashes in arguments (``C:\\Users\\…`` reaches ``wslpath`` as
147
+ ``C:Users…`` with the separators gone), whereas ``wslpath`` accepts the
148
+ forward-slash spelling ``C:/Users/…`` directly.
149
+ """
150
+ result = subprocess.run(
151
+ ["wsl.exe", "wslpath", "-u", windows_path.replace("\\", "/")],
152
+ capture_output=True,
153
+ text=True,
154
+ timeout=20,
155
+ )
156
+ wsl_path = result.stdout.strip()
157
+ if not wsl_path:
158
+ raise RuntimeError(
159
+ f"wslpath could not translate {windows_path!r} (is WSL available?): "
160
+ f"{result.stderr.strip()}"
161
+ )
162
+ return wsl_path
163
+
164
+
165
+ def _spawn_vampire(input_str: str, vampire_path: str, timeout: int = 30,
166
+ use_wsl: bool = False,
167
+ extra_args: Tuple[str, ...] = ()) -> Tuple[str, bool]:
168
+ """Write the TPTP problem to a temp file, run Vampire, return its raw stdout.
169
+
170
+ Shared by :func:`_run_vampire` (the original bool-returning route, which
171
+ calls this with ``extra_args=()`` — an unchanged command line) and
172
+ :func:`check_entailment_vampire_detailed` (the SZS/TSTP-reading route,
173
+ which adds ``--proof tptp`` so Vampire's proof is printed as annotated
174
+ TSTP ``fof(...)`` statements instead of its native ``N. formula [rule
175
+ N1,N2]`` numbered-line format — only the TSTP form is what
176
+ :func:`atp.tstp.parse_tstp_derivation` reads).
177
+
178
+ Returns:
179
+ ``(stdout, timed_out)`` — ``stdout`` is Vampire's captured stdout text
180
+ followed by its stderr (``""`` if the process timed out before
181
+ producing any): Vampire writes its own ``User error: ...`` to stdout,
182
+ but a launcher failure (``wsl.exe`` cannot find the binary) or a crash
183
+ speaks on stderr, and a refusal whose explanation was dropped would
184
+ read like a run that merely found nothing. ``timed_out``
185
+ is ``True`` iff the subprocess exceeded ``timeout`` seconds. A
186
+ subprocess timeout is swallowed into this return value, exactly as the
187
+ Prover9 runner swallows one into a bool; any OTHER error — notably
188
+ ``FileNotFoundError`` for a wrong ``vampire_path`` — propagates to the
189
+ caller. The temporary file is always removed, even when the subprocess
190
+ raises.
191
+
192
+ With ``use_wsl=True`` Vampire is invoked inside WSL as
193
+ ``wsl.exe <vampire_path> <extra_args...> <file>``, and the Windows temp-file
194
+ path is first translated to its ``/mnt/...`` form with ``wslpath`` so a
195
+ Linux Vampire under WSL can read the file the Windows side created.
196
+ """
197
+ with tempfile.NamedTemporaryFile(mode="w", suffix=".p", delete=False,
198
+ encoding="utf-8") as temp_file:
199
+ temp_file.write(input_str)
200
+ temp_filename = temp_file.name
201
+
202
+ try:
203
+ if use_wsl:
204
+ command = ["wsl.exe", vampire_path, *extra_args, _to_wsl_path(temp_filename)]
205
+ else:
206
+ command = [vampire_path, *extra_args, temp_filename]
207
+ result = subprocess.run(
208
+ command,
209
+ capture_output=True,
210
+ text=True,
211
+ timeout=timeout,
212
+ )
213
+ return (result.stdout or "") + (result.stderr or ""), False
214
+ except subprocess.TimeoutExpired:
215
+ return "", True
216
+ finally:
217
+ try:
218
+ os.unlink(temp_filename)
219
+ except OSError:
220
+ pass
221
+
222
+
223
+ def _run_vampire(input_str: str, vampire_path: str, timeout: int = 30,
224
+ use_wsl: bool = False) -> bool:
225
+ """Run Vampire and decide entailment from its stdout (see :func:`_spawn_vampire`
226
+ for the process plumbing this delegates to; behaviour is unchanged from before
227
+ the refactor — a timeout still reports ``False``, other errors still propagate).
228
+ """
229
+ stdout, timed_out = _spawn_vampire(input_str, vampire_path, timeout=timeout,
230
+ use_wsl=use_wsl)
231
+ if timed_out:
232
+ return False
233
+ return _is_entailed_output(stdout)
234
+
235
+
236
+ def check_logical_entailment_vampire(premises: List[Node], conclusion: Node,
237
+ vampire_path: str, timeout: int = 30,
238
+ use_wsl: bool = False,
239
+ tff: Optional[bool] = None,
240
+ sort: Optional[str] = None) -> bool:
241
+ """Return whether ``premises`` entail ``conclusion``, decided by Vampire.
242
+
243
+ Args:
244
+ premises: a list of classical (or many-sorted) FOL premise formulas.
245
+ conclusion: the (classical or many-sorted) FOL conclusion formula.
246
+ vampire_path: path to a Vampire executable (e.g. ``"/usr/bin/vampire"``).
247
+ With ``use_wsl=True`` this is the command/path INSIDE WSL — e.g.
248
+ ``"vampire"`` if it is on the WSL ``PATH``, or ``"/home/me/vampire"``.
249
+ timeout: seconds to allow the Vampire process before giving up and
250
+ returning ``False`` (default 30).
251
+ use_wsl: when True, run Vampire inside WSL via ``wsl.exe`` and translate
252
+ the temp-file path to its ``/mnt/...`` form, so a Windows host can
253
+ drive a Linux Vampire installed in WSL.
254
+ tff: which TPTP dialect to export as — see :func:`_generate_vampire_input`.
255
+ ``None`` (the default) tries the native typed ``tff`` route whenever
256
+ a sort is used and writes ``fof`` when the typed writer refuses the
257
+ problem; ``True``/``False`` force one route (``True`` raises the
258
+ typed writer's refusal).
259
+ sort: ``None`` (default) leaves ``tff`` in charge as above; ``'real'``
260
+ or ``'int'`` opts into the single-numeric-sort typed arithmetic
261
+ route (:func:`atp._tff_problem.generate_tff_arith_problem`)
262
+ UNCONDITIONALLY, activating Vampire's native arithmetic decision
263
+ procedures — see that module's docstring for the fragment it
264
+ covers (a formula that genuinely mixes several sorts, or is
265
+ outside the arithmetic fragment, raises ``NotImplementedError``
266
+ naming the construct rather than silently falling back). Without
267
+ ``sort`` arithmetic is NOT asked for: a numeral is a constant identified
268
+ by its value and ``+ - * / < > ≤ ≥`` are uninterpreted symbols, which
269
+ the problem writers write under ordinary words (see
270
+ :mod:`unicode_logic_kit.atp._tptp_problem`), so ``1 ≠ 2`` and ``1 + 1 = 2``
271
+ are not provable here and are with ``sort='int'``.
272
+
273
+ Returns:
274
+ ``True`` iff Vampire proves the conclusion follows from the premises.
275
+ Note that every premise and the conclusion must be a closed sentence:
276
+ Vampire rejects formulas with unquantified (free) variables, and such a
277
+ rejection is reported as ``False`` (no proof), not raised — EXCEPT on
278
+ the ``sort=`` route, which refuses a free variable
279
+ (:class:`~unicode_logic_kit.atp._tff_problem.TfaRefusal`, a ``ValueError`` and a
280
+ ``NotImplementedError``) instead of silently picking an implicit-closure
281
+ convention (see :mod:`atp._tff_problem`'s module docstring).
282
+
283
+ Raises:
284
+ FileNotFoundError: ``vampire_path`` does not point to an executable (or,
285
+ with ``use_wsl=True``, ``wsl.exe`` itself is not found).
286
+ NotImplementedError: either a formula is outside the fragment the
287
+ selected route covers (see :func:`_generate_vampire_input`), or
288
+ two distinct predicate (or function/constant, or — ``tff``/
289
+ ``sort`` route — sort) names across ``premises``/``conclusion``
290
+ would render as the same TPTP identifier — see
291
+ :mod:`atp._tptp_problem`'s, :mod:`atp.tptp_tff`'s, and
292
+ :mod:`atp._tff_problem`'s module docstrings for why that is
293
+ refused rather than silently merged into one symbol. With
294
+ ``tff=True`` it is also a :class:`~unicode_logic_kit.atp.tptp_tff.Tf0Refusal`
295
+ when the typed text would ask another question than the kit's (that
296
+ class is a ``ValueError`` too). On the ``sort=`` route it is also a
297
+ :class:`~unicode_logic_kit.atp._tff_problem.TfaRefusal` for a free
298
+ variable, an arity conflict, or a constant-vs-function name clash
299
+ (a ``ValueError`` too; see :mod:`atp._tff_problem`'s module docstring).
300
+ ValueError: on the ``sort=`` route only — an invalid ``sort``.
301
+ """
302
+ vampire_input = _generate_vampire_input(premises, conclusion, tff=tff, sort=sort)
303
+ return _run_vampire(vampire_input, vampire_path, timeout=timeout,
304
+ use_wsl=use_wsl)
305
+
306
+
307
+ # ---------------------------------------------------------------------------
308
+ # SZS/TSTP-reading route (additive)
309
+ # ---------------------------------------------------------------------------
310
+
311
+ # How much of Vampire's stdout to keep in "output_excerpt": the TAIL, since
312
+ # that is where the SZS status line and (when present) the end of the proof
313
+ # live for Vampire's default output ordering; the full text is kept whenever
314
+ # it is already shorter than this.
315
+ _EXCERPT_CHARS = 4000
316
+
317
+
318
+ def check_entailment_vampire_detailed(premises: List[Node], conclusion: Node,
319
+ vampire_path: str, timeout: int = 30,
320
+ use_wsl: bool = False,
321
+ tff: Optional[bool] = None,
322
+ sort: Optional[str] = None,
323
+ premise_names: Optional[Sequence[str]] = None,
324
+ axiom_names: bool = False) -> dict:
325
+ """Run Vampire and read its SZS status + TSTP derivation, not just a bool.
326
+
327
+ Builds the same TPTP ``fof`` problem as :func:`check_logical_entailment_vampire`
328
+ (every premise an ``axiom``, the conclusion the single ``conjecture`` — a
329
+ ``query="conjecture"`` framing in :mod:`atp.tstp` terms) and drives the same
330
+ subprocess plumbing (:func:`_spawn_vampire`), but decides the outcome by
331
+ reading the ``% SZS status ...`` line with :func:`atp.tstp.extract_szs_status`
332
+ and :func:`atp.tstp.szs_to_verdict_fields` instead of the old
333
+ ``"SZS status Theorem" in stdout`` substring test — so ``CounterSatisfiable``,
334
+ ``Timeout``, ``GaveUp``, etc. each come back as their own honest
335
+ ``atp.protocol`` status/reason pair rather than all collapsing into "not
336
+ entailed". Unlike :func:`check_logical_entailment_vampire`, this function
337
+ passes ``--proof tptp`` on Vampire's command line: Vampire's DEFAULT proof
338
+ output is its own native numbered-line format (``10. mortal(socrates)
339
+ [resolution 7,8]``), which carries the same information but is not
340
+ TSTP-annotated ``fof``/``cnf`` syntax, so ``--proof tptp`` is what makes
341
+ :func:`atp.tstp.parse_tstp_derivation` able to read it into a proof DAG.
342
+
343
+ Args:
344
+ premises: a list of classical FOL premise formulas.
345
+ conclusion: the classical FOL conclusion formula.
346
+ vampire_path: path to a Vampire executable — see
347
+ :func:`check_logical_entailment_vampire`.
348
+ timeout: seconds to allow the Vampire process before giving up
349
+ (default 30).
350
+ use_wsl: drive a Linux Vampire under WSL — see
351
+ :func:`check_logical_entailment_vampire`.
352
+ sort: ``None`` (default) leaves ``tff`` in charge; ``'real'``/``'int'``
353
+ opts into the single-numeric-sort typed arithmetic route — see
354
+ :func:`check_logical_entailment_vampire`'s ``sort`` for the full
355
+ contract. Every route (``fof``, ``tff``, ``sort``) hands back a
356
+ :class:`~unicode_logic_kit.atp._tptp_problem.TptpNameMap`, so
357
+ ``output_excerpt`` below IS reverse-mapped to original kit-level
358
+ names on all of them. ``derivation`` still degrades to ``None``
359
+ on the ``tff`` and ``sort`` routes (see below) — :mod:`atp.tstp`
360
+ reads only ``fof``/``cnf`` derivation lines, never ``tff``, a
361
+ pre-existing gap.
362
+ premise_names: one name per premise for the problem's ``axiom`` lines
363
+ instead of ``premise_<i>``, in whichever dialect is written (see
364
+ :func:`atp._tptp_problem.generate_tptp_problem_with_mapping`). Naming the
365
+ premises also asks Vampire to print the names of the axioms of its proof
366
+ (``--output_axiom_names on``), which it otherwise replaces by ``unknown``, so
367
+ that ``relevant_premises`` below can be read.
368
+ axiom_names: ask Vampire for the names of the axioms of its proof without naming
369
+ the premises (they are then ``premise_<i>``). The command line stays
370
+ ``--proof tptp`` when neither this nor ``premise_names`` is given.
371
+
372
+ Returns:
373
+ A JSON-compatible dict:
374
+
375
+ * ``szs_status``: the raw SZS status token (e.g. ``"Theorem"``), or
376
+ ``None`` if Vampire's output had no ``SZS status`` line at all (a
377
+ timeout before any output, a Vampire build/mode that suppresses
378
+ the line, or Vampire REFUSING the problem). With no line,
379
+ ``status``/``reason`` fall back to the same "Refutation found"
380
+ substring heuristic :func:`check_logical_entailment_vampire` uses
381
+ (``PROVED`` when it is there, so this function is never STRICTLY
382
+ less informative than the old one); otherwise to what Vampire
383
+ printed about how its search ended. A ``Termination reason:`` line
384
+ (``Refutation not found, incomplete strategy`` — Vampire 5.0.1 ends
385
+ a non-theorem of a typed-arithmetic problem this way, with no SZS
386
+ line) is an honest ``UNKNOWN``: ``"incomplete"``, or ``"timeout"``
387
+ / ``"bound_hit"`` when the reason is Vampire's own ``Time limit`` /
388
+ another limit (see :func:`_ended_by_itself`). Only when it printed
389
+ neither a verdict, a refutation nor such an account, although
390
+ nothing cut it off — or printed an error — is it ``ERROR``/
391
+ ``"infra"``: a refusal or a crash. Its own message is in
392
+ ``output_excerpt``.
393
+ * ``status``: ``atp.protocol.PROVED`` / ``REFUTED`` / ``UNKNOWN`` /
394
+ ``ERROR`` (``UNKNOWN``/``"timeout"`` on a subprocess timeout;
395
+ ``ERROR``/``"infra"`` when there is no SZS line and no account of
396
+ a search that ended, or an SZS line that itself names an error such
397
+ as ``SyntaxError``).
398
+ * ``reason``: the matching ``atp.protocol`` reason axis value, or
399
+ ``None`` for a definitive verdict.
400
+ * ``output_excerpt``: the last :data:`_EXCERPT_CHARS` characters of
401
+ Vampire's stdout and stderr (the whole thing, if shorter) — always
402
+ present, even ``""`` on a timeout, so a caller always has something
403
+ to show.
404
+ * ``derivation``: :class:`atp.tstp.TstpDerivation`'s ``to_dict()``
405
+ when the output contained at least one parseable ``fof``/``cnf``
406
+ statement, else ``None`` (no derivation to report — not an error).
407
+ :func:`atp.tstp.parse_tstp_derivation` only recognises ``fof``/
408
+ ``cnf`` statements (see its own docstring), never ``tff``/``tcf``
409
+ ones, so on the ``tff`` route ``derivation`` is CURRENTLY ALWAYS
410
+ ``None`` — even for a genuine, successful proof whose stdout is
411
+ full of well-formed ``tff(...)`` proof lines. This is a real gap
412
+ in the READER (:func:`atp.tstp.parse_tstp_derivation` reads
413
+ ``fof``/``cnf`` lines only), not in the name mapping: the excerpt
414
+ of a ``tff`` run IS translated back through the TF0 writer's name
415
+ map.
416
+ * ``dialect``: which writer produced the problem Vampire was given:
417
+ ``"fof"``, ``"tff"`` or ``"tfa"`` (the ``sort=`` route).
418
+ * ``tff_fallback``: ``None``, or — when ``tff=None`` tried the typed
419
+ writer for a sorted problem, was refused, and wrote ``fof`` instead —
420
+ the sentence that says so and quotes the typed writer's reason (the
421
+ backend puts it into the verdict's ``detail``).
422
+ * ``relevant_premises``: for a PROVED answer to a run that asked for the axiom
423
+ names (``premise_names`` or ``axiom_names``), the sorted 0-based indices of the
424
+ premises the proof's axiom leaves are
425
+ (:func:`atp.tstp.relevant_premises_from_tstp`, read through the problem's name
426
+ map); ``None`` otherwise, and when the proof cannot be read that way.
427
+ * ``background_used``: the ``(name, meaning)`` pairs of the background axioms the
428
+ writer added on its own (the non-emptiness of a sort, the membership of a sorted
429
+ constant) that the proof used — background, not premises, so they are not in
430
+ ``relevant_premises``; ``()`` when none, or when ``relevant_premises`` is
431
+ ``None``.
432
+
433
+ Every symbol name in ``output_excerpt`` and in ``derivation``'s
434
+ formulas has already been translated back from whatever ASCII-safe
435
+ token the problem writer may have substituted (a non-ASCII or
436
+ digit-leading kit-level name, or a function/constant renamed because
437
+ its word is a predicate's: ``agent`` -> ``agent_term``) to the
438
+ ORIGINAL kit-level name — on the ``fof`` route
439
+ (:func:`atp._tptp_problem.generate_tptp_problem_with_mapping`), the
440
+ ``tff`` route (:func:`atp.tptp_tff.generate_tff_problem_with_mapping`)
441
+ and the ``sort`` route alike. A name Vampire introduced itself (a
442
+ Skolem constant, a clausification symbol) was never one of ours and
443
+ is left as Vampire printed it, and so is a SORT name on the ``tff``
444
+ route (the TF0 map covers predicates, functions and constants, not
445
+ sorts).
446
+
447
+ Raises:
448
+ FileNotFoundError: ``vampire_path`` does not point to an executable
449
+ (or, with ``use_wsl=True``, ``wsl.exe`` itself is not found) —
450
+ same contract as :func:`check_logical_entailment_vampire`.
451
+ NotImplementedError: a formula is outside the fragment the selected
452
+ route covers, or a symbol-folding collision (see
453
+ :func:`check_logical_entailment_vampire`'s ``Raises`` for both),
454
+ surfaced before any subprocess is spawned — same contract as
455
+ :func:`check_logical_entailment_vampire`.
456
+ """
457
+ from .protocol import ERROR, PROVED, UNKNOWN
458
+ from .tstp import (
459
+ _premise_use_from_tstp, extract_szs_status, parse_tstp_derivation,
460
+ reverse_map_derivation, szs_to_verdict_fields,
461
+ )
462
+
463
+ if sort is not None:
464
+ vampire_input, name_map = generate_tff_arith_problem(
465
+ premises, conclusion, sort=sort, **names_kwargs(premise_names))
466
+ dialect, fallback_note = "tfa", None
467
+ else:
468
+ built = generate_tptp_problem_for_prover(
469
+ premises, conclusion, tff=tff, fof_writer=generate_tptp_problem_with_mapping,
470
+ premise_names=premise_names)
471
+ vampire_input, name_map = built.text, built.name_map
472
+ dialect, fallback_note = built.dialect, built.fallback_note
473
+ read_axiom_names = axiom_names or premise_names is not None
474
+ extra_args = ("--proof", "tptp") + (("--output_axiom_names", "on") if read_axiom_names else ())
475
+ stdout, timed_out = _spawn_vampire(vampire_input, vampire_path, timeout=timeout,
476
+ use_wsl=use_wsl, extra_args=extra_args)
477
+
478
+ if timed_out:
479
+ return {
480
+ "szs_status": None,
481
+ "status": UNKNOWN,
482
+ "reason": "timeout",
483
+ "output_excerpt": "",
484
+ "derivation": None,
485
+ "dialect": dialect,
486
+ "tff_fallback": fallback_note,
487
+ "relevant_premises": None,
488
+ "background_used": (),
489
+ }
490
+
491
+ if name_map is None:
492
+ excerpt = stdout[-_EXCERPT_CHARS:]
493
+ else:
494
+ pred_rev, term_rev = name_map.reverse_rendered()
495
+ excerpt = reverse_map_text(stdout[-_EXCERPT_CHARS:], pred_rev, term_rev)
496
+ szs = extract_szs_status(stdout)
497
+ if szs is None:
498
+ if _is_entailed_output(stdout):
499
+ status, reason = PROVED, None
500
+ elif (ended := _ended_by_itself(stdout)) is not None:
501
+ # No SZS line, but Vampire printed how its search ended
502
+ # ("Termination reason: Refutation not found, incomplete strategy"):
503
+ # it READ the problem and gave up (or hit its own limit). That is
504
+ # an honest UNKNOWN, not a refusal.
505
+ status, reason = UNKNOWN, ended[0]
506
+ else:
507
+ # No SZS verdict, no refutation and no account of a search, and the
508
+ # process was not cut off by our timeout (handled above): Vampire
509
+ # REFUSED the problem (its "User error: ..." / parse-error text is
510
+ # in the output) or died. That is a failure, not "incomplete" --
511
+ # reporting it as UNKNOWN made a problem Vampire could not read
512
+ # look like one it ran out of time on.
513
+ status, reason = ERROR, "infra"
514
+ else:
515
+ status, reason = szs_to_verdict_fields(szs, query="conjecture")
516
+
517
+ parsed = parse_tstp_derivation(stdout)
518
+ derivation = parsed if name_map is None else reverse_map_derivation(parsed, name_map)
519
+ derivation_dict = derivation.to_dict() if derivation.steps else None
520
+
521
+ # The proof's axiom leaves, by the names the problem gave them: read from the text
522
+ # Vampire printed (a premise name is not a symbol, so never the renamed excerpt).
523
+ relevant: Optional[Tuple[int, ...]] = None
524
+ background_used: Tuple[Tuple[str, str], ...] = ()
525
+ if read_axiom_names and status == PROVED:
526
+ use = _premise_use_from_tstp(stdout, len(premises), name_map)
527
+ if use is not None:
528
+ relevant, background_used = use
529
+
530
+ return {
531
+ "szs_status": szs,
532
+ "status": status,
533
+ "reason": reason,
534
+ "output_excerpt": excerpt,
535
+ "derivation": derivation_dict,
536
+ "dialect": dialect,
537
+ "tff_fallback": fallback_note,
538
+ "relevant_premises": relevant,
539
+ "background_used": background_used,
540
+ }