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,412 @@
1
+ r"""Isabelle/HOL export for relevant logic B (simplified Routley-Meyer semantics).
2
+
3
+ The modal exporter (:mod:`unicode_logic_kit.hol.isabelle_modal`) embeds a formula
4
+ over a single accessibility relation and reads truth as box/diamond over it. B's
5
+ ``->`` is not that: it is the Priest-Sylvan **simplified** Routley-Meyer clause
6
+ implemented by :mod:`unicode_logic_kit.semantics.relevant` -- normal worlds get a
7
+ "material" reading (quantify over the WHOLE model), non-normal worlds route
8
+ through a ternary relation ``R`` -- so it needs its own embedding, which is what
9
+ this module emits.
10
+
11
+ Like :mod:`unicode_logic_kit.hol.isabelle_conditional`, the embedding is
12
+ *lightweight shallow*: a formula becomes a predicate on worlds (``tau = w =>
13
+ bool``) and each propositional atom is an uninterpreted ``w => bool`` constant.
14
+ The frame structure -- the normal-worlds predicate ``N``, the Routley star
15
+ ``star`` and the ternary relation ``R`` -- are uninterpreted consts, and the
16
+ frame conditions :class:`~unicode_logic_kit.semantics.relevant.RelevantModel`
17
+ enforces in its ``__post_init__`` are bundled into a single ``wellformed``
18
+ proposition that is a **premise of the goal**, never an ``axiomatization``:
19
+
20
+ - ``N`` is nonempty (``relevant.py`` lines 121-122: ``if not normal: raise
21
+ ValueError``);
22
+ - ``star`` is a total involution on ``W`` (lines 126-135: ``star[star[w]] ==
23
+ w`` for every ``w``) -- totality of the ``w`` -> ``w`` map is free in HOL's
24
+ total function type, so only involution needs stating;
25
+ - ``R`` is sourced only at NON-normal worlds, i.e. ``R subseteq (W \ N) x W x
26
+ W`` (lines 142-145: ``if a in normal: raise ValueError``).
27
+
28
+ As a premise (mirroring ``nested Sel`` in ``isabelle_conditional``), nitpick
29
+ constructs ``N``/``star``/``R`` itself and can certify a countermodel as
30
+ *genuine* -- axiomatising them instead would downgrade nitpick's verdict to
31
+ ``quasi_genuine`` and lose the refutation half of the decision procedure.
32
+
33
+ No hereditariness / persistence condition is encoded because
34
+ :class:`~unicode_logic_kit.semantics.relevant.RelevantModel` does not impose one:
35
+ its ``__post_init__`` places no monotonicity constraint on ``valuation``
36
+ (unlike, say, an intuitionistic Kripke model) -- this is exactly the
37
+ "simplified" in "simplified Routley-Meyer semantics" (Priest & Sylvan 1992),
38
+ which drops the compatibility/heredity apparatus of the original relational
39
+ semantics for B. So atoms are plain uninterpreted ``tau`` constants here, same
40
+ as in ``isabelle_conditional``.
41
+
42
+ ``AndC``/``OrC``/``NegC``/``ImpC``/``IffC`` are the same truth conditions as
43
+ :func:`unicode_logic_kit.semantics.relevant.rel_satisfies`'s ``_sat`` clauses, so
44
+ what Isabelle certifies here is what the toolkit's own evaluator computes:
45
+
46
+ - ``w |= A /\ B`` / ``w |= A \/ B`` pointwise (``_sat`` And/Or clauses);
47
+ - ``w |= ~A`` iff ``w* |=/ A`` -- the Routley star (``_sat`` Not clause);
48
+ - ``w |= A -> B``, ``w`` normal, iff every ``A``-world of the WHOLE model is a
49
+ ``B``-world (``_sat`` Implies clause, ``world in model.normal`` branch);
50
+ - ``w |= A -> B``, ``w`` non-normal, iff for every triple ``R(w, x, y)``,
51
+ ``x |= A`` implies ``y |= B`` (``_sat`` Implies clause, ``else`` branch);
52
+ - ``w |= A <-> B`` iff ``w |= A -> B`` and ``w |= B -> A`` at the SAME world
53
+ (``_sat`` Iff clause: it recurses on ``Implies(left, right)`` /
54
+ ``Implies(right, left)``, not a primitive biconditional clause) -- so
55
+ ``IffC`` is defined via ``AndC``/``ImpC``, not as its own case split.
56
+
57
+ Only ``Not``/``And``/``Or``/``Implies``/``Iff`` over nullary atoms are accepted,
58
+ matching :func:`unicode_logic_kit.semantics.relevant._reject_non_propositional`
59
+ exactly (no quantifiers, modalities, lambda terms, ``Xor``, or non-nullary
60
+ atoms -- there is no B reading for them here either).
61
+ """
62
+
63
+ from typing import Optional, Sequence, Tuple
64
+
65
+ from ..fol.nodes import Node, Atom, Not, And, Or, Implies, Iff
66
+ from ..fol._msfl_nodes import key_text
67
+ from ..fol._truth_constants import refuse_truth_constants
68
+ from .deepshallow._common import AtomConsts, theory_name_ok
69
+
70
+ #: Why the B embeddings have no reading of ``$true`` / ``$false`` (the reason the
71
+ #: refusal names, the same one :mod:`unicode_logic_kit.semantics.relevant` gives).
72
+ _NO_CONSTANT_WHY = (
73
+ "relevant logic has two truths and two falsities, an additive pair and a "
74
+ "multiplicative pair, which differ in B, and TPTP's $true / $false names "
75
+ "neither pair")
76
+ from ._ho_common import ThfNames
77
+
78
+ #: The Isabelle type of an embedded formula: a predicate on worlds.
79
+ _TAU = r"w \<Rightarrow> bool"
80
+
81
+ #: Definitions the goal has to unfold before a proof method can see through it.
82
+ _DEFS = ("wellformed_def", "NegC_def", "AndC_def", "OrC_def", "ImpC_def", "IffC_def")
83
+
84
+ #: Proof methods tried in order, as a ``by (... | ...)`` battery. ``smt (verit)``
85
+ #: is tried first as a precaution, not a measured choice: house rules forbid
86
+ #: running ``isabelle build`` from this toolkit, so this ordering has not been
87
+ #: benchmarked. It mirrors ``isabelle_conditional.DEFAULT_METHODS``, whose
88
+ #: verit-first order *was* measured necessary there because ``blast`` diverges
89
+ #: on a goal that needs the sphere-nesting premise. ``ImpC`` here has an
90
+ #: analogous shape -- a case split on ``N x`` with a nested ``forall y z. R x y
91
+ #: z -> ...`` on one branch -- so the same defensive ordering is used rather
92
+ #: than assuming ``blast``-first is safe.
93
+ DEFAULT_METHODS: Tuple[str, ...] = ("smt (verit)", "blast", "force", "metis")
94
+
95
+ _PREAMBLE = r'''typedecl w \<comment> \<open>worlds\<close>
96
+ type_synonym tau = "w \<Rightarrow> bool"
97
+
98
+ consts N :: "w \<Rightarrow> bool" \<comment> \<open>N w: w is a NORMAL world\<close>
99
+ consts star :: "w \<Rightarrow> w" \<comment> \<open>the Routley star w |-> w*\<close>
100
+ consts R :: "w \<Rightarrow> w \<Rightarrow> w \<Rightarrow> bool" \<comment> \<open>the ternary relation R w x y\<close>
101
+
102
+ text \<open>The three well-formedness conditions RelevantModel.__post_init__ enforces
103
+ (semantics/relevant.py lines 121-145): N is nonempty, star is a total involution
104
+ on W (totality is free in HOL's total function type w => w, so only involution
105
+ is stated), and R is sourced only at NON-normal worlds. Bundled as a single
106
+ premise of the goal rather than baked into N/star/R by fiat, so nitpick builds
107
+ N/star/R itself and can certify a counter-model as genuine.\<close>
108
+
109
+ definition wellformed :: "bool" where
110
+ "wellformed \<equiv> (\<exists>x. N x) \<and> (\<forall>x. star (star x) = x)
111
+ \<and> (\<forall>x y z. R x y z \<longrightarrow> \<not> N x)"
112
+
113
+ definition NegC :: "tau \<Rightarrow> tau" where "NegC f \<equiv> \<lambda>x. \<not> f (star x)"
114
+ definition AndC :: "tau \<Rightarrow> tau \<Rightarrow> tau" where "AndC f g \<equiv> \<lambda>x. f x \<and> g x"
115
+ definition OrC :: "tau \<Rightarrow> tau \<Rightarrow> tau" where "OrC f g \<equiv> \<lambda>x. f x \<or> g x"
116
+
117
+ text \<open>A -> B at a NORMAL world w: every A-world of the WHOLE model is a B-world
118
+ (relevant.py _sat, the ``world in model.normal`` branch). At a NON-normal world:
119
+ for every R-triple R w x y, x |= A implies y |= B (the ``else`` branch).\<close>
120
+
121
+ definition ImpC :: "tau \<Rightarrow> tau \<Rightarrow> tau" where
122
+ "ImpC f g \<equiv> \<lambda>x.
123
+ (N x \<longrightarrow> (\<forall>y. f y \<longrightarrow> g y))
124
+ \<and> (\<not> N x \<longrightarrow> (\<forall>y z. R x y z \<longrightarrow> f y \<longrightarrow> g z))"
125
+
126
+ text \<open>A <-> B is (A -> B) /\ (B -> A) at the SAME world (relevant.py _sat's Iff
127
+ clause recurses on Implies(left,right)/Implies(right,left) -- not a primitive
128
+ biconditional clause), so IffC is built from ImpC/AndC rather than case-split
129
+ directly.\<close>
130
+
131
+ definition IffC :: "tau \<Rightarrow> tau \<Rightarrow> tau" where
132
+ "IffC f g \<equiv> AndC (ImpC f g) (ImpC g f)"
133
+ '''
134
+
135
+
136
+ def _encode(formula: Node, atoms: AtomConsts) -> str:
137
+ """Recursive worker for :func:`to_isabelle_relevant`.
138
+
139
+ Mirrors :func:`unicode_logic_kit.semantics.relevant._reject_non_propositional`
140
+ exactly: only nullary :class:`Atom`\\ s and the connectives Not/And/Or/
141
+ Implies/Iff are accepted; everything else -- quantifiers, modalities, lambda
142
+ terms, ``Xor``, non-nullary atoms -- raises :class:`TypeError`, the same
143
+ exception type the oracle raises for the same inputs.
144
+ """
145
+ if isinstance(formula, Atom):
146
+ refuse_truth_constants([formula], "to_isabelle_relevant", _NO_CONSTANT_WHY,
147
+ error=TypeError)
148
+ if formula.args:
149
+ raise TypeError(
150
+ "to_isabelle_relevant: only nullary propositional atoms are "
151
+ f"supported; got {formula.to_unicode_str()!r} with arguments -- "
152
+ "matching semantics.relevant._reject_non_propositional's "
153
+ "rejection of non-nullary atoms.")
154
+ return atoms.name(key_text(formula))
155
+ if isinstance(formula, And):
156
+ return f"(AndC {_encode(formula.left, atoms)} {_encode(formula.right, atoms)})"
157
+ if isinstance(formula, Or):
158
+ return f"(OrC {_encode(formula.left, atoms)} {_encode(formula.right, atoms)})"
159
+ if isinstance(formula, Not):
160
+ return f"(NegC {_encode(formula.formula, atoms)})"
161
+ if isinstance(formula, Implies):
162
+ return f"(ImpC {_encode(formula.left, atoms)} {_encode(formula.right, atoms)})"
163
+ if isinstance(formula, Iff):
164
+ return f"(IffC {_encode(formula.left, atoms)} {_encode(formula.right, atoms)})"
165
+ raise TypeError(
166
+ f"to_isabelle_relevant: unsupported node type {type(formula).__name__} "
167
+ f"in {formula.to_unicode_str()!r}. The B semantics "
168
+ "(semantics.relevant) interprets only the propositional connectives "
169
+ "not/and/or/implies/iff over nullary atoms -- no quantifiers, "
170
+ "modalities, lambda terms, or Xor; see "
171
+ "semantics.relevant._reject_non_propositional.")
172
+
173
+
174
+ def battery_proof(methods: Sequence[str] = DEFAULT_METHODS) -> str:
175
+ """An ``unfolding ...`` + ``by (m1 | m2 | ...)`` proof over the shallow definitions."""
176
+ if not methods:
177
+ raise ValueError("battery_proof: need at least one proof method.")
178
+ alts = " | ".join(methods)
179
+ return f" unfolding {' '.join(_DEFS)}\n by ({alts})"
180
+
181
+
182
+ def nitpick_proof(card: str = "1-3", timeout: int = 60) -> str:
183
+ """A ``nitpick[expect = genuine]`` refutation attempt over the world type ``w``."""
184
+ return (f" nitpick[card w = {card}, timeout = {int(timeout)}, "
185
+ f"expect = genuine]\n oops")
186
+
187
+
188
+ def to_isabelle_relevant(formula: Node, *,
189
+ theory_name: str = "RelDecide",
190
+ proof: Optional[str] = None,
191
+ lemma_name: str = "goal") -> str:
192
+ r"""Emit a self-contained theory asserting ``formula``'s validity in B.
193
+
194
+ The goal is ``wellformed \<Longrightarrow> \<forall>x. N x \<longrightarrow> phi x`` --
195
+ truth at every NORMAL world of every interpretation satisfying the frame
196
+ conditions bundled into ``wellformed`` (see the module docstring), matching
197
+ :func:`unicode_logic_kit.semantics.relevant.rel_valid`'s "true at every normal
198
+ world of every interpretation" contract. ``wellformed`` is a premise rather
199
+ than an axiom so that nitpick can build ``N``/``star``/``R`` itself and
200
+ certify a counter-model as genuine.
201
+
202
+ Args:
203
+ formula: the AST node to decide -- the propositional connectives
204
+ ``¬ ∧ ∨ → ↔`` over nullary atoms.
205
+ theory_name: the Isabelle theory identifier.
206
+ proof: the proof text under the lemma; defaults to :func:`battery_proof`.
207
+ Pass :func:`nitpick_proof` to emit the refutation theory instead.
208
+ lemma_name: the lemma identifier.
209
+
210
+ Raises:
211
+ ValueError: on an illegal theory name.
212
+ TypeError: propagated from :func:`_encode` -- a quantifier, modality,
213
+ lambda term, ``Xor``, or non-nullary atom, mirroring
214
+ :func:`unicode_logic_kit.semantics.relevant._reject_non_propositional`.
215
+ """
216
+ if not theory_name_ok(theory_name):
217
+ raise ValueError(f"illegal theory name {theory_name!r}.")
218
+ atoms = AtomConsts()
219
+ term = _encode(formula, atoms)
220
+ body = [f"theory {theory_name}", " imports Main", "begin", "", _PREAMBLE]
221
+ decls = atoms.decls(_TAU)
222
+ if decls:
223
+ body.append("\n".join(decls))
224
+ body.append("")
225
+ body.append(
226
+ f'lemma {lemma_name}: "wellformed \\<Longrightarrow> '
227
+ f'\\<forall>x. N x \\<longrightarrow> ({term}) x"')
228
+ body.append(proof if proof is not None else battery_proof())
229
+ body.append("")
230
+ body.append("end")
231
+ return "\n".join(body) + "\n"
232
+
233
+
234
+ # --------------------------------------------------------------------------
235
+ # THF
236
+ # --------------------------------------------------------------------------
237
+ #
238
+ # The THF sibling of the theory above: the same shallow embedding (a formula
239
+ # is true or false AT A WORLD; ``N``/``star``/``R`` uninterpreted; the frame
240
+ # conditions bundled into ``wellformed``), for a higher-order ATP (Leo-III,
241
+ # Vampire-THF, Satallax) instead of Isabelle. The conjecture is
242
+ # ``wellformed => ![X:w] : ((N @ X) => φ_at(X))``, matching the Isabelle goal's
243
+ # "true at every NORMAL world of every interpretation" contract exactly.
244
+ #
245
+ # UNLIKE the Isabelle encoding (and an earlier draft of this function), ``φ``
246
+ # is NOT built from separately-declared ``NegC``/``AndC``/``OrC``/``ImpC``/
247
+ # ``IffC`` *combinators* that :func:`_thf_encode` would apply to already-built
248
+ # ``tau``-typed subterms (``impc @ (andc @ p @ q) @ p``, mirroring the
249
+ # Isabelle term exactly). :func:`_thf_encode` instead THREADS the current
250
+ # world through the recursion and emits ``φ``, at that world, as one native
251
+ # THF formula (``&``/``|``/``~``/``=>``/``!``/``?`` applied to already-a-world
252
+ # atoms) -- the same "world-relativised connective, no free-standing
253
+ # constant" style :mod:`unicode_logic_kit.hol.ho_modal`'s box/diamond nesting
254
+ # uses. This is NOT a change of meaning: a combinator applied to arguments and
255
+ # the fully-inlined, world-threaded reading of the same formula are
256
+ # beta-equivalent term for term (only ``ImpC``'s two case-split branches are
257
+ # genuinely new content per node, and those are exactly the N/R clauses below,
258
+ # transcribed unchanged from ``_PREAMBLE``). It is a change of PROOF-SEARCH
259
+ # DIFFICULTY: measured by hand with a battery of THF micro-examples in the
260
+ # scratchpad, Vampire 5.0.1's default portfolio needs real higher-order
261
+ # UNIFICATION to match a combinator's parameter against another combinator's
262
+ # PARTIAL APPLICATION (``F := (andc @ p @ q)`` to unfold ``impc``'s body) and
263
+ # failed to close even ``(P∧Q)→P`` in 60s / a 150-strategy CASC sweep in 30s;
264
+ # feeding it the same formula already reduced to ordinary quantified
265
+ # first-order-shaped THF over ``w`` (no function-valued arguments passed
266
+ # between macros at all) closes in well under a second. ``wellformed`` stays a
267
+ # single named, ARGUMENT-FREE ``$o`` ``definition`` (unfolding a nullary
268
+ # biconditional needs no higher-order unification either way) and stays a
269
+ # PREMISE of the conjecture rather than an ``axiom``, for the reason given in
270
+ # the module docstring: a model finder run on this problem's negation should
271
+ # still be free to build its own N/star/R rather than trusting an axiomatised
272
+ # triple.
273
+
274
+ #: The embedding's own functors, pre-claimed so no user atom's THF stem can
275
+ #: collide with them (see hol._ho_common.ThfNames).
276
+ _THF_RESERVED = ("w", "n", "star", "r", "wellformed")
277
+
278
+ _THF_PRELUDE = [
279
+ "% Relevant logic B (simplified Routley-Meyer semantics) -> THF.",
280
+ "% Shallow embedding: a formula is true/false AT A WORLD; N/star/R are",
281
+ "% uninterpreted and their frame conditions are bundled into `wellformed`,",
282
+ "% a PREMISE of the conjecture (never an axiom) -- see isabelle_relevant.py's",
283
+ "% module docstring, _PREAMBLE, and the comment above _THF_PRELUDE for why",
284
+ "% the connectives below are inlined at each world rather than routed",
285
+ "% through separately-declared NegC/AndC/OrC/ImpC/IffC combinators.",
286
+ "thf(w_type, type, ( w : $tType )).",
287
+ "thf(n_type, type, ( n : w > $o )).",
288
+ "thf(star_type, type, ( star : w > w )).",
289
+ "thf(r_type, type, ( r : w > w > w > $o )).",
290
+ "thf(wellformed_type, type, ( wellformed : $o )).",
291
+ "thf(wellformed_def, definition, ( wellformed <=> "
292
+ "( ( ? [X: w] : ( n @ X ) ) "
293
+ "& ( ! [X: w] : ( ( star @ ( star @ X ) ) = X ) ) "
294
+ "& ( ! [X: w, Y: w, Z: w] : ( ( r @ X @ Y @ Z ) => ( ~ ( n @ X ) ) ) ) ) )).",
295
+ ]
296
+
297
+
298
+ def _thf_implies_at(left: Node, right: Node, world: str,
299
+ names: ThfNames, depth: int) -> str:
300
+ """The world-threaded rendering of ``left -> right`` at ``world``.
301
+
302
+ ImpC's own case split (:func:`_encode`'s ``ImpC`` clause, unchanged): at a
303
+ NORMAL world every ``left``-world of the WHOLE model is a ``right``-world;
304
+ at a non-normal world, every R-triple routes ``left`` to ``right``. ``Y``/
305
+ ``Z`` are named from ``depth`` (not a running total) so that two SIBLING
306
+ ``->`` nodes may freely reuse the same token -- each ``!`` binds only its
307
+ own subformula -- while a NESTED ``->`` (reached through ``left``/
308
+ ``right``, always recursed at ``depth + 1``) never reuses an ancestor's.
309
+
310
+ This deliberately parallels :mod:`unicode_logic_kit.hol.ho_modal`'s OWN
311
+ Kripke-world binders (``f"W{depth}"`` in its ``_thf``/``_thf_arg``), not
312
+ :func:`~unicode_logic_kit.hol._ho_common.bound_token`: ``bound_token`` renames
313
+ a USER-level binder apart from other in-scope user binders of the SAME
314
+ source name (``thirdorder.py``/``ho_modal.py`` call it for the caller's own
315
+ ``Quantifier``/``SecondOrderQuantifier``/``Lambda`` nodes), and no such node
316
+ ever reaches this encoder -- ``_thf_encode`` accepts only nullary atoms and
317
+ the propositional connectives, so there is no user binder to rename apart
318
+ from anything. ``Y{depth}``/``Z{depth}`` name this EMBEDDING's own frame
319
+ quantifiers instead, exactly the role ``ho_modal.py``'s plain ``W{depth}``
320
+ plays for its own world binders (see the comment above its ``_thf``).
321
+ """
322
+ y, z = f"Y{depth}", f"Z{depth}"
323
+ left_at_y = _thf_encode(left, y, names, depth + 1)
324
+ right_at_y = _thf_encode(right, y, names, depth + 1)
325
+ right_at_z = _thf_encode(right, z, names, depth + 1)
326
+ return (f"( ( ( n @ {world} ) => ( ! [{y}: w] : "
327
+ f"( {left_at_y} => {right_at_y} ) ) ) "
328
+ f"& ( ( ~ ( n @ {world} ) ) => ( ! [{y}: w, {z}: w] : "
329
+ f"( ( r @ {world} @ {y} @ {z} ) => "
330
+ f"( {left_at_y} => {right_at_z} ) ) ) ) )")
331
+
332
+
333
+ def _thf_encode(formula: Node, world: str, names: ThfNames, depth: int = 0) -> str:
334
+ """Recursive worker for :func:`to_thf_relevant`: ``formula`` rendered TRUE AT ``world``.
335
+
336
+ Structurally identical to :func:`_encode`'s own recursion -- only nullary
337
+ :class:`Atom`\\ s and Not/And/Or/Implies/Iff are accepted, everything else
338
+ raises :class:`TypeError`, the same refusal :func:`_encode` makes,
339
+ mirroring :func:`unicode_logic_kit.semantics.relevant._reject_non_propositional`
340
+ -- but threading ``world`` (a THF term, not a fixed variable: ``¬`` passes
341
+ down ``( star @ world )``) instead of building a free-standing ``tau``
342
+ term. See the comment above ``_THF_PRELUDE`` for why.
343
+ """
344
+ if isinstance(formula, Atom):
345
+ refuse_truth_constants([formula], "to_thf_relevant", _NO_CONSTANT_WHY,
346
+ error=TypeError)
347
+ if formula.args:
348
+ raise TypeError(
349
+ "to_thf_relevant: only nullary propositional atoms are "
350
+ f"supported; got {formula.to_unicode_str()!r} with arguments -- "
351
+ "matching semantics.relevant._reject_non_propositional's "
352
+ "rejection of non-nullary atoms.")
353
+ functor = names.functor("predicate", key_text(formula))
354
+ return f"( {functor} @ {world} )"
355
+ if isinstance(formula, And):
356
+ return (f"( {_thf_encode(formula.left, world, names, depth)} & "
357
+ f"{_thf_encode(formula.right, world, names, depth)} )")
358
+ if isinstance(formula, Or):
359
+ return (f"( {_thf_encode(formula.left, world, names, depth)} | "
360
+ f"{_thf_encode(formula.right, world, names, depth)} )")
361
+ if isinstance(formula, Not):
362
+ return f"( ~ {_thf_encode(formula.formula, f'( star @ {world} )', names, depth)} )"
363
+ if isinstance(formula, Implies):
364
+ return _thf_implies_at(formula.left, formula.right, world, names, depth)
365
+ if isinstance(formula, Iff):
366
+ return (f"( {_thf_implies_at(formula.left, formula.right, world, names, depth)} "
367
+ f"& {_thf_implies_at(formula.right, formula.left, world, names, depth)} )")
368
+ raise TypeError(
369
+ f"to_thf_relevant: unsupported node type {type(formula).__name__} "
370
+ f"in {formula.to_unicode_str()!r}. The B semantics "
371
+ "(semantics.relevant) interprets only the propositional connectives "
372
+ "not/and/or/implies/iff over nullary atoms -- no quantifiers, "
373
+ "modalities, lambda terms, or Xor; see "
374
+ "semantics.relevant._reject_non_propositional.")
375
+
376
+
377
+ def to_thf_relevant(formula: Node) -> str:
378
+ r"""Emit a self-contained THF (TH0) problem asserting ``formula``'s validity in B.
379
+
380
+ The THF sibling of :func:`to_isabelle_relevant`: the same shallow embedding
381
+ (a formula is true/false at a world; ``N``/``star``/``R`` uninterpreted;
382
+ the frame conditions bundled into ``wellformed``), for a higher-order ATP
383
+ (Leo-III, Vampire-THF, Satallax) instead of Isabelle. The conjecture is
384
+ ``wellformed => ![X:w] : ((N @ X) => φ_at(X))`` -- truth at every NORMAL
385
+ world of every interpretation satisfying the frame conditions, matching
386
+ :func:`unicode_logic_kit.semantics.relevant.rel_valid`'s "true at every
387
+ normal world of every interpretation" contract, exactly as
388
+ :func:`to_isabelle_relevant`'s goal does (see the comment above
389
+ ``_THF_PRELUDE`` for why ``φ_at(X)`` is rendered inline rather than as a
390
+ combinator applied to ``X``). As elsewhere in the toolkit, this function
391
+ only emits the problem; it does not run a prover.
392
+
393
+ Args:
394
+ formula: the AST node to encode -- the propositional connectives
395
+ ``¬ ∧ ∨ → ↔`` over nullary atoms.
396
+
397
+ Raises:
398
+ TypeError: propagated from :func:`_thf_encode` -- a quantifier,
399
+ modality, lambda term, ``Xor``, or non-nullary atom, mirroring
400
+ :func:`to_isabelle_relevant`'s own refusal and
401
+ :func:`unicode_logic_kit.semantics.relevant._reject_non_propositional`.
402
+ """
403
+ names = ThfNames(reserved=_THF_RESERVED)
404
+ body = _thf_encode(formula, "X", names)
405
+ lines = list(_THF_PRELUDE)
406
+ for label in sorted({key_text(atom) for atom in formula.atoms()}):
407
+ functor = names.functor("predicate", label)
408
+ lines.append(f"thf({functor}_type, type, ( {functor} : w > $o )).")
409
+ lines.append(
410
+ "thf(goal, conjecture, ( wellformed => "
411
+ f"( ! [X: w] : ( ( n @ X ) => {body} ) ) )).")
412
+ return "\n".join(lines) + "\n"