Skip to main content

pyELK

PyPI Python License

pyELK is a Java-free implementation of the core reasoning behaviour of ELK Reasoner 0.6.0. It provides one typed Python API with a portable pure-Python backend and an optional Rust/PyO3 accelerator. Both backends consume the same deterministic compiled ontology and return the same canonical public values.

The distribution is pyelk-reasoner; the import package is pyelk:

python -m pip install pyelk-reasoner

The separate pyelk distribution on PyPI is an unrelated graph-layout project. A normal install selects a compatible native wheel when one is published and otherwise installs the universal Python wheel. Python 3.10 and 3.12 run the complete release suites; the native module uses the CPython 3.10 limited ABI and is additionally exercised on every supported CPython minor through 3.14.

Start with the documentation index or the getting-started guide. The distribution is pyelk-reasoner; do not install the unrelated pyelk graph-layout package. Existing 0.1 users should read the 0.2 migration guide before reusing persisted pyowl-core snapshots or custom encoded-view providers.

First classification

pyELK delegates OWL syntax, structural values, imports, and immutable snapshots to the independent pyowl-core package. A path, bytes-like value, or caller-owned stream can be passed directly. Streams require a stable document_iri; pyELK never closes them.

from io import BytesIO

import pyowl_core as owl
from pyelk import Reasoner

document = (
    b"Prefix(:=<urn:readme#>) Ontology(<urn:readme> "
    b"Declaration(Class(:A)) Declaration(Class(:B)) SubClassOf(:A :B))"
)
options = owl.LoadOptions(
    format=owl.DocumentFormat.FUNCTIONAL,
    imports=owl.ImportPolicy.IGNORE,
    backend=owl.BackendPreference.PYTHON,
)

with Reasoner(document, load_options=options) as reasoner:
    byte_result = reasoner.classify()
    taxonomy = byte_result.require_complete()
    assert reasoner.backend.name in {"python", "rust"}
    assert taxonomy.node(owl.Class(owl.IRI("urn:readme#A"))) is not None

stream = BytesIO(document)
with Reasoner(
    stream,
    document_iri="urn:readme:stream",
    load_options=options,
) as reasoner:
    assert reasoner.classify() == byte_result
assert not stream.closed

Reasoner also accepts str/PathLike paths, OntologyDocument, every immutable OntologyView, and SnapshotProvider. Import resolution is explicit through LoadOptions and an optional pyowl-core ImportResolver; unresolved or intentionally ignored imports are never silently presented as complete reasoning.

Completeness is part of every answer

Each operation returns ReasoningResult[T], containing value, complete, and canonical reasons. complete means complete for the pinned ELK 0.6 procedure, not for arbitrary OWL. Inspect the metadata when a partial value is useful, or call require_complete() before using a value that must be authoritative.

import pyowl_core as owl
from pyelk import Reasoner
from pyelk.exceptions import IncompleteReasoningError

document = (
    b"Prefix(:=<urn:readme#>) Ontology(<urn:readme> "
    b"SubClassOf(ObjectAllValuesFrom(:p :A) :B))"
)
options = owl.LoadOptions(
    format=owl.DocumentFormat.FUNCTIONAL,
    imports=owl.ImportPolicy.IGNORE,
    backend=owl.BackendPreference.PYTHON,
)
with Reasoner(document, load_options=options) as reasoner:
    result = reasoner.classify()
    assert not result.complete
    assert any("OBJECT_ALL_VALUES_FROM" in issue.features for issue in result.reasons)
    try:
        result.require_complete()
    except IncompleteReasoningError as error:
        assert error.reasons == result.reasons
    else:
        raise AssertionError("an incomplete result was accepted")

Set ReasonerConfig(unsupported="error") when unsupported ontology constructs should fail during construction instead of producing an explicitly incomplete result. The exact construct, polarity, combination, per-operation monitor, and entailment-query boundary are listed in specs/compatibility.md.

Exact-OM and shared snapshots

When a caller already owns a pyowl-core view, pass it directly. pyELK retains that exact object, does not parse it again, and never converts public entities into a second OWL model. An Exact-OM-style owner can expose only owl_snapshot(); it is called once. Source, target, and bridge views can be combined as an identity-preserving OntologyComposite.

import pyowl_core as owl
from pyelk import Reasoner

