Design: RDF reasoning in Kastor and onto-quality v0.4

This document describes how RDF reasoning is exposed in Kastor and how onto-quality v0.4 uses it to run SHACL validation over a materialized (asserted + entailed) graph.


Part 1 — Reasoning in the Kastor RDF stack

Goals

  • Provide a single facade for “run a reasoner on an RdfGraph” without locking callers to one engine.
  • Allow multiple backends (in-memory reference rules, Jena, RDF4J, …) via provider registration.
  • Keep core free of heavyweight reasoner dependencies; live implementations live in modules such as :rdf:reasoning, :rdf:jena, :rdf:rdf4j.

Main types (module :rdf:reasoning)

API Role
RdfReasoning Factory: reasoner(config), reasoner(type), provider discovery helpers.
RdfReasoner reason(graph), getInferredTriples, classify, isConsistent, validateOntology.
ReasonerConfig ReasonerType, enabled rules, timeouts, streaming/batch hints, custom rules.
ReasonerRegistry Registers RdfReasonerProvider instances; ServiceLoader discovery from META-INF/services/...RdfReasonerProvider.
ReasoningResult Original graph, inferred triples, optional classification, consistency, timings.

Providers shipped in this repo

  • Memory (MemoryReasonerProvider): RDFS rules rdfs2, rdfs3, rdfs5, rdfs7, rdfs9 and rdfs11 applied to a fixpoint (no axiomatic triples). Useful for tests and fast smoke checks; not full RDFS entailment or OWL.
  • Jena (JenaReasonerProvider): RDFS, OWL_MICRO (Jena’s OWL Micro rule reasoner — fast, incomplete OWL fragment), OWL_RL (Jena’s OWL rule reasoner — close to, but not a complete implementation of, OWL 2 RL) and CUSTOM rules (requires customRules). OWL_EL, OWL_QL and OWL_DL are rejected. This is the reference implementation for realistic rule-based entailment when Jena is on the classpath.
  • RDF4J (Rdf4jReasonerProvider): RDFS only.
  • HermiT (:rdf:reasoning-hermit): HERMIT and OWL_DL (OWL 2 DL).

Choosing a backend

At runtime, ReasonerRegistry.createReasoner(config) picks one registered provider that declares support for the requested ReasonerType. When several providers support the same type, the highest RdfReasonerProvider.priority() wins (HermiT 100, Jena 50, RDF4J 40, memory −100), so selection is deterministic for a given classpath. Callers that need specific semantics regardless of the classpath should still instantiate a concrete provider (as onto-quality does for v0.4; see Part 2).

Relation to SHACL validation

SHACL validation in Kastor operates on an RdfGraph. The SHACL engine does not automatically perform OWL/RDFS materialization; if rules depend on entailed triples (e.g. rdfs:subClassOf implying rdf:type for individuals), those triples must appear in the data graph passed to validation (or shapes must be written to match asserted data only).


Part 2 — onto-quality v0.4: reasoning before SHACL

Problem

Largely structural SHACL catalogs often assume explicit rdf:type arcs. RDFS (and light OWL) can entail additional type and property assertions. Without materialization, conformance can disagree with an editor or triple store that applies the same entailment regime.

Approach

  1. Optional expansion step: merge asserted triples with inferred triples from a configured profile.
  2. Run QualityChecker.check(graph, profile) so expansion and reasoning preflight (consistency) share one materialization pass where supported.
  3. Do not change shape semantics or pitfall catalogues; v0.4 is operational only.

API (module :tools:onto-quality)

API Meaning
OntoQualityReasoningProfile NONE, RDFS, OWL_MICRO (Jena OWL Micro rule reasoner, ReasonerType.OWL_MICRO; no equality reasoning), OWL_RL (Jena OWL rule reasoner, ReasonerType.OWL_RL; derives owl:sameAs from functional properties), HERMIT (OWL 2 DL via HermiT when module present). OWL_MICRO and OWL_RL are distinct profiles.
OntoQualityReasoning.expand expand(graph, profile) → materialized RdfGraph.
QualityChecker.check(graph, profile) Expand (single reason() per profile), run SHACL, merge OWL inconsistency rows (HermiT) as violations when reported.
OntoQualityReasoning.expand(graph, ReasonerConfig) Advanced: custom timeouts/rules when you depend on :rdf:reasoning.

Why Jena is fixed for this step

onto-quality already validates with the Jena-backed SHACL stack. v0.4 uses JenaReasonerProvider directly so that:

  • RDFS materialization includes the usual subclass-based instance typing (not only transitivity on class axioms).
  • Results are aligned with common Jena+RDFS expectations.
  • Behavior does not depend on which provider ReasonerRegistry happens to return first.

CLI (onto-qa)

On check and pipeline:

  • --reasoner none (default) — no expansion.
  • --reasoner rdfs — RDFS materialization before SHACL.
  • --reasoner owl-micro — Jena OWL Micro rule reasoner (ReasonerType.OWL_MICRO binding; fast, incomplete OWL fragment, no equality reasoning).
  • --reasoner owl-rl — Jena OWL rule reasoner (ReasonerType.OWL_RL binding; not complete OWL 2 RL).

owl-micro is a separate profile, not an alias of owl-rl. For example, if ex:hasMother is an owl:FunctionalProperty and ex:alice ex:hasMother ex:carol , ex:caroline, then owl-rl derives ex:carol owl:sameAs ex:caroline. owl-micro does not, because its rule set has no functional-property or owl:sameAs rules.

  • --reasoner hermit — HermiT-backed OWL 2 DL materialization (requires :rdf:reasoning-hermit); inconsistent ontologies fail quality checks via merged K07 rows when pitfall metadata is present (included in QualityChecker.default() / CLI --catalog all).

When a non-none profile is selected, the CLI logs the active reasoning profile name.

Limitations and non-goals

References