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,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
|