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.
- unicode_logic_kit/__init__.py +385 -0
- unicode_logic_kit/__main__.py +520 -0
- unicode_logic_kit/_deadline.py +219 -0
- unicode_logic_kit/ace/__init__.py +126 -0
- unicode_logic_kit/ace/_align.py +135 -0
- unicode_logic_kit/ace/chem_lexicon.py +128 -0
- unicode_logic_kit/ace/drs_reader.py +570 -0
- unicode_logic_kit/ace/mapping.py +666 -0
- unicode_logic_kit/ace/reverse_modal.py +138 -0
- unicode_logic_kit/ace/runner.py +551 -0
- unicode_logic_kit/ace/translate.py +452 -0
- unicode_logic_kit/ace/verbalize.py +1070 -0
- unicode_logic_kit/api.py +1284 -0
- unicode_logic_kit/atp/__init__.py +177 -0
- unicode_logic_kit/atp/_ascii_names.py +113 -0
- unicode_logic_kit/atp/_html.py +72 -0
- unicode_logic_kit/atp/_substructural_input.py +228 -0
- unicode_logic_kit/atp/_tff_problem.py +715 -0
- unicode_logic_kit/atp/_tptp_problem.py +1111 -0
- unicode_logic_kit/atp/_writer_support.py +289 -0
- unicode_logic_kit/atp/clingo_backend.py +1180 -0
- unicode_logic_kit/atp/cvc5_backend.py +1385 -0
- unicode_logic_kit/atp/eprover_backend.py +732 -0
- unicode_logic_kit/atp/finite_domain.py +1055 -0
- unicode_logic_kit/atp/fitch.py +1547 -0
- unicode_logic_kit/atp/fitch_search.py +551 -0
- unicode_logic_kit/atp/hets_backend.py +339 -0
- unicode_logic_kit/atp/hybrid_down.py +120 -0
- unicode_logic_kit/atp/incremental.py +250 -0
- unicode_logic_kit/atp/kripke_enum.py +741 -0
- unicode_logic_kit/atp/lambek.py +436 -0
- unicode_logic_kit/atp/leo3_backend.py +332 -0
- unicode_logic_kit/atp/linear.py +738 -0
- unicode_logic_kit/atp/lj.py +705 -0
- unicode_logic_kit/atp/logic_backends.py +566 -0
- unicode_logic_kit/atp/ltl_tableau.py +1084 -0
- unicode_logic_kit/atp/minizinc_backend.py +1402 -0
- unicode_logic_kit/atp/modal_tableau.py +1382 -0
- unicode_logic_kit/atp/nanocop_backend.py +410 -0
- unicode_logic_kit/atp/portfolio.py +489 -0
- unicode_logic_kit/atp/protocol.py +1803 -0
- unicode_logic_kit/atp/prover9_entailment.py +1153 -0
- unicode_logic_kit/atp/resolution.py +1376 -0
- unicode_logic_kit/atp/resolution_check.py +1114 -0
- unicode_logic_kit/atp/sequent.py +1050 -0
- unicode_logic_kit/atp/tableau.py +921 -0
- unicode_logic_kit/atp/tableau_check.py +543 -0
- unicode_logic_kit/atp/tptp_ncl.py +811 -0
- unicode_logic_kit/atp/tptp_tff.py +1546 -0
- unicode_logic_kit/atp/tstp.py +1333 -0
- unicode_logic_kit/atp/tstp_check.py +1096 -0
- unicode_logic_kit/atp/twee_backend.py +236 -0
- unicode_logic_kit/atp/twee_check.py +711 -0
- unicode_logic_kit/atp/twee_entailment.py +953 -0
- unicode_logic_kit/atp/vampire_entailment.py +540 -0
- unicode_logic_kit/atp/z3_arith.py +470 -0
- unicode_logic_kit/atp/z3_equivalence.py +36 -0
- unicode_logic_kit/atp/z3_fuzzy.py +362 -0
- unicode_logic_kit/atp/z3_input.py +500 -0
- unicode_logic_kit/atp/z3_models.py +208 -0
- unicode_logic_kit/chem/__init__.py +88 -0
- unicode_logic_kit/chem/_naming.py +284 -0
- unicode_logic_kit/chem/cache.py +185 -0
- unicode_logic_kit/chem/interop.py +244 -0
- unicode_logic_kit/chem/mol.py +525 -0
- unicode_logic_kit/chem/signature.py +112 -0
- unicode_logic_kit/comorphism.py +497 -0
- unicode_logic_kit/dl/__init__.py +384 -0
- unicode_logic_kit/dl/classification.py +227 -0
- unicode_logic_kit/dl/concepts.py +632 -0
- unicode_logic_kit/dl/datatypes.py +818 -0
- unicode_logic_kit/dl/owl_functional.py +2433 -0
- unicode_logic_kit/dl/owl_manchester.py +1637 -0
- unicode_logic_kit/dl/owl_reasoner.py +790 -0
- unicode_logic_kit/dl/parser.py +391 -0
- unicode_logic_kit/dl/tableau.py +4048 -0
- unicode_logic_kit/dl/translate.py +2704 -0
- unicode_logic_kit/drt/__init__.py +94 -0
- unicode_logic_kit/drt/export.py +179 -0
- unicode_logic_kit/drt/nodes.py +506 -0
- unicode_logic_kit/drt/parser.py +965 -0
- unicode_logic_kit/drt/resolve.py +195 -0
- unicode_logic_kit/drt/reverse.py +175 -0
- unicode_logic_kit/eval/__init__.py +106 -0
- unicode_logic_kit/eval/batch.py +382 -0
- unicode_logic_kit/eval/canonical.py +663 -0
- unicode_logic_kit/eval/chem_batch.py +606 -0
- unicode_logic_kit/eval/converses.py +200 -0
- unicode_logic_kit/eval/datasets/__init__.py +136 -0
- unicode_logic_kit/eval/datasets/_base.py +263 -0
- unicode_logic_kit/eval/datasets/_proofwriter_proof.py +422 -0
- unicode_logic_kit/eval/datasets/c3po.py +678 -0
- unicode_logic_kit/eval/datasets/folio.py +158 -0
- unicode_logic_kit/eval/datasets/fracas.py +418 -0
- unicode_logic_kit/eval/datasets/groves.py +191 -0
- unicode_logic_kit/eval/datasets/logicbench.py +467 -0
- unicode_logic_kit/eval/datasets/logicnli.py +303 -0
- unicode_logic_kit/eval/datasets/malls.py +133 -0
- unicode_logic_kit/eval/datasets/pfolio.py +594 -0
- unicode_logic_kit/eval/datasets/pmb.py +242 -0
- unicode_logic_kit/eval/datasets/prontoqa.py +611 -0
- unicode_logic_kit/eval/datasets/proofwriter.py +1431 -0
- unicode_logic_kit/eval/datasets/proverqa.py +674 -0
- unicode_logic_kit/eval/datasets/willow.py +478 -0
- unicode_logic_kit/eval/equivalence.py +466 -0
- unicode_logic_kit/eval/exercise_gen.py +533 -0
- unicode_logic_kit/eval/explain.py +791 -0
- unicode_logic_kit/eval/generality.py +750 -0
- unicode_logic_kit/eval/metric_hf.py +458 -0
- unicode_logic_kit/eval/predicate_match.py +343 -0
- unicode_logic_kit/eval/theory_check.py +1170 -0
- unicode_logic_kit/eval/validate.py +306 -0
- unicode_logic_kit/fol/__init__.py +177 -0
- unicode_logic_kit/fol/_atom_keys.py +510 -0
- unicode_logic_kit/fol/_fol_nodes.py +3586 -0
- unicode_logic_kit/fol/_free_parameters.py +105 -0
- unicode_logic_kit/fol/_ho_nodes.py +448 -0
- unicode_logic_kit/fol/_hybrid_nodes.py +308 -0
- unicode_logic_kit/fol/_identifiers.py +1091 -0
- unicode_logic_kit/fol/_lambek_nodes.py +112 -0
- unicode_logic_kit/fol/_linear_nodes.py +352 -0
- unicode_logic_kit/fol/_modal_nodes.py +1467 -0
- unicode_logic_kit/fol/_msfl_nodes.py +2196 -0
- unicode_logic_kit/fol/_numeral_symbols.py +231 -0
- unicode_logic_kit/fol/_so_nodes.py +200 -0
- unicode_logic_kit/fol/_symbol_names.py +81 -0
- unicode_logic_kit/fol/_team_nodes.py +181 -0
- unicode_logic_kit/fol/_tptp_symbols.py +551 -0
- unicode_logic_kit/fol/_truth_constants.py +117 -0
- unicode_logic_kit/fol/casl_export.py +1135 -0
- unicode_logic_kit/fol/casl_import.py +929 -0
- unicode_logic_kit/fol/derivation.py +367 -0
- unicode_logic_kit/fol/dialect_detect.py +70 -0
- unicode_logic_kit/fol/dialect_repair.py +537 -0
- unicode_logic_kit/fol/frames.py +637 -0
- unicode_logic_kit/fol/grammars/terminals.lark +31 -0
- unicode_logic_kit/fol/lambda_tools.py +297 -0
- unicode_logic_kit/fol/latex_input.py +429 -0
- unicode_logic_kit/fol/modal_translation.py +944 -0
- unicode_logic_kit/fol/msflparser.py +1033 -0
- unicode_logic_kit/fol/naming.py +422 -0
- unicode_logic_kit/fol/nodes.py +241 -0
- unicode_logic_kit/fol/normalforms.py +492 -0
- unicode_logic_kit/fol/pal.py +287 -0
- unicode_logic_kit/fol/prolog_export.py +566 -0
- unicode_logic_kit/fol/prolog_input.py +505 -0
- unicode_logic_kit/fol/prover9_input.py +1325 -0
- unicode_logic_kit/fol/qml.py +1760 -0
- unicode_logic_kit/fol/qmltp_input.py +525 -0
- unicode_logic_kit/fol/sanitize.py +221 -0
- unicode_logic_kit/fol/serialize.py +79 -0
- unicode_logic_kit/fol/signature.py +1290 -0
- unicode_logic_kit/fol/simplify_check.py +544 -0
- unicode_logic_kit/fol/spans.py +594 -0
- unicode_logic_kit/fol/tptp_input.py +1503 -0
- unicode_logic_kit/fol/tptp_repair.py +941 -0
- unicode_logic_kit/fol/unification.py +157 -0
- unicode_logic_kit/fol/verbalize.py +263 -0
- unicode_logic_kit/hets/__init__.py +163 -0
- unicode_logic_kit/hets/bridge.py +142 -0
- unicode_logic_kit/hets/client.py +748 -0
- unicode_logic_kit/hets/docker.py +420 -0
- unicode_logic_kit/hets/dol.py +712 -0
- unicode_logic_kit/hets/haskell_json.py +355 -0
- unicode_logic_kit/hets/owl_backend.py +794 -0
- unicode_logic_kit/hets/owl_cli.py +598 -0
- unicode_logic_kit/hets/symbols.py +512 -0
- unicode_logic_kit/hol/__init__.py +140 -0
- unicode_logic_kit/hol/_ho_common.py +323 -0
- unicode_logic_kit/hol/_isabelle_binders.py +125 -0
- unicode_logic_kit/hol/classical.py +812 -0
- unicode_logic_kit/hol/deepshallow/__init__.py +45 -0
- unicode_logic_kit/hol/deepshallow/_common.py +177 -0
- unicode_logic_kit/hol/deepshallow/conditional.py +225 -0
- unicode_logic_kit/hol/deepshallow/intuitionistic.py +181 -0
- unicode_logic_kit/hol/deepshallow/modal.py +217 -0
- unicode_logic_kit/hol/deepshallow/qml.py +406 -0
- unicode_logic_kit/hol/deepshallow/relevant.py +206 -0
- unicode_logic_kit/hol/free.py +753 -0
- unicode_logic_kit/hol/goedel.py +336 -0
- unicode_logic_kit/hol/ho_modal.py +1743 -0
- unicode_logic_kit/hol/intuitionistic.py +403 -0
- unicode_logic_kit/hol/isabelle_conditional.py +593 -0
- unicode_logic_kit/hol/isabelle_modal.py +1908 -0
- unicode_logic_kit/hol/isabelle_relevant.py +412 -0
- unicode_logic_kit/hol/isabelle_runner.py +1147 -0
- unicode_logic_kit/hol/isabelle_substructural.py +884 -0
- unicode_logic_kit/hol/lean.py +1018 -0
- unicode_logic_kit/hol/manyvalued.py +921 -0
- unicode_logic_kit/hol/secondorder.py +687 -0
- unicode_logic_kit/hol/thf_modal.py +941 -0
- unicode_logic_kit/hol/thirdorder.py +397 -0
- unicode_logic_kit/ilp/__init__.py +89 -0
- unicode_logic_kit/ilp/readback.py +389 -0
- unicode_logic_kit/ilp/separation.py +153 -0
- unicode_logic_kit/ilp/task.py +730 -0
- unicode_logic_kit/logic.py +163 -0
- unicode_logic_kit/mcp/__init__.py +28 -0
- unicode_logic_kit/mcp/__main__.py +5 -0
- unicode_logic_kit/mcp/chem_tools.py +1031 -0
- unicode_logic_kit/mcp/server.py +2453 -0
- unicode_logic_kit/mcp/syntax_spec.py +681 -0
- unicode_logic_kit/prob/__init__.py +53 -0
- unicode_logic_kit/prob/_bdd.py +225 -0
- unicode_logic_kit/prob/_column_gen.py +668 -0
- unicode_logic_kit/prob/distribution.py +686 -0
- unicode_logic_kit/prob/nilsson.py +470 -0
- unicode_logic_kit/py.typed +0 -0
- unicode_logic_kit/semantics/__init__.py +137 -0
- unicode_logic_kit/semantics/_modal_reject.py +156 -0
- unicode_logic_kit/semantics/action_models.py +466 -0
- unicode_logic_kit/semantics/asp_models.py +1200 -0
- unicode_logic_kit/semantics/conditional.py +580 -0
- unicode_logic_kit/semantics/dynamic_epistemic.py +95 -0
- unicode_logic_kit/semantics/free_logic.py +913 -0
- unicode_logic_kit/semantics/fuzzy.py +384 -0
- unicode_logic_kit/semantics/fuzzy_kripke.py +442 -0
- unicode_logic_kit/semantics/intuitionistic.py +581 -0
- unicode_logic_kit/semantics/kripke.py +1139 -0
- unicode_logic_kit/semantics/manyvalued.py +580 -0
- unicode_logic_kit/semantics/matrix.py +342 -0
- unicode_logic_kit/semantics/model_eval.py +1135 -0
- unicode_logic_kit/semantics/modelfinder.py +1036 -0
- unicode_logic_kit/semantics/nonmonotonic.py +372 -0
- unicode_logic_kit/semantics/relevant.py +331 -0
- unicode_logic_kit/semantics/secondorder.py +657 -0
- unicode_logic_kit/semantics/structures.py +352 -0
- unicode_logic_kit/semantics/tarski.py +975 -0
- unicode_logic_kit/semantics/team.py +315 -0
- unicode_logic_kit/semantics/team_translation.py +416 -0
- unicode_logic_kit/semantics/thirdorder.py +358 -0
- unicode_logic_kit/semantics/tnorm.py +85 -0
- unicode_logic_kit/semantics/truthtable.py +201 -0
- unicode_logic_kit-0.31.0.dist-info/METADATA +333 -0
- unicode_logic_kit-0.31.0.dist-info/RECORD +237 -0
- unicode_logic_kit-0.31.0.dist-info/WHEEL +4 -0
- 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)
|