options = owl.LoadOptions(
    format=owl.DocumentFormat.FUNCTIONAL,
    imports=owl.ImportPolicy.IGNORE,
    backend=owl.BackendPreference.PYTHON,
)
source = owl.load_snapshot(
    b"Prefix(:=<urn:shared#>) Ontology(SubClassOf(:A :B))",
    options=options,
)
target = owl.load_snapshot(
    b"Prefix(:=<urn:shared#>) Ontology(SubClassOf(:B :C))",
    options=options,
)
shared = owl.compose_views(source, target, roles=("source", "target"))


class ExactModel:
    def __init__(self, view: owl.OntologyView) -> None:
        self.view = view
        self.calls = 0

    def owl_snapshot(self) -> owl.OntologyView:
        self.calls += 1
        return self.view


model = ExactModel(shared)
with Reasoner(model) as reasoner:
    assert reasoner.ontology is shared
    taxonomy = reasoner.classify().require_complete()
    public_a = next(entity for entity in shared.signature() if entity.iri.value.endswith("#A"))
    assert taxonomy.node(public_a).members[0] == public_a
assert model.calls == 1

pyELK imports neither Exact-OM nor any consumer-private record type. The interoperability boundary is only pyowl-core's OntologyView/SnapshotProvider contract.

OAEI and process boundaries

For a worker process, persist the shared snapshot with pyowl-core's versioned wire format. The receiving process should verify and memory-map it before constructing the reasoner. No OWL parser runs on this path.

from pathlib import Path
from tempfile import TemporaryDirectory

import pyowl_core as owl
from pyelk import Reasoner

options = owl.LoadOptions(
    format=owl.DocumentFormat.FUNCTIONAL,
    imports=owl.ImportPolicy.IGNORE,
    backend=owl.BackendPreference.PYTHON,
)
document = b"Prefix(:=<urn:wire#>) Ontology(SubClassOf(:A :B))"
original = owl.load_snapshot(document, options=options)

with TemporaryDirectory() as directory:
    path = Path(directory) / "ontology.pyocore"
    path.write_bytes(owl.encode_snapshot(original))
    mapped = owl.open_snapshot(path, mmap=True, verify=True)
    try:
        with Reasoner(mapped) as reasoner:
            assert reasoner.ontology is mapped
            assert reasoner.classify().require_complete().nodes
    finally:
        mapped.close()

This is the supported OAEI-Bio-ML-eval handoff for timeout-isolated workers. Do not serialize private pyELK indexes or consumer records as an interchange format.

Backend selection and diagnostics

The default auto mode uses a compatible native extension when its version/ABI/IR handshake passes, and otherwise selects Python while retaining the fallback reason. Use ReasonerConfig(backend="python" | "rust") for a session-specific choice, or these process controls:

  • PYELK_BACKEND=auto|python|rust selects the default request;
  • PYELK_PURE_PYTHON=1 forces Python and prevents even probing _native;
  • an explicit Rust request fails with BackendUnavailableError rather than falling back.
import pyowl_core as owl
from pyelk import backend_report

report = backend_report()
assert report.selection_error is None
assert report.selected in {"python", "rust"}
assert report.core_package_version == owl.__version__
assert report.core_api_version == owl.API_VERSION
assert report.core_model_schema_version == owl.MODEL_SCHEMA_VERSION
assert report.core_wire_format_version == owl.WIRE_FORMAT_VERSION
assert report.core_adapter_protocol_version == owl.ADAPTER_PROTOCOL_VERSION

if report.selected == "python" and report.rust.reason:
    print(f"native backend not selected: {report.rust.reason}")

reasoner.backend reports the immutable choice, effective worker count, native availability, and fallback reason for a live session. reasoner.diagnostics() returns an immutable scalar mapping. Every session reports its canonical compiler_digest, compiler-cache and private-IR schema versions, implementation version, total consumer compile time, scalar-row materialization count, and an exact encoded buffer/copy/segment ledger. ingestion_path is scalar-python, scalar-wire, or encoded-native; scalar sessions report contractual zero/false encoded counters. Encoded sessions additionally report view-publication time, native validation, compilation, session-build, and total native-boundary durations. Recursive composite sessions count each temporary resolved posting byte and anonymous-scope mapping pair in encoded_staging_copy_bytes; they never label those bounded metadata copies as zero-copy. pyelk.core.require_core_compatibility() performs the full pyowl-core package/API/model/wire/ adapter guard explicitly.

Supported reasoning surface

The public facade provides consistency, class and object-property classification, realization, class-expression satisfiability, equivalent/sub/super-class queries, instances/types, object-property views, entity enumeration, and the pinned supported entailment families. It follows OWL 2 Direct Semantics and ELK's quiet inconsistent-ontology values.

