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,941 @@
|
|
|
1
|
+
"""Full-family modal THF export — a Benzmüller-style higher-order shallow embedding.
|
|
2
|
+
|
|
3
|
+
This module extends :func:`unicode_logic_kit.fol.qml.to_thf_modal` (which only handles
|
|
4
|
+
the *alethic* ``□`` / ``◇`` fragment plus object quantifiers and **raises** on the
|
|
5
|
+
other modalities) to the **full modal family** the toolkit's AST can express:
|
|
6
|
+
|
|
7
|
+
- alethic ``□φ`` / ``◇φ`` over relation ``r``;
|
|
8
|
+
- epistemic ``K_a φ`` (``Knows(agent, φ)``) over an AGENT-INDEXED ``rk``;
|
|
9
|
+
- doxastic ``B_a φ`` (``Believes(agent, φ)``) over an AGENT-INDEXED ``rb``;
|
|
10
|
+
- assertive ``Say_a φ`` (``Says(agent, φ)``) over an AGENT-INDEXED ``rs``;
|
|
11
|
+
- bouletic ``Want_a φ`` (``Wants(agent, φ)``) over an AGENT-INDEXED ``rw``;
|
|
12
|
+
- deontic ``Oφ`` / ``Pφ`` over a SERIAL relation ``d``;
|
|
13
|
+
- temporal ``Gφ`` / ``Fφ`` / ``Xφ`` over ``t`` / ``tnext``, with the
|
|
14
|
+
past mirrors ``⒣`` / ``⒫`` / ``⒴`` over the CONVERSE relations and strong
|
|
15
|
+
``Ⓤ`` / ``⒮`` as impredicative least fixpoints (``muntil`` / ``msince``);
|
|
16
|
+
- hybrid nominals ``i`` and ``@i φ`` as ``mu`` world constants.
|
|
17
|
+
|
|
18
|
+
It is a genuine higher-order shallow embedding (SSE): a modal proposition is a
|
|
19
|
+
function ``mu > $o`` (world → bool), the modalities are λ-lifted quantifiers over the
|
|
20
|
+
relevant accessibility relation, and object quantifiers are ``existsAt``-guarded
|
|
21
|
+
(actualist). The emitted THF problem is **complete and self-contained** — type
|
|
22
|
+
declarations, every lifted operator, frame axioms (per relation where relevant),
|
|
23
|
+
domain axioms, and the conjecture ``mvalid @ ⟨formula⟩`` — ready for a higher-order
|
|
24
|
+
ATP (Leo-III, Satallax). As elsewhere in the toolkit, this module only *emits* the
|
|
25
|
+
problem; it does **not** run any prover.
|
|
26
|
+
|
|
27
|
+
Faithfulness to :func:`unicode_logic_kit.semantics.kripke.satisfies_modal`:
|
|
28
|
+
|
|
29
|
+
- epistemic/doxastic accessibility is **agent-indexed**. The agent of ``Knows`` /
|
|
30
|
+
``Believes`` is a first-class TERM (Variable or Constant). In THF it becomes a real
|
|
31
|
+
``$i`` argument of the relation: ``rk : $i > mu > mu > $o`` and
|
|
32
|
+
``mknows = ^[A:$i, Phi:mu>$o, W:mu] : ![V:mu] : (rk @ A @ W @ V) => Phi @ V``. A bound
|
|
33
|
+
object variable in agent position (``∀x (Student(x) → K_x φ)``) is bound by the
|
|
34
|
+
embedding's ``mforall`` (which binds an ``$i`` variable), so it correctly quantifies
|
|
35
|
+
over agents — exactly mirroring ``satisfies_modal``'s per-agent ``"K:"+agent`` /
|
|
36
|
+
``"B:"+agent`` relations. Named agents become ordinary ``$i`` constants (the same
|
|
37
|
+
individual sort as object-quantifier variables), so an agent can also be an object.
|
|
38
|
+
- deontic ``O`` / ``P`` are a box / diamond over a **serial** relation ``d`` (Standard
|
|
39
|
+
Deontic Logic, KD), matching the evaluator's serial ``"deontic"`` relation; the
|
|
40
|
+
serial axiom is always emitted for ``d``.
|
|
41
|
+
- temporal ``G`` / ``F`` / ``X`` are box / diamond / box over ``t`` / ``tnext`` — but
|
|
42
|
+
**note** the one-step-vs-closure caveat below.
|
|
43
|
+
- equality ``=`` / ``≠`` is **rigid identity**, THF's own ``=`` with no world argument —
|
|
44
|
+
the reading of :func:`unicode_logic_kit.fol.qml.qml_is_valid` (and of
|
|
45
|
+
:func:`unicode_logic_kit.fol.qml.to_thf_modal`, byte-compatible on the conjecture).
|
|
46
|
+
See "Equality" below. (``satisfies_modal`` has no term semantics and REFUSES an
|
|
47
|
+
equality atom by name; it is not the oracle for identity.)
|
|
48
|
+
|
|
49
|
+
Equality
|
|
50
|
+
--------
|
|
51
|
+
An identity atom ``t₁ = t₂`` is lifted through one extra macro, ``meq``, emitted only
|
|
52
|
+
when the formula contains identity:
|
|
53
|
+
``meq = ^ [A: $i, B: $i, W: mu] : ( A = B )`` — THF's native ``=`` over the individual
|
|
54
|
+
sort ``$i``, with a world binder ``W`` the body never mentions, so it takes **no
|
|
55
|
+
world argument** and cannot vary by world. ``t₁ ≠ t₂`` is lowered to ``¬(t₁ = t₂)``
|
|
56
|
+
first, exactly as :func:`unicode_logic_kit.fol.qml.qml_translate` does (the lowering is
|
|
57
|
+
:func:`unicode_logic_kit.fol.qml._st_equality` itself, reached through
|
|
58
|
+
:func:`unicode_logic_kit.hol.isabelle_modal._lower_identity`), and a non-binary ``=`` /
|
|
59
|
+
``≠`` atom raises ``ValueError``, as in ``qml``. Nothing is declared for identity — no
|
|
60
|
+
``feq`` / ``fneq`` functor exists in the emitted problem — so a prover gets
|
|
61
|
+
reflexivity, symmetry, transitivity and congruence (hence Leibniz's law, for ``mbox``
|
|
62
|
+
too) from its own equality, not from an axiom.
|
|
63
|
+
|
|
64
|
+
Identity ranges over the whole individual sort ``$i`` and is **not** existence-guarded
|
|
65
|
+
(``qml``'s documented varying-domain choice): ``a = a`` is a theorem at a world where
|
|
66
|
+
``a`` does not exist, whereas ``∃x (x = c)`` — which goes through the
|
|
67
|
+
``existsAt``-guarded ``mexists`` — is a theorem under ``constant`` / ``possibilist``
|
|
68
|
+
and not under the actualist modes. ``a = b → □(a = b)``, ``a ≠ b → □(a ≠ b)`` and
|
|
69
|
+
``◇(a = b) → a = b`` are theorems under ``frame='K'``; ``□(a = b) → a = b`` is NOT
|
|
70
|
+
(a dead-end world) and is one exactly when the frame is reflexive or serial. The
|
|
71
|
+
ordering atoms ``<`` ``>`` ``≤`` ``≥`` remain ordinary world-relativized predicates, as in
|
|
72
|
+
``qml``.
|
|
73
|
+
|
|
74
|
+
CAVEAT — temporal closure. ``satisfies_modal`` reads ``G``/``F`` over the
|
|
75
|
+
*reflexive-transitive closure* of the temporal relation, and ``X`` over the immediate
|
|
76
|
+
successors. A first-order / higher-order shallow embedding cannot express transitive
|
|
77
|
+
closure (it is not first-order definable). This module therefore embeds:
|
|
78
|
+
|
|
79
|
+
- ``Xφ`` as a box over the **one-step** relation ``tnext`` — faithful to
|
|
80
|
+
``satisfies_modal`` on the *universal* "all next states" reading of ``Next``;
|
|
81
|
+
- ``Gφ`` / ``Fφ`` as box / diamond over ``t`` **made reflexive and transitive** by the
|
|
82
|
+
emitted ``t_refl`` / ``t_trans`` frame axioms. On a frame whose ``t`` is already its
|
|
83
|
+
own reflexive-transitive closure the embedding coincides with the evaluator; in
|
|
84
|
+
general the embedding's ``G``/``F`` quantify over the *transitive closure of the
|
|
85
|
+
emitted ``t``*, which is the standard SSE rendering of LTL ``G``/``F`` but is only an
|
|
86
|
+
**approximation** of ``satisfies_modal``'s closure over the caller's raw temporal
|
|
87
|
+
edges. ``Until`` / ``Since`` **are supported**: TH0 quantifies over predicates, so
|
|
88
|
+
strong Until/Since embed directly as the impredicative Knaster–Tarski least
|
|
89
|
+
fixpoints ``muntil`` / ``msince`` over the one-step ``tnext`` — the same least
|
|
90
|
+
fixpoint Isabelle's ``inductive muntil`` compiles to.
|
|
91
|
+
|
|
92
|
+
The two temporal relations are **linked** by the emitted ``tnext_in_t`` inclusion
|
|
93
|
+
axiom ``![W,V]: (tnext @ W @ V) => (t @ W @ V)``. In ``satisfies_modal`` ``X`` reads
|
|
94
|
+
the immediate successors of the *single* ``"temporal"`` relation while ``G``/``F``
|
|
95
|
+
read its reflexive-transitive closure, so every one-step ``X``-successor is reachable
|
|
96
|
+
by ``G``/``F``; the inclusion axiom reproduces exactly that containment in the
|
|
97
|
+
embedding. Without it ``Gφ→Xφ`` — valid for the evaluator — was a non-theorem of the
|
|
98
|
+
emitted problem (``t`` and ``tnext`` were unconstrained relative to each other); with
|
|
99
|
+
it ``Gφ→Xφ`` (and ``Xφ→Fφ`` wherever the evaluator agrees) is now a theorem of the
|
|
100
|
+
embedding.
|
|
101
|
+
|
|
102
|
+
Cross-family bridges (``bridges=``). ``frame=`` and ``systems=`` each constrain ONE
|
|
103
|
+
relation; a *bridge* relates two relations of different families and is therefore a
|
|
104
|
+
separate, opt-in option — ``knowledge_implies_belief`` (``K_a φ → B_a φ``, condition
|
|
105
|
+
``rb ⊆ rk``, axiom ``rb_in_rk``), ``sincerity`` (``Say_a φ → B_a φ``, ``rb ⊆ rs``,
|
|
106
|
+
``rb_in_rs``) and ``ought_implies_can`` (``Oφ → ◇φ``, ``∀W ∃V. d W V ∧ r W V``,
|
|
107
|
+
``d_meets_r``). The names, the required families and the axiom names are
|
|
108
|
+
single-sourced from :mod:`unicode_logic_kit.hol.isabelle_modal`, so the two HOL routes
|
|
109
|
+
cannot drift apart. Each condition is the *exact* correspondent of its schema,
|
|
110
|
+
measured against :func:`satisfies_modal` over every frame on ≤ 2 worlds — in
|
|
111
|
+
particular ``ought_implies_can`` is deliberately NOT the folklore inclusion
|
|
112
|
+
``d ⊆ r``, which on its own does not validate ``Oφ → ◇φ`` at all and which, with
|
|
113
|
+
seriality, additionally validates the strictly stronger ``□φ → Oφ``. Requesting a
|
|
114
|
+
bridge whose partner family does not occur in the formula raises ``ValueError``
|
|
115
|
+
here too — see :func:`_thf_bridge_axioms` for why this route refuses even though
|
|
116
|
+
its relations are all declared unconditionally.
|
|
117
|
+
|
|
118
|
+
UNDECIDABILITY / honesty. First-order modal logic is **undecidable** — though the
|
|
119
|
+
standard systems emitted here (K/T/S4/S5, deontic KD) are still *semi-decidable*:
|
|
120
|
+
validity is recursively enumerable. (Higher-order modal logic and full second-order
|
|
121
|
+
logic are *not even semi-decidable*.) "Emit a problem a prover may discharge" is the
|
|
122
|
+
honest claim — never "decide every instance". The propositional
|
|
123
|
+
fragments (modal K/T/S4/S5, deontic KD) are decidable, but this export targets the
|
|
124
|
+
quantified family, so a ``Theorem`` verdict depends on an external HOL ATP and is not
|
|
125
|
+
guaranteed to terminate.
|
|
126
|
+
|
|
127
|
+
Public API: :func:`to_thf_modal_full`, plus the introspection helpers
|
|
128
|
+
:func:`thf_full_definitions` and :func:`thf_full_frame_axioms`.
|
|
129
|
+
"""
|
|
130
|
+
|
|
131
|
+
from typing import Dict, List, Optional, Sequence, Tuple
|
|
132
|
+
|
|
133
|
+
from ..fol.nodes import (
|
|
134
|
+
Node, Variable, Constant, Number, Function,
|
|
135
|
+
Atom, Not, And, Or, Xor, Implies, Iff, Quantifier, Contrast,
|
|
136
|
+
Box, Diamond, Knows, Believes, Says, Wants,
|
|
137
|
+
Obligatory, Permitted, Always, Eventually, Next, Until, Since,
|
|
138
|
+
Historically, Once, Previous, Nominal, At,
|
|
139
|
+
SortedQuantifier,
|
|
140
|
+
)
|
|
141
|
+
# Down (the ↓ binder, N1) is not yet re-exported through fol.nodes / fol's
|
|
142
|
+
# public __init__ / the top-level unicode_logic_kit package (that three-file
|
|
143
|
+
# edit is outside this change's file ownership — see the change's own
|
|
144
|
+
# report); imported directly from its defining module in the meantime, the
|
|
145
|
+
# same class object either import path would give.
|
|
146
|
+
from ..fol._hybrid_nodes import Down
|
|
147
|
+
|
|
148
|
+
# Re-use qml.py's THF helpers verbatim so the two exports stay byte-compatible on the
|
|
149
|
+
# overlapping (alethic + object-quantifier + equality) fragment.
|
|
150
|
+
from ..fol.qml import (
|
|
151
|
+
_FRAMES, _CONSTANT_MODES, _ACTUALIST_MODES,
|
|
152
|
+
_THF_FRAME, _THF_DOMAIN, _THF_PRED_ALIAS, _THF_RESERVED,
|
|
153
|
+
_thf_name, _thf_term, _thf_signature, _ThfNames,
|
|
154
|
+
_FORALL, _EQUALITY_PREDICATES,
|
|
155
|
+
)
|
|
156
|
+
from ..fol._free_parameters import free_parameter_names
|
|
157
|
+
from ..fol._numeral_symbols import numerals_as_constants, prefixed_numeral_name
|
|
158
|
+
from ..fol._symbol_names import dedupe
|
|
159
|
+
from ..fol._truth_constants import truth_value
|
|
160
|
+
from ..fol._msfl_nodes import nonempty_sort_axioms, sort_membership_axioms
|
|
161
|
+
|
|
162
|
+
# The cross-family bridge REGISTRY is single-sourced from the Isabelle route: the
|
|
163
|
+
# names, the families each bridge needs, and the fact names are shared, so the two
|
|
164
|
+
# HOL routes cannot answer differently on the same input (the divergence this
|
|
165
|
+
# option exists to remove). Only the axiom TEXT is route-local (_THF_BRIDGE_LINES).
|
|
166
|
+
from .isabelle_modal import (
|
|
167
|
+
BRIDGES, _BRIDGES as _BRIDGE_SPEC, _validate_bridges,
|
|
168
|
+
_has_identity, _lower_identity,
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
__all__ = ["to_thf_modal_full", "thf_full_definitions", "thf_full_frame_axioms",
|
|
172
|
+
"BRIDGES"]
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
# Accessibility-relation THF functors, one per modal family. Alethic ``r`` keeps the
|
|
176
|
+
# qml.py name so the alethic fragment is identical across both exports.
|
|
177
|
+
_R_ALETHIC = "r" # mu > mu > $o
|
|
178
|
+
_R_KNOWS = "rk" # $i > mu > mu > $o (AGENT-indexed)
|
|
179
|
+
_R_BELIEVES = "rb" # $i > mu > mu > $o (AGENT-indexed)
|
|
180
|
+
_R_DEONTIC = "d" # mu > mu > $o (serial)
|
|
181
|
+
_R_TEMPORAL = "t" # mu > mu > $o (G/F)
|
|
182
|
+
_R_NEXT = "tnext" # mu > mu > $o (X, one-step)
|
|
183
|
+
_R_SAYS = "rs" # $i > mu > mu > $o (AGENT-indexed, Says, plain K)
|
|
184
|
+
_R_WANTS = "rw" # $i > mu > mu > $o (AGENT-indexed, Wants, plain K)
|
|
185
|
+
|
|
186
|
+
# Every fixed functor of the FULL export (qml's core set plus the extra relations
|
|
187
|
+
# and lifted operators of this module). A user symbol sanitising onto one of
|
|
188
|
+
# these is pushed to a suffixed variant by the resolver — a predicate literally
|
|
189
|
+
# named "t" or "muntil" must not silently re-declare a built-in at another type.
|
|
190
|
+
_THF_RESERVED_FULL = _THF_RESERVED | frozenset({
|
|
191
|
+
_R_KNOWS, _R_BELIEVES, _R_DEONTIC, _R_TEMPORAL, _R_NEXT, _R_SAYS, _R_WANTS,
|
|
192
|
+
"mknows", "mbelieves", "mobl", "mperm", "malways", "meventually", "mnext",
|
|
193
|
+
"msays", "mwants", "mhistorically", "monce", "mprevious", "muntil", "msince",
|
|
194
|
+
})
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
# ---------------------------------------------------------------------------
|
|
198
|
+
# Lifted operators (the SSE macro definitions).
|
|
199
|
+
# ---------------------------------------------------------------------------
|
|
200
|
+
#
|
|
201
|
+
# The classical / alethic / object-quantifier / mvalid macros are copied from
|
|
202
|
+
# qml.py's _THF_DEFS (kept inline rather than imported so the emitted block is a
|
|
203
|
+
# single self-contained string and so adding the extra families does not depend on
|
|
204
|
+
# qml.py's private layout). The new macros are:
|
|
205
|
+
#
|
|
206
|
+
# mknows / mbelieves : agent-indexed box, AGENT is a real $i argument so a bound
|
|
207
|
+
# object variable in agent position quantifies over agents.
|
|
208
|
+
# mobl / mperm : deontic box / diamond over the serial relation d.
|
|
209
|
+
# malways / meventually : temporal box / diamond over t (refl-trans).
|
|
210
|
+
# mnext : temporal box over the one-step relation tnext.
|
|
211
|
+
_THF_DEFS_FULL = """\
|
|
212
|
+
thf(mnot, definition, ( mnot = ( ^ [Phi: mu>$o, W: mu] : ~ ( Phi @ W ) ) )).
|
|
213
|
+
thf(mand, definition, ( mand = ( ^ [Phi: mu>$o, Psi: mu>$o, W: mu] : ( ( Phi @ W ) & ( Psi @ W ) ) ) )).
|
|
214
|
+
thf(mor, definition, ( mor = ( ^ [Phi: mu>$o, Psi: mu>$o, W: mu] : ( ( Phi @ W ) | ( Psi @ W ) ) ) )).
|
|
215
|
+
thf(mimplies, definition, ( mimplies = ( ^ [Phi: mu>$o, Psi: mu>$o, W: mu] : ( ( Phi @ W ) => ( Psi @ W ) ) ) )).
|
|
216
|
+
thf(mequiv, definition, ( mequiv = ( ^ [Phi: mu>$o, Psi: mu>$o, W: mu] : ( ( Phi @ W ) <=> ( Psi @ W ) ) ) )).
|
|
217
|
+
thf(mbox, definition, ( mbox = ( ^ [Phi: mu>$o, W: mu] : ! [V: mu] : ( ( r @ W @ V ) => ( Phi @ V ) ) ) )).
|
|
218
|
+
thf(mdia, definition, ( mdia = ( ^ [Phi: mu>$o, W: mu] : ? [V: mu] : ( ( r @ W @ V ) & ( Phi @ V ) ) ) )).
|
|
219
|
+
thf(mknows, definition, ( mknows = ( ^ [A: $i, Phi: mu>$o, W: mu] : ! [V: mu] : ( ( rk @ A @ W @ V ) => ( Phi @ V ) ) ) )).
|
|
220
|
+
thf(mbelieves, definition, ( mbelieves = ( ^ [A: $i, Phi: mu>$o, W: mu] : ! [V: mu] : ( ( rb @ A @ W @ V ) => ( Phi @ V ) ) ) )).
|
|
221
|
+
thf(mobl, definition, ( mobl = ( ^ [Phi: mu>$o, W: mu] : ! [V: mu] : ( ( d @ W @ V ) => ( Phi @ V ) ) ) )).
|
|
222
|
+
thf(mperm, definition, ( mperm = ( ^ [Phi: mu>$o, W: mu] : ? [V: mu] : ( ( d @ W @ V ) & ( Phi @ V ) ) ) )).
|
|
223
|
+
thf(malways, definition, ( malways = ( ^ [Phi: mu>$o, W: mu] : ! [V: mu] : ( ( t @ W @ V ) => ( Phi @ V ) ) ) )).
|
|
224
|
+
thf(meventually, definition, ( meventually = ( ^ [Phi: mu>$o, W: mu] : ? [V: mu] : ( ( t @ W @ V ) & ( Phi @ V ) ) ) )).
|
|
225
|
+
thf(mnext, definition, ( mnext = ( ^ [Phi: mu>$o, W: mu] : ! [V: mu] : ( ( tnext @ W @ V ) => ( Phi @ V ) ) ) )).
|
|
226
|
+
thf(msays, definition, ( msays = ( ^ [A: $i, Phi: mu>$o, W: mu] : ! [V: mu] : ( ( rs @ A @ W @ V ) => ( Phi @ V ) ) ) )).
|
|
227
|
+
thf(mwants, definition, ( mwants = ( ^ [A: $i, Phi: mu>$o, W: mu] : ! [V: mu] : ( ( rw @ A @ W @ V ) => ( Phi @ V ) ) ) )).
|
|
228
|
+
thf(mhistorically, definition, ( mhistorically = ( ^ [Phi: mu>$o, W: mu] : ! [V: mu] : ( ( t @ V @ W ) => ( Phi @ V ) ) ) )).
|
|
229
|
+
thf(monce, definition, ( monce = ( ^ [Phi: mu>$o, W: mu] : ? [V: mu] : ( ( t @ V @ W ) & ( Phi @ V ) ) ) )).
|
|
230
|
+
thf(mprevious, definition, ( mprevious = ( ^ [Phi: mu>$o, W: mu] : ! [V: mu] : ( ( tnext @ V @ W ) => ( Phi @ V ) ) ) )).
|
|
231
|
+
thf(muntil, definition, ( muntil = ( ^ [Phi: mu>$o, Psi: mu>$o, W: mu] : ! [S: mu>$o] : ( ( ( ! [V: mu] : ( ( Psi @ V ) => ( S @ V ) ) ) & ( ! [V: mu, U: mu] : ( ( ( Phi @ V ) & ( tnext @ V @ U ) & ( S @ U ) ) => ( S @ V ) ) ) ) => ( S @ W ) ) ) )).
|
|
232
|
+
thf(msince, definition, ( msince = ( ^ [Phi: mu>$o, Psi: mu>$o, W: mu] : ! [S: mu>$o] : ( ( ( ! [V: mu] : ( ( Psi @ V ) => ( S @ V ) ) ) & ( ! [V: mu, U: mu] : ( ( ( Phi @ V ) & ( tnext @ U @ V ) & ( S @ U ) ) => ( S @ V ) ) ) ) => ( S @ W ) ) ) )).
|
|
233
|
+
thf(mforall, definition, ( mforall = ( ^ [Phi: $i>(mu>$o), W: mu] : ! [X: $i] : ( ( existsAt @ X @ W ) => ( Phi @ X @ W ) ) ) )).
|
|
234
|
+
thf(mexists, definition, ( mexists = ( ^ [Phi: $i>(mu>$o), W: mu] : ? [X: $i] : ( ( existsAt @ X @ W ) & ( Phi @ X @ W ) ) ) )).
|
|
235
|
+
thf(mvalid, definition, ( mvalid = ( ^ [Phi: mu>$o] : ! [W: mu] : ( Phi @ W ) ) )).\
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
# ---------------------------------------------------------------------------
|
|
240
|
+
# Rigid identity.
|
|
241
|
+
# ---------------------------------------------------------------------------
|
|
242
|
+
#
|
|
243
|
+
# Object identity is NOT a world-indexed predicate (fol.qml's "Equality is rigid"): it
|
|
244
|
+
# is THF's own `=` over `$i`, lifted by a macro whose world binder is unused. The macro
|
|
245
|
+
# is emitted only when the formula contains identity, so an equality-free problem is
|
|
246
|
+
# byte-for-byte what it was before; `to_thf_modal` (fol.qml) emits the SAME line, so
|
|
247
|
+
# the two exports stay byte-compatible on the alethic + equality fragment.
|
|
248
|
+
_THF_RIGID_EQ = "meq"
|
|
249
|
+
_THF_RIGID_EQ_DEF = ("thf(meq, definition, "
|
|
250
|
+
"( meq = ( ^ [A: $i, B: $i, W: mu] : ( A = B ) ) )).")
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
class _RigidNames(_ThfNames):
|
|
254
|
+
""":class:`~unicode_logic_kit.fol.qml._ThfNames` that reads ``=`` as the ``meq`` macro.
|
|
255
|
+
|
|
256
|
+
Identity is not a predicate: it gets no entry in ``pred`` (so ``_thf_signature``
|
|
257
|
+
declares no ``feq``) and ``atom`` names the macro. ``meq`` is reserved only when the
|
|
258
|
+
formula contains identity, so a user predicate / constant / function literally named
|
|
259
|
+
``meq`` is pushed to ``meq_2`` exactly then and not otherwise. (As before, the
|
|
260
|
+
resolver still lets ``=`` / ``≠`` claim ``feq`` / ``fneq`` first, which only
|
|
261
|
+
matters to a user symbol spelled that way; it stays unique.)
|
|
262
|
+
"""
|
|
263
|
+
|
|
264
|
+
#: Nominal name -> its world constant, filled by :func:`_resolve_names`.
|
|
265
|
+
nominal: Dict[str, str]
|
|
266
|
+
|
|
267
|
+
def __init__(self, formula: Node, reserved=_THF_RESERVED):
|
|
268
|
+
if _has_identity(formula):
|
|
269
|
+
reserved = frozenset(reserved) | {_THF_RIGID_EQ}
|
|
270
|
+
super().__init__(formula, reserved=reserved)
|
|
271
|
+
for key in [k for k in self.pred if k[0] in _EQUALITY_PREDICATES]:
|
|
272
|
+
del self.pred[key]
|
|
273
|
+
|
|
274
|
+
def atom(self, node: Atom) -> str:
|
|
275
|
+
if node.predicate == "=":
|
|
276
|
+
return _THF_RIGID_EQ
|
|
277
|
+
return super().atom(node)
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
# Frame axioms for the *temporal* relation t. G/F are read over a reflexive-transitive
|
|
281
|
+
# t (see the module-level caveat), so t carries refl+trans; X uses a separate one-step
|
|
282
|
+
# relation tnext. The `tnext ⊆ t` inclusion links the two relations: every one-step
|
|
283
|
+
# successor (over which X quantifies) is reachable by the henceforth relation t (over
|
|
284
|
+
# which G/F quantify). Without it Gφ→Xφ — VALID for satisfies_modal, where X's
|
|
285
|
+
# immediate "temporal" successors are a subset of G's reflexive-transitive closure of
|
|
286
|
+
# the *same* "temporal" relation — would be a non-theorem of the embedding (t and tnext
|
|
287
|
+
# would be unconstrained relative to each other). With it Gφ→Xφ (and Xφ→Fφ wherever the
|
|
288
|
+
# oracle agrees) becomes a theorem, aligning the embedding with the evaluator.
|
|
289
|
+
_THF_TEMPORAL_AXIOMS = [
|
|
290
|
+
"thf(t_refl, axiom, ( ! [W: mu] : ( t @ W @ W ) )).",
|
|
291
|
+
"thf(t_trans, axiom, ( ! [W: mu, V: mu, U: mu] : "
|
|
292
|
+
"( ( ( t @ W @ V ) & ( t @ V @ U ) ) => ( t @ W @ U ) ) )).",
|
|
293
|
+
"thf(tnext_in_t, axiom, ( ! [W: mu, V: mu] : "
|
|
294
|
+
"( ( tnext @ W @ V ) => ( t @ W @ V ) ) )).",
|
|
295
|
+
]
|
|
296
|
+
|
|
297
|
+
# Deontic d is serial (Standard Deontic Logic / KD): every world has a d-successor.
|
|
298
|
+
_THF_DEONTIC_AXIOM = "thf(d_serial, axiom, ( ! [W: mu] : ? [V: mu] : ( d @ W @ V ) ))."
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
# Optional per-system frame axioms for the AGENT-INDEXED epistemic/doxastic relations.
|
|
302
|
+
# Each is universally quantified over the agent A as well as the worlds, so the
|
|
303
|
+
# property holds per agent — matching the per-agent relations of satisfies_modal.
|
|
304
|
+
# "S5" (refl+trans+sym) is the usual logic of knowledge; "KD45" (serial+trans+eucl)
|
|
305
|
+
# the usual logic of belief.
|
|
306
|
+
def _agent_frame_axioms(rel: str, conds: Sequence[str], tag: str) -> List[str]:
|
|
307
|
+
"""THF frame axioms for an agent-indexed relation ``rel`` (each ∀ over the agent A).
|
|
308
|
+
|
|
309
|
+
Raises on a condition with no per-agent schema (Löb / directed / connected):
|
|
310
|
+
silently dropping one would emit a WEAKER logic than the caller requested.
|
|
311
|
+
"""
|
|
312
|
+
unsupported = [c for c in conds
|
|
313
|
+
if c not in ("refl", "trans", "sym", "serial", "eucl")]
|
|
314
|
+
if unsupported:
|
|
315
|
+
raise NotImplementedError(
|
|
316
|
+
f"to_thf_modal_full: the requested agent-indexed system needs the frame "
|
|
317
|
+
f"condition(s) {unsupported}, which have no per-agent axiom schema here; "
|
|
318
|
+
"use frame= on the alethic relation for those systems.")
|
|
319
|
+
out: List[str] = []
|
|
320
|
+
if "refl" in conds:
|
|
321
|
+
out.append(f"thf({tag}_refl, axiom, ( ! [A: $i, W: mu] : ( {rel} @ A @ W @ W ) )).")
|
|
322
|
+
if "trans" in conds:
|
|
323
|
+
out.append(
|
|
324
|
+
f"thf({tag}_trans, axiom, ( ! [A: $i, W: mu, V: mu, U: mu] : "
|
|
325
|
+
f"( ( ( {rel} @ A @ W @ V ) & ( {rel} @ A @ V @ U ) ) "
|
|
326
|
+
f"=> ( {rel} @ A @ W @ U ) ) )).")
|
|
327
|
+
if "sym" in conds:
|
|
328
|
+
out.append(
|
|
329
|
+
f"thf({tag}_sym, axiom, ( ! [A: $i, W: mu, V: mu] : "
|
|
330
|
+
f"( ( {rel} @ A @ W @ V ) => ( {rel} @ A @ V @ W ) ) )).")
|
|
331
|
+
if "serial" in conds:
|
|
332
|
+
out.append(
|
|
333
|
+
f"thf({tag}_serial, axiom, ( ! [A: $i, W: mu] : ? [V: mu] : "
|
|
334
|
+
f"( {rel} @ A @ W @ V ) )).")
|
|
335
|
+
if "eucl" in conds:
|
|
336
|
+
out.append(
|
|
337
|
+
f"thf({tag}_eucl, axiom, ( ! [A: $i, W: mu, V: mu, U: mu] : "
|
|
338
|
+
f"( ( ( {rel} @ A @ W @ V ) & ( {rel} @ A @ W @ U ) ) "
|
|
339
|
+
f"=> ( {rel} @ A @ V @ U ) ) )).")
|
|
340
|
+
return out
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
# ---------------------------------------------------------------------------
|
|
344
|
+
# Cross-family bridge axioms (opt-in, ``bridges=``).
|
|
345
|
+
# ---------------------------------------------------------------------------
|
|
346
|
+
#
|
|
347
|
+
# Only the axiom TEXT lives here; the bridge names and the families each one needs
|
|
348
|
+
# come from hol.isabelle_modal's registry, so `grep rb_in_rk` finds both routes and
|
|
349
|
+
# neither can gain a bridge the other lacks. The TPTP formula names are the SAME
|
|
350
|
+
# identifiers as the Isabelle fact names (rb_in_rk / rb_in_rs / d_meets_r) and
|
|
351
|
+
# cannot collide with the signature declarations, which all carry a `_decl` suffix.
|
|
352
|
+
#
|
|
353
|
+
# Types check out unconditionally: rk / rb / rs are `$i > mu > mu > $o` and d / r
|
|
354
|
+
# are `mu > mu > $o`, and this route declares every relation whether or not its
|
|
355
|
+
# operators occur (see the comment at the declaration block), so a bridge axiom is
|
|
356
|
+
# always WELL-FORMED here. That is exactly why the refusal below is a policy
|
|
357
|
+
# decision rather than a syntactic necessity.
|
|
358
|
+
_THF_BRIDGE_LINES = {
|
|
359
|
+
"knowledge_implies_belief": [
|
|
360
|
+
"thf(rb_in_rk, axiom, ( ! [A: $i, W: mu, V: mu] : "
|
|
361
|
+
"( ( rb @ A @ W @ V ) => ( rk @ A @ W @ V ) ) ))."],
|
|
362
|
+
"sincerity": [
|
|
363
|
+
"thf(rb_in_rs, axiom, ( ! [A: $i, W: mu, V: mu] : "
|
|
364
|
+
"( ( rb @ A @ W @ V ) => ( rs @ A @ W @ V ) ) ))."],
|
|
365
|
+
"ought_implies_can": [
|
|
366
|
+
"thf(d_meets_r, axiom, ( ! [W: mu] : ? [V: mu] : "
|
|
367
|
+
"( ( d @ W @ V ) & ( r @ W @ V ) ) ))."],
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def _thf_bridge_axioms(used: Dict[str, bool], bridges) -> List[str]:
|
|
372
|
+
"""THF axioms for the requested cross-family bridges, in registry order.
|
|
373
|
+
|
|
374
|
+
Each is the exact frame correspondent of the schema its option is named after
|
|
375
|
+
(``rb ⊆ rk``, ``rb ⊆ rs``, ``∀W ∃V. d W V ∧ r W V``), measured — not the
|
|
376
|
+
folklore ``d ⊆ r``, which fails to validate ``Oφ → ◇φ`` alone and, with
|
|
377
|
+
seriality, over-validates ``□φ → Oφ``. Emission order follows the shared
|
|
378
|
+
registry, not the caller's set-iteration order, so the output is deterministic.
|
|
379
|
+
|
|
380
|
+
Honest contract — why this route REFUSES a bridge whose partner family does not
|
|
381
|
+
occur in the formula, even though it could emit it. Unlike the Isabelle route,
|
|
382
|
+
every relation here is declared unconditionally (the ``_THF_DEFS_FULL`` macro
|
|
383
|
+
block references them all), so ``d_meets_r`` for a ``□``-only formula would be
|
|
384
|
+
perfectly well-formed THF. Emitting it anyway would nonetheless be wrong twice
|
|
385
|
+
over: it would make this route DISAGREE with
|
|
386
|
+
:func:`~unicode_logic_kit.hol.isabelle_modal.to_isabelle_modal` on the same input
|
|
387
|
+
— the exact divergence class the ``bridges=`` option exists to remove — and it
|
|
388
|
+
is not conservative, since ``d_meets_r`` entails seriality of the alethic ``r``
|
|
389
|
+
and so silently turns ``□P → ◇P`` from a non-theorem into a theorem under
|
|
390
|
+
``frame='K'``. Silently skipping it would be worse still (a weaker logic than
|
|
391
|
+
requested, with no signal). So: ``ValueError``, same wording as the Isabelle
|
|
392
|
+
route modulo the function name.
|
|
393
|
+
"""
|
|
394
|
+
requested = _validate_bridges(bridges, "to_thf_modal_full")
|
|
395
|
+
if not requested:
|
|
396
|
+
return []
|
|
397
|
+
out: List[str] = []
|
|
398
|
+
for name, spec in _BRIDGE_SPEC.items():
|
|
399
|
+
if name not in requested:
|
|
400
|
+
continue
|
|
401
|
+
missing = [op for fam, op in spec["needs"] if not used[fam]]
|
|
402
|
+
if missing:
|
|
403
|
+
raise ValueError(
|
|
404
|
+
f"to_thf_modal_full: the bridge {name!r} relates {spec['rels']}, "
|
|
405
|
+
f"but the formula contains no {' / '.join(missing)} operator. This "
|
|
406
|
+
"route declares every relation unconditionally, so the axiom would "
|
|
407
|
+
"be well-formed here — but emitting it would make this route "
|
|
408
|
+
"disagree with hol.isabelle_modal on the same input (the exact "
|
|
409
|
+
"divergence this option exists to remove), and it is not "
|
|
410
|
+
"conservative either (d_meets_r entails seriality of the alethic "
|
|
411
|
+
"r, which would silently make []P -> <>P valid under frame='K'); "
|
|
412
|
+
"while skipping it would emit a weaker logic than requested. Drop "
|
|
413
|
+
"the bridge, or state the formula in both families.")
|
|
414
|
+
out += _THF_BRIDGE_LINES[name]
|
|
415
|
+
return out
|
|
416
|
+
|
|
417
|
+
|
|
418
|
+
# ---------------------------------------------------------------------------
|
|
419
|
+
# Lifting the formula to a THF term of type mu > $o.
|
|
420
|
+
# ---------------------------------------------------------------------------
|
|
421
|
+
def _lift(node: Node, names: "_RigidNames") -> str:
|
|
422
|
+
"""Render a full-family modal formula as a THF term of type ``mu > $o``.
|
|
423
|
+
|
|
424
|
+
Classical connectives, alethic □/◇, object quantifiers and atoms are handled
|
|
425
|
+
exactly as in :func:`unicode_logic_kit.fol.qml._thf_lift`; the additional families
|
|
426
|
+
are lifted through the macros defined in :data:`_THF_DEFS_FULL`. ``names`` is the
|
|
427
|
+
per-formula de-colliding functor resolver (so distinct symbols never collapse).
|
|
428
|
+
"""
|
|
429
|
+
if isinstance(node, Atom):
|
|
430
|
+
# An identity atom reaches here as `=` (`≠` was lowered to `¬(=)` up front) and
|
|
431
|
+
# `names.atom` answers the `meq` macro, so it lifts as `( meq @ t1 @ t2 )`.
|
|
432
|
+
constant = truth_value(node)
|
|
433
|
+
if constant is not None:
|
|
434
|
+
# `$true` / `$false`: the proposition true (false) at every world.
|
|
435
|
+
return "( ^ [W: mu] : $true )" if constant else "( ^ [W: mu] : $false )"
|
|
436
|
+
head = names.atom(node)
|
|
437
|
+
if not node.args:
|
|
438
|
+
return head
|
|
439
|
+
return "( " + " @ ".join([head] + [_thf_term(a, names) for a in node.args]) + " )"
|
|
440
|
+
if isinstance(node, Not):
|
|
441
|
+
return f"( mnot @ {_lift(node.formula, names)} )"
|
|
442
|
+
if isinstance(node, And):
|
|
443
|
+
return f"( mand @ {_lift(node.left, names)} @ {_lift(node.right, names)} )"
|
|
444
|
+
if isinstance(node, Or):
|
|
445
|
+
return f"( mor @ {_lift(node.left, names)} @ {_lift(node.right, names)} )"
|
|
446
|
+
if isinstance(node, Xor):
|
|
447
|
+
# Xor is ¬(φ ⇔ ψ): no dedicated macro, build it from mnot/mequiv.
|
|
448
|
+
return f"( mnot @ ( mequiv @ {_lift(node.left, names)} @ {_lift(node.right, names)} ) )"
|
|
449
|
+
if isinstance(node, Implies):
|
|
450
|
+
return f"( mimplies @ {_lift(node.left, names)} @ {_lift(node.right, names)} )"
|
|
451
|
+
if isinstance(node, Iff):
|
|
452
|
+
return f"( mequiv @ {_lift(node.left, names)} @ {_lift(node.right, names)} )"
|
|
453
|
+
|
|
454
|
+
if isinstance(node, Box):
|
|
455
|
+
return f"( mbox @ {_lift(node.formula, names)} )"
|
|
456
|
+
if isinstance(node, Diamond):
|
|
457
|
+
return f"( mdia @ {_lift(node.formula, names)} )"
|
|
458
|
+
|
|
459
|
+
if isinstance(node, Contrast):
|
|
460
|
+
# Truth-functionally conjunction (Contrast's own contract).
|
|
461
|
+
return f"( mand @ {_lift(node.left, names)} @ {_lift(node.right, names)} )"
|
|
462
|
+
|
|
463
|
+
# Agent-indexed epistemic / doxastic / assertive / bouletic: the agent is a
|
|
464
|
+
# real $i argument.
|
|
465
|
+
if isinstance(node, Knows):
|
|
466
|
+
return f"( mknows @ {_thf_term(node.agent, names)} @ {_lift(node.formula, names)} )"
|
|
467
|
+
if isinstance(node, Believes):
|
|
468
|
+
return f"( mbelieves @ {_thf_term(node.agent, names)} @ {_lift(node.formula, names)} )"
|
|
469
|
+
if isinstance(node, Says):
|
|
470
|
+
return f"( msays @ {_thf_term(node.agent, names)} @ {_lift(node.formula, names)} )"
|
|
471
|
+
if isinstance(node, Wants):
|
|
472
|
+
return f"( mwants @ {_thf_term(node.agent, names)} @ {_lift(node.formula, names)} )"
|
|
473
|
+
|
|
474
|
+
if isinstance(node, Obligatory):
|
|
475
|
+
return f"( mobl @ {_lift(node.formula, names)} )"
|
|
476
|
+
if isinstance(node, Permitted):
|
|
477
|
+
return f"( mperm @ {_lift(node.formula, names)} )"
|
|
478
|
+
|
|
479
|
+
if isinstance(node, Always):
|
|
480
|
+
return f"( malways @ {_lift(node.formula, names)} )"
|
|
481
|
+
if isinstance(node, Eventually):
|
|
482
|
+
return f"( meventually @ {_lift(node.formula, names)} )"
|
|
483
|
+
if isinstance(node, Next):
|
|
484
|
+
return f"( mnext @ {_lift(node.formula, names)} )"
|
|
485
|
+
|
|
486
|
+
if isinstance(node, Quantifier):
|
|
487
|
+
x = node.variable.name.upper()
|
|
488
|
+
binder = "mforall" if node.type in (_FORALL, "forall") else "mexists"
|
|
489
|
+
return f"( {binder} @ ( ^ [{x}: $i] : {_lift(node.formula, names)} ) )"
|
|
490
|
+
|
|
491
|
+
if isinstance(node, Historically):
|
|
492
|
+
# Box over the CONVERSE of t (faithful to satisfies_modal's past reading).
|
|
493
|
+
return f"( mhistorically @ {_lift(node.formula, names)} )"
|
|
494
|
+
if isinstance(node, Once):
|
|
495
|
+
return f"( monce @ {_lift(node.formula, names)} )"
|
|
496
|
+
if isinstance(node, Previous):
|
|
497
|
+
return f"( mprevious @ {_lift(node.formula, names)} )"
|
|
498
|
+
|
|
499
|
+
if isinstance(node, Until):
|
|
500
|
+
# Strong Until as the impredicative Knaster–Tarski least fixpoint over
|
|
501
|
+
# the one-step tnext — TH0 quantifies over predicates, so the same least
|
|
502
|
+
# fixpoint Isabelle's `inductive muntil` compiles to is directly shallow-
|
|
503
|
+
# embeddable (the earlier claim that it is not was simply wrong).
|
|
504
|
+
return f"( muntil @ {_lift(node.left, names)} @ {_lift(node.right, names)} )"
|
|
505
|
+
if isinstance(node, Since):
|
|
506
|
+
# The converse-relation mirror of muntil (backward one-step paths).
|
|
507
|
+
return f"( msince @ {_lift(node.left, names)} @ {_lift(node.right, names)} )"
|
|
508
|
+
|
|
509
|
+
if isinstance(node, Nominal):
|
|
510
|
+
# True at exactly the named world: the reserved nom_ world constant, the
|
|
511
|
+
# same convention as standard_translation and isabelle_modal. The functor
|
|
512
|
+
# comes from the resolver's per-formula nominal map, so two DISTINCT
|
|
513
|
+
# nominals whose names sanitise alike ('A' / 'a') stay distinct worlds.
|
|
514
|
+
return f"( ^ [W: mu] : ( W = {names.nominal[node.name]} ) )"
|
|
515
|
+
if isinstance(node, At):
|
|
516
|
+
return (f"( ^ [W: mu] : ( {_lift(node.formula, names)} "
|
|
517
|
+
f"@ {names.nominal[node.nominal.name]} ) )")
|
|
518
|
+
if isinstance(node, Down):
|
|
519
|
+
raise NotImplementedError(
|
|
520
|
+
"to_thf_modal_full: the ↓ binder is not supported by this HOL "
|
|
521
|
+
"shallow embedding — H(@,↓) validity is undecidable, and this "
|
|
522
|
+
"emitter's job (a TPTP THF conjecture for an ATP) assumes a goal "
|
|
523
|
+
"shape a prover can be expected to close, not an open research "
|
|
524
|
+
"question. Use unicode_logic_kit.fol.modal_translation.down_is_valid "
|
|
525
|
+
"(Z3, PROVED-only) or unicode_logic_kit.atp.kripke_enum.KripkeEnumBackend "
|
|
526
|
+
"/ modal_enum_search (bounded search, REFUTED-only) instead.")
|
|
527
|
+
|
|
528
|
+
if isinstance(node, SortedQuantifier):
|
|
529
|
+
raise NotImplementedError(
|
|
530
|
+
"to_thf_modal_full: SortedQuantifier is not supported; use a plain ∀x/∃x.")
|
|
531
|
+
if type(node).__name__ in ("Would", "Might"):
|
|
532
|
+
raise NotImplementedError(
|
|
533
|
+
"to_thf_modal_full: the counterfactuals □→/◇→ read a similarity "
|
|
534
|
+
"ordering (Lewis spheres), not an accessibility relation — use "
|
|
535
|
+
"hol.isabelle_conditional / isabelle_decide_counterfactual.")
|
|
536
|
+
raise NotImplementedError(
|
|
537
|
+
f"to_thf_modal_full: unsupported node type {type(node).__name__}.")
|
|
538
|
+
|
|
539
|
+
|
|
540
|
+
def _nominal_names(formula: Node):
|
|
541
|
+
"""All nominal names occurring in ``formula`` (Nominal and At sites), sorted."""
|
|
542
|
+
return sorted({n.name for n in formula.walk() if isinstance(n, Nominal)})
|
|
543
|
+
|
|
544
|
+
|
|
545
|
+
def _sort_functors(original: Node, names: "_ThfNames") -> List[str]:
|
|
546
|
+
"""THF functors of the sorts ``original`` quantifies over, in first-occurrence order.
|
|
547
|
+
|
|
548
|
+
Takes the UNrelativized formula: afterwards a sort guard is indistinguishable
|
|
549
|
+
from any other unary predicate. Reuses
|
|
550
|
+
:func:`~unicode_logic_kit.fol._msfl_nodes.nonempty_sort_axioms`'s own scan — as
|
|
551
|
+
:func:`~unicode_logic_kit.fol.qml._sort_names_used` and
|
|
552
|
+
:func:`~unicode_logic_kit.hol.isabelle_modal._sort_consts` do — so the three
|
|
553
|
+
routes can never disagree about which sorts a formula uses, then maps each
|
|
554
|
+
through the de-colliding resolver so the axiom names the same functor the
|
|
555
|
+
conjecture does. A sort with no unary atom in the relativized formula has no
|
|
556
|
+
type declaration, so it is skipped rather than referenced undeclared.
|
|
557
|
+
"""
|
|
558
|
+
out: List[str] = []
|
|
559
|
+
for axiom in nonempty_sort_axioms(original):
|
|
560
|
+
assert isinstance(axiom, Quantifier) and isinstance(axiom.formula, Atom) # ∃x S(x)
|
|
561
|
+
functor = names.pred.get((axiom.formula.predicate, 1))
|
|
562
|
+
if functor is not None and functor not in out:
|
|
563
|
+
out.append(functor)
|
|
564
|
+
return out
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
def _sort_members(original: Node, names: "_ThfNames") -> List[Tuple[str, str]]:
|
|
568
|
+
"""``(guard functor, individual functor)`` of every sorted constant of ``original``.
|
|
569
|
+
|
|
570
|
+
One pair per distinct ``c:S``, in first-occurrence order (the order of
|
|
571
|
+
:func:`~unicode_logic_kit.fol._msfl_nodes.sort_membership_axioms`), each name taken
|
|
572
|
+
from ``names`` — which must come from :func:`_resolve_names` called with the same
|
|
573
|
+
``original``, so the guard of a sort that occurs only through a sorted constant is
|
|
574
|
+
declared and the pair names exactly the functors the conjecture uses.
|
|
575
|
+
"""
|
|
576
|
+
out: List[Tuple[str, str]] = []
|
|
577
|
+
for atom in sort_membership_axioms(original):
|
|
578
|
+
assert isinstance(atom, Atom) and isinstance(atom.args[0], Constant) # ``S(c)``
|
|
579
|
+
pair = (names.pred[(atom.predicate, 1)], names.constant(atom.args[0].name))
|
|
580
|
+
if pair not in out:
|
|
581
|
+
out.append(pair)
|
|
582
|
+
return out
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
def _resolve_names(formula: Node, original: Optional[Node] = None) -> "_RigidNames":
|
|
586
|
+
"""Build the per-formula functor resolver, including a de-colliding nominal map.
|
|
587
|
+
|
|
588
|
+
User symbols are resolved against the full export's reserved functor set;
|
|
589
|
+
then each nominal name gets a ``nom_``-prefixed ``mu`` constant deduped
|
|
590
|
+
against BOTH the user functors and the other nominals. Without this, two
|
|
591
|
+
distinct nominals sanitising alike (``'A'`` / ``'a'``) would silently
|
|
592
|
+
collapse to the SAME world constant — the emitted problem would load but no
|
|
593
|
+
longer mean what the source formula meant (and a user constant literally
|
|
594
|
+
named ``nom_a`` would conflate with the nominal ``a``'s world).
|
|
595
|
+
|
|
596
|
+
``original`` is the many-sorted formula ``formula`` was relativized from. In
|
|
597
|
+
``formula`` a sorted constant ``c:S`` is the plain ``c`` and the guard ``S`` is
|
|
598
|
+
mentioned only if a sorted QUANTIFIER used it; resolving over ``formula``
|
|
599
|
+
together with the membership atom ``S(c)`` of every sorted constant of
|
|
600
|
+
``original`` gives a sort that occurs only through a sorted constant its guard
|
|
601
|
+
functor and type declaration too. Without a sorted constant the resolver is
|
|
602
|
+
built over ``formula`` itself, so every name is what it was.
|
|
603
|
+
"""
|
|
604
|
+
scope = formula
|
|
605
|
+
if original is not None:
|
|
606
|
+
for atom in sort_membership_axioms(original):
|
|
607
|
+
scope = And(scope, atom)
|
|
608
|
+
formula = scope
|
|
609
|
+
names = _RigidNames(formula, reserved=_THF_RESERVED_FULL)
|
|
610
|
+
taken = (set(_THF_RESERVED_FULL)
|
|
611
|
+
| set(names.pred.values()) | set(names.const.values())
|
|
612
|
+
| set(names.func.values()))
|
|
613
|
+
names.nominal = {}
|
|
614
|
+
for raw in _nominal_names(formula):
|
|
615
|
+
names.nominal[raw] = dedupe("nom_" + _thf_name(raw), taken)
|
|
616
|
+
return names
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
# ---------------------------------------------------------------------------
|
|
620
|
+
# Which families occur — so we only declare/axiomatise the relations we use.
|
|
621
|
+
# ---------------------------------------------------------------------------
|
|
622
|
+
def _families_used(formula: Node) -> Dict[str, bool]:
|
|
623
|
+
"""Scan ``formula`` for which modal families occur (controls what gets emitted)."""
|
|
624
|
+
used = {"alethic": False, "epistemic": False, "doxastic": False,
|
|
625
|
+
"assertive": False, "bouletic": False,
|
|
626
|
+
"deontic": False, "temporal": False, "tnext": False}
|
|
627
|
+
for n in formula.walk():
|
|
628
|
+
if isinstance(n, (Box, Diamond)):
|
|
629
|
+
used["alethic"] = True
|
|
630
|
+
elif isinstance(n, Knows):
|
|
631
|
+
used["epistemic"] = True
|
|
632
|
+
elif isinstance(n, Believes):
|
|
633
|
+
used["doxastic"] = True
|
|
634
|
+
elif isinstance(n, Says):
|
|
635
|
+
used["assertive"] = True
|
|
636
|
+
elif isinstance(n, Wants):
|
|
637
|
+
used["bouletic"] = True
|
|
638
|
+
elif isinstance(n, (Obligatory, Permitted)):
|
|
639
|
+
used["deontic"] = True
|
|
640
|
+
elif isinstance(n, (Always, Eventually, Historically, Once)):
|
|
641
|
+
# ⒣/⒫ read the CONVERSE of the same henceforth t, whose refl/trans
|
|
642
|
+
# axioms constrain the past readings equally.
|
|
643
|
+
used["temporal"] = True
|
|
644
|
+
elif isinstance(n, (Next, Previous, Until, Since)):
|
|
645
|
+
# ⒴ reads the converse of tnext; the muntil/msince fixpoints step
|
|
646
|
+
# over tnext — all need the one-step relation's link into t.
|
|
647
|
+
used["tnext"] = True
|
|
648
|
+
return used
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
def thf_full_definitions() -> str:
|
|
652
|
+
"""Return the block of lifted-operator THF definitions used by the full export."""
|
|
653
|
+
return _THF_DEFS_FULL
|
|
654
|
+
|
|
655
|
+
|
|
656
|
+
# Agent-indexed families that systems= may constrain, with their relation + tag.
|
|
657
|
+
_AGENT_SYSTEM_FAMILIES = (
|
|
658
|
+
("epistemic", _R_KNOWS, "rk"),
|
|
659
|
+
("doxastic", _R_BELIEVES, "rb"),
|
|
660
|
+
("assertive", _R_SAYS, "rs"),
|
|
661
|
+
("bouletic", _R_WANTS, "rw"),
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
|
|
665
|
+
def thf_full_frame_axioms(frame: str = "K",
|
|
666
|
+
systems: Optional[Dict[str, str]] = None,
|
|
667
|
+
temporal_closure: bool = True,
|
|
668
|
+
bridges: Optional[Sequence[str]] = None) -> List[str]:
|
|
669
|
+
"""Return the frame axioms the full export would emit (for inspection / testing).
|
|
670
|
+
|
|
671
|
+
``frame`` constrains the alethic relation ``r`` (K/T/S4/S5/KD/KD45); ``systems`` is
|
|
672
|
+
an optional mapping that constrains the agent-indexed epistemic / doxastic /
|
|
673
|
+
assertive / bouletic relations, e.g. ``{"epistemic": "S5", "doxastic": "KD45"}``.
|
|
674
|
+
The deontic relation ``d`` is always serial; the temporal relation ``t`` is
|
|
675
|
+
reflexive-transitive unless ``temporal_closure=False`` (which keeps only the
|
|
676
|
+
``tnext ⊆ t`` inclusion, leaving ``t`` an arbitrary relation — mirroring
|
|
677
|
+
``isabelle_modal_theory``'s parameter of the same name). ``bridges`` appends the
|
|
678
|
+
cross-family bridge axioms (see :func:`_thf_bridge_axioms`).
|
|
679
|
+
|
|
680
|
+
ASYMMETRY, stated so this helper does not look like it contradicts the emitter:
|
|
681
|
+
it takes no formula, so it cannot know which families occur and emits every
|
|
682
|
+
requested axiom UNCONDITIONALLY — exactly as it already emits the ``systems=``
|
|
683
|
+
axioms and ``d_serial`` unconditionally while :func:`to_thf_modal_full` gates
|
|
684
|
+
them on the families actually used. Unknown bridge names are still rejected
|
|
685
|
+
here, so a typo is caught in the inspection path too; the "family absent"
|
|
686
|
+
refusal can only be made by the real emitter.
|
|
687
|
+
"""
|
|
688
|
+
if frame not in _FRAMES:
|
|
689
|
+
raise ValueError(f"to_thf_modal_full: unknown frame {frame!r}.")
|
|
690
|
+
systems = systems or {}
|
|
691
|
+
axioms: List[str] = [_THF_FRAME[c] for c in _FRAMES[frame]]
|
|
692
|
+
for fam, rel, tag in _AGENT_SYSTEM_FAMILIES:
|
|
693
|
+
sys = systems.get(fam)
|
|
694
|
+
if sys is not None:
|
|
695
|
+
if sys not in _FRAMES:
|
|
696
|
+
raise ValueError(
|
|
697
|
+
f"to_thf_modal_full: unknown system {sys!r} for {fam} "
|
|
698
|
+
f"(use one of {sorted(_FRAMES)}).")
|
|
699
|
+
axioms += _agent_frame_axioms(rel, _FRAMES[sys], tag)
|
|
700
|
+
axioms.append(_THF_DEONTIC_AXIOM)
|
|
701
|
+
axioms += _THF_TEMPORAL_AXIOMS if temporal_closure else _THF_TEMPORAL_AXIOMS[-1:]
|
|
702
|
+
requested = _validate_bridges(bridges, "to_thf_modal_full")
|
|
703
|
+
for name in _BRIDGE_SPEC: # registry order, not the caller's set order
|
|
704
|
+
if name in requested:
|
|
705
|
+
axioms += _THF_BRIDGE_LINES[name]
|
|
706
|
+
return axioms
|
|
707
|
+
|
|
708
|
+
|
|
709
|
+
def to_thf_modal_full(formula: Node, mode: str = "constant", frame: str = "K",
|
|
710
|
+
systems: Optional[Dict[str, str]] = None,
|
|
711
|
+
temporal_closure: bool = True,
|
|
712
|
+
bridges: Optional[Sequence[str]] = None) -> str:
|
|
713
|
+
"""Emit a complete Benzmüller-style **THF** shallow embedding of a full-family modal formula.
|
|
714
|
+
|
|
715
|
+
Unlike :func:`unicode_logic_kit.fol.qml.to_thf_modal` (alethic-only), this covers the
|
|
716
|
+
whole modal family the AST expresses: alethic ``□``/``◇``, agent-indexed epistemic
|
|
717
|
+
``K_a`` and doxastic ``B_a``, deontic ``O``/``P`` (serial / KD), and temporal
|
|
718
|
+
``G``/``F``/``X``. It produces a self-contained THF problem — type declarations,
|
|
719
|
+
every lifted operator, the relevant frame axioms, the ``existsAt`` domain axioms for
|
|
720
|
+
``mode``, and the conjecture ``mvalid @ ⟨formula⟩`` — ready for a higher-order ATP
|
|
721
|
+
(Leo-III / Satallax). The toolkit only emits the problem; it does not run a prover.
|
|
722
|
+
|
|
723
|
+
A many-sorted formula also gets its sort facts, as ``axiom`` lines beside the
|
|
724
|
+
conjecture: ``nonempty_sort<i>`` (the sort is non-empty at every world) and, per
|
|
725
|
+
sorted constant ``c:S``, ``sort_member<i>`` (``! [W: mu] : ( S @ c @ W )``: ``c`` is
|
|
726
|
+
an element of ``S`` at EVERY world, unguarded by ``existsAt``, because a constant is
|
|
727
|
+
rigid and may lie outside the domain of a world). A sort that occurs only through
|
|
728
|
+
a sorted constant gets its guard declared too. A formula without a sorted constant
|
|
729
|
+
gets no ``sort_member`` line and its text is unchanged.
|
|
730
|
+
|
|
731
|
+
Parameters:
|
|
732
|
+
formula: the modal formula to embed.
|
|
733
|
+
mode: object-quantifier domain regime — ``"constant"`` / ``"possibilist"``
|
|
734
|
+
(unrelativised) or ``"varying"`` / ``"increasing"`` / ``"cumulative"`` /
|
|
735
|
+
``"decreasing"`` (actualist, ``existsAt``-guarded).
|
|
736
|
+
frame: constrains the ALETHIC relation ``r`` (K/T/S4/S5/KD/KD45).
|
|
737
|
+
systems: optional per-family system selection for the AGENT-INDEXED epistemic /
|
|
738
|
+
doxastic / assertive / bouletic relations, e.g. ``{"epistemic": "S5",
|
|
739
|
+
"doxastic": "KD45"}``. Each value is a frame name from
|
|
740
|
+
``{K,T,S4,S5,KD,KD45}``; the property is asserted ``∀A`` over the agent, so
|
|
741
|
+
it holds per agent.
|
|
742
|
+
temporal_closure: emit the ``t_refl`` / ``t_trans`` axioms making ``t``
|
|
743
|
+
reflexive-transitive (default). ``False`` keeps only the ``tnext ⊆ t``
|
|
744
|
+
inclusion, so ``t`` denotes an arbitrary relation — mirroring
|
|
745
|
+
``isabelle_modal_theory``'s parameter of the same name.
|
|
746
|
+
bridges: optional collection of CROSS-family bridge names (:data:`BRIDGES`,
|
|
747
|
+
shared verbatim with ``hol.isabelle_modal``), **off by default**:
|
|
748
|
+
``"knowledge_implies_belief"`` (``K_a φ → B_a φ``, axiom ``rb_in_rk``,
|
|
749
|
+
condition ``rb ⊆ rk``), ``"sincerity"`` (``Say_a φ → B_a φ``,
|
|
750
|
+
``rb_in_rs``, ``rb ⊆ rs``) and ``"ought_implies_can"`` (``Oφ → ◇φ``,
|
|
751
|
+
``d_meets_r``, ``∀W ∃V. d W V ∧ r W V`` — NOT the folklore ``d ⊆ r``).
|
|
752
|
+
Each condition is the exact correspondent of its schema, so a bridge
|
|
753
|
+
adds that principle and nothing stronger. Requesting a bridge whose
|
|
754
|
+
partner family does not occur in the formula raises ``ValueError``,
|
|
755
|
+
matching ``to_isabelle_modal`` even though the axiom would be
|
|
756
|
+
well-formed here — see :func:`_thf_bridge_axioms`.
|
|
757
|
+
|
|
758
|
+
Agent-indexedness (faithful to ``satisfies_modal``): the agent of ``Knows`` /
|
|
759
|
+
``Believes`` / ``Says`` / ``Wants`` is a first-class TERM and is carried into
|
|
760
|
+
``rk`` / ``rb`` / ``rs`` / ``rw`` as a real ``$i`` argument, so a bound object
|
|
761
|
+
variable in agent position quantifies over agents
|
|
762
|
+
(``∀x (Student(x) → K_x φ)``). A named agent becomes an ordinary ``$i`` constant.
|
|
763
|
+
|
|
764
|
+
Caveats: ``Until`` / ``Since`` ARE supported — as the impredicative
|
|
765
|
+
Knaster–Tarski least-fixpoint macros ``muntil`` / ``msince`` over the one-step
|
|
766
|
+
``tnext`` (TH0 quantifies over predicates, so the least fixpoint is directly
|
|
767
|
+
expressible). Temporal ``G``/``F`` are read over a reflexive-transitive ``t`` and
|
|
768
|
+
only approximate ``satisfies_modal``'s closure over the caller's raw temporal
|
|
769
|
+
edges (see the module docstring). Equality ``=`` / ``≠`` is RIGID identity — THF's
|
|
770
|
+
own ``=`` with no world argument, through the ``meq`` macro (``≠`` lowered to
|
|
771
|
+
``¬(=)``) — the reading of :func:`~unicode_logic_kit.fol.qml.qml_is_valid`, so
|
|
772
|
+
``a = a`` and ``a = b → □(a = b)`` are theorems and ``□(a = b) → a = b`` is one
|
|
773
|
+
exactly under a reflexive / serial ``frame`` (module docstring, "Equality").
|
|
774
|
+
First-order/higher-order modal logic is undecidable, so a ``Theorem`` verdict
|
|
775
|
+
requires an external HOL ATP and is not guaranteed to terminate.
|
|
776
|
+
"""
|
|
777
|
+
if frame not in _FRAMES:
|
|
778
|
+
raise ValueError(f"to_thf_modal_full: unknown frame {frame!r}.")
|
|
779
|
+
if mode not in _ACTUALIST_MODES and mode not in _CONSTANT_MODES:
|
|
780
|
+
raise ValueError(
|
|
781
|
+
f"to_thf_modal_full: unknown mode {mode!r} (use one of "
|
|
782
|
+
f"{sorted(_ACTUALIST_MODES | _CONSTANT_MODES)}).")
|
|
783
|
+
# Reject a bare string / an unknown bridge name before any emission work.
|
|
784
|
+
_validate_bridges(bridges, "to_thf_modal_full")
|
|
785
|
+
|
|
786
|
+
# A many-sorted formula (SortedQuantifier / SortedConstant) is relativized
|
|
787
|
+
# ONCE, here, before anything scans or lifts it — same choice, and same
|
|
788
|
+
# reason, as hol.isabelle_modal.isabelle_modal_theory and fol.qml's
|
|
789
|
+
# qml_translate: the resulting sort-guard atom is an ordinary predicate
|
|
790
|
+
# the existing signature scan / lift already handle, and doing it once,
|
|
791
|
+
# up front, also catches a SortedConstant anywhere in the formula, not
|
|
792
|
+
# only directly under a SortedQuantifier.
|
|
793
|
+
#
|
|
794
|
+
# A numeral is a constant identified by its value (1 and 1.0 are one), named ``n1``:
|
|
795
|
+
# a user constant spelled like it is refused, not merged with it.
|
|
796
|
+
[formula], _ = numerals_as_constants([formula], where="to_thf_modal_full",
|
|
797
|
+
spell=prefixed_numeral_name)
|
|
798
|
+
original = formula
|
|
799
|
+
formula = formula._relativize([])
|
|
800
|
+
# Rigid identity (qml's reading): `≠` -> `¬(=)`, a non-binary `=` refused. A formula
|
|
801
|
+
# without an identity atom is returned unchanged, so it emits byte-for-byte as before.
|
|
802
|
+
formula = _lower_identity(formula, "to_thf_modal_full")
|
|
803
|
+
|
|
804
|
+
used = _families_used(formula)
|
|
805
|
+
systems = systems or {}
|
|
806
|
+
|
|
807
|
+
lines: List[str] = [
|
|
808
|
+
f"% Full-family shallow embedding of a quantified modal formula "
|
|
809
|
+
f"(mode={mode}, frame={frame}).",
|
|
810
|
+
"% Families: alethic r, epistemic rk(agent), doxastic rb(agent), "
|
|
811
|
+
"deontic d(serial), temporal t.",
|
|
812
|
+
"% Conjecture is 'Theorem' iff the formula is valid under this regime "
|
|
813
|
+
"(external HOL ATP required).",
|
|
814
|
+
"thf(mu_type, type, ( mu : $tType )).",
|
|
815
|
+
]
|
|
816
|
+
|
|
817
|
+
# --- relation type declarations ---
|
|
818
|
+
# The lifted-operator definitions block (_THF_DEFS_FULL) is emitted WHOLE and its
|
|
819
|
+
# macros (mbox/mdia, mknows/mbelieves, mobl/mperm, mnext) reference every relation
|
|
820
|
+
# r/rk/rb/d/tnext. So all of them must be declared unconditionally — otherwise a
|
|
821
|
+
# Box-only formula would still emit mknows etc. referencing an undeclared rk, and a
|
|
822
|
+
# strict THF parser (Leo-III/Satallax) would reject the problem. Unused declared
|
|
823
|
+
# symbols are harmless.
|
|
824
|
+
lines.append("thf(r_decl, type, ( r : ( mu > mu > $o ) )).")
|
|
825
|
+
lines.append("thf(rk_decl, type, ( rk : ( $i > mu > mu > $o ) )).")
|
|
826
|
+
lines.append("thf(rb_decl, type, ( rb : ( $i > mu > mu > $o ) )).")
|
|
827
|
+
lines.append("thf(d_decl, type, ( d : ( mu > mu > $o ) )).")
|
|
828
|
+
lines.append("thf(t_decl, type, ( t : ( mu > mu > $o ) )).")
|
|
829
|
+
lines.append("thf(tnext_decl, type, ( tnext : ( mu > mu > $o ) )).")
|
|
830
|
+
lines.append("thf(rs_decl, type, ( rs : ( $i > mu > mu > $o ) )).")
|
|
831
|
+
lines.append("thf(rw_decl, type, ( rw : ( $i > mu > mu > $o ) )).")
|
|
832
|
+
lines.append("thf(existsAt_decl, type, ( existsAt : ( $i > mu > $o ) )).")
|
|
833
|
+
|
|
834
|
+
# --- signature of the object-level predicates / constants / functions ---
|
|
835
|
+
# (named agents fall out here as ordinary $i constants, matching rk/rb's $i slot).
|
|
836
|
+
# The de-colliding resolver guarantees distinct source symbols never share a functor
|
|
837
|
+
# (e.g. Ab / ab) and never shadow a built-in (a predicate named "r"/"mbox" is pushed
|
|
838
|
+
# to r_2/mbox_2), so a non-valid formula can neither collapse to a tautology nor
|
|
839
|
+
# re-declare an export-internal relation at a conflicting type.
|
|
840
|
+
names = _resolve_names(formula, original)
|
|
841
|
+
# One mu constant per nominal (reserved nom_ prefix; true at exactly the
|
|
842
|
+
# world it names — matching standard_translation / isabelle_modal). The map
|
|
843
|
+
# is deduped, so nominals 'A'/'a' get distinct constants (nom_a / nom_a_2).
|
|
844
|
+
for raw in _nominal_names(formula):
|
|
845
|
+
f = names.nominal[raw]
|
|
846
|
+
lines.append(f"thf({f}_decl, type, ( {f} : mu )).")
|
|
847
|
+
lines += _thf_signature(formula, names)
|
|
848
|
+
|
|
849
|
+
# --- the lifted-operator definitions (one self-contained block) ---
|
|
850
|
+
lines.append(_THF_DEFS_FULL)
|
|
851
|
+
if _has_identity(formula):
|
|
852
|
+
lines.append(_THF_RIGID_EQ_DEF)
|
|
853
|
+
|
|
854
|
+
# --- standing axiom: every world has an existing individual ---
|
|
855
|
+
lines.append(
|
|
856
|
+
"thf(nonempty_dom, axiom, ( ! [W: mu] : ? [X: $i] : ( existsAt @ X @ W ) )).")
|
|
857
|
+
|
|
858
|
+
# --- standing axiom per SORT: every sort is non-empty at every world ---
|
|
859
|
+
# _relativize left each sort guard an ordinary WORLD-RELATIVE predicate, so
|
|
860
|
+
# without this nothing forces S to hold of anything and ∀x:S P(x) → ∃x:S P(x)
|
|
861
|
+
# would be unprovable here while the classical many-sorted routes call it
|
|
862
|
+
# valid (fol._msfl_nodes.nonempty_sort_axioms states the same convention
|
|
863
|
+
# there, fol.qml.qml_axioms the same per-world version, and
|
|
864
|
+
# hol.isabelle_modal._nonempty_sort_axioms the same lines for the sibling
|
|
865
|
+
# route). Under an actualist mode the witness must also EXIST at the world,
|
|
866
|
+
# or it could not instantiate the existsAt-guarded mexists.
|
|
867
|
+
for i, sort in enumerate(_sort_functors(original, names)):
|
|
868
|
+
body = (f"( ( existsAt @ X @ W ) & ( {sort} @ X @ W ) )"
|
|
869
|
+
if mode in _ACTUALIST_MODES else f"( {sort} @ X @ W )")
|
|
870
|
+
lines.append(
|
|
871
|
+
f"thf(nonempty_sort{i}, axiom, ( ! [W: mu] : ? [X: $i] : {body} )).")
|
|
872
|
+
|
|
873
|
+
# --- standing axiom per SORTED CONSTANT: it is an element of its sort ---
|
|
874
|
+
# ``c:S`` denotes an element of ``S`` (the reading every many-sorted route of the
|
|
875
|
+
# kit shares, fol._msfl_nodes.sort_membership_axioms). A constant is a rigid
|
|
876
|
+
# designator, so ``S @ c @ W`` holds at EVERY world -- and it is NOT guarded by
|
|
877
|
+
# ``existsAt``, in any mode: a constant may lie outside the local domain (the
|
|
878
|
+
# reading fol.qml documents), and a guarded fact would never fire in the modes
|
|
879
|
+
# where that matters. As an AXIOM it is part of the problem, not of the
|
|
880
|
+
# conjecture, so it can only be used, never proved. The names are distinct from
|
|
881
|
+
# the ``nonempty_sort`` family above.
|
|
882
|
+
for i, (sort, const) in enumerate(_sort_members(original, names)):
|
|
883
|
+
lines.append(
|
|
884
|
+
f"thf(sort_member{i}, axiom, ( ! [W: mu] : ( {sort} @ {const} @ W ) )).")
|
|
885
|
+
|
|
886
|
+
# --- alethic frame axioms (frame) ---
|
|
887
|
+
for cond in _FRAMES[frame]:
|
|
888
|
+
lines.append(_THF_FRAME[cond])
|
|
889
|
+
|
|
890
|
+
# --- agent-indexed epistemic/doxastic/assertive/bouletic system axioms ---
|
|
891
|
+
for fam, rel, tag in _AGENT_SYSTEM_FAMILIES:
|
|
892
|
+
if not used[fam]:
|
|
893
|
+
continue
|
|
894
|
+
sys = systems.get(fam)
|
|
895
|
+
if sys is None:
|
|
896
|
+
continue
|
|
897
|
+
if sys not in _FRAMES:
|
|
898
|
+
raise ValueError(
|
|
899
|
+
f"to_thf_modal_full: unknown system {sys!r} for {fam} "
|
|
900
|
+
f"(use one of {sorted(_FRAMES)}).")
|
|
901
|
+
for ax in _agent_frame_axioms(rel, _FRAMES[sys], tag):
|
|
902
|
+
lines.append(ax)
|
|
903
|
+
|
|
904
|
+
# --- deontic seriality (Standard Deontic Logic, KD) ---
|
|
905
|
+
if used["deontic"]:
|
|
906
|
+
lines.append(_THF_DEONTIC_AXIOM)
|
|
907
|
+
|
|
908
|
+
# --- temporal frame axioms: refl/trans for G/F (see caveat) plus the tnext ⊆ t
|
|
909
|
+
# inclusion linking X's one-step relation to G/F's henceforth relation. Emitted
|
|
910
|
+
# whenever EITHER the temporal (G/F) or next (X) family occurs, since the linking
|
|
911
|
+
# axiom is exactly what makes a mixed formula such as Gφ→Xφ — valid for
|
|
912
|
+
# satisfies_modal — a theorem of the embedding. (t and tnext are declared
|
|
913
|
+
# unconditionally, so the axiom is always well-formed.)
|
|
914
|
+
if used["temporal"] or used["tnext"]:
|
|
915
|
+
lines += _THF_TEMPORAL_AXIOMS if temporal_closure else _THF_TEMPORAL_AXIOMS[-1:]
|
|
916
|
+
|
|
917
|
+
# --- opt-in cross-family bridge axioms (raises when a partner family is absent) ---
|
|
918
|
+
lines += _thf_bridge_axioms(used, bridges)
|
|
919
|
+
|
|
920
|
+
# --- object-quantifier domain regime ---
|
|
921
|
+
if mode in _THF_DOMAIN:
|
|
922
|
+
lines.append(_THF_DOMAIN[mode])
|
|
923
|
+
elif mode not in ("varying",) and mode not in _CONSTANT_MODES:
|
|
924
|
+
raise ValueError(f"to_thf_modal_full: unknown mode {mode!r}.")
|
|
925
|
+
|
|
926
|
+
# --- the conjecture ---
|
|
927
|
+
# A free variable is a parameter (one unknown individual), bound in front of the whole
|
|
928
|
+
# conjecture exactly as fol.qml.to_thf_modal binds it: THF has no free variables, and
|
|
929
|
+
# for a conjecture that stands alone "for the parameter" and "for every individual"
|
|
930
|
+
# are one validity. Under an actualist regime the individual exists at the world of
|
|
931
|
+
# evaluation.
|
|
932
|
+
goal = _lift(formula, names)
|
|
933
|
+
parameters = free_parameter_names([formula])
|
|
934
|
+
if mode in _ACTUALIST_MODES:
|
|
935
|
+
for name in reversed(parameters):
|
|
936
|
+
goal = f"( mimplies @ ( existsAt @ {names.variable(name)} ) @ {goal} )"
|
|
937
|
+
conjecture = f"mvalid @ {goal}"
|
|
938
|
+
for name in reversed(parameters):
|
|
939
|
+
conjecture = f"! [{names.variable(name)}: $i] : ( {conjecture} )"
|
|
940
|
+
lines.append(f"thf(goal, conjecture, ( {conjecture} )).")
|
|
941
|
+
return "\n".join(lines) + "\n"
|