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,242 @@
1
+ """Adapter for the Parallel Meaning Bank (PMB): SBN documents, read through
2
+ :mod:`unicode_logic_kit.drt` into gold FOL via :func:`~unicode_logic_kit.drt.parser.parse_sbn`
3
+ and :func:`~unicode_logic_kit.drt.export.drs_to_fol`.
4
+
5
+ Unlike every other adapter in this package, PMB carries no ready-made NL/FOL pair file:
6
+ its unit of data is ONE DIRECTORY PER DOCUMENT (``p<NN>/d<NNNN>/``), holding the DRS in
7
+ SBN notation (``<lang>.drs.sbn``) alongside the raw sentence(s) it annotates
8
+ (``<lang>.raw``) and a few other sibling files this adapter does not read. This module's
9
+ whole job is producing gold FOL from the SBN side; the NL side is carried through
10
+ best-effort, since that is what the sibling file happens to contain.
11
+
12
+ Source, release layout, and license (verified 2026-09-17 against a local pmb-5.1.0
13
+ extraction's own ``licenses/`` directory)
14
+ --------------------------------------------------------------------------------------
15
+ Source: https://pmb.let.rug.nl/data.php (releases, ``pmb-<version>.zip``). Like every
16
+ loader in this package, :func:`load_pmb` reads LOCAL files the caller already obtained —
17
+ nothing here downloads anything.
18
+
19
+ Each PMB release's ``data/<lang>/gold/p<NN>/d<NNNN>/`` directory is one annotated
20
+ document, ``<lang>`` a two-letter code (``en``, ``de``, ``it``, ``nl``, ...). This
21
+ adapter reads whichever ``<lang>.drs.sbn`` files it is given or finds; it does not
22
+ special-case English.
23
+
24
+ **License is TWO-LAYERED, and the two layers are NOT the same license** (the raw
25
+ proposal that motivated this module conflated them — corrected here):
26
+
27
+ * The PMB **annotations** (the DRS/SBN layer this module actually parses) are
28
+ Open Data Commons Attribution License v1.0 (ODC-BY —
29
+ https://opendatacommons.org/licenses/by/1.0/), stated plainly in the release's own
30
+ ``licenses/PMB_LICENSE.txt``: share/create/adapt freely, attribution required.
31
+ * The **raw texts** (``<lang>.raw``, what :func:`load_pmb` puts in ``nl_conclusion``)
32
+ are explicitly NOT covered by ODC-BY: the same ``PMB_LICENSE.txt`` says each is
33
+ "published under their respective license terms, as noted in their accompanying .met
34
+ files" — but in the actual pmb-5.1.0 release, a document's ``.met`` file records only
35
+ a ``subcorpus:`` label (``Tatoeba``, ``SICK``, ``RTE``, ``TREC2002``/``TREC2003``,
36
+ ``INTERSECT``, ``GMB``, ...) and a ``source:`` URL, never an explicit license string —
37
+ so a caller who wants to REDISTRIBUTE ``nl_conclusion`` text must trace that source
38
+ themselves (the release's ``licenses/*-README.txt`` files document several of the
39
+ subcorpora's own terms, e.g. ``SICK-README.txt``, ``Tatoeba_LICENSE.txt``). This
40
+ adapter reads a document's ``.met`` file for neither field (out of scope: neither the
41
+ DRS nor the label this module produces depends on it) and copies no PMB file into this
42
+ repository — a caller redistributing what :func:`load_pmb` yields is bound by the same
43
+ two-layer license above, not a blanket statement this module could truthfully make.
44
+
45
+ How much of the SBN release does :func:`parse_sbn` actually accept? — measured, not
46
+ guessed
47
+ ------------------------------------------------------------------------------------
48
+ Run against pmb-5.1.0's complete English gold release, all 12053
49
+ ``data/en/gold/p*/d*/en.drs.sbn`` files (2026-09-17): **9624 / 12053 (79.8%) parse AND
50
+ round-trip** (``drs_to_fol(parse_sbn(text)[0]).to_unicode_str()`` re-parses via
51
+ :func:`unicode_logic_kit.api.parse_any` to the byte-identical string). Every one of the
52
+ remaining 2429 raises :class:`~unicode_logic_kit.drt.parser.SBNSyntaxError` naming an
53
+ out-of-scope construct — NONE raises anything else (no crash). By construct:
54
+
55
+ * 1123 — a WILDCARD role target (``?``, ``+``, or bare ``-``): boxer's own placeholder
56
+ for a wh-question's unresolved filler ("which singer" -> ``Name ?``) or a vague
57
+ comparative degree ("long hair" -> ``Value +``). Neither has a determinate DRS reading.
58
+ * 953 — a box-operator this subset does not support: the SDRT discourse relations
59
+ (``CONTINUATION`` 448, ``CONTRAST`` 91, ``CONJUNCTION`` 78, ``CONSEQUENCE`` 50,
60
+ ``EXPLANATION`` 42, ``CONDITION`` 5, ``PRECONDITION`` 4, ``RESULT`` 4,
61
+ ``ALTERNATION`` 2, ``COMMENTARY`` 1, ``ELABORATION`` 1) and the two modal operators
62
+ (``POSSIBILITY`` 174, ``NECESSITY`` 53) — this kit's DRS conditions have no
63
+ discourse-relation or modal-box reading.
64
+ * 162 — a clock-time constant (``ClockTime 14:30``): a genuine PMB constant kind this
65
+ subset's bare-constant widening (deictic words, integers) does not cover.
66
+ * 116 — a sense token not of the WordNet form ``lemma.pos.NN`` (this subset's
67
+ ``LEMMA`` is ``[a-z][a-z_]*``, see :mod:`unicode_logic_kit.drt.parser`'s module
68
+ docstring), split three ways: 95 with a hyphen inside the lemma (``ping-pong.n.01``,
69
+ ``t-shirt.n.01``, ``well-known.a.01``, ...) — a real WordNet compounding convention
70
+ this module does not widen ``LEMMA`` for, unlike the (deliberately narrower) role-name
71
+ hyphen it does accept; 19 with an apostrophe/possessive inside the lemma
72
+ (``parkinson's_disease.n.01``, ``ladies'_room.n.01``, ``alzheimer's.n.01``,
73
+ ``old_wives'_tale.n.01``, ...) — a DIFFERENT WordNet convention from hyphenation, not
74
+ a subcase of it; and 2 with a digit inside the lemma (``co2.n.01``, ``fiat_500.n.01``).
75
+ * a long tail (75) of one-off constants this subset does not parse: 10 ``YearOfCentury``
76
+ decade constants (``'198X'``), 21 sports-score values (``6-2``), 16 day-of-week names
77
+ (``monday``), 12 decimal and 4 fraction ``Quantity`` values (``3.4``, ``3/4``), 10
78
+ single-letter grade values, one role-hook offset that points outside its document, and
79
+ one DRS accessibility violation (:meth:`~unicode_logic_kit.drt.nodes.DRS.validate`
80
+ refusing a role target that escapes its own negation scope).
81
+
82
+ This is a ROBUSTNESS/coverage measurement, not a translation-ACCURACY one: PMB ships no
83
+ independent gold FOL to compare the 9624 successes against, so "correct" here means
84
+ "parses under this subset's own documented grammar and round-trips through this kit's
85
+ FOL printer/parser" — see ``tests/test_datasets_pmb.py`` for the differential this
86
+ number comes from, gated behind the ``UFK_PMB_SBN_DIR`` environment variable so it never
87
+ runs without a real, locally-extracted release.
88
+ """
89
+
90
+ from pathlib import Path
91
+ from typing import FrozenSet, Iterable, Iterator, Optional, Tuple, Union
92
+
93
+ from ._base import DatasetExample, _register_dataset_info
94
+ from ...drt.export import drs_to_fol
95
+ from ...drt.parser import SBNSyntaxError, parse_sbn
96
+
97
+ __all__ = ["load_pmb"]
98
+
99
+
100
+ _register_dataset_info(
101
+ "pmb",
102
+ license=(
103
+ "TWO layers, not one (see the module docstring for the full derivation): the "
104
+ "PMB ANNOTATIONS (the DRS/SBN layer this loader parses) are ODC-BY 1.0 "
105
+ "(https://opendatacommons.org/licenses/by/1.0/, attribution required) per the "
106
+ "release's own licenses/PMB_LICENSE.txt (verified 2026-09-17); the RAW TEXTS "
107
+ "(nl_conclusion) are explicitly NOT ODC-BY -- PMB_LICENSE.txt defers to each "
108
+ "document's own .met file, which in practice records only a subcorpus label "
109
+ "and a source URL, never an explicit license string, so redistributing raw "
110
+ "sentence text requires tracing that source yourself."
111
+ ),
112
+ source_url="https://pmb.let.rug.nl/data.php",
113
+ citation_hint=(
114
+ 'Abzianidze et al., "The Parallel Meaning Bank: Towards a Multilingual Corpus '
115
+ 'of Translations Annotated with Compositional Meaning Representations", '
116
+ "EACL 2017."
117
+ ),
118
+ )
119
+
120
+
121
+ def _iter_sbn_paths(paths: Union[str, Path, Iterable[Union[str, Path]]]) -> Iterator[Path]:
122
+ """Normalize ``paths`` (see :func:`load_pmb`) into a sorted stream of ``.drs.sbn``
123
+ file paths: a single directory is WALKED for PMB's own release layout
124
+ (``p<NN>/d<NNNN>/<lang>.drs.sbn``, any depth/part/language); anything else is
125
+ treated as an iterable of file paths the caller already obtained."""
126
+ if isinstance(paths, (str, Path)):
127
+ root = Path(paths)
128
+ if not root.is_dir():
129
+ raise ValueError(
130
+ f"pmb: {root} is not a directory to walk — pass an iterable of "
131
+ f".drs.sbn file paths instead, or a real PMB release directory")
132
+ yield from sorted(root.rglob("*.drs.sbn"))
133
+ return
134
+ for p in paths:
135
+ yield Path(p)
136
+
137
+
138
+ def _doc_id(path: Path) -> Tuple[str, str, str]:
139
+ """PMB's own ``p<NN>/d<NNNN>`` document identifier, read off ``path``'s two parent
140
+ directories (the DOCUMENT directory, then the PART directory it sits in) — the
141
+ release's own addressing scheme, not a positional fallback."""
142
+ doc_dir = path.parent.name
143
+ part_dir = path.parent.parent.name
144
+ return f"{part_dir}/{doc_dir}", part_dir, doc_dir
145
+
146
+
147
+ def _sibling_raw_text(sbn_path: Path) -> Optional[str]:
148
+ """The sentence(s) ``sbn_path`` annotates, read from the sibling ``<lang>.raw``
149
+ file PMB ships alongside every ``<lang>.drs.sbn`` — BEST-EFFORT: ``None`` if that
150
+ file is missing or empty (never raised; a missing raw sibling says nothing about
151
+ whether the SBN itself, which is what this adapter is really reading, is usable)."""
152
+ name = sbn_path.name
153
+ if not name.endswith(".drs.sbn"):
154
+ return None
155
+ lang = name[: -len(".drs.sbn")]
156
+ raw_path = sbn_path.with_name(f"{lang}.raw")
157
+ if not raw_path.is_file():
158
+ return None
159
+ text = raw_path.read_text(encoding="utf-8").strip()
160
+ return text or None
161
+
162
+
163
+ def load_pmb(paths: Union[str, Path, Iterable[Union[str, Path]]], *,
164
+ known_bad_ids: Optional[FrozenSet[str]] = None,
165
+ ) -> Iterator[DatasetExample]:
166
+ """Read PMB SBN documents into :class:`DatasetExample` objects, one per document.
167
+
168
+ Field mapping: ``id`` = the ``p<NN>/d<NNNN>`` fragment PMB itself addresses the
169
+ document by; ``nl_premises``/``fol_premises`` are always ``()`` (PMB annotates
170
+ single documents, not premise/conclusion pairs); ``nl_conclusion`` = the sibling
171
+ ``<lang>.raw`` text (best-effort — see :func:`_sibling_raw_text` — ``None`` if
172
+ absent); ``fol_conclusion`` = ``drs_to_fol(parse_sbn(text)[0]).to_unicode_str()``;
173
+ ``label`` is always ``None`` (PMB carries no entailment/classification label).
174
+
175
+ A document whose ``parse_sbn`` call raises
176
+ :class:`~unicode_logic_kit.drt.parser.SBNSyntaxError` (see the module docstring for
177
+ how common this is, and why) is NEVER silently dropped: it still yields a
178
+ :class:`DatasetExample`, with ``fol_conclusion=None`` (the same "nothing to check"
179
+ convention :mod:`~unicode_logic_kit.eval.datasets.fracas` uses for its four
180
+ question-less problems) and the refusal's message recorded verbatim in
181
+ ``meta["parse_error"]`` — the SAME "refuse loudly, never approximate" contract
182
+ ``parse_sbn`` itself follows, carried one level up instead of raised out of this
183
+ generator (which would abort the whole iteration on the FIRST bad document, useless
184
+ for a corpus of thousands).
185
+
186
+ ``meta`` also carries ``part``/``doc`` (the two path components ``id`` is built
187
+ from), ``lang`` (the language code ``<lang>.drs.sbn`` was read from), and, on a
188
+ successful parse, ``sbn_mapping`` (:meth:`~unicode_logic_kit.drt.parser.SBNMapping.to_dict`
189
+ — the sense-token/quoted-constant sanitization :func:`parse_sbn` applied, ``None``
190
+ when parsing failed).
191
+
192
+ Args:
193
+ paths: EITHER a single local directory to WALK for PMB's own release layout
194
+ (``p<NN>/d<NNNN>/<lang>.drs.sbn``, any language/part, any depth), OR an
195
+ iterable of local ``.drs.sbn`` file paths the caller already obtained.
196
+ Nothing here downloads anything, matching every other adapter in this
197
+ package.
198
+ known_bad_ids: ids (in the ``"pNN/dNNNN"`` form) to flag as ``known_bad`` — the
199
+ same caller-curated mechanic every adapter has; independent of, and not
200
+ inferred from, a document's own ``parse_error``.
201
+
202
+ Raises:
203
+ ValueError: ``paths`` is a single path that is not a directory, or the same
204
+ document id is produced twice (a caller data-integrity problem — e.g. two
205
+ different release trees passed together — named rather than silently
206
+ merged).
207
+ """
208
+ known_bad = frozenset(known_bad_ids) if known_bad_ids else frozenset()
209
+ seen: set = set()
210
+
211
+ for sbn_path in _iter_sbn_paths(paths):
212
+ example_id, part, doc = _doc_id(sbn_path)
213
+ if example_id in seen:
214
+ raise ValueError(
215
+ f"pmb: duplicate document id {example_id!r} (from {sbn_path}) — "
216
+ f"the same p<NN>/d<NNNN> was produced twice")
217
+ seen.add(example_id)
218
+
219
+ lang = sbn_path.name[: -len(".drs.sbn")] if sbn_path.name.endswith(".drs.sbn") else None
220
+ text = sbn_path.read_text(encoding="utf-8")
221
+ nl_conclusion = _sibling_raw_text(sbn_path)
222
+
223
+ meta: dict = {"part": part, "doc": doc, "lang": lang,
224
+ "sbn_mapping": None, "parse_error": None}
225
+ fol_conclusion: Optional[str] = None
226
+ try:
227
+ drs, mapping = parse_sbn(text)
228
+ fol_conclusion = drs_to_fol(drs).to_unicode_str()
229
+ meta["sbn_mapping"] = mapping.to_dict()
230
+ except SBNSyntaxError as e:
231
+ meta["parse_error"] = str(e)
232
+
233
+ yield DatasetExample(
234
+ id=example_id,
235
+ nl_premises=(),
236
+ fol_premises=(),
237
+ nl_conclusion=nl_conclusion,
238
+ fol_conclusion=fol_conclusion,
239
+ label=None,
240
+ known_bad=example_id in known_bad,
241
+ meta=meta,
242
+ )