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,715 @@
|
|
|
1
|
+
"""Native TFA (typed first-order arithmetic) export: ONE numeric sort, typed.
|
|
2
|
+
|
|
3
|
+
:mod:`atp.tptp_tff` already emits genuinely typed TPTP (`tff(...)`), but its
|
|
4
|
+
own module docstring puts TPTP's built-in arithmetic sorts (`$int`/`$rat`/
|
|
5
|
+
`$real`) explicitly out of scope, refusing every arithmetic operator/
|
|
6
|
+
comparison BY NAME (see that module's 'Scope: TF0 only' section) — its
|
|
7
|
+
union-find sort-inference machinery is built for genuine many-sorted
|
|
8
|
+
problems (several DISTINCT user sorts coexisting), which is a harder, and
|
|
9
|
+
different, problem than this module solves. This module is the arithmetic
|
|
10
|
+
sibling that FILLS that gap, mirroring :mod:`atp.z3_arith`'s already-proven
|
|
11
|
+
design choice instead: the WHOLE problem lives in ONE numeric sort chosen by
|
|
12
|
+
the caller (`"real"` or `"int"`, exactly :class:`atp.z3_arith.ArithEnv`'s own
|
|
13
|
+
two choices) — no attempt at general sort inference, no mixing of a numeric
|
|
14
|
+
and a non-numeric individual domain in the same problem. A predicate or
|
|
15
|
+
function that is not itself arithmetic (`Human`, `Prime`, ...) is simply
|
|
16
|
+
declared as an uninterpreted symbol over that SAME numeric sort — exactly
|
|
17
|
+
:class:`~unicode_logic_kit.atp.z3_arith.ArithEnv`'s `get_pred`/`get_func`
|
|
18
|
+
convention, carried over to typed TPTP text.
|
|
19
|
+
|
|
20
|
+
Why a native `tff` export matters here specifically: this is the ONE problem
|
|
21
|
+
writer that asks for ARITHMETIC. The other problem writers, the classical `fof`
|
|
22
|
+
route (:mod:`atp._tptp_problem`) and the many-sorted TF0 route
|
|
23
|
+
(:mod:`atp.tptp_tff`), read `+`/`-`/`*`/`/` and `</>/≤/≥` as ordinary
|
|
24
|
+
uninterpreted symbols and a number as an ordinary constant, which is what this
|
|
25
|
+
kit means by them unless arithmetic is asked for by name (writing TPTP's
|
|
26
|
+
arithmetic dollar-words under an untyped `fof` role would be a trap: a prover
|
|
27
|
+
reads `$less(1,2)` and `$sum(1,1) = 2` as theorems of its own arithmetic and
|
|
28
|
+
`p(1)` as a type error). This module emits TPTP's own arithmetic vocabulary
|
|
29
|
+
(`$sum`/`$difference`/`$product`/`$quotient`, `$less`/`$greater`/`$lesseq`/
|
|
30
|
+
`$greatereq` — see :class:`~unicode_logic_kit.fol.nodes.Function.TPTP_ARITH_OPS`
|
|
31
|
+
and :class:`~unicode_logic_kit.fol.nodes.Atom.PREFIX_PREDS_TPTP`, reused verbatim,
|
|
32
|
+
see :func:`_render_term`/:func:`_render_atom`, except that `/` over `$int` is
|
|
33
|
+
`$quotient_e`: see 'Arithmetic operators' below) and the number literals under `tff` type
|
|
34
|
+
declarations that pin every symbol to `$int`/`$real`, which is what actually
|
|
35
|
+
lets Vampire switch on its built-in linear-arithmetic reasoning. E 3.5.1 reads the
|
|
36
|
+
text but does no arithmetic with it (it stops with a type error on `$sum` and the
|
|
37
|
+
other operators, and reads a `$real` literal approximately), so the E backend refuses
|
|
38
|
+
by name what E cannot do: see the module docstring of :mod:`atp.eprover_backend`.
|
|
39
|
+
|
|
40
|
+
Design rule: refuse loudly, never guess a sort
|
|
41
|
+
-----------------------------------------------
|
|
42
|
+
A :class:`~unicode_logic_kit.fol.nodes.SortedQuantifier` /
|
|
43
|
+
:class:`~unicode_logic_kit.fol.nodes.SortedConstant` /
|
|
44
|
+
:class:`~unicode_logic_kit.fol.nodes.SortedCount` node is, by construction,
|
|
45
|
+
introducing a genuinely different (and not necessarily numeric) sort — the
|
|
46
|
+
exact situation this module's single-sort design exists to avoid silently
|
|
47
|
+
guessing about. Rather than either (a) lowering it through
|
|
48
|
+
:func:`~unicode_logic_kit.fol.nodes.to_fol` into an uninterpreted guard
|
|
49
|
+
predicate over the numeric sort the way :func:`atp.z3_arith.to_z3_arith`
|
|
50
|
+
does, or (b) attempting real multi-sort inference the way
|
|
51
|
+
:mod:`atp.tptp_tff` does, :func:`_check_fragment` below refuses every one of
|
|
52
|
+
these nodes BY NAME with `NotImplementedError`, naming the many-sorted
|
|
53
|
+
export route (:func:`atp.tptp_tff.generate_tff_problem`) as the alternative
|
|
54
|
+
for a problem that genuinely mixes several sorts. The (unsorted) `Count`
|
|
55
|
+
quantifier and every non-classical construct (modal/temporal, second-order,
|
|
56
|
+
Łukasiewicz, lambda) are refused the same way — none of those are in scope
|
|
57
|
+
for a linear/simple-nonlinear arithmetic exporter.
|
|
58
|
+
|
|
59
|
+
Free variables are refused, not implicitly closed
|
|
60
|
+
---------------------------------------------------
|
|
61
|
+
Mirrors :mod:`atp.tptp_tff`'s identical refusal, for a related but distinct
|
|
62
|
+
reason: TPTP itself gives no single universally-agreed implicit-closure
|
|
63
|
+
convention for a free variable (conventionally universal, but not every
|
|
64
|
+
reader/writer pair treats it that way), and this module's own differential
|
|
65
|
+
test oracle (:mod:`atp.z3_arith`'s `is_valid_arith`/`is_satisfiable_arith`)
|
|
66
|
+
treats an unquantified symbol as an ARBITRARY witness whose reading
|
|
67
|
+
(existential vs. universal) depends on which of the two functions is asked
|
|
68
|
+
— silently picking one TPTP convention here could give a verdict that
|
|
69
|
+
disagrees with the oracle for a reason that has nothing to do with either
|
|
70
|
+
route's own correctness. Every variable this module renders therefore comes
|
|
71
|
+
from an explicit :class:`~unicode_logic_kit.fol.nodes.Quantifier` binder.
|
|
72
|
+
|
|
73
|
+
Name legality and round-tripping: reuses :mod:`atp._tptp_problem` directly
|
|
74
|
+
-----------------------------------------------------------------------------
|
|
75
|
+
(A predicate and a function/constant that fold to the same identifier are
|
|
76
|
+
renamed on the term side, exactly as in the ``fof`` and TF0 writers, and
|
|
77
|
+
recorded in the returned map — see :func:`atp._tptp_problem._separate_term_names`.
|
|
78
|
+
TFF has one flat symbol table, so leaving the pair alone would declare one
|
|
79
|
+
identifier at two types.)
|
|
80
|
+
|
|
81
|
+
Unlike :mod:`atp.tptp_tff` (which duplicates the small ASCII/fold renaming
|
|
82
|
+
primitives to avoid a circular import with :mod:`atp._tptp_problem`, since
|
|
83
|
+
THAT module imports the many-sorted writer), this module has no such cycle
|
|
84
|
+
— nothing in :mod:`atp._tptp_problem` depends on this one — so it reuses
|
|
85
|
+
:func:`atp._tptp_problem._sanitize_for_tptp` and
|
|
86
|
+
:func:`atp._tptp_problem._check_no_symbol_collisions` directly rather than
|
|
87
|
+
re-implementing them a third time. Both already skip exactly the right
|
|
88
|
+
symbols (`=`/`≠`, the `<`/`>`/`≤`/`≥` dollar-predicates, and the `+`/`-`/`*`/
|
|
89
|
+
`/` dollar-functions — see their own module docstring) during collection AND
|
|
90
|
+
rewriting, which is precisely what this single-sort fragment also needs.
|
|
91
|
+
The returned :class:`~unicode_logic_kit.atp._tptp_problem.TptpNameMap` is a
|
|
92
|
+
REAL, usable mapping here, as it is on :mod:`atp.tptp_tff`'s many-sorted
|
|
93
|
+
`tff` route (:func:`~unicode_logic_kit.atp.tptp_tff.generate_tff_problem_with_mapping`
|
|
94
|
+
returns one too, and the backends use it to read a prover's output back to
|
|
95
|
+
kit-level names; sort names are the one thing that route leaves out of its
|
|
96
|
+
map, and this route has no sorts): this module's own rendering
|
|
97
|
+
(:func:`_fold_pred`/:func:`_fold_term`) applies EXACTLY the same
|
|
98
|
+
first-character fold :meth:`~unicode_logic_kit.fol.nodes.Node.to_tptp` uses,
|
|
99
|
+
so :meth:`TptpNameMap.reverse_rendered` — built for that fold — works
|
|
100
|
+
unmodified against a prover's raw TFA-route output too.
|
|
101
|
+
|
|
102
|
+
A variable that has no TPTP spelling (``ä`` is written ``Ä``, ``x-1``, ``1x``) is
|
|
103
|
+
renamed to a fresh legal one, per formula and without a record
|
|
104
|
+
(:func:`unicode_logic_kit.fol._tptp_symbols.legalise_variables`: a variable is bound).
|
|
105
|
+
The nullary atoms ``$true`` and ``$false`` are TPTP's own propositions (this kit's
|
|
106
|
+
TPTP reader produces them): they are written verbatim and are neither declared nor
|
|
107
|
+
renamed.
|
|
108
|
+
|
|
109
|
+
Numeric literals
|
|
110
|
+
-----------------
|
|
111
|
+
:class:`~unicode_logic_kit.fol.nodes.Number` renders as a plain TPTP integer
|
|
112
|
+
literal for `sort="int"` (a literal WITH a decimal point is refused —
|
|
113
|
+
`2.0` was written with a decimal point for a reason, and silently truncating
|
|
114
|
+
it to `2` could silently change the formula's meaning) and as a
|
|
115
|
+
decimal-fraction real literal (guaranteed a `.` — `3` becomes `3.0`) for
|
|
116
|
+
`sort="real"`. An integer is written with its own digits under both sorts and
|
|
117
|
+
never through a `float`: `9007199254740993` stays `9007199254740993` (as a real,
|
|
118
|
+
`9007199254740993.0`, a different number from `9007199254740992.0`) and `10**400`
|
|
119
|
+
is written, not an `OverflowError`. A float with a fractional part is written as
|
|
120
|
+
the shortest text that reads back as it; one that would need TPTP's exponent syntax
|
|
121
|
+
to render (a very small float) is refused rather than emitting invalid TPTP text.
|
|
122
|
+
|
|
123
|
+
Arithmetic operators
|
|
124
|
+
---------------------
|
|
125
|
+
`+`, `-`, `*` are `$sum`, `$difference`, `$product` and `/` is `$quotient` under
|
|
126
|
+
`sort="real"`; under `sort="int"` a `/` is the integer division `$quotient_e` (TPTP's
|
|
127
|
+
`$quotient` is for rationals and reals only, and Vampire stops at it over `$int`).
|
|
128
|
+
`$quotient_e` is the Euclidean quotient, whose remainder is never negative, which is
|
|
129
|
+
what Z3's `/` on integers is (:mod:`atp.z3_arith`), so the two routes agree on
|
|
130
|
+
`-7 / 2`, which is `-4`. A one-argument `-` (the node the Prover9 and SMT-LIB readers
|
|
131
|
+
build for `-t` and `(- t)`) is the negation `$uminus`, as it is in :mod:`atp.z3_arith`.
|
|
132
|
+
Every other operator is binary: `+`, `*`, `/` at another number of arguments, and `-`
|
|
133
|
+
at three or more, are refused by name, because TPTP's `$sum(X)` and its like are
|
|
134
|
+
ill-typed.
|
|
135
|
+
"""
|
|
136
|
+
|
|
137
|
+
import math
|
|
138
|
+
from dataclasses import dataclass, field
|
|
139
|
+
from typing import Dict, List, Optional, Sequence, Set, Tuple, Union
|
|
140
|
+
|
|
141
|
+
from ..fol.nodes import (
|
|
142
|
+
Node, Variable, Constant, Number, Function,
|
|
143
|
+
Atom, Not, And, Or, Xor, Implies, Iff, Quantifier,
|
|
144
|
+
free_variables,
|
|
145
|
+
)
|
|
146
|
+
from ..fol._fol_nodes import constant_name_to_ascii, tptp_fold_first_letter
|
|
147
|
+
from ..fol._tptp_symbols import is_tptp_boolean_atom, legalise_variables
|
|
148
|
+
from ._tptp_problem import (
|
|
149
|
+
TptpNameMap, _sanitize_for_tptp, _check_no_symbol_collisions,
|
|
150
|
+
_separate_term_names,
|
|
151
|
+
)
|
|
152
|
+
from ._writer_support import (
|
|
153
|
+
check_against_generated, normalise_premise_names, refusals_speak_as, tptp_name_token,
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
__all__ = [
|
|
157
|
+
"generate_tff_arith_problem", "formula_to_tff_arith", "TFA_SORT_TOKENS", "TfaRefusal",
|
|
158
|
+
]
|
|
159
|
+
|
|
160
|
+
|
|
161
|
+
class TfaRefusal(ValueError, NotImplementedError):
|
|
162
|
+
"""The typed arithmetic (TFA) writer will not write this problem.
|
|
163
|
+
|
|
164
|
+
Raised for a free variable, a predicate or a function used at two arities, and a name
|
|
165
|
+
used both as a constant and as a function: the writer declares ONE type per name, so
|
|
166
|
+
those problems cannot be written as typed text. The message names the symbol.
|
|
167
|
+
|
|
168
|
+
It derives from both :class:`ValueError` and :class:`NotImplementedError`: the
|
|
169
|
+
refusals were ``ValueError`` before this class existed, and a backend reports a
|
|
170
|
+
``NotImplementedError`` of a writer as ``unknown`` / ``unsupported`` with the writer's
|
|
171
|
+
message, which is what a refusal of a problem that this writer cannot state is (an
|
|
172
|
+
invalid ``sort=`` or a premise name the writer cannot spell stays a plain
|
|
173
|
+
``ValueError``: it is a wrong argument, not a problem the writer cannot state)."""
|
|
174
|
+
|
|
175
|
+
#: The two numeric sorts this exporter supports, and their TPTP dollar-word
|
|
176
|
+
#: tokens — the exact pair :class:`atp.z3_arith.ArithEnv` accepts.
|
|
177
|
+
TFA_SORT_TOKENS: Dict[str, str] = {"real": "$real", "int": "$int"}
|
|
178
|
+
|
|
179
|
+
_INVALID_SORT = (
|
|
180
|
+
"generate_tff_arith_problem: sort must be 'real' or 'int', got {sort!r} "
|
|
181
|
+
"(mirrors atp.z3_arith.ArithEnv's own two choices)."
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
# =============================================================================
|
|
186
|
+
# Fragment gate
|
|
187
|
+
# =============================================================================
|
|
188
|
+
|
|
189
|
+
# Every classical connective/quantifier plus the arithmetic term/atom nodes —
|
|
190
|
+
# deliberately NOT SortedQuantifier/SortedConstant/SortedCount/Count (see
|
|
191
|
+
# module docstring's 'Design rule' section) nor anything non-classical.
|
|
192
|
+
_ALLOWED_TFA_NODES = frozenset({
|
|
193
|
+
"Variable", "Constant", "Number", "Function",
|
|
194
|
+
"Atom", "Not", "And", "Or", "Xor", "Implies", "Iff", "Quantifier",
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _check_fragment(formula: Node) -> None:
|
|
199
|
+
"""Walk ``formula`` and refuse (loudly, by name) the first node outside
|
|
200
|
+
the single-numeric-sort arithmetic fragment this exporter covers."""
|
|
201
|
+
for node in formula.walk():
|
|
202
|
+
cls = type(node).__name__
|
|
203
|
+
if cls not in _ALLOWED_TFA_NODES:
|
|
204
|
+
raise NotImplementedError(
|
|
205
|
+
f"generate_tff_arith_problem: {cls!r} is outside the "
|
|
206
|
+
"single-numeric-sort arithmetic fragment this exporter "
|
|
207
|
+
"covers (only Number, Variable, Constant, Function, Atom, "
|
|
208
|
+
"Not, And, Or, Xor, Implies, Iff, and a plain, unsorted "
|
|
209
|
+
"Quantifier are expressible in ONE numeric sort — see the "
|
|
210
|
+
"module docstring). A SortedQuantifier/SortedConstant/"
|
|
211
|
+
"SortedCount/Count would introduce a genuinely different "
|
|
212
|
+
"domain, which this single-sort exporter refuses to guess "
|
|
213
|
+
"about rather than silently fold into the numeric sort; "
|
|
214
|
+
"use atp.tptp_tff.generate_tff_problem (many-sorted TF0) or "
|
|
215
|
+
"the classical fof route for a formula that genuinely mixes "
|
|
216
|
+
"several sorts."
|
|
217
|
+
)
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def _check_no_free_variables(formula: Node) -> None:
|
|
221
|
+
free = free_variables(formula)
|
|
222
|
+
if free:
|
|
223
|
+
names = ", ".join(sorted(v.name for v in free))
|
|
224
|
+
raise TfaRefusal(
|
|
225
|
+
f"generate_tff_arith_problem: free variable(s) {names} — every "
|
|
226
|
+
"variable must be bound by an enclosing Quantifier; this "
|
|
227
|
+
"exporter refuses to pick an implicit-closure convention rather "
|
|
228
|
+
"than risk disagreeing with the atp.z3_arith differential "
|
|
229
|
+
"oracle for a reason unrelated to either route's correctness "
|
|
230
|
+
"(see module docstring)."
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
# =============================================================================
|
|
235
|
+
# Signature analysis: arity + constant/function-name-clash checks only —
|
|
236
|
+
# every argument/result position is the ONE caller-chosen numeric sort, so
|
|
237
|
+
# (unlike atp.tptp_tff) no union-find sort inference is needed at all.
|
|
238
|
+
# =============================================================================
|
|
239
|
+
|
|
240
|
+
@dataclass
|
|
241
|
+
class _Signature:
|
|
242
|
+
pred_arity: Dict[str, int] = field(default_factory=dict)
|
|
243
|
+
func_arity: Dict[str, int] = field(default_factory=dict)
|
|
244
|
+
const_names: Set[str] = field(default_factory=set)
|
|
245
|
+
func_names: Set[str] = field(default_factory=set)
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
_CONST_VS_FUNCTION = (
|
|
249
|
+
"generate_tff_arith_problem: {name!r} is used both as a constant and as "
|
|
250
|
+
"a function — this exporter declares at most one 'tff(...,type,...)' "
|
|
251
|
+
"entry per name, so the two uses cannot be reconciled into one symbol."
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
_PRED_VS_FUNCTION = (
|
|
255
|
+
"generate_tff_arith_problem: predicate {pred!r} and {kind} {other!r} "
|
|
256
|
+
"would both render as the TPTP identifier {rendered!r} — TFF has ONE "
|
|
257
|
+
"flat symbol table (unlike untyped fof, where syntactic position alone "
|
|
258
|
+
"tells a predicate from a function/constant apart, which is why "
|
|
259
|
+
"atp._tptp_problem._check_no_symbol_collisions guards them as two "
|
|
260
|
+
"separate namespaces), so a predicate and a function/constant cannot "
|
|
261
|
+
"share a rendered identifier here; rename one of them before exporting "
|
|
262
|
+
"this problem."
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def _check_no_predicate_function_collision(sig: _Signature) -> None:
|
|
267
|
+
"""BACKSTOP. Both entry points run :func:`~unicode_logic_kit.atp._tptp_problem
|
|
268
|
+
._separate_term_names` before :func:`_analyze`, which renames the term side
|
|
269
|
+
of every such clash, so this check cannot fire for text those entry points
|
|
270
|
+
produce; it stays so that a clash which ever slipped past that pass is
|
|
271
|
+
refused by name instead of being written as two type declarations.
|
|
272
|
+
|
|
273
|
+
TFF (unlike untyped ``fof``) has ONE flat symbol table: a predicate
|
|
274
|
+
and a function/constant declared with the same rendered TPTP identifier
|
|
275
|
+
would get two conflicting ``tff(...,type,...)`` entries. The reused
|
|
276
|
+
:func:`~unicode_logic_kit.atp._tptp_problem._check_no_symbol_collisions`
|
|
277
|
+
(called before :func:`_analyze`, see its callers) only guards
|
|
278
|
+
predicates and functions/constants as two SEPARATE namespaces — correct
|
|
279
|
+
for the untyped ``fof`` route it was built for, where syntactic position
|
|
280
|
+
alone disambiguates a predicate from a function — so it does not catch
|
|
281
|
+
a cross-namespace clash. This checks ACROSS the two namespaces instead,
|
|
282
|
+
once ``_analyze`` has collected the full per-problem signature (by which
|
|
283
|
+
point :func:`_check_no_symbol_collisions` has already ruled out an
|
|
284
|
+
internal clash within each namespace, so only pred-vs-term needs
|
|
285
|
+
checking here).
|
|
286
|
+
"""
|
|
287
|
+
term_rendered: Dict[str, Tuple[str, str]] = {}
|
|
288
|
+
for name in sig.func_names:
|
|
289
|
+
term_rendered[tptp_fold_first_letter(name)] = ("function", name)
|
|
290
|
+
for name in sig.const_names:
|
|
291
|
+
term_rendered[tptp_fold_first_letter(constant_name_to_ascii(name))] = ("constant", name)
|
|
292
|
+
for pred_name in sig.pred_arity:
|
|
293
|
+
rendered = tptp_fold_first_letter(pred_name)
|
|
294
|
+
hit = term_rendered.get(rendered)
|
|
295
|
+
if hit is not None:
|
|
296
|
+
kind, other = hit
|
|
297
|
+
raise NotImplementedError(_PRED_VS_FUNCTION.format(
|
|
298
|
+
pred=pred_name, kind=kind, other=other, rendered=rendered))
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
def _analyze(formulas: Sequence[Node], check_flat_table: bool = True) -> _Signature:
|
|
302
|
+
"""Collect every predicate/function/constant name and arity used across
|
|
303
|
+
``formulas`` (already ASCII-sanitised — see :func:`generate_tff_arith_problem`),
|
|
304
|
+
checking for an arity conflict or a constant/function clash on the way.
|
|
305
|
+
|
|
306
|
+
``check_flat_table=False`` skips the final predicate-vs-term backstop; the
|
|
307
|
+
entry points use it for the validation pass that runs BEFORE the term side
|
|
308
|
+
is renamed, so that an arity or constant-vs-function error still names the
|
|
309
|
+
symbol the caller wrote rather than its ``<name>_term`` replacement."""
|
|
310
|
+
sig = _Signature()
|
|
311
|
+
for f in formulas:
|
|
312
|
+
for node in f.walk():
|
|
313
|
+
if isinstance(node, Atom):
|
|
314
|
+
if (node.predicate in Atom.INFIX_PREDS_TPTP or node.predicate in Atom.PREFIX_PREDS_TPTP
|
|
315
|
+
or is_tptp_boolean_atom(node)):
|
|
316
|
+
continue
|
|
317
|
+
arity = len(node.args)
|
|
318
|
+
prev = sig.pred_arity.get(node.predicate)
|
|
319
|
+
if prev is not None and prev != arity:
|
|
320
|
+
raise TfaRefusal(
|
|
321
|
+
f"generate_tff_arith_problem: predicate "
|
|
322
|
+
f"'{node.predicate}' used with conflicting arities "
|
|
323
|
+
f"{prev} and {arity}."
|
|
324
|
+
)
|
|
325
|
+
sig.pred_arity[node.predicate] = arity
|
|
326
|
+
elif isinstance(node, Function):
|
|
327
|
+
if node.name in Function.TPTP_ARITH_OPS:
|
|
328
|
+
continue
|
|
329
|
+
if node.name in sig.const_names:
|
|
330
|
+
raise TfaRefusal(_CONST_VS_FUNCTION.format(name=node.name))
|
|
331
|
+
arity = len(node.args)
|
|
332
|
+
prev = sig.func_arity.get(node.name)
|
|
333
|
+
if prev is not None and prev != arity:
|
|
334
|
+
raise TfaRefusal(
|
|
335
|
+
f"generate_tff_arith_problem: function "
|
|
336
|
+
f"'{node.name}' used with conflicting arities "
|
|
337
|
+
f"{prev} and {arity}."
|
|
338
|
+
)
|
|
339
|
+
sig.func_arity[node.name] = arity
|
|
340
|
+
sig.func_names.add(node.name)
|
|
341
|
+
elif isinstance(node, Constant):
|
|
342
|
+
if node.name in sig.func_names:
|
|
343
|
+
raise TfaRefusal(_CONST_VS_FUNCTION.format(name=node.name))
|
|
344
|
+
sig.const_names.add(node.name)
|
|
345
|
+
if check_flat_table:
|
|
346
|
+
_check_no_predicate_function_collision(sig)
|
|
347
|
+
return sig
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
# =============================================================================
|
|
351
|
+
# Rendering — numbers, terms, atoms, formulas, type declarations
|
|
352
|
+
# =============================================================================
|
|
353
|
+
|
|
354
|
+
def _fold_pred(name: str) -> str:
|
|
355
|
+
"""The exact fold :meth:`~unicode_logic_kit.fol.nodes.Atom.to_tptp` applies
|
|
356
|
+
to a predicate name — first character only."""
|
|
357
|
+
return tptp_fold_first_letter(name)
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
def _fold_term(name: str) -> str:
|
|
361
|
+
"""The exact fold :meth:`~unicode_logic_kit.fol.nodes.Function.to_tptp` /
|
|
362
|
+
:meth:`~unicode_logic_kit.fol.nodes.Constant.to_tptp` apply to a
|
|
363
|
+
function/constant name."""
|
|
364
|
+
return tptp_fold_first_letter(constant_name_to_ascii(name))
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def _render_number(value: Union[int, float], sort: str) -> str:
|
|
368
|
+
"""The TPTP literal of the numeral ``value`` under ``sort``, in the digits it has.
|
|
369
|
+
|
|
370
|
+
An integer is written with its own digits under both sorts (``9007199254740993`` stays
|
|
371
|
+
``9007199254740993``, and under ``real`` it is ``9007199254740993.0``): the text never goes
|
|
372
|
+
through a ``float``, which holds 53 bits and would make ``2**53 + 1`` the numeral ``2**53``, or
|
|
373
|
+
raise ``OverflowError`` for ``10**400``. A float is written as the shortest text that reads back
|
|
374
|
+
as it (``repr``), which a ``$real`` literal can be; one that needs an exponent is refused.
|
|
375
|
+
|
|
376
|
+
Raises:
|
|
377
|
+
NotImplementedError: the value is no integer or finite float (a ``bool`` is no numeral), is
|
|
378
|
+
a float under ``sort='int'`` (no ``$int`` literal has a decimal point), or is a float
|
|
379
|
+
whose text needs TPTP's exponent syntax.
|
|
380
|
+
"""
|
|
381
|
+
if isinstance(value, bool) or not isinstance(value, (int, float)):
|
|
382
|
+
raise NotImplementedError(
|
|
383
|
+
f"generate_tff_arith_problem: Number({value!r}) holds a {type(value).__name__}, and "
|
|
384
|
+
"only an int or a finite float is a numeral this exporter can write — convert the "
|
|
385
|
+
"value first (a bool is no number)."
|
|
386
|
+
)
|
|
387
|
+
if isinstance(value, float) and not math.isfinite(value):
|
|
388
|
+
raise NotImplementedError(
|
|
389
|
+
f"generate_tff_arith_problem: Number({value!r}) is not a finite number, and TPTP has "
|
|
390
|
+
"no literal for it — use a finite value."
|
|
391
|
+
)
|
|
392
|
+
if sort == "int":
|
|
393
|
+
if isinstance(value, float):
|
|
394
|
+
raise NotImplementedError(
|
|
395
|
+
f"generate_tff_arith_problem: numeric literal {value!r} has "
|
|
396
|
+
"a decimal point, which has no $int representation — pass "
|
|
397
|
+
"sort='real', or drop the decimal point if a whole number "
|
|
398
|
+
"was meant."
|
|
399
|
+
)
|
|
400
|
+
return str(value)
|
|
401
|
+
if isinstance(value, int):
|
|
402
|
+
return f"{value}.0"
|
|
403
|
+
text = repr(value)
|
|
404
|
+
if "e" in text or "E" in text:
|
|
405
|
+
raise NotImplementedError(
|
|
406
|
+
f"generate_tff_arith_problem: numeric literal {value!r} would "
|
|
407
|
+
f"render as {text!r}, which needs TPTP's exponent syntax — out "
|
|
408
|
+
"of scope for this exporter (only plain decimal-fraction $real "
|
|
409
|
+
"literals are supported)."
|
|
410
|
+
)
|
|
411
|
+
return text
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def _operator_word(name: str, arity: int, sort: str) -> str:
|
|
415
|
+
"""The TPTP dollar-word of the arithmetic operator ``name`` applied to ``arity`` arguments.
|
|
416
|
+
|
|
417
|
+
``$sum``, ``$difference``, ``$product`` are binary and the same over both sorts. ``/`` is
|
|
418
|
+
``$quotient`` over ``$real``, and over ``$int`` the integer division ``$quotient_e`` (TPTP's
|
|
419
|
+
``$quotient`` is for rationals and reals only: Vampire stops at it over ``$int``): the
|
|
420
|
+
Euclidean quotient, whose remainder is never negative, which is what Z3's ``/`` on integers
|
|
421
|
+
is (``-7 / 2`` is ``-4``, ``7 / -2`` is ``-3``, ``-7 / -2`` is ``4``). A one-argument ``-``
|
|
422
|
+
is the negation ``$uminus`` over both sorts, the reading
|
|
423
|
+
:mod:`unicode_logic_kit.atp.z3_arith` gives it.
|
|
424
|
+
|
|
425
|
+
Raises:
|
|
426
|
+
NotImplementedError: the operator has a number of arguments that is neither two nor,
|
|
427
|
+
for ``-``, one. TPTP's ``$sum(X)`` and ``$difference(X, Y, Z)`` are ill-typed, and
|
|
428
|
+
:mod:`unicode_logic_kit.atp.z3_arith` reads such a node as an uninterpreted function,
|
|
429
|
+
which this exporter cannot write under the operator's name, so the node is refused
|
|
430
|
+
by name.
|
|
431
|
+
"""
|
|
432
|
+
if name == "-" and arity == 1:
|
|
433
|
+
return "$uminus"
|
|
434
|
+
if arity != 2:
|
|
435
|
+
raise NotImplementedError(
|
|
436
|
+
f"generate_tff_arith_problem: the arithmetic operator {name!r} is applied to {arity} "
|
|
437
|
+
f"argument(s), and {Function.TPTP_ARITH_OPS[name]} takes two. The kit's other "
|
|
438
|
+
"arithmetic route (atp.z3_arith) reads an operator at that number of arguments as an "
|
|
439
|
+
"uninterpreted function, which this exporter cannot write under that name."
|
|
440
|
+
)
|
|
441
|
+
if name == "/" and sort == "int":
|
|
442
|
+
return "$quotient_e"
|
|
443
|
+
return Function.TPTP_ARITH_OPS[name]
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
def _render_term(node: Node, sort: str) -> str:
|
|
447
|
+
if isinstance(node, Number):
|
|
448
|
+
return _render_number(node.value, sort)
|
|
449
|
+
if isinstance(node, Variable):
|
|
450
|
+
return node.name.upper()
|
|
451
|
+
if isinstance(node, Constant):
|
|
452
|
+
return _fold_term(node.name)
|
|
453
|
+
if isinstance(node, Function):
|
|
454
|
+
args = [_render_term(a, sort) for a in node.args]
|
|
455
|
+
if node.name in Function.TPTP_ARITH_OPS:
|
|
456
|
+
tptp_name = _operator_word(node.name, len(node.args), sort)
|
|
457
|
+
else:
|
|
458
|
+
tptp_name = _fold_term(node.name)
|
|
459
|
+
if not args:
|
|
460
|
+
return tptp_name
|
|
461
|
+
return f"{tptp_name}({','.join(args)})"
|
|
462
|
+
raise AssertionError(
|
|
463
|
+
f"generate_tff_arith_problem: unreachable term node {type(node).__name__!r}."
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
def _render_atom(node: Atom, sort: str) -> str:
|
|
468
|
+
if is_tptp_boolean_atom(node):
|
|
469
|
+
return node.to_tptp() # the truth constant's own word, whichever spelling it has
|
|
470
|
+
if ((node.predicate in Atom.INFIX_PREDS_TPTP or node.predicate in Atom.PREFIX_PREDS_TPTP)
|
|
471
|
+
and len(node.args) != 2):
|
|
472
|
+
raise NotImplementedError(
|
|
473
|
+
f"generate_tff_arith_problem: the comparison {node.predicate!r} is applied to "
|
|
474
|
+
f"{len(node.args)} argument(s), and a comparison relates two terms. It has no "
|
|
475
|
+
"typed TPTP text, so it is refused rather than written as a symbol a prover rejects."
|
|
476
|
+
)
|
|
477
|
+
if node.predicate in Atom.INFIX_PREDS_TPTP and len(node.args) == 2:
|
|
478
|
+
op = Atom.INFIX_PREDS_TPTP[node.predicate]
|
|
479
|
+
left = _render_term(node.args[0], sort)
|
|
480
|
+
right = _render_term(node.args[1], sort)
|
|
481
|
+
return f"({left} {op} {right})"
|
|
482
|
+
if node.predicate in Atom.PREFIX_PREDS_TPTP and len(node.args) == 2:
|
|
483
|
+
op = Atom.PREFIX_PREDS_TPTP[node.predicate]
|
|
484
|
+
left = _render_term(node.args[0], sort)
|
|
485
|
+
right = _render_term(node.args[1], sort)
|
|
486
|
+
return f"{op}({left},{right})"
|
|
487
|
+
pred = _fold_pred(node.predicate)
|
|
488
|
+
if not node.args:
|
|
489
|
+
return pred
|
|
490
|
+
args = ",".join(_render_term(a, sort) for a in node.args)
|
|
491
|
+
return f"{pred}({args})"
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
def _quant_symbol(qtype: str) -> str:
|
|
495
|
+
if qtype in ("∀", "forall"):
|
|
496
|
+
return "!"
|
|
497
|
+
if qtype in ("∃", "exists"):
|
|
498
|
+
return "?"
|
|
499
|
+
raise ValueError(f"generate_tff_arith_problem: unknown quantifier type {qtype!r}.")
|
|
500
|
+
|
|
501
|
+
|
|
502
|
+
def _render(node: Node, sort: str) -> str:
|
|
503
|
+
if isinstance(node, Atom):
|
|
504
|
+
return _render_atom(node, sort)
|
|
505
|
+
if isinstance(node, Not):
|
|
506
|
+
return f"~({_render(node.formula, sort)})"
|
|
507
|
+
if isinstance(node, And):
|
|
508
|
+
return f"({_render(node.left, sort)} & {_render(node.right, sort)})"
|
|
509
|
+
if isinstance(node, Or):
|
|
510
|
+
return f"({_render(node.left, sort)} | {_render(node.right, sort)})"
|
|
511
|
+
if isinstance(node, Xor):
|
|
512
|
+
return f"({_render(node.left, sort)} <~> {_render(node.right, sort)})"
|
|
513
|
+
if isinstance(node, Implies):
|
|
514
|
+
return f"({_render(node.left, sort)} => {_render(node.right, sort)})"
|
|
515
|
+
if isinstance(node, Iff):
|
|
516
|
+
return f"({_render(node.left, sort)} <=> {_render(node.right, sort)})"
|
|
517
|
+
if isinstance(node, Quantifier):
|
|
518
|
+
var = node.variable.name.upper()
|
|
519
|
+
q = _quant_symbol(node.type)
|
|
520
|
+
sort_tok = TFA_SORT_TOKENS[sort]
|
|
521
|
+
return f"({q}[{var}: {sort_tok}]: {_render(node.formula, sort)})"
|
|
522
|
+
raise AssertionError(
|
|
523
|
+
f"generate_tff_arith_problem: unreachable formula node {type(node).__name__!r}."
|
|
524
|
+
)
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
def _render_map_type(arg_sorts: List[str], result_sort: str) -> str:
|
|
528
|
+
"""Mirrors ``atp.tptp_tff._render_map_type`` — duplicated (small, and
|
|
529
|
+
tied to this module's own type-declaration rendering) rather than
|
|
530
|
+
imported, per the sibling modules' own established convention of
|
|
531
|
+
duplicating small rendering primitives instead of cross-importing them."""
|
|
532
|
+
if not arg_sorts:
|
|
533
|
+
return result_sort
|
|
534
|
+
if len(arg_sorts) == 1:
|
|
535
|
+
return f"{arg_sorts[0]} > {result_sort}"
|
|
536
|
+
return "(" + " * ".join(arg_sorts) + ") > " + result_sort
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
def _render_type_decls(sig: _Signature, sort: str) -> List[str]:
|
|
540
|
+
sort_tok = TFA_SORT_TOKENS[sort]
|
|
541
|
+
lines: List[str] = []
|
|
542
|
+
counter = [0]
|
|
543
|
+
|
|
544
|
+
def next_name(prefix: str) -> str:
|
|
545
|
+
counter[0] += 1
|
|
546
|
+
return f"{prefix}_{counter[0]}"
|
|
547
|
+
|
|
548
|
+
for name in sorted(sig.func_names):
|
|
549
|
+
arity = sig.func_arity[name]
|
|
550
|
+
type_str = _render_map_type([sort_tok] * arity, sort_tok)
|
|
551
|
+
lines.append(f"tff({next_name('func_decl')}, type, {_fold_term(name)}: {type_str} ).")
|
|
552
|
+
for name in sorted(sig.const_names):
|
|
553
|
+
lines.append(f"tff({next_name('const_decl')}, type, {_fold_term(name)}: {sort_tok} ).")
|
|
554
|
+
for name in sorted(sig.pred_arity):
|
|
555
|
+
arity = sig.pred_arity[name]
|
|
556
|
+
type_str = _render_map_type([sort_tok] * arity, "$o")
|
|
557
|
+
lines.append(f"tff({next_name('pred_decl')}, type, {_fold_pred(name)}: {type_str} ).")
|
|
558
|
+
return lines
|
|
559
|
+
|
|
560
|
+
|
|
561
|
+
# =============================================================================
|
|
562
|
+
# Public API
|
|
563
|
+
# =============================================================================
|
|
564
|
+
|
|
565
|
+
def formula_to_tff_arith(formula: Node, sort: str = "real") -> str:
|
|
566
|
+
"""Render a single formula as bare TFA formula text (no ``tff(...)``
|
|
567
|
+
statement wrapper, no type declarations) — the single-numeric-sort
|
|
568
|
+
sibling of :func:`atp.tptp_tff.formula_to_tff`.
|
|
569
|
+
|
|
570
|
+
Runs the fragment check, free-variable check, and ASCII/name-legality
|
|
571
|
+
sanitisation on ``formula`` ALONE, exactly like
|
|
572
|
+
:func:`~unicode_logic_kit.atp.tptp_tff.formula_to_tff` does for the
|
|
573
|
+
many-sorted route.
|
|
574
|
+
|
|
575
|
+
Args:
|
|
576
|
+
formula: a classical (or plain-quantified, unsorted) FOL formula —
|
|
577
|
+
see the module docstring's 'Design rule' for exactly which node
|
|
578
|
+
types are in scope.
|
|
579
|
+
sort: ``'real'`` (default) or ``'int'`` — the ONE numeric sort every
|
|
580
|
+
individual in ``formula`` is declared to inhabit.
|
|
581
|
+
|
|
582
|
+
Raises:
|
|
583
|
+
NotImplementedError: ``formula`` contains a node outside the
|
|
584
|
+
single-sort arithmetic fragment (see module docstring), a
|
|
585
|
+
numeric literal that cannot be rendered for the chosen ``sort``
|
|
586
|
+
(see :func:`_render_number`), an arithmetic operator or comparison
|
|
587
|
+
applied to other than two arguments (a one-argument ``-`` is the
|
|
588
|
+
negation and is written), a
|
|
589
|
+
name-folding collision within a
|
|
590
|
+
namespace (two distinct predicates, or two distinct functions/
|
|
591
|
+
constants), or two LEGAL variables that render as one TPTP variable
|
|
592
|
+
(``x`` and ``X``: ``∀x ∃X R(x, X)`` would be ``![X]: ?[X]: r(X,X)``).
|
|
593
|
+
A predicate and a function/constant that would render
|
|
594
|
+
as the same TFF identifier are NOT refused: the term side is
|
|
595
|
+
renamed (see the module docstring), and
|
|
596
|
+
:func:`generate_tff_arith_problem` returns the map.
|
|
597
|
+
TfaRefusal: a free variable, an arity conflict, or a constant-vs-function
|
|
598
|
+
name clash (a ``ValueError`` and a ``NotImplementedError`` at once).
|
|
599
|
+
ValueError: an invalid ``sort``.
|
|
600
|
+
"""
|
|
601
|
+
with refusals_speak_as("formula_to_tff_arith", "generate_tff_arith_problem"):
|
|
602
|
+
if sort not in TFA_SORT_TOKENS:
|
|
603
|
+
raise ValueError(_INVALID_SORT.format(sort=sort))
|
|
604
|
+
_check_fragment(formula)
|
|
605
|
+
_check_no_free_variables(formula)
|
|
606
|
+
[sanitised], name_map = _sanitize_for_tptp([formula])
|
|
607
|
+
sanitised = legalise_variables(sanitised)
|
|
608
|
+
_check_no_symbol_collisions([sanitised], where="generate_tff_arith_problem",
|
|
609
|
+
subject="formula")
|
|
610
|
+
_analyze([sanitised], check_flat_table=False) # errors name the caller's own symbols
|
|
611
|
+
[sanitised], _name_map = _separate_term_names([sanitised], name_map)
|
|
612
|
+
_analyze([sanitised]) # validation side-effect only (backstop for the flat table)
|
|
613
|
+
return _render(sanitised, sort)
|
|
614
|
+
|
|
615
|
+
|
|
616
|
+
def generate_tff_arith_problem(premises: List[Node], conclusion: Optional[Node] = None,
|
|
617
|
+
sort: str = "real", *,
|
|
618
|
+
premise_names: Optional[Sequence[str]] = None
|
|
619
|
+
) -> Tuple[str, TptpNameMap]:
|
|
620
|
+
"""Build a native, genuinely-typed TPTP ``tff`` problem string, with
|
|
621
|
+
EVERY individual declared to inhabit the ONE numeric sort ``sort``.
|
|
622
|
+
|
|
623
|
+
The single-numeric-sort sibling of
|
|
624
|
+
:func:`atp.tptp_tff.generate_tff_problem` (many-sorted, arithmetic-free)
|
|
625
|
+
and the typed-TPTP sibling of :func:`atp.z3_arith.to_z3_arith`/
|
|
626
|
+
:func:`atp.z3_arith.is_valid_arith` (same single-sort design, aimed at
|
|
627
|
+
Vampire instead of Z3; E reads the text but does no arithmetic with it) — see
|
|
628
|
+
the module docstring for the full design rationale.
|
|
629
|
+
|
|
630
|
+
Args:
|
|
631
|
+
premises: classical FOL premises using the arithmetic operators
|
|
632
|
+
(``+ - * /``), comparisons (``= ≠ < > ≤ ≥``), and ordinary
|
|
633
|
+
(non-arithmetic) predicates/functions/constants/plain
|
|
634
|
+
Quantifiers freely — every individual position is the ONE
|
|
635
|
+
numeric sort ``sort``. See the module docstring's 'Design rule'
|
|
636
|
+
for exactly which node types are refused.
|
|
637
|
+
conclusion: the conjecture, or ``None`` for a problem without one (no
|
|
638
|
+
``conjecture`` line is written; the prover is asked whether the
|
|
639
|
+
premises are satisfiable).
|
|
640
|
+
sort: ``'real'`` (default) or ``'int'`` — the numeric sort every
|
|
641
|
+
declared symbol and quantifier ranges over, exactly
|
|
642
|
+
:class:`atp.z3_arith.ArithEnv`'s own two choices.
|
|
643
|
+
premise_names: one name per premise for the ``axiom`` lines instead of
|
|
644
|
+
``premise_<i>``, written as a TPTP name and recorded, in order and also by
|
|
645
|
+
default, in the returned map's ``premises`` — see
|
|
646
|
+
:func:`~unicode_logic_kit.atp._tptp_problem.generate_tptp_problem_with_mapping`.
|
|
647
|
+
The names the writer gives its own lines are ``goal``, ``func_decl_<n>``,
|
|
648
|
+
``const_decl_<n>`` and ``pred_decl_<n>``; a premise named like one of them
|
|
649
|
+
is a ``ValueError``.
|
|
650
|
+
|
|
651
|
+
Returns:
|
|
652
|
+
A ``(text, name_map)`` pair: ``text`` is the complete ``tff``
|
|
653
|
+
problem, newline-terminated — type declarations first (functions,
|
|
654
|
+
then constants, then predicates, alphabetically within each —
|
|
655
|
+
declaration-before-use, matching
|
|
656
|
+
:func:`atp.tptp_tff.generate_tff_problem`'s own ordering), then one
|
|
657
|
+
``tff(premise_<i>, axiom, ...).`` per premise (1-based), then
|
|
658
|
+
``tff(goal, conjecture, ...).`` unless ``conclusion`` is ``None``.
|
|
659
|
+
``name_map`` is the
|
|
660
|
+
:class:`~unicode_logic_kit.atp._tptp_problem.TptpNameMap` recording
|
|
661
|
+
every ASCII-legality rename applied (see module docstring's 'Name
|
|
662
|
+
legality' section) — pass it to
|
|
663
|
+
:func:`~unicode_logic_kit.atp._tptp_problem.apply_reverse_tptp` or use
|
|
664
|
+
its :meth:`~unicode_logic_kit.atp._tptp_problem.TptpNameMap.reverse_rendered`
|
|
665
|
+
to translate a prover's own output back to kit-level names, exactly
|
|
666
|
+
as the classical ``fof`` route's mapping is used.
|
|
667
|
+
|
|
668
|
+
Raises:
|
|
669
|
+
NotImplementedError: a node outside the single-sort arithmetic
|
|
670
|
+
fragment (see module docstring), a numeric literal that cannot
|
|
671
|
+
be rendered for the chosen ``sort``, an arithmetic operator or
|
|
672
|
+
comparison applied to other than two arguments, a name-folding
|
|
673
|
+
collision within a namespace, or two LEGAL variables of one formula that render as
|
|
674
|
+
one TPTP variable (``x`` and ``X``) — see
|
|
675
|
+
:func:`formula_to_tff_arith`. (A predicate and a function/constant
|
|
676
|
+
that fold to one identifier are renamed on the term side and
|
|
677
|
+
recorded in ``name_map``.)
|
|
678
|
+
TypeError: ``premise_names`` is one string rather than a sequence of strings, or
|
|
679
|
+
one of its entries is not a string.
|
|
680
|
+
TfaRefusal: a free variable, an arity conflict, or a constant-vs-function name
|
|
681
|
+
clash — see :func:`formula_to_tff_arith`. It is a ``ValueError`` as it
|
|
682
|
+
always was, and a ``NotImplementedError`` too, so that a backend reports
|
|
683
|
+
it as ``unknown`` / ``unsupported`` with this message.
|
|
684
|
+
ValueError: an invalid ``sort``; or ``premise_names`` holds other than one name
|
|
685
|
+
per premise, a name no TPTP name spells, two names that are the same as
|
|
686
|
+
written, or a name the writer gives one of its own lines.
|
|
687
|
+
"""
|
|
688
|
+
if sort not in TFA_SORT_TOKENS:
|
|
689
|
+
raise ValueError(_INVALID_SORT.format(sort=sort))
|
|
690
|
+
premises = list(premises)
|
|
691
|
+
premise_labels = normalise_premise_names(premise_names, len(premises),
|
|
692
|
+
where="generate_tff_arith_problem")
|
|
693
|
+
formulas = premises + ([] if conclusion is None else [conclusion])
|
|
694
|
+
for f in formulas:
|
|
695
|
+
_check_fragment(f)
|
|
696
|
+
_check_no_free_variables(f)
|
|
697
|
+
sanitised, name_map = _sanitize_for_tptp(formulas)
|
|
698
|
+
sanitised = [legalise_variables(f) for f in sanitised]
|
|
699
|
+
_check_no_symbol_collisions(sanitised, where="generate_tff_arith_problem")
|
|
700
|
+
_analyze(sanitised, check_flat_table=False) # errors name the caller's own symbols
|
|
701
|
+
sanitised, name_map = _separate_term_names(sanitised, name_map)
|
|
702
|
+
sig = _analyze(sanitised)
|
|
703
|
+
|
|
704
|
+
lines = _render_type_decls(sig, sort)
|
|
705
|
+
generated = [("goal", "the conjecture")] if conclusion is not None else []
|
|
706
|
+
for line in lines:
|
|
707
|
+
# tff(func_decl_1, type, ...): the name of each declaration the writer gave
|
|
708
|
+
generated.append((line[len("tff("):line.index(",")], "a type declaration"))
|
|
709
|
+
check_against_generated(premise_labels, generated, where="generate_tff_arith_problem")
|
|
710
|
+
for label, premise in zip(premise_labels, sanitised):
|
|
711
|
+
lines.append(f"tff({tptp_name_token(label)}, axiom, {_render(premise, sort)} ).")
|
|
712
|
+
if conclusion is not None:
|
|
713
|
+
lines.append(f"tff(goal, conjecture, {_render(sanitised[-1], sort)} ).")
|
|
714
|
+
name_map.premises = premise_labels
|
|
715
|
+
return "\n".join(lines) + "\n", name_map
|