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,812 @@
|
|
|
1
|
+
"""Classical FOL / MSFOL → HOL exporters: TPTP **THF** problems and Isabelle/HOL theories.
|
|
2
|
+
|
|
3
|
+
A classical first-order formula embeds into higher-order logic *trivially*: the
|
|
4
|
+
first-order fragment (TPTP ``fof``) is a syntactic subset of typed higher-order
|
|
5
|
+
logic (TPTP ``thf``), and likewise sits inside Isabelle/HOL over uninterpreted
|
|
6
|
+
constants and predicates. This module turns a toolkit :class:`~unicode_logic_kit.fol.nodes.Node`
|
|
7
|
+
into:
|
|
8
|
+
|
|
9
|
+
* a complete, self-contained TPTP **THF** problem — a single individual type, a
|
|
10
|
+
typed declaration for every predicate / function / constant, and the formula as
|
|
11
|
+
the ``conjecture`` (:func:`to_thf_fol`); and
|
|
12
|
+
* a loadable **Isabelle/HOL** theory — the same uninterpreted signature declared
|
|
13
|
+
with ``consts`` and the formula as a real ``lemma`` to be discharged by an
|
|
14
|
+
external prover / Sledgehammer (:func:`to_isabelle_fol`).
|
|
15
|
+
|
|
16
|
+
**MSFOL (many-sorted FOL).** Two routes map a many-sorted formula into single-sorted
|
|
17
|
+
HOL. The *typed* route would give each sort its own HOL type; the *guard-relativization*
|
|
18
|
+
route — the one implemented here for robustness — turns each sort into a unary guard
|
|
19
|
+
predicate and relativizes the quantifiers (``∀x:S φ ↦ ∀x. S(x) → φ``, ``∃x:S φ ↦
|
|
20
|
+
∃x. S(x) ∧ φ``). That reduction is exactly the toolkit's
|
|
21
|
+
:func:`~unicode_logic_kit.fol.nodes.to_fol`, so the MSFOL exporters reuse it and then
|
|
22
|
+
emit the resulting plain-FOL formula. That reduction forgets two facts the many-sorted
|
|
23
|
+
reading carries — no sort is empty, and a sorted constant ``c:S`` is an element of ``S``
|
|
24
|
+
(:func:`~unicode_logic_kit.fol.nodes.sort_axioms`) — so the MSFOL exporters state them
|
|
25
|
+
(``include_sort_facts=True``, the default). They are stated OUTSIDE the conjecture: a
|
|
26
|
+
THF ``axiom`` line per fact, Isabelle hypotheses ``⟦…⟧ ⟹ φ`` of the lemma, a hypothesis
|
|
27
|
+
of the Lean theorem. A fact conjoined to the goal itself (``Human(socrates) ∧ φ``) could
|
|
28
|
+
never be proved, even for a tautology ``φ``, because the guard is an uninterpreted
|
|
29
|
+
predicate. Only a formula that is itself ASSERTED (``conjecture=False``) keeps the
|
|
30
|
+
membership facts as a conjunct — there they are part of what is asserted — and gets the
|
|
31
|
+
non-emptiness facts as separate ``axiom`` lines. ``include_sort_facts=False`` is the bare
|
|
32
|
+
relativisation, with no sort facts at all.
|
|
33
|
+
|
|
34
|
+
**Equality.** By default, to stay consistent with the rest of the toolkit's HOL layer
|
|
35
|
+
(``qml.to_thf_modal``), ``=`` / ``≠`` are emitted as *uninterpreted* binary predicates
|
|
36
|
+
(``feq`` / ``fneq``), **not** primitive HOL identity. Pass ``native_equality=True`` to
|
|
37
|
+
:func:`to_thf_fol` / :func:`to_isabelle_fol` (and the MSFOL wrappers) to emit ``=`` /
|
|
38
|
+
``≠`` as the target logic's own built-in identity instead — TPTP THF's native infix
|
|
39
|
+
``=`` / ``!=`` and Isabelle/HOL's polymorphic ``=`` / ``\\<noteq>`` are both genuine,
|
|
40
|
+
axiom-free HOL identity at every type (including the uninterpreted individual type
|
|
41
|
+
``$i`` / ``i``), so this is a *rendering* choice, not new machinery: no congruence or
|
|
42
|
+
reflexivity axiom is generated or needed, because the target format's own ``=``
|
|
43
|
+
already validates them. The comparison predicates ``<`` ``>`` ``≤`` ``≥`` are
|
|
44
|
+
unaffected by this flag and always stay uninterpreted (``flt``/``fgt``/``fle``/``fge``)
|
|
45
|
+
— they have no built-in counterpart in either target. The default remains ``False``
|
|
46
|
+
(uninterpreted ``feq``/``fneq``) so existing output is unchanged; if you want genuine
|
|
47
|
+
HOL identity without the flag, post-process the output or add the congruence/identity
|
|
48
|
+
axioms yourself.
|
|
49
|
+
|
|
50
|
+
**Honesty / scope.** This module only *emits* problems and theories; it does **not**
|
|
51
|
+
run Leo-III, Satallax, Vampire, Isabelle, or Sledgehammer. Classical first-order
|
|
52
|
+
logic is **semi-decidable only** (validity is recursively enumerable; invalidity is
|
|
53
|
+
not), and the HOL embedding inherits that — a prover may confirm a valid conjecture
|
|
54
|
+
but is not guaranteed to terminate on an invalid one. No claim of decision is made:
|
|
55
|
+
:func:`to_thf_fol` / :func:`to_isabelle_fol` produce a *sound* problem an external
|
|
56
|
+
prover may discharge, nothing more.
|
|
57
|
+
|
|
58
|
+
Public API: :func:`to_thf_fol`, :func:`to_isabelle_fol`, :func:`to_thf_msfol`,
|
|
59
|
+
:func:`to_isabelle_msfol`.
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
from typing import Dict, List, Sequence, Tuple
|
|
63
|
+
|
|
64
|
+
from ..fol._fol_nodes import constant_name_to_ascii
|
|
65
|
+
from ..fol._numeral_symbols import numerals_as_constants, prefixed_numeral_name
|
|
66
|
+
from ..fol._symbol_names import dedupe
|
|
67
|
+
from ..fol._truth_constants import truth_value
|
|
68
|
+
from ..fol._msfl_nodes import _reduce_nl_nodes
|
|
69
|
+
from ..fol.nodes import (
|
|
70
|
+
Node, Variable, Constant, Number, Function, Measure,
|
|
71
|
+
Atom, Not, And, Or, Xor, Implies, Iff, Quantifier,
|
|
72
|
+
SortedQuantifier, to_fol, sort_axioms, nonempty_sort_axioms,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Equality / inequality are uninterpreted binary predicates in this toolkit's HOL
|
|
76
|
+
# layer (NOT primitive HOL identity), matching qml.to_thf_modal. These aliases give
|
|
77
|
+
# them valid, distinct functor / constant names in THF and Isabelle.
|
|
78
|
+
_PRED_ALIAS = {"=": "feq", "≠": "fneq", "<": "flt", ">": "fgt", "≤": "fle", "≥": "fge"}
|
|
79
|
+
|
|
80
|
+
# The two symbols native_equality=True renders as the target logic's own built-in
|
|
81
|
+
# identity (binary use only — a same-named symbol at any OTHER arity is left as an
|
|
82
|
+
# ordinary uninterpreted predicate via _PRED_ALIAS, same as when the flag is off).
|
|
83
|
+
_NATIVE_EQ_NAMES = {"=", "≠"}
|
|
84
|
+
|
|
85
|
+
_FORALL = "∀"
|
|
86
|
+
_EXISTS = "∃"
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def _is_native_eq(name: str, arity: int, native_equality: bool) -> bool:
|
|
90
|
+
"""True if ``(name, arity)`` is rendered as the target's built-in identity.
|
|
91
|
+
|
|
92
|
+
Only ``=``/``≠`` at their natural binary arity qualify — this bypasses the
|
|
93
|
+
``feq``/``fneq`` alias (no declaration, no functor application) in favour of
|
|
94
|
+
THF's native infix ``=``/``!=`` or Isabelle's polymorphic ``=``/``\\<noteq>``.
|
|
95
|
+
"""
|
|
96
|
+
return native_equality and name in _NATIVE_EQ_NAMES and arity == 2
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# ===========================================================================
|
|
100
|
+
# Shared: name sanitisation and signature extraction
|
|
101
|
+
# ===========================================================================
|
|
102
|
+
|
|
103
|
+
def _sanitize(name: str) -> str:
|
|
104
|
+
"""Lower-case-initial ASCII alphanumeric/underscore functor stem for THF/Isabelle.
|
|
105
|
+
|
|
106
|
+
Equality/comparison glyphs map through ``_PRED_ALIAS``. Every other name is
|
|
107
|
+
FIRST transliterated to ASCII with :func:`~unicode_logic_kit.fol._fol_nodes
|
|
108
|
+
.constant_name_to_ascii` — the same transliteration ``Constant.to_tptp`` /
|
|
109
|
+
``to_prover9`` already use (ASCII passthrough; a Greek letter maps to its
|
|
110
|
+
conventional name; any other non-ASCII character becomes a reversible
|
|
111
|
+
``uXXXX`` codepoint escape) — and only THEN run through the legacy
|
|
112
|
+
alnum-or-underscore filter. Since ``constant_name_to_ascii`` is the identity
|
|
113
|
+
on a string that is already pure ASCII, this changes nothing for a name that
|
|
114
|
+
was already legal: the underscore/digit-prefix behaviour below is exactly
|
|
115
|
+
what it was before. What it fixes is that ``str.isalnum()`` is ``True`` for
|
|
116
|
+
almost every Unicode letter (``ś``, ``中``, …), so the OLD filter alone let
|
|
117
|
+
those straight through as "harmless" — THF/Isabelle are ASCII-only formats,
|
|
118
|
+
so that was silent corruption, not sanitisation. A leading digit (which can
|
|
119
|
+
now also arise from a transliterated escape, though those always start with
|
|
120
|
+
the ASCII letter ``u``) is still prefixed with ``p`` so the result is a
|
|
121
|
+
legal lower identifier, and so is a leading underscore: the constant
|
|
122
|
+
``'_sk0'``, the constant ``'-3'`` and the function ``+`` would otherwise be
|
|
123
|
+
written ``_sk0``, ``_3`` and ``_``, which neither THF nor Isabelle reads as
|
|
124
|
+
an identifier.
|
|
125
|
+
|
|
126
|
+
NOTE: this is *not* injective — neither the old filter (``'Ab'``/``'ab'``
|
|
127
|
+
collide) nor ``constant_name_to_ascii`` (a literal ``'theta'`` and the Greek
|
|
128
|
+
``'θ'`` both fold to ``'theta'``) guarantee that. De-collision is the job of
|
|
129
|
+
:class:`_SymbolResolver` (predicates/functions/constants) and
|
|
130
|
+
:class:`_VarResolver` (variables), which route every declaration AND usage
|
|
131
|
+
through :func:`~unicode_logic_kit.fol._symbol_names.dedupe` to a unique, valid
|
|
132
|
+
identifier per symbol. Do not emit ``_sanitize`` output directly for a
|
|
133
|
+
declaration or a usage.
|
|
134
|
+
"""
|
|
135
|
+
if name in _PRED_ALIAS:
|
|
136
|
+
return _PRED_ALIAS[name]
|
|
137
|
+
ascii_name = constant_name_to_ascii(name)
|
|
138
|
+
safe = "".join(c if (c.isalnum() or c == "_") else "_" for c in ascii_name)
|
|
139
|
+
if not safe:
|
|
140
|
+
return "p"
|
|
141
|
+
if safe[0].isdigit() or safe[0] == "_":
|
|
142
|
+
safe = "p" + safe
|
|
143
|
+
return safe[:1].lower() + safe[1:]
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
# Backwards-compatible alias (older call sites / tests may reference ``_safe_name``).
|
|
147
|
+
_safe_name = _sanitize
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _signature(formula: Node):
|
|
151
|
+
"""Collect (predicates, functions, constants) with arities from ``formula``.
|
|
152
|
+
|
|
153
|
+
Returns ``(preds, funcs, consts)`` where ``preds`` and ``funcs`` are sets of
|
|
154
|
+
``(name, arity)`` pairs — a predicate or function used at *two* arities yields
|
|
155
|
+
*two* distinct entries (each a distinct emitted symbol) — and ``consts`` is a
|
|
156
|
+
set of nullary individual names (plain constants plus numeric literals, the
|
|
157
|
+
latter rendered ``n<value>``). Equality / comparison atoms contribute their
|
|
158
|
+
glyph as an ordinary predicate.
|
|
159
|
+
"""
|
|
160
|
+
preds, funcs, consts = set(), set(), set()
|
|
161
|
+
for n in formula.walk():
|
|
162
|
+
if isinstance(n, Atom):
|
|
163
|
+
if truth_value(n) is None: # `$true` / `$false` are written as THF's / HOL's own
|
|
164
|
+
preds.add((n.predicate, len(n.args)))
|
|
165
|
+
elif isinstance(n, Function):
|
|
166
|
+
funcs.add((n.name, len(n.args)))
|
|
167
|
+
elif isinstance(n, Measure):
|
|
168
|
+
# μ(entity, dimension) is the binary function ``measure`` — the same
|
|
169
|
+
# symbol Measure.to_z3 / to_prover9 / to_tptp emit.
|
|
170
|
+
funcs.add(("measure", 2))
|
|
171
|
+
elif isinstance(n, Constant):
|
|
172
|
+
consts.add(n.name)
|
|
173
|
+
elif isinstance(n, Number):
|
|
174
|
+
consts.add("n" + str(n.value))
|
|
175
|
+
return preds, funcs, consts
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
# ===========================================================================
|
|
179
|
+
# Global symbol resolver — DISTINCT source symbols map to DISTINCT identifiers
|
|
180
|
+
# ===========================================================================
|
|
181
|
+
|
|
182
|
+
# Category tags. Every emitted symbol is keyed by (category, raw_name, arity);
|
|
183
|
+
# predicates and functions carry their arity so the SAME raw name used at two
|
|
184
|
+
# arities becomes two distinct, distinctly-named emitted symbols.
|
|
185
|
+
_CAT_PRED = "pred"
|
|
186
|
+
_CAT_FUNC = "func"
|
|
187
|
+
_CAT_CONST = "const"
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class _SymbolResolver:
|
|
191
|
+
"""Assign a UNIQUE, valid emitted identifier to every source symbol.
|
|
192
|
+
|
|
193
|
+
Keyed by ``(category, raw_name, arity)``. Distinct source symbols — whether
|
|
194
|
+
they differ by category (a predicate vs. a function vs. a constant sharing a
|
|
195
|
+
raw name), by raw name, or by arity (a predicate used at two arities) — are
|
|
196
|
+
guaranteed distinct emitted identifiers. The ``_PRED_ALIAS`` targets
|
|
197
|
+
(``feq``/``fneq``/…) are reserved up front so a user symbol named ``feq``
|
|
198
|
+
cannot collide with the ``=`` alias.
|
|
199
|
+
|
|
200
|
+
Both *declarations* and *usages* must go through :meth:`name`, so a symbol is
|
|
201
|
+
declared and referenced under exactly the same (de-collided) identifier.
|
|
202
|
+
|
|
203
|
+
``native_equality=True`` makes this resolver skip reserving ``feq``/``fneq``
|
|
204
|
+
for the binary ``=``/``≠`` entries (they render as native identity and need no
|
|
205
|
+
identifier at all), which also frees those stems for a user symbol literally
|
|
206
|
+
named ``feq``/``fneq`` — there is no longer an alias target to collide with.
|
|
207
|
+
"""
|
|
208
|
+
|
|
209
|
+
def __init__(self, formula: Node, native_equality: bool = False):
|
|
210
|
+
self._used = set()
|
|
211
|
+
self._map: Dict[Tuple[str, str, int], str] = {}
|
|
212
|
+
preds, funcs, consts = _signature(formula)
|
|
213
|
+
# Assign the equality/comparison alias predicates (=, ≠, …) FIRST so they
|
|
214
|
+
# claim their natural alias names (feq, fneq, …); any user symbol that
|
|
215
|
+
# would sanitise to the same stem is then de-collided away from them. This
|
|
216
|
+
# makes the alias targets collision-proof while keeping = -> feq when there
|
|
217
|
+
# is no competing user 'feq'. Skip this for a (name, arity) that
|
|
218
|
+
# native_equality renders natively — it needs no alias/identifier.
|
|
219
|
+
for name, arity in sorted(preds):
|
|
220
|
+
if name in _PRED_ALIAS and not _is_native_eq(name, arity, native_equality):
|
|
221
|
+
self._assign(_CAT_PRED, name, arity)
|
|
222
|
+
# Deterministic assignment order so emitted problems are stable.
|
|
223
|
+
for name, arity in sorted(preds):
|
|
224
|
+
if name not in _PRED_ALIAS:
|
|
225
|
+
self._assign(_CAT_PRED, name, arity)
|
|
226
|
+
for name, arity in sorted(funcs):
|
|
227
|
+
self._assign(_CAT_FUNC, name, arity)
|
|
228
|
+
for name in sorted(consts):
|
|
229
|
+
self._assign(_CAT_CONST, name, 0)
|
|
230
|
+
|
|
231
|
+
def _assign(self, category: str, raw: str, arity: int) -> str:
|
|
232
|
+
key = (category, raw, arity)
|
|
233
|
+
if key in self._map:
|
|
234
|
+
return self._map[key]
|
|
235
|
+
cand = dedupe(_sanitize(raw), self._used)
|
|
236
|
+
self._map[key] = cand
|
|
237
|
+
return cand
|
|
238
|
+
|
|
239
|
+
def name(self, category: str, raw: str, arity: int) -> str:
|
|
240
|
+
"""Return the unique emitted identifier for ``(category, raw, arity)``.
|
|
241
|
+
|
|
242
|
+
Assigns one on demand (covers any symbol not pre-seeded, e.g. an alias
|
|
243
|
+
target) so declarations and usages always agree.
|
|
244
|
+
"""
|
|
245
|
+
key = (category, raw, arity)
|
|
246
|
+
if key not in self._map:
|
|
247
|
+
return self._assign(category, raw, arity)
|
|
248
|
+
return self._map[key]
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
class _VarResolver:
|
|
252
|
+
"""Map DISTINCT bound/free variable names to DISTINCT emitted variable tokens.
|
|
253
|
+
|
|
254
|
+
THF upper-cases variable tokens, so distinct source names that differ only in
|
|
255
|
+
case (``x`` vs. ``X``) would otherwise collide on one token. De-collide by
|
|
256
|
+
suffixing while preserving the natural token for the first claimant.
|
|
257
|
+
|
|
258
|
+
``used`` is the set of tokens a variable must not take. THF needs none (a variable is
|
|
259
|
+
upper-case and a functor lower-case, so the two never meet). Isabelle needs the
|
|
260
|
+
functor tokens: its binder ``\\<forall> x.`` shadows a constant ``x`` in its own
|
|
261
|
+
scope, so a variable spelled like a constant of the theory would capture it. The
|
|
262
|
+
set is shared, not copied: a symbol that the functor resolver names later is kept
|
|
263
|
+
off the variables' tokens as well.
|
|
264
|
+
"""
|
|
265
|
+
|
|
266
|
+
def __init__(self, render, used=None):
|
|
267
|
+
self._render = render # raw_name -> base token
|
|
268
|
+
self._used = set() if used is None else used
|
|
269
|
+
self._map: Dict[str, str] = {}
|
|
270
|
+
|
|
271
|
+
def token(self, raw: str) -> str:
|
|
272
|
+
if raw in self._map:
|
|
273
|
+
return self._map[raw]
|
|
274
|
+
cand = dedupe(self._render(raw), self._used)
|
|
275
|
+
self._map[raw] = cand
|
|
276
|
+
return cand
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def _free_variables(formula: Node) -> List[str]:
|
|
280
|
+
"""Return the names of variables occurring free in ``formula`` (first-occurrence order).
|
|
281
|
+
|
|
282
|
+
Quantifier / SortedQuantifier bind their variable; everything else is structural.
|
|
283
|
+
"""
|
|
284
|
+
out: List[str] = []
|
|
285
|
+
seen = set()
|
|
286
|
+
|
|
287
|
+
def rec(node: Node, bound: frozenset):
|
|
288
|
+
if isinstance(node, Variable):
|
|
289
|
+
if node.name not in bound and node.name not in seen:
|
|
290
|
+
seen.add(node.name)
|
|
291
|
+
out.append(node.name)
|
|
292
|
+
return
|
|
293
|
+
if isinstance(node, (Quantifier, SortedQuantifier)):
|
|
294
|
+
rec(node.formula, bound | {node.variable.name})
|
|
295
|
+
return
|
|
296
|
+
for child in node._child_nodes():
|
|
297
|
+
rec(child, bound)
|
|
298
|
+
|
|
299
|
+
rec(formula, frozenset())
|
|
300
|
+
return out
|
|
301
|
+
|
|
302
|
+
|
|
303
|
+
# ===========================================================================
|
|
304
|
+
# (A) TPTP THF export
|
|
305
|
+
# ===========================================================================
|
|
306
|
+
|
|
307
|
+
def _thf_term(node: Node, syms: "_SymbolResolver", vars_: "_VarResolver") -> str:
|
|
308
|
+
"""Render an individual (``$i``) term in THF applicative (``@``) form."""
|
|
309
|
+
if isinstance(node, Variable):
|
|
310
|
+
return vars_.token(node.name)
|
|
311
|
+
if isinstance(node, Constant):
|
|
312
|
+
return syms.name(_CAT_CONST, node.name, 0)
|
|
313
|
+
if isinstance(node, Number):
|
|
314
|
+
return syms.name(_CAT_CONST, "n" + str(node.value), 0)
|
|
315
|
+
if isinstance(node, Function):
|
|
316
|
+
head = syms.name(_CAT_FUNC, node.name, len(node.args))
|
|
317
|
+
return "( " + " @ ".join([head] + [_thf_term(a, syms, vars_) for a in node.args]) + " )"
|
|
318
|
+
if isinstance(node, Measure):
|
|
319
|
+
head = syms.name(_CAT_FUNC, "measure", 2)
|
|
320
|
+
return ("( " + " @ ".join([head, _thf_term(node.entity, syms, vars_),
|
|
321
|
+
_thf_term(node.dimension, syms, vars_)]) + " )")
|
|
322
|
+
raise NotImplementedError(
|
|
323
|
+
f"to_thf_fol: unsupported term {type(node).__name__} (classical FOL terms only)."
|
|
324
|
+
)
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def _thf_formula(node: Node, syms: "_SymbolResolver", vars_: "_VarResolver",
|
|
328
|
+
native_equality: bool = False) -> str:
|
|
329
|
+
"""Render a classical FOL formula as a THF ``$o`` term.
|
|
330
|
+
|
|
331
|
+
Connectives use the THF/FOF operators (``~ & | => <=> <~>``); quantifiers bind
|
|
332
|
+
individual variables ``! [X: $i]`` / ``? [X: $i]``; atoms apply their declared
|
|
333
|
+
predicate constant with ``@``. Every predicate / function / constant / variable
|
|
334
|
+
name is routed through the resolvers so distinct source symbols stay distinct.
|
|
335
|
+
Anything outside the classical fragment (modal, second-order, Łukasiewicz,
|
|
336
|
+
lambda) raises ``NotImplementedError``.
|
|
337
|
+
|
|
338
|
+
With ``native_equality=True``, a binary ``=``/``≠`` atom renders as THF's own
|
|
339
|
+
infix ``( a = b )`` / ``( a != b )`` instead of applying the ``feq``/``fneq``
|
|
340
|
+
functor — genuine HOL identity, built in at every type, no declaration needed.
|
|
341
|
+
"""
|
|
342
|
+
def f(n):
|
|
343
|
+
return _thf_formula(n, syms, vars_, native_equality)
|
|
344
|
+
if isinstance(node, Atom):
|
|
345
|
+
constant = truth_value(node)
|
|
346
|
+
if constant is not None:
|
|
347
|
+
return "$true" if constant else "$false" # THF's own constants
|
|
348
|
+
if _is_native_eq(node.predicate, len(node.args), native_equality):
|
|
349
|
+
op = "=" if node.predicate == "=" else "!="
|
|
350
|
+
left = _thf_term(node.args[0], syms, vars_)
|
|
351
|
+
right = _thf_term(node.args[1], syms, vars_)
|
|
352
|
+
return f"( {left} {op} {right} )"
|
|
353
|
+
head = syms.name(_CAT_PRED, node.predicate, len(node.args))
|
|
354
|
+
if not node.args:
|
|
355
|
+
return head
|
|
356
|
+
return "( " + " @ ".join([head] + [_thf_term(a, syms, vars_) for a in node.args]) + " )"
|
|
357
|
+
if isinstance(node, Not):
|
|
358
|
+
return f"( ~ {f(node.formula)} )"
|
|
359
|
+
if isinstance(node, And):
|
|
360
|
+
return f"( {f(node.left)} & {f(node.right)} )"
|
|
361
|
+
if isinstance(node, Or):
|
|
362
|
+
return f"( {f(node.left)} | {f(node.right)} )"
|
|
363
|
+
if isinstance(node, Implies):
|
|
364
|
+
return f"( {f(node.left)} => {f(node.right)} )"
|
|
365
|
+
if isinstance(node, Iff):
|
|
366
|
+
return f"( {f(node.left)} <=> {f(node.right)} )"
|
|
367
|
+
if isinstance(node, Xor):
|
|
368
|
+
return f"( {f(node.left)} <~> {f(node.right)} )"
|
|
369
|
+
if isinstance(node, Quantifier):
|
|
370
|
+
x = vars_.token(node.variable.name)
|
|
371
|
+
binder = "!" if node.type in (_FORALL, "forall") else "?"
|
|
372
|
+
return f"( {binder} [{x}: $i] : {f(node.formula)} )"
|
|
373
|
+
raise NotImplementedError(
|
|
374
|
+
f"to_thf_fol: {type(node).__name__} is outside the classical FOL fragment "
|
|
375
|
+
"supported by the THF export (no modal / second-order / Łukasiewicz / "
|
|
376
|
+
"substructural / lambda). Modal family → hol.thf_modal.to_thf_modal_full; "
|
|
377
|
+
"second-order → hol.secondorder.to_thf_so; K3/LP → hol.manyvalued; "
|
|
378
|
+
"ILL/Lambek derivations → hol.isabelle_substructural."
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
|
|
382
|
+
def _thf_signature_decls(formula: Node, syms: "_SymbolResolver",
|
|
383
|
+
native_equality: bool = False) -> List[str]:
|
|
384
|
+
"""Type declarations for every predicate / function / constant in ``formula``.
|
|
385
|
+
|
|
386
|
+
Each declaration uses the resolver-assigned identifier, so a symbol is
|
|
387
|
+
declared under exactly the name its usages reference, and distinct symbols
|
|
388
|
+
(including a predicate at two arities) get distinct, single-typed decls.
|
|
389
|
+
A binary ``=``/``≠`` skips its declaration when ``native_equality=True`` —
|
|
390
|
+
THF's built-in identity is polymorphic and needs no type declaration.
|
|
391
|
+
"""
|
|
392
|
+
preds, funcs, consts = _signature(formula)
|
|
393
|
+
decls: List[str] = []
|
|
394
|
+
for name, arity in sorted(preds):
|
|
395
|
+
if _is_native_eq(name, arity, native_equality):
|
|
396
|
+
continue
|
|
397
|
+
ident = syms.name(_CAT_PRED, name, arity)
|
|
398
|
+
typ = " > ".join(["$i"] * arity + ["$o"]) if arity else "$o"
|
|
399
|
+
decls.append(f"thf({ident}_decl, type, ( {ident} : ( {typ} ) )).")
|
|
400
|
+
for name, arity in sorted(funcs):
|
|
401
|
+
ident = syms.name(_CAT_FUNC, name, arity)
|
|
402
|
+
typ = " > ".join(["$i"] * (arity + 1))
|
|
403
|
+
decls.append(f"thf({ident}_decl, type, ( {ident} : ( {typ} ) )).")
|
|
404
|
+
for name in sorted(consts):
|
|
405
|
+
ident = syms.name(_CAT_CONST, name, 0)
|
|
406
|
+
decls.append(f"thf({ident}_decl, type, ( {ident} : $i )).")
|
|
407
|
+
return decls
|
|
408
|
+
|
|
409
|
+
|
|
410
|
+
def to_thf_fol(formula: Node, conjecture: bool = True, native_equality: bool = False) -> str:
|
|
411
|
+
"""Emit a complete TPTP **THF** problem for a classical FOL ``formula``.
|
|
412
|
+
|
|
413
|
+
The problem declares the individual type ``$i`` implicitly (TPTP's built-in),
|
|
414
|
+
a typed constant for every predicate / function / constant in the signature,
|
|
415
|
+
and the formula itself. With ``conjecture=True`` (default) the formula is the
|
|
416
|
+
``conjecture`` — a higher-order ATP (Leo-III, Satallax) reports ``Theorem`` iff
|
|
417
|
+
the formula is valid; with ``conjecture=False`` it is emitted as an ``axiom``
|
|
418
|
+
(e.g. to assert it as a hypothesis in a larger problem).
|
|
419
|
+
|
|
420
|
+
A free variable of a CONJECTURE is closed universally before emission (TPTP formula
|
|
421
|
+
roles do not admit free variables). A free variable is a parameter of the problem, one
|
|
422
|
+
unknown individual, and for a single formula with no premise the two readings
|
|
423
|
+
coincide: the formula is valid for the parameter iff it is valid for every individual.
|
|
424
|
+
An ASSERTED formula (``conjecture=False``) is a premise of a larger problem, and there
|
|
425
|
+
the closure would say more than the formula says: ``∀x P(x)`` entails ``P(a)``, the
|
|
426
|
+
premise ``P(x)`` does not. So an asserted formula with a free variable is refused by
|
|
427
|
+
name; state the parameter with a constant, or bind the variable with a quantifier.
|
|
428
|
+
|
|
429
|
+
By default, equality ``=`` / ``≠`` becomes the uninterpreted predicate ``feq`` /
|
|
430
|
+
``fneq`` (see module docstring);
|
|
431
|
+
pass ``native_equality=True`` to instead emit THF's own built-in infix
|
|
432
|
+
``=`` / ``!=`` — genuine HOL identity, so congruence/substitutivity for every
|
|
433
|
+
declared function and predicate is free (no axioms to add). The comparison
|
|
434
|
+
predicates ``<`` ``>`` ``≤`` ``≥`` are unaffected either way.
|
|
435
|
+
|
|
436
|
+
Classical FOL is *semi-decidable only*: a prover may confirm a valid conjecture
|
|
437
|
+
but is not guaranteed to terminate otherwise. This function only emits the
|
|
438
|
+
problem; it does not run any prover.
|
|
439
|
+
|
|
440
|
+
Raises:
|
|
441
|
+
NotImplementedError: ``conjecture`` is false and ``formula`` has a free variable;
|
|
442
|
+
or a numeral and a constant are spelled alike.
|
|
443
|
+
"""
|
|
444
|
+
return _thf_problem(formula, conjecture, native_equality)
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
def _scope(formula: Node, background: Sequence[Node]) -> Node:
|
|
448
|
+
"""``formula`` and every background fact as one tree, for signature scans only.
|
|
449
|
+
|
|
450
|
+
A problem's declarations and symbol names must cover the sort facts it states
|
|
451
|
+
as well as its formula — a sort that occurs only through a sorted constant is
|
|
452
|
+
mentioned by the fact alone. With no background this is ``formula`` itself, so
|
|
453
|
+
a problem without sort facts is resolved and declared exactly as it was.
|
|
454
|
+
"""
|
|
455
|
+
scope = formula
|
|
456
|
+
for fact in background:
|
|
457
|
+
scope = And(scope, fact)
|
|
458
|
+
return scope
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
def _refuse_open_assertion(formula: Node, conjecture: bool, where: str) -> None:
|
|
462
|
+
"""Refuse an ASSERTED ``formula`` that has a free variable, by name.
|
|
463
|
+
|
|
464
|
+
A free variable is a parameter of the problem: one unknown individual, the same in
|
|
465
|
+
every formula. Closing it universally is the same thing for a conjecture that stands
|
|
466
|
+
alone (valid for the parameter iff valid for every individual), but an axiom is a
|
|
467
|
+
premise of a larger problem, and ``∀x P(x)`` says more than ``P(x)`` does. So the
|
|
468
|
+
writer states no closure for an axiom and tells the caller how to state the parameter.
|
|
469
|
+
``where`` is the name of the writer, which the refusal opens with.
|
|
470
|
+
|
|
471
|
+
Raises:
|
|
472
|
+
NotImplementedError: ``conjecture`` is false and ``formula`` has a free variable.
|
|
473
|
+
"""
|
|
474
|
+
if conjecture:
|
|
475
|
+
return
|
|
476
|
+
free = _free_variables(formula)
|
|
477
|
+
if free:
|
|
478
|
+
names = ", ".join(repr(name) for name in free)
|
|
479
|
+
noun = "variable" if len(free) == 1 else "variables"
|
|
480
|
+
raise NotImplementedError(
|
|
481
|
+
f"{where}: the asserted formula (conjecture=False) has the free {noun} "
|
|
482
|
+
f"{names}, and an axiom cannot say what a free variable stands for. Closing it "
|
|
483
|
+
"universally would assert more than the formula says (a free variable is a "
|
|
484
|
+
"PARAMETER of the problem, one unknown individual shared by every formula: "
|
|
485
|
+
"'∀x P(x)' entails 'P(a)', the premise 'P(x)' does not). State the parameter "
|
|
486
|
+
"yourself: replace the variable by a constant, or bind it with a quantifier, "
|
|
487
|
+
"or emit the formula as the conjecture (conjecture=True), where, for one "
|
|
488
|
+
"formula with no premise, the closure and the parameter reading coincide.")
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
def _thf_problem(formula: Node, conjecture: bool, native_equality: bool,
|
|
492
|
+
background: Sequence[Node] = (), where: str = "to_thf_fol") -> str:
|
|
493
|
+
"""The THF problem of ``formula``, with ``background`` as ``axiom`` lines before it.
|
|
494
|
+
|
|
495
|
+
``background`` are closed sentences (the many-sorted reading's sort facts, see
|
|
496
|
+
:func:`_msfol_split`). They are named ``nonempty_sort_<i>`` (an ``∃``) and
|
|
497
|
+
``sort_member_<i>`` (an atom) and come after the type declarations, so a prover
|
|
498
|
+
can use them but never has to prove them. ``where`` names the public writer that
|
|
499
|
+
is calling, for the refusal of an asserted formula that has a free variable.
|
|
500
|
+
"""
|
|
501
|
+
formula = _reduce_nl_nodes(formula) # Contrast → ∧, Count → witnesses
|
|
502
|
+
# A numeral is a constant identified by its value (1 and 1.0 are one), named ``n1``:
|
|
503
|
+
# a user constant spelled like it is refused, not merged with it.
|
|
504
|
+
[formula], _ = numerals_as_constants([formula], where="to_thf_fol",
|
|
505
|
+
spell=prefixed_numeral_name)
|
|
506
|
+
_refuse_open_assertion(formula, conjecture, where)
|
|
507
|
+
closed = formula
|
|
508
|
+
for name in reversed(_free_variables(formula)):
|
|
509
|
+
closed = Quantifier(_FORALL, Variable(name), closed)
|
|
510
|
+
role = "conjecture" if conjecture else "axiom"
|
|
511
|
+
scope = _scope(closed, background)
|
|
512
|
+
syms = _SymbolResolver(scope, native_equality=native_equality)
|
|
513
|
+
vars_ = _VarResolver(lambda raw: _sanitize(raw).upper())
|
|
514
|
+
lines = [
|
|
515
|
+
"% Classical FOL embedded into THF (first-order fragment of HOL).",
|
|
516
|
+
f"% The formula is emitted as the {role}; '$i' is the individual type.",
|
|
517
|
+
]
|
|
518
|
+
# Only the comment differs, and only when '=' / '≠' actually occur at their
|
|
519
|
+
# native binary arity — a formula without them emits byte-identical output
|
|
520
|
+
# whether native_equality is True or False (nothing about it would differ).
|
|
521
|
+
preds, _, _ = _signature(scope)
|
|
522
|
+
if any(_is_native_eq(n, a, native_equality) for n, a in preds):
|
|
523
|
+
lines.append("% '=' / '≠' are THF's native, built-in HOL identity (no axioms needed).")
|
|
524
|
+
else:
|
|
525
|
+
lines.append("% '=' / '≠' are uninterpreted predicates (feq / fneq), not HOL identity.")
|
|
526
|
+
if background:
|
|
527
|
+
lines.append("% The sort facts below are axioms of the problem, not conjuncts of the goal.")
|
|
528
|
+
lines += _thf_signature_decls(scope, syms, native_equality=native_equality)
|
|
529
|
+
nonempty = member = 0
|
|
530
|
+
for fact in background:
|
|
531
|
+
if isinstance(fact, Quantifier):
|
|
532
|
+
name, nonempty = f"nonempty_sort_{nonempty}", nonempty + 1
|
|
533
|
+
else:
|
|
534
|
+
name, member = f"sort_member_{member}", member + 1
|
|
535
|
+
lines.append(f"thf({name}, axiom, "
|
|
536
|
+
f"{_thf_formula(fact, syms, vars_, native_equality=native_equality)}).")
|
|
537
|
+
lines.append(f"thf(goal, {role}, "
|
|
538
|
+
f"{_thf_formula(closed, syms, vars_, native_equality=native_equality)}).")
|
|
539
|
+
return "\n".join(lines) + "\n"
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
def _msfol_split(formula: Node, conjecture: bool,
|
|
543
|
+
include_sort_facts: bool) -> Tuple[Node, Tuple[Node, ...]]:
|
|
544
|
+
"""Split a many-sorted ``formula`` into its plain-FOL image and the sort facts to state beside it.
|
|
545
|
+
|
|
546
|
+
The image is :func:`~unicode_logic_kit.fol.nodes.to_fol`: each sort a unary guard
|
|
547
|
+
predicate, each sorted quantifier relativized, each sorted constant its plain
|
|
548
|
+
name. What that forgets is stated separately, by
|
|
549
|
+
:func:`~unicode_logic_kit.fol.nodes.sort_axioms` — no sort is empty, and a sorted
|
|
550
|
+
constant ``c:S`` is an element of ``S``.
|
|
551
|
+
|
|
552
|
+
For a CONJECTURE both kinds are returned as background facts, never folded into
|
|
553
|
+
the formula: a conjunct ``S(c) ∧ φ`` could not be proved even for a tautology
|
|
554
|
+
``φ``. For an asserted formula (``conjecture=False``) the membership atoms are
|
|
555
|
+
part of what is asserted and stay the conjunct ``to_fol`` builds, and only the
|
|
556
|
+
non-emptiness facts are returned. ``include_sort_facts=False`` is the bare
|
|
557
|
+
relativisation, with no facts at all. A formula with no sorted node returns
|
|
558
|
+
``to_fol(formula)`` and ``()``.
|
|
559
|
+
"""
|
|
560
|
+
if not include_sort_facts:
|
|
561
|
+
return to_fol(formula), ()
|
|
562
|
+
if conjecture:
|
|
563
|
+
return to_fol(formula), tuple(sort_axioms(formula))
|
|
564
|
+
return to_fol(formula, include_sort_facts=True), tuple(nonempty_sort_axioms(formula))
|
|
565
|
+
|
|
566
|
+
|
|
567
|
+
def to_thf_msfol(formula: Node, conjecture: bool = True,
|
|
568
|
+
include_sort_facts: bool = True, native_equality: bool = False) -> str:
|
|
569
|
+
"""Emit a TPTP **THF** problem for a *many-sorted* FOL ``formula`` via guard relativization.
|
|
570
|
+
|
|
571
|
+
Each sort becomes a unary guard predicate and each sorted quantifier is
|
|
572
|
+
relativized (``∀x:S φ ↦ ∀x. S(x) → φ``, ``∃x:S φ ↦ ∃x. S(x) ∧ φ``) by the
|
|
573
|
+
toolkit's :func:`~unicode_logic_kit.fol.nodes.to_fol`; the resulting plain-FOL
|
|
574
|
+
formula is then emitted as :func:`to_thf_fol` does. With
|
|
575
|
+
``include_sort_facts=True`` (default) the two facts that reduction forgets are
|
|
576
|
+
stated too (:func:`~unicode_logic_kit.fol.nodes.sort_axioms`): every sort is
|
|
577
|
+
non-empty (``∃x S(x)``) and a sorted constant is in its sort
|
|
578
|
+
(``Mortal(socrates:Human)`` carries ``Human(socrates)``). For a ``conjecture``
|
|
579
|
+
each is a separate THF ``axiom`` (``nonempty_sort_<i>`` / ``sort_member_<i>``) —
|
|
580
|
+
a fact conjoined to the goal, ``Human(socrates) ∧ φ``, could never be proved,
|
|
581
|
+
even for a tautology ``φ`` — so the problem asks whether the formula follows from
|
|
582
|
+
them, which is the many-sorted question. An asserted formula
|
|
583
|
+
(``conjecture=False``) keeps the membership atoms as a conjunct, since they are
|
|
584
|
+
part of what is asserted, and gets the non-emptiness facts as ``axiom`` lines.
|
|
585
|
+
``include_sort_facts=False`` emits the bare relativisation, no sort facts. All
|
|
586
|
+
sorts share the single THF individual type ``$i`` — the relativization, not the
|
|
587
|
+
type system, keeps the sorts apart. See :func:`to_thf_fol` for ``native_equality``
|
|
588
|
+
and for the free variable of an asserted formula, which is refused by name.
|
|
589
|
+
|
|
590
|
+
Raises:
|
|
591
|
+
NotImplementedError: ``conjecture`` is false and ``formula`` has a free variable.
|
|
592
|
+
"""
|
|
593
|
+
plain, background = _msfol_split(formula, conjecture, include_sort_facts)
|
|
594
|
+
return _thf_problem(plain, conjecture, native_equality, background, where="to_thf_msfol")
|
|
595
|
+
|
|
596
|
+
|
|
597
|
+
# ===========================================================================
|
|
598
|
+
# (B) Isabelle/HOL export
|
|
599
|
+
# ===========================================================================
|
|
600
|
+
|
|
601
|
+
_ISA_BINOP = {And: "\\<and>", Or: "\\<or>", Implies: "\\<longrightarrow>",
|
|
602
|
+
Iff: "\\<longleftrightarrow>"}
|
|
603
|
+
|
|
604
|
+
|
|
605
|
+
def _isa_term(node: Node, syms: "_SymbolResolver", vars_: "_VarResolver") -> str:
|
|
606
|
+
"""Render an individual term in Isabelle/HOL term syntax (curried application)."""
|
|
607
|
+
if isinstance(node, Variable):
|
|
608
|
+
return vars_.token(node.name)
|
|
609
|
+
if isinstance(node, Constant):
|
|
610
|
+
return syms.name(_CAT_CONST, node.name, 0)
|
|
611
|
+
if isinstance(node, Number):
|
|
612
|
+
return syms.name(_CAT_CONST, "n" + str(node.value), 0)
|
|
613
|
+
if isinstance(node, Function):
|
|
614
|
+
head = syms.name(_CAT_FUNC, node.name, len(node.args))
|
|
615
|
+
return "(" + " ".join([head] + [_isa_term(a, syms, vars_) for a in node.args]) + ")"
|
|
616
|
+
if isinstance(node, Measure):
|
|
617
|
+
head = syms.name(_CAT_FUNC, "measure", 2)
|
|
618
|
+
return ("(" + " ".join([head, _isa_term(node.entity, syms, vars_),
|
|
619
|
+
_isa_term(node.dimension, syms, vars_)]) + ")")
|
|
620
|
+
raise NotImplementedError(
|
|
621
|
+
f"to_isabelle_fol: unsupported term {type(node).__name__} (classical FOL terms only)."
|
|
622
|
+
)
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
def _isa_formula(node: Node, syms: "_SymbolResolver", vars_: "_VarResolver",
|
|
626
|
+
native_equality: bool = False) -> str:
|
|
627
|
+
"""Render a classical FOL formula in Isabelle/HOL syntax (fully parenthesised).
|
|
628
|
+
|
|
629
|
+
Uses Isabelle's logical-symbol control sequences (``\\<not>``, ``\\<and>`` …)
|
|
630
|
+
and meta/object quantifiers ``\\<forall> x. …`` / ``\\<exists> x. …``. Atoms
|
|
631
|
+
apply their predicate by curried juxtaposition. Xor is rendered as the negated
|
|
632
|
+
biconditional ``\\<not>(l \\<longleftrightarrow> r)``. Every functor / variable
|
|
633
|
+
name is routed through the resolvers so distinct source symbols stay distinct.
|
|
634
|
+
|
|
635
|
+
With ``native_equality=True``, a binary ``=``/``≠`` atom renders as Isabelle's
|
|
636
|
+
own infix ``(a = b)`` / ``(a \\<noteq> b)`` instead of the ``feq``/``fneq``
|
|
637
|
+
consts — genuine, polymorphic HOL identity, no declaration needed.
|
|
638
|
+
"""
|
|
639
|
+
def g(n):
|
|
640
|
+
return _isa_formula(n, syms, vars_, native_equality)
|
|
641
|
+
if isinstance(node, Atom):
|
|
642
|
+
constant = truth_value(node)
|
|
643
|
+
if constant is not None:
|
|
644
|
+
return "True" if constant else "False" # HOL's own constants
|
|
645
|
+
if _is_native_eq(node.predicate, len(node.args), native_equality):
|
|
646
|
+
left = _isa_term(node.args[0], syms, vars_)
|
|
647
|
+
right = _isa_term(node.args[1], syms, vars_)
|
|
648
|
+
op = "=" if node.predicate == "=" else "\\<noteq>"
|
|
649
|
+
return f"({left} {op} {right})"
|
|
650
|
+
head = syms.name(_CAT_PRED, node.predicate, len(node.args))
|
|
651
|
+
if not node.args:
|
|
652
|
+
return head
|
|
653
|
+
return "(" + " ".join([head] + [_isa_term(a, syms, vars_) for a in node.args]) + ")"
|
|
654
|
+
if isinstance(node, Not):
|
|
655
|
+
return f"(\\<not> {g(node.formula)})"
|
|
656
|
+
if type(node) in _ISA_BINOP:
|
|
657
|
+
op = _ISA_BINOP[type(node)]
|
|
658
|
+
return f"({g(node.left)} {op} {g(node.right)})"
|
|
659
|
+
if isinstance(node, Xor):
|
|
660
|
+
inner = f"({g(node.left)} \\<longleftrightarrow> {g(node.right)})"
|
|
661
|
+
return f"(\\<not> {inner})"
|
|
662
|
+
if isinstance(node, Quantifier):
|
|
663
|
+
binder = "\\<forall>" if node.type in (_FORALL, "forall") else "\\<exists>"
|
|
664
|
+
return f"({binder} {vars_.token(node.variable.name)}. {g(node.formula)})"
|
|
665
|
+
raise NotImplementedError(
|
|
666
|
+
f"to_isabelle_fol: {type(node).__name__} is outside the classical FOL fragment "
|
|
667
|
+
"supported by the Isabelle export (no modal / second-order / Łukasiewicz / "
|
|
668
|
+
"substructural / lambda). Modal family → hol.isabelle_modal.to_isabelle_modal; "
|
|
669
|
+
"second-order → hol.secondorder.to_isabelle_so; K3/LP → hol.manyvalued; "
|
|
670
|
+
"ILL/Lambek derivations → hol.isabelle_substructural."
|
|
671
|
+
)
|
|
672
|
+
|
|
673
|
+
|
|
674
|
+
def _isa_consts_block(formula: Node, syms: "_SymbolResolver",
|
|
675
|
+
native_equality: bool = False) -> List[str]:
|
|
676
|
+
"""``consts`` declarations for every predicate / function / constant.
|
|
677
|
+
|
|
678
|
+
Individuals live in a single uninterpreted HOL type ``i``; a k-ary predicate
|
|
679
|
+
has type ``i ⇒ … ⇒ bool`` and a k-ary function ``i ⇒ … ⇒ i``. Each ``consts``
|
|
680
|
+
entry uses the resolver-assigned identifier, so no two declarations share a
|
|
681
|
+
name (no duplicate-``consts`` load error) and every name matches its usages.
|
|
682
|
+
A binary ``=``/``≠`` skips its declaration when ``native_equality=True`` —
|
|
683
|
+
Isabelle's polymorphic ``=`` is built in and needs no ``consts`` entry.
|
|
684
|
+
"""
|
|
685
|
+
preds, funcs, consts = _signature(formula)
|
|
686
|
+
lines: List[str] = []
|
|
687
|
+
arrow = " \\<Rightarrow> "
|
|
688
|
+
for name, arity in sorted(preds):
|
|
689
|
+
if _is_native_eq(name, arity, native_equality):
|
|
690
|
+
continue
|
|
691
|
+
ident = syms.name(_CAT_PRED, name, arity)
|
|
692
|
+
typ = arrow.join(["i"] * arity + ["bool"]) if arity else "bool"
|
|
693
|
+
lines.append(f"consts {ident} :: \"{typ}\"")
|
|
694
|
+
for name, arity in sorted(funcs):
|
|
695
|
+
ident = syms.name(_CAT_FUNC, name, arity)
|
|
696
|
+
typ = arrow.join(["i"] * (arity + 1))
|
|
697
|
+
lines.append(f"consts {ident} :: \"{typ}\"")
|
|
698
|
+
for name in sorted(consts):
|
|
699
|
+
ident = syms.name(_CAT_CONST, name, 0)
|
|
700
|
+
lines.append(f"consts {ident} :: \"i\"")
|
|
701
|
+
return lines
|
|
702
|
+
|
|
703
|
+
|
|
704
|
+
def to_isabelle_fol(formula: Node, theory_name: str = "FOL_Export",
|
|
705
|
+
lemma_name: str = "goal", proof: str = "oops",
|
|
706
|
+
native_equality: bool = False) -> str:
|
|
707
|
+
"""Emit a loadable **Isabelle/HOL** theory with ``formula`` as a real ``lemma``.
|
|
708
|
+
|
|
709
|
+
The theory declares a single uninterpreted individual type ``i`` and a
|
|
710
|
+
``consts`` entry for every predicate / function / constant in the signature,
|
|
711
|
+
then states the formula as ``lemma <lemma_name>: "⌜formula⌝"`` over those
|
|
712
|
+
uninterpreted symbols. Free variables are left as Isabelle schematic/free
|
|
713
|
+
term variables (HOL closes them implicitly at the lemma level). The lemma is a
|
|
714
|
+
goal, never an assertion, so a free variable is the parameter it is: one
|
|
715
|
+
individual, for which the lemma holds iff it holds for every individual (the
|
|
716
|
+
refusal that :func:`to_thf_fol` makes for an asserted formula has no counterpart
|
|
717
|
+
here).
|
|
718
|
+
|
|
719
|
+
The proof line defaults to ``oops`` (the lemma is *stated* but deliberately
|
|
720
|
+
left open, so the theory loads without claiming a proof). Pass
|
|
721
|
+
``proof="by auto"``, ``"sledgehammer"``, ``"by blast"``, … to attempt a
|
|
722
|
+
discharge — but note that classical FOL is semi-decidable only, so no tactic
|
|
723
|
+
is guaranteed to close every valid lemma, and this function does not run
|
|
724
|
+
Isabelle. By default, equality ``=`` / ``≠`` is the uninterpreted predicate
|
|
725
|
+
``feq`` / ``fneq`` (see module docstring), not HOL ``=``; pass
|
|
726
|
+
``native_equality=True`` to emit Isabelle's own polymorphic ``=`` / ``\\<noteq>``
|
|
727
|
+
instead — genuine HOL identity, so congruence closes by ``simp``/``auto`` with
|
|
728
|
+
no axioms added. The comparison predicates ``<`` ``>`` ``≤`` ``≥`` are
|
|
729
|
+
unaffected either way.
|
|
730
|
+
"""
|
|
731
|
+
return _isabelle_problem(formula, theory_name, lemma_name, proof, native_equality)
|
|
732
|
+
|
|
733
|
+
|
|
734
|
+
def _isabelle_problem(formula: Node, theory_name: str, lemma_name: str, proof: str,
|
|
735
|
+
native_equality: bool, background: Sequence[Node] = ()) -> str:
|
|
736
|
+
r"""The Isabelle theory of ``formula``, with ``background`` as the HYPOTHESES of the lemma.
|
|
737
|
+
|
|
738
|
+
``background`` are closed sentences (the many-sorted reading's sort facts, see
|
|
739
|
+
:func:`_msfol_split`). They are not ``axiomatization`` facts and not conjuncts of
|
|
740
|
+
the goal but premises of the lemma, ``\<lbrakk>f1; f2\<rbrakk> \<Longrightarrow> φ``: a premise is used by
|
|
741
|
+
``blast`` / ``auto`` / ``metis`` / ``nitpick`` without a ``using`` clause, which an
|
|
742
|
+
``axiomatization`` fact is not — and the proof battery of
|
|
743
|
+
:func:`~unicode_logic_kit.hol.isabelle_runner.isabelle_decide_fol` names no facts.
|
|
744
|
+
"""
|
|
745
|
+
formula = _reduce_nl_nodes(formula) # Contrast → ∧, Count → witnesses
|
|
746
|
+
# A numeral is a constant identified by its value (1 and 1.0 are one), named ``n1``:
|
|
747
|
+
# a user constant spelled like it is refused, not merged with it.
|
|
748
|
+
[formula], _ = numerals_as_constants([formula], where="to_isabelle_fol",
|
|
749
|
+
spell=prefixed_numeral_name)
|
|
750
|
+
scope = _scope(formula, background)
|
|
751
|
+
# Only the comment differs, and only when '=' / '≠' actually occur at their
|
|
752
|
+
# native binary arity — a formula without them emits byte-identical output
|
|
753
|
+
# whether native_equality is True or False (nothing about it would differ).
|
|
754
|
+
preds, _, _ = _signature(scope)
|
|
755
|
+
if any(_is_native_eq(n, a, native_equality) for n, a in preds):
|
|
756
|
+
eq_comment = " '=' / '≠' are Isabelle's own built-in HOL identity (no axioms needed)."
|
|
757
|
+
else:
|
|
758
|
+
eq_comment = " '=' / '≠' are the uninterpreted predicates feq / fneq, NOT HOL identity."
|
|
759
|
+
lines = [
|
|
760
|
+
f"theory {theory_name}",
|
|
761
|
+
" imports Main",
|
|
762
|
+
"begin",
|
|
763
|
+
"",
|
|
764
|
+
"(* Classical FOL embedded into Isabelle/HOL over an uninterpreted",
|
|
765
|
+
" individual type and uninterpreted predicates/functions/constants.",
|
|
766
|
+
eq_comment,
|
|
767
|
+
" FOL is semi-decidable only: no tactic closes every valid lemma. *)",
|
|
768
|
+
"",
|
|
769
|
+
"typedecl i \\<comment> \\<open>uninterpreted individuals\\<close>",
|
|
770
|
+
]
|
|
771
|
+
syms = _SymbolResolver(scope, native_equality=native_equality)
|
|
772
|
+
# A binder shadows the constants of its own name, so the bound variables take their
|
|
773
|
+
# tokens from the pool the constants, functions and predicates already took theirs from.
|
|
774
|
+
vars_ = _VarResolver(_sanitize, used=syms._used)
|
|
775
|
+
lines += _isa_consts_block(scope, syms, native_equality=native_equality)
|
|
776
|
+
lines.append("")
|
|
777
|
+
statement = _isa_formula(formula, syms, vars_, native_equality=native_equality)
|
|
778
|
+
if background:
|
|
779
|
+
facts = "; ".join(_isa_formula(fact, syms, vars_, native_equality=native_equality)
|
|
780
|
+
for fact in background)
|
|
781
|
+
statement = f"\\<lbrakk>{facts}\\<rbrakk> \\<Longrightarrow> {statement}"
|
|
782
|
+
lines.append(f"lemma {lemma_name}: \"{statement}\"")
|
|
783
|
+
lines.append(f" {proof}")
|
|
784
|
+
lines.append("")
|
|
785
|
+
lines.append("end")
|
|
786
|
+
return "\n".join(lines) + "\n"
|
|
787
|
+
|
|
788
|
+
|
|
789
|
+
def to_isabelle_msfol(formula: Node, theory_name: str = "MSFOL_Export",
|
|
790
|
+
lemma_name: str = "goal", proof: str = "oops",
|
|
791
|
+
include_sort_facts: bool = True, native_equality: bool = False) -> str:
|
|
792
|
+
"""Emit an **Isabelle/HOL** theory for a many-sorted formula via guard relativization.
|
|
793
|
+
|
|
794
|
+
Reduces ``formula`` with :func:`~unicode_logic_kit.fol.nodes.to_fol` (each sort
|
|
795
|
+
becomes a unary guard predicate over the single individual type ``i``, each
|
|
796
|
+
sorted quantifier is relativized) and emits the result as
|
|
797
|
+
:func:`to_isabelle_fol` does. With ``include_sort_facts=True`` (default) the two
|
|
798
|
+
facts that reduction forgets are stated too
|
|
799
|
+
(:func:`~unicode_logic_kit.fol.nodes.sort_axioms`): every sort is non-empty
|
|
800
|
+
(``∃x S(x)``) and a sorted constant is in its sort (``Human(socrates)`` for
|
|
801
|
+
``Mortal(socrates:Human)``). They are HYPOTHESES of the lemma, not conjuncts of
|
|
802
|
+
the goal — ``lemma goal: "⟦∃x. human x; human socrates⟧ ⟹ φ"`` — because a goal
|
|
803
|
+
``human socrates ∧ φ`` could never be proved, even for a tautology ``φ``, the
|
|
804
|
+
guard being an uninterpreted predicate; and a premise needs no ``using`` clause
|
|
805
|
+
for ``blast`` / ``auto`` / ``nitpick`` to use it. So the lemma asks whether the
|
|
806
|
+
formula follows from the sort facts, which is the many-sorted question.
|
|
807
|
+
``include_sort_facts=False`` is the bare relativisation, no sort facts. See
|
|
808
|
+
:func:`to_isabelle_fol` for ``native_equality``.
|
|
809
|
+
"""
|
|
810
|
+
plain, background = _msfol_split(formula, True, include_sort_facts)
|
|
811
|
+
return _isabelle_problem(plain, theory_name, lemma_name, proof, native_equality,
|
|
812
|
+
background)
|