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,397 @@
1
+ r"""Classical third-order logic → HOL/THF (predicates of properties, mapped natively).
2
+
3
+ Second-order logic quantifies over predicates; :mod:`unicode_logic_kit.hol.secondorder`
4
+ exports that, and a HOL prover takes it directly because a predicate variable is
5
+ just a variable at a function type. Third-order logic is the next step and it is
6
+ a step in a different direction: a predicate whose ARGUMENT is a predicate —
7
+ ``Positive(G)``, ``Essence(G, x)``, ``Positive(λx. ¬G(x))``. HOL takes that
8
+ directly too, for the same reason, one type higher::
9
+
10
+ x : i an individual
11
+ G : i => bool a property
12
+ Positive : (i => bool) => bool a predicate OF properties
13
+ Essence : (i => bool) => i => bool a relation between a property and an individual
14
+
15
+ so the embedding is again a translation and not a simulation: an object
16
+ quantifier is a HOL ``∀`` at type ``i``, a property quantifier a HOL ``∀`` at
17
+ ``i => bool``, and an application is an application.
18
+
19
+ **Where the types come from.** They are not written in the surface syntax. What
20
+ each argument slot holds is inferred from the formulas TOGETHER by
21
+ :func:`unicode_logic_kit.fol.nodes.analyse_signatures` — ``Positive(G)`` in one
22
+ formula and ``G(x)`` in another jointly say that ``Positive`` takes a property
23
+ of arity 1 — with bound predicate variables renamed apart first so two local
24
+ bindings named ``P`` are never mistaken for one symbol. A property slot that
25
+ nothing in the input constrains defaults to arity 1 and is reported, not
26
+ silently assumed.
27
+
28
+ **Standard semantics, as in the second-order module.** The quantifiers range
29
+ over the prover's full function space, i.e. standard (full) semantics. Validity
30
+ at these orders is not semi-decidable, so an external prover may fail on a valid
31
+ conjecture; the kit emits the problem and does not run one. For Henkin
32
+ (general-models) semantics, add comprehension axioms yourself — this module does
33
+ not do it for you.
34
+
35
+ **Equality** follows the kit's HOL convention: ``=`` / ``≠`` are the
36
+ uninterpreted relations ``feq`` / ``fneq``, not primitive HOL identity.
37
+
38
+ **Constants and free variables.** A constant is a particular individual that its name
39
+ stands for, and a free variable is a parameter of the problem: one unknown element, the
40
+ same in every formula (see :mod:`unicode_logic_kit.fol._free_parameters`). The two are
41
+ different symbols even when they are spelled alike, so ``P(x)`` with a free ``x`` and
42
+ ``P('x')`` are two statements, and each is declared under a name of its own: the second
43
+ ``x`` is written ``x_2``. A constant is never looked up among the bound variables, so no
44
+ binder, of any name, captures it: ``∀x P(x, 'x')`` is written with the bound variable
45
+ ``X_V`` and the constant ``x`` in THF and with ``x_2`` and ``x`` in Isabelle. A constant of
46
+ any name has a legal identifier in the target: a name that is an Isabelle identifier is
47
+ written as it is, and any other (``John Doe``, ``G-910``, ``θ``) under the kit's ASCII
48
+ stem of it; in THF every name goes through that stem. The identifiers are made unique over
49
+ every kind of symbol of the problem, so two names that share a stem (``a b`` and ``a_b``)
50
+ are still two symbols.
51
+
52
+ Public API: :func:`to_thf_to`, :func:`to_isabelle_to`.
53
+ """
54
+
55
+ from typing import Dict, Optional, Sequence
56
+
57
+ from ..fol.nodes import (
58
+ Node, Atom, Not, And, Or, Xor, Implies, Iff, Quantifier,
59
+ SecondOrderQuantifier, PredicateTerm,
60
+ Variable, Constant, Function, LambdaVar, Lambda,
61
+ analyse_signatures,
62
+ )
63
+ from ..fol._ho_nodes import INDIVIDUAL
64
+ from ..fol._truth_constants import truth_value
65
+ from ._ho_common import (
66
+ UnsupportedHigherOrderNode, EQUALITY, FREE_VARIABLE, CONSTANT, ISABELLE_BUILT_IN,
67
+ peel_lambdas, rename_apart, bound_pred_names, atom_predicates,
68
+ function_symbols, individual_symbols, ThfNames, IsabelleNames, bound_token,
69
+ )
70
+ from ._isabelle_binders import (
71
+ PREDICATE, VARIABLE, BinderScope, binder_tokens, collect_binders, declared_names,
72
+ )
73
+
74
+ _ALL = r"\<forall>"
75
+ _EX = r"\<exists>"
76
+ _AND = r"\<and>"
77
+ _OR = r"\<or>"
78
+ _IMP = r"\<longrightarrow>"
79
+ _IFF = r"\<longleftrightarrow>"
80
+ _NOT = r"\<not>"
81
+ _LAM = r"\<lambda>"
82
+ _FUN = r"\<Rightarrow>"
83
+ _NEQ = r"\<noteq>"
84
+
85
+
86
+ def _isa_prop_type(arity: int) -> str:
87
+ """Isabelle type of a property of ``arity`` arguments: ``i => … => bool``."""
88
+ return "".join(f"i {_FUN} " for _ in range(arity)) + "bool"
89
+
90
+
91
+ def _isa_slot_type(kind) -> str:
92
+ """Isabelle type of one argument slot: ``i``, or a parenthesised property type."""
93
+ return "i" if kind == INDIVIDUAL else f"({_isa_prop_type(kind[1])})"
94
+
95
+
96
+ def _thf_prop_type(arity: int) -> str:
97
+ """THF type of a property of ``arity`` arguments: ``$i > … > $o``."""
98
+ return " > ".join(["$i"] * arity + ["$o"])
99
+
100
+
101
+ def _thf_slot_type(kind) -> str:
102
+ """THF type of one argument slot."""
103
+ return "$i" if kind == INDIVIDUAL else f"( {_thf_prop_type(kind[1])} )"
104
+
105
+
106
+ # --------------------------------------------------------------------------
107
+ # Isabelle
108
+ # --------------------------------------------------------------------------
109
+
110
+ _BINARY_ISA = {And: _AND, Or: _OR, Implies: _IMP, Iff: _IFF}
111
+
112
+
113
+ def _isa_arg(node: Node, arity: Dict[str, int], display: Dict[str, str],
114
+ scope: Optional[BinderScope] = None,
115
+ symbols: Optional[IsabelleNames] = None) -> str:
116
+ """Render a node in ARGUMENT position — an individual term or a property.
117
+
118
+ ``scope`` holds the binders that enclose the node and the names they are printed under: a
119
+ variable or a lambda variable bound by one of them is printed under its binder's name. A
120
+ free variable and a constant are not bound by any binder, whatever their spelling: each is
121
+ printed under the name ``symbols`` gives it, and the two have two names. Without a ``scope``
122
+ every binder is printed under its own name.
123
+ """
124
+ if scope is None:
125
+ scope = BinderScope({})
126
+ if symbols is None:
127
+ symbols = IsabelleNames(ISABELLE_BUILT_IN)
128
+ if isinstance(node, (Variable, LambdaVar)):
129
+ # a binder's token is never empty, so "" means: no binder of this name encloses the node
130
+ return scope.token(VARIABLE, node.name, default="") or symbols.symbol(FREE_VARIABLE, node.name)
131
+ if isinstance(node, Constant):
132
+ return symbols.symbol(CONSTANT, node.name)
133
+ if isinstance(node, PredicateTerm):
134
+ return display.get(node.name) or symbols.symbol("predicate", node.name)
135
+ if isinstance(node, Lambda):
136
+ names, body = peel_lambdas(node)
137
+ binders = []
138
+ for name in names:
139
+ token, scope = scope.enter(VARIABLE, name)
140
+ binders.append(f"{_LAM}{token}::i.")
141
+ return f"({' '.join(binders)} {_isa(body, arity, display, scope, symbols)})"
142
+ if isinstance(node, Function):
143
+ head = symbols.symbol("function", node.name)
144
+ args = " ".join(_isa_arg(a, arity, display, scope, symbols) for a in node.args)
145
+ return f"({head} {args})" if args else head
146
+ raise UnsupportedHigherOrderNode(
147
+ f"thirdorder: {type(node).__name__} cannot stand in argument position; an "
148
+ f"argument is an individual term, a predicate name, or a λ-abstraction.")
149
+
150
+
151
+ def _isa(node: Node, arity: Dict[str, int], display: Dict[str, str],
152
+ scope: Optional[BinderScope] = None,
153
+ symbols: Optional[IsabelleNames] = None) -> str:
154
+ """Render ``node`` as an Isabelle/HOL formula.
155
+
156
+ ``display`` gives each bound predicate variable the name it is printed under, ``scope`` each
157
+ enclosing object binder (see :mod:`unicode_logic_kit.hol._isabelle_binders`); without a
158
+ ``scope`` every binder is printed under its own name. ``symbols`` names the free symbols
159
+ (predicates, functions, free variables, constants) and is the one the declarations were
160
+ written under.
161
+ """
162
+ if scope is None:
163
+ scope = BinderScope({})
164
+ if symbols is None:
165
+ symbols = IsabelleNames(ISABELLE_BUILT_IN)
166
+ if isinstance(node, Not):
167
+ return f"({_NOT} {_isa(node.formula, arity, display, scope, symbols)})"
168
+ glyph = _BINARY_ISA.get(type(node))
169
+ if glyph is not None:
170
+ left = _isa(node.left, arity, display, scope, symbols)
171
+ right = _isa(node.right, arity, display, scope, symbols)
172
+ return f"({left} {glyph} {right})"
173
+ if isinstance(node, Xor):
174
+ left = _isa(node.left, arity, display, scope, symbols)
175
+ right = _isa(node.right, arity, display, scope, symbols)
176
+ return f"({left} {_NEQ} {right})"
177
+ if isinstance(node, Quantifier):
178
+ binder = _ALL if node.type == "∀" else _EX
179
+ token, inner = scope.enter(VARIABLE, node.variable.name)
180
+ body = _isa(node.formula, arity, display, inner, symbols)
181
+ return f"({binder}{token}::i. {body})"
182
+ if isinstance(node, SecondOrderQuantifier):
183
+ binder = _ALL if node.type == "∀" else _EX
184
+ k = arity.get(node.predicate, node.arity)
185
+ name = display.get(node.predicate, node.predicate)
186
+ body = _isa(node.formula, arity, display, scope, symbols)
187
+ return f"({binder}{name}::{_isa_prop_type(k)}. {body})"
188
+ if isinstance(node, Atom):
189
+ if truth_value(node) is not None:
190
+ return "True" if truth_value(node) else "False"
191
+ if node.predicate in EQUALITY:
192
+ name = EQUALITY[node.predicate]
193
+ else:
194
+ name = display.get(node.predicate) or symbols.symbol("predicate", node.predicate)
195
+ if not node.args:
196
+ return name
197
+ args = " ".join(_isa_arg(a, arity, display, scope, symbols) for a in node.args)
198
+ return f"({name} {args})"
199
+ raise UnsupportedHigherOrderNode(
200
+ f"thirdorder: no reading for {type(node).__name__}. This export covers "
201
+ f"CLASSICAL third-order syntax; modal operators belong to "
202
+ f"hol.ho_modal.to_isabelle_ho_modal.")
203
+
204
+
205
+ def to_isabelle_to(formula: Node, name: str = "TO_Goal",
206
+ assumptions: Sequence[Node] = (),
207
+ proof: str = None) -> str:
208
+ """Emit a self-contained Isabelle/HOL theory for a classical third-order formula.
209
+
210
+ ``assumptions`` are asserted with ``axiomatization`` and are analysed
211
+ together with ``formula``, so a symbol whose argument types only the
212
+ assumptions determine is still typed correctly. Without a ``proof`` the goal
213
+ is left ``oops`` — the kit states the problem; it does not invent a proof.
214
+
215
+ ``name`` becomes the theory name and must be a legal Isabelle identifier.
216
+ """
217
+ formulas = list(assumptions) + [formula]
218
+ apart, display = rename_apart(formulas)
219
+ signatures = analyse_signatures(apart)
220
+ bound = set()
221
+ for f in apart:
222
+ bound |= bound_pred_names(f)
223
+
224
+ lines = [
225
+ "(* Classical third-order logic -> HOL (predicates of properties are native). *)",
226
+ "(* Standard (full) semantics; validity at this order is NOT semi-decidable, *)",
227
+ "(* so a sound prover may fail to close a valid goal. *)",
228
+ f"theory {name}",
229
+ " imports Main",
230
+ "begin",
231
+ "",
232
+ "typedecl i \\<comment> \\<open>individuals\\<close>",
233
+ "",
234
+ ]
235
+ predicates = [pred for pred in sorted(signatures.slots)
236
+ if not (pred in bound or pred in EQUALITY)]
237
+ individuals = individual_symbols(apart)
238
+ aliases = sorted({EQUALITY[p] for f in apart for p in atom_predicates(f) if p in EQUALITY})
239
+ functions = sorted(function_symbols(apart).items())
240
+ # One pool of identifiers for every symbol the theory declares: a constant is never
241
+ # declared under the name of a free variable, a predicate or a function, nor of an alias
242
+ # of the embedding, and a name that is no Isabelle identifier gets one.
243
+ symbols = IsabelleNames(ISABELLE_BUILT_IN | set(aliases))
244
+ symbols.claim([("predicate", pred) for pred in predicates]
245
+ + [("function", symbol) for symbol, _ in functions] + individuals)
246
+ for pred in predicates:
247
+ arrow = "".join(f"{_isa_slot_type(k)} {_FUN} " for k in signatures.slots[pred])
248
+ lines.append(f'consts {symbols.symbol("predicate", pred)} :: "{arrow}bool"')
249
+ for kind, name in individuals:
250
+ lines.append(f'consts {symbols.symbol(kind, name)} :: "i"')
251
+ for symbol in aliases:
252
+ lines.append(f'consts {symbol} :: "i {_FUN} i {_FUN} bool"')
253
+ for symbol, k in functions:
254
+ arrow = "".join(f"i {_FUN} " for _ in range(k))
255
+ lines.append(f'consts {symbols.symbol("function", symbol)} :: "{arrow}i"')
256
+ lines.append("")
257
+ # A binder shadows a constant of its own spelling inside its scope: every binder (object
258
+ # quantifier, lambda parameter, bound predicate variable) is printed under a name that no
259
+ # declared symbol has. ``display`` gives each renamed-apart predicate variable that name.
260
+ tokens = binder_tokens(collect_binders(formulas), declared_names(lines))
261
+ display = {fresh: tokens[(PREDICATE, original)] for fresh, original in display.items()}
262
+ scope = BinderScope(tokens)
263
+ if signatures.defaulted:
264
+ pairs = ", ".join(f"{p}[{i}]" for p, i in sorted(signatures.defaulted))
265
+ lines.append(f"\\<comment> \\<open>arity defaulted to 1 (nothing in the "
266
+ f"input fixes it): {pairs}\\<close>")
267
+ lines.append("")
268
+ for index, assumption in enumerate(apart[:-1], start=1):
269
+ lines.append(f'axiomatization where assumption{index}: '
270
+ f'"{_isa(assumption, signatures.arity, display, scope, symbols)}"')
271
+ if len(apart) > 1:
272
+ lines.append("")
273
+ lines.append(f'lemma "{_isa(apart[-1], signatures.arity, display, scope, symbols)}"')
274
+ lines.append(f" {proof}" if proof else
275
+ " oops \\<comment> \\<open>try: by auto / by blast / sledgehammer\\<close>")
276
+ lines.append("")
277
+ lines.append("end")
278
+ return "\n".join(lines) + "\n"
279
+
280
+
281
+ # --------------------------------------------------------------------------
282
+ # THF
283
+ # --------------------------------------------------------------------------
284
+
285
+ _BINARY_THF = {And: "&", Or: "|", Implies: "=>", Iff: "<=>", Xor: "<~>"}
286
+
287
+
288
+ def _thf_arg(node: Node, upper: Dict[str, str], display: Dict[str, str],
289
+ names: ThfNames) -> str:
290
+ """Render a node in ARGUMENT position in THF.
291
+
292
+ ``upper`` maps each source name bound in scope to its THF variable token;
293
+ a variable that is not in it is free and spelled through ``names``. A constant
294
+ is never looked up in ``upper``: no binder captures it, whatever its spelling.
295
+ """
296
+ if isinstance(node, (Variable, LambdaVar)):
297
+ return upper.get(node.name) or names.functor(FREE_VARIABLE, node.name)
298
+ if isinstance(node, Constant):
299
+ return names.functor(CONSTANT, node.name)
300
+ if isinstance(node, PredicateTerm):
301
+ return upper.get(node.name) or names.functor("predicate", node.name)
302
+ if isinstance(node, Lambda):
303
+ params, body = peel_lambdas(node)
304
+ fresh = dict(upper)
305
+ for n in params:
306
+ fresh[n] = bound_token(n, "_V", fresh)
307
+ binders = ", ".join(f"{fresh[n]}: $i" for n in params)
308
+ return f"( ^ [{binders}] : {_thf(body, fresh, display, names)} )"
309
+ if isinstance(node, Function):
310
+ functor = names.functor("function", node.name)
311
+ args = " @ ".join(_thf_arg(a, upper, display, names) for a in node.args)
312
+ return f"( {functor} @ {args} )" if node.args else functor
313
+ raise UnsupportedHigherOrderNode(
314
+ f"thirdorder: {type(node).__name__} cannot stand in argument position.")
315
+
316
+
317
+ def _thf(node: Node, upper: Dict[str, str], display: Dict[str, str],
318
+ names: ThfNames) -> str:
319
+ """Render ``node`` as a THF (TH0) formula."""
320
+ if isinstance(node, Not):
321
+ return f"( ~ {_thf(node.formula, upper, display, names)} )"
322
+ glyph = _BINARY_THF.get(type(node))
323
+ if glyph is not None:
324
+ return (f"( {_thf(node.left, upper, display, names)} {glyph} "
325
+ f"{_thf(node.right, upper, display, names)} )")
326
+ if isinstance(node, Quantifier):
327
+ var = bound_token(node.variable.name, "_V", upper)
328
+ fresh = dict(upper, **{node.variable.name: var})
329
+ quant = "!" if node.type == "∀" else "?"
330
+ return f"( {quant} [{var}: $i] : {_thf(node.formula, fresh, display, names)} )"
331
+ if isinstance(node, SecondOrderQuantifier):
332
+ var = bound_token(display.get(node.predicate, node.predicate), "_P", upper)
333
+ fresh = dict(upper, **{node.predicate: var})
334
+ quant = "!" if node.type == "∀" else "?"
335
+ return (f"( {quant} [{var}: {_thf_prop_type(node.arity)}] : "
336
+ f"{_thf(node.formula, fresh, display, names)} )")
337
+ if isinstance(node, Atom):
338
+ if truth_value(node) is not None:
339
+ return "$true" if truth_value(node) else "$false"
340
+ if node.predicate in EQUALITY:
341
+ name = EQUALITY[node.predicate]
342
+ else:
343
+ name = upper.get(node.predicate) or names.functor("predicate", node.predicate)
344
+ if not node.args:
345
+ return name
346
+ args = " @ ".join(_thf_arg(a, upper, display, names) for a in node.args)
347
+ return f"( {name} @ {args} )"
348
+ raise UnsupportedHigherOrderNode(
349
+ f"thirdorder: no THF reading for {type(node).__name__}; this export "
350
+ f"covers classical third-order syntax.")
351
+
352
+
353
+ def to_thf_to(formula: Node, assumptions: Sequence[Node] = (),
354
+ conjecture: bool = True) -> str:
355
+ """Emit a THF (TH0) problem for a classical third-order formula.
356
+
357
+ ``assumptions`` become ``axiom`` formulas and are typed together with the
358
+ goal. With ``conjecture=False`` the formula is emitted as an ``axiom``
359
+ instead — the form to hand a model finder when the question is
360
+ satisfiability rather than validity.
361
+ """
362
+ formulas = list(assumptions) + [formula]
363
+ apart, display = rename_apart(formulas)
364
+ signatures = analyse_signatures(apart)
365
+ bound = set()
366
+ for f in apart:
367
+ bound |= bound_pred_names(f)
368
+
369
+ lines = [
370
+ "% Classical third-order logic -> THF (predicates of properties are native).",
371
+ "% Standard (full) semantics; validity at this order is NOT semi-decidable,",
372
+ "% so a sound prover may fail to close a valid goal.",
373
+ ]
374
+ names = ThfNames(reserved=EQUALITY.values())
375
+ for pred in sorted(signatures.slots):
376
+ if pred in bound or pred in EQUALITY:
377
+ continue
378
+ parts = [_thf_slot_type(k) for k in signatures.slots[pred]]
379
+ thf_type = " > ".join(parts + ["$o"]) if parts else "$o"
380
+ functor = names.functor("predicate", pred)
381
+ lines.append(f"thf({functor}_type, type, ( {functor} : {thf_type} )).")
382
+ for kind, symbol in individual_symbols(apart):
383
+ functor = names.functor(kind, symbol)
384
+ lines.append(f"thf({functor}_type, type, ( {functor} : $i )).")
385
+ for symbol in sorted({EQUALITY[p] for f in apart for p in atom_predicates(f)
386
+ if p in EQUALITY}):
387
+ lines.append(f"thf({symbol}_type, type, ( {symbol} : $i > $i > $o )).")
388
+ for symbol, k in sorted(function_symbols(apart).items()):
389
+ functor = names.functor("function", symbol)
390
+ lines.append(f"thf({functor}_type, type, ( {functor} : "
391
+ f"{' > '.join(['$i'] * (k + 1))} )).")
392
+ for index, assumption in enumerate(apart[:-1], start=1):
393
+ lines.append(f"thf(assumption{index}, axiom, "
394
+ f"( {_thf(assumption, {}, display, names)} )).")
395
+ role = "conjecture" if conjecture else "axiom"
396
+ lines.append(f"thf(goal, {role}, ( {_thf(apart[-1], {}, display, names)} )).")
397
+ return "\n".join(lines) + "\n"
@@ -0,0 +1,89 @@
1
+ """Inductive logic programming: structures in, a learning task out.
2
+
3
+ The other end of :mod:`unicode_logic_kit.fol.prolog_input`. That module reads a
4
+ learner's answer; this one writes the learner's question — the background
5
+ knowledge, examples and language bias an ILP system (Popper, Aleph, Metagol)
6
+ needs, built from the very same
7
+ :class:`~unicode_logic_kit.semantics.structures.FiniteStructure` objects the
8
+ kit's model checker evaluates against. The loop closes:
9
+
10
+ structure --IlpTask--> Prolog task --learner--> clause
11
+ --clause_to_formula--> kit formula --model checker--> verdicts
12
+
13
+ Why a module and not a script
14
+ -----------------------------
15
+ Because two encoding mistakes are easy to make, impossible to see in the
16
+ output, and both produce a hypothesis that scores **precision 1.00 and means
17
+ nothing**. Both were made while building this kit's own pre-trial, and both
18
+ are now structurally impossible here rather than written down as advice:
19
+
20
+ **1. Example-local individual names.** A structure names its individuals
21
+ locally — every molecule has a ``c1``. Emit those raw and the same constant
22
+ denotes a different thing in each example, so a learner can join *across*
23
+ examples. It will:
24
+
25
+ .. code-block:: prolog
26
+
27
+ amide(A) :- bSINGLE(A,B,C), n(D,B), bDOUBLE(D,E,C), bDOUBLE(A,C,E).
28
+
29
+ scored perfectly by hopping between molecules through a shared atom name.
30
+ :class:`IlpTask` prefixes every individual with its example and **refuses the
31
+ task** if two ``(example, individual)`` pairs would still collide — the check
32
+ is in the constructor, so a colliding task cannot be written at all.
33
+
34
+ **2. The example argument on every predicate.** Carry the example everywhere
35
+ (``c(M,X)``, ``bond(M,X,Y)``) and the learner can introduce a *second* example
36
+ variable and connect through it. The resulting clause is not a statement about
37
+ one structure, and cannot be translated into one. Here the example argument
38
+ exists on the membership predicate **alone** — the emitter has no way to put
39
+ it anywhere else — and :func:`clause_to_formula` refuses, on the way back, any
40
+ clause in which the example variable survives dropping the membership atoms.
41
+
42
+ A third guard falls out of the same reasoning: a **0-ary** background
43
+ predicate cannot be attached to an example at all, so it would hold globally
44
+ across every example. It is excluded from an inferred vocabulary (visibly, via
45
+ :attr:`IlpTask.excluded_predicates` and a comment in the emitted file) and
46
+ refused if named explicitly.
47
+
48
+ What this module does NOT do
49
+ ----------------------------
50
+ It does not run a learner. Popper needs SWI-Prolog and, from v4, ``janus_swi``
51
+ — a dependency the kit does not take on for a file format. It writes the three
52
+ files Popper reads (``bk.pl``, ``exs.pl``, ``bias.pl``) via
53
+ :meth:`IlpTask.write`, and Aleph's genuinely different three-file layout
54
+ (``<stem>.b``/``.f``/``.n`` — ``modeh``/``modeb``/``determination`` mode
55
+ declarations rather than ``head_pred``/``body_pred``, bare-atom examples split
56
+ by label rather than one ``pos``/``neg``-wrapped file) via
57
+ :meth:`IlpTask.write_aleph`, and reads back the text either learner prints.
58
+ Everything in between is the learner's business.
59
+
60
+ It also does not decide whether your task is a *good* one. It does offer the
61
+ one check that has to happen first: :func:`check_separation` asks whether a
62
+ reference formula actually separates the two example sets under the kit's own
63
+ model checker. If it does not, the task is broken and no answer from any
64
+ learner would have meant anything.
65
+
66
+ >>> from unicode_logic_kit.semantics import FiniteStructure
67
+ >>> from unicode_logic_kit.ilp import IlpTask, Example
68
+ >>> yes = FiniteStructure(domain=("a1",), extensions={("p", 1): [("a1",)]})
69
+ >>> no = FiniteStructure(domain=("a1",), extensions={("p", 1): []})
70
+ >>> task = IlpTask("target", [Example("e1", yes, True),
71
+ ... Example("e2", no, False)])
72
+ >>> print(task.examples_text())
73
+ pos(target(e1)).
74
+ neg(target(e2)).
75
+ <BLANKLINE>
76
+ """
77
+
78
+ from .task import (
79
+ Example, IlpTask, IlpEncodingError, task_from_structures, to_prolog_atom,
80
+ )
81
+ from .readback import clause_to_formula, hypothesis_to_formulas
82
+ from .separation import SeparationReport, check_separation
83
+
84
+ __all__ = [
85
+ "Example", "IlpTask", "IlpEncodingError", "task_from_structures",
86
+ "to_prolog_atom",
87
+ "clause_to_formula", "hypothesis_to_formulas",
88
+ "SeparationReport", "check_separation",
89
+ ]