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,308 @@
|
|
|
1
|
+
"""Hybrid-logic node classes: nominals, the satisfaction operator @, and ↓.
|
|
2
|
+
|
|
3
|
+
Hybrid logic H(@) extends modal logic with NOMINALS — atomic formulas i, j, …
|
|
4
|
+
that are true at EXACTLY ONE world, naming it — and the satisfaction operator
|
|
5
|
+
``@i φ`` (*at the world named i, φ holds*). Both parse in the modal mode
|
|
6
|
+
(``MSFLParser(modal=True)``): a bare lowercase name in formula position is a
|
|
7
|
+
nominal, and ``@i φ`` applies the satisfaction operator. H(@) over K stays
|
|
8
|
+
DECIDABLE.
|
|
9
|
+
|
|
10
|
+
Adding the ``↓`` binder gives the full hybrid language H(@,↓): ``↓x.φ`` binds
|
|
11
|
+
the state variable ``x`` to the CURRENT world, then evaluates ``φ`` (which may
|
|
12
|
+
refer back to ``x`` as an ordinary nominal or via ``@x``). H(@,↓) validity is
|
|
13
|
+
UNDECIDABLE (Areces, Blackburn & Marx 1999) — but the standard translation
|
|
14
|
+
into classical FOL stays MEANING-PRESERVING for it (that correspondence is
|
|
15
|
+
exactly what defines the "bounded fragment"), so validity is co-r.e.: a Z3
|
|
16
|
+
route can still PROVE it (never REFUTE it presentably — see
|
|
17
|
+
``fol.modal_translation.down_is_valid``), and the Kripke evaluator
|
|
18
|
+
(``satisfies_modal``) still evaluates ``↓`` on any finite model, including
|
|
19
|
+
inside a bounded finite-model search that REFUTES it
|
|
20
|
+
(``atp.kripke_enum.KripkeEnumBackend``). No route here DECIDES full H(@,↓);
|
|
21
|
+
a route that stays sound only by staying bounded to a fragment it can fully
|
|
22
|
+
search (the labelled modal tableau, the QML embedding, the HOL shallow
|
|
23
|
+
embeddings) refuses a ``↓``-containing formula BY NAME instead of silently
|
|
24
|
+
mis-scoping.
|
|
25
|
+
|
|
26
|
+
Reasoning routes: the Kripke evaluator (``KripkeModel(nominals={...})`` +
|
|
27
|
+
``satisfies_modal``) evaluates hybrid formulas directly (route A, always
|
|
28
|
+
terminating), and the standard translation maps them to classical FOL (a
|
|
29
|
+
nominal becomes a world-equality with a fresh world constant; ``↓x`` becomes a
|
|
30
|
+
local rebinding of ``x`` to the current-world term, with NO fresh quantifier),
|
|
31
|
+
so ``hybrid_is_valid`` (H(@) only) / ``down_is_valid`` (H(@,↓), PROVED-only) /
|
|
32
|
+
``KripkeEnumBackend`` (bounded search, REFUTED-only) decide validity with Z3 /
|
|
33
|
+
finite enumeration per frame (route B). The direct classical exporters reject
|
|
34
|
+
(a nominal — and ``↓`` doubly so — is world-relative).
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
from dataclasses import dataclass
|
|
38
|
+
|
|
39
|
+
from ._fol_nodes import (
|
|
40
|
+
Node, Z3Env,
|
|
41
|
+
NODE_CLASSES, register_operator, register_parser_op,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
# Shared rejection message: hybrid constructs are world-relative, so they have no
|
|
45
|
+
# direct classical export; the standard translation is the sanctioned route.
|
|
46
|
+
_NO_HYBRID_EXPORT = (
|
|
47
|
+
"Hybrid-logic constructs (nominals / the satisfaction operator @) are "
|
|
48
|
+
"world-relative and have no direct first-order export. Use the standard "
|
|
49
|
+
"translation (unicode_logic_kit.standard_translation) or hybrid_is_valid, or "
|
|
50
|
+
"evaluate in a KripkeModel with a nominal assignment."
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
# Rejection message for Down specifically: same world-relativity as Nominal/At,
|
|
54
|
+
# PLUS H(@,↓) validity is undecidable, so the sanctioned routes are narrower
|
|
55
|
+
# than hybrid_is_valid's (which itself refuses ↓ — see modal_translation.py).
|
|
56
|
+
_NO_DOWN_EXPORT = (
|
|
57
|
+
"The ↓ binder is world-relative (like every hybrid-logic construct) and "
|
|
58
|
+
"H(@,↓) validity is undecidable, so it has no direct first-order export "
|
|
59
|
+
"and no bare-bool validity check. Use "
|
|
60
|
+
"unicode_logic_kit.fol.modal_translation.down_is_valid (Z3, PROVED-only) or "
|
|
61
|
+
"unicode_logic_kit.atp.kripke_enum.KripkeEnumBackend (bounded search, "
|
|
62
|
+
"REFUTED-only) for validity, or evaluate directly in a KripkeModel with "
|
|
63
|
+
"unicode_logic_kit.semantics.kripke.satisfies_modal."
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@dataclass(frozen=True)
|
|
68
|
+
class Nominal(Node):
|
|
69
|
+
"""A nominal ``i`` — an atomic formula true at exactly one world (naming it).
|
|
70
|
+
|
|
71
|
+
``name`` is a lowercase identifier (a legal NAME token, so it round-trips).
|
|
72
|
+
In a :class:`~unicode_logic_kit.semantics.kripke.KripkeModel` the assignment
|
|
73
|
+
``nominals={"i": world}`` fixes which world each nominal names; the nominal
|
|
74
|
+
is then true at that world and false everywhere else.
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
name: str
|
|
78
|
+
|
|
79
|
+
def _tree_parts(self):
|
|
80
|
+
"""Return the nominal's name as an atomic tree label."""
|
|
81
|
+
return self.name, []
|
|
82
|
+
|
|
83
|
+
def to_dict(self):
|
|
84
|
+
"""Serialise to dict with the type tag and the nominal's name."""
|
|
85
|
+
return {"_type": "Nominal", "name": self.name}
|
|
86
|
+
|
|
87
|
+
@staticmethod
|
|
88
|
+
def from_dict(d):
|
|
89
|
+
"""Deserialise a Nominal from a dict produced by to_dict."""
|
|
90
|
+
return Nominal(d["name"])
|
|
91
|
+
|
|
92
|
+
def to_z3(self, env: Z3Env = None):
|
|
93
|
+
"""Reject direct Z3 export: nominals are world-relative."""
|
|
94
|
+
raise NotImplementedError(_NO_HYBRID_EXPORT)
|
|
95
|
+
|
|
96
|
+
def to_prover9(self) -> str:
|
|
97
|
+
"""Reject direct Prover9 export: nominals are world-relative."""
|
|
98
|
+
raise NotImplementedError(_NO_HYBRID_EXPORT)
|
|
99
|
+
|
|
100
|
+
def to_tptp(self) -> str:
|
|
101
|
+
"""Reject direct TPTP export: nominals are world-relative."""
|
|
102
|
+
raise NotImplementedError(_NO_HYBRID_EXPORT)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@dataclass(frozen=True)
|
|
106
|
+
class At(Node):
|
|
107
|
+
"""The satisfaction operator ``@i φ`` — at the world named ``i``, ``φ`` holds.
|
|
108
|
+
|
|
109
|
+
``nominal`` is the :class:`Nominal` naming the evaluation world (a bare
|
|
110
|
+
string is coerced); ``formula`` is evaluated THERE, regardless of the
|
|
111
|
+
current world. ``@`` is self-dual (``@i φ ↔ ¬@i ¬φ``) and a normal
|
|
112
|
+
modality.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
nominal: Nominal
|
|
116
|
+
formula: Node
|
|
117
|
+
|
|
118
|
+
def __post_init__(self):
|
|
119
|
+
"""Coerce a bare string to a Nominal and validate the field types."""
|
|
120
|
+
if isinstance(self.nominal, str):
|
|
121
|
+
object.__setattr__(self, "nominal", Nominal(self.nominal))
|
|
122
|
+
if not isinstance(self.nominal, Nominal):
|
|
123
|
+
raise ValueError("At: nominal must be a Nominal (or a bare string).")
|
|
124
|
+
|
|
125
|
+
@property
|
|
126
|
+
def agent(self):
|
|
127
|
+
"""The nominal, exposed under the generic agent-prefix renderer contract.
|
|
128
|
+
|
|
129
|
+
The Unicode/LaTeX renderers drive every ``agent_prefix`` operator
|
|
130
|
+
generically via ``node.agent`` (as for ``K_a`` / ``B_a``); exposing the
|
|
131
|
+
nominal here lets ``@i φ`` render with zero renderer special-casing.
|
|
132
|
+
"""
|
|
133
|
+
return self.nominal
|
|
134
|
+
|
|
135
|
+
def _tree_parts(self):
|
|
136
|
+
"""Return the @-label (with the nominal) and the formula child."""
|
|
137
|
+
return f"@{self.nominal.name}", [self.formula]
|
|
138
|
+
|
|
139
|
+
def to_dict(self):
|
|
140
|
+
"""Serialise to dict with the nominal and the serialised formula."""
|
|
141
|
+
return {"_type": "At", "nominal": self.nominal.to_dict(),
|
|
142
|
+
"formula": self.formula.to_dict()}
|
|
143
|
+
|
|
144
|
+
@staticmethod
|
|
145
|
+
def from_dict(d):
|
|
146
|
+
"""Deserialise an At from a dict produced by to_dict."""
|
|
147
|
+
return At(Node.from_dict(d["nominal"]), Node.from_dict(d["formula"]))
|
|
148
|
+
|
|
149
|
+
def to_z3(self, env: Z3Env = None):
|
|
150
|
+
"""Reject direct Z3 export: @ is world-relative."""
|
|
151
|
+
raise NotImplementedError(_NO_HYBRID_EXPORT)
|
|
152
|
+
|
|
153
|
+
def to_prover9(self) -> str:
|
|
154
|
+
"""Reject direct Prover9 export: @ is world-relative."""
|
|
155
|
+
raise NotImplementedError(_NO_HYBRID_EXPORT)
|
|
156
|
+
|
|
157
|
+
def to_tptp(self) -> str:
|
|
158
|
+
"""Reject direct TPTP export: @ is world-relative."""
|
|
159
|
+
raise NotImplementedError(_NO_HYBRID_EXPORT)
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
@dataclass(frozen=True)
|
|
163
|
+
class Down(Node):
|
|
164
|
+
"""The ↓ binder ``↓x.φ`` — binds the state variable ``x`` to the CURRENT
|
|
165
|
+
world, then evaluates ``φ``, in which ``x`` may occur as an ordinary
|
|
166
|
+
:class:`Nominal` or via :class:`At`. Nesting ``↓`` under a modality lets a
|
|
167
|
+
formula "look back" at a world named earlier — e.g. ``↓x.□¬x``
|
|
168
|
+
(irreflexivity: no successor of the current world IS the current world)
|
|
169
|
+
or ``↓x.◇x`` (reflexivity) — expressive power plain H(@) does not have
|
|
170
|
+
(a nominal only ever names a world FIXED IN ADVANCE by the model, never
|
|
171
|
+
"whichever world evaluation happens to be at").
|
|
172
|
+
|
|
173
|
+
``variable`` is a :class:`Nominal` naming the bound state variable — this
|
|
174
|
+
reuses the Nominal shape rather than adding a new leaf kind, since ↓
|
|
175
|
+
binds exactly the same kind of name (a lowercase, NAME-legal identifier)
|
|
176
|
+
that a plain nominal already is; a bare string is coerced, matching
|
|
177
|
+
:class:`At`'s own convention. ``formula`` is the scope.
|
|
178
|
+
|
|
179
|
+
Binding is by NAME, not by a separate de Bruijn/alpha-renaming layer
|
|
180
|
+
(unlike :class:`~unicode_logic_kit.fol._fol_nodes.Quantifier`'s object
|
|
181
|
+
variable): an occurrence of ``Nominal(x)`` or ``At(Nominal(x), …)``
|
|
182
|
+
anywhere in ``formula`` NOT itself inside a nested ``Down(Nominal(x), …)``
|
|
183
|
+
refers to THIS binder — a nested ``↓x`` of the same name shadows it
|
|
184
|
+
exactly the way a nested ``∀x`` would shadow an outer one, so
|
|
185
|
+
``↓x.↓x.φ`` makes the outer binding entirely inert. This is deliberately
|
|
186
|
+
NOT wired into the generic ``_subst`` / capture-avoiding ``replace`` /
|
|
187
|
+
alpha-renaming machinery those FOL binders use (``fol/_msfl_nodes.py``):
|
|
188
|
+
a nominal is never a first-order term, so no first-order substitution
|
|
189
|
+
ever needs to reach inside one, and the two evaluators that DO give ↓ its
|
|
190
|
+
meaning (:func:`~unicode_logic_kit.semantics.kripke.satisfies_modal` and
|
|
191
|
+
:func:`~unicode_logic_kit.fol.modal_translation.standard_translation`) each
|
|
192
|
+
implement this by-name (re)binding directly, as a small environment keyed
|
|
193
|
+
on the name — see their own docstrings.
|
|
194
|
+
"""
|
|
195
|
+
|
|
196
|
+
variable: Nominal
|
|
197
|
+
formula: Node
|
|
198
|
+
|
|
199
|
+
def __post_init__(self):
|
|
200
|
+
"""Coerce a bare string to a Nominal and validate the field types."""
|
|
201
|
+
if isinstance(self.variable, str):
|
|
202
|
+
object.__setattr__(self, "variable", Nominal(self.variable))
|
|
203
|
+
if not isinstance(self.variable, Nominal):
|
|
204
|
+
raise ValueError("Down: variable must be a Nominal (or a bare string).")
|
|
205
|
+
|
|
206
|
+
def _tree_parts(self):
|
|
207
|
+
"""Return the ↓-label (with the bound name) and the formula child."""
|
|
208
|
+
return f"↓{self.variable.name}", [self.formula]
|
|
209
|
+
|
|
210
|
+
def to_dict(self):
|
|
211
|
+
"""Serialise to dict with the bound variable and the serialised formula."""
|
|
212
|
+
return {"_type": "Down", "variable": self.variable.to_dict(),
|
|
213
|
+
"formula": self.formula.to_dict()}
|
|
214
|
+
|
|
215
|
+
@staticmethod
|
|
216
|
+
def from_dict(d):
|
|
217
|
+
"""Deserialise a Down from a dict produced by to_dict."""
|
|
218
|
+
return Down(Node.from_dict(d["variable"]), Node.from_dict(d["formula"]))
|
|
219
|
+
|
|
220
|
+
def to_z3(self, env: Z3Env = None):
|
|
221
|
+
"""Reject direct Z3 export: ↓ is world-relative and undecidable."""
|
|
222
|
+
raise NotImplementedError(_NO_DOWN_EXPORT)
|
|
223
|
+
|
|
224
|
+
def to_prover9(self) -> str:
|
|
225
|
+
"""Reject direct Prover9 export: ↓ is world-relative and undecidable."""
|
|
226
|
+
raise NotImplementedError(_NO_DOWN_EXPORT)
|
|
227
|
+
|
|
228
|
+
def to_tptp(self) -> str:
|
|
229
|
+
"""Reject direct TPTP export: ↓ is world-relative and undecidable."""
|
|
230
|
+
raise NotImplementedError(_NO_DOWN_EXPORT)
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
NODE_CLASSES.update({"Nominal": Nominal, "At": At, "Down": Down})
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
# =========================
|
|
237
|
+
# Renderer + parser registration (modal mode)
|
|
238
|
+
# =========================
|
|
239
|
+
#
|
|
240
|
+
# @ registers as a regular agent_prefix operator (the nominal doubles as the
|
|
241
|
+
# "agent", see At.agent), so the Unicode/LaTeX renderers need no new branch.
|
|
242
|
+
# Nominal is atomic and rendered by an explicit branch in _msfl_nodes (bare name).
|
|
243
|
+
|
|
244
|
+
register_operator(At, "agent_prefix", "@", "@", 4)
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def _nominal_transform(items):
|
|
248
|
+
"""Build a Nominal from a NAME token (or the Constant the base pass made of it)."""
|
|
249
|
+
first = items[0]
|
|
250
|
+
name = getattr(first, "name", None) or str(first)
|
|
251
|
+
return Nominal(name)
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def _at_transform(items):
|
|
255
|
+
"""Build an At from [ATNOM token '@i', body] (strip the leading '@')."""
|
|
256
|
+
return At(Nominal(str(items[0])[1:]), items[1])
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
# A bare lowercase name in formula position is a nominal; @i binds like ¬ / K_a.
|
|
260
|
+
# Both token classes are accepted because the lexer splits lowercase identifiers
|
|
261
|
+
# by shape: single letters (+digits) are VARIABLE, multi-letter names are NAME —
|
|
262
|
+
# and the standard hybrid nominals i, j, k fall in the VARIABLE class. ATNOM has
|
|
263
|
+
# lexer priority 5 so '@i' lexes as one token.
|
|
264
|
+
#
|
|
265
|
+
# The bare-name form is emitted as a SEPARATE rule at grammar priority -1 (injected
|
|
266
|
+
# via the terminal-defs hook) rather than an inline prefix alternative. A lambda
|
|
267
|
+
# application argument (``?app_arg: formula | atom_term``) is also a bare name, so
|
|
268
|
+
# without the lowered priority Earley would resolve ``(λx. P(x))(y)`` to the nominal
|
|
269
|
+
# reading and silently turn the term argument into Nominal('y'); the -1 priority
|
|
270
|
+
# makes the term (atom_term) reading win wherever the two overlap, while a nominal
|
|
271
|
+
# still parses everywhere a formula is actually required (``i``, ``P ∧ i``, ``@i j``).
|
|
272
|
+
register_parser_op(Nominal, "modal", "prefix", "nominal_", "nominal",
|
|
273
|
+
_nominal_transform,
|
|
274
|
+
terminal_name="_nominal_rule",
|
|
275
|
+
terminal_def="?nominal.-1: (NAME | VARIABLE)")
|
|
276
|
+
register_parser_op(At, "modal", "prefix", "at_", "ATNOM prefix",
|
|
277
|
+
_at_transform,
|
|
278
|
+
terminal_name="ATNOM", terminal_def="ATNOM.5: /@[a-z][a-zA-Z0-9]*/")
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
def _down_transform(items):
|
|
282
|
+
"""Build a Down from [DOWNARROW token, bound name (Variable/Constant), body]."""
|
|
283
|
+
bound = items[1]
|
|
284
|
+
name = getattr(bound, "name", None) or str(bound)
|
|
285
|
+
return Down(Nominal(name), items[2])
|
|
286
|
+
|
|
287
|
+
|
|
288
|
+
# ↓ binds at the SAME "quantifier" grammar level as ∀/∃/the counting quantifier
|
|
289
|
+
# (structurally identical to the already-shipped Count registration: same level,
|
|
290
|
+
# same named-terminal-glyph mechanism), so ``↓x.□¬x`` parses with the same tight
|
|
291
|
+
# binding a plain quantifier gets, extending as far right as possible through
|
|
292
|
+
# ``prefix``. The bound name accepts BOTH lexer classes, ``(NAME | VARIABLE)`` —
|
|
293
|
+
# not just VARIABLE — for the same reason the bare-nominal rule above does: a
|
|
294
|
+
# hybrid nominal name is legally either a single-letter VARIABLE-class token
|
|
295
|
+
# (the standard i, j, k) or a multi-letter NAME-class one, and ↓'s bound name is
|
|
296
|
+
# the very same kind of name (it IS a Nominal, see Down's docstring), so
|
|
297
|
+
# restricting it to VARIABLE alone would let ``i`` be bound by ↓ but not
|
|
298
|
+
# ``world1`` — an arbitrary asymmetry with the unbound nominal syntax. No
|
|
299
|
+
# priority annotation is needed here (unlike the bare-nominal rule's -1): DOWNARROW
|
|
300
|
+
# is a distinguishing token that starts this alternative, so it cannot share an
|
|
301
|
+
# LALR state with the term/atom_term alternatives the bare-nominal rule's
|
|
302
|
+
# priority exists to disambiguate (verified empirically in
|
|
303
|
+
# tests/test_modal_lalr_fallback.py, which — because "modal" is an LALR-first,
|
|
304
|
+
# Earley-fallback mode regardless, see msflparser.py's _HYBRID_MODES — also
|
|
305
|
+
# guarantees correctness even if that empirical claim ever stopped holding).
|
|
306
|
+
register_parser_op(Down, "modal", "quantifier", "down_",
|
|
307
|
+
'DOWNARROW (NAME | VARIABLE) "." prefix', _down_transform,
|
|
308
|
+
terminal_name="DOWNARROW", terminal_def='DOWNARROW: "↓"')
|