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,112 @@
1
+ """Lambek-calculus node classes and the ``lambek`` parser mode.
2
+
3
+ The Lambek calculus L (Lambek 1958) is the substructural logic of ORDERED
4
+ resources — the type logic of categorial grammar. On top of dropping weakening
5
+ and contraction (as linear logic does) it also drops EXCHANGE: the antecedent
6
+ of a sequent is a sequence, not a multiset, so ``A, A\\B ⊢ B`` is derivable but
7
+ ``A\\B, A ⊢ B`` is not. The ``lambek`` mode (``MSFLParser(lambek=True)``)
8
+ provides the three connectives over atomic categories (nullary predicates such
9
+ as ``NP``, ``S``):
10
+
11
+ - ``A • B`` the product (A followed by B),
12
+ - ``A \\ B`` *under*: combines with an ``A`` on its LEFT to give a ``B``,
13
+ - ``B / A`` *over*: combines with an ``A`` on its RIGHT to give a ``B``.
14
+
15
+ ``•`` binds tighter than ``\\`` and ``/`` (the categorial convention); ``\\``
16
+ and ``/`` are right-associative in the grammar — parenthesise where the
17
+ non-associative reading matters. Derivability lives in
18
+ ``unicode_logic_kit.atp.lambek`` (a complete, terminating cut-free decision
19
+ procedure — L is decidable). There is no classical export: collapsing the
20
+ directional implications to ``→`` erases word order, which is the calculus's
21
+ entire point, so ``to_z3`` / ``to_prover9`` / ``to_tptp`` reject.
22
+ """
23
+
24
+ from dataclasses import dataclass
25
+
26
+ from ._fol_nodes import (
27
+ Node, Z3Env,
28
+ NODE_CLASSES, register_operator, register_parser_op, _fold_binary,
29
+ )
30
+
31
+ # Shared rejection message: the order-forgetting collapse is deliberately absent.
32
+ _NO_LAMBEK_EXPORT = (
33
+ "Lambek-calculus types have no classical first-order export: collapsing the "
34
+ "directional implications \\ and / to → forgets word order, which is what "
35
+ "the calculus is about. Use the decision procedure "
36
+ "(unicode_logic_kit.lambek_derivable / lambek_prove) instead."
37
+ )
38
+
39
+
40
+ def _lambek_binary(name, doc):
41
+ """Build a frozen binary Lambek node class with the shared boilerplate."""
42
+
43
+ @dataclass(frozen=True)
44
+ class _Binary(Node):
45
+ left: Node
46
+ right: Node
47
+
48
+ def to_dict(self):
49
+ """Serialise to dict with type tag and recursively serialised operands."""
50
+ return {"_type": name, "left": self.left.to_dict(),
51
+ "right": self.right.to_dict()}
52
+
53
+ @staticmethod
54
+ def from_dict(d):
55
+ """Deserialise from a dict produced by to_dict."""
56
+ return NODE_CLASSES[name](Node.from_dict(d["left"]),
57
+ Node.from_dict(d["right"]))
58
+
59
+ def to_z3(self, env: Z3Env = None):
60
+ """Reject Z3 export: Lambek types have no order-preserving collapse."""
61
+ raise NotImplementedError(_NO_LAMBEK_EXPORT)
62
+
63
+ def to_prover9(self) -> str:
64
+ """Reject Prover9 export: Lambek types have no order-preserving collapse."""
65
+ raise NotImplementedError(_NO_LAMBEK_EXPORT)
66
+
67
+ def to_tptp(self) -> str:
68
+ """Reject TPTP export: Lambek types have no order-preserving collapse."""
69
+ raise NotImplementedError(_NO_LAMBEK_EXPORT)
70
+
71
+ _Binary.__name__ = _Binary.__qualname__ = name
72
+ _Binary.__doc__ = doc
73
+ return _Binary
74
+
75
+
76
+ Product = _lambek_binary(
77
+ "Product",
78
+ "The Lambek product ``A • B``: an ``A`` immediately followed by a ``B``.")
79
+ Under = _lambek_binary(
80
+ "Under",
81
+ "The Lambek *under* type ``A \\\\ B``: combines with an ``A`` on its LEFT "
82
+ "to yield a ``B`` (fields are as written: left=A, right=B).")
83
+ Over = _lambek_binary(
84
+ "Over",
85
+ "The Lambek *over* type ``B / A``: combines with an ``A`` on its RIGHT "
86
+ "to yield a ``B`` (fields are as written: left=B, right=A).")
87
+
88
+
89
+ NODE_CLASSES.update({"Product": Product, "Under": Under, "Over": Over})
90
+
91
+
92
+ # =========================
93
+ # Renderer + parser registration — the "lambek" mode
94
+ # =========================
95
+ #
96
+ # All three connectives are regular registry operators (no renderer branches).
97
+ # '\\' and '/' sit at the implication level (right-assoc; parenthesise for the
98
+ # non-associative reading), '•' at the tighter same-level group. The '/' glyph
99
+ # is arithmetic division in the TERM layer of other modes; at the formula level
100
+ # of the lambek mode it is unambiguous (categories are formulas, not terms).
101
+
102
+ register_operator(Product, "level2", "•", "\\bullet", 3)
103
+ register_operator(Under, "binary_implies", "\\", "\\backslash", 2)
104
+ register_operator(Over, "binary_implies", "/", "/", 2)
105
+
106
+ register_parser_op(Product, "lambek", "level2", "product_", '"•"',
107
+ lambda items: _fold_binary(items, Product),
108
+ only_name="only_product")
109
+ register_parser_op(Under, "lambek", "implication", "under_", '"\\\\"',
110
+ lambda items: Under(items[0], items[1]))
111
+ register_parser_op(Over, "lambek", "implication", "over_", '"/"',
112
+ lambda items: Over(items[0], items[1]))
@@ -0,0 +1,352 @@
1
+ """Intuitionistic linear logic (ILL) node classes and the ``linear`` parser mode.
2
+
3
+ Linear logic (Girard 1987) is the substructural logic in which formulas are
4
+ RESOURCES: the structural rules of weakening and contraction are dropped, so a
5
+ hypothesis is used exactly once unless explicitly banked under ``!``. The
6
+ ``linear`` mode (``MSFLParser(linear=True)``) covers propositional
7
+ intuitionistic linear logic:
8
+
9
+ - ``⊗`` multiplicative conjunction (*tensor*: both resources, side by side),
10
+ - ``⊸`` linear implication (*lollipop*: consume the antecedent, produce the
11
+ consequent),
12
+ - ``&`` additive conjunction (*with*: a choice offered — either can be taken,
13
+ not both),
14
+ - ``⊕`` additive disjunction (one of the two, the provider chooses),
15
+ - ``!`` the exponential (*of course*: an unlimited, reusable supply),
16
+ - ``𝟙`` the multiplicative unit (the empty resource),
17
+ - ``⊤`` the additive truth (*top*: always available, no information — the
18
+ additive unit of ``&``; ``⊤R`` holds for ANY context, so ⊤ carries no
19
+ information about what it takes to reach it),
20
+ - ``𝟘`` the additive falsity (*zero*: the impossible resource — the additive
21
+ unit of ``⊕``; ``0L`` derives ANY sequent once 𝟘 is in the antecedent, ILL's
22
+ analogue of ex falso).
23
+
24
+ ``⊗`` / ``&`` / ``⊕`` sit in the no-mixing same-level group (parenthesise mixed
25
+ chains); ``⊸`` is right-associative; ``!`` binds like ¬. The glyph ``⊕`` is the
26
+ classical Xor / fuzzy strong disjunction in OTHER modes — per-mode glyph reuse
27
+ is the established pattern here. Provability lives in
28
+ ``unicode_logic_kit.atp.linear`` (a cut-free backward-search prover, complete and
29
+ terminating for the !-free fragment); there is no classical export — the
30
+ classical collapse (⊗,& → ∧; ⊕ → ∨; ⊸ → →; drop !) would erase exactly the
31
+ resource distinctions the logic is about, so ``to_z3`` / ``to_prover9`` /
32
+ ``to_tptp`` reject.
33
+ """
34
+
35
+ from dataclasses import dataclass
36
+
37
+ from ._fol_nodes import (
38
+ Node, Z3Env,
39
+ NODE_CLASSES, register_operator, register_parser_op, _fold_binary,
40
+ )
41
+
42
+ # Shared rejection message: the classical collapse is deliberately not provided.
43
+ _NO_LINEAR_EXPORT = (
44
+ "Linear-logic formulas have no classical first-order export: collapsing "
45
+ "⊗/& to ∧, ⊕ to ∨, ⊸ to →, and dropping ! erases the resource distinctions "
46
+ "the logic exists to draw. Use the sequent prover "
47
+ "(unicode_logic_kit.ill_prove / ill_derivable) instead."
48
+ )
49
+
50
+
51
+ def _linear_binary(name, doc):
52
+ """Build a frozen binary linear-logic node class with the shared boilerplate."""
53
+
54
+ @dataclass(frozen=True)
55
+ class _Binary(Node):
56
+ left: Node
57
+ right: Node
58
+
59
+ def to_dict(self):
60
+ """Serialise to dict with type tag and recursively serialised operands."""
61
+ return {"_type": name, "left": self.left.to_dict(),
62
+ "right": self.right.to_dict()}
63
+
64
+ @staticmethod
65
+ def from_dict(d):
66
+ """Deserialise from a dict produced by to_dict."""
67
+ return NODE_CLASSES[name](Node.from_dict(d["left"]),
68
+ Node.from_dict(d["right"]))
69
+
70
+ def to_z3(self, env: Z3Env = None):
71
+ """Reject Z3 export: linear connectives have no classical collapse here."""
72
+ raise NotImplementedError(_NO_LINEAR_EXPORT)
73
+
74
+ def to_prover9(self) -> str:
75
+ """Reject Prover9 export: linear connectives have no classical collapse here."""
76
+ raise NotImplementedError(_NO_LINEAR_EXPORT)
77
+
78
+ def to_tptp(self) -> str:
79
+ """Reject TPTP export: linear connectives have no classical collapse here."""
80
+ raise NotImplementedError(_NO_LINEAR_EXPORT)
81
+
82
+ _Binary.__name__ = _Binary.__qualname__ = name
83
+ _Binary.__doc__ = doc
84
+ return _Binary
85
+
86
+
87
+ Tensor = _linear_binary(
88
+ "Tensor",
89
+ "Multiplicative conjunction ``A ⊗ B``: both resources, available side by side.")
90
+ With = _linear_binary(
91
+ "With",
92
+ "Additive conjunction ``A & B``: an offered choice — either can be taken, "
93
+ "but only one.")
94
+ OPlus = _linear_binary(
95
+ "OPlus",
96
+ "Additive disjunction ``A ⊕ B``: one of the two holds, the provider chooses.")
97
+ LinearImplies = _linear_binary(
98
+ "LinearImplies",
99
+ "Linear implication ``A ⊸ B``: consuming one ``A`` produces one ``B``.")
100
+
101
+
102
+ @dataclass(frozen=True)
103
+ class OfCourse(Node):
104
+ """The exponential ``!A``: an unlimited, reusable supply of the resource A.
105
+
106
+ ``!`` reintroduces weakening and contraction for the banked formula — the
107
+ only gateway back to classical-style reuse.
108
+ """
109
+
110
+ formula: Node
111
+
112
+ def to_dict(self):
113
+ """Serialise to dict with the recursively serialised operand."""
114
+ return {"_type": "OfCourse", "formula": self.formula.to_dict()}
115
+
116
+ @staticmethod
117
+ def from_dict(d):
118
+ """Deserialise an OfCourse from a dict produced by to_dict."""
119
+ return OfCourse(Node.from_dict(d["formula"]))
120
+
121
+ def to_z3(self, env: Z3Env = None):
122
+ """Reject Z3 export: the exponential has no classical collapse here."""
123
+ raise NotImplementedError(_NO_LINEAR_EXPORT)
124
+
125
+ def to_prover9(self) -> str:
126
+ """Reject Prover9 export: the exponential has no classical collapse here."""
127
+ raise NotImplementedError(_NO_LINEAR_EXPORT)
128
+
129
+ def to_tptp(self) -> str:
130
+ """Reject TPTP export: the exponential has no classical collapse here."""
131
+ raise NotImplementedError(_NO_LINEAR_EXPORT)
132
+
133
+
134
+ @dataclass(frozen=True)
135
+ class One(Node):
136
+ """The multiplicative unit ``𝟙``: the empty resource (⊗'s neutral element)."""
137
+
138
+ def _tree_parts(self):
139
+ """Return the unit glyph as an atomic tree label."""
140
+ return "𝟙", []
141
+
142
+ def to_dict(self):
143
+ """Serialise to dict with the type tag only (the unit has no children)."""
144
+ return {"_type": "One"}
145
+
146
+ @staticmethod
147
+ def from_dict(d):
148
+ """Deserialise a One from a dict produced by to_dict."""
149
+ return One()
150
+
151
+ def to_z3(self, env: Z3Env = None):
152
+ """Reject Z3 export: the unit has no classical collapse here."""
153
+ raise NotImplementedError(_NO_LINEAR_EXPORT)
154
+
155
+ def to_prover9(self) -> str:
156
+ """Reject Prover9 export: the unit has no classical collapse here."""
157
+ raise NotImplementedError(_NO_LINEAR_EXPORT)
158
+
159
+ def to_tptp(self) -> str:
160
+ """Reject TPTP export: the unit has no classical collapse here."""
161
+ raise NotImplementedError(_NO_LINEAR_EXPORT)
162
+
163
+
164
+ @dataclass(frozen=True)
165
+ class Top(Node):
166
+ """The additive truth ``⊤``: always derivable (rule ``⊤R``, no premise, any
167
+ context) — the additive unit of ``&`` (``With``). Carries no information: unlike
168
+ ``𝟙`` (which must be consumed exactly, ``⊗``'s unit), ``⊤`` is reachable from
169
+ ANY antecedent, so e.g. ``⊤ ⊸ A`` is NOT derivable in general (having reached
170
+ ⊤ tells the consumer nothing that would let it produce A)."""
171
+
172
+ def _tree_parts(self):
173
+ """Return the top glyph as an atomic tree label."""
174
+ return "⊤", []
175
+
176
+ def to_dict(self):
177
+ """Serialise to dict with the type tag only (⊤ has no children)."""
178
+ return {"_type": "Top"}
179
+
180
+ @staticmethod
181
+ def from_dict(d):
182
+ """Deserialise a Top from a dict produced by to_dict."""
183
+ return Top()
184
+
185
+ def to_z3(self, env: Z3Env = None):
186
+ """Reject Z3 export: ⊤ has no classical collapse here."""
187
+ raise NotImplementedError(_NO_LINEAR_EXPORT)
188
+
189
+ def to_prover9(self) -> str:
190
+ """Reject Prover9 export: ⊤ has no classical collapse here."""
191
+ raise NotImplementedError(_NO_LINEAR_EXPORT)
192
+
193
+ def to_tptp(self) -> str:
194
+ """Reject TPTP export: ⊤ has no classical collapse here."""
195
+ raise NotImplementedError(_NO_LINEAR_EXPORT)
196
+
197
+
198
+ @dataclass(frozen=True)
199
+ class Zero(Node):
200
+ """The additive falsity ``𝟘``: once in the antecedent, ANY sequent is derivable
201
+ (rule ``0L``, no premise) — the additive unit of ``⊕`` (``OPlus``) and ILL's
202
+ analogue of ex falso quodlibet. Dual to ``⊤``: ``⊤`` is always a valid RIGHT
203
+ conclusion, ``𝟘`` is always a usable LEFT hypothesis."""
204
+
205
+ def _tree_parts(self):
206
+ """Return the zero glyph as an atomic tree label."""
207
+ return "𝟘", []
208
+
209
+ def to_dict(self):
210
+ """Serialise to dict with the type tag only (𝟘 has no children)."""
211
+ return {"_type": "Zero"}
212
+
213
+ @staticmethod
214
+ def from_dict(d):
215
+ """Deserialise a Zero from a dict produced by to_dict."""
216
+ return Zero()
217
+
218
+ def to_z3(self, env: Z3Env = None):
219
+ """Reject Z3 export: 𝟘 has no classical collapse here."""
220
+ raise NotImplementedError(_NO_LINEAR_EXPORT)
221
+
222
+ def to_prover9(self) -> str:
223
+ """Reject Prover9 export: 𝟘 has no classical collapse here."""
224
+ raise NotImplementedError(_NO_LINEAR_EXPORT)
225
+
226
+ def to_tptp(self) -> str:
227
+ """Reject TPTP export: 𝟘 has no classical collapse here."""
228
+ raise NotImplementedError(_NO_LINEAR_EXPORT)
229
+
230
+
231
+ NODE_CLASSES.update({
232
+ "Tensor": Tensor, "With": With, "OPlus": OPlus,
233
+ "LinearImplies": LinearImplies, "OfCourse": OfCourse, "One": One,
234
+ "Top": Top, "Zero": Zero,
235
+ })
236
+
237
+
238
+ # =========================
239
+ # Renderer + parser registration — the "linear" mode
240
+ # =========================
241
+ #
242
+ # Every connective is a regular registry operator, so the Unicode/LaTeX
243
+ # renderers need no new branch; only the nullary constants (𝟙, and now ⊤/𝟘)
244
+ # need an explicit renderer branch in _msfl_nodes (register_operator has no
245
+ # "nullary" fixity — see _fol_nodes._VALID_FIXITIES — because every fixity it
246
+ # supports arranges at least one operand). PARSING ⊤/𝟘 needs no such branch
247
+ # (register_parser_op below is self-contained), so 'MSFLParser(linear=True)'
248
+ # already round-trips them; Node.to_unicode_str()/to_latex() on a formula
249
+ # that CONTAINS ⊤ or 𝟘 also renders correctly, via the central _msfl_nodes
250
+ # branch (see the comment above render_ill_formula below for why that
251
+ # function exists alongside to_unicode_str() anyway — a stricter
252
+ # round-trip guarantee, not a Top/Zero workaround; its only caller in this
253
+ # kit is atp.linear.ILLSequent).
254
+
255
+ register_operator(Tensor, "level2", "⊗", "\\otimes", 3)
256
+ register_operator(With, "level2", "&", "\\mathbin{\\&}", 3)
257
+ register_operator(OPlus, "level2", "⊕", "\\oplus", 3)
258
+ register_operator(LinearImplies, "binary_implies", "⊸", "\\multimap", 2)
259
+ register_operator(OfCourse, "prefix", "!", "\\mathord{!}", 4)
260
+
261
+ register_parser_op(Tensor, "linear", "level2", "tensor_", '"⊗"',
262
+ lambda items: _fold_binary(items, Tensor),
263
+ only_name="only_tensor")
264
+ register_parser_op(With, "linear", "level2", "with_", '"&"',
265
+ lambda items: _fold_binary(items, With),
266
+ only_name="only_with")
267
+ register_parser_op(OPlus, "linear", "level2", "oplus_", '"⊕"',
268
+ lambda items: _fold_binary(items, OPlus),
269
+ only_name="only_oplus")
270
+ register_parser_op(LinearImplies, "linear", "implication", "limp_", '"⊸"',
271
+ lambda items: LinearImplies(items[0], items[1]))
272
+ register_parser_op(OfCourse, "linear", "prefix", "ofcourse_", '"!" prefix',
273
+ lambda items: OfCourse(items[0]))
274
+ register_parser_op(One, "linear", "prefix", "one_", '"𝟙"',
275
+ lambda items: One())
276
+ register_parser_op(Top, "linear", "prefix", "top_", '"⊤"',
277
+ lambda items: Top())
278
+ register_parser_op(Zero, "linear", "prefix", "zero_", '"𝟘"',
279
+ lambda items: Zero())
280
+
281
+
282
+ # ---------------------------------------------------------------------------
283
+ # A self-contained, fully-parenthesised Unicode renderer for ILL formulas.
284
+ # ---------------------------------------------------------------------------
285
+ #
286
+ # Node.to_unicode_str() dispatches through unicode_logic_kit.fol._msfl_nodes,
287
+ # whose explicit branch table special-cases all three nullary linear-mode
288
+ # constants — One ("𝟙"), Top ("⊤"), Zero ("𝟘") — wherever they occur in a
289
+ # formula's tree, including nested, e.g. inside a Tensor; none of them raises.
290
+ # Verified live: Top().to_unicode_str() == "⊤", Zero().to_latex() ==
291
+ # "\\mathbf{0}", and Tensor(Atom("P", []), Zero()).to_unicode_str() ==
292
+ # "P ⊗ 𝟘". So render_ill_formula below is NOT a Top/Zero workaround. The
293
+ # reason its caller in this kit, atp.linear.ILLSequent, uses it instead of
294
+ # to_unicode_str() is that to_unicode_str() does precedence-based MINIMAL
295
+ # parenthesisation — e.g. Tensor(Atom("P", []), Atom("Q", [])).to_unicode_str()
296
+ # == "P ⊗ Q" — whereas render_ill_formula fully parenthesises every compound
297
+ # subformula (the same Tensor renders as "(P ⊗ Q)"; always safe, since
298
+ # MSFLParser accepts redundant parens anywhere), which guarantees that
299
+ # round-tripping through MSFLParser(linear=True).parse reproduces a
300
+ # structurally equal AST — a guarantee minimal parenthesisation does not make
301
+ # in general. It is a deliberately-conservative renderer for its
302
+ # round-trip-sensitive caller, not a substitute for a rendering gap.
303
+ def render_ill_formula(f: Node) -> str:
304
+ """Render an ILL (``linear`` mode) formula to Unicode text, fully
305
+ parenthesised.
306
+
307
+ Renders the same symbols as ``f.to_unicode_str()`` (Top/Zero included —
308
+ see the module comment above), but always produces text that re-parses
309
+ (via ``MSFLParser(linear=True).parse``) to a structurally equal formula,
310
+ because every compound subformula is fully parenthesised — unlike
311
+ ``to_unicode_str()``'s precedence-based minimal parenthesisation. Used by
312
+ its round-trip-sensitive caller, ``atp.linear.ILLSequent``, in place of
313
+ ``to_unicode_str()``. A constant whose bare name would read as something else is
314
+ written in quotes, as ``to_unicode_str()`` writes it.
315
+ """
316
+ return _render_ill(f, lambda atom: atom.to_unicode_str())
317
+
318
+
319
+ def _render_ill(f: Node, atom_text) -> str:
320
+ """The fully parenthesised rendering of :func:`render_ill_formula`, with the text of an
321
+ atom (and of anything else that is not an ILL connective) given by ``atom_text``."""
322
+ if isinstance(f, Top):
323
+ return "⊤"
324
+ if isinstance(f, Zero):
325
+ return "𝟘"
326
+ if isinstance(f, One):
327
+ return "𝟙"
328
+ if isinstance(f, OfCourse):
329
+ return f"!({_render_ill(f.formula, atom_text)})"
330
+ if isinstance(f, Tensor):
331
+ return f"({_render_ill(f.left, atom_text)} ⊗ {_render_ill(f.right, atom_text)})"
332
+ if isinstance(f, With):
333
+ return f"({_render_ill(f.left, atom_text)} & {_render_ill(f.right, atom_text)})"
334
+ if isinstance(f, OPlus):
335
+ return f"({_render_ill(f.left, atom_text)} ⊕ {_render_ill(f.right, atom_text)})"
336
+ if isinstance(f, LinearImplies):
337
+ return f"({_render_ill(f.left, atom_text)} ⊸ {_render_ill(f.right, atom_text)})"
338
+ # Atom (and anything else the linear grammar can produce): its own text
339
+ # is safe here since an Atom's children are TERMS (Variable/Constant/
340
+ # Number/Function), never Top/Zero.
341
+ return atom_text(f)
342
+
343
+
344
+ def _ill_sort_key(f: Node) -> str:
345
+ """A total-order string key for sorting ILL formulas (multiset bookkeeping
346
+ in atp.linear): the fully parenthesised rendering first (readable, and identical to
347
+ the pre-Top/Zero ordering for any formula without Top/Zero), with every constant
348
+ written by its bare name (``key_text``) so that the quotes of a quoted constant play
349
+ no part in the order, then ``repr()`` as a tiebreaker.
350
+ """
351
+ from ._msfl_nodes import key_text # lazy: avoid a module-load-order cycle
352
+ return _render_ill(f, key_text) + "\x00" + repr(f)