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,452 @@
|
|
|
1
|
+
"""ACE text → ONE kit formula — including what a classical DRS cannot hold.
|
|
2
|
+
|
|
3
|
+
The DRS route (:func:`unicode_logic_kit.ace.mapping.ace_to_drs`) stops honestly
|
|
4
|
+
at modality and questions: the kit's classical DRS core has no place for
|
|
5
|
+
them. THIS route translates APE's DRS straight to a kit FORMULA, where the
|
|
6
|
+
kit does have places — the modal family for ACE's four modal boxes, and an
|
|
7
|
+
open formula for a wh-question:
|
|
8
|
+
|
|
9
|
+
- ``must`` → ``□`` (:class:`~unicode_logic_kit.fol._modal_nodes.Box`) —
|
|
10
|
+
Attempto's reading: necessity;
|
|
11
|
+
- ``can`` → ``◇`` (:class:`~unicode_logic_kit.fol._modal_nodes.Diamond`) —
|
|
12
|
+
possibility;
|
|
13
|
+
- ``should`` → ``Ⓞ`` (:class:`~unicode_logic_kit.fol._modal_nodes.Obligatory`)
|
|
14
|
+
— recommendation, read deontically;
|
|
15
|
+
- ``may`` → ``Ⓟ`` (:class:`~unicode_logic_kit.fol._modal_nodes.Permitted`) —
|
|
16
|
+
admissibility, read deontically.
|
|
17
|
+
|
|
18
|
+
The first two are alethic, the second two deontic — that split is a
|
|
19
|
+
DOCUMENTED CHOICE, not something ACE fixes: Attempto glosses ``should``/
|
|
20
|
+
``may`` as recommendation/admissibility, which is what the kit's deontic
|
|
21
|
+
operators mean, while ``must``/``can`` gloss as necessity/possibility. A
|
|
22
|
+
caller who wants "must" read as obligation can rewrite ``Box`` → ``Obligatory``
|
|
23
|
+
on the result; nothing here is lost, only labeled.
|
|
24
|
+
|
|
25
|
+
The translation is the standard Kamp/Reyle one — the same
|
|
26
|
+
:func:`unicode_logic_kit.drt.export.drs_to_fol` performs on the pure fragment,
|
|
27
|
+
extended pointwise at the box operators — and it REUSES the mapping module's
|
|
28
|
+
atomic-condition table and renamer, so both routes speak one vocabulary.
|
|
29
|
+
That overlap is pinned by a three-way differential in the tests: on every
|
|
30
|
+
corpus sentence the DRS route covers, this route's formula, the DRS route's
|
|
31
|
+
formula and Attempto's own TPTP are pairwise Z3-equivalent.
|
|
32
|
+
|
|
33
|
+
Questions come out as :class:`unicode_logic_kit.ace.AceFormula` with a ``kind``:
|
|
34
|
+
|
|
35
|
+
- a wh-question ("Who waits?") yields an OPEN formula — the queried referent
|
|
36
|
+
stays free, named in ``query_variables`` with its question word — so
|
|
37
|
+
answering is model finding (``api.countermodel`` on the negation, or
|
|
38
|
+
``semantics``' model enumeration) rather than proof;
|
|
39
|
+
- a yes/no question ("Does John wait?") yields a closed formula and
|
|
40
|
+
``kind="yesno_question"`` — answering is an entailment check against
|
|
41
|
+
whatever theory the caller holds. This is exactly the sentence the TPTP
|
|
42
|
+
route refuses (APE renders it as a ``conjecture``, which ``ace_to_fol``
|
|
43
|
+
must not flatten into an assertion); here the interrogative force survives
|
|
44
|
+
in ``kind`` instead of being dropped.
|
|
45
|
+
- a question box mixed with assertions in one text raises: the merged box
|
|
46
|
+
shares referents across both parts, and splitting them would either break
|
|
47
|
+
the binding or quantify the premises into the question — split the TEXT.
|
|
48
|
+
|
|
49
|
+
Commands (``command``) and negation as failure (``~``) still raise
|
|
50
|
+
:class:`~unicode_logic_kit.ace.mapping.AceUnsupportedError`: the kit has no
|
|
51
|
+
imperative semantics, and ``~`` is not classical negation — those verdicts
|
|
52
|
+
are unchanged from the DRS route, and unchanged on purpose.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
from __future__ import annotations
|
|
56
|
+
|
|
57
|
+
from dataclasses import dataclass
|
|
58
|
+
from typing import List, Optional, Tuple
|
|
59
|
+
|
|
60
|
+
from ..drt.export import _CARD_TO_COUNT
|
|
61
|
+
from ..drt.nodes import is_referent
|
|
62
|
+
from ..fol.nodes import (
|
|
63
|
+
And, Atom, Box, Constant, Count, Diamond, Function, Implies, Node, Not,
|
|
64
|
+
Number, Obligatory, Or, Permitted, Quantifier, Variable,
|
|
65
|
+
)
|
|
66
|
+
from .drs_reader import (
|
|
67
|
+
AceAtom, AceCommand, AceCondition, AceCondList, AceDrs, AceExpr, AceImpl,
|
|
68
|
+
AceInt, AceModal, AceNaf, AceNamed, AceNeg, AceOr, AceQuestion, AceReal,
|
|
69
|
+
AceVar, parse_ape_drs,
|
|
70
|
+
)
|
|
71
|
+
from .mapping import (
|
|
72
|
+
AceUnsupportedError, ConditionReport, _ACE_OPS, _be_events_to_drop,
|
|
73
|
+
_map_atom, _Renamer, _shell_render, _Unsupported,
|
|
74
|
+
)
|
|
75
|
+
from .runner import AceParseError, run_ape
|
|
76
|
+
|
|
77
|
+
__all__ = ["ace_to_formula", "ace_drs_to_formula", "AceFormula"]
|
|
78
|
+
|
|
79
|
+
_MODAL_NODES = {"must": Box, "can": Diamond, "should": Obligatory,
|
|
80
|
+
"may": Permitted}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@dataclass(frozen=True)
|
|
84
|
+
class AceFormula:
|
|
85
|
+
"""What an ACE text says, as one kit formula plus its illocution.
|
|
86
|
+
|
|
87
|
+
``kind`` is ``"assertion"``, ``"wh_question"`` or ``"yesno_question"``.
|
|
88
|
+
For a wh-question the formula is OPEN: each ``query_variables`` entry is
|
|
89
|
+
``(kit variable name, question word)`` — ``(("x1", "who"),)`` for "Who
|
|
90
|
+
waits?" — and those variables are deliberately unquantified.
|
|
91
|
+
``modalities`` lists which of ACE's modal boxes occurred, in first-use
|
|
92
|
+
order, so a caller can tell a plain formula from one that needs a modal
|
|
93
|
+
backend without walking the tree."""
|
|
94
|
+
|
|
95
|
+
formula: Node
|
|
96
|
+
kind: str = "assertion"
|
|
97
|
+
query_variables: Tuple[Tuple[str, str], ...] = ()
|
|
98
|
+
modalities: Tuple[str, ...] = ()
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class _Stop(Exception):
|
|
102
|
+
"""Internal: abort translation at the first unsupported condition."""
|
|
103
|
+
|
|
104
|
+
def __init__(self, render: str, reason: str, milestone: str,
|
|
105
|
+
sentence=None, token=None):
|
|
106
|
+
super().__init__(reason)
|
|
107
|
+
self.row = ConditionReport(render, "unsupported", reason=reason,
|
|
108
|
+
milestone=milestone, sentence=sentence,
|
|
109
|
+
token=token)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class _Translator:
|
|
113
|
+
def __init__(self, root: AceDrs):
|
|
114
|
+
self.renamer = _Renamer(root)
|
|
115
|
+
self.dropped = _be_events_to_drop(root)
|
|
116
|
+
self.modalities: List[str] = []
|
|
117
|
+
|
|
118
|
+
# -- pieces ------------------------------------------------------------
|
|
119
|
+
def term(self, name: str) -> Node:
|
|
120
|
+
return Variable(name) if is_referent(name) else Constant(name)
|
|
121
|
+
|
|
122
|
+
def atom(self, cond: AceAtom) -> Node:
|
|
123
|
+
if cond.functor == "formula":
|
|
124
|
+
return self.arithmetic(cond)
|
|
125
|
+
mapped = _map_atom(cond, self.renamer, self.dropped)
|
|
126
|
+
if isinstance(mapped, _Unsupported):
|
|
127
|
+
raise _Stop(cond.render(), mapped.reason, mapped.milestone,
|
|
128
|
+
cond.sentence, cond.token)
|
|
129
|
+
kind = mapped[0]
|
|
130
|
+
if kind == "eq":
|
|
131
|
+
return Atom("=", (self.term(mapped[1]), self.term(mapped[2])))
|
|
132
|
+
if kind == "pred":
|
|
133
|
+
return Atom(mapped[1], tuple(self.term(a) for a in mapped[2]))
|
|
134
|
+
if kind == "card":
|
|
135
|
+
return self.card(mapped[1], mapped[2], mapped[3])
|
|
136
|
+
if kind == "part":
|
|
137
|
+
return Atom("Part_of", (self.term(mapped[1]),
|
|
138
|
+
self.term(mapped[2])))
|
|
139
|
+
# group_noun: |g| op n, every part a noun; the group referent stays
|
|
140
|
+
# a term for the verb (ACE's collective reading), mirroring the
|
|
141
|
+
# mapping route's Card + membership-duplex expansion.
|
|
142
|
+
_, group, noun, op, count = mapped
|
|
143
|
+
member = self.renamer.fresh("x")
|
|
144
|
+
membership = Quantifier(
|
|
145
|
+
"∀", Variable(member),
|
|
146
|
+
Implies(Atom("Part_of", (Variable(member), self.term(group))),
|
|
147
|
+
Atom(noun, (Variable(member),))))
|
|
148
|
+
return And(self.card(group, op, count), membership)
|
|
149
|
+
|
|
150
|
+
def card(self, group: str, op: str, n: int) -> Node:
|
|
151
|
+
"""``|group| op n`` as the kit's counting quantifier over parts —
|
|
152
|
+
the same lowering drt.export gives the Card condition."""
|
|
153
|
+
count_op, shift = _CARD_TO_COUNT[op]
|
|
154
|
+
counter = self.renamer.fresh("p")
|
|
155
|
+
return Count(count_op, Number(n + shift), Variable(counter),
|
|
156
|
+
Atom("Part_of", (Variable(counter), self.term(group))))
|
|
157
|
+
|
|
158
|
+
def arithmetic(self, cond: AceAtom) -> Node:
|
|
159
|
+
"""``formula(expr, op, expr)`` → a native kit comparison atom over
|
|
160
|
+
Function/Number terms — ``1 + 2 = 3.`` becomes exactly what the
|
|
161
|
+
kit's own parser produces for ``1 + 2 = 3``. The default FOL→z3
|
|
162
|
+
route reads ``+`` as an UNINTERPRETED symbol; the decision procedure
|
|
163
|
+
for this fragment is :func:`unicode_logic_kit.atp.z3_arith.is_valid_arith`
|
|
164
|
+
(measured: it proves ``1+2=3`` and refutes ``1+2=4``; the default
|
|
165
|
+
route proves neither)."""
|
|
166
|
+
if len(cond.args) != 3 or not isinstance(cond.args[1], str):
|
|
167
|
+
raise _Stop(cond.render(), "a formula condition outside the "
|
|
168
|
+
"measured shapes", "")
|
|
169
|
+
left, op, right = cond.args
|
|
170
|
+
if op not in ("=", "<", ">"):
|
|
171
|
+
raise _Stop(cond.render(),
|
|
172
|
+
f"comparison operator {op!r} is not in the measured "
|
|
173
|
+
"corpus", "")
|
|
174
|
+
return Atom(op, (self.arith_term(left, cond),
|
|
175
|
+
self.arith_term(right, cond)))
|
|
176
|
+
|
|
177
|
+
def arith_term(self, term, cond: AceAtom) -> Node:
|
|
178
|
+
if isinstance(term, AceInt):
|
|
179
|
+
return Number(term.value)
|
|
180
|
+
if isinstance(term, AceReal):
|
|
181
|
+
return Number(term.value)
|
|
182
|
+
if isinstance(term, AceExpr):
|
|
183
|
+
if term.op not in ("+", "-", "*", "/"):
|
|
184
|
+
raise _Stop(cond.render(),
|
|
185
|
+
f"arithmetic operator {term.op!r} is not in the "
|
|
186
|
+
"measured corpus", "")
|
|
187
|
+
return Function(term.op, (self.arith_term(term.left, cond),
|
|
188
|
+
self.arith_term(term.right, cond)))
|
|
189
|
+
if isinstance(term, AceVar):
|
|
190
|
+
return Variable(self.renamer.referent(term))
|
|
191
|
+
if isinstance(term, AceNamed):
|
|
192
|
+
return self.term(self.renamer.constant(term))
|
|
193
|
+
raise _Stop(cond.render(), "an arithmetic term outside the measured "
|
|
194
|
+
"shapes", "")
|
|
195
|
+
|
|
196
|
+
def conjoin(self, nodes: List[Node], box: AceDrs) -> Node:
|
|
197
|
+
if not nodes:
|
|
198
|
+
raise _Stop(box.render(), "an empty box (no conditions) is not "
|
|
199
|
+
"in the measured corpus", "")
|
|
200
|
+
result = nodes[0]
|
|
201
|
+
for node in nodes[1:]:
|
|
202
|
+
result = And(result, node)
|
|
203
|
+
return result
|
|
204
|
+
|
|
205
|
+
def exists(self, box: AceDrs, body: Node) -> Node:
|
|
206
|
+
for ref in reversed(self._box_refs(box)):
|
|
207
|
+
body = Quantifier("∃", Variable(ref), body)
|
|
208
|
+
return body
|
|
209
|
+
|
|
210
|
+
def _box_refs(self, box: AceDrs) -> List[str]:
|
|
211
|
+
return [self.renamer.referent(r) for r in box.referents
|
|
212
|
+
if r.name not in self.dropped]
|
|
213
|
+
|
|
214
|
+
# -- the standard translation, extended at the box operators -----------
|
|
215
|
+
def box(self, box: AceDrs) -> Node:
|
|
216
|
+
lists = [c for c in box.conditions if isinstance(c, AceCondList)]
|
|
217
|
+
if lists:
|
|
218
|
+
return self.box_with_lists(box, lists)
|
|
219
|
+
return self.exists(box, self.conjoin(
|
|
220
|
+
[self.condition(c) for c in box.conditions], box))
|
|
221
|
+
|
|
222
|
+
def box_with_lists(self, box: AceDrs, lists) -> Node:
|
|
223
|
+
"""The counting reading of ``exactly``/``at most`` — APE's list
|
|
224
|
+
condition, translated as a counting quantifier over INDIVIDUALS.
|
|
225
|
+
|
|
226
|
+
"Exactly 2 dogs bark." arrives as ``[predicate(B,bark,A),
|
|
227
|
+
object(A,dog,...,exactly,2)]`` inside ``drs([A,B], [ [...] ])``: the
|
|
228
|
+
list marks that the cardinality is a MAXIMALITY claim over
|
|
229
|
+
everything satisfying the scope, not the size of one witness group.
|
|
230
|
+
First-order, that is ``∃=2 a (Dog(a) ∧ ∃b Bark(b, a))`` — count the
|
|
231
|
+
individuals, distribute the scope over them. This is a DOCUMENTED
|
|
232
|
+
SEMANTIC CHOICE: a collective reading of a maximality claim ("exactly
|
|
233
|
+
2 men lift the table TOGETHER, and no other group does") is not
|
|
234
|
+
first-order expressible, and the corpus scopes (bark, wait) are
|
|
235
|
+
naturally distributive; the choice is stated here, in the guide and
|
|
236
|
+
in the CHANGELOG rather than buried.
|
|
237
|
+
|
|
238
|
+
Mechanics: the list's counted ``object`` names the count variable;
|
|
239
|
+
the list's other conditions form the matrix; enclosing referents
|
|
240
|
+
used ONLY inside the list (the scope's events) are ∃-bound INSIDE
|
|
241
|
+
the matrix. A referent shared between the list and the rest of the
|
|
242
|
+
box would make the rebinding change meaning — refused, unmeasured.
|
|
243
|
+
"""
|
|
244
|
+
outside_vars: set = set()
|
|
245
|
+
for cond in box.conditions:
|
|
246
|
+
if not isinstance(cond, AceCondList):
|
|
247
|
+
outside_vars |= _vars_of(cond)
|
|
248
|
+
list_only: set = set()
|
|
249
|
+
pieces: List[Node] = []
|
|
250
|
+
for group in lists:
|
|
251
|
+
counted = [c for c in group.conditions
|
|
252
|
+
if isinstance(c, AceAtom) and c.functor == "object"
|
|
253
|
+
and len(c.args) == 6 and c.args[4] in _ACE_OPS
|
|
254
|
+
and not (c.args[4] == "eq" and c.args[5] == 1)]
|
|
255
|
+
if len(counted) != 1:
|
|
256
|
+
raise _Stop("[…]", f"{len(counted)} counted objects in one "
|
|
257
|
+
"list condition — the measured corpus has "
|
|
258
|
+
"exactly one", "")
|
|
259
|
+
obj = counted[0]
|
|
260
|
+
ref, noun, _cls, _unit, op, count = obj.args
|
|
261
|
+
if not isinstance(ref, AceVar) or ref.name in outside_vars:
|
|
262
|
+
raise _Stop(obj.render(),
|
|
263
|
+
"the counted referent is shared with conditions "
|
|
264
|
+
"outside the list — rebinding it would change "
|
|
265
|
+
"their meaning", "")
|
|
266
|
+
rest = [c for c in group.conditions if c is not obj]
|
|
267
|
+
inner_vars = set()
|
|
268
|
+
for c in rest:
|
|
269
|
+
inner_vars |= _vars_of(c)
|
|
270
|
+
inner_vars.discard(ref.name)
|
|
271
|
+
shared = inner_vars & outside_vars
|
|
272
|
+
if shared:
|
|
273
|
+
raise _Stop("[…]",
|
|
274
|
+
f"referents {sorted(shared)!r} are used inside "
|
|
275
|
+
"AND outside the list condition — not in the "
|
|
276
|
+
"measured corpus", "")
|
|
277
|
+
count_var = self.renamer.referent(ref)
|
|
278
|
+
matrix_parts = [Atom(_kit_pred(noun), (Variable(count_var),))]
|
|
279
|
+
matrix_parts += [self.condition(c) for c in rest]
|
|
280
|
+
matrix: Node = matrix_parts[0]
|
|
281
|
+
for part in matrix_parts[1:]:
|
|
282
|
+
matrix = And(matrix, part)
|
|
283
|
+
for inner in sorted(inner_vars):
|
|
284
|
+
declared = [r for r in box.referents if r.name == inner]
|
|
285
|
+
if declared:
|
|
286
|
+
matrix = Quantifier(
|
|
287
|
+
"∃", Variable(self.renamer.referent(declared[0])),
|
|
288
|
+
matrix)
|
|
289
|
+
count_op, shift = _CARD_TO_COUNT[_ACE_OPS[op]]
|
|
290
|
+
pieces.append(Count(count_op, Number(count + shift),
|
|
291
|
+
Variable(count_var), matrix))
|
|
292
|
+
list_only |= inner_vars | {ref.name}
|
|
293
|
+
other = [self.condition(c) for c in box.conditions
|
|
294
|
+
if not isinstance(c, AceCondList)]
|
|
295
|
+
body = pieces[0]
|
|
296
|
+
for piece in pieces[1:] + other:
|
|
297
|
+
body = And(body, piece)
|
|
298
|
+
remaining = AceDrs(
|
|
299
|
+
tuple(r for r in box.referents if r.name not in list_only),
|
|
300
|
+
())
|
|
301
|
+
return self.exists(remaining, body)
|
|
302
|
+
|
|
303
|
+
def condition(self, cond: AceCondition) -> Node:
|
|
304
|
+
if isinstance(cond, AceAtom):
|
|
305
|
+
return self.atom(cond)
|
|
306
|
+
if isinstance(cond, AceNeg):
|
|
307
|
+
return Not(self.box(cond.drs))
|
|
308
|
+
if isinstance(cond, AceImpl):
|
|
309
|
+
# The duplex rule (donkey sentences): the antecedent's referents
|
|
310
|
+
# go universal over the whole conditional — same construction as
|
|
311
|
+
# drt.export.drs_to_fol.
|
|
312
|
+
result = Implies(
|
|
313
|
+
self.conjoin([self.condition(c)
|
|
314
|
+
for c in cond.antecedent.conditions],
|
|
315
|
+
cond.antecedent),
|
|
316
|
+
self.box(cond.consequent))
|
|
317
|
+
for ref in reversed(self._box_refs(cond.antecedent)):
|
|
318
|
+
result = Quantifier("∀", Variable(ref), result)
|
|
319
|
+
return result
|
|
320
|
+
if isinstance(cond, AceOr):
|
|
321
|
+
return Or(self.box(cond.left), self.box(cond.right))
|
|
322
|
+
if isinstance(cond, AceModal):
|
|
323
|
+
if cond.modality not in self.modalities:
|
|
324
|
+
self.modalities.append(cond.modality)
|
|
325
|
+
return _MODAL_NODES[cond.modality](self.box(cond.drs))
|
|
326
|
+
if isinstance(cond, AceQuestion):
|
|
327
|
+
raise _Stop("question(…)",
|
|
328
|
+
"a question box below the top level — the merged "
|
|
329
|
+
"text shares referents across assertion and "
|
|
330
|
+
"question; split the text", "")
|
|
331
|
+
if isinstance(cond, AceCommand):
|
|
332
|
+
raise _Stop("command(…)",
|
|
333
|
+
"an imperative: no command semantics decided", "")
|
|
334
|
+
if isinstance(cond, AceNaf):
|
|
335
|
+
raise _Stop("~(…)",
|
|
336
|
+
"negation as failure is not classical negation; "
|
|
337
|
+
"the non-monotonic route is undecided", "")
|
|
338
|
+
if isinstance(cond, AceCondList):
|
|
339
|
+
# Handled at the BOX level (see box()) because the counting
|
|
340
|
+
# reading rebinds enclosing referents; reaching it here means
|
|
341
|
+
# a list nested somewhere the corpus never produces one.
|
|
342
|
+
raise _Stop("[…]", "a list condition below a box's top level "
|
|
343
|
+
"is not in the measured corpus", "")
|
|
344
|
+
raise _Stop(_shell_render(cond), "not in the measured corpus", "")
|
|
345
|
+
|
|
346
|
+
# -- questions at the top ----------------------------------------------
|
|
347
|
+
def question(self, box: AceDrs) -> AceFormula:
|
|
348
|
+
query_vars: List[Tuple[str, str]] = []
|
|
349
|
+
plain: List[AceCondition] = []
|
|
350
|
+
for cond in box.conditions:
|
|
351
|
+
if isinstance(cond, AceAtom) and cond.functor == "query":
|
|
352
|
+
if not (len(cond.args) == 2 and isinstance(cond.args[0], AceVar)
|
|
353
|
+
and isinstance(cond.args[1], str)):
|
|
354
|
+
raise _Stop(cond.render(), "a query condition outside "
|
|
355
|
+
"the measured shapes", "")
|
|
356
|
+
query_vars.append((self.renamer.referent(cond.args[0]),
|
|
357
|
+
cond.args[1]))
|
|
358
|
+
else:
|
|
359
|
+
plain.append(cond)
|
|
360
|
+
body = self.conjoin([self.condition(c) for c in plain], box)
|
|
361
|
+
queried = {name for name, _ in query_vars}
|
|
362
|
+
for ref in reversed(self._box_refs(box)):
|
|
363
|
+
if ref not in queried: # the queried referents stay FREE
|
|
364
|
+
body = Quantifier("∃", Variable(ref), body)
|
|
365
|
+
if query_vars:
|
|
366
|
+
return AceFormula(body, "wh_question", tuple(query_vars),
|
|
367
|
+
tuple(self.modalities))
|
|
368
|
+
return AceFormula(body, "yesno_question", (),
|
|
369
|
+
tuple(self.modalities))
|
|
370
|
+
|
|
371
|
+
|
|
372
|
+
def _vars_of(cond) -> set:
|
|
373
|
+
"""Every APE variable name occurring anywhere in a condition."""
|
|
374
|
+
names = set()
|
|
375
|
+
stack = [cond]
|
|
376
|
+
while stack:
|
|
377
|
+
current = stack.pop()
|
|
378
|
+
if isinstance(current, AceAtom):
|
|
379
|
+
stack.extend(current.args)
|
|
380
|
+
elif isinstance(current, AceVar):
|
|
381
|
+
names.add(current.name)
|
|
382
|
+
elif isinstance(current, AceCondList):
|
|
383
|
+
stack.extend(current.conditions)
|
|
384
|
+
elif isinstance(current, AceDrs):
|
|
385
|
+
stack.extend(current.conditions)
|
|
386
|
+
stack.extend(current.referents)
|
|
387
|
+
elif isinstance(current, (AceNeg, AceNaf, AceModal, AceQuestion,
|
|
388
|
+
AceCommand)):
|
|
389
|
+
stack.append(current.drs)
|
|
390
|
+
elif isinstance(current, AceImpl):
|
|
391
|
+
stack.extend((current.antecedent, current.consequent))
|
|
392
|
+
elif isinstance(current, AceOr):
|
|
393
|
+
stack.extend((current.left, current.right))
|
|
394
|
+
elif isinstance(current, AceExpr):
|
|
395
|
+
stack.extend((current.left, current.right))
|
|
396
|
+
return names
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
def _kit_pred(noun: str) -> str:
|
|
400
|
+
from .mapping import _kit_predicate
|
|
401
|
+
return _kit_predicate(noun)
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
def ace_drs_to_formula(ace_drs: AceDrs) -> AceFormula:
|
|
405
|
+
"""APE's DRS → :class:`unicode_logic_kit.ace.AceFormula` — the pure function
|
|
406
|
+
behind :func:`ace_to_formula`, offline-testable on recorded DRS terms."""
|
|
407
|
+
translator = _Translator(ace_drs)
|
|
408
|
+
questions = [c for c in ace_drs.conditions if isinstance(c, AceQuestion)]
|
|
409
|
+
try:
|
|
410
|
+
if questions:
|
|
411
|
+
if len(questions) > 1 or len(ace_drs.conditions) > 1 \
|
|
412
|
+
or ace_drs.referents:
|
|
413
|
+
raise _Stop("question(…)",
|
|
414
|
+
"a question mixed with assertions (or a second "
|
|
415
|
+
"question) in one text — the merged box shares "
|
|
416
|
+
"referents across the parts; split the text", "")
|
|
417
|
+
return translator.question(questions[0].drs)
|
|
418
|
+
formula = translator.box(ace_drs)
|
|
419
|
+
except _Stop as stop:
|
|
420
|
+
raise AceUnsupportedError(
|
|
421
|
+
f"this route cannot carry the text — {stop.row.condition}: "
|
|
422
|
+
f"{stop.row.reason}"
|
|
423
|
+
+ (f" [{stop.row.milestone}]" if stop.row.milestone else ""),
|
|
424
|
+
(stop.row,)) from None
|
|
425
|
+
return AceFormula(formula, "assertion", (), tuple(translator.modalities))
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
def ace_to_formula(text: str, *, ulex: Optional[str] = None,
|
|
429
|
+
guess: bool = False, timeout: float = 30.0) -> AceFormula:
|
|
430
|
+
"""ACE text → one kit formula with modality, questions and all.
|
|
431
|
+
|
|
432
|
+
The union of what this and :func:`~unicode_logic_kit.ace.mapping.ace_to_drs`
|
|
433
|
+
accept is deliberate: everything the DRS route carries, plus modal boxes
|
|
434
|
+
and top-level questions. What neither carries — commands, negation as
|
|
435
|
+
failure, real cardinalities, arithmetic — raises
|
|
436
|
+
:class:`~unicode_logic_kit.ace.mapping.AceUnsupportedError` here too, with
|
|
437
|
+
the same reasons and milestones.
|
|
438
|
+
|
|
439
|
+
Raises:
|
|
440
|
+
unicode_logic_kit.ace.runner.AceParseError: the text is not ACE.
|
|
441
|
+
unicode_logic_kit.ace.mapping.AceUnsupportedError: see above.
|
|
442
|
+
unicode_logic_kit.ace.runner.ApeUnavailableError: no APE binary
|
|
443
|
+
reachable.
|
|
444
|
+
"""
|
|
445
|
+
result = run_ape(text, ulex=ulex, guess=guess, timeout=timeout)
|
|
446
|
+
if not result.accepted:
|
|
447
|
+
first = result.error_messages[0]
|
|
448
|
+
raise AceParseError(
|
|
449
|
+
f"not ACE: {first.value!r}"
|
|
450
|
+
+ (f" (repair hint: {first.repair!r})" if first.repair else ""),
|
|
451
|
+
result.error_messages)
|
|
452
|
+
return ace_drs_to_formula(parse_ape_drs(result.drs))
|