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,287 @@
1
+ """Public Announcement Logic (PAL): syntactic reduction of announcements.
2
+
3
+ Public announcement logic extends static modal logic with an update operator: a
4
+ truthful public announcement of ``φ`` deletes every world where ``φ`` is false, and
5
+ ``[φ!]ψ`` / ``⟨φ!⟩ψ`` (see :mod:`unicode_logic_kit.fol._modal_nodes`, classes
6
+ :class:`~unicode_logic_kit.fol._modal_nodes.Announce` /
7
+ :class:`~unicode_logic_kit.fol._modal_nodes.AnnounceDiamond`) say what holds *after*
8
+ that update. :func:`reduce_announcements` implements the standard PAL result that
9
+ every announcement is ELIMINABLE: PAL over the propositional-modal fragment
10
+ interpreted here is exactly as expressive as the announcement-free base logic, via
11
+ the reduction axioms below.
12
+
13
+ Public API: :func:`reduce_announcements`.
14
+
15
+ CORRECTNESS ARGUMENT
16
+ =====================
17
+
18
+ The whole reduction rests on one invariant, proved by induction on ψ:
19
+
20
+ (INV) M, w ⊨ ψ^φ IFF M|φ, w ⊨ ψ (for every w with M, w ⊨ φ)
21
+
22
+ — "ψ RELATIVIZED to φ, evaluated in the ORIGINAL model M, agrees with ψ evaluated
23
+ in the φ-RESTRICTED model M|φ" (:func:`unicode_logic_kit.semantics.dynamic_epistemic.announce`
24
+ builds M|φ: the submodel on the worlds satisfying φ, with every relation and the
25
+ valuation cut down to survivors). Given (INV), the announcement operators reduce
26
+ immediately from their own truth conditions
27
+ (:func:`unicode_logic_kit.semantics.kripke.satisfies_modal`'s Announce/AnnounceDiamond
28
+ cases, restated in :mod:`unicode_logic_kit.semantics.dynamic_epistemic`'s module
29
+ docstring)::
30
+
31
+ M, w ⊨ [φ!]ψ iff M,w⊨φ implies M|φ,w⊨ψ iff (INV) M,w⊨φ implies M,w⊨ψ^φ
32
+ iff M, w ⊨ (φ → ψ^φ)
33
+
34
+ M, w ⊨ ⟨φ!⟩ψ iff M,w⊨φ and M|φ,w⊨ψ iff (INV) M,w⊨φ and M,w⊨ψ^φ
35
+ iff M, w ⊨ (φ ∧ ψ^φ)
36
+
37
+ which are exactly the two clauses :func:`reduce_announcements` applies to a
38
+ top-level Announce/AnnounceDiamond node (with the inner announcement-free ψ, φ
39
+ already produced by pre-reducing everything below them — "innermost first").
40
+
41
+ Proof of (INV), by structural induction on ψ (each case implements the matching
42
+ standard PAL reduction axiom):
43
+
44
+ * ``ψ = p`` (atom): p^φ = p. p's truth value only depends on the valuation at w,
45
+ which M|φ COPIES verbatim from M for every survivor w — so M,w⊨p iff M|φ,w⊨p.
46
+ * ``ψ = ¬χ``, ``χ₁∘χ₂`` (∘ ∈ {∧,∨,→,↔,⊕}): the classical connectives commute with
47
+ restriction (M|φ's valuation at w is M's, unchanged) and with the induction
48
+ hypothesis applied to χ / χ₁ / χ₂ at the SAME world w, which — because w ⊨ φ
49
+ throughout this induction — is still a world of M|φ. So the classical clauses
50
+ relativize by simply relativizing their immediate subformulas: (¬χ)^φ=¬χ^φ,
51
+ (χ₁∘χ₂)^φ = χ₁^φ ∘ χ₂^φ.
52
+ * ``ψ = K_a χ`` (and identically B_a / Say_a / Want_a / □ / Ⓞ, every UNIVERSAL —
53
+ "box" — modality): M|φ,w ⊨ K_a χ iff χ holds at every "K:a"-successor of w that
54
+ SURVIVED the restriction (M|φ only keeps edges between survivors) — i.e. at
55
+ every successor v of w in M with v⊨φ. By the induction hypothesis (applicable
56
+ at each such v, since v⊨φ), that is M,v⊨χ^φ. So the whole condition is: for
57
+ every M-successor v of w, v⊨φ implies M,v⊨χ^φ — i.e. M,w ⊨ K_a(φ → χ^φ). Hence
58
+ (K_aχ)^φ = K_a(φ → χ^φ) (identically for B_a, Say_a, Want_a, □, Ⓞ, each over its
59
+ own relation).
60
+ * ``ψ = ◇χ`` (and identically Ⓟ, every EXISTENTIAL — "diamond" — modality): dual
61
+ reasoning — M|φ,w ⊨ ◇χ iff SOME surviving successor v (so v⊨φ) has M|φ,v⊨χ, iff
62
+ (by the induction hypothesis) M,v⊨χ^φ — i.e. some M-successor v has v⊨φ AND
63
+ M,v⊨χ^φ, i.e. M,w ⊨ ◇(φ ∧ χ^φ). Hence (◇χ)^φ = ◇(φ ∧ χ^φ) (identically Ⓟ).
64
+ * ``ψ = [χ!]ω`` (a NESTED announcement): reduced FIRST, entirely on its own —
65
+ ``([χ!]ω)^φ`` is defined as ``ρ^φ`` where ``ρ = reduce_announcements([χ!]ω)`` is
66
+ the (announcement-free) result of reducing the inner announcement by itself.
67
+ This is sound because reduce_announcements is truth-preserving in M (it is
68
+ exactly this same theorem, applied one level down: M,v⊨[χ!]ω iff M,v⊨ρ, at
69
+ every world v, by structural induction on the announcement itself) — so
70
+ substituting ρ for [χ!]ω anywhere and THEN relativizing by φ computes the same
71
+ (INV) as relativizing [χ!]ω directly would, if the latter were defined. This
72
+ is the "innermost-first" rule: :func:`reduce_announcements` eliminates deeper
73
+ announcements before an enclosing one is relativized, so RELATIVIZE (the
74
+ induction above) is only ever asked to handle an announcement-free ψ.
75
+
76
+ Where the induction BREAKS — REJECTED, not silently mishandled:
77
+
78
+ * Temporal closure / one-step operators (Ⓖ Ⓕ Ⓝ Ⓤ ⒣ ⒫ ⒴ ⒮): the induction step for
79
+ a box/diamond modality above crucially used that M|φ's accessibility relation is
80
+ M's relation RESTRICTED to survivors — a ONE-STEP fact. Always/Eventually/Until
81
+ (and their past duals) quantify over the REFLEXIVE-TRANSITIVE CLOSURE of the
82
+ one-step relation; the closure of the RESTRICTED relation is in general a
83
+ STRICT SUBSET of the RESTRICTION of the closure (a path through M can visit a
84
+ ¬φ-world and come back to φ-worlds; M|φ has no such path, since it never
85
+ contains the ¬φ-world at all, but M's closure "sees past" it) — "restriction of
86
+ a closure relation is not the closure of the restriction". So no box/diamond-style
87
+ axiom is sound for these operators under an announcement, including the
88
+ one-step Next: this module rejects it too, deliberately more conservative than
89
+ a operator-by-operator soundness argument might require, rather than risk
90
+ publishing an axiom that has not been verified.
91
+ * Would / Might (Lewis counterfactuals): interpreted over a similarity ordering
92
+ of worlds (Lewis spheres), not an accessibility relation — restriction has no
93
+ defined action on a sphere system in this codebase, so relativizing them is
94
+ undefined, not merely unproven.
95
+ * Nominal / At (hybrid H(@)): a nominal names EXACTLY ONE world; announcement
96
+ restriction can delete that very world (if it fails φ), after which the
97
+ nominal names nothing — undefined, not merely unproven.
98
+ * Quantifier / SortedQuantifier (first-order): :func:`satisfies_modal` interprets
99
+ these ACTUALISTICALLY over a per-world domain ``D_w``; :func:`announce`'s model
100
+ restriction never updates ``domains``, so relativizing a quantifier would be
101
+ reasoning about domains the restriction does not touch — out of scope here
102
+ (the propositional/ground modal fragment this reduction targets never needs
103
+ one anyway).
104
+
105
+ Any of the above found ANYWHERE inside the ψ (or φ) being relativized — however
106
+ deeply buried — raises :class:`NotImplementedError` identifying which family and
107
+ pointing at :func:`unicode_logic_kit.semantics.dynamic_epistemic.announce` /
108
+ :func:`unicode_logic_kit.semantics.kripke.satisfies_modal` for direct, MODEL-level
109
+ (rather than syntactic) evaluation, which handles Announce/AnnounceDiamond
110
+ directly and has no such restriction.
111
+
112
+ EXPORTS: :func:`reduce_announcements` is the sanctioned route from a PAL formula to
113
+ every classical modal back-end. Since Announce/AnnounceDiamond nodes themselves
114
+ reject to_z3/to_prover9/to_tptp (and the Isabelle/THF/Isabelle-Isabelle embeddings
115
+ in :mod:`unicode_logic_kit.hol.isabelle_modal` / :mod:`unicode_logic_kit.hol.thf_modal`
116
+ have no case for them either, so they raise the same generic "unsupported node
117
+ type" error every other out-of-scope construct does), a PAL formula bound for any
118
+ of those back-ends — or for :mod:`unicode_logic_kit.atp.modal_tableau` (which applies
119
+ this reduction itself, as a pre-pass, on every public entry point) — MUST be
120
+ reduced first: ``to_isabelle_modal(reduce_announcements(formula))``, etc.
121
+ """
122
+
123
+ from .nodes import (
124
+ Node,
125
+ Atom, Not, And, Or, Xor, Implies, Iff,
126
+ Box, Diamond, Knows, Believes, Says, Wants,
127
+ Always, Eventually, Next, Until,
128
+ Historically, Once, Previous, Since,
129
+ Obligatory, Permitted,
130
+ Would, Might,
131
+ Nominal, At,
132
+ Quantifier, SortedQuantifier,
133
+ )
134
+ from ._modal_nodes import Announce, AnnounceDiamond
135
+
136
+ __all__ = ["reduce_announcements"]
137
+
138
+
139
+ # Node types with no sound relativization rule (see the module docstring's
140
+ # "Where the induction BREAKS" section) — grouped by WHY, so the raised message
141
+ # names the exact reason rather than a generic "unsupported".
142
+ _TEMPORAL_UNDER_ANNOUNCEMENT = (Always, Eventually, Next, Until,
143
+ Historically, Once, Previous, Since)
144
+ _TEMPORAL_MSG = (
145
+ "pal.reduce_announcements: {name} (a temporal operator) cannot be "
146
+ "relativized under an announcement: Always/Eventually/Until (and their past "
147
+ "duals Historically/Once/Since, and the one-step Next) quantify over the "
148
+ "REFLEXIVE-TRANSITIVE CLOSURE of the one-step temporal relation, and "
149
+ "restriction of a CLOSURE relation is not the closure of the restriction — "
150
+ "the box/diamond reduction axiom this module uses for K_a/B_a/□/Ⓞ/◇/Ⓟ is "
151
+ "UNSOUND here. Evaluate the announcement directly instead: "
152
+ "unicode_logic_kit.semantics.kripke.satisfies_modal (which interprets "
153
+ "Announce/AnnounceDiamond via the restricted-model semantics of "
154
+ "unicode_logic_kit.semantics.dynamic_epistemic.announce) has no such "
155
+ "restriction."
156
+ )
157
+ _COUNTERFACTUAL_MSG = (
158
+ "pal.reduce_announcements: {name} (a Lewis counterfactual, □→/◇→) cannot be "
159
+ "relativized under an announcement: it is evaluated over a similarity "
160
+ "ordering of worlds (Lewis spheres), not an accessibility relation, and "
161
+ "announcement restriction has no defined action on a sphere system in this "
162
+ "codebase. Evaluate the announcement directly with "
163
+ "unicode_logic_kit.semantics.kripke.satisfies_modal instead."
164
+ )
165
+ _HYBRID_MSG = (
166
+ "pal.reduce_announcements: {name} (a hybrid-logic construct, a nominal or "
167
+ "@) cannot be relativized under an announcement: a nominal names EXACTLY "
168
+ "ONE world, and announcement restriction can delete that very world (if it "
169
+ "fails the announcement), after which the nominal would name nothing. "
170
+ "Evaluate the announcement directly with "
171
+ "unicode_logic_kit.semantics.kripke.satisfies_modal instead."
172
+ )
173
+ _QUANTIFIER_MSG = (
174
+ "pal.reduce_announcements: {name} (a first-order quantifier) cannot be "
175
+ "relativized under an announcement: satisfies_modal interprets object "
176
+ "quantifiers actualistically over a per-world domain D_w, and "
177
+ "dynamic_epistemic.announce's model restriction never updates the domains "
178
+ "map, so there is no sound reduction axiom for a quantifier here. Evaluate "
179
+ "the announcement directly with unicode_logic_kit.semantics.kripke.satisfies_modal "
180
+ "instead (with domains= set on the KripkeModel)."
181
+ )
182
+
183
+
184
+ def _relativize(psi: Node, phi: Node) -> Node:
185
+ """Compute ψ^φ (ψ relativized to the announced φ) — see the module docstring's
186
+ CORRECTNESS ARGUMENT for the invariant this implements and its proof.
187
+
188
+ Precondition: ψ is already announcement-free (the caller pre-reduces every
189
+ nested Announce/AnnounceDiamond bottom-up before relativizing), and φ is
190
+ likewise announcement-free.
191
+ """
192
+ if isinstance(psi, Atom):
193
+ return psi
194
+ if isinstance(psi, Not):
195
+ return Not(_relativize(psi.formula, phi))
196
+ if isinstance(psi, And):
197
+ return And(_relativize(psi.left, phi), _relativize(psi.right, phi))
198
+ if isinstance(psi, Or):
199
+ return Or(_relativize(psi.left, phi), _relativize(psi.right, phi))
200
+ if isinstance(psi, Xor):
201
+ return Xor(_relativize(psi.left, phi), _relativize(psi.right, phi))
202
+ if isinstance(psi, Implies):
203
+ return Implies(_relativize(psi.left, phi), _relativize(psi.right, phi))
204
+ if isinstance(psi, Iff):
205
+ return Iff(_relativize(psi.left, phi), _relativize(psi.right, phi))
206
+
207
+ # --- box-wise (universal) modalities: (Xψ)^φ = X(φ → ψ^φ) ---
208
+ if isinstance(psi, Knows):
209
+ return Knows(psi.agent, Implies(phi, _relativize(psi.formula, phi)))
210
+ if isinstance(psi, Believes):
211
+ return Believes(psi.agent, Implies(phi, _relativize(psi.formula, phi)))
212
+ if isinstance(psi, Says):
213
+ return Says(psi.agent, Implies(phi, _relativize(psi.formula, phi)))
214
+ if isinstance(psi, Wants):
215
+ return Wants(psi.agent, Implies(phi, _relativize(psi.formula, phi)))
216
+ if isinstance(psi, Box):
217
+ return Box(Implies(phi, _relativize(psi.formula, phi)))
218
+ if isinstance(psi, Obligatory):
219
+ return Obligatory(Implies(phi, _relativize(psi.formula, phi)))
220
+
221
+ # --- diamond-wise (existential) modalities: (Xψ)^φ = X(φ ∧ ψ^φ) ---
222
+ if isinstance(psi, Diamond):
223
+ return Diamond(And(phi, _relativize(psi.formula, phi)))
224
+ if isinstance(psi, Permitted):
225
+ return Permitted(And(phi, _relativize(psi.formula, phi)))
226
+
227
+ # --- nested announcement: eliminate it FIRST (on its own), THEN relativize
228
+ # the announcement-free result — "innermost first" (module docstring). Only
229
+ # reachable if a caller invokes _relativize directly on un-pre-reduced input;
230
+ # reduce_announcements itself always pre-reduces, so this never fires there.
231
+ if isinstance(psi, (Announce, AnnounceDiamond)):
232
+ return _relativize(reduce_announcements(psi), phi)
233
+
234
+ # --- no sound rule: reject with a precise, family-specific explanation ---
235
+ if isinstance(psi, _TEMPORAL_UNDER_ANNOUNCEMENT):
236
+ raise NotImplementedError(_TEMPORAL_MSG.format(name=type(psi).__name__))
237
+ if isinstance(psi, (Would, Might)):
238
+ raise NotImplementedError(_COUNTERFACTUAL_MSG.format(name=type(psi).__name__))
239
+ if isinstance(psi, (Nominal, At)):
240
+ raise NotImplementedError(_HYBRID_MSG.format(name=type(psi).__name__))
241
+ if isinstance(psi, (Quantifier, SortedQuantifier)):
242
+ raise NotImplementedError(_QUANTIFIER_MSG.format(name=type(psi).__name__))
243
+
244
+ raise NotImplementedError(
245
+ f"pal.reduce_announcements: no relativization rule for "
246
+ f"{type(psi).__name__} under an announcement.")
247
+
248
+
249
+ def _reduce(node: Node) -> Node:
250
+ """Bottom-up worker for :func:`reduce_announcements`: reduce every child
251
+ first (innermost first), then eliminate a top-level Announce/AnnounceDiamond.
252
+ """
253
+ if isinstance(node, (Announce, AnnounceDiamond)):
254
+ phi = _reduce(node.announcement)
255
+ psi = _reduce(node.formula)
256
+ relativized = _relativize(psi, phi)
257
+ if isinstance(node, Announce):
258
+ return Implies(phi, relativized)
259
+ return And(phi, relativized)
260
+ # Not an announcement: recurse structurally into every Node-valued field
261
+ # (Node.map_children — the generic engine every other structural pass in
262
+ # this codebase uses), so an announcement buried under ANY other
263
+ # construct (∧, K_a, Ⓖ, ∀x, …) is still found and eliminated, as long as
264
+ # it does not itself end up inside a relativization that rejects it.
265
+ return node.map_children(_reduce)
266
+
267
+
268
+ def reduce_announcements(formula: Node) -> Node:
269
+ """Eliminate every :class:`Announce` / :class:`AnnounceDiamond` in ``formula``,
270
+ returning an equivalent announcement-free modal formula.
271
+
272
+ Applies the standard PAL reduction axioms — ``[φ!]ψ ≡ φ → ψ^φ`` and
273
+ ``⟨φ!⟩ψ ≡ φ ∧ ψ^φ`` — bottom-up, so a NESTED announcement (whether inside the
274
+ announcement or the post-condition of an outer one) is reduced before the
275
+ outer one is relativized. See the module docstring for the full correctness
276
+ argument (the relativization invariant ``M,w⊨ψ^φ iff M|φ,w⊨ψ`` and its proof)
277
+ and for exactly which constructs raise :class:`NotImplementedError` when they
278
+ occur under an announcement's scope (temporal closure/one-step operators,
279
+ Lewis counterfactuals, hybrid nominals/@, and first-order quantifiers) —
280
+ always because the reduction is UNSOUND or UNDEFINED there, never silently
281
+ approximated.
282
+
283
+ A formula with no announcement anywhere is returned as a structurally equal
284
+ (but freshly rebuilt) copy — a safe no-op pass. Idempotent: reducing an
285
+ already announcement-free formula returns an equal formula unchanged.
286
+ """
287
+ return _reduce(formula)