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,403 @@
1
+ """Intuitionistic propositional logic → HOL, via the Gödel–McKinsey–Tarski (GMT)
2
+ box-translation into modal **S4** composed with the alethic shallow embedding.
3
+
4
+ Intuitionistic propositional logic (IPL) has no truth-functional semantics, but
5
+ Gödel (1933) and McKinsey–Tarski (1948) showed it embeds *faithfully* into the
6
+ modal logic **S4**: a propositional formula ``A`` is intuitionistically valid iff
7
+ its box-translation ``T(A)`` is S4-valid. The translation prefixes ``□`` exactly
8
+ where the intuitionistic clauses quantify over future worlds:
9
+
10
+ T(p) = □p (atoms are "stable" — forced henceforth)
11
+ T(¬A) = □¬T(A)
12
+ T(A ∧ B) = T(A) ∧ T(B)
13
+ T(A ∨ B) = T(A) ∨ T(B)
14
+ T(A → B) = □(T(A) → T(B))
15
+
16
+ (``A ↔ B`` and ``A ⊕ B`` are first expanded to their ∧/∨/→/¬ definitions —
17
+ matching the very clauses :meth:`IntKripkeModel.forces` uses — and then translated.)
18
+
19
+ FALSUM. The textbook GMT rule is ``T(⊥)=⊥`` with ``⊥`` a *genuine* logical constant,
20
+ and that is the rule here. The nullary atoms ``⊥`` and ``⊤`` (the reserved atom
21
+ ``"⊥"`` of :data:`unicode_logic_kit.atp.fitch.FALSUM` among them) are the falsity and
22
+ truth constants, the same as ``$false`` and ``$true``: the intuitionistic Kripke
23
+ evaluator :func:`unicode_logic_kit.semantics.intuitionistic.int_valid` forces ``⊤`` at
24
+ every world and ``⊥`` at none, so ``⊥→p`` and ``¬⊥`` are valid and ``⊥`` alone and
25
+ ``⊤→p`` are not. The box-translation keeps a constant as it is, ``T(⊥)=⊥`` and
26
+ ``T(⊤)=⊤`` (a constant needs no box: it is the same at every world), and never
27
+ declares a symbol for one. See :func:`gmt_translate`.
28
+
29
+ The resulting S4 modal formula is handed to the existing alethic SSE
30
+ (:mod:`unicode_logic_kit.fol.qml`) with an **S4 frame** (reflexive + transitive),
31
+ so the emitted THF problem / Isabelle theory is a *Theorem* exactly when the
32
+ original formula is intuitionistically valid. The accessibility relation ``r`` of
33
+ S4 is precisely the intuitionistic "≥" pre-order, and the GMT box mirrors the
34
+ "for every later world" quantifier of the ``→``/``¬`` forcing clauses; this is why
35
+ ``p ∨ ¬p``, ``¬¬p → p`` and Peirce's law come out **non-theorems**, while
36
+ ``p → ¬¬p`` and the constructive tautologies are theorems.
37
+
38
+ EQUALITY IS REFUSED. ``a = b`` / ``a ≠ b`` are not propositional letters, and this
39
+ module is the PROPOSITIONAL one: its ground truth :func:`int_valid` keys an atom by
40
+ its rendered form (``"a = a"`` is just another variable, so ``a = a`` is not valid
41
+ there), while the S4 side of the GMT embedding goes through
42
+ :func:`unicode_logic_kit.fol.qml.qml_is_valid`, which reads ``=`` as RIGID identity
43
+ over the object domain (``a = a`` valid, ``a = b → □(a = b)`` valid). Fed an identity
44
+ atom the two sides of the module's own cross-check therefore answer different
45
+ questions and :func:`gmt_validity_matches_int_valid` reports a "mismatch" that is no
46
+ bug in either — and :func:`to_isabelle_intuitionistic` would emit a real proof
47
+ for ``a = a`` about an uninterpreted constant ``feq``, which Isabelle cannot check.
48
+ The two could be made to agree only by giving intuitionistic logic a term semantics
49
+ of its own (intuitionistic logic WITH equality — not what ``int_valid`` or the
50
+ G4ip prover decide, and not part of the GMT theorem, which is about propositional
51
+ formulas) or by reading identity as an uninterpreted relation on the S4 side (the
52
+ silent approximation the kit refuses, and against ``fol.qml``). So :func:`gmt_translate`
53
+ — every other public function goes through it — raises ``NotImplementedError``
54
+ naming the atom, with the shared
55
+ :func:`~unicode_logic_kit.semantics._modal_reject.reject_equality` the propositional
56
+ modal tableau and the Kripke evaluator use, scanning the WHOLE formula up front.
57
+ Other atoms with arguments (``P(a)``, ``a < b``) stay ordinary propositional letters
58
+ on both sides, exactly as before.
59
+
60
+ HONESTY. This module only *emits* a HOL problem/theory; it does not run Isabelle,
61
+ Leo-III, Satallax or Sledgehammer. IPL is decidable (finite model property —
62
+ which is what :func:`unicode_logic_kit.semantics.intuitionistic.int_valid` exploits),
63
+ and propositional S4 is decidable, so a HOL prover *can* discharge every emitted
64
+ conjecture; but the SSE target is full HOL, whose theorem-hood is only
65
+ semi-decidable, so an external prover may time out. The cross-checking function
66
+ :func:`gmt_validity_matches_int_valid` does not need an external prover: it compares
67
+ :func:`int_valid` against the toolkit's *own* S4-validity of the translation
68
+ (:func:`unicode_logic_kit.fol.qml.qml_is_valid` under an S4 frame, decided by Z3 — the
69
+ fragment is propositional, hence decidable), so the GMT correctness claim is verified
70
+ end-to-end inside the toolkit.
71
+
72
+ Public API: :func:`gmt_translate`, :func:`to_thf_intuitionistic`,
73
+ :func:`to_isabelle_intuitionistic`, :func:`gmt_is_s4_valid`,
74
+ :func:`gmt_validity_matches_int_valid`.
75
+ """
76
+
77
+ from unicode_logic_kit.fol.nodes import (
78
+ Node, Atom, Not, And, Or, Xor, Implies, Iff,
79
+ Box, Quantifier, SortedQuantifier,
80
+ )
81
+ from unicode_logic_kit.fol._so_nodes import SecondOrderQuantifier
82
+ from unicode_logic_kit.fol.qml import to_thf_modal
83
+ from unicode_logic_kit.fol._truth_constants import truth_value
84
+ from unicode_logic_kit.semantics._modal_reject import reject_equality
85
+
86
+
87
+ #: How the GMT embedding reads an atom, and so why it cannot read identity: the
88
+ #: clause the shared equality refusal quotes, and the sentence that points elsewhere.
89
+ _EQUALITY_ROUTE = "the propositional Gödel–McKinsey–Tarski embedding"
90
+ _EQUALITY_ATOM_READING = ("an atom is a propositional letter keyed by its rendered "
91
+ "form, exactly as int_valid reads it")
92
+ _EQUALITY_INSTEAD = (
93
+ "The kit has no intuitionistic logic with equality, and the two sides of this "
94
+ "module's cross-check would answer different questions: int_valid reads the "
95
+ "atom as an uninterpreted proposition while the S4 side (fol.qml) reads '=' as "
96
+ "rigid identity. For identity under modal (classical) reasoning use "
97
+ "unicode_logic_kit.fol.qml.qml_is_valid or another first-order route, not here.")
98
+
99
+
100
+ def _check_propositional(formula: Node) -> None:
101
+ """Refuse ``formula`` if it is not propositional IPL.
102
+
103
+ Raises ``ValueError`` on a quantifier (out of scope) and ``NotImplementedError``
104
+ on an equality / disequality atom (``=`` / ``≠``, refused by name — see the
105
+ module docstring's "EQUALITY IS REFUSED"), scanning the WHOLE tree before any
106
+ translation or oracle call so no verdict is produced that never looked at it.
107
+
108
+ Mirrors :func:`unicode_logic_kit.semantics.intuitionistic._check_propositional`:
109
+ the GMT embedding here is for *propositional* IPL, whose decidability the
110
+ cross-check relies on. (Quantified IPL needs varying first-order domains.)
111
+ """
112
+ for node in formula.walk():
113
+ if isinstance(node, (Quantifier, SortedQuantifier, SecondOrderQuantifier)):
114
+ raise ValueError(
115
+ "intuitionistic GMT: only propositional formulas are supported "
116
+ "(quantified intuitionistic logic needs varying domains, out of scope)."
117
+ )
118
+ reject_equality(node, "intuitionistic GMT", _EQUALITY_ROUTE,
119
+ atom_reading=_EQUALITY_ATOM_READING,
120
+ instead=_EQUALITY_INSTEAD)
121
+
122
+
123
+ def gmt_translate(formula: Node) -> Node:
124
+ """Return the Gödel–McKinsey–Tarski box-translation ``T(formula)`` (a modal S4 Node).
125
+
126
+ The result is a classical modal formula built from :class:`Box`, the Boolean
127
+ connectives and the original atoms; it is S4-valid iff ``formula`` is
128
+ intuitionistically valid. ``↔`` and ``⊕`` are expanded to their ∧/∨/→/¬
129
+ definitions before translation, matching the intuitionistic forcing clauses.
130
+ The truth constants ``⊤`` / ``$true`` and ``⊥`` / ``$false`` are kept as they are
131
+ (``T(⊥)=⊥``), the reading :func:`int_valid` gives them; see the module docstring's
132
+ FALSUM note.
133
+
134
+ Raises ``ValueError`` on a quantifier and ``NotImplementedError`` on an equality
135
+ atom (``=`` / ``≠``) anywhere in ``formula`` — see the module docstring's
136
+ "EQUALITY IS REFUSED". Every other function of this module goes through here,
137
+ so they all refuse alike.
138
+ """
139
+ _check_propositional(formula)
140
+ return _gmt(formula)
141
+
142
+
143
+ def _gmt(f: Node) -> Node:
144
+ """Core box-translation recursion (no validation; see :func:`gmt_translate`)."""
145
+ if isinstance(f, Atom):
146
+ if truth_value(f) is not None:
147
+ return f # T($true) = $true, T($false) = $false
148
+ return Box(f) # T(p) = □p
149
+ if isinstance(f, Not):
150
+ return Box(Not(_gmt(f.formula))) # T(¬A) = □¬T(A)
151
+ if isinstance(f, And):
152
+ return And(_gmt(f.left), _gmt(f.right)) # T(A∧B) = T(A)∧T(B)
153
+ if isinstance(f, Or):
154
+ return Or(_gmt(f.left), _gmt(f.right)) # T(A∨B) = T(A)∨T(B)
155
+ if isinstance(f, Implies):
156
+ return Box(Implies(_gmt(f.left), _gmt(f.right))) # T(A→B) = □(T(A)→T(B))
157
+ if isinstance(f, Iff):
158
+ # A↔B ≡ (A→B)∧(B→A) — exactly the intuitionistic forcing clause for ↔.
159
+ return _gmt(And(Implies(f.left, f.right), Implies(f.right, f.left)))
160
+ if isinstance(f, Xor):
161
+ # A⊕B ≡ (A∨B)∧¬(A∧B) — the intuitionistic forcing clause for ⊕.
162
+ a, b = f.left, f.right
163
+ return _gmt(And(Or(a, b), Not(And(a, b))))
164
+ raise NotImplementedError(
165
+ f"intuitionistic GMT: unsupported node {type(f).__name__} "
166
+ "(propositional ∧/∨/→/¬/↔/⊕/⊥ and atoms only)."
167
+ )
168
+
169
+
170
+ def to_thf_intuitionistic(formula: Node) -> str:
171
+ """Emit a Benzmüller-style TPTP **THF** problem whose theorem-hood ≡ IPL-validity.
172
+
173
+ Box-translates ``formula`` (GMT) into modal S4 and embeds the result with the
174
+ alethic shallow embedding under an S4 frame (reflexive + transitive). The
175
+ conjecture ``mvalid @ ⟨T(formula)⟩`` is a *Theorem* (dischargeable by a HOL ATP
176
+ such as Leo-III / Satallax) iff ``formula`` is intuitionistically valid.
177
+
178
+ The toolkit only *emits* the problem; it does not run the prover.
179
+ """
180
+ return to_thf_modal(gmt_translate(formula), mode="constant", frame="S4")
181
+
182
+
183
+ # --- Isabelle/HOL rendering of the GMT-translated S4 modal formula -----------
184
+ #
185
+ # A complete, *loadable* theory (not the alethic-fragment skeleton in qml.py,
186
+ # which leaves the lemma inside a comment and defines only mnot/mbox/mvalid).
187
+ # Worlds are an opaque type ``i``; a modal proposition is ``i ⇒ bool``; the
188
+ # operators are the standard Benzmüller-style lifted abbreviations and the frame
189
+ # is fixed to S4 (``r`` reflexive + transitive) via a locale-free axiomatization
190
+ # discharged inside the lemma's assumptions, so the theory is self-contained and
191
+ # Sledgehammer/auto can attack the goal directly.
192
+
193
+ _ISA_PROP = "\\<Rightarrow>"
194
+ _ISA_NOT = "\\<^bold>\\<not>"
195
+ _ISA_AND = "\\<^bold>\\<and>"
196
+ _ISA_OR = "\\<^bold>\\<or>"
197
+ _ISA_IMP = "\\<^bold>\\<rightarrow>"
198
+ _ISA_BOX = "\\<^bold>\\<box>"
199
+
200
+
201
+ # Aliases for reserved/symbolic atoms whose char-by-char sanitisation would otherwise
202
+ # collapse to the bare reserved token '_' (Isabelle's wildcard — a theory using it as a
203
+ # consts name will NOT load) and merge distinct atoms (⊥/⊤/=/≠ all → '_'). Each alias is
204
+ # a distinct, valid lowercase identifier. (The THF path aliases these separately.)
205
+ # ``=`` / ``≠`` are aliased only so this sanitiser stays total and injective for any
206
+ # atom NAME it is asked about: no public function reaches it with an identity atom,
207
+ # because gmt_translate refuses one by name (module docstring, "EQUALITY IS REFUSED").
208
+ _ISA_ATOM_ALIASES = {"⊥": "bottom", "⊤": "top", "=": "feq", "≠": "fneq"}
209
+
210
+ # Identifiers the emitted theory already uses structurally: the world type ``i``, the
211
+ # accessibility relation ``r`` and the free variables ``w``/``v``/``u`` of its frame
212
+ # axioms (``r_refl: "r w w"``, ``r_trans: "r w v ⟹ r v u ⟹ r w u"``), and the lifted
213
+ # operator names. An atom predicate that sanitised to one of these would emit a SECOND
214
+ # ``consts`` with that name (a duplicate-constant clash that Isabelle rejects — so the
215
+ # theory would not load even for a valid formula, e.g. the atom ``r`` in ``r → r``), or
216
+ # make the frame axioms ill-typed (an atom ``w`` shadowing the type-``i`` bound var).
217
+ # A colliding atom is de-collided with the ``p_`` prefix (a trailing ``_`` would be a
218
+ # *bad* Isabelle identifier).
219
+ _ISA_RESERVED = frozenset({
220
+ "i", "r", "w", "v", "u",
221
+ "mnot", "mand", "mor", "mimp", "mbox", "mvalid",
222
+ })
223
+
224
+
225
+ def _isa_atom_name(name: str) -> str:
226
+ """A safe, *distinct* Isabelle constant name for an atom predicate.
227
+
228
+ Distinct source atoms must map to distinct legal identifiers. Reserved/symbolic
229
+ atoms (⊥, ⊤, =, ≠) are aliased to dedicated lowercase ids; everything else is
230
+ sanitised char-by-char (alnum/underscore), a ``p_`` prefix is prepended when the
231
+ result is empty, is the bare reserved ``_`` token, or starts with ``_`` (all of
232
+ which are illegal or reserved as a bare Isabelle constant name), and a name that
233
+ would collide with a structural identifier of the theory (the relation ``r``, an
234
+ axiom variable ``w``/``v``/``u``, the world type ``i``, a lifted operator) is
235
+ de-collided with the ``p_`` prefix (``r`` → ``p_r``).
236
+ """
237
+ if name in _ISA_ATOM_ALIASES:
238
+ return _ISA_ATOM_ALIASES[name]
239
+ safe = "".join(c if (c.isalnum() or c == "_") else "_" for c in name)
240
+ if not safe or safe[0] == "_" or not safe[0].isalpha():
241
+ safe = "p_" + safe
242
+ if safe in _ISA_RESERVED:
243
+ safe = "p_" + safe # p_r / p_w / … (a trailing '_' is a bad Isabelle id)
244
+ return safe
245
+
246
+
247
+ def _isa_atoms(formula: Node):
248
+ """Distinct atom predicate names in ``formula`` (each a propositional letter)."""
249
+ out, seen = [], set()
250
+ for n in formula.walk():
251
+ if (isinstance(n, Atom) and n.predicate not in seen
252
+ and truth_value(n) is None):
253
+ seen.add(n.predicate)
254
+ out.append(n.predicate)
255
+ return out
256
+
257
+
258
+ def _isa_render(node: Node) -> str:
259
+ """Render a GMT-translated S4 modal Node in Isabelle bold-operator syntax."""
260
+ if isinstance(node, Atom):
261
+ if truth_value(node) is not None:
262
+ return "(\\<lambda>_. True)" if truth_value(node) else "(\\<lambda>_. False)"
263
+ return _isa_atom_name(node.predicate)
264
+ if isinstance(node, Not):
265
+ return f"({_ISA_NOT}{_isa_render(node.formula)})"
266
+ if isinstance(node, And):
267
+ return f"({_isa_render(node.left)} {_ISA_AND} {_isa_render(node.right)})"
268
+ if isinstance(node, Or):
269
+ return f"({_isa_render(node.left)} {_ISA_OR} {_isa_render(node.right)})"
270
+ if isinstance(node, Implies):
271
+ return f"({_isa_render(node.left)} {_ISA_IMP} {_isa_render(node.right)})"
272
+ if isinstance(node, Box):
273
+ return f"({_ISA_BOX}{_isa_render(node.formula)})"
274
+ # GMT output only ever contains Atom/Not/And/Or/Implies/Box.
275
+ raise NotImplementedError(
276
+ f"to_isabelle_intuitionistic: unexpected node {type(node).__name__} "
277
+ "in the S4 translation."
278
+ )
279
+
280
+
281
+ def to_isabelle_intuitionistic(formula: Node, theory_name: str = "IPL_GMT") -> str:
282
+ """Emit a complete, loadable **Isabelle/HOL** theory whose lemma ≡ IPL-validity.
283
+
284
+ Box-translates ``formula`` (GMT) into modal S4, then writes a self-contained
285
+ theory: worlds as a type ``i``; an accessibility relation ``r`` fixed to be
286
+ reflexive + transitive (the **S4** frame); the lifted modal operators as
287
+ abbreviations; the atoms as ``consts``; and the embedded formula as a genuine
288
+ ``lemma`` (``\\<lfloor> T(formula) \\<rfloor>``). The lemma is a theorem iff
289
+ ``formula`` is intuitionistically valid (Gödel–McKinsey–Tarski).
290
+
291
+ The proof is emitted **verdict-dependently** from the module's *decidable* S4
292
+ oracle :func:`gmt_is_s4_valid` (Z3 on the GMT→S4 translation, decisive on this
293
+ propositional fragment): when the formula is valid the theory carries a real,
294
+ Isabelle-checked proof (``using r_refl r_trans by (metis … | meson … | blast |
295
+ auto)`` — the S4 frame facts must be in scope, a bare ``axiomatization`` fact is
296
+ not in the default claset); when it is not valid the lemma is left ``oops`` (no
297
+ proof exists; see
298
+ :func:`~unicode_logic_kit.semantics.intuitionistic.int_countermodel`). The decision
299
+ deliberately does **not** use ``int_valid``'s default 3-world bound, which is
300
+ incomplete (IPL's finite-model bound grows with the formula).
301
+
302
+ Unlike :func:`unicode_logic_kit.fol.qml.to_isabelle_modal` (an alethic skeleton
303
+ that puts the lemma in a comment and defines only ``mnot``/``mbox``/``mvalid``),
304
+ this emits the full operator set and frame axioms, so the theory loads as-is.
305
+ The toolkit only *emits* the theory; it does not run Isabelle.
306
+ """
307
+ s4 = gmt_translate(formula)
308
+ # Decide validity with the module's own DECIDABLE S4 oracle (Z3 on the GMT→S4
309
+ # translation), NOT int_valid's bounded Kripke search: int_valid defaults to a
310
+ # 3-world bound, but IPL's finite-model-property bound grows with the formula, so
311
+ # int_valid can wrongly call a non-theorem valid — e.g. (p→q)∨(q→r)∨(r→p) is
312
+ # IPL-INVALID but needs 4 worlds to refute — which would emit a real proof for a
313
+ # NON-theorem that then fails to build. gmt_is_s4_valid is decisive on this
314
+ # propositional S4 fragment, so the emitted proof is sound by construction.
315
+ valid = gmt_is_s4_valid(formula)
316
+ atoms = _isa_atoms(s4)
317
+ R = "\\<Rightarrow>"
318
+ lines = [
319
+ f"theory {theory_name}",
320
+ " imports Main",
321
+ "begin",
322
+ "",
323
+ "(* Goedel--McKinsey--Tarski embedding of intuitionistic propositional logic *)",
324
+ "(* into modal S4, then the Benzmueller shallow embedding into HOL. *)",
325
+ f"(* Original IPL formula: {formula.to_unicode_str()} *)",
326
+ "(* The lemma is a theorem iff the formula is intuitionistically valid. *)",
327
+ "",
328
+ "typedecl i \\<comment> \\<open>the type of worlds (Kripke stages)\\<close>",
329
+ "",
330
+ "consts r :: \"i \\<Rightarrow> i \\<Rightarrow> bool\" \\<comment> \\<open>accessibility (the intuitionistic \\<open>\\<le>\\<close>)\\<close>",
331
+ "",
332
+ "axiomatization where",
333
+ " r_refl: \"r w w\" and",
334
+ " r_trans: \"r w v \\<Longrightarrow> r v u \\<Longrightarrow> r w u\"",
335
+ "",
336
+ "type_synonym \\<sigma> = \"i \\<Rightarrow> bool\" \\<comment> \\<open>a modal proposition\\<close>",
337
+ "",
338
+ f"abbreviation mnot :: \"\\<sigma> {R} \\<sigma>\" (\"{_ISA_NOT}_\" [52] 53)",
339
+ " where \"" + _ISA_NOT + "\\<phi> \\<equiv> \\<lambda>w. \\<not> \\<phi> w\"",
340
+ f"abbreviation mand :: \"\\<sigma> {R} \\<sigma> {R} \\<sigma>\" (infixr \"{_ISA_AND}\" 51)",
341
+ " where \"\\<phi> " + _ISA_AND + " \\<psi> \\<equiv> \\<lambda>w. \\<phi> w \\<and> \\<psi> w\"",
342
+ f"abbreviation mor :: \"\\<sigma> {R} \\<sigma> {R} \\<sigma>\" (infixr \"{_ISA_OR}\" 50)",
343
+ " where \"\\<phi> " + _ISA_OR + " \\<psi> \\<equiv> \\<lambda>w. \\<phi> w \\<or> \\<psi> w\"",
344
+ f"abbreviation mimp :: \"\\<sigma> {R} \\<sigma> {R} \\<sigma>\" (infixr \"{_ISA_IMP}\" 49)",
345
+ " where \"\\<phi> " + _ISA_IMP + " \\<psi> \\<equiv> \\<lambda>w. \\<phi> w \\<longrightarrow> \\<psi> w\"",
346
+ f"abbreviation mbox :: \"\\<sigma> {R} \\<sigma>\" (\"{_ISA_BOX}_\" [52] 53)",
347
+ " where \"" + _ISA_BOX + "\\<phi> \\<equiv> \\<lambda>w. \\<forall>v. r w v \\<longrightarrow> \\<phi> v\"",
348
+ "abbreviation mvalid :: \"\\<sigma> \\<Rightarrow> bool\" (\"\\<lfloor>_\\<rfloor>\")",
349
+ " where \"\\<lfloor>\\<phi>\\<rfloor> \\<equiv> \\<forall>w. \\<phi> w\"",
350
+ "",
351
+ ]
352
+ for a in atoms:
353
+ lines.append(f"consts {_isa_atom_name(a)} :: \"\\<sigma>\"")
354
+ lines.append("")
355
+ lines.append("lemma gmt_goal: \"\\<lfloor> " + _isa_render(s4) + " \\<rfloor>\"")
356
+ if valid:
357
+ # A real, Isabelle-checked proof. The S4 frame facts must be brought into
358
+ # scope (a bare `axiomatization` fact is not in the default claset), then a
359
+ # small method battery closes the GMT goal.
360
+ lines.append(" using r_refl r_trans")
361
+ lines.append(" by (metis r_refl r_trans | meson r_refl r_trans | blast | auto)")
362
+ else:
363
+ lines.append(" \\<comment> \\<open>NOT intuitionistically valid: no proof exists "
364
+ "(see int_countermodel for a Kripke counter-model)\\<close>")
365
+ lines.append(" oops")
366
+ lines.append("")
367
+ lines.append("end")
368
+ return "\n".join(lines) + "\n"
369
+
370
+
371
+ def gmt_is_s4_valid(formula: Node, timeout: int = 10000) -> bool:
372
+ """Return True iff the GMT translation ``T(formula)`` is **S4-valid**.
373
+
374
+ Decided *inside the toolkit* by handing the modal S4 translation to the alethic
375
+ shallow embedding under an S4 frame (reflexive + transitive) and discharging it
376
+ with Z3 (propositional S4 is decidable — there are no object quantifiers, so the
377
+ embedding is decidable here). By the GMT theorem this is True iff ``formula`` is
378
+ intuitionistically valid; :func:`gmt_validity_matches_int_valid` uses it as an
379
+ independent check of :func:`int_valid` that does not need an external HOL prover.
380
+
381
+ TIMEOUT CAVEAT: the problem class is decidable, but Z3 still runs under
382
+ ``timeout`` (ms) — on a large/deeply-nested formula it can answer *unknown*,
383
+ which the Z3 validity oracle conservatively maps to False. So ``True`` is
384
+ always a proof, while ``False`` on a very large formula may mean "timed out",
385
+ not "refuted" — raise ``timeout`` for such inputs, or use
386
+ :func:`unicode_logic_kit.atp.lj.int_prove` (the G4ip proof search), which is a
387
+ terminating decision procedure with no solver timeout in play.
388
+ """
389
+ from unicode_logic_kit.fol.qml import qml_is_valid
390
+ return qml_is_valid(gmt_translate(formula), mode="constant", frame="S4", timeout=timeout)
391
+
392
+
393
+ def gmt_validity_matches_int_valid(formula: Node, max_worlds: int = 3,
394
+ timeout: int = 10000) -> bool:
395
+ """True iff the S4-validity of ``T(formula)`` agrees with :func:`int_valid`.
396
+
397
+ The GMT-correctness oracle: returns True when the GMT→S4 embedding pronounces
398
+ exactly the same verdict as the toolkit's native intuitionistic decision
399
+ procedure. (A green result is the faithfulness witness; it does not by itself
400
+ say whether ``formula`` is valid — call :func:`int_valid` for that.)
401
+ """
402
+ from unicode_logic_kit.semantics.intuitionistic import int_valid
403
+ return gmt_is_s4_valid(formula, timeout=timeout) == int_valid(formula, max_worlds=max_worlds)