The complete fragment is deliberately “ELK 0.6 compatible,” not “all OWL 2 EL.” Named classes/properties, intersections, existential restrictions, the core class axioms, individual equality/difference, supported assertions, domains/ranges, and named property hierarchies/chains are handled according to the pinned converter. Several data constructs, cardinalities, universals, inverses, keys, and other features are ignored or partial with exact completeness reasons. Annotation axioms remain in the shared snapshot but are non-logical to pyELK.

Runtime and current limits

Installed pyELK never launches or embeds Java. Java is used only by opt-in development tools that regenerate or compare frozen ELK 0.6 oracle data; JARs and class files are rejected from release artifacts. The Python fallback also needs no Rust/C/C++ compiler at installation or runtime.

Version 0.2 sessions are immutable. Incremental reasoning, proofs/explanations/tracing, method-for-method OWL API compatibility, datatype reasoning, a CLI, and a Protégé plugin are outside scope. Input syntaxes and import retrieval are exactly those supplied by the installed compatible pyowl-core. Native availability is platform-wheel dependent; Python remains the semantic reference and fallback.

Development verification

The default suite is Java-free:

PYELK_BUILD_PURE=1 PYELK_PURE_PYTHON=1 python -m pytest
ruff format --check .
ruff check .
mypy
lint-imports

The unified frozen-corpus runner covers all 124 upstream ontologies and 138 goldens:

python tests/parity/runner.py --backend python --workers 1
python tests/parity/runner.py --backend rust --workers 1 --workers 0

Integrated benchmark metadata and commands live in benchmarks/manifest.toml and tools/benchmark.py. Java oracle and performance gates are opt-in and pinned; ordinary tests and installed artifacts have no Java dependency. See specs/verification.md for the release gates and specs/traceability.md for the implemented source-to-test map.

pyELK is an independent Apache-2.0 reimplementation informed by the pinned ELK source and tests. Attribution and modification details are in NOTICE.pyelk.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

pyelk_reasoner-0.2.0.tar.gz (239.3 kB view details)

Uploaded Source

Built Distributions

If you're not sure about the file name format, learn more about wheel file names.

pyelk_reasoner-0.2.0-py3-none-any.whl (145.9 kB view details)

Uploaded Python 3

pyelk_reasoner-0.2.0-cp310-abi3-win_amd64.whl (951.1 kB view details)

Uploaded CPython 3.10+Windows x86-64

pyelk_reasoner-0.2.0-cp310-abi3-musllinux_1_2_x86_64.whl (1.3 MB view details)

Uploaded CPython 3.10+musllinux: musl 1.2+ x86-64

pyelk_reasoner-0.2.0-cp310-abi3-musllinux_1_2_aarch64.whl (1.2 MB view details)

Uploaded CPython 3.10+musllinux: musl 1.2+ ARM64

