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,416 @@
|
|
|
1
|
+
"""Dependence logic -> existential second-order logic (Skolem/Henkin translation).
|
|
2
|
+
|
|
3
|
+
Väänänen's team semantics (:mod:`unicode_logic_kit.semantics.team`) evaluates a
|
|
4
|
+
dependence-logic SENTENCE by brute-force search over teams; this module instead
|
|
5
|
+
*translates* a sentence into an ordinary classical formula headed by existential
|
|
6
|
+
second-order (ESO) quantifiers, whose truth in a structure — checked by
|
|
7
|
+
:func:`unicode_logic_kit.semantics.secondorder.satisfies_so` / :func:`holds` — is
|
|
8
|
+
PROVABLY EQUIVALENT to the team-semantic truth of the original sentence, for the
|
|
9
|
+
fragment described below. This realises half of Väänänen's headline theorem that
|
|
10
|
+
dependence logic captures exactly ESO (the other half, ESO -> D, is not needed
|
|
11
|
+
here): every sentence of the *SKOLEM-NORMAL fragment* is equivalent to the
|
|
12
|
+
existence of a family of SKOLEM FUNCTIONS obeying the dependence/independence
|
|
13
|
+
constraints written into the sentence.
|
|
14
|
+
|
|
15
|
+
THE TRANSLATION (Skolemisation with restricted dependency)
|
|
16
|
+
------------------------------------------------------------
|
|
17
|
+
Each existential — plain ``∃u``, an independence-friendly ``∃u/{V}``, or a
|
|
18
|
+
plain ``∃u`` immediately guarded by a dependence atom ``=(t̄,u)`` — is replaced
|
|
19
|
+
by a fresh GRAPH PREDICATE ``F_u`` of arity ``|args| + 1`` (the last argument is
|
|
20
|
+
``u``'s value), existentially quantified at the very front of the formula,
|
|
21
|
+
together with two first-order axioms making ``F_u`` TOTAL and FUNCTIONAL in its
|
|
22
|
+
first ``|args|`` positions (so it really is the graph of a function
|
|
23
|
+
``args -> u``):
|
|
24
|
+
|
|
25
|
+
- **unguarded** ``∃u φ`` (no dependence atom, not slashed): ``args`` = every
|
|
26
|
+
``∀``-bound variable enclosing this ``∃`` (in order) — the ordinary
|
|
27
|
+
first-order Skolem function of the full universal prefix. (Any INTERVENING
|
|
28
|
+
``∃``-bound variable is safely omittable here: it is itself already a
|
|
29
|
+
function of those same ``∀``'s, so it carries no information an unrestricted
|
|
30
|
+
witness could not already reconstruct.)
|
|
31
|
+
- **slashed** ``∃u/{V} φ``: ``args`` = every variable BOUND SO FAR — ``∀`` or
|
|
32
|
+
``∃`` alike, in binding order — EXCEPT those named in the slash set ``V``.
|
|
33
|
+
Note this is *every enclosing binder*, not just the ``∀``'s: independence is
|
|
34
|
+
a constraint on the witness function's visible ARGUMENT NAMES, and a
|
|
35
|
+
variable not literally in ``V`` stays visible even if it happens to be a
|
|
36
|
+
deterministic function of one that is — which is exactly the SIGNALLING
|
|
37
|
+
phenomenon (Väänänen 2007 §4): ``∀x∃z(z=x ∧ ∃y/{x}(y=x))`` lets ``y``
|
|
38
|
+
recover ``x`` via ``z``, and IS true everywhere, unlike ``∀x∃y/{x}(y=x)``
|
|
39
|
+
with no such intervening ``z``.
|
|
40
|
+
- **dependence-atom-guarded** ``∃u(=(t̄,u) ∧ φ)`` (the dependence atom a
|
|
41
|
+
top-level conjunct in the IMMEDIATE body of the ``∃`` binding its dependent
|
|
42
|
+
variable ``u``, the canonical Väänänen normal form): ``args`` = exactly
|
|
43
|
+
``t̄`` — the atom's own determinant terms override the default.
|
|
44
|
+
|
|
45
|
+
Every occurrence is then rewritten in place, innermost, as the classical
|
|
46
|
+
``∃u(F_u(args, u) ∧ φ')`` (``φ'`` = the (recursively translated) rest of the
|
|
47
|
+
guard's body, or bare ``F_u(args,u)`` if the guard was the whole body) — an
|
|
48
|
+
ORDINARY first-order ``∃u``, not a team-semantic one. Every ``F_u`` and its two
|
|
49
|
+
axioms then float to the front: ``(χ ∧ ∃F ρ) ≡ ∃F(χ ∧ ρ)`` for ``F`` fresh is
|
|
50
|
+
an unconditional second-order equivalence, so nesting position never matters.
|
|
51
|
+
``∀`` is passed through unchanged; literals (atoms, negated atoms) are passed
|
|
52
|
+
through unchanged (they are already classical).
|
|
53
|
+
|
|
54
|
+
FAITHFULNESS — exactly what is (and is not) covered
|
|
55
|
+
------------------------------------------------------
|
|
56
|
+
1. **Plain FO ∃, in any position.** ``⊨ ∀x̄∃u ψ(x̄,u) ↔ ∃F(∀x̄ ψ(x̄,F(x̄)))`` is a
|
|
57
|
+
basic, unconditional second-order validity (given a witness for every
|
|
58
|
+
``x̄``, PICK one — a purely combinatorial choice on a finite domain, no
|
|
59
|
+
axiom of choice needed) — the ordinary Skolemisation identity. Dependence
|
|
60
|
+
logic makes this exact: Väänänen 2007, Prop. 3.9 (flatness), shows every
|
|
61
|
+
formula with NO dependence atom and NO slash is FLAT — team truth equals
|
|
62
|
+
ordinary classical truth pointwise — so classical Skolemisation of a
|
|
63
|
+
dependence/slash-FREE sentence, in ANY connective shape (``∧``/``∨`` mixed
|
|
64
|
+
with quantifiers arbitrarily — not just prenex), is faithful to its team
|
|
65
|
+
truth. This is why a *plain FOL* sentence translates to something classically
|
|
66
|
+
equivalent to itself (mod the vacuous Skolem layer if it has any ``∃`` at
|
|
67
|
+
all).
|
|
68
|
+
2. **A dependence-atom-guarded or slashed ∃, PROVIDED no ``∨`` is anywhere in
|
|
69
|
+
the sentence.** This is exactly the classical Hintikka–Sandu Skolem/game
|
|
70
|
+
semantics of independence-friendly logic, which Väänänen 2007 (Thm. 4.10 and
|
|
71
|
+
surrounding discussion) shows AGREES with the compositional team semantics
|
|
72
|
+
for a quantifier-and-conjunction prefix over a quantifier-free (dependence
|
|
73
|
+
atoms aside) matrix — the shape this module accepts (arbitrary ``∀``/``∃``/
|
|
74
|
+
``∃/{…}`` nesting through ``∧``, since ``∧`` commutes freely with an
|
|
75
|
+
existential's Skolem function exactly as in case 1).
|
|
76
|
+
3. **``∨`` (the split disjunction) is REJECTED outright whenever the sentence
|
|
77
|
+
contains ANY dependence atom or slashed existential**, even in a part of the
|
|
78
|
+
tree that looks "far away" from the ``∨``. Team-semantic ``∨`` lets the team
|
|
79
|
+
split into ANY partition, and once a non-flat construct (dependence atom,
|
|
80
|
+
slash) is in play this split is exactly the source of dependence logic's
|
|
81
|
+
extra expressive power (Väänänen 2007 §4) — establishing a Skolem-normal-form
|
|
82
|
+
equivalence for ``∨`` mixed with non-flat constructs needs a case analysis
|
|
83
|
+
this module does not attempt. This is the CONSERVATIVE choice the module
|
|
84
|
+
commits to: soundness over coverage. (``∨`` remains fully supported, in any
|
|
85
|
+
position, for a dependence/slash-FREE sentence — case 1 above.)
|
|
86
|
+
4. **A "loose" dependence atom** — one that is not a top-level conjunct of the
|
|
87
|
+
IMMEDIATE body of the ``∃`` binding its own dependent variable (e.g. it
|
|
88
|
+
guards a ``∀``-bound variable, sits several ``∧``-levels away from its own
|
|
89
|
+
binder, or names a variable other than the enclosing ``∃``'s) — is rejected:
|
|
90
|
+
the guard extraction only ever looks at an ``∃``'s own immediate (flattened)
|
|
91
|
+
conjunct list, so any dependence atom not consumed there surfaces, unchanged,
|
|
92
|
+
to the generic recursion, which raises.
|
|
93
|
+
5. **Shadowing** (the same variable name bound twice along one ancestor chain,
|
|
94
|
+
e.g. ``∀x∃x…``) is rejected: the translation reuses each bound name as-is
|
|
95
|
+
(no fresh-renaming pass), which is sound only when names are not reused
|
|
96
|
+
along a single scope chain. Reuse across SIBLING branches (``∃x φ ∧ ∃x ψ``)
|
|
97
|
+
is unaffected — it is not shadowing.
|
|
98
|
+
6. **``¬``** is accepted only in front of an atom (mirroring
|
|
99
|
+
:mod:`unicode_logic_kit.semantics.team`'s own restriction); ``→``/``↔`` are
|
|
100
|
+
not part of the team fragment at all and are rejected immediately (as any
|
|
101
|
+
node outside the recognised shapes is).
|
|
102
|
+
|
|
103
|
+
The INPUT must already be a SENTENCE (no free object variables) — the same
|
|
104
|
+
assumption :func:`unicode_logic_kit.semantics.team.team_models` makes for
|
|
105
|
+
starting from the singleton empty-assignment team.
|
|
106
|
+
|
|
107
|
+
Every rejection raises :class:`NotImplementedError` naming the offending shape
|
|
108
|
+
and pointing back at ``team_satisfies`` / ``team_models`` as the fallback
|
|
109
|
+
(brute-force, but total over the whole team fragment).
|
|
110
|
+
|
|
111
|
+
Public API: :func:`dependence_to_eso`, :func:`dependence_holds_eso`.
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
from typing import FrozenSet, List, Optional, Tuple
|
|
115
|
+
|
|
116
|
+
from ..fol.nodes import (
|
|
117
|
+
Node, Variable, Atom, Not, And, Or, Implies, Quantifier,
|
|
118
|
+
Dependence, SlashedExists, SecondOrderQuantifier,
|
|
119
|
+
)
|
|
120
|
+
from .tarski import Structure
|
|
121
|
+
|
|
122
|
+
_FORALL = ("∀", "forall")
|
|
123
|
+
_EXISTS = ("∃", "exists")
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
# ===========================================================================
|
|
127
|
+
# And-tree flattening (to locate a guard adjacent to its own ∃, regardless of
|
|
128
|
+
# the parser's left-associative nesting) and rebuilding.
|
|
129
|
+
# ===========================================================================
|
|
130
|
+
|
|
131
|
+
def _flatten_and(node: Node) -> List[Node]:
|
|
132
|
+
"""Return the list of top-level conjuncts of an (arbitrarily nested) And-tree."""
|
|
133
|
+
if isinstance(node, And):
|
|
134
|
+
return _flatten_and(node.left) + _flatten_and(node.right)
|
|
135
|
+
return [node]
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def _rebuild_and(conjuncts: List[Node]) -> Node:
|
|
139
|
+
"""Left-fold a non-empty conjunct list back into a binary And-tree."""
|
|
140
|
+
result = conjuncts[0]
|
|
141
|
+
for c in conjuncts[1:]:
|
|
142
|
+
result = And(result, c)
|
|
143
|
+
return result
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def _extract_guard(body: Node, var: str) -> Tuple[Optional[Dependence], Optional[Node]]:
|
|
147
|
+
"""Find a dependence atom ``=(t̄,var)`` among ``body``'s top-level conjuncts.
|
|
148
|
+
|
|
149
|
+
Returns ``(guard, rest)`` where ``rest`` is the And of the remaining
|
|
150
|
+
conjuncts (``None`` if the guard was the ONLY conjunct), or ``(None, None)``
|
|
151
|
+
if no conjunct is a dependence atom whose dependent (last) argument is
|
|
152
|
+
exactly the variable ``var``. Only the FIRST matching conjunct is used.
|
|
153
|
+
"""
|
|
154
|
+
conjuncts = _flatten_and(body)
|
|
155
|
+
for i, c in enumerate(conjuncts):
|
|
156
|
+
if (isinstance(c, Dependence) and isinstance(c.args[-1], Variable)
|
|
157
|
+
and c.args[-1].name == var):
|
|
158
|
+
rest_conjuncts = conjuncts[:i] + conjuncts[i + 1:]
|
|
159
|
+
rest = _rebuild_and(rest_conjuncts) if rest_conjuncts else None
|
|
160
|
+
return c, rest
|
|
161
|
+
return None, None
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
# ===========================================================================
|
|
165
|
+
# Totality / functionality axiom for a Skolem graph predicate.
|
|
166
|
+
# ===========================================================================
|
|
167
|
+
|
|
168
|
+
def _forall_chain(vs: List[Variable], body: Node) -> Node:
|
|
169
|
+
"""Fold ``∀v1 ∀v2 … body`` over ``vs`` (``body`` unchanged if ``vs`` is empty)."""
|
|
170
|
+
result = body
|
|
171
|
+
for v in reversed(vs):
|
|
172
|
+
result = Quantifier("∀", v, result)
|
|
173
|
+
return result
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _wf_axiom(fname: str, arity: int) -> Node:
|
|
177
|
+
"""``F`` is TOTAL and FUNCTIONAL in its first ``arity`` arguments.
|
|
178
|
+
|
|
179
|
+
Totality: ``∀x̄ ∃y F(x̄,y)`` (a bare ``∃y F(y)`` when ``arity == 0``, i.e.
|
|
180
|
+
``F`` is non-empty). Functionality: ``∀x̄∀y1∀y2((F(x̄,y1)∧F(x̄,y2))→y1=y2)``.
|
|
181
|
+
Together these make ``F``'s graph exactly the graph of a total function
|
|
182
|
+
``dom^arity -> dom`` (a single constant when ``arity == 0``).
|
|
183
|
+
"""
|
|
184
|
+
xs = [Variable(f"_{fname}_x{i}") for i in range(arity)]
|
|
185
|
+
y0, y1, y2 = Variable(f"_{fname}_y0"), Variable(f"_{fname}_y1"), Variable(f"_{fname}_y2")
|
|
186
|
+
totality = _forall_chain(xs, Quantifier("∃", y0, Atom(fname, tuple(xs) + (y0,))))
|
|
187
|
+
functionality = _forall_chain(
|
|
188
|
+
xs + [y1, y2],
|
|
189
|
+
Implies(
|
|
190
|
+
And(Atom(fname, tuple(xs) + (y1,)), Atom(fname, tuple(xs) + (y2,))),
|
|
191
|
+
Atom("=", (y1, y2)),
|
|
192
|
+
),
|
|
193
|
+
)
|
|
194
|
+
return And(totality, functionality)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
# ===========================================================================
|
|
198
|
+
# The translation
|
|
199
|
+
# ===========================================================================
|
|
200
|
+
|
|
201
|
+
def _contains_any(node: Node, classes) -> bool:
|
|
202
|
+
"""Whether any descendant of ``node`` (``node`` included) is an instance of ``classes``."""
|
|
203
|
+
return any(isinstance(n, classes) for n in node.walk())
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def _fragment_error(node: Node) -> NotImplementedError:
|
|
207
|
+
"""The generic rejection for a node/shape outside the covered fragment."""
|
|
208
|
+
return NotImplementedError(
|
|
209
|
+
f"dependence_to_eso: {type(node).__name__} is outside the fragment this "
|
|
210
|
+
"translation is proven faithful for (literals, ∧, split ∨ in dependence/"
|
|
211
|
+
"slash-FREE sub-formulas, ∀/∃, a dependence atom that is a top-level "
|
|
212
|
+
"conjunct in the immediate body of the ∃ binding its own dependent "
|
|
213
|
+
"variable, and slashed ∃x/{…}); evaluate with "
|
|
214
|
+
"unicode_logic_kit.semantics.team.team_satisfies / team_models instead."
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
def dependence_to_eso(sentence: Node) -> Node:
|
|
219
|
+
"""Translate a dependence-logic SENTENCE to an equivalent ESO formula.
|
|
220
|
+
|
|
221
|
+
Returns a classical :class:`~unicode_logic_kit.fol.nodes.Node`, headed by one
|
|
222
|
+
``∃``-:class:`SecondOrderQuantifier` per Skolemised existential in
|
|
223
|
+
``sentence`` (zero if ``sentence`` has no existential at all — then the
|
|
224
|
+
result is just the classically-translated formula, unwrapped). The result
|
|
225
|
+
is meant for :func:`unicode_logic_kit.semantics.secondorder.satisfies_so` /
|
|
226
|
+
:func:`holds`, or for :func:`unicode_logic_kit.hol.secondorder.to_isabelle_so`
|
|
227
|
+
/ :func:`to_thf_so`. See the module docstring for the exact supported
|
|
228
|
+
grammar and the faithfulness argument.
|
|
229
|
+
|
|
230
|
+
Raises:
|
|
231
|
+
NotImplementedError: for any node/shape outside the covered fragment —
|
|
232
|
+
``∨`` combined (anywhere) with a dependence atom or slashed
|
|
233
|
+
existential, a dependence atom not guarding its own ``∃``, ``¬`` of
|
|
234
|
+
a non-atom, a shadowed (re-bound) variable name, or any node type
|
|
235
|
+
the team fragment itself does not recognise (``→``, ``↔``, …).
|
|
236
|
+
"""
|
|
237
|
+
if _contains_any(sentence, (Or,)) and _contains_any(sentence, (Dependence, SlashedExists)):
|
|
238
|
+
raise NotImplementedError(
|
|
239
|
+
"dependence_to_eso: the split disjunction ∨ is combined with a "
|
|
240
|
+
"dependence atom or slashed existential somewhere in this sentence. "
|
|
241
|
+
"∨ lets a team split into ANY partition, and once a non-flat "
|
|
242
|
+
"construct (dependence atom / slash) is present that split is "
|
|
243
|
+
"exactly the source of dependence logic's extra expressive power "
|
|
244
|
+
"(Väänänen 2007 §4) — no Skolem-normal-form equivalence is "
|
|
245
|
+
"established here for that combination, so it is rejected rather "
|
|
246
|
+
"than silently mistranslated. ∨ IS supported anywhere in a "
|
|
247
|
+
"dependence/slash-FREE (plain first-order) sentence. Evaluate the "
|
|
248
|
+
"full sentence with team_satisfies / team_models instead."
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
used_preds = {n.predicate for n in sentence.walk() if isinstance(n, Atom)}
|
|
252
|
+
skolems: List[Tuple[str, int]] = []
|
|
253
|
+
wf_axioms: List[Node] = []
|
|
254
|
+
|
|
255
|
+
def fresh_pred(base: str) -> str:
|
|
256
|
+
name, candidate, i = f"F_{base}", f"F_{base}", 0
|
|
257
|
+
while candidate in used_preds:
|
|
258
|
+
candidate = f"{name}_{i}"
|
|
259
|
+
i += 1
|
|
260
|
+
used_preds.add(candidate)
|
|
261
|
+
return candidate
|
|
262
|
+
|
|
263
|
+
def introduce_skolem(var: Variable, args: Tuple[Node, ...]) -> Atom:
|
|
264
|
+
"""Register a fresh Skolem predicate for ``var`` over ``args`` and return its atom."""
|
|
265
|
+
fname = fresh_pred(var.name)
|
|
266
|
+
arity = len(args)
|
|
267
|
+
skolems.append((fname, arity + 1))
|
|
268
|
+
wf_axioms.append(_wf_axiom(fname, arity))
|
|
269
|
+
return Atom(fname, args + (var,))
|
|
270
|
+
|
|
271
|
+
def translate(node: Node, foralls: Tuple[str, ...], scope: Tuple[str, ...],
|
|
272
|
+
bound: FrozenSet[str]) -> Node:
|
|
273
|
+
if isinstance(node, Atom):
|
|
274
|
+
return node
|
|
275
|
+
|
|
276
|
+
if isinstance(node, Not):
|
|
277
|
+
if not isinstance(node.formula, Atom):
|
|
278
|
+
raise NotImplementedError(
|
|
279
|
+
f"dependence_to_eso: ¬ applies only to atoms in the team "
|
|
280
|
+
f"fragment (found ¬ of a {type(node.formula).__name__}); "
|
|
281
|
+
"evaluate with team_satisfies / team_models instead."
|
|
282
|
+
)
|
|
283
|
+
return node
|
|
284
|
+
|
|
285
|
+
if isinstance(node, Dependence):
|
|
286
|
+
raise NotImplementedError(
|
|
287
|
+
f"dependence_to_eso: the dependence atom {node.to_unicode_str()!r} "
|
|
288
|
+
"does not guard its own existential. The supported canonical "
|
|
289
|
+
"pattern is ∃u(=(t̄,u) ∧ …), where the dependence atom is a "
|
|
290
|
+
"TOP-LEVEL conjunct in the IMMEDIATE body of the ∃ binding its "
|
|
291
|
+
"own dependent variable u; evaluate with team_satisfies / "
|
|
292
|
+
"team_models instead."
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
if isinstance(node, And):
|
|
296
|
+
return And(translate(node.left, foralls, scope, bound),
|
|
297
|
+
translate(node.right, foralls, scope, bound))
|
|
298
|
+
|
|
299
|
+
if isinstance(node, Or):
|
|
300
|
+
# Reachable only for a dependence/slash-FREE sentence (checked up
|
|
301
|
+
# front), where ∨ is fully flat and safe in any position.
|
|
302
|
+
return Or(translate(node.left, foralls, scope, bound),
|
|
303
|
+
translate(node.right, foralls, scope, bound))
|
|
304
|
+
|
|
305
|
+
if isinstance(node, Quantifier):
|
|
306
|
+
name = node.variable.name
|
|
307
|
+
if name in bound:
|
|
308
|
+
raise NotImplementedError(
|
|
309
|
+
f"dependence_to_eso: variable {name!r} is bound twice along "
|
|
310
|
+
"one scope chain (shadowing); the translation reuses each "
|
|
311
|
+
"bound name as-is, which requires distinct names along any "
|
|
312
|
+
"single nesting path. Rename the inner binder."
|
|
313
|
+
)
|
|
314
|
+
new_bound = bound | {name}
|
|
315
|
+
new_scope = scope + (name,)
|
|
316
|
+
|
|
317
|
+
if node.type in _FORALL:
|
|
318
|
+
return Quantifier("∀", node.variable,
|
|
319
|
+
translate(node.formula, foralls + (name,), new_scope, new_bound))
|
|
320
|
+
|
|
321
|
+
if node.type not in _EXISTS:
|
|
322
|
+
raise ValueError(f"dependence_to_eso: unknown quantifier type {node.type!r}.")
|
|
323
|
+
|
|
324
|
+
guard, rest = _extract_guard(node.formula, name)
|
|
325
|
+
if guard is not None:
|
|
326
|
+
args = tuple(guard.args[:-1])
|
|
327
|
+
else:
|
|
328
|
+
args = tuple(Variable(n) for n in foralls)
|
|
329
|
+
rest = node.formula
|
|
330
|
+
graph_atom = introduce_skolem(node.variable, args)
|
|
331
|
+
body = (graph_atom if rest is None
|
|
332
|
+
else And(graph_atom, translate(rest, foralls, new_scope, new_bound)))
|
|
333
|
+
return Quantifier("∃", node.variable, body)
|
|
334
|
+
|
|
335
|
+
if isinstance(node, SlashedExists):
|
|
336
|
+
name = node.variable.name
|
|
337
|
+
if name in bound:
|
|
338
|
+
raise NotImplementedError(
|
|
339
|
+
f"dependence_to_eso: variable {name!r} is bound twice along "
|
|
340
|
+
"one scope chain (shadowing); the translation reuses each "
|
|
341
|
+
"bound name as-is, which requires distinct names along any "
|
|
342
|
+
"single nesting path. Rename the inner binder."
|
|
343
|
+
)
|
|
344
|
+
new_bound = bound | {name}
|
|
345
|
+
new_scope = scope + (name,)
|
|
346
|
+
# Uniformity ("independent of V") is a constraint on the WITNESS
|
|
347
|
+
# FUNCTION'S visible ARGUMENTS, checked by NAME against every
|
|
348
|
+
# variable bound so far (∀ or ∃) — not just the enclosing ∀'s. An
|
|
349
|
+
# intervening ∃ that happens to be a deterministic function of a
|
|
350
|
+
# slashed ∀ (e.g. ∃z(z=x ∧ ∃y/{x} …)) is NOT itself named in the
|
|
351
|
+
# slash set, so it stays visible and the "independent" witness can
|
|
352
|
+
# still depend on it — the signalling phenomenon (Väänänen 2007
|
|
353
|
+
# §4; Hodges' compositional semantics for IF logic). Using `scope`
|
|
354
|
+
# (every bound name so far) rather than `foralls` here is what
|
|
355
|
+
# makes this case translate faithfully.
|
|
356
|
+
args = tuple(Variable(n) for n in scope if n not in node.slashed)
|
|
357
|
+
graph_atom = introduce_skolem(node.variable, args)
|
|
358
|
+
body = And(graph_atom, translate(node.formula, foralls, new_scope, new_bound))
|
|
359
|
+
return Quantifier("∃", node.variable, body)
|
|
360
|
+
|
|
361
|
+
raise _fragment_error(node)
|
|
362
|
+
|
|
363
|
+
core = translate(sentence, foralls=(), scope=(), bound=frozenset())
|
|
364
|
+
combined = _rebuild_and(list(wf_axioms) + [core])
|
|
365
|
+
result = combined
|
|
366
|
+
for fname, arity in skolems:
|
|
367
|
+
result = SecondOrderQuantifier("∃", fname, arity, result)
|
|
368
|
+
return result
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def dependence_holds_eso(sentence: Node, structure: Structure, fast: bool = False) -> bool:
|
|
372
|
+
"""Translate ``sentence`` via :func:`dependence_to_eso` and check it in ``structure``.
|
|
373
|
+
|
|
374
|
+
The "checking side" this module's own faithfulness argument (and its test
|
|
375
|
+
suite, ``holds(dependence_to_eso(sentence), structure)``) always pairs the
|
|
376
|
+
translation with: a convenience for that exact two-step pattern, plus an
|
|
377
|
+
opt-in fast path (roadmap C24).
|
|
378
|
+
|
|
379
|
+
``dependence_to_eso``'s output is always a SINGLE, leading, all-``∃``
|
|
380
|
+
block of ``SecondOrderQuantifier`` nodes wrapping a classical core (see
|
|
381
|
+
the module docstring: one fresh ``∃F_u`` per Skolemised existential,
|
|
382
|
+
nested front-to-back, never alternating with a ``∀``) — always within
|
|
383
|
+
:func:`~unicode_logic_kit.semantics.asp_models.asp_holds_so`'s single-block
|
|
384
|
+
fragment, so ``fast=True`` here never raises for that reason (it can
|
|
385
|
+
still raise for a symbol :func:`~unicode_logic_kit.semantics.asp_models.asp_holds_so`
|
|
386
|
+
needs that ``structure`` does not interpret, or a missing ``clingo``).
|
|
387
|
+
|
|
388
|
+
Args:
|
|
389
|
+
sentence: a dependence-logic sentence in :func:`dependence_to_eso`'s
|
|
390
|
+
supported fragment.
|
|
391
|
+
structure: the structure to check the translation against.
|
|
392
|
+
fast: opt-in (default ``False``, matching this module's own tests'
|
|
393
|
+
existing ``holds(dependence_to_eso(sentence), structure)`` call
|
|
394
|
+
exactly); ``True`` checks via
|
|
395
|
+
:func:`~unicode_logic_kit.semantics.asp_models.asp_holds_so`
|
|
396
|
+
instead of the brute-force
|
|
397
|
+
:func:`~unicode_logic_kit.semantics.secondorder.holds`.
|
|
398
|
+
|
|
399
|
+
Returns:
|
|
400
|
+
Whether ``structure`` satisfies the ESO translation of ``sentence``
|
|
401
|
+
(provably equivalent to ``sentence``'s own team-semantic truth, for
|
|
402
|
+
the fragment :func:`dependence_to_eso` covers — see the module
|
|
403
|
+
docstring's "FAITHFULNESS" section).
|
|
404
|
+
|
|
405
|
+
Raises:
|
|
406
|
+
NotImplementedError: from :func:`dependence_to_eso`, for ``sentence``
|
|
407
|
+
outside its fragment.
|
|
408
|
+
ImportError: ``fast=True`` and ``clingo`` (the optional ``asp``
|
|
409
|
+
extra) is not installed.
|
|
410
|
+
"""
|
|
411
|
+
translated = dependence_to_eso(sentence)
|
|
412
|
+
if fast:
|
|
413
|
+
from .asp_models import asp_holds_so
|
|
414
|
+
return asp_holds_so(translated, structure)
|
|
415
|
+
from .secondorder import holds
|
|
416
|
+
return holds(translated, structure)
|