Ontology reasoning
The ontology layer compiles immutable named-class/object-property axioms to a standalone positive-Datalog program. It uses no database handle and adds no mandatory solver dependency. It does not change asserted labels, relationships, Cypher results or sampler membership. All database interactions are explicit adapters; core and compiler depend only inward and on the standard library.
Optional offline RDF import
Install gestaltdb[rdf] and import from
gestaltdb.ontology.adapters.rdf. import_rdf(RDFDocument(text),
ontology_id="urn:my-ontology") returns an immutable import report; call its
compile() method before reasoning. Turtle, N-Triples and UTF-8 RDF/XML are
supported. Named terms retain full IRIs. Class/object-property declarations and
the selected axiom forms are accepted, including binary owl:propertyChainAxiom
lists, left-side owl:intersectionOf/owl:unionOf collections (with
intersection superclasses expanded to subclass pairs) and named class/property
forms; ABox statements, restrictions, builtin
class/datatype semantics and other unsupported logic fail strictly by default.
Right-side unions, unscoped owl:ReflexiveProperty, longer chains and
unused collections are rejected or reported incomplete.
No URLs or files are loaded. Resolve owl:imports explicitly with an in-memory
imports={iri: RDFDocument(...)} map. Cycles are deduplicated. JSON-LD plugins,
XML DTDs/entities and implicit network import resolution are not supported.
RDFImportLimits bounds cumulative bytes, triples and documents. These are
not an operating-system sandbox. strict=False returns diagnostics and
complete=False on unsupported logic; compiling that report additionally
requires allow_incomplete=True. Never represent such output as complete
OWL/RDFS reasoning. Standard label/comment/version annotations are counted and
ignored, not converted into logical facts.
Sampling and Cypher type hierarchies
gestaltdb.ontology.adapters.hierarchy builds the standalone
LabelHierarchy and RelationHierarchy values used by opt-in Cypher
matching and sampler expansion from an ontology’s direct SubClassOf and
SubPropertyOf axioms:
from gestaltdb.ontology import Ontology, SubClassOf, SubPropertyOf
from gestaltdb.ontology.adapters.hierarchy import (
label_hierarchy_from_ontology,
relation_hierarchy_from_ontology,
)
ontology = Ontology("urn:example:freight", (
SubClassOf("Ocean_Freight", "Transport_Edge"),
SubClassOf("Refrigerated_Freight", "Ocean_Freight"),
SubPropertyOf("Ocean_Freight", "Transport_Edge"),
))
labels = label_hierarchy_from_ontology(ontology)
relations = relation_hierarchy_from_ontology(ontology)
assert labels.closure("Transport_Edge") == (
"Transport_Edge", "Ocean_Freight", "Refrigerated_Freight")
assert relations.closure("Transport_Edge") == (
"Transport_Edge", "Ocean_Freight")
Pass labels to GraphDB.query(..., label_hierarchy=labels) and
relations to SamplerSnapshot.expand_relations(...).
Only direct subsumption is flattened; the ontology revision is not modified and
no closure is persisted. Multiple superclasses are preserved, duplicate
declarations are deduplicated, and cycles or self-parenting fail closed.
EquivalentClasses, EquivalentProperties, intersection and union
subsumption require a canonicalization policy that a strict hierarchy cannot
infer, so the adapter raises ValueError rather than silently dropping those
semantics. Resolve them explicitly or build the hierarchy directly.
Standalone example
from gestaltdb.ontology import Domain, Ontology, SubClassOf, TYPE_PREDICATE, compile_ontology
from gestaltdb.reasoning import DatalogEngine, Fact, ReasoningLimits
plan = compile_ontology(Ontology("people", [
Domain("worksFor", "Researcher"), SubClassOf("Researcher", "Person"),
]))
result = DatalogEngine().evaluate(
plan.program, [Fact("alice", "worksFor", "acme")],
limits=ReasoningLimits(), collect_proofs=True,
)
person = Fact("alice", TYPE_PREDICATE, "Person")
assert person in result
explanation = result.explain(person)
assert not explanation.truncated
Use result.match(subject=..., predicate=..., object=...) for immutable result
lookups. The compiled origin table maps exact witness rules back to source axioms.
The profile supports subclass/subproperty, named equivalence, domain/range,
inverse, symmetric and transitive properties, plus binary property chains,
left-side class conjunction/disjunction and scoped reflexivity. Cycles terminate
under set semantics.
It is a selected gestalt-core-v1 fragment, not full RDFS or OWL support.
Equality, datatype reasoning, disjointness, existential generation and negation
are not supported. Domain/range infer memberships rather than validate missing data.
Read-only temporal claims
This source adapter never writes claims. The separate explicit, point-only publication operation is documented in Explicit point publication.
Install an optional backend for this example (gestaltdb[lmdb]). The existing
read view pins one valid-time point and visible commit prefix. The adapter verifies
its source, policy, mapping and ontology revision before returning a scoped result.
from tempfile import TemporaryDirectory
from gestaltdb.graphdb import GraphDB
from gestaltdb.ontology import Domain, Ontology, compile_ontology
from gestaltdb.reasoning import ReasoningLimits
from gestaltdb.reasoning.adapters.claims import ClaimMapping, ClaimSelection, PinnedClaimSource
plan = compile_ontology(Ontology("people", [Domain("worksFor", "Person")]))
with TemporaryDirectory() as directory:
graph = GraphDB.create(directory, backend="lmdb", serializer="json")
try:
graph.assert_claim(subject="alice", predicate="worksFor", object="acme",
agent="registry", polarity="positive", valid=(0, 100))
with graph.read_view(valid_time=50) as view:
with PinnedClaimSource(view, world="default", ontology_revision=plan.revision_id,
selection=ClaimSelection(), mapping=ClaimMapping()) as source:
scoped = source.evaluate(plan, limits=ReasoningLimits(), collect_proofs=True)
assert scoped.context.ontology_revision == plan.revision_id
finally:
graph.close()
Only selected positive entity claims participate. Explicit negatives do not
suppress independent positive support; confidence does not become a probability.
Recognized derived claims are excluded unless deliberately opted in. The source
does not initialize identity, rebuild indexes or publish results. A point result
does not justify an unbounded validity interval. Raw evidence iteration alone is
not a context-bound atomic evaluation; use the adapter’s evaluate method.
For bounded batched extraction with explicit budgets, capability probing and
per-batch verification, see Pinned batched claim extraction.
Limits and deployment
For explicit current-property-graph projection and derived-relation neighbor sampling, see Current-graph ontology adapters. This is separate from temporal claims.
Inference bounds fail closed; proof-node/edge bounds only truncate the witness trace, explicitly. A trace stores one witness, not all independent supports. Counters are not a wall-clock/RSS sandbox for arbitrary Python inputs. Neither the native engine nor a solver swap makes temporal extraction free. No JVM integration is supported. Clingo was faster on larger dense synthetic closures, but does not provide the same work-counter/witness contract in this v1. See Ontology solver benchmarks and v1 decision for measured trade-offs, startup/footprint caveats and reproducible evidence. The development contract is Ontology v1 implementation contract. For additive relational IR and structural plan inspection, see Relational execution contracts (no new execution semantics).