Skip to content

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_graph and the digests cover asserted triples only.
  • Planner statistics are refreshed automatically after each run.

How you run it ​

sql
pgrdf.materialize(graph_id BIGINT, profile TEXT DEFAULT 'owl-rl') → JSONB

The profile selects the rules:

  • 'owl-rl' (default): OWL 2 RL forward chaining. It also adds a few axiomatic triples, such as typing every resource as owl: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 ​

sql
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 axiomatic

With '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:

sql
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 | current

The 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 ​

pgRDF is released under the MIT license. Documentation built with VitePress, served via GitHub Pages.