psychologyReason
Work out what follows from a graph (subclass, subproperty, equivalence, inverse, transitive relations) and store it as queryable triples. Reason runs in one session over one graph, so the graph has to fit your hardware.
What it is
Reason computes the entailment closure of a graph under a chosen profile and stores every inferred triple in the same graph, flagged as inferred. After Reason, Query and Validate see asserted and inferred triples together.
- Re-running replaces. Each run drops the previous inferred triples and derives them again, so running it twice is safe.
- Inferred triples stay out of exports and digests.
export_graphand the digests cover asserted triples only. - Planner statistics are refreshed automatically after each run.
How you run it
pgrdf.materialize(graph_id BIGINT, profile TEXT DEFAULT 'owl-rl') → JSONBThe profile selects the rules:
'owl-rl'(default): OWL 2 RL forward chaining. It also adds a few axiomatic triples, such as typing every resource asowl:Thing.'rdfs': the RDFS rules only.
Any other profile refuses with SQLSTATE 22023: materialize: unknown profile "owl-dl" (supported: 'owl-rl', 'rdfs'). A locked graph refuses with 55P03. See Pillar 3 — Materialization for the rule set.
The result is a JSON report: profile, base_triples, inferred_triples_written, previous_inferred_dropped, reasoner_errors, auto_analyzed, and timings (elapsed_ms, load_ms, reason_ms, diff_ms, write_ms, analyze_ms).
Example
SELECT pgrdf.add_graph('http://example.org/org');
SELECT pgrdf.parse_turtle('
@prefix ex: <http://example.com/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
ex:alice a ex:Engineer .
ex:Engineer rdfs:subClassOf ex:Person .
ex:Person rdfs:subClassOf ex:Agent .
', pgrdf.graph_id('http://example.org/org'));
SELECT pgrdf.materialize(pgrdf.graph_id('http://example.org/org'));
-- → {"profile": "owl-rl", "base_triples": 3, "inferred_triples_written": 10,
-- "previous_inferred_dropped": 0, "reasoner_errors": [], "auto_analyzed": true,
-- "elapsed_ms": 1.16829, ...}
SELECT * FROM pgrdf.sparql(
'PREFIX ex: <http://example.com/>
SELECT ?type WHERE {
GRAPH <http://example.org/org> { ex:alice a ?type }
} ORDER BY ?type');
-- {"type": "http://example.com/Agent"} ← inferred, two steps up
-- {"type": "http://example.com/Engineer"} ← asserted
-- {"type": "http://example.com/Person"} ← inferred
-- {"type": "http://www.w3.org/2002/07/owl#Thing"} ← OWL 2 RL axiomaticWith 'rdfs' instead, the same graph gets 3 inferred triples and alice's types are Agent, Engineer and Person, without owl:Thing.
Is the materialization current?
graph_inventory() records whether a graph's inferred triples still match its asserted ones:
SELECT iri, asserted, inferred, materialization
FROM pgrdf.graph_inventory() WHERE iri = 'http://example.org/org';
-- iri | asserted | inferred | materialization
-- ------------------------+----------+----------+-----------------
-- http://example.org/org | 3 | 3 | currentThe values are never, current, stale (the asserted count has changed since the last run: run materialize again) and unknown (inferred triples with no run on record, for example after a copy or a carve). See Is the materialization current? for the details.
Where it sits in a chain
After Import, or after Carve; before Validate, Query and Seal. Reason is the verb that decides whether you need to carve. If a graph is too large to reason over in one session, carve a right-sized slice and reason over that.
Scaling class — one graph, one session
The reasoner runs on a single thread in one database session. You don't reason over a multi-billion-triple source on ordinary hardware; you reason over a graph sized to your machine. See Reasoning at scale.
See also
- Pillar 3 — Materialization — the OWL 2 RL and RDFS rule sets.
- Pattern: Load → Reason → Query — a worked walkthrough.
- Carve — right-size a graph before reasoning.