unicode-logic-kit 0.31.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- unicode_logic_kit/__init__.py +385 -0
- unicode_logic_kit/__main__.py +520 -0
- unicode_logic_kit/_deadline.py +219 -0
- unicode_logic_kit/ace/__init__.py +126 -0
- unicode_logic_kit/ace/_align.py +135 -0
- unicode_logic_kit/ace/chem_lexicon.py +128 -0
- unicode_logic_kit/ace/drs_reader.py +570 -0
- unicode_logic_kit/ace/mapping.py +666 -0
- unicode_logic_kit/ace/reverse_modal.py +138 -0
- unicode_logic_kit/ace/runner.py +551 -0
- unicode_logic_kit/ace/translate.py +452 -0
- unicode_logic_kit/ace/verbalize.py +1070 -0
- unicode_logic_kit/api.py +1284 -0
- unicode_logic_kit/atp/__init__.py +177 -0
- unicode_logic_kit/atp/_ascii_names.py +113 -0
- unicode_logic_kit/atp/_html.py +72 -0
- unicode_logic_kit/atp/_substructural_input.py +228 -0
- unicode_logic_kit/atp/_tff_problem.py +715 -0
- unicode_logic_kit/atp/_tptp_problem.py +1111 -0
- unicode_logic_kit/atp/_writer_support.py +289 -0
- unicode_logic_kit/atp/clingo_backend.py +1180 -0
- unicode_logic_kit/atp/cvc5_backend.py +1385 -0
- unicode_logic_kit/atp/eprover_backend.py +732 -0
- unicode_logic_kit/atp/finite_domain.py +1055 -0
- unicode_logic_kit/atp/fitch.py +1547 -0
- unicode_logic_kit/atp/fitch_search.py +551 -0
- unicode_logic_kit/atp/hets_backend.py +339 -0
- unicode_logic_kit/atp/hybrid_down.py +120 -0
- unicode_logic_kit/atp/incremental.py +250 -0
- unicode_logic_kit/atp/kripke_enum.py +741 -0
- unicode_logic_kit/atp/lambek.py +436 -0
- unicode_logic_kit/atp/leo3_backend.py +332 -0
- unicode_logic_kit/atp/linear.py +738 -0
- unicode_logic_kit/atp/lj.py +705 -0
- unicode_logic_kit/atp/logic_backends.py +566 -0
- unicode_logic_kit/atp/ltl_tableau.py +1084 -0
- unicode_logic_kit/atp/minizinc_backend.py +1402 -0
- unicode_logic_kit/atp/modal_tableau.py +1382 -0
- unicode_logic_kit/atp/nanocop_backend.py +410 -0
- unicode_logic_kit/atp/portfolio.py +489 -0
- unicode_logic_kit/atp/protocol.py +1803 -0
- unicode_logic_kit/atp/prover9_entailment.py +1153 -0
- unicode_logic_kit/atp/resolution.py +1376 -0
- unicode_logic_kit/atp/resolution_check.py +1114 -0
- unicode_logic_kit/atp/sequent.py +1050 -0
- unicode_logic_kit/atp/tableau.py +921 -0
- unicode_logic_kit/atp/tableau_check.py +543 -0
- unicode_logic_kit/atp/tptp_ncl.py +811 -0
- unicode_logic_kit/atp/tptp_tff.py +1546 -0
- unicode_logic_kit/atp/tstp.py +1333 -0
- unicode_logic_kit/atp/tstp_check.py +1096 -0
- unicode_logic_kit/atp/twee_backend.py +236 -0
- unicode_logic_kit/atp/twee_check.py +711 -0
- unicode_logic_kit/atp/twee_entailment.py +953 -0
- unicode_logic_kit/atp/vampire_entailment.py +540 -0
- unicode_logic_kit/atp/z3_arith.py +470 -0
- unicode_logic_kit/atp/z3_equivalence.py +36 -0
- unicode_logic_kit/atp/z3_fuzzy.py +362 -0
- unicode_logic_kit/atp/z3_input.py +500 -0
- unicode_logic_kit/atp/z3_models.py +208 -0
- unicode_logic_kit/chem/__init__.py +88 -0
- unicode_logic_kit/chem/_naming.py +284 -0
- unicode_logic_kit/chem/cache.py +185 -0
- unicode_logic_kit/chem/interop.py +244 -0
- unicode_logic_kit/chem/mol.py +525 -0
- unicode_logic_kit/chem/signature.py +112 -0
- unicode_logic_kit/comorphism.py +497 -0
- unicode_logic_kit/dl/__init__.py +384 -0
- unicode_logic_kit/dl/classification.py +227 -0
- unicode_logic_kit/dl/concepts.py +632 -0
- unicode_logic_kit/dl/datatypes.py +818 -0
- unicode_logic_kit/dl/owl_functional.py +2433 -0
- unicode_logic_kit/dl/owl_manchester.py +1637 -0
- unicode_logic_kit/dl/owl_reasoner.py +790 -0
- unicode_logic_kit/dl/parser.py +391 -0
- unicode_logic_kit/dl/tableau.py +4048 -0
- unicode_logic_kit/dl/translate.py +2704 -0
- unicode_logic_kit/drt/__init__.py +94 -0
- unicode_logic_kit/drt/export.py +179 -0
- unicode_logic_kit/drt/nodes.py +506 -0
- unicode_logic_kit/drt/parser.py +965 -0
- unicode_logic_kit/drt/resolve.py +195 -0
- unicode_logic_kit/drt/reverse.py +175 -0
- unicode_logic_kit/eval/__init__.py +106 -0
- unicode_logic_kit/eval/batch.py +382 -0
- unicode_logic_kit/eval/canonical.py +663 -0
- unicode_logic_kit/eval/chem_batch.py +606 -0
- unicode_logic_kit/eval/converses.py +200 -0
- unicode_logic_kit/eval/datasets/__init__.py +136 -0
- unicode_logic_kit/eval/datasets/_base.py +263 -0
- unicode_logic_kit/eval/datasets/_proofwriter_proof.py +422 -0
- unicode_logic_kit/eval/datasets/c3po.py +678 -0
- unicode_logic_kit/eval/datasets/folio.py +158 -0
- unicode_logic_kit/eval/datasets/fracas.py +418 -0
- unicode_logic_kit/eval/datasets/groves.py +191 -0
- unicode_logic_kit/eval/datasets/logicbench.py +467 -0
- unicode_logic_kit/eval/datasets/logicnli.py +303 -0
- unicode_logic_kit/eval/datasets/malls.py +133 -0
- unicode_logic_kit/eval/datasets/pfolio.py +594 -0
- unicode_logic_kit/eval/datasets/pmb.py +242 -0
- unicode_logic_kit/eval/datasets/prontoqa.py +611 -0
- unicode_logic_kit/eval/datasets/proofwriter.py +1431 -0
- unicode_logic_kit/eval/datasets/proverqa.py +674 -0
- unicode_logic_kit/eval/datasets/willow.py +478 -0
- unicode_logic_kit/eval/equivalence.py +466 -0
- unicode_logic_kit/eval/exercise_gen.py +533 -0
- unicode_logic_kit/eval/explain.py +791 -0
- unicode_logic_kit/eval/generality.py +750 -0
- unicode_logic_kit/eval/metric_hf.py +458 -0
- unicode_logic_kit/eval/predicate_match.py +343 -0
- unicode_logic_kit/eval/theory_check.py +1170 -0
- unicode_logic_kit/eval/validate.py +306 -0
- unicode_logic_kit/fol/__init__.py +177 -0
- unicode_logic_kit/fol/_atom_keys.py +510 -0
- unicode_logic_kit/fol/_fol_nodes.py +3586 -0
- unicode_logic_kit/fol/_free_parameters.py +105 -0
- unicode_logic_kit/fol/_ho_nodes.py +448 -0
- unicode_logic_kit/fol/_hybrid_nodes.py +308 -0
- unicode_logic_kit/fol/_identifiers.py +1091 -0
- unicode_logic_kit/fol/_lambek_nodes.py +112 -0
- unicode_logic_kit/fol/_linear_nodes.py +352 -0
- unicode_logic_kit/fol/_modal_nodes.py +1467 -0
- unicode_logic_kit/fol/_msfl_nodes.py +2196 -0
- unicode_logic_kit/fol/_numeral_symbols.py +231 -0
- unicode_logic_kit/fol/_so_nodes.py +200 -0
- unicode_logic_kit/fol/_symbol_names.py +81 -0
- unicode_logic_kit/fol/_team_nodes.py +181 -0
- unicode_logic_kit/fol/_tptp_symbols.py +551 -0
- unicode_logic_kit/fol/_truth_constants.py +117 -0
- unicode_logic_kit/fol/casl_export.py +1135 -0
- unicode_logic_kit/fol/casl_import.py +929 -0
- unicode_logic_kit/fol/derivation.py +367 -0
- unicode_logic_kit/fol/dialect_detect.py +70 -0
- unicode_logic_kit/fol/dialect_repair.py +537 -0
- unicode_logic_kit/fol/frames.py +637 -0
- unicode_logic_kit/fol/grammars/terminals.lark +31 -0
- unicode_logic_kit/fol/lambda_tools.py +297 -0
- unicode_logic_kit/fol/latex_input.py +429 -0
- unicode_logic_kit/fol/modal_translation.py +944 -0
- unicode_logic_kit/fol/msflparser.py +1033 -0
- unicode_logic_kit/fol/naming.py +422 -0
- unicode_logic_kit/fol/nodes.py +241 -0
- unicode_logic_kit/fol/normalforms.py +492 -0
- unicode_logic_kit/fol/pal.py +287 -0
- unicode_logic_kit/fol/prolog_export.py +566 -0
- unicode_logic_kit/fol/prolog_input.py +505 -0
- unicode_logic_kit/fol/prover9_input.py +1325 -0
- unicode_logic_kit/fol/qml.py +1760 -0
- unicode_logic_kit/fol/qmltp_input.py +525 -0
- unicode_logic_kit/fol/sanitize.py +221 -0
- unicode_logic_kit/fol/serialize.py +79 -0
- unicode_logic_kit/fol/signature.py +1290 -0
- unicode_logic_kit/fol/simplify_check.py +544 -0
- unicode_logic_kit/fol/spans.py +594 -0
- unicode_logic_kit/fol/tptp_input.py +1503 -0
- unicode_logic_kit/fol/tptp_repair.py +941 -0
- unicode_logic_kit/fol/unification.py +157 -0
- unicode_logic_kit/fol/verbalize.py +263 -0
- unicode_logic_kit/hets/__init__.py +163 -0
- unicode_logic_kit/hets/bridge.py +142 -0
- unicode_logic_kit/hets/client.py +748 -0
- unicode_logic_kit/hets/docker.py +420 -0
- unicode_logic_kit/hets/dol.py +712 -0
- unicode_logic_kit/hets/haskell_json.py +355 -0
- unicode_logic_kit/hets/owl_backend.py +794 -0
- unicode_logic_kit/hets/owl_cli.py +598 -0
- unicode_logic_kit/hets/symbols.py +512 -0
- unicode_logic_kit/hol/__init__.py +140 -0
- unicode_logic_kit/hol/_ho_common.py +323 -0
- unicode_logic_kit/hol/_isabelle_binders.py +125 -0
- unicode_logic_kit/hol/classical.py +812 -0
- unicode_logic_kit/hol/deepshallow/__init__.py +45 -0
- unicode_logic_kit/hol/deepshallow/_common.py +177 -0
- unicode_logic_kit/hol/deepshallow/conditional.py +225 -0
- unicode_logic_kit/hol/deepshallow/intuitionistic.py +181 -0
- unicode_logic_kit/hol/deepshallow/modal.py +217 -0
- unicode_logic_kit/hol/deepshallow/qml.py +406 -0
- unicode_logic_kit/hol/deepshallow/relevant.py +206 -0
- unicode_logic_kit/hol/free.py +753 -0
- unicode_logic_kit/hol/goedel.py +336 -0
- unicode_logic_kit/hol/ho_modal.py +1743 -0
- unicode_logic_kit/hol/intuitionistic.py +403 -0
- unicode_logic_kit/hol/isabelle_conditional.py +593 -0
- unicode_logic_kit/hol/isabelle_modal.py +1908 -0
- unicode_logic_kit/hol/isabelle_relevant.py +412 -0
- unicode_logic_kit/hol/isabelle_runner.py +1147 -0
- unicode_logic_kit/hol/isabelle_substructural.py +884 -0
- unicode_logic_kit/hol/lean.py +1018 -0
- unicode_logic_kit/hol/manyvalued.py +921 -0
- unicode_logic_kit/hol/secondorder.py +687 -0
- unicode_logic_kit/hol/thf_modal.py +941 -0
- unicode_logic_kit/hol/thirdorder.py +397 -0
- unicode_logic_kit/ilp/__init__.py +89 -0
- unicode_logic_kit/ilp/readback.py +389 -0
- unicode_logic_kit/ilp/separation.py +153 -0
- unicode_logic_kit/ilp/task.py +730 -0
- unicode_logic_kit/logic.py +163 -0
- unicode_logic_kit/mcp/__init__.py +28 -0
- unicode_logic_kit/mcp/__main__.py +5 -0
- unicode_logic_kit/mcp/chem_tools.py +1031 -0
- unicode_logic_kit/mcp/server.py +2453 -0
- unicode_logic_kit/mcp/syntax_spec.py +681 -0
- unicode_logic_kit/prob/__init__.py +53 -0
- unicode_logic_kit/prob/_bdd.py +225 -0
- unicode_logic_kit/prob/_column_gen.py +668 -0
- unicode_logic_kit/prob/distribution.py +686 -0
- unicode_logic_kit/prob/nilsson.py +470 -0
- unicode_logic_kit/py.typed +0 -0
- unicode_logic_kit/semantics/__init__.py +137 -0
- unicode_logic_kit/semantics/_modal_reject.py +156 -0
- unicode_logic_kit/semantics/action_models.py +466 -0
- unicode_logic_kit/semantics/asp_models.py +1200 -0
- unicode_logic_kit/semantics/conditional.py +580 -0
- unicode_logic_kit/semantics/dynamic_epistemic.py +95 -0
- unicode_logic_kit/semantics/free_logic.py +913 -0
- unicode_logic_kit/semantics/fuzzy.py +384 -0
- unicode_logic_kit/semantics/fuzzy_kripke.py +442 -0
- unicode_logic_kit/semantics/intuitionistic.py +581 -0
- unicode_logic_kit/semantics/kripke.py +1139 -0
- unicode_logic_kit/semantics/manyvalued.py +580 -0
- unicode_logic_kit/semantics/matrix.py +342 -0
- unicode_logic_kit/semantics/model_eval.py +1135 -0
- unicode_logic_kit/semantics/modelfinder.py +1036 -0
- unicode_logic_kit/semantics/nonmonotonic.py +372 -0
- unicode_logic_kit/semantics/relevant.py +331 -0
- unicode_logic_kit/semantics/secondorder.py +657 -0
- unicode_logic_kit/semantics/structures.py +352 -0
- unicode_logic_kit/semantics/tarski.py +975 -0
- unicode_logic_kit/semantics/team.py +315 -0
- unicode_logic_kit/semantics/team_translation.py +416 -0
- unicode_logic_kit/semantics/thirdorder.py +358 -0
- unicode_logic_kit/semantics/tnorm.py +85 -0
- unicode_logic_kit/semantics/truthtable.py +201 -0
- unicode_logic_kit-0.31.0.dist-info/METADATA +333 -0
- unicode_logic_kit-0.31.0.dist-info/RECORD +237 -0
- unicode_logic_kit-0.31.0.dist-info/WHEEL +4 -0
- unicode_logic_kit-0.31.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,598 @@
|
|
|
1
|
+
r"""The HETS COMMAND-LINE route: ``owl_to_tptp``, the only way to the lossy translation.
|
|
2
|
+
|
|
3
|
+
Why a second route at all
|
|
4
|
+
-------------------------
|
|
5
|
+
:mod:`~unicode_logic_kit.hets.client` speaks HETS' REST API and never shells
|
|
6
|
+
out. That covers everything the server offers — except one switch. HETS
|
|
7
|
+
refuses a comorphism whose source sublogic does not cover the theory, and
|
|
8
|
+
``GET /theory`` then answers HTTP 422 (see
|
|
9
|
+
:class:`~unicode_logic_kit.hets.client.HetsSublogicError`). ``hets-server``'s
|
|
10
|
+
``-Y`` flag translates ANYWAY, dropping the axioms it cannot express, and
|
|
11
|
+
``-Y`` exists ONLY on the command line: the REST API has no equivalent. For a
|
|
12
|
+
real ontology that one flag is the difference between a FOL image and nothing
|
|
13
|
+
at all — measured on OEO 2.13.0, two ``DataPropertyRange(d rdfs:Literal)``
|
|
14
|
+
axioms out of 4041 push the whole ontology out of ``OWL22CASL``'s sublogic.
|
|
15
|
+
|
|
16
|
+
So this module exists, and it is deliberately thin: it builds a command line,
|
|
17
|
+
runs it through ``docker exec``, copies the output back and reads the console. All the
|
|
18
|
+
interpretation lives in :mod:`~unicode_logic_kit.hets.symbols`, which is pure
|
|
19
|
+
and fully offline-testable — that split is what makes this specifiable on a
|
|
20
|
+
machine with no HETS.
|
|
21
|
+
|
|
22
|
+
``lossy=True`` runs the NON-LOSSY translation FIRST, on purpose
|
|
23
|
+
---------------------------------------------------------------
|
|
24
|
+
``-Y`` is SILENT. Measured from HETS' own console output: the lossy run prints
|
|
25
|
+
only ``Translated using comorphism OWL22CASL;CASL2TPTP_FOF : OWL -> TPTP``,
|
|
26
|
+
while the sublogic warning appears only in the run WITHOUT ``-Y``::
|
|
27
|
+
|
|
28
|
+
### Warning:
|
|
29
|
+
for 'OWL22CASL;CASL2TPTP_FOF' expected sublogic 'NP-sROIQux-D|-|'
|
|
30
|
+
but found sublogic 'NP-sROIQ-D|Literal|dateTime|decimal|integer|string|' ...
|
|
31
|
+
Keeping untranslated theory
|
|
32
|
+
|
|
33
|
+
A single ``-Y`` run would therefore hand the caller a quietly smaller theory
|
|
34
|
+
with nothing to indicate it. That is exactly the silent approximation this kit
|
|
35
|
+
refuses, so ``lossy=True`` runs the probe first and fills
|
|
36
|
+
:attr:`OwlTptpResult.sublogic_mismatch` from HETS' own warning text. The
|
|
37
|
+
mismatch is then reported even when no ``client=`` was passed and
|
|
38
|
+
:attr:`OwlTptpResult.omitted_axioms` is consequently ``None``.
|
|
39
|
+
|
|
40
|
+
The COST, stated rather than hidden: two HETS runs per call, measured at
|
|
41
|
+
4.6 s + 4.4 s for OEO's 3.85 MB, so ~9 s instead of ~4.5 s. On a much larger
|
|
42
|
+
ontology that doubling is the dominant cost of the call, and the only way to
|
|
43
|
+
avoid it is to pass ``client=`` and let :func:`~unicode_logic_kit.hets.symbols.untranslated_axioms`
|
|
44
|
+
compute the loss from ``/dg`` against the TPTP instead — which is both exact
|
|
45
|
+
and free. ``lossy=False`` does one run and raises
|
|
46
|
+
:class:`~unicode_logic_kit.hets.client.HetsSublogicError`.
|
|
47
|
+
|
|
48
|
+
EXIT 0 is not success
|
|
49
|
+
---------------------
|
|
50
|
+
Both non-lossy runs above exit 0 and write the UNTRANSLATED theory; with
|
|
51
|
+
``-o tptp`` no ``.tptp`` file is written at all. The wrong separator exits 0
|
|
52
|
+
too (``### Warning: Cannot find logic comorphism OWL22CASL;CASL2TPTP_FOF``).
|
|
53
|
+
So success is the CONJUNCTION of: a ``.tptp`` file appeared in the output
|
|
54
|
+
directory, AND the console contains ``Translated using comorphism``. Anything
|
|
55
|
+
else is a named refusal.
|
|
56
|
+
|
|
57
|
+
What this module will NOT do: normalise the ontology
|
|
58
|
+
----------------------------------------------------
|
|
59
|
+
HETS 0.108.0 rejects an ``AnnotationAssertion`` whose subject is not declared
|
|
60
|
+
(``Incorrect AnnotationAssertion axiom. Axiom subject is not declared:
|
|
61
|
+
'obo:BFO_0000134'``), which OWL 2 does not require. Fixing that inside this
|
|
62
|
+
kit would need three things it must not do silently: (1) CHOOSE an entity kind
|
|
63
|
+
for an annotation-only IRI — the requesting project's own log reads "kind not
|
|
64
|
+
stated in the file, assumed ObjectProperty", i.e. a guess that puts a symbol
|
|
65
|
+
into the logical signature; (2) an RDF/XML WRITER, which this kit does not
|
|
66
|
+
have and should not grow for this; (3) DELETE two ``rdfs:isDefinedBy``
|
|
67
|
+
annotations, i.e. lose information. So :func:`owl_to_tptp` instead DETECTS the
|
|
68
|
+
pattern in HETS' console, collects every undeclared subject and raises
|
|
69
|
+
:class:`HetsOwlNormalizationError` listing them with both remedies and the
|
|
70
|
+
explicit statement that this kit will not choose the kind. That turns an
|
|
71
|
+
opaque HETS failure into a one-line instruction and leaves the semantic
|
|
72
|
+
choice with the ontology's owner.
|
|
73
|
+
|
|
74
|
+
Two routes
|
|
75
|
+
----------
|
|
76
|
+
No ``unicode_logic_kit.dl`` file is touched and no construct reaches the
|
|
77
|
+
description-logic tableau. The two-route rule applies at the level the whole
|
|
78
|
+
OEO request is about: :func:`owl_to_tptp` produces the SECOND route's FOL
|
|
79
|
+
image of an ontology, to be compared against
|
|
80
|
+
:func:`unicode_logic_kit.dl.tbox_to_fol` / :func:`unicode_logic_kit.dl.kb_to_fol`
|
|
81
|
+
plus :func:`unicode_logic_kit.api.prove`. The helper therefore must never
|
|
82
|
+
present an incomplete image as complete — which is what
|
|
83
|
+
:attr:`~OwlTptpResult.sublogic_mismatch`, :attr:`~OwlTptpResult.omitted_axioms`,
|
|
84
|
+
:attr:`~unicode_logic_kit.hets.symbols.HetsSymbol.in_tptp` and
|
|
85
|
+
:attr:`~unicode_logic_kit.hets.symbols.HetsSymbolTable.unmapped_tptp` are for —
|
|
86
|
+
and HETS' own translation defects are listed in
|
|
87
|
+
:mod:`~unicode_logic_kit.hets.symbols`' docstring so a disagreement is
|
|
88
|
+
attributable rather than blamed on this kit.
|
|
89
|
+
"""
|
|
90
|
+
|
|
91
|
+
from __future__ import annotations
|
|
92
|
+
|
|
93
|
+
import os
|
|
94
|
+
import posixpath
|
|
95
|
+
import re
|
|
96
|
+
import shutil
|
|
97
|
+
import subprocess
|
|
98
|
+
import uuid
|
|
99
|
+
from dataclasses import dataclass
|
|
100
|
+
from typing import List, Optional, Tuple, Union
|
|
101
|
+
|
|
102
|
+
from ..atp.protocol import BackendUnavailable
|
|
103
|
+
from .client import HetsClient, HetsSublogicError, _SUBLOGIC_RE
|
|
104
|
+
from .docker import HETS_IMAGE, HetsContainer
|
|
105
|
+
from .symbols import (
|
|
106
|
+
HetsSymbolTable,
|
|
107
|
+
UntranslatedAxiom,
|
|
108
|
+
hets_prefixes,
|
|
109
|
+
hets_symbol_table,
|
|
110
|
+
untranslated_axioms,
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
__all__ = [
|
|
114
|
+
"SublogicMismatch",
|
|
115
|
+
"OwlTptpResult",
|
|
116
|
+
"HetsOwlNormalizationError",
|
|
117
|
+
"owl_to_tptp",
|
|
118
|
+
"UFK_HETS_CONTAINER",
|
|
119
|
+
]
|
|
120
|
+
|
|
121
|
+
#: The environment variable naming a running HETS container to ``docker
|
|
122
|
+
#: exec`` into. Deliberately NOT ``$UFK_HETS_URL``:
|
|
123
|
+
#: :func:`~unicode_logic_kit.hets.docker.discover_hets_url` may legitimately
|
|
124
|
+
#: return a URL that is not a local container at all (its own docstring says
|
|
125
|
+
#: so — a shared server, a CI sidecar), and ``docker exec`` into a URL is
|
|
126
|
+
#: impossible. So the command-line route gets its own discovery and its own
|
|
127
|
+
#: variable, mirroring ``discover_hets_url``'s "never guess, list every
|
|
128
|
+
#: option tried" contract rather than reusing it.
|
|
129
|
+
UFK_HETS_CONTAINER = "UFK_HETS_CONTAINER"
|
|
130
|
+
|
|
131
|
+
#: The console line that means the translation actually happened. HETS writes
|
|
132
|
+
#: the composition with ``;`` here even though the command line takes ``:``.
|
|
133
|
+
_TRANSLATED_RE = re.compile(
|
|
134
|
+
r"Translated using comorphism\s+(\S+)\s*:\s*(\S+)\s*->\s*(\S+)")
|
|
135
|
+
|
|
136
|
+
#: HETS could not resolve the comorphism name at all — what the ``;``
|
|
137
|
+
#: spelling produces on the command line, with EXIT 0 and no output file.
|
|
138
|
+
_NO_COMORPHISM_RE = re.compile(r"Cannot find logic comorphism\s+(\S+)")
|
|
139
|
+
|
|
140
|
+
#: ``Incorrect AnnotationAssertion axiom. Axiom subject is not declared:
|
|
141
|
+
#: 'obo:BFO_0000134'`` — HETS may report only the first, so the refusal says
|
|
142
|
+
#: the list may be incomplete.
|
|
143
|
+
_UNDECLARED_RE = re.compile(
|
|
144
|
+
r"Axiom subject is not declared:\s*'([^']*)'")
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
class HetsOwlNormalizationError(RuntimeError):
|
|
148
|
+
"""HETS refused the ontology because an annotation subject is undeclared.
|
|
149
|
+
|
|
150
|
+
Attributes:
|
|
151
|
+
undeclared: every IRI HETS named, in the order it named them. HETS
|
|
152
|
+
may stop at the first, so this list can be incomplete — the
|
|
153
|
+
message says so.
|
|
154
|
+
|
|
155
|
+
A ``RuntimeError`` subclass, like the other HETS exceptions: the server
|
|
156
|
+
(here, the binary) is fine and the request was understood; the INPUT is
|
|
157
|
+
the problem. This kit will not repair it — see the module docstring for
|
|
158
|
+
the three things such a repair would have to guess.
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
def __init__(self, message: str, *, undeclared: Tuple[str, ...]):
|
|
162
|
+
super().__init__(message)
|
|
163
|
+
self.undeclared = undeclared
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
@dataclass(frozen=True)
|
|
167
|
+
class SublogicMismatch:
|
|
168
|
+
"""HETS' sublogic complaint, parsed into its three parts.
|
|
169
|
+
|
|
170
|
+
Fields:
|
|
171
|
+
|
|
172
|
+
* ``comorphism`` — as HETS spells it, with ``;``
|
|
173
|
+
(``"OWL22CASL;CASL2TPTP_FOF"``).
|
|
174
|
+
* ``expected`` — the sublogic the comorphism covers.
|
|
175
|
+
* ``found`` — the sublogic the theory actually is.
|
|
176
|
+
"""
|
|
177
|
+
|
|
178
|
+
comorphism: str
|
|
179
|
+
expected: str
|
|
180
|
+
found: str
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
@dataclass(frozen=True)
|
|
184
|
+
class OwlTptpResult:
|
|
185
|
+
"""The outcome of one :func:`owl_to_tptp` call.
|
|
186
|
+
|
|
187
|
+
Fields:
|
|
188
|
+
|
|
189
|
+
* ``tptp`` — the ``.tptp`` text, exactly as HETS wrote it. Ready for
|
|
190
|
+
:func:`unicode_logic_kit.fol.tptp_input.parse_tptp` — unlike the
|
|
191
|
+
REST ``/theory`` rendering, the ``-o tptp`` file carries no
|
|
192
|
+
header.
|
|
193
|
+
* ``node`` — the development-graph node name, recovered from the output
|
|
194
|
+
file name (HETS percent-encodes the node IRI into it).
|
|
195
|
+
* ``comorphism`` — the comorphism HETS reports having used, in HETS'
|
|
196
|
+
``;`` spelling.
|
|
197
|
+
* ``lossy`` — whether ``-Y`` was actually used for the run that produced
|
|
198
|
+
:attr:`tptp`.
|
|
199
|
+
* ``sublogic_mismatch`` — the loss REPORT. ``None`` means the probe run
|
|
200
|
+
succeeded without ``-Y``, so nothing was dropped for sublogic
|
|
201
|
+
reasons. Non-``None`` means ``-Y`` was needed, and names the
|
|
202
|
+
sublogic gap.
|
|
203
|
+
* ``console`` — HETS' stdout and stderr for the run that produced
|
|
204
|
+
:attr:`tptp`, verbatim.
|
|
205
|
+
* ``theory`` — the ``-o th`` text — the TRANSLATED theory.
|
|
206
|
+
* ``pretty`` — the ``-o pp.dol`` text — HETS' pretty-printed rendering of the
|
|
207
|
+
SOURCE ontology, and the only one of the three outputs that
|
|
208
|
+
carries the ``Prefix: p: <iri>`` lines (measured: the translated
|
|
209
|
+
``.th`` of a TPTP chain has none). Feed it to
|
|
210
|
+
:func:`~unicode_logic_kit.hets.symbols.hets_prefixes`; that is what
|
|
211
|
+
:attr:`symbols` already does.
|
|
212
|
+
* ``symbols`` — the OWL-entity/TPTP-symbol join, or ``None`` when no
|
|
213
|
+
``client=`` was given. ``None`` means NOT COMPUTED, never
|
|
214
|
+
"nothing found": the join needs ``/dg``, which needs the REST
|
|
215
|
+
client.
|
|
216
|
+
* ``omitted_axioms`` — the axioms with no formula in :attr:`tptp`, or
|
|
217
|
+
``None`` when no ``client=`` was given. An empty tuple ``()``
|
|
218
|
+
means "computed, and the answer is none".
|
|
219
|
+
"""
|
|
220
|
+
|
|
221
|
+
tptp: str
|
|
222
|
+
node: str
|
|
223
|
+
comorphism: str
|
|
224
|
+
lossy: bool
|
|
225
|
+
sublogic_mismatch: Optional[SublogicMismatch]
|
|
226
|
+
console: str
|
|
227
|
+
theory: str
|
|
228
|
+
pretty: str = ""
|
|
229
|
+
symbols: Optional[HetsSymbolTable] = None
|
|
230
|
+
omitted_axioms: Optional[Tuple[UntranslatedAxiom, ...]] = None
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
@dataclass(frozen=True)
|
|
234
|
+
class _Run:
|
|
235
|
+
"""One ``hets-server`` invocation's raw outcome."""
|
|
236
|
+
|
|
237
|
+
argv: List[str]
|
|
238
|
+
returncode: int
|
|
239
|
+
console: str
|
|
240
|
+
out_dir: str
|
|
241
|
+
outputs: dict # basename -> text, for the files HETS wrote
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
def _docker_bin() -> str:
|
|
245
|
+
found = shutil.which("docker")
|
|
246
|
+
if found is None:
|
|
247
|
+
raise BackendUnavailable(
|
|
248
|
+
"hets: no `docker` binary found on PATH, and owl_to_tptp reaches "
|
|
249
|
+
"hets-server through `docker exec`. Install Docker Desktop "
|
|
250
|
+
"(https://www.docker.com/products/docker-desktop/) and ensure "
|
|
251
|
+
"`docker` is on PATH. A remote $UFK_HETS_URL cannot substitute: "
|
|
252
|
+
"the lossy switch (-Y) exists only on the command line and HETS' "
|
|
253
|
+
"REST route has no equivalent.")
|
|
254
|
+
return found
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
def _resolve_container(container, start_container: bool
|
|
258
|
+
) -> Tuple[str, Optional[HetsContainer]]:
|
|
259
|
+
"""Name the container to exec into; never guess, list every option tried.
|
|
260
|
+
|
|
261
|
+
Returns ``(name, owned)`` where ``owned`` is a container this call
|
|
262
|
+
started (and must stop) or ``None``.
|
|
263
|
+
"""
|
|
264
|
+
if isinstance(container, HetsContainer):
|
|
265
|
+
return container.name, None
|
|
266
|
+
if isinstance(container, str) and container.strip():
|
|
267
|
+
return container.strip(), None
|
|
268
|
+
from_env = os.environ.get(UFK_HETS_CONTAINER, "").strip()
|
|
269
|
+
if from_env:
|
|
270
|
+
return from_env, None
|
|
271
|
+
if start_container:
|
|
272
|
+
started = HetsContainer()
|
|
273
|
+
started.start()
|
|
274
|
+
return started.name, started
|
|
275
|
+
raise BackendUnavailable(
|
|
276
|
+
"hets: owl_to_tptp needs a Hets CONTAINER to 'docker exec' into, not "
|
|
277
|
+
"just a server URL. Tried, in order:\n"
|
|
278
|
+
" 1. container= — not given\n"
|
|
279
|
+
f" 2. ${UFK_HETS_CONTAINER} — not set\n"
|
|
280
|
+
"Fix one of:\n"
|
|
281
|
+
" - set $" + UFK_HETS_CONTAINER + " to a running spechub2/hets "
|
|
282
|
+
"container name\n"
|
|
283
|
+
" - pass container=<name> or container=<HetsContainer>\n"
|
|
284
|
+
" - call owl_to_tptp(..., start_container=True) to have this kit "
|
|
285
|
+
"start and own one (needs Docker Desktop / a running daemon; image "
|
|
286
|
+
f"{HETS_IMAGE})\n"
|
|
287
|
+
"A remote $UFK_HETS_URL cannot be used instead: the lossy switch "
|
|
288
|
+
"(-Y) exists only on the command line and Hets' REST route has no "
|
|
289
|
+
"equivalent.")
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
def _exec(docker: str, container: str, command: str, timeout: float) -> Tuple[int, str]:
|
|
293
|
+
"""Run one shell command inside the container; return (exit code, console)."""
|
|
294
|
+
argv = [docker, "exec", container, "sh", "-c", command]
|
|
295
|
+
proc = subprocess.run(argv, capture_output=True, text=True, timeout=timeout)
|
|
296
|
+
return proc.returncode, (proc.stdout or "") + (proc.stderr or "")
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def _hets_command(remote_in: str, remote_out: str, comorphism: str,
|
|
300
|
+
lossy: bool) -> str:
|
|
301
|
+
"""The exact ``hets-server`` command line, as the evidence records it.
|
|
302
|
+
|
|
303
|
+
``hets-server -v2 -a none [-Y] -t <comorphism> -o th,tptp,pp.dol -O <dir> <file>``
|
|
304
|
+
— ``-v2`` so the ``Translated using comorphism`` line is printed at all,
|
|
305
|
+
``-a none`` so HETS does not try to PROVE anything, and ``-O`` into a
|
|
306
|
+
directory this call owns.
|
|
307
|
+
|
|
308
|
+
Three outputs, each for a measured reason. ``tptp`` is the problem itself.
|
|
309
|
+
``th`` is the translated theory. ``pp.dol`` is the PRETTY-PRINTED SOURCE,
|
|
310
|
+
and it is the only one of the three that carries HETS' ``Prefix: p: <iri>``
|
|
311
|
+
lines: measured on the real ontology, the translated ``.th`` of a TPTP
|
|
312
|
+
chain has ZERO prefix lines (it is a TPTP theory, not an OWL one) while
|
|
313
|
+
``pp.dol`` has all 15. Without it :attr:`OwlTptpResult.symbols` would
|
|
314
|
+
silently leave every CURIE unexpanded. All three come out of ONE
|
|
315
|
+
``hets-server`` run — verified live; adding ``pp.dol`` costs no extra
|
|
316
|
+
invocation.
|
|
317
|
+
"""
|
|
318
|
+
parts = ["hets-server", "-v2", "-a", "none"]
|
|
319
|
+
if lossy:
|
|
320
|
+
parts.append("-Y")
|
|
321
|
+
parts += ["-t", comorphism, "-o", "th,tptp,pp.dol",
|
|
322
|
+
"-O", remote_out, remote_in]
|
|
323
|
+
return " ".join(parts)
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
def _refuse_bad_comorphism(comorphism: str) -> None:
|
|
327
|
+
"""Refuse the two comorphism spellings that fail silently or abort."""
|
|
328
|
+
if ";" in comorphism:
|
|
329
|
+
raise RuntimeError(
|
|
330
|
+
"hets: Hets could not find the comorphism "
|
|
331
|
+
f"{comorphism!r} (its console says \"Cannot find logic "
|
|
332
|
+
"comorphism\"). On the command line the composition separator is "
|
|
333
|
+
"':' — pass comorphism="
|
|
334
|
+
f"\"{comorphism.replace(';', ':')}\". Hets spells it with ';' in "
|
|
335
|
+
"its own messages; that spelling is not accepted as input.")
|
|
336
|
+
if "SoftFOL" in comorphism:
|
|
337
|
+
raise RuntimeError(
|
|
338
|
+
"hets: owl_to_tptp does not offer the SoftFOL/DFG route: Hets "
|
|
339
|
+
"0.108.0 aborts on CASL numerals "
|
|
340
|
+
"(\"SuleCFOL2SoftFOL.transOPSYMB: unknown op: Qual_op_name 1 "
|
|
341
|
+
"...\", exit 1), which any ontology with a DataHasValue numeral "
|
|
342
|
+
"or a DatatypeRestriction produces. Use "
|
|
343
|
+
"OWL22CASL:CASL2TPTP_FOF.")
|
|
344
|
+
|
|
345
|
+
|
|
346
|
+
def _check_console(run: _Run, comorphism: str) -> None:
|
|
347
|
+
"""Turn HETS' known console failures into named refusals."""
|
|
348
|
+
undeclared = tuple(_UNDECLARED_RE.findall(run.console))
|
|
349
|
+
if undeclared:
|
|
350
|
+
listed = "\n".join(f" - {iri}" for iri in undeclared)
|
|
351
|
+
raise HetsOwlNormalizationError(
|
|
352
|
+
"hets: Hets refuses this ontology because an AnnotationAssertion "
|
|
353
|
+
"names a subject the file never declares. OWL 2 does not require "
|
|
354
|
+
"such a declaration, so this is a Hets restriction, not an error "
|
|
355
|
+
"in the ontology. Hets named:\n"
|
|
356
|
+
f"{listed}\n"
|
|
357
|
+
"(Hets may report only the FIRST one, so this list can be "
|
|
358
|
+
"incomplete — fix these and re-run.)\n"
|
|
359
|
+
"Two remedies, both yours to choose:\n"
|
|
360
|
+
" - add a Declaration( <Kind>( <iri> ) ) for each IRI above, or\n"
|
|
361
|
+
" - remove the annotation assertions that name them.\n"
|
|
362
|
+
"This kit will NOT choose the Kind for you: an annotation-only "
|
|
363
|
+
"IRI has no kind stated in the file, and guessing one would put a "
|
|
364
|
+
"symbol into the logical signature on a guess — which changes "
|
|
365
|
+
"what the translated theory means. Nor will it rewrite your "
|
|
366
|
+
"RDF/XML: this kit has no OWL writer and will not grow one to "
|
|
367
|
+
"edit your ontology behind your back.",
|
|
368
|
+
undeclared=undeclared)
|
|
369
|
+
missing = _NO_COMORPHISM_RE.search(run.console)
|
|
370
|
+
if missing:
|
|
371
|
+
raise RuntimeError(
|
|
372
|
+
f"hets: Hets could not find the comorphism {missing.group(1)!r} "
|
|
373
|
+
"(its console says \"Cannot find logic comorphism\"). On the "
|
|
374
|
+
"command line the composition separator is ':' — pass "
|
|
375
|
+
f"comorphism=\"{comorphism}\" spelled with ':'. Hets spells it "
|
|
376
|
+
"with ';' in its own messages; that spelling is not accepted as "
|
|
377
|
+
"input.")
|
|
378
|
+
|
|
379
|
+
|
|
380
|
+
def _sublogic_mismatch(console: str) -> Optional[SublogicMismatch]:
|
|
381
|
+
"""HETS' sublogic warning, parsed, or ``None`` if it did not complain."""
|
|
382
|
+
match = _SUBLOGIC_RE.search(console)
|
|
383
|
+
if not match:
|
|
384
|
+
return None
|
|
385
|
+
comorphism, expected, found = match.groups()
|
|
386
|
+
return SublogicMismatch(comorphism=comorphism, expected=expected, found=found)
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
def _pick_output(run: _Run, suffix: str) -> Tuple[str, str]:
|
|
390
|
+
"""The one file with this suffix; never a pick between two.
|
|
391
|
+
|
|
392
|
+
Returns ``(basename, text)``.
|
|
393
|
+
"""
|
|
394
|
+
candidates = sorted(name for name in run.outputs if name.endswith(suffix))
|
|
395
|
+
if len(candidates) == 1:
|
|
396
|
+
return candidates[0], run.outputs[candidates[0]]
|
|
397
|
+
if not candidates:
|
|
398
|
+
raise RuntimeError(
|
|
399
|
+
f"hets: hets-server exited {run.returncode} and reported a "
|
|
400
|
+
f"translation, but wrote no {suffix} file into {run.out_dir} "
|
|
401
|
+
f"(it wrote {sorted(run.outputs) or 'nothing'}). Its console "
|
|
402
|
+
"was:\n" + run.console)
|
|
403
|
+
raise RuntimeError(
|
|
404
|
+
f"hets: hets-server wrote {len(candidates)} {suffix} files into "
|
|
405
|
+
f"{run.out_dir} ({', '.join(candidates)}) and this helper will not "
|
|
406
|
+
"pick between them — one ontology file should produce one translated "
|
|
407
|
+
"theory. Translate a single-ontology file, or inspect the directory "
|
|
408
|
+
"yourself.")
|
|
409
|
+
|
|
410
|
+
|
|
411
|
+
def _node_from_filename(basename: str, suffix: str) -> str:
|
|
412
|
+
"""Recover the development-graph node IRI from HETS' output file name.
|
|
413
|
+
|
|
414
|
+
HETS writes ``<stem>_<percent-encoded node IRI><suffix>``, e.g.
|
|
415
|
+
``oeo-hets_https%3A%2F%2Fopenenergyplatform.org%2Fontology%2Foeo%2F.tptp``.
|
|
416
|
+
"""
|
|
417
|
+
import urllib.parse
|
|
418
|
+
|
|
419
|
+
stem = basename[:-len(suffix)] if basename.endswith(suffix) else basename
|
|
420
|
+
_, sep, encoded = stem.partition("_")
|
|
421
|
+
return urllib.parse.unquote(encoded) if sep else stem
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
def _run_hets(docker: str, container: str, remote_dir: str, remote_in: str,
|
|
425
|
+
comorphism: str, lossy: bool, timeout: float) -> _Run:
|
|
426
|
+
"""One translation attempt, with its own output directory, read back."""
|
|
427
|
+
out_dir = posixpath.join(remote_dir, "out_lossy" if lossy else "out_probe")
|
|
428
|
+
command = _hets_command(remote_in, out_dir, comorphism, lossy)
|
|
429
|
+
argv = [docker, "exec", container, "sh", "-c",
|
|
430
|
+
f"mkdir -p {out_dir} && cd {remote_dir} && {command}"]
|
|
431
|
+
proc = subprocess.run(argv, capture_output=True, text=True, timeout=timeout)
|
|
432
|
+
console = (proc.stdout or "") + (proc.stderr or "")
|
|
433
|
+
_code, listing = _exec(docker, container,
|
|
434
|
+
f"ls -1 {out_dir} 2>/dev/null || true", timeout)
|
|
435
|
+
outputs = {}
|
|
436
|
+
for name in (line.strip() for line in listing.splitlines()):
|
|
437
|
+
if not name:
|
|
438
|
+
continue
|
|
439
|
+
code, text = _exec(docker, container,
|
|
440
|
+
f"cat {posixpath.join(out_dir, name)}", timeout)
|
|
441
|
+
if code == 0:
|
|
442
|
+
outputs[name] = text
|
|
443
|
+
return _Run(argv=argv, returncode=proc.returncode, console=console,
|
|
444
|
+
out_dir=out_dir, outputs=outputs)
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
def owl_to_tptp(path: str, *, lossy: bool = True,
|
|
448
|
+
comorphism: str = "OWL22CASL:CASL2TPTP_FOF",
|
|
449
|
+
container: Union[str, HetsContainer, None] = None,
|
|
450
|
+
start_container: bool = False,
|
|
451
|
+
client: Optional[HetsClient] = None,
|
|
452
|
+
timeout: float = 900.0) -> OwlTptpResult:
|
|
453
|
+
r"""Translate an OWL ontology file to TPTP through ``hets-server``.
|
|
454
|
+
|
|
455
|
+
The command line, exactly as the evidence records it working::
|
|
456
|
+
|
|
457
|
+
hets-server -v2 -a none [-Y] -t <comorphism> -o th,tptp -O <dir> <file>
|
|
458
|
+
|
|
459
|
+
Args:
|
|
460
|
+
path: a local OWL file (RDF/XML or functional syntax — whatever HETS
|
|
461
|
+
reads). It is copied into the container and removed again.
|
|
462
|
+
lossy: ``True`` (the default) runs the NON-LOSSY translation first as
|
|
463
|
+
a probe and only falls back to ``-Y`` when HETS complains about
|
|
464
|
+
the sublogic, so a loss is always REPORTED on
|
|
465
|
+
:attr:`OwlTptpResult.sublogic_mismatch` rather than being silent.
|
|
466
|
+
Costs a second HETS run; see the module docstring for the
|
|
467
|
+
measured price and the ``client=`` alternative. ``False`` does
|
|
468
|
+
one run and raises
|
|
469
|
+
:class:`~unicode_logic_kit.hets.client.HetsSublogicError` instead.
|
|
470
|
+
comorphism: a HETS comorphism composition, ``:``-separated. The
|
|
471
|
+
``;`` spelling HETS uses in its own messages is refused by name
|
|
472
|
+
(it exits 0 and silently writes nothing), and any ``SoftFOL``
|
|
473
|
+
target is refused by name too (HETS 0.108.0 aborts on CASL
|
|
474
|
+
numerals).
|
|
475
|
+
container: a running container's name, or a
|
|
476
|
+
:class:`~unicode_logic_kit.hets.docker.HetsContainer`. Falls back
|
|
477
|
+
to ``$UFK_HETS_CONTAINER``.
|
|
478
|
+
start_container: start (and stop) a container owned by this call when
|
|
479
|
+
nothing else is available.
|
|
480
|
+
client: a :class:`~unicode_logic_kit.hets.client.HetsClient` for the
|
|
481
|
+
SAME HETS. Given one, the result also carries
|
|
482
|
+
:attr:`~OwlTptpResult.symbols` and
|
|
483
|
+
:attr:`~OwlTptpResult.omitted_axioms`, computed from ``/dg``.
|
|
484
|
+
Without one both are ``None`` — not empty.
|
|
485
|
+
timeout: seconds for each ``docker exec``.
|
|
486
|
+
|
|
487
|
+
Returns:
|
|
488
|
+
An :class:`OwlTptpResult`.
|
|
489
|
+
|
|
490
|
+
Raises:
|
|
491
|
+
~unicode_logic_kit.atp.protocol.BackendUnavailable: no container to
|
|
492
|
+
exec into, or no ``docker`` on PATH.
|
|
493
|
+
~unicode_logic_kit.hets.client.HetsSublogicError: ``lossy=False`` and
|
|
494
|
+
HETS refused the comorphism for this theory.
|
|
495
|
+
HetsOwlNormalizationError: HETS refused the ontology because an
|
|
496
|
+
annotation subject is undeclared.
|
|
497
|
+
RuntimeError: a bad comorphism spelling, or HETS exited without
|
|
498
|
+
writing a ``.tptp``.
|
|
499
|
+
"""
|
|
500
|
+
_refuse_bad_comorphism(comorphism)
|
|
501
|
+
docker = _docker_bin()
|
|
502
|
+
container_name, owned = _resolve_container(container, start_container)
|
|
503
|
+
remote_dir = posixpath.join("/tmp", f"ufk_owl_to_tptp_{uuid.uuid4().hex[:12]}")
|
|
504
|
+
local_name = os.path.basename(path) or "ontology.owl"
|
|
505
|
+
remote_in = posixpath.join(remote_dir, local_name)
|
|
506
|
+
try:
|
|
507
|
+
_exec(docker, container_name, f"mkdir -p {remote_dir}", timeout)
|
|
508
|
+
copy = subprocess.run(
|
|
509
|
+
[docker, "cp", path, f"{container_name}:{remote_in}"],
|
|
510
|
+
capture_output=True, text=True, timeout=timeout)
|
|
511
|
+
if copy.returncode != 0:
|
|
512
|
+
raise RuntimeError(
|
|
513
|
+
f"hets: `docker cp {path} {container_name}:{remote_in}` failed "
|
|
514
|
+
f"(exit {copy.returncode}): "
|
|
515
|
+
f"{(copy.stderr or '').strip()!r}")
|
|
516
|
+
|
|
517
|
+
probe = _run_hets(docker, container_name, remote_dir, remote_in,
|
|
518
|
+
comorphism, lossy=False, timeout=timeout)
|
|
519
|
+
_check_console(probe, comorphism)
|
|
520
|
+
mismatch = _sublogic_mismatch(probe.console)
|
|
521
|
+
translated = _TRANSLATED_RE.search(probe.console)
|
|
522
|
+
|
|
523
|
+
if translated and not mismatch:
|
|
524
|
+
run, used_lossy = probe, False
|
|
525
|
+
elif not lossy:
|
|
526
|
+
if mismatch:
|
|
527
|
+
raise HetsSublogicError(
|
|
528
|
+
"hets: Hets refuses the comorphism "
|
|
529
|
+
f"{mismatch.comorphism!r} for this ontology — it covers "
|
|
530
|
+
f"sublogic {mismatch.expected!r} but the theory is "
|
|
531
|
+
f"{mismatch.found!r}, so hets-server kept the "
|
|
532
|
+
"UNTRANSLATED theory and wrote no .tptp. Pass lossy=True "
|
|
533
|
+
"to run hets-server -Y, which translates anyway and "
|
|
534
|
+
"reports the axioms it omitted on the result's "
|
|
535
|
+
".omitted_axioms.",
|
|
536
|
+
comorphism=mismatch.comorphism,
|
|
537
|
+
expected=mismatch.expected, found=mismatch.found,
|
|
538
|
+
body=probe.console)
|
|
539
|
+
run, used_lossy = probe, False
|
|
540
|
+
else:
|
|
541
|
+
run = _run_hets(docker, container_name, remote_dir, remote_in,
|
|
542
|
+
comorphism, lossy=True, timeout=timeout)
|
|
543
|
+
_check_console(run, comorphism)
|
|
544
|
+
used_lossy = True
|
|
545
|
+
translated = _TRANSLATED_RE.search(run.console)
|
|
546
|
+
|
|
547
|
+
if not translated:
|
|
548
|
+
raise RuntimeError(
|
|
549
|
+
f"hets: hets-server exited {run.returncode} but wrote no "
|
|
550
|
+
".tptp into the output directory and its console does not "
|
|
551
|
+
"contain \"Translated using comorphism\" — Hets keeps the "
|
|
552
|
+
"UNTRANSLATED theory and only warns. Its console was:\n"
|
|
553
|
+
+ run.console)
|
|
554
|
+
tptp_name, tptp_text = _pick_output(run, ".tptp")
|
|
555
|
+
try:
|
|
556
|
+
_theory_name, theory_text = _pick_output(run, ".th")
|
|
557
|
+
except RuntimeError:
|
|
558
|
+
theory_text = ""
|
|
559
|
+
try:
|
|
560
|
+
_pretty_name, pretty_text = _pick_output(run, ".pp.dol")
|
|
561
|
+
except RuntimeError:
|
|
562
|
+
pretty_text = ""
|
|
563
|
+
node = _node_from_filename(tptp_name, ".tptp")
|
|
564
|
+
|
|
565
|
+
symbols = None
|
|
566
|
+
omitted = None
|
|
567
|
+
if client is not None:
|
|
568
|
+
with open(path, encoding="utf-8") as handle:
|
|
569
|
+
iri = client.upload(handle.read(), local_name)
|
|
570
|
+
dg = client.dg(iri)
|
|
571
|
+
# pp.dol first: it is the SOURCE rendering and the only output
|
|
572
|
+
# that carries the Prefix: lines. The translated .th is tried as
|
|
573
|
+
# a fallback for a comorphism whose target keeps them.
|
|
574
|
+
prefixes = hets_prefixes(pretty_text) or hets_prefixes(theory_text)
|
|
575
|
+
symbols = hets_symbol_table(dg, tptp_text, prefixes=prefixes)
|
|
576
|
+
omitted = untranslated_axioms(dg, tptp_text)
|
|
577
|
+
|
|
578
|
+
return OwlTptpResult(
|
|
579
|
+
tptp=tptp_text,
|
|
580
|
+
node=node,
|
|
581
|
+
comorphism=translated.group(1),
|
|
582
|
+
lossy=used_lossy,
|
|
583
|
+
sublogic_mismatch=mismatch,
|
|
584
|
+
console=run.console,
|
|
585
|
+
theory=theory_text,
|
|
586
|
+
pretty=pretty_text,
|
|
587
|
+
symbols=symbols,
|
|
588
|
+
omitted_axioms=omitted,
|
|
589
|
+
)
|
|
590
|
+
finally:
|
|
591
|
+
# Never leave anything behind in a container this kit does not own,
|
|
592
|
+
# and never write outside /tmp.
|
|
593
|
+
try:
|
|
594
|
+
_exec(docker, container_name, f"rm -rf {remote_dir}", timeout)
|
|
595
|
+
except Exception:
|
|
596
|
+
pass
|
|
597
|
+
if owned is not None:
|
|
598
|
+
owned.stop()
|