Pyling notebook 02
From one rule to a reusable OWL profile¶
The first notebook encoded one implication close to the application. That approach stops scaling when a domain uses OWL terms such as rdfs:domain, owl:inverseOf, or owl:TransitiveProperty: each construct brings its own semantics, and those semantics interact.
Here we load the complete OWL 2 RL/RDF ruleset maintained for Eyeling. Our Python code will describe only a small research graph. The maintained N3 profile supplies the reusable meaning.
This is forward materialization: we compute useful consequences ahead of later queries.
1. Load the semantics as data¶
Keeping the profile at its published URL makes an important architectural point: rule knowledge can be versioned and reused independently of application code. The notebook shows exactly which artifact it executes.
from urllib.request import Request, urlopen
from rdflib import Namespace, RDF, RDFS, OWL
from pyling import reason_stream
OWL2RL_URL = "https://raw.githubusercontent.com/pietercolpaert/rdfjs-inference-engine/refs/heads/main/rules/owl2rl/owl2rl-eyeling.n3"
def load_text(url):
request = Request(url, headers={"User-Agent": "pyling-notebook"})
with urlopen(request, timeout=30) as response:
return response.read().decode("utf-8")
owl2rl_rules = load_text(OWL2RL_URL)
print(f"Loaded {len(owl2rl_rules.splitlines()):,} lines of OWL 2 RL N3 rules")
Loaded 1,174 lines of OWL 2 RL N3 rules
2. Describe a small world with several semantic shortcuts¶
Alice authored a paper, collaborates with Bob, and is Bob's ancestor; Bob is Carol's ancestor. The ontology says enough for a reasoner to recover facts that were not repeated in the instance data:
- domain and range identify Alice and the paper;
- the class hierarchy carries Alice from
ResearchertoPersontoAgent; - an inverse property turns
authoredaround; - symmetric and transitive properties add the remaining relationships.
This graph is small enough to inspect, but it exercises several parts of the real profile at once.
data = """
@prefix : <http://example.org/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
:authored rdfs:domain :Researcher ;
rdfs:range :Paper ;
owl:inverseOf :hasAuthor .
:collaboratesWith a owl:SymmetricProperty .
:ancestorOf a owl:TransitiveProperty .
:Researcher rdfs:subClassOf :Person .
:Person rdfs:subClassOf :Agent .
:alice :authored :paper42 ;
:collaboratesWith :bob ;
:ancestorOf :bob .
:bob :ancestorOf :carol .
"""
3. Let the profile connect the shortcuts¶
The invocation is no more complicated than in notebook 01. Complexity belongs in the profile, not in a chain of Python conditionals.
result = reason_stream(
{"sources": [owl2rl_rules, data]},
include_input_facts_in_closure=True,
)
closure = result.as_rdflib_graph(include_input_facts=True)
print(f"Derived {len(result.derived)} triples")
Derived 58 triples
4. Ask for evidence, not just a triple count¶
Each assertion below represents a different entailment path. Treating them as executable expectations turns the notebook into both an explanation and a regression check.
EX = Namespace("http://example.org/")
expected = {
(EX.alice, RDF.type, EX.Researcher): "domain",
(EX.alice, RDF.type, EX.Person): "subclass",
(EX.alice, RDF.type, EX.Agent): "transitive subclass",
(EX.paper42, RDF.type, EX.Paper): "range",
(EX.paper42, EX.hasAuthor, EX.alice): "inverse property",
(EX.bob, EX.collaboratesWith, EX.alice): "symmetric property",
(EX.alice, EX.ancestorOf, EX.carol): "transitive property",
}
for triple, explanation in expected.items():
assert triple in closure, explanation
print(f"{explanation:<20} {triple[0].n3()} {triple[1].n3()} {triple[2].n3()}")
domain <http://example.org/alice> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Researcher> subclass <http://example.org/alice> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Person> transitive subclass <http://example.org/alice> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Agent> range <http://example.org/paper42> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Paper> inverse property <http://example.org/paper42> <http://example.org/hasAuthor> <http://example.org/alice> symmetric property <http://example.org/bob> <http://example.org/collaboratesWith> <http://example.org/alice> transitive property <http://example.org/alice> <http://example.org/ancestorOf> <http://example.org/carol>
What we gained, and what we did not¶
The application supplied a vocabulary and instance data; the downloaded profile supplied their operational semantics. This is OWL 2 RL forward materialization, not an OWL 2 DL tableau reasoner. The profile also represents detected inconsistencies as explicit resources so an application can inspect them.
Inference enriches trusted facts. It does not decide whether uncertain facts should have entered the graph in the first place. Notebook 03 moves to that boundary: facts extracted by a neural model, checked by deterministic rules.