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,94 @@
1
+ """The ``drt`` subpackage — Discourse Representation Theory (Kamp & Reyle).
2
+
3
+ Single-sentence FOL cannot express **cross-sentence anaphora** ("A farmer owns a donkey.
4
+ He beats it.") or **donkey sentences** ("Every farmer who owns a donkey beats it.") — the
5
+ pronoun's antecedent lives in a different sentence/clause than the pronoun, and the donkey
6
+ sentence's indefinite ("a donkey") gets a UNIVERSAL reading once it sits in a conditional's
7
+ antecedent. Discourse Representation Theory (Kamp & Reyle, *From Discourse to Logic*, 1993)
8
+ is the classical fix: represent a discourse as a **box** of referents + conditions, with a
9
+ structural ACCESSIBILITY relation deciding which referents a pronoun (or a later sentence)
10
+ can see, then give the whole box a first-order **standard translation** that gets the
11
+ donkey reading right for free.
12
+
13
+ This subpackage implements exactly that core, and nothing beyond it — see "Scope" below.
14
+
15
+ Modules
16
+ ---------
17
+ * :mod:`unicode_logic_kit.drt.nodes` — the ``DRS`` data model (``referents``, ``conditions``)
18
+ and its condition variants — the five classical ones (``Pred``, ``Eq``, ``Neg``, ``Impl``, ``Or``) plus the 0.24.0 plural-DRT pair (``Card``, ``Part``) —
19
+ plus the accessibility checker :meth:`~unicode_logic_kit.drt.nodes.DRS.validate`.
20
+ * :mod:`unicode_logic_kit.drt.parser` — :func:`~unicode_logic_kit.drt.parser.parse_drs` for a
21
+ compact hand-written box notation, and
22
+ :func:`~unicode_logic_kit.drt.parser.parse_sbn` for a precisely documented SUBSET of the
23
+ Parallel Meaning Bank's Sequence Box Notation (SBN) — PMB is the natural "found data"
24
+ source of real DRSs, so SBN is this subpackage's interchange FROM format.
25
+ * :mod:`unicode_logic_kit.drt.resolve` — :func:`~unicode_logic_kit.drt.resolve.resolve_anaphora`,
26
+ accessibility-respecting pronoun resolution (right-to-left / most-recent preference; not
27
+ an NLP guesser — see that module's docstring).
28
+ * :mod:`unicode_logic_kit.drt.export` — :func:`~unicode_logic_kit.drt.export.drs_to_fol`, the
29
+ standard translation into the kit's own FOL AST
30
+ (:mod:`unicode_logic_kit.fol.nodes`), ready for ``unicode_logic_kit.api.prove`` /
31
+ ``unicode_logic_kit.api.check``.
32
+ * :mod:`unicode_logic_kit.drt.reverse` — :func:`~unicode_logic_kit.drt.reverse.fol_to_drs`,
33
+ the standard translation run BACKWARDS: rebuilds the box structure of any
34
+ formula in the export's image and refuses everything outside it by name
35
+ (:class:`~unicode_logic_kit.drt.reverse.FolToDrsError`) — the bridge that
36
+ lets ``ace.formula_to_ace`` answer "is this formula expressible as ACE?".
37
+
38
+ Quick usage — the classic donkey sentence, entailed from its premises via Z3::
39
+
40
+ from unicode_logic_kit import drt, api
41
+
42
+ rule = drt.parse_drs(
43
+ "[x, y | Farmer(x), Donkey(y), Owns(x, y)] -> [ | Beats(x, y)]")
44
+ facts = drt.parse_drs("[ | Farmer(john), Donkey(daisy), Owns(john, daisy)]")
45
+ goal = drt.parse_drs("[ | Beats(john, daisy)]")
46
+
47
+ v = api.prove(drt.drs_to_fol(goal),
48
+ [drt.drs_to_fol(rule), drt.drs_to_fol(facts)])
49
+ assert v.status == "proved"
50
+
51
+ Scope (deliberately tight — this is a single kit subpackage, not a discourse-parsing system)
52
+ ------------------------------------------------------------------------------------------------
53
+ IN scope: the classical propositional DRS core (five condition variants), box-notation and
54
+ SBN-subset parsing, accessibility-respecting explicit-marker pronoun resolution, and the
55
+ standard translation to FOL.
56
+
57
+ OUT of scope, refused loudly rather than silently approximated:
58
+
59
+ * multi-paragraph discourse machinery (discourse segmentation, rhetorical relations,
60
+ coreference chains spanning many sentences) — this subpackage parses and represents ONE
61
+ assembled DRS (one sentence, or a small hand-built discourse) per call;
62
+ * event semantics beyond plain predicates (no Davidsonian event variables are threaded
63
+ through automatically, no thematic-role ontology beyond "whatever role name the input
64
+ uses") — a ``Pred``'s arguments are exactly what the box notation or SBN input says;
65
+ * presupposition projection (definites, factives, "again", ...) — DRT's presupposition
66
+ extensions (van der Sandt's binding theory and successors) are a substantial theory of
67
+ their own and are not implemented here; a presupposition trigger in an SBN/box-notation
68
+ input is just an ordinary predicate as far as this subpackage is concerned, with no
69
+ special accommodation/binding behaviour.
70
+
71
+ See each module's docstring for the precise grammar/semantics decisions and their sources.
72
+ """
73
+
74
+ from .nodes import (
75
+ DRS, Card, CARD_OPS, Condition, Pred, Eq, Neg, Impl, Or, Part,
76
+ is_referent, is_predicate_name, is_constant_name, walk_boxes,
77
+ )
78
+ from .parser import (
79
+ parse_drs, DRSSyntaxError,
80
+ parse_sbn, SBNSyntaxError, SBNMapping,
81
+ )
82
+ from .resolve import resolve_anaphora, Resolution, ResolutionReport, PRONOUN
83
+ from .export import drs_to_fol
84
+ from .reverse import fol_to_drs, FolToDrsError
85
+
86
+ __all__ = [
87
+ "DRS", "Condition", "Pred", "Eq", "Neg", "Impl", "Or", "Card", "Part",
88
+ "CARD_OPS",
89
+ "is_referent", "is_predicate_name", "is_constant_name", "walk_boxes",
90
+ "parse_drs", "DRSSyntaxError",
91
+ "parse_sbn", "SBNSyntaxError", "SBNMapping",
92
+ "resolve_anaphora", "Resolution", "ResolutionReport", "PRONOUN",
93
+ "drs_to_fol", "fol_to_drs", "FolToDrsError",
94
+ ]
@@ -0,0 +1,179 @@
1
+ """The standard translation of DRT into first-order logic (unicode_logic_kit.drt.export).
2
+
3
+ The standard translation (Kamp & Reyle, *From Discourse to Logic*, 1993) reads a box's
4
+ own referents as existential quantifiers over the conjunction of its conditions, with two
5
+ condition types contributing their own quantificational structure:
6
+
7
+ * ``Pred(name, args)`` ↦ ``name(args...)`` (an atom; each arg is a Variable if it's a
8
+ referent — :func:`~unicode_logic_kit.drt.nodes.is_referent` — else a Constant)
9
+ * ``Eq(a, b)`` ↦ ``a = b``
10
+ * ``Neg(D)`` ↦ ``¬τ(D)`` where ``τ(D)`` existentially closes D's own
11
+ referents — i.e. ``¬∃...`` : negation-as-failure-to-find-a-witness, matching the
12
+ accessibility rule that D's referents don't escape it (see ``nodes.py``).
13
+ * ``Or(D1, D2)`` ↦ ``τ(D1) ∨ τ(D2)`` — each disjunct closes its own referents
14
+ independently, again matching D1/D2's mutually inaccessible referents.
15
+ * ``Impl(A, B)`` ↦ ``∀(A's referents) (conj(A's conditions) → τ(B))`` — THE
16
+ donkey-sentence rule. Unlike every other condition, A's referents become UNIVERSAL
17
+ (not existential) quantifiers, and A's own conditions are conjoined DIRECTLY into the
18
+ antecedent (NOT re-wrapped in their own existential closure) — both together are what
19
+ make "Every farmer who owns a donkey beats it" come out as ``∀x∀y((Farmer(x) ∧
20
+ Donkey(y) ∧ Owns(x,y)) → Beats(x,y))`` rather than something existential-then-vacuous.
21
+ ``τ(B)`` (B's OWN translation, i.e. its own referents existentially closed as usual) is
22
+ the consequent — B may introduce further referents of its own beyond what A shares with
23
+ it (accessible to B per the donkey accessibility rule, but not bound by A's ∀ unless
24
+ they ARE A's own referents).
25
+
26
+ A box with zero conditions translates to a reflexive-equality tautology (``c_tautology =
27
+ c_tautology`` — a constant the kit's own parser reads back) — the same "no constraint" idiom
28
+ :mod:`unicode_logic_kit.dl.translate` uses for an empty TBox/ABox — so an empty box never
29
+ silently vanishes into an ill-typed conjunction of zero formulas.
30
+
31
+ Closedness, and why :func:`drs_to_fol` always calls :meth:`~unicode_logic_kit.drt.nodes.DRS.validate`
32
+ -----------------------------------------------------------------------------------------------------
33
+ The classical Kamp/Reyle accessibility relation (see ``nodes.py``'s module docstring) is
34
+ EXACTLY the condition under which this translation binds every referent it uses: a
35
+ referent-shaped argument is accessible to the box it occurs in iff, walking the same
36
+ recursive structure this translation walks, some ∃/∀ introduced along the way would bind
37
+ it. So an accessibility VIOLATION (a referent used somewhere it isn't accessible)
38
+ translates to a genuinely FREE variable in the output formula — silently. To make that
39
+ impossible, :func:`drs_to_fol` calls ``drs.validate()`` before translating anything, so an
40
+ invalid DRS is refused loudly (``ValueError`` from ``validate()``) rather than handed back
41
+ as an FOL formula that fails ``unicode_logic_kit.api.check(...).ok`` for a reason the caller
42
+ would have to rediscover by re-deriving accessibility themselves.
43
+
44
+ Constants
45
+ -----------
46
+ By the time a DRS reaches this module, every constant-shaped ``Pred``/``Eq`` argument is
47
+ ALREADY a legal kit constant name — :mod:`unicode_logic_kit.drt.nodes` refuses anything else
48
+ at construction time, and :mod:`unicode_logic_kit.drt.parser` sanitizes quoted/irregular
49
+ constants (recording the mapping) before ever building a ``Pred``/``Eq``. So this module
50
+ does no sanitization of its own: a non-referent argument becomes
51
+ ``unicode_logic_kit.fol.nodes.Constant(name)`` verbatim.
52
+ """
53
+
54
+ from typing import List
55
+
56
+ from ..fol.nodes import (
57
+ Node, Variable, Constant, Atom, Count, Number,
58
+ Not as _FNot, And as _FAnd, Or as _FOr, Implies, Quantifier,
59
+ )
60
+ from .nodes import (
61
+ DRS, Card, Condition, Eq, Impl, Neg, Or, Part, Pred, is_referent,
62
+ walk_boxes,
63
+ )
64
+
65
+ __all__ = ["drs_to_fol"]
66
+
67
+
68
+ class _FreshCounters:
69
+ """Fresh counting variables for :class:`Card` translations.
70
+
71
+ ``Card(g, ">=", 3)`` becomes ``∃≥3 p (Part_of(p, g))`` and the counting
72
+ variable ``p`` must collide with NOTHING the tree declares — a clash
73
+ would capture. Seeded once per :func:`drs_to_fol` call with every
74
+ declared referent, then hands out ``p1, p2, …`` skipping taken names.
75
+ (``Count`` binds its own variable, so two Cards may NOT share one name
76
+ if one ends up in the scope of the other's matrix — cheap to avoid by
77
+ never reusing any.)
78
+ """
79
+
80
+ def __init__(self, drs: DRS):
81
+ self.taken = {r for box, _ in walk_boxes(drs) for r in box.referents}
82
+ self.i = 0
83
+
84
+ def next(self) -> str:
85
+ while True:
86
+ self.i += 1
87
+ candidate = f"p{self.i}"
88
+ if candidate not in self.taken:
89
+ self.taken.add(candidate)
90
+ return candidate
91
+
92
+
93
+ #: Card's kit-spelled ops onto Count's op codes; ``>``/``<`` shift the bound
94
+ #: (``> n`` ≡ ``≥ n+1``; ``< n`` ≡ ``≤ n-1``, and ``< 0`` is refused at
95
+ #: Card construction so the subtraction never goes negative).
96
+ _CARD_TO_COUNT = {"=": ("eq", 0), ">=": ("ge", 0), "<=": ("le", 0),
97
+ ">": ("ge", +1), "<": ("le", -1)}
98
+
99
+
100
+ def _term(name: str) -> Node:
101
+ """A DRS argument (already validated legal by nodes.py) as a FOL term."""
102
+ return Variable(name) if is_referent(name) else Constant(name)
103
+
104
+
105
+ # The constant the empty-box tautology is built from. ``c_`` plus a word is the
106
+ # kit's CONSTANT terminal (fol._identifiers.constant_pattern), so the text this
107
+ # prints reads back through api.parse_any as the SAME formula. Until 0.30.0 it
108
+ # was Constant("_"), which printed `_ = _` -- text the kit's own parser rejects
109
+ # ("Unexpected character '_'"): drs_to_fol(DRS(referents=(), conditions=()))
110
+ # was a formula that could be computed but not handed back as text. The same
111
+ # fix, and the same name, as unicode_logic_kit.dl.translate's. `c = c` is valid
112
+ # whatever `c` denotes, so a DRS that happens to use a constant `c_tautology`
113
+ # is still rendered correctly.
114
+ _TAUTOLOGY_CONSTANT = Constant("c_tautology")
115
+
116
+
117
+ def _tautology() -> Node:
118
+ """The FOL image of "no constraint" — an empty box's condition list. Mirrors
119
+ unicode_logic_kit.dl.translate's empty-TBox/ABox idiom."""
120
+ return Atom("=", (_TAUTOLOGY_CONSTANT, _TAUTOLOGY_CONSTANT))
121
+
122
+
123
+ def _conjoin(parts: List[Node]) -> Node:
124
+ """Fold a (possibly empty) list of formulas into a single left-associated conjunction."""
125
+ if not parts:
126
+ return _tautology()
127
+ result = parts[0]
128
+ for p in parts[1:]:
129
+ result = _FAnd(result, p)
130
+ return result
131
+
132
+
133
+ def _translate_cond(cond: Condition, fresh: _FreshCounters) -> Node:
134
+ if isinstance(cond, Pred):
135
+ return Atom(cond.name, tuple(_term(a) for a in cond.args))
136
+ if isinstance(cond, Eq):
137
+ return Atom("=", (_term(cond.a), _term(cond.b)))
138
+ if isinstance(cond, Card):
139
+ op, shift = _CARD_TO_COUNT[cond.op]
140
+ counter = fresh.next()
141
+ return Count(op, Number(cond.n + shift), Variable(counter),
142
+ Atom("Part_of", (Variable(counter), _term(cond.ref))))
143
+ if isinstance(cond, Part):
144
+ return Atom("Part_of", (_term(cond.member), _term(cond.group)))
145
+ if isinstance(cond, Neg):
146
+ return _FNot(_translate_box(cond.drs, fresh))
147
+ if isinstance(cond, Impl):
148
+ antecedent = _conjoin([_translate_cond(c, fresh)
149
+ for c in cond.antecedent.conditions])
150
+ result: Node = Implies(antecedent, _translate_box(cond.consequent, fresh))
151
+ for r in reversed(cond.antecedent.referents):
152
+ result = Quantifier("∀", Variable(r), result)
153
+ return result
154
+ if isinstance(cond, Or):
155
+ return _FOr(_translate_box(cond.left, fresh),
156
+ _translate_box(cond.right, fresh))
157
+ raise TypeError(f"drs_to_fol: unsupported condition {type(cond).__name__}")
158
+
159
+
160
+ def _translate_box(box: DRS, fresh: _FreshCounters) -> Node:
161
+ result: Node = _conjoin([_translate_cond(c, fresh) for c in box.conditions])
162
+ for r in reversed(box.referents):
163
+ result = Quantifier("∃", Variable(r), result)
164
+ return result
165
+
166
+
167
+ def drs_to_fol(drs: DRS) -> Node:
168
+ """The standard translation of ``drs`` into a kit FOL :class:`~unicode_logic_kit.fol.nodes.Node`
169
+ (see the module docstring for the per-condition rules and the donkey-sentence rule
170
+ for ``Impl``).
171
+
172
+ Calls ``drs.validate()`` first, so an accessibility violation is refused
173
+ (``ValueError``) here rather than silently producing a formula with a free variable
174
+ (see "Closedness" above). For a CLOSED, accessibility-valid ``drs`` the result is a
175
+ closed formula — ``unicode_logic_kit.api.check(drs_to_fol(drs)).ok`` holds — ready for
176
+ ``unicode_logic_kit.api.prove``.
177
+ """
178
+ drs.validate()
179
+ return _translate_box(drs, _FreshCounters(drs))