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,323 @@
|
|
|
1
|
+
"""Shared machinery for the two third-order HOL exporters.
|
|
2
|
+
|
|
3
|
+
:mod:`unicode_logic_kit.hol.thirdorder` (classical) and
|
|
4
|
+
:mod:`unicode_logic_kit.hol.ho_modal` (the shallow modal embedding) differ in
|
|
5
|
+
exactly one thing: whether a proposition is a truth value or a function from
|
|
6
|
+
worlds to truth values. Everything BEFORE that decision is the same work —
|
|
7
|
+
renaming bound predicate variables apart so a theory-wide signature analysis
|
|
8
|
+
answers the right question, collecting the free symbols that need declaring, and
|
|
9
|
+
peeling λ-binders off a property argument — so it lives here rather than twice.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import re
|
|
13
|
+
from typing import Dict, Iterable, List, Sequence, Set, Tuple
|
|
14
|
+
|
|
15
|
+
from ..fol.nodes import (
|
|
16
|
+
Node, Atom, Quantifier, SecondOrderQuantifier, PredicateTerm,
|
|
17
|
+
Variable, Constant, Function, LambdaVar, Lambda,
|
|
18
|
+
)
|
|
19
|
+
from ..fol._symbol_names import dedupe
|
|
20
|
+
from ..fol._truth_constants import truth_value
|
|
21
|
+
from ..fol.qml import _thf_name
|
|
22
|
+
|
|
23
|
+
#: The two kinds of individual symbol a writer declares. A free variable is a PARAMETER of the
|
|
24
|
+
#: problem: one unknown element, the same in every formula (see
|
|
25
|
+
#: :mod:`unicode_logic_kit.fol._free_parameters`). A constant is a particular element that its
|
|
26
|
+
#: name stands for. ``P(x)`` with a free ``x`` and ``P('x')`` are two statements, so the two are
|
|
27
|
+
#: two symbols of the output even when they are spelled alike. The string ``"individual"`` is the
|
|
28
|
+
#: key the free variable has always had.
|
|
29
|
+
FREE_VARIABLE = "individual"
|
|
30
|
+
CONSTANT = "constant"
|
|
31
|
+
|
|
32
|
+
_LOWER_WORD = re.compile(r"[a-z][A-Za-z0-9_]*\Z")
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def functor_stem(name: str) -> str:
|
|
36
|
+
"""A TPTP lower word (``[a-z][A-Za-z0-9_]*``) that ``name`` is turned into; NOT injective.
|
|
37
|
+
|
|
38
|
+
This is the kit's THF stem (:func:`~unicode_logic_kit.fol.qml._thf_name`: ASCII, a Greek
|
|
39
|
+
letter by its name, any other non-ASCII character as a ``uXXXX`` escape, every other
|
|
40
|
+
character as ``_``, a ``p`` in front of a leading digit, a lower-case initial) with one
|
|
41
|
+
repair: a name that starts with an underscore (``_sk0``, ``-3``) would still not be a lower
|
|
42
|
+
word, so it gets a ``p`` in front as well. Two names can have one stem (``a b`` and ``a_b``);
|
|
43
|
+
:func:`~unicode_logic_kit.fol._symbol_names.dedupe` over one pool is what keeps the symbols
|
|
44
|
+
apart.
|
|
45
|
+
"""
|
|
46
|
+
stem = _thf_name(name)
|
|
47
|
+
return stem if _LOWER_WORD.match(stem) else "p" + stem
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class UnsupportedHigherOrderNode(NotImplementedError):
|
|
51
|
+
"""Raised for an AST node a third-order embedding has no reading for.
|
|
52
|
+
|
|
53
|
+
Subclasses NotImplementedError, matching the kit's other export refusals: a
|
|
54
|
+
route that cannot carry a construct says so by name instead of dropping it.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
#: The ordering predicates ``<`` ``>`` ``≤`` ``≥``. Neither target format has an
|
|
59
|
+
#: ordering on the uninterpreted individual type, so they are ordinary
|
|
60
|
+
#: uninterpreted relations (WORLD-relativised in the modal embedding, exactly as
|
|
61
|
+
#: :mod:`unicode_logic_kit.fol.qml` reads them) under the stems named here.
|
|
62
|
+
ORDERING = {"<": "flt", ">": "fgt", "≤": "fle", "≥": "fge"}
|
|
63
|
+
|
|
64
|
+
#: The aliases of the CLASSICAL third-order exporter
|
|
65
|
+
#: (:mod:`unicode_logic_kit.hol.thirdorder`), which reads ``=`` / ``≠`` as the
|
|
66
|
+
#: uninterpreted relations ``feq`` / ``fneq`` — not primitive HOL identity — next to
|
|
67
|
+
#: the :data:`ORDERING` predicates. The MODAL exporter
|
|
68
|
+
#: (:mod:`unicode_logic_kit.hol.ho_modal`) does NOT use the first two entries: there
|
|
69
|
+
#: object identity is RIGID (HOL's own ``=`` over the individual type, no world
|
|
70
|
+
#: argument, as in :func:`unicode_logic_kit.fol.qml.qml_is_valid`), so it takes only
|
|
71
|
+
#: :data:`ORDERING` and reads ``=`` / ``≠`` through
|
|
72
|
+
#: :func:`unicode_logic_kit.hol.isabelle_modal._lower_identity`.
|
|
73
|
+
EQUALITY = {"=": "feq", "≠": "fneq", **ORDERING}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def peel_lambdas(node: Node) -> Tuple[List[str], Node]:
|
|
77
|
+
"""Split a (possibly nested) Lambda into its binder names and its body.
|
|
78
|
+
|
|
79
|
+
``λx. λy. φ`` denotes a binary relation, so the number of binders IS the
|
|
80
|
+
arity of the property the argument denotes.
|
|
81
|
+
"""
|
|
82
|
+
names = []
|
|
83
|
+
while isinstance(node, Lambda):
|
|
84
|
+
names.append(node.param.name)
|
|
85
|
+
node = node.body
|
|
86
|
+
return names, node
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def rename_bound_pred(node: Node, old: str, new: str) -> Node:
|
|
90
|
+
"""Rename every occurrence of the predicate variable ``old`` to ``new``.
|
|
91
|
+
|
|
92
|
+
Renames it in HEAD position (``Atom``) and in ARGUMENT position
|
|
93
|
+
(``PredicateTerm``) alike, and stops at an inner binder over the same name,
|
|
94
|
+
whose occurrences belong to that binding rather than this one.
|
|
95
|
+
"""
|
|
96
|
+
if isinstance(node, SecondOrderQuantifier) and node.predicate == old:
|
|
97
|
+
return node
|
|
98
|
+
if isinstance(node, Atom) and node.predicate == old:
|
|
99
|
+
return Atom(new, [rename_bound_pred(a, old, new) for a in node.args])
|
|
100
|
+
if isinstance(node, PredicateTerm) and node.name == old:
|
|
101
|
+
return PredicateTerm(new)
|
|
102
|
+
return node.map_children(lambda child: rename_bound_pred(child, old, new))
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
def rename_apart(formulas: Sequence[Node]) -> Tuple[List[Node], Dict[str, str]]:
|
|
106
|
+
"""Give every second-order binder a name unique across ``formulas``.
|
|
107
|
+
|
|
108
|
+
A theory-wide signature analysis merges by NAME, and a bound variable's name
|
|
109
|
+
is scoped to its own formula: two axioms may each bind ``P`` at a different
|
|
110
|
+
arity without disagreeing about anything, and a binder may even shadow
|
|
111
|
+
another of the same name inside one formula. Renaming apart first is what
|
|
112
|
+
keeps the analysis answering the question that was actually asked — what the
|
|
113
|
+
FREE symbols are, and what arity each binding really has — instead of a
|
|
114
|
+
false conflict between two local bindings.
|
|
115
|
+
|
|
116
|
+
Returns the renamed formulas and a ``{fresh name: original name}`` map. The
|
|
117
|
+
renaming is for the ANALYSIS; emission renders the renamed tree but prints
|
|
118
|
+
the original names back through that map, which is sound because the emitted
|
|
119
|
+
binders reproduce the original scoping exactly.
|
|
120
|
+
"""
|
|
121
|
+
counter = [0]
|
|
122
|
+
original: Dict[str, str] = {}
|
|
123
|
+
|
|
124
|
+
def walk(node: Node) -> Node:
|
|
125
|
+
if isinstance(node, SecondOrderQuantifier):
|
|
126
|
+
counter[0] += 1
|
|
127
|
+
fresh = f"Bound{counter[0]}"
|
|
128
|
+
original[fresh] = node.predicate
|
|
129
|
+
renamed = rename_bound_pred(node.formula, node.predicate, fresh)
|
|
130
|
+
return SecondOrderQuantifier(node.type, fresh, node.arity, walk(renamed))
|
|
131
|
+
return node.map_children(walk)
|
|
132
|
+
|
|
133
|
+
return [walk(f) for f in formulas], original
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def bound_pred_names(node: Node, acc: Set[str] = None) -> Set[str]:
|
|
137
|
+
"""Collect every predicate-variable name bound by a second-order quantifier."""
|
|
138
|
+
acc = set() if acc is None else acc
|
|
139
|
+
if isinstance(node, SecondOrderQuantifier):
|
|
140
|
+
acc.add(node.predicate)
|
|
141
|
+
for child in node._child_nodes():
|
|
142
|
+
bound_pred_names(child, acc)
|
|
143
|
+
return acc
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def atom_predicates(node: Node, acc: Set[str] = None) -> Set[str]:
|
|
147
|
+
"""Collect every predicate name applied in ``node``."""
|
|
148
|
+
acc = set() if acc is None else acc
|
|
149
|
+
if isinstance(node, Atom) and truth_value(node) is None:
|
|
150
|
+
acc.add(node.predicate) # (`$true` / `$false` are constants, not predicates)
|
|
151
|
+
for child in node._child_nodes():
|
|
152
|
+
atom_predicates(child, acc)
|
|
153
|
+
return acc
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def function_symbols(formulas: Sequence[Node], acc: Dict[str, int] = None) -> Dict[str, int]:
|
|
157
|
+
"""Collect ``{function name: arity}`` over ``formulas``."""
|
|
158
|
+
acc = {} if acc is None else acc
|
|
159
|
+
for node in formulas:
|
|
160
|
+
if isinstance(node, Function):
|
|
161
|
+
acc[node.name] = len(node.args)
|
|
162
|
+
function_symbols(list(node._child_nodes()), acc)
|
|
163
|
+
return acc
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def constant_name(node: Constant) -> str:
|
|
167
|
+
"""The name of the constant ``node``, which has to be a string.
|
|
168
|
+
|
|
169
|
+
Every other kind of name has no identifier in either target, so it is refused here, by
|
|
170
|
+
name, before any text is written.
|
|
171
|
+
"""
|
|
172
|
+
if not isinstance(node.name, str):
|
|
173
|
+
raise TypeError(
|
|
174
|
+
f"the name of a constant must be a string, got {node.name!r} "
|
|
175
|
+
f"({type(node.name).__name__}): a constant of that name has no identifier in THF "
|
|
176
|
+
"or in Isabelle; write the name as a string")
|
|
177
|
+
return node.name
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def individual_symbols(formulas: Sequence[Node]) -> List[Tuple[str, str]]:
|
|
181
|
+
"""The individual symbols of ``formulas`` that a closed theory has to declare, as ``(kind, name)``.
|
|
182
|
+
|
|
183
|
+
``kind`` is :data:`CONSTANT` for a :class:`~unicode_logic_kit.fol.nodes.Constant` and
|
|
184
|
+
:data:`FREE_VARIABLE` for an object variable bound by no quantifier or λ. A closed HOL theory
|
|
185
|
+
has no free variables, so the second is declared as an individual constant too, but it stays
|
|
186
|
+
the symbol it is: one unknown element, never the constant that is spelled like it.
|
|
187
|
+
|
|
188
|
+
The list is sorted by name and, within one name, the free variable comes first; for a formula
|
|
189
|
+
in which no variable is spelled like a constant that is the order of the sorted names.
|
|
190
|
+
"""
|
|
191
|
+
variables: Set[str] = set()
|
|
192
|
+
constants: Set[str] = set()
|
|
193
|
+
|
|
194
|
+
def walk(node: Node, bound: frozenset) -> None:
|
|
195
|
+
if isinstance(node, Constant):
|
|
196
|
+
constants.add(constant_name(node))
|
|
197
|
+
return
|
|
198
|
+
if isinstance(node, Variable):
|
|
199
|
+
if node.name not in bound:
|
|
200
|
+
variables.add(node.name)
|
|
201
|
+
return
|
|
202
|
+
if isinstance(node, LambdaVar):
|
|
203
|
+
return
|
|
204
|
+
if isinstance(node, Quantifier):
|
|
205
|
+
walk(node.formula, bound | {node.variable.name})
|
|
206
|
+
return
|
|
207
|
+
if isinstance(node, Lambda):
|
|
208
|
+
walk(node.body, bound | {node.param.name})
|
|
209
|
+
return
|
|
210
|
+
for child in node._child_nodes():
|
|
211
|
+
walk(child, bound)
|
|
212
|
+
|
|
213
|
+
for formula in formulas:
|
|
214
|
+
walk(formula, frozenset())
|
|
215
|
+
symbols = [(FREE_VARIABLE, name) for name in variables] + [(CONSTANT, name) for name in constants]
|
|
216
|
+
return sorted(symbols, key=lambda symbol: (symbol[1], symbol[0] != FREE_VARIABLE))
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
class ThfNames:
|
|
220
|
+
"""THF identifiers for one problem emitted by a third-order exporter.
|
|
221
|
+
|
|
222
|
+
A THF constant and an annotated formula's name must be a lower word: an
|
|
223
|
+
upper-case initial makes a token a VARIABLE, so the kit's ``Positive``/``G``
|
|
224
|
+
written out verbatim is not a constant at all and the problem does not parse.
|
|
225
|
+
Free symbols therefore go through the kit's THF stem (ASCII, lower-case
|
|
226
|
+
initial, see :func:`functor_stem`) and are made unique — keyed by
|
|
227
|
+
``(kind, name)``, because THF has one namespace for predicates, individuals
|
|
228
|
+
and functions, and pushed off ``reserved`` (the embedding's own functors) so
|
|
229
|
+
``G`` and ``g``, or a user predicate named ``r``, cannot collide with each
|
|
230
|
+
other or with the accessibility relation. The kinds a writer uses are
|
|
231
|
+
``"predicate"``, ``"function"``, ``"nominal"`` and the two of an individual,
|
|
232
|
+
:data:`FREE_VARIABLE` and :data:`CONSTANT`: a free variable and a constant that
|
|
233
|
+
are spelled alike are two symbols with two functors. A bound variable is not
|
|
234
|
+
named here; its token is upper-case (see :func:`bound_token`) and a functor
|
|
235
|
+
lower-case, so a bound variable never has the token of a symbol of this class.
|
|
236
|
+
"""
|
|
237
|
+
|
|
238
|
+
def __init__(self, reserved: Sequence[str] = ()):
|
|
239
|
+
self._used: Set[str] = set(reserved)
|
|
240
|
+
self._functors: Dict[Tuple[str, str], str] = {}
|
|
241
|
+
self._units: Set[str] = set()
|
|
242
|
+
|
|
243
|
+
def functor(self, kind: str, name: str) -> str:
|
|
244
|
+
"""The unique THF constant for the free symbol ``name`` of ``kind``."""
|
|
245
|
+
key = (kind, name)
|
|
246
|
+
if key not in self._functors:
|
|
247
|
+
self._functors[key] = dedupe(functor_stem(name), self._used)
|
|
248
|
+
return self._functors[key]
|
|
249
|
+
|
|
250
|
+
def unit(self, name: str) -> str:
|
|
251
|
+
"""Claim a unique annotated-formula name derived from ``name``."""
|
|
252
|
+
return dedupe(functor_stem(name), self._units)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
_ISABELLE_IDENTIFIER = re.compile(r"[A-Za-z][A-Za-z0-9_']*\Z")
|
|
256
|
+
|
|
257
|
+
#: The constants of HOL that the text of an exporter names itself (``True`` and ``False`` for
|
|
258
|
+
#: ``$true`` / ``$false``): a symbol of the formula never takes one of these names.
|
|
259
|
+
ISABELLE_BUILT_IN = frozenset({"True", "False"})
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def is_isabelle_identifier(name: str) -> bool:
|
|
263
|
+
"""Whether ``name`` is an Isabelle identifier as it stands: an ASCII letter, then ASCII
|
|
264
|
+
letters, digits, underscores and apostrophes."""
|
|
265
|
+
return _ISABELLE_IDENTIFIER.match(name) is not None
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
class IsabelleNames:
|
|
269
|
+
"""Isabelle identifiers for the free symbols of one theory emitted by a third-order exporter.
|
|
270
|
+
|
|
271
|
+
A symbol is keyed by ``(kind, name)`` (``"predicate"``, ``"function"``, :data:`FREE_VARIABLE`,
|
|
272
|
+
:data:`CONSTANT`), and the identifiers are made unique over ONE pool, because an Isabelle
|
|
273
|
+
theory has one namespace for the constants it declares: a constant ``P`` and a predicate
|
|
274
|
+
``P``, a free variable ``x`` and a constant ``x``, are four symbols and must not be declared
|
|
275
|
+
under one name. ``reserved`` are the names the text already gives a meaning to (the
|
|
276
|
+
embedding's own vocabulary, HOL's ``True`` and ``False``): no symbol takes one of them.
|
|
277
|
+
|
|
278
|
+
A name that is an Isabelle identifier (:func:`is_isabelle_identifier`) is written as it is,
|
|
279
|
+
so ``socrates`` and ``Alice`` keep their names and the text of a formula whose symbols are
|
|
280
|
+
plain words is the text it always was. Any other name (``John Doe``, ``G-910``, ``C++``,
|
|
281
|
+
``2008SummerOlympics``, ``θ``, ``_sk0``) is written under its :func:`functor_stem`. Names
|
|
282
|
+
that are identifiers claim their own spelling FIRST, so a name that merely sanitises onto
|
|
283
|
+
one (``a b`` next to ``a_b``) is the one pushed to ``a_b_2``.
|
|
284
|
+
"""
|
|
285
|
+
|
|
286
|
+
def __init__(self, reserved: Iterable[str] = ()):
|
|
287
|
+
self._used: Set[str] = set(reserved)
|
|
288
|
+
self._tokens: Dict[Tuple[str, str], str] = {}
|
|
289
|
+
|
|
290
|
+
def claim(self, symbols: Iterable[Tuple[str, str]]) -> None:
|
|
291
|
+
"""Name ``symbols`` (``(kind, name)`` pairs): those that are identifiers first, each
|
|
292
|
+
group in the order given, so the order a caller gives decides who keeps a contested name."""
|
|
293
|
+
pending = [s for s in dict.fromkeys(symbols) if s not in self._tokens]
|
|
294
|
+
for verbatim in (True, False):
|
|
295
|
+
for kind, name in pending:
|
|
296
|
+
if is_isabelle_identifier(name) == verbatim:
|
|
297
|
+
base = name if verbatim else functor_stem(name)
|
|
298
|
+
self._tokens[(kind, name)] = dedupe(base, self._used)
|
|
299
|
+
|
|
300
|
+
def symbol(self, kind: str, name: str) -> str:
|
|
301
|
+
"""The identifier of the symbol ``name`` of ``kind`` (named now when it has none yet)."""
|
|
302
|
+
if (kind, name) not in self._tokens:
|
|
303
|
+
self.claim([(kind, name)])
|
|
304
|
+
return self._tokens[(kind, name)]
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
def bound_token(name: str, suffix: str, scope: Dict[str, str]) -> str:
|
|
308
|
+
"""An upper-case THF variable token for a binder of ``name``.
|
|
309
|
+
|
|
310
|
+
``scope`` maps each source name bound further out to its token. The result
|
|
311
|
+
differs from every one of those tokens: ``x`` and ``X`` share a stem, and
|
|
312
|
+
reusing it for an inner binder of a DIFFERENT name would capture the outer
|
|
313
|
+
variable's occurrences inside the body.
|
|
314
|
+
"""
|
|
315
|
+
stem = _thf_name(name).upper()
|
|
316
|
+
if not stem[:1].isalpha():
|
|
317
|
+
stem = "V" + stem
|
|
318
|
+
stem += suffix
|
|
319
|
+
taken = set(scope.values())
|
|
320
|
+
token, i = stem, 2
|
|
321
|
+
while token in taken:
|
|
322
|
+
token, i = f"{stem}{i}", i + 1
|
|
323
|
+
return token
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
"""The names an Isabelle writer gives its binders, kept off every symbol of the theory.
|
|
2
|
+
|
|
3
|
+
An Isabelle binder ``\\<forall>x::i.`` or ``\\<lambda>x::i.`` shadows a constant ``x`` inside its own
|
|
4
|
+
scope, so a binder that is spelled like a constant, a function, a predicate or one of the embedding's
|
|
5
|
+
own abbreviations captures it: ``∀x P(x, c_x)`` written as ``\\<forall>x::i. (P x x)`` is the formula
|
|
6
|
+
``∀x P(x, x)``. The writers of the second order, the third order and the third-order modal embedding
|
|
7
|
+
print the names of the source, so each of them asks this module for the name of every binder: the
|
|
8
|
+
source name when no symbol of the theory is spelled like it, a suffixed variant (``x_2``) when one is.
|
|
9
|
+
|
|
10
|
+
A binder is either a VARIABLE (an object quantifier, a lambda parameter, the variable of a
|
|
11
|
+
cardinality term: all individuals, one namespace) or a PREDICATE variable (bound by a second-order
|
|
12
|
+
quantifier). Two binders of one kind and one name are one token: shadowing means the same in the
|
|
13
|
+
text as in the source. A variable and a predicate variable of one name get two tokens.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
import re
|
|
17
|
+
from typing import Dict, FrozenSet, Iterable, List, Mapping, Optional, Tuple
|
|
18
|
+
|
|
19
|
+
from ..fol._symbol_names import dedupe
|
|
20
|
+
from ..fol.nodes import (
|
|
21
|
+
Node, Quantifier, Cardinality, SortedCardinality, Lambda, SecondOrderQuantifier,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
__all__ = ["VARIABLE", "PREDICATE", "declared_names", "binder_tokens", "collect_binders",
|
|
25
|
+
"BinderScope"]
|
|
26
|
+
|
|
27
|
+
VARIABLE = "variable"
|
|
28
|
+
PREDICATE = "predicate"
|
|
29
|
+
|
|
30
|
+
_IDENTIFIER = re.compile(r"[A-Za-z][A-Za-z0-9_]*\Z")
|
|
31
|
+
_DECLARATION = re.compile(r"^\s*(?:consts|abbreviation|definition|fun|primrec|inductive)\s+"
|
|
32
|
+
r"([A-Za-z][A-Za-z0-9_']*)")
|
|
33
|
+
|
|
34
|
+
#: Names the writers' own text gives a meaning to inside a binder's scope.
|
|
35
|
+
_BUILT_IN = frozenset({"True", "False", "card"})
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def declared_names(lines: Iterable[str]) -> FrozenSet[str]:
|
|
39
|
+
"""The names that the declarations among ``lines`` (``consts``, ``abbreviation``, …) introduce.
|
|
40
|
+
|
|
41
|
+
These are the symbols of the theory a binder must keep clear of; they are read off the text
|
|
42
|
+
the writer emits, so a symbol added to the vocabulary later is covered without being listed.
|
|
43
|
+
"""
|
|
44
|
+
names = set(_BUILT_IN)
|
|
45
|
+
for line in "\n".join(lines).splitlines(): # an element may hold several lines
|
|
46
|
+
match = _DECLARATION.match(line)
|
|
47
|
+
if match:
|
|
48
|
+
names.add(match.group(1))
|
|
49
|
+
return frozenset(names)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _identifier(name: str) -> str:
|
|
53
|
+
"""``name`` as a legal Isabelle identifier: letters, digits and underscores, a letter first."""
|
|
54
|
+
if _IDENTIFIER.match(name):
|
|
55
|
+
return name
|
|
56
|
+
safe = "".join(c if (c.isascii() and (c.isalnum() or c == "_")) else "_" for c in name)
|
|
57
|
+
return safe if safe[:1].isalpha() else "v" + safe
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def binder_tokens(binders: Iterable[Tuple[str, str]],
|
|
61
|
+
taken: Iterable[str]) -> Dict[Tuple[str, str], str]:
|
|
62
|
+
"""The name each binder is printed under, ``{(kind, name): token}``.
|
|
63
|
+
|
|
64
|
+
``binders`` are ``(kind, name)`` pairs, ``kind`` being :data:`VARIABLE` or :data:`PREDICATE`;
|
|
65
|
+
``taken`` are the names of the theory (:func:`declared_names`). The token of a binder is its
|
|
66
|
+
own name (as a legal identifier) unless that is taken, by a symbol or by the token of another
|
|
67
|
+
binder, in which case it is the first of ``name_2``, ``name_3``, … that is not. The order of
|
|
68
|
+
``binders`` does not matter: they are claimed in sorted order.
|
|
69
|
+
"""
|
|
70
|
+
used = set(taken)
|
|
71
|
+
tokens: Dict[Tuple[str, str], str] = {}
|
|
72
|
+
for kind, name in sorted(set(binders), key=lambda b: (b[1], b[0])):
|
|
73
|
+
tokens[(kind, name)] = dedupe(_identifier(name), used)
|
|
74
|
+
return tokens
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class BinderScope:
|
|
78
|
+
"""The binders in scope at one point of a formula, and the names they are printed under.
|
|
79
|
+
|
|
80
|
+
Immutable: :meth:`enter` returns the scope inside a binder. An occurrence of a variable is
|
|
81
|
+
printed under its binder's token when a binder of that name encloses it, and under
|
|
82
|
+
``default`` otherwise (a free individual, declared as a constant of its own name).
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
__slots__ = ("_tokens", "_in_scope")
|
|
86
|
+
|
|
87
|
+
def __init__(self, tokens: Mapping[Tuple[str, str], str],
|
|
88
|
+
in_scope: Optional[Mapping[Tuple[str, str], str]] = None):
|
|
89
|
+
self._tokens = tokens
|
|
90
|
+
self._in_scope: Dict[Tuple[str, str], str] = dict(in_scope or {})
|
|
91
|
+
|
|
92
|
+
def enter(self, kind: str, name: str) -> Tuple[str, "BinderScope"]:
|
|
93
|
+
"""``(token, scope inside the binder)`` for a binder of ``kind`` and ``name``: its token
|
|
94
|
+
when it has one, its own name when it has none."""
|
|
95
|
+
token = self._tokens.get((kind, name), name)
|
|
96
|
+
inner = dict(self._in_scope)
|
|
97
|
+
inner[(kind, name)] = token
|
|
98
|
+
return token, BinderScope(self._tokens, inner)
|
|
99
|
+
|
|
100
|
+
def token(self, kind: str, name: str, default: Optional[str] = None) -> str:
|
|
101
|
+
"""The token of the enclosing binder of ``kind`` and ``name``, else ``default``
|
|
102
|
+
(``name`` itself when no default is given)."""
|
|
103
|
+
found = self._in_scope.get((kind, name))
|
|
104
|
+
if found is not None:
|
|
105
|
+
return found
|
|
106
|
+
return name if default is None else default
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def collect_binders(formulas: Iterable[Node]) -> List[Tuple[str, str]]:
|
|
110
|
+
"""Every binder of ``formulas``, as ``(kind, name)``.
|
|
111
|
+
|
|
112
|
+
An object quantifier, a cardinality term and a lambda abstraction bind a VARIABLE (the name
|
|
113
|
+
of its ``variable`` or ``param``); a second-order quantifier binds a PREDICATE variable
|
|
114
|
+
(its ``predicate``).
|
|
115
|
+
"""
|
|
116
|
+
found: List[Tuple[str, str]] = []
|
|
117
|
+
for formula in formulas:
|
|
118
|
+
for node in formula.walk():
|
|
119
|
+
if isinstance(node, (Quantifier, Cardinality, SortedCardinality)):
|
|
120
|
+
found.append((VARIABLE, node.variable.name))
|
|
121
|
+
elif isinstance(node, Lambda):
|
|
122
|
+
found.append((VARIABLE, node.param.name))
|
|
123
|
+
elif isinstance(node, SecondOrderQuantifier):
|
|
124
|
+
found.append((PREDICATE, node.predicate))
|
|
125
|
+
return found
|