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,470 @@
1
+ """Arithmetic-aware Z3 translation (Real/Int) and SMT solving over the theory of numbers.
2
+
3
+ The default ``Node.to_z3`` translates every term into a single *uninterpreted*
4
+ sort, so arithmetic is opaque to it: ``x + 1 = 2 ∧ x > 0`` cannot be solved and
5
+ ``∀x (x * 2 = x + x)`` cannot be proved valid, because ``+``, ``*``, ``>`` and the
6
+ numeric literals carry no meaning. This module instead interprets numbers,
7
+ arithmetic functions, and comparisons in Z3's theory of reals (or integers), so
8
+ those formulas are decided by genuine arithmetic reasoning.
9
+
10
+ Translation rules (see ``to_z3_arith``):
11
+
12
+ * ``Number`` → ``z3.RealVal``/``z3.IntVal``; a numeral that is not an
13
+ integer (``2.5``) has no integer literal and is refused by name under ``sort='int'``
14
+ (``NotImplementedError``), never read as its integer part.
15
+ * ``Constant`` → a Real/Int constant named after the symbol.
16
+ * ``Variable`` → a Real/Int constant of its own, named ``x!v`` for
17
+ the variable ``x`` (see below).
18
+ * ``Function`` ``+ - * /`` (binary) → the matching Z3 arithmetic operator.
19
+ * ``Function`` ``-`` (one argument) → the negation of its argument: ``-t`` is ``0 - t``. This is
20
+ the node the Prover9 and SMT-LIB readers build for ``-t`` and ``(- t)``, and what the typed
21
+ TPTP writer writes as ``$uminus``.
22
+ * other ``Function`` → an uninterpreted function over the numeric sort (also
23
+ ``+``, ``*``, ``/`` at a number of arguments other than two, and ``-`` at three or more).
24
+ * ``Atom`` ``= ≠ < > ≤ ≥`` (binary) → the matching Z3 comparison.
25
+ * other ``Atom`` (predicate) → an uninterpreted (numeric-sort)→Bool function.
26
+ * ``Not/And/Or/Xor/Implies/Iff`` → the matching Z3 connective.
27
+ * ``Quantifier`` → ``z3.ForAll``/``z3.Exists`` over the bound
28
+ variable's numeric constant.
29
+
30
+ **A variable is a symbol of its own.** A :class:`~unicode_logic_kit.fol.nodes.Variable` and
31
+ a :class:`~unicode_logic_kit.fol.nodes.Constant` of one name are two symbols, in every
32
+ position, exactly as they are in :class:`~unicode_logic_kit.fol.nodes.Z3Env`: with
33
+ ``c = Constant('x')`` the quantifier of ``∀x x ≤ c`` binds the variable and leaves ``c``
34
+ alone, so the formula says that ``c`` is an upper bound of every number and is not valid.
35
+ The naming is :func:`~unicode_logic_kit.fol._fol_nodes.z3_variable_name` (``x!v``) for a
36
+ variable and :func:`~unicode_logic_kit.fol._fol_nodes.z3_constant_name` for a constant (its
37
+ own name), the one the other Z3 routes use. A variable that a sort guard or a counting
38
+ expansion mints is a variable like any other, so it never meets a user constant spelled
39
+ like it. A model reports a constant under its plain name, and a free variable under its
40
+ own name too (``x!v`` only when a constant ``x`` is declared as well).
41
+
42
+ A counting quantifier (``∃≥n x φ``, ``∃≤n``, ``∃=n``) is expanded to its distinct-witnesses
43
+ formula (:meth:`~unicode_logic_kit.fol.nodes.Count._expand`), as the default route expands it;
44
+ a sorted one, and the sorted quantifiers, sorted constants and Łukasiewicz operators, are
45
+ first lowered to classical FOL with :func:`unicode_logic_kit.fol.nodes.to_fol`; the sort
46
+ guards introduced there become uninterpreted predicates over the numeric sort. A sorted constant
47
+ ``c:S`` inside an atom or a function is the SAME numeric symbol as the plain
48
+ constant ``c`` (there is one constant, whatever it is annotated with). Lambda
49
+ nodes have no first-order meaning and raise ``TypeError`` (beta-reduce and
50
+ lambda-eliminate them first); a ``SortedCardinality`` term is a set cardinality,
51
+ which has no first-order reading, and is refused by name with
52
+ ``NotImplementedError``.
53
+
54
+ **Many-sorted (MSFOL) soundness.** A sort guard is just an uninterpreted
55
+ predicate over the (real/int) numeric sort once lowered — nothing stops Z3
56
+ from interpreting it as always-false, even though the numeric domain itself
57
+ is infinite, and MSFOL, by convention, never gives a sort an EMPTY universe
58
+ (see the classical-reasoning guide's many-sorted section). Lowering likewise
59
+ reads a sorted constant ``c:S`` as the plain constant ``c``, which forgets that
60
+ ``c`` lies in ``S``. The three `*_arith` decision functions below close both
61
+ gaps exactly like :mod:`unicode_logic_kit.atp.z3_models` does for the default
62
+ uninterpreted-sort translation: they add
63
+ ``unicode_logic_kit.fol._msfl_nodes.sort_axioms(formula)`` (one ``∃x S(x)`` per
64
+ sort, one ``S(c)`` per sorted constant), translated with :func:`to_z3_arith`
65
+ through the SAME :class:`ArithEnv` as ``formula`` itself (so a sort's guard
66
+ predicate and a constant are the identical Z3 declaration in both places), as
67
+ extra unconditional assertions — never folded inside :func:`to_z3_arith`
68
+ itself, which stays polarity-blind and shared with every other caller. Empty
69
+ for an unsorted formula, so behaviour there is unchanged.
70
+ """
71
+
72
+ from typing import Dict, List, Optional, Tuple
73
+
74
+ import z3
75
+
76
+ from ..fol._fol_nodes import check_z3_name, z3_constant_name, z3_variable_name
77
+ from ..fol._msfl_nodes import sort_axioms
78
+ from ..fol._tptp_symbols import is_tptp_boolean_atom as _is_tptp_boolean_atom
79
+ from ..fol._tptp_symbols import truth_constant_word as _truth_constant_word
80
+ from ..fol.nodes import (
81
+ Node, to_fol,
82
+ Variable, Constant, Count, Number, Function,
83
+ Atom, Not, And, Or, Xor, Implies, Iff, Quantifier,
84
+ SortedQuantifier, SortedConstant, SortedCount, SortedCardinality,
85
+ WeakConjunction, WeakDisjunction, StrongConjunction, StrongDisjunction,
86
+ LukNegation, LukImplication, LukEquivalence,
87
+ LambdaVar, Lambda, Application,
88
+ )
89
+ from .z3_models import model_assignment
90
+
91
+
92
+ # Node types that must be eliminated (lowered to classical FOL) before the
93
+ # arithmetic translation can run.
94
+ _MSFL_NODES = (
95
+ SortedQuantifier, SortedConstant, SortedCount,
96
+ WeakConjunction, WeakDisjunction, StrongConjunction, StrongDisjunction,
97
+ LukNegation, LukImplication, LukEquivalence,
98
+ )
99
+
100
+ # Lambda-calculus nodes have no first-order arithmetic meaning.
101
+ _LAMBDA_NODES = (LambdaVar, Lambda, Application)
102
+
103
+ _ARITH_OPS = frozenset({"+", "-", "*", "/"})
104
+
105
+
106
+ def _refuse_sorted_cardinality(node: SortedCardinality):
107
+ """Raise the refusal for a ``SortedCardinality`` term, which names it and says why."""
108
+ raise NotImplementedError(
109
+ f"to_z3_arith: {type(node).__name__} ('|{{{node.variable.name}:{node.sort} : φ}}|') "
110
+ "is a set cardinality, a second-order notion with no first-order counterpart. "
111
+ "Express a fixed-bound sorted count with the SortedCount quantifier "
112
+ "(∃≥n / ∃≤n / ∃=n x:S) instead.")
113
+
114
+
115
+ class ArithEnv:
116
+ """Caches Z3 declarations for an arithmetic translation over one numeric sort.
117
+
118
+ A single environment is threaded through a whole formula so that every
119
+ occurrence of a name maps to the same Z3 declaration. The numeric sort is
120
+ fixed at construction: ``'real'`` (the default) uses ``z3.RealSort`` and
121
+ ``'int'`` uses ``z3.IntSort``.
122
+
123
+ A function and a predicate are keyed on ``(name, arity)``, each in a table
124
+ of its own, exactly as in :class:`~unicode_logic_kit.fol.nodes.Z3Env`: one
125
+ name at two arities is two symbols. A numeral is the value it names here
126
+ (``z3.RealVal``), never a symbol, so a constant named ``1`` is a symbol
127
+ that is not the number 1 and the numeral/constant refusal of ``Z3Env`` has
128
+ no counterpart in this translation.
129
+
130
+ A :class:`~unicode_logic_kit.fol.nodes.Variable` is a symbol of its own: the variable ``x``
131
+ and the constant ``x`` are two Z3 constants (``x!v`` and ``x``), kept in two tables, so
132
+ a quantifier over ``x`` never binds the constant of that name. A function of no
133
+ arguments is the constant of its name.
134
+ """
135
+
136
+ def __init__(self, sort: str = "real"):
137
+ """Initialise empty symbol/variable/function/predicate tables for the given sort.
138
+
139
+ Args:
140
+ sort: ``'real'`` or ``'int'``; selects the Z3 numeric sort that every
141
+ term lives in. Any other value raises ``ValueError``.
142
+ """
143
+ if sort not in ("real", "int"):
144
+ raise ValueError(f"sort must be 'real' or 'int', got {sort!r}")
145
+ self.sort_name = sort
146
+ self.sort = z3.RealSort() if sort == "real" else z3.IntSort()
147
+ self.symbols: Dict[str, z3.ExprRef] = {}
148
+ self.variables: Dict[str, z3.ExprRef] = {}
149
+ self.funcs: Dict[Tuple[str, int], z3.FuncDeclRef] = {}
150
+ self.preds: Dict[Tuple[str, int], z3.FuncDeclRef] = {}
151
+
152
+ def num(self, value) -> z3.ExprRef:
153
+ """Return a Z3 numeric literal for ``value`` in this environment's sort.
154
+
155
+ Raises:
156
+ NotImplementedError: the sort is ``'int'`` and ``value`` is not an integer. No
157
+ integer is ``2.5``: Z3 would take the integer part of it and read the
158
+ numeral as ``2``, and ``2.5 = 2`` would be valid. Pass ``sort='real'``, or
159
+ write a whole number.
160
+ """
161
+ if self.sort_name == "real":
162
+ return z3.RealVal(value)
163
+ if not isinstance(value, int):
164
+ raise NotImplementedError(
165
+ f"to_z3_arith: the numeral {value!r} has a fractional part, so it is no "
166
+ "integer and has no literal under sort='int' (reading it as its integer part "
167
+ "would make 2.5 = 2 valid). Pass sort='real', or write a whole number.")
168
+ return z3.IntVal(value)
169
+
170
+ def get_symbol(self, name: str) -> z3.ExprRef:
171
+ """Get or create the Z3 numeric constant of the CONSTANT ``name``.
172
+
173
+ Named as the constant is (:func:`~unicode_logic_kit.fol._fol_nodes.z3_constant_name`).
174
+ A variable is not asked for here (:meth:`get_variable`).
175
+ """
176
+ if name not in self.symbols:
177
+ check_z3_name(name)
178
+ self.symbols[name] = z3.Const(z3_constant_name(name), self.sort)
179
+ return self.symbols[name]
180
+
181
+ def get_variable(self, name: str) -> z3.ExprRef:
182
+ """Get or create the Z3 numeric constant that stands for the VARIABLE ``name``.
183
+
184
+ A symbol of its own, named by :func:`~unicode_logic_kit.fol._fol_nodes.z3_variable_name`
185
+ and apart from the constant of the same name, so that a quantifier over it never
186
+ binds that constant.
187
+ """
188
+ if name not in self.variables:
189
+ check_z3_name(name)
190
+ self.variables[name] = z3.Const(z3_variable_name(name), self.sort)
191
+ return self.variables[name]
192
+
193
+ def get_func(self, name: str, arity: int) -> z3.FuncDeclRef:
194
+ """Get or create an uninterpreted function mapping (numeric sort)^arity → numeric sort.
195
+
196
+ Keyed on ``(name, arity)``: one name at two arities is two functions. A function
197
+ of no arguments is the constant of its name, so it takes the constant's Z3 name.
198
+ """
199
+ key = (name, arity)
200
+ if key not in self.funcs:
201
+ check_z3_name(name)
202
+ z3_name = z3_constant_name(name) if arity == 0 else name
203
+ self.funcs[key] = z3.Function(z3_name, *([self.sort] * arity), self.sort)
204
+ return self.funcs[key]
205
+
206
+ def get_pred(self, name: str, arity: int) -> z3.FuncDeclRef:
207
+ """Get or create an uninterpreted predicate mapping (numeric sort)^arity → Bool.
208
+
209
+ Keyed on ``(name, arity)``: one name at two arities is two predicates.
210
+ """
211
+ key = (name, arity)
212
+ if key not in self.preds:
213
+ check_z3_name(name)
214
+ self.preds[key] = z3.Function(name, *([self.sort] * arity), z3.BoolSort())
215
+ return self.preds[key]
216
+
217
+
218
+ def _term_to_z3(node: Node, env: ArithEnv) -> z3.ExprRef:
219
+ """Translate a term-position node (number, symbol, or arithmetic/uninterpreted function)."""
220
+ if isinstance(node, Number):
221
+ return env.num(node.value)
222
+ if isinstance(node, Variable):
223
+ return env.get_variable(node.name)
224
+ if isinstance(node, Constant):
225
+ return env.get_symbol(node.name)
226
+ if isinstance(node, SortedConstant):
227
+ # One constant, whatever it is annotated with: ``c:S`` and plain ``c`` are the
228
+ # same numeric symbol. (That ``c`` lies in ``S`` is a fact the decision
229
+ # functions assert next to the formula, see ``_sort_assertions``.)
230
+ return env.get_symbol(node.name)
231
+ if isinstance(node, SortedCardinality):
232
+ _refuse_sorted_cardinality(node)
233
+ if isinstance(node, Function):
234
+ args = [_term_to_z3(a, env) for a in node.args]
235
+ if node.name in _ARITH_OPS and len(args) == 2:
236
+ left, right = args
237
+ if node.name == "+":
238
+ return left + right
239
+ if node.name == "-":
240
+ return left - right
241
+ if node.name == "*":
242
+ return left * right
243
+ return left / right # node.name == "/"
244
+ if node.name == "-" and len(args) == 1:
245
+ # The negation, not a function symbol of its own: ``∀x (-x + x = 0)`` is valid.
246
+ return -args[0]
247
+ return env.get_func(node.name, len(node.args))(*args)
248
+ if isinstance(node, _LAMBDA_NODES):
249
+ raise TypeError(
250
+ "Lambda terms have no arithmetic meaning; beta-reduce and "
251
+ "lambda-eliminate before to_z3_arith."
252
+ )
253
+ raise TypeError(f"to_z3_arith: cannot translate term node {type(node).__name__}")
254
+
255
+
256
+ def to_z3_arith(node: Node, env: Optional[ArithEnv] = None, sort: str = "real") -> z3.ExprRef:
257
+ """Translate a formula/term node into an arithmetic-interpreted Z3 expression.
258
+
259
+ Numbers, the binary arithmetic functions ``+ - * /``, and the comparisons
260
+ ``= ≠ < > ≤ ≥`` are mapped to Z3's interpreted Real/Int operations; all other
261
+ functions and predicates become uninterpreted symbols over the numeric sort.
262
+ Sorted and Łukasiewicz nodes are lowered with :func:`to_fol` first (the
263
+ resulting sort guards become uninterpreted predicates). Lambda nodes raise
264
+ ``TypeError``.
265
+
266
+ Args:
267
+ node: the AST node to translate.
268
+ env: an :class:`ArithEnv` to thread declarations through; created from
269
+ ``sort`` when omitted. Pass an explicit env to share symbols across
270
+ several translations.
271
+ sort: ``'real'`` (default) or ``'int'``; only consulted when ``env`` is
272
+ ``None``.
273
+
274
+ Returns:
275
+ A Z3 expression — Bool for formula nodes, numeric for term nodes.
276
+
277
+ Raises:
278
+ NotImplementedError: the sort is ``'int'`` and a numeral is not an integer (``2.5``),
279
+ or the node is a ``SortedCardinality``.
280
+ """
281
+ if env is None:
282
+ env = ArithEnv(sort)
283
+
284
+ # Lower sorted / Łukasiewicz constructs to classical FOL up front, then
285
+ # translate the result. (to_fol turns sorts into ordinary predicates, which
286
+ # this translation treats as uninterpreted numeric-sort predicates.)
287
+ if isinstance(node, _MSFL_NODES):
288
+ return to_z3_arith(to_fol(node), env)
289
+
290
+ # A counting quantifier is first-order: it is the distinct-witnesses formula, which is what
291
+ # the default route translates (:meth:`Count.to_z3`). The witnesses are variables, so they
292
+ # are symbols ``x0!v`` of their own here and meet no constant, predicate or function.
293
+ if isinstance(node, Count):
294
+ return to_z3_arith(node._expand(), env)
295
+
296
+ if isinstance(node, SortedCardinality):
297
+ _refuse_sorted_cardinality(node)
298
+
299
+ # Term-position nodes.
300
+ if isinstance(node, (Number, Variable, Constant, Function)):
301
+ return _term_to_z3(node, env)
302
+
303
+ if isinstance(node, Atom):
304
+ if _is_tptp_boolean_atom(node): # TPTP's defined propositions
305
+ return z3.BoolVal(_truth_constant_word(node) == "$true")
306
+ if node.predicate in ("=", "≠", "<", ">", "≤", "≥") and len(node.args) == 2:
307
+ left = _term_to_z3(node.args[0], env)
308
+ right = _term_to_z3(node.args[1], env)
309
+ if node.predicate == "=":
310
+ return left == right
311
+ if node.predicate == "≠":
312
+ return left != right
313
+ if node.predicate == "<":
314
+ return left < right
315
+ if node.predicate == ">":
316
+ return left > right
317
+ if node.predicate == "≤":
318
+ return left <= right
319
+ return left >= right # "≥"
320
+ pred = env.get_pred(node.predicate, len(node.args))
321
+ return pred(*[_term_to_z3(a, env) for a in node.args])
322
+
323
+ if isinstance(node, Not):
324
+ return z3.Not(to_z3_arith(node.formula, env))
325
+ if isinstance(node, And):
326
+ return z3.And(to_z3_arith(node.left, env), to_z3_arith(node.right, env))
327
+ if isinstance(node, Or):
328
+ return z3.Or(to_z3_arith(node.left, env), to_z3_arith(node.right, env))
329
+ if isinstance(node, Xor):
330
+ return z3.Xor(to_z3_arith(node.left, env), to_z3_arith(node.right, env))
331
+ if isinstance(node, Implies):
332
+ return z3.Implies(to_z3_arith(node.left, env), to_z3_arith(node.right, env))
333
+ if isinstance(node, Iff):
334
+ return to_z3_arith(node.left, env) == to_z3_arith(node.right, env)
335
+
336
+ if isinstance(node, Quantifier):
337
+ z3_var = env.get_variable(node.variable.name)
338
+ body = to_z3_arith(node.formula, env)
339
+ if node.type in ("forall", "∀"):
340
+ return z3.ForAll([z3_var], body)
341
+ if node.type in ("exists", "∃"):
342
+ return z3.Exists([z3_var], body)
343
+ raise ValueError(f"Unknown quantifier: {node.type}")
344
+
345
+ if isinstance(node, _LAMBDA_NODES):
346
+ raise TypeError(
347
+ "Lambda terms have no arithmetic meaning; beta-reduce and "
348
+ "lambda-eliminate before to_z3_arith."
349
+ )
350
+
351
+ raise TypeError(f"to_z3_arith: unknown node type {type(node).__name__}")
352
+
353
+
354
+ def _solver(timeout: int) -> z3.Solver:
355
+ """Create a Z3 solver with a deterministic seed and the given millisecond timeout."""
356
+ solver = z3.Solver()
357
+ solver.set("timeout", timeout)
358
+ solver.set("random_seed", 42)
359
+ return solver
360
+
361
+
362
+ def _sort_assertions(formula: Node, env: ArithEnv):
363
+ """Translate ``sort_axioms(formula)`` through ``env``.
364
+
365
+ Threading the SAME env through the axioms and ``formula`` itself is what
366
+ makes a sort's guard predicate and a sorted constant resolve to the
367
+ identical Z3 declaration in both places — see the module docstring. Empty
368
+ for an unsorted formula.
369
+ """
370
+ return [to_z3_arith(axiom, env) for axiom in sort_axioms(formula)]
371
+
372
+
373
+ def _model_assignment(model: z3.ModelRef, env: ArithEnv) -> Dict[str, str]:
374
+ """Read a satisfying ``z3.ModelRef`` into ``{key: interpretation}`` under the kit's names.
375
+
376
+ The keys are those of :func:`unicode_logic_kit.atp.z3_models.model_assignment`, with the
377
+ numeric constants decoded the way that function decodes the constants of the
378
+ uninterpreted sort: a constant is under its plain name; a free variable (a symbol of its
379
+ own) is under its own name too, and as ``x!v`` when a constant ``x`` is declared as well.
380
+ """
381
+ return model_assignment(model, ranges=(str(env.sort),))
382
+
383
+
384
+ def is_satisfiable_arith(formula: Node, sort: str = "real", timeout: int = 10000) -> bool:
385
+ """Return True iff ``formula`` has a model under interpreted arithmetic.
386
+
387
+ Translates with :func:`to_z3_arith` and asks Z3 for a model. A Z3 ``unknown``
388
+ result (e.g. a hard quantified formula hitting ``timeout`` milliseconds) is
389
+ treated as not-known-satisfiable and returns False. A many-sorted
390
+ ``formula``'s sorts are asserted non-empty, and its sorted constants are
391
+ asserted to lie in their sorts, alongside it — see the module docstring.
392
+
393
+ Args:
394
+ formula: the formula to check.
395
+ sort: ``'real'`` (default) or ``'int'`` numeric sort.
396
+ timeout: solver timeout in milliseconds.
397
+
398
+ Raises:
399
+ NotImplementedError: ``sort`` is ``'int'`` and a numeral of ``formula`` is not an
400
+ integer (``2.5``): it has no integer literal, and is refused rather than read as
401
+ its integer part (see :meth:`ArithEnv.num`).
402
+ """
403
+ solver = _solver(timeout)
404
+ env = ArithEnv(sort)
405
+ solver.add(to_z3_arith(formula, env))
406
+ for assertion in _sort_assertions(formula, env):
407
+ solver.add(assertion)
408
+ return solver.check() == z3.sat
409
+
410
+
411
+ def is_valid_arith(formula: Node, sort: str = "real", timeout: int = 10000) -> bool:
412
+ """Return True iff ``formula`` is valid under interpreted arithmetic.
413
+
414
+ A formula is valid exactly when its negation is unsatisfiable, so this asks
415
+ Z3 to refute ``¬formula``. A Z3 ``unknown`` result (e.g. hitting ``timeout``
416
+ milliseconds) is treated as not-known-valid and returns False. A
417
+ many-sorted ``formula``'s sorts are asserted non-empty and its sorted
418
+ constants are asserted to lie in their sorts, as extra, UNNEGATED premises
419
+ alongside the negated goal — see the module docstring.
420
+
421
+ Args:
422
+ formula: the formula to check.
423
+ sort: ``'real'`` (default) or ``'int'`` numeric sort.
424
+ timeout: solver timeout in milliseconds.
425
+
426
+ Raises:
427
+ NotImplementedError: ``sort`` is ``'int'`` and a numeral of ``formula`` is not an
428
+ integer (``2.5``): it has no integer literal, and is refused rather than read as
429
+ its integer part (see :meth:`ArithEnv.num`).
430
+ """
431
+ solver = _solver(timeout)
432
+ env = ArithEnv(sort)
433
+ for assertion in _sort_assertions(formula, env):
434
+ solver.add(assertion)
435
+ solver.add(z3.Not(to_z3_arith(formula, env)))
436
+ return solver.check() == z3.unsat
437
+
438
+
439
+ def get_model_arith(formula: Node, sort: str = "real", timeout: int = 10000) -> Optional[dict]:
440
+ """Return a satisfying assignment under interpreted arithmetic, or None.
441
+
442
+ On ``sat``, returns a dict mapping each Z3 declaration name (numeric
443
+ constants, plus any uninterpreted functions/predicates) to the string form of
444
+ its interpretation — e.g. ``{"x": "1"}`` for ``x + 1 = 2 ∧ x > 0``. A constant
445
+ is reported under its plain name; a free variable is a symbol of its own and is
446
+ reported under its name too, as ``x!v`` only when a constant ``x`` is part of the
447
+ formula as well. Returns
448
+ None when the formula is unsatisfiable or Z3 cannot decide it within
449
+ ``timeout`` milliseconds. A many-sorted ``formula``'s sorts are asserted
450
+ non-empty, and its sorted constants are asserted to lie in their sorts,
451
+ alongside it — see the module docstring.
452
+
453
+ Args:
454
+ formula: the formula to solve.
455
+ sort: ``'real'`` (default) or ``'int'`` numeric sort.
456
+ timeout: solver timeout in milliseconds.
457
+
458
+ Raises:
459
+ NotImplementedError: ``sort`` is ``'int'`` and a numeral of ``formula`` is not an
460
+ integer (``2.5``): it has no integer literal, and is refused rather than read as
461
+ its integer part (see :meth:`ArithEnv.num`).
462
+ """
463
+ solver = _solver(timeout)
464
+ env = ArithEnv(sort)
465
+ solver.add(to_z3_arith(formula, env))
466
+ for assertion in _sort_assertions(formula, env):
467
+ solver.add(assertion)
468
+ if solver.check() != z3.sat:
469
+ return None
470
+ return _model_assignment(solver.model(), env)
@@ -0,0 +1,36 @@
1
+ from ..fol.nodes import Node, Z3Env
2
+ from ..fol._msfl_nodes import sort_axioms
3
+ from z3 import Solver, unsat, set_param, Not
4
+
5
+
6
+ def formulas_are_equivalent(formula1: Node, formula2: Node, timeout: int=10000) -> bool:
7
+ """Return True iff ``formula1`` and ``formula2`` are logically equivalent.
8
+
9
+ Asks Z3 to refute ¬(φ ↔ ψ): the two formulas are equivalent exactly when
10
+ that negation is unsatisfiable. The arguments are interchangeable (the check
11
+ is symmetric). Returns False if Z3 finds a model of the negation (the
12
+ formulas differ) or returns ``unknown`` within ``timeout`` milliseconds.
13
+
14
+ Every sort occurring in either formula is asserted non-empty, and every
15
+ sorted constant ``c:S`` of either formula is asserted to lie in ``S``,
16
+ alongside the negation, as :func:`~unicode_logic_kit.atp.z3_models.is_valid`
17
+ does: the equivalence is decided over legal many-sorted structures, not
18
+ over ones that make a sort empty or put a constant outside its sort.
19
+ """
20
+
21
+ env = Z3Env() # one environment for both formulas and the facts
22
+ phi = formula1.to_z3(env)
23
+ psi = formula2.to_z3(env)
24
+
25
+ solver = Solver()
26
+ solver.set("timeout", timeout)
27
+ solver.set("random_seed", 42)
28
+ for axiom in sort_axioms(formula1, formula2):
29
+ solver.add(axiom.to_z3(env))
30
+ solver.add(Not(phi==psi))
31
+
32
+ result = solver.check()
33
+ if result == unsat:
34
+ return True
35
+
36
+ return False