Reasoning is easy when you're only adding data. The hard part comes when you delete something.
Consider this scenario:
# Ontology :Dog rdfs:subClassOf :Animal . # Data :rex a :Dog . # Inferred (by reasoner) :rex a :Animal .
Now you delete the triple :rex a :Dog. What happens to the inferred triple :rex a :Animal?
:rex a :Dog
:rex a :Animal
This is the retraction problem — knowing which inferences to retract when their supporting evidence is removed. Each triplestore handles it differently, and it's one of the most important architectural decisions in reasoning systems.
These are some of the most common approaches — not an exhaustive list — illustrated by engines that document them well.
How it works: Delete ALL inferred triples, then re-run the entire reasoning process from scratch.
Before deletion: Base triples: 1,000,000 Inferred triples: 3,000,000 Total: 4,000,000 After deleting 1 triple: 1. Delete all 3,000,000 inferred triples 2. Re-run forward-chaining on 999,999 base triples 3. Materialize ~2,999,997 inferred triples 4. Total time: Same as initial reasoning
After the recomputation, the materialization is exactly what the remaining base triples derive — retraction is achieved by reconstruction.
GraphDB handles "the deletion of explicit statements and their inferences" with a technique it calls smooth delete.
Its documentation describes the algorithm in three steps:
So retraction is incremental — only the inferences that are "no longer supported" get removed.
Source: GraphDB documentation — Delete optimizations.
RDFox "uses materialization-based reasoning to precompute and store all triples that logically follow from the input graph and rules in a query-independent way." The challenge their documentation describes: "whenever data triples and/or rules are added and/or deleted, the 'old' materialization must be replaced with the 'new' materialization that contains all triples that follow from the updated input."
Rather than recomputing from scratch, RDFox implements algorithms for "maintaining [materializations] under addition/deletion updates that may affect both the data and the rules."
One scope note from their docs: "deletion of triples is restricted to those that are explicit in the input graph and hence one does not consider deletion of derived triples — a complex problem known in the literature as belief revision or view update."
A classic algorithm from that research literature for this problem is DRed (Delete/Rederive) — explained in detail at the end of this guide.
Source: RDFox documentation — Reasoning.
In Stardog's words: "Stardog performs reasoning in a lazy and late-binding fashion: it does not materialize inferences; rather, reasoning is performed at query time." Mechanically, "Stardog rewrites the user's query with respect to a schema, and then executes the resulting expanded query against the data in the normal way."
-- User writes: SELECT ?x WHERE { ?x a :Animal } -- Rewritten with respect to the schema: SELECT ?x WHERE { { ?x a :Animal } UNION { ?x a :Dog } -- because :Dog rdfs:subClassOf :Animal UNION { ?x a :Pet } -- because :Pet rdfs:subClassOf :Animal }
The consequence for retraction, per their docs: "inferences are visible in query results, but they are not explicitly stored within the Stardog database" — so when data changes, there is nothing materialized to retract.
Source: Stardog documentation — Inference Engine.
RDF Studio's built-in store ships with a native Rust OWL 2 RL engine that maintains its materialization incrementally for instance data:
Note: RDF Studio is also a workbench over triplestores — when connected to GraphDB, RDFox, or another SPARQL endpoint, the connected store's own retraction strategy applies, exactly as described above. The built-in engine is designed for modeling and moderate-sized datasets, not for competing with dedicated reasoning engines at scale.
For those who want to understand the internals:
Dependency Graph: inferred_triple → Set[Set[base_triple]] Each inferred triple has one or more "support sets" Each support set is a minimal set of base triples that justify the inference An inferred triple is valid if ANY of its support sets is complete
function delete(triple): remove triple from base_triples // Phase 1: Over-delete affected = { t ∈ inferred | triple ∈ any_support_set(t) } for each t in affected: remove t from inferred_triples // Cascade: t may support other inferences affected += { u ∈ inferred | t ∈ any_support_set(u) } // Phase 2: Rederive for each t in affected (in dependency order): if can_derive(t, base_triples ∪ inferred_triples): restore t to inferred_triples update support_sets(t)
Consider:
A → B (rule 1) A → C (rule 2) B ∧ C → D (rule 3)
If A is deleted:
But what if B was also supported by another base fact E?
Without the over-delete phase, you might miss cascading effects and leave stale inferences behind.