pyelk_reasoner-0.2.0-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl (1.2 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ x86-64

pyelk_reasoner-0.2.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl (1.1 MB view details)

Uploaded CPython 3.10+manylinux: glibc 2.17+ ARM64

pyelk_reasoner-0.2.0-cp310-abi3-macosx_11_0_arm64.whl (1.0 MB view details)

Uploaded CPython 3.10+macOS 11.0+ ARM64

pyelk_reasoner-0.2.0-cp310-abi3-macosx_10_12_x86_64.whl (1.1 MB view details)

Uploaded CPython 3.10+macOS 10.12+ x86-64

File details

Details for the file pyelk_reasoner-0.2.0.tar.gz.

File metadata

  • Download URL: pyelk_reasoner-0.2.0.tar.gz
  • Upload date:
  • Size: 239.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for pyelk_reasoner-0.2.0.tar.gz
Algorithm Hash digest
SHA256 74dca1f7ef94f05999cdaf8328642f94ceb449f0d49247c048e26d39fba3fb68
MD5 fc3d4201e4aad7a3a1d84712051ece19
BLAKE2b-256 c87e1cf2a3c43d25f18d21dafe0927edd0e4ba7d21848c7d3decd432f2c6de88

See more details on using hashes here.

File details

Details for the file pyelk_reasoner-0.2.0-py3-none-any.whl.

File metadata

  • Download URL: pyelk_reasoner-0.2.0-py3-none-any.whl
  • Upload date:
  • Size: 145.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.3

File hashes

Hashes for pyelk_reasoner-0.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 019193f4be2ac423bdb5b4f6e299762530397d4bf897f5b833c84ed8d087e20e
MD5 4298af9f894a3cf9527b77f9806fb4ad
BLAKE2b-256 67c5b7d73d00d6a4b5bb0840bb057922086cdf140466cb5f9d4c1bc59ab49177

See more details on using hashes here.

File details

Details for the file pyelk_reasoner-0.2.0-cp310-abi3-win_amd64.whl.

File metadata

File hashes

Hashes for pyelk_reasoner-0.2.0-cp310-abi3-win_amd64.whl
Algorithm Hash digest
SHA256 e8b808d5e60dad577510a17271f09843674cf996340ec665ad66cf06f32793d8
MD5 307e571dc09712bec16a1ab44c41ecf2
BLAKE2b-256 02b5d92b6c22b6307863cab1c7f48fc3a0d582d10a5536e507eccbeda7b1828a

See more details on using hashes here.

File details

Details for the file pyelk_reasoner-0.2.0-cp310-abi3-musllinux_1_2_x86_64.whl.

File metadata

File hashes

Hashes for pyelk_reasoner-0.2.0-cp310-abi3-musllinux_1_2_x86_64.whl
Algorithm Hash digest
SHA256 8be1e4c3f242572199f98f3fd72f524f443527144a34e2f6473f6a8087cd9b8b
MD5 96c84b3e525bb8d92850e0becb204d0d
BLAKE2b-256 795452195ab79c43151fada2645c7e8ef8ebd4bc79688e3e5e9213734c5a30c7

See more details on using hashes here.

File details

Details for the file pyelk_reasoner-0.2.0-cp310-abi3-musllinux_1_2_aarch64.whl.

File metadata

File hashes

Hashes for pyelk_reasoner-0.2.0-cp310-abi3-musllinux_1_2_aarch64.whl
Algorithm Hash digest
SHA256 7d1e1ea970b8eefb528fe2c0b0e26d758bd68d81378c50994ca3466209217758
MD5 0509c2397c1b338f5a69194a5b04a825
BLAKE2b-256 a665fc60ae5337250ddf70bbadf4fb0839d27df8fa3c2113fb5d9e75e56d95d8

See more details on using hashes here.

File details

Details for the file pyelk_reasoner-0.2.0-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl.

File metadata

File hashes

Hashes for pyelk_reasoner-0.2.0-cp310-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl
Algorithm Hash digest
SHA256 584940db4f1a75637eb618ac41d6fb6f724a6cf90324d67b53756dbeb89af2af
MD5 39acc953a0981bfb804d4950e3c2c30f
BLAKE2b-256 2c18f6974a5b999e34d6337eee8885d1ca016f53f05cd7f733fc230ddc4122a6

See more details on using hashes here.

File details

Details for the file pyelk_reasoner-0.2.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl.

File metadata

File hashes

Hashes for pyelk_reasoner-0.2.0-cp310-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl
Algorithm Hash digest
SHA256 c2ff9e6f5909a20bd8e594688e16d03a7d3be261b97660e07c84895ae38f7f92
MD5 47ad9dc20a6fd893a103e5da51b3a42a
BLAKE2b-256 6a2c44cff830352c39c94b4ad24e2afe00ae05a03000385f26d4dc1cbc79fc8c

See more details on using hashes here.

File details

Details for the file pyelk_reasoner-0.2.0-cp310-abi3-macosx_11_0_arm64.whl.

File metadata

File hashes

Hashes for pyelk_reasoner-0.2.0-cp310-abi3-macosx_11_0_arm64.whl
Algorithm Hash digest
SHA256 e9519bd02480df6c63284eb46a5e046bbb857782568b12ea44d2575c068e63d5
MD5 1ad4f8c312088834deab391a9d90d41c
BLAKE2b-256 3db9cd8b44971dd93be0cf177bc8ac54ed49e3585136758875b0768591666eaf

See more details on using hashes here.

File details

Details for the file pyelk_reasoner-0.2.0-cp310-abi3-macosx_10_12_x86_64.whl.

File metadata

File hashes

Hashes for pyelk_reasoner-0.2.0-cp310-abi3-macosx_10_12_x86_64.whl
Algorithm Hash digest
SHA256 261735da52cf0ca1f827178fc3186dc94fea12ae4d9910db18957e17eb2a6636
MD5 16883e0e0d99e5d6e76f4cd8c34533a8
BLAKE2b-256 e691a938e60c1daeb05f24740781df01eed0aa91b0bb94f433e39f6f9e2af218

See more details on using hashes here.

Release history Release notifications | RSS feed

This release

0.2.0 This release

9 files

0.1.1

9 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page