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.
Files changed (237) hide show
  1. unicode_logic_kit/__init__.py +385 -0
  2. unicode_logic_kit/__main__.py +520 -0
  3. unicode_logic_kit/_deadline.py +219 -0
  4. unicode_logic_kit/ace/__init__.py +126 -0
  5. unicode_logic_kit/ace/_align.py +135 -0
  6. unicode_logic_kit/ace/chem_lexicon.py +128 -0
  7. unicode_logic_kit/ace/drs_reader.py +570 -0
  8. unicode_logic_kit/ace/mapping.py +666 -0
  9. unicode_logic_kit/ace/reverse_modal.py +138 -0
  10. unicode_logic_kit/ace/runner.py +551 -0
  11. unicode_logic_kit/ace/translate.py +452 -0
  12. unicode_logic_kit/ace/verbalize.py +1070 -0
  13. unicode_logic_kit/api.py +1284 -0
  14. unicode_logic_kit/atp/__init__.py +177 -0
  15. unicode_logic_kit/atp/_ascii_names.py +113 -0
  16. unicode_logic_kit/atp/_html.py +72 -0
  17. unicode_logic_kit/atp/_substructural_input.py +228 -0
  18. unicode_logic_kit/atp/_tff_problem.py +715 -0
  19. unicode_logic_kit/atp/_tptp_problem.py +1111 -0
  20. unicode_logic_kit/atp/_writer_support.py +289 -0
  21. unicode_logic_kit/atp/clingo_backend.py +1180 -0
  22. unicode_logic_kit/atp/cvc5_backend.py +1385 -0
  23. unicode_logic_kit/atp/eprover_backend.py +732 -0
  24. unicode_logic_kit/atp/finite_domain.py +1055 -0
  25. unicode_logic_kit/atp/fitch.py +1547 -0
  26. unicode_logic_kit/atp/fitch_search.py +551 -0
  27. unicode_logic_kit/atp/hets_backend.py +339 -0
  28. unicode_logic_kit/atp/hybrid_down.py +120 -0
  29. unicode_logic_kit/atp/incremental.py +250 -0
  30. unicode_logic_kit/atp/kripke_enum.py +741 -0
  31. unicode_logic_kit/atp/lambek.py +436 -0
  32. unicode_logic_kit/atp/leo3_backend.py +332 -0
  33. unicode_logic_kit/atp/linear.py +738 -0
  34. unicode_logic_kit/atp/lj.py +705 -0
  35. unicode_logic_kit/atp/logic_backends.py +566 -0
  36. unicode_logic_kit/atp/ltl_tableau.py +1084 -0
  37. unicode_logic_kit/atp/minizinc_backend.py +1402 -0
  38. unicode_logic_kit/atp/modal_tableau.py +1382 -0
  39. unicode_logic_kit/atp/nanocop_backend.py +410 -0
  40. unicode_logic_kit/atp/portfolio.py +489 -0
  41. unicode_logic_kit/atp/protocol.py +1803 -0
  42. unicode_logic_kit/atp/prover9_entailment.py +1153 -0
  43. unicode_logic_kit/atp/resolution.py +1376 -0
  44. unicode_logic_kit/atp/resolution_check.py +1114 -0
  45. unicode_logic_kit/atp/sequent.py +1050 -0
  46. unicode_logic_kit/atp/tableau.py +921 -0
  47. unicode_logic_kit/atp/tableau_check.py +543 -0
  48. unicode_logic_kit/atp/tptp_ncl.py +811 -0
  49. unicode_logic_kit/atp/tptp_tff.py +1546 -0
  50. unicode_logic_kit/atp/tstp.py +1333 -0
  51. unicode_logic_kit/atp/tstp_check.py +1096 -0
  52. unicode_logic_kit/atp/twee_backend.py +236 -0
  53. unicode_logic_kit/atp/twee_check.py +711 -0
  54. unicode_logic_kit/atp/twee_entailment.py +953 -0
  55. unicode_logic_kit/atp/vampire_entailment.py +540 -0
  56. unicode_logic_kit/atp/z3_arith.py +470 -0
  57. unicode_logic_kit/atp/z3_equivalence.py +36 -0
  58. unicode_logic_kit/atp/z3_fuzzy.py +362 -0
  59. unicode_logic_kit/atp/z3_input.py +500 -0
  60. unicode_logic_kit/atp/z3_models.py +208 -0
  61. unicode_logic_kit/chem/__init__.py +88 -0
  62. unicode_logic_kit/chem/_naming.py +284 -0
  63. unicode_logic_kit/chem/cache.py +185 -0
  64. unicode_logic_kit/chem/interop.py +244 -0
  65. unicode_logic_kit/chem/mol.py +525 -0
  66. unicode_logic_kit/chem/signature.py +112 -0
  67. unicode_logic_kit/comorphism.py +497 -0
  68. unicode_logic_kit/dl/__init__.py +384 -0
  69. unicode_logic_kit/dl/classification.py +227 -0
  70. unicode_logic_kit/dl/concepts.py +632 -0
  71. unicode_logic_kit/dl/datatypes.py +818 -0
  72. unicode_logic_kit/dl/owl_functional.py +2433 -0
  73. unicode_logic_kit/dl/owl_manchester.py +1637 -0
  74. unicode_logic_kit/dl/owl_reasoner.py +790 -0
  75. unicode_logic_kit/dl/parser.py +391 -0
  76. unicode_logic_kit/dl/tableau.py +4048 -0
  77. unicode_logic_kit/dl/translate.py +2704 -0
  78. unicode_logic_kit/drt/__init__.py +94 -0
  79. unicode_logic_kit/drt/export.py +179 -0
  80. unicode_logic_kit/drt/nodes.py +506 -0
  81. unicode_logic_kit/drt/parser.py +965 -0
  82. unicode_logic_kit/drt/resolve.py +195 -0
  83. unicode_logic_kit/drt/reverse.py +175 -0
  84. unicode_logic_kit/eval/__init__.py +106 -0
  85. unicode_logic_kit/eval/batch.py +382 -0
  86. unicode_logic_kit/eval/canonical.py +663 -0
  87. unicode_logic_kit/eval/chem_batch.py +606 -0
  88. unicode_logic_kit/eval/converses.py +200 -0
  89. unicode_logic_kit/eval/datasets/__init__.py +136 -0
  90. unicode_logic_kit/eval/datasets/_base.py +263 -0
  91. unicode_logic_kit/eval/datasets/_proofwriter_proof.py +422 -0
  92. unicode_logic_kit/eval/datasets/c3po.py +678 -0
  93. unicode_logic_kit/eval/datasets/folio.py +158 -0
  94. unicode_logic_kit/eval/datasets/fracas.py +418 -0
  95. unicode_logic_kit/eval/datasets/groves.py +191 -0
  96. unicode_logic_kit/eval/datasets/logicbench.py +467 -0
  97. unicode_logic_kit/eval/datasets/logicnli.py +303 -0
  98. unicode_logic_kit/eval/datasets/malls.py +133 -0
  99. unicode_logic_kit/eval/datasets/pfolio.py +594 -0
  100. unicode_logic_kit/eval/datasets/pmb.py +242 -0
  101. unicode_logic_kit/eval/datasets/prontoqa.py +611 -0
  102. unicode_logic_kit/eval/datasets/proofwriter.py +1431 -0
  103. unicode_logic_kit/eval/datasets/proverqa.py +674 -0
  104. unicode_logic_kit/eval/datasets/willow.py +478 -0
  105. unicode_logic_kit/eval/equivalence.py +466 -0
  106. unicode_logic_kit/eval/exercise_gen.py +533 -0
  107. unicode_logic_kit/eval/explain.py +791 -0
  108. unicode_logic_kit/eval/generality.py +750 -0
  109. unicode_logic_kit/eval/metric_hf.py +458 -0
  110. unicode_logic_kit/eval/predicate_match.py +343 -0
  111. unicode_logic_kit/eval/theory_check.py +1170 -0
  112. unicode_logic_kit/eval/validate.py +306 -0
  113. unicode_logic_kit/fol/__init__.py +177 -0
  114. unicode_logic_kit/fol/_atom_keys.py +510 -0
  115. unicode_logic_kit/fol/_fol_nodes.py +3586 -0
  116. unicode_logic_kit/fol/_free_parameters.py +105 -0
  117. unicode_logic_kit/fol/_ho_nodes.py +448 -0
  118. unicode_logic_kit/fol/_hybrid_nodes.py +308 -0
  119. unicode_logic_kit/fol/_identifiers.py +1091 -0
  120. unicode_logic_kit/fol/_lambek_nodes.py +112 -0
  121. unicode_logic_kit/fol/_linear_nodes.py +352 -0
  122. unicode_logic_kit/fol/_modal_nodes.py +1467 -0
  123. unicode_logic_kit/fol/_msfl_nodes.py +2196 -0
  124. unicode_logic_kit/fol/_numeral_symbols.py +231 -0
  125. unicode_logic_kit/fol/_so_nodes.py +200 -0
  126. unicode_logic_kit/fol/_symbol_names.py +81 -0
  127. unicode_logic_kit/fol/_team_nodes.py +181 -0
  128. unicode_logic_kit/fol/_tptp_symbols.py +551 -0
  129. unicode_logic_kit/fol/_truth_constants.py +117 -0
  130. unicode_logic_kit/fol/casl_export.py +1135 -0
  131. unicode_logic_kit/fol/casl_import.py +929 -0
  132. unicode_logic_kit/fol/derivation.py +367 -0
  133. unicode_logic_kit/fol/dialect_detect.py +70 -0
  134. unicode_logic_kit/fol/dialect_repair.py +537 -0
  135. unicode_logic_kit/fol/frames.py +637 -0
  136. unicode_logic_kit/fol/grammars/terminals.lark +31 -0
  137. unicode_logic_kit/fol/lambda_tools.py +297 -0
  138. unicode_logic_kit/fol/latex_input.py +429 -0
  139. unicode_logic_kit/fol/modal_translation.py +944 -0
  140. unicode_logic_kit/fol/msflparser.py +1033 -0
  141. unicode_logic_kit/fol/naming.py +422 -0
  142. unicode_logic_kit/fol/nodes.py +241 -0
  143. unicode_logic_kit/fol/normalforms.py +492 -0
  144. unicode_logic_kit/fol/pal.py +287 -0
  145. unicode_logic_kit/fol/prolog_export.py +566 -0
  146. unicode_logic_kit/fol/prolog_input.py +505 -0
  147. unicode_logic_kit/fol/prover9_input.py +1325 -0
  148. unicode_logic_kit/fol/qml.py +1760 -0
  149. unicode_logic_kit/fol/qmltp_input.py +525 -0
  150. unicode_logic_kit/fol/sanitize.py +221 -0
  151. unicode_logic_kit/fol/serialize.py +79 -0
  152. unicode_logic_kit/fol/signature.py +1290 -0
  153. unicode_logic_kit/fol/simplify_check.py +544 -0
  154. unicode_logic_kit/fol/spans.py +594 -0
  155. unicode_logic_kit/fol/tptp_input.py +1503 -0
  156. unicode_logic_kit/fol/tptp_repair.py +941 -0
  157. unicode_logic_kit/fol/unification.py +157 -0
  158. unicode_logic_kit/fol/verbalize.py +263 -0
  159. unicode_logic_kit/hets/__init__.py +163 -0
  160. unicode_logic_kit/hets/bridge.py +142 -0
  161. unicode_logic_kit/hets/client.py +748 -0
  162. unicode_logic_kit/hets/docker.py +420 -0
  163. unicode_logic_kit/hets/dol.py +712 -0
  164. unicode_logic_kit/hets/haskell_json.py +355 -0
  165. unicode_logic_kit/hets/owl_backend.py +794 -0
  166. unicode_logic_kit/hets/owl_cli.py +598 -0
  167. unicode_logic_kit/hets/symbols.py +512 -0
  168. unicode_logic_kit/hol/__init__.py +140 -0
  169. unicode_logic_kit/hol/_ho_common.py +323 -0
  170. unicode_logic_kit/hol/_isabelle_binders.py +125 -0
  171. unicode_logic_kit/hol/classical.py +812 -0
  172. unicode_logic_kit/hol/deepshallow/__init__.py +45 -0
  173. unicode_logic_kit/hol/deepshallow/_common.py +177 -0
  174. unicode_logic_kit/hol/deepshallow/conditional.py +225 -0
  175. unicode_logic_kit/hol/deepshallow/intuitionistic.py +181 -0
  176. unicode_logic_kit/hol/deepshallow/modal.py +217 -0
  177. unicode_logic_kit/hol/deepshallow/qml.py +406 -0
  178. unicode_logic_kit/hol/deepshallow/relevant.py +206 -0
  179. unicode_logic_kit/hol/free.py +753 -0
  180. unicode_logic_kit/hol/goedel.py +336 -0
  181. unicode_logic_kit/hol/ho_modal.py +1743 -0
  182. unicode_logic_kit/hol/intuitionistic.py +403 -0
  183. unicode_logic_kit/hol/isabelle_conditional.py +593 -0
  184. unicode_logic_kit/hol/isabelle_modal.py +1908 -0
  185. unicode_logic_kit/hol/isabelle_relevant.py +412 -0
  186. unicode_logic_kit/hol/isabelle_runner.py +1147 -0
  187. unicode_logic_kit/hol/isabelle_substructural.py +884 -0
  188. unicode_logic_kit/hol/lean.py +1018 -0
  189. unicode_logic_kit/hol/manyvalued.py +921 -0
  190. unicode_logic_kit/hol/secondorder.py +687 -0
  191. unicode_logic_kit/hol/thf_modal.py +941 -0
  192. unicode_logic_kit/hol/thirdorder.py +397 -0
  193. unicode_logic_kit/ilp/__init__.py +89 -0
  194. unicode_logic_kit/ilp/readback.py +389 -0
  195. unicode_logic_kit/ilp/separation.py +153 -0
  196. unicode_logic_kit/ilp/task.py +730 -0
  197. unicode_logic_kit/logic.py +163 -0
  198. unicode_logic_kit/mcp/__init__.py +28 -0
  199. unicode_logic_kit/mcp/__main__.py +5 -0
  200. unicode_logic_kit/mcp/chem_tools.py +1031 -0
  201. unicode_logic_kit/mcp/server.py +2453 -0
  202. unicode_logic_kit/mcp/syntax_spec.py +681 -0
  203. unicode_logic_kit/prob/__init__.py +53 -0
  204. unicode_logic_kit/prob/_bdd.py +225 -0
  205. unicode_logic_kit/prob/_column_gen.py +668 -0
  206. unicode_logic_kit/prob/distribution.py +686 -0
  207. unicode_logic_kit/prob/nilsson.py +470 -0
  208. unicode_logic_kit/py.typed +0 -0
  209. unicode_logic_kit/semantics/__init__.py +137 -0
  210. unicode_logic_kit/semantics/_modal_reject.py +156 -0
  211. unicode_logic_kit/semantics/action_models.py +466 -0
  212. unicode_logic_kit/semantics/asp_models.py +1200 -0
  213. unicode_logic_kit/semantics/conditional.py +580 -0
  214. unicode_logic_kit/semantics/dynamic_epistemic.py +95 -0
  215. unicode_logic_kit/semantics/free_logic.py +913 -0
  216. unicode_logic_kit/semantics/fuzzy.py +384 -0
  217. unicode_logic_kit/semantics/fuzzy_kripke.py +442 -0
  218. unicode_logic_kit/semantics/intuitionistic.py +581 -0
  219. unicode_logic_kit/semantics/kripke.py +1139 -0
  220. unicode_logic_kit/semantics/manyvalued.py +580 -0
  221. unicode_logic_kit/semantics/matrix.py +342 -0
  222. unicode_logic_kit/semantics/model_eval.py +1135 -0
  223. unicode_logic_kit/semantics/modelfinder.py +1036 -0
  224. unicode_logic_kit/semantics/nonmonotonic.py +372 -0
  225. unicode_logic_kit/semantics/relevant.py +331 -0
  226. unicode_logic_kit/semantics/secondorder.py +657 -0
  227. unicode_logic_kit/semantics/structures.py +352 -0
  228. unicode_logic_kit/semantics/tarski.py +975 -0
  229. unicode_logic_kit/semantics/team.py +315 -0
  230. unicode_logic_kit/semantics/team_translation.py +416 -0
  231. unicode_logic_kit/semantics/thirdorder.py +358 -0
  232. unicode_logic_kit/semantics/tnorm.py +85 -0
  233. unicode_logic_kit/semantics/truthtable.py +201 -0
  234. unicode_logic_kit-0.31.0.dist-info/METADATA +333 -0
  235. unicode_logic_kit-0.31.0.dist-info/RECORD +237 -0
  236. unicode_logic_kit-0.31.0.dist-info/WHEEL +4 -0
  237. 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