Skip to content

Commit

Permalink
Fix OWL-EL-from-RDF example
Browse files Browse the repository at this point in the history
  • Loading branch information
monsterkrampe committed May 14, 2024
1 parent 5962860 commit 28b40e9
Show file tree
Hide file tree
Showing 2 changed files with 172 additions and 85 deletions.
75 changes: 73 additions & 2 deletions examples/owl-el/from-owl-rdf/owl-rdf-complete-reasoning.rls
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,23 @@
%%% nf:subClassOf(?C,?D): ?C is syntactically specified to be a subclass of ?D
%%% nf:subPropChain(?S1,?S2,?R): there was a role chain axiom ?S1 o ?S2 -> ?R
%%% nf:subProp(?R,?S): ?R is a subproperty of ?S (directly or indirectly)
%%%
%%% Also, this program implements EL reasoning based on the (optimized) encoding described here:
%%% https://link.springer.com/article/10.1007/s10817-013-9296-3
%%% The description of rules at the bottom reference the names from Figure 3.
%%%
%%% The reasoning is applied to the normalised ontologies.

@prefix nf: <http://rulewerk.semantic-web.org/normalForm/> .
@prefix inf: <http://rulewerk.semantic-web.org/inferred/> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix sct: <http://www.ihtsdo.org/owlname#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix genid: <https://rulewerk.semantic-web.org/.well-known/genid/> .

@import TRIPLE :- rdf { resource = "../data/el.ttl" } .
@import TRIPLE :- rdf { resource = "galen-el.nt.gz" } .

%%% Mark classes:
ClassObject(owl:someValuesFrom) .
Expand Down Expand Up @@ -93,4 +100,68 @@ nf:isSubClass(?C1), nf:isSubClass(?C2) :- nf:conj(?X, ?C1, ?C2), nf:isSubClass(?
directSubProp(?R,?S) :- TRIPLE(?R,rdfs:subPropertyOf,?S) .
% Initialise role hierarchy only for roles in subclass positions:
nf:subProp(?P,?P) :- nf:exists(?C,?P,?D), nf:isSubClass(?C) .
nf:subProp(?R,?T) :- nf:subProp(?R,?S), directSubProp(?S,?T) .
nf:subProp(?R,?T) :- nf:subProp(?R,?S), directSubProp(?S,?T) .

%%% Inference rules

% Start classification for all named classes
inf:init(?C) :- nf:isMainClass(?C) .
% R_init
inf:init(?C) :- inf:ex(?E, ?R, ?C) .

% R_0: Every class is a sub class of itself
inf:subClassOf(?C, ?C) :- inf:init(?C) .
% R_\top: Every class is a sub class of owl:Thing
inf:subClassOf(?C, "<http://www.w3.org/2002/07/owl#Thing>") :- nf:isMainClass(?C) .

% R_\sqcap^-:
% If ?C is contained in the intersection of ?D1 and ?D1,
% then ?C is contained in ?D1 and ?C is contained in ?D2.
inf:subClassOf(?C,?D1), inf:subClassOf(?C,?D2) :- inf:subClassOf(?C,?Y), nf:conj(?Y,?D1,?D2) .

% R_\sqcap^+:
% If ?C is contained in ?D1 and ?D2,
% then ?C is contained in the intersection ?I of ?D1 ?D2
inf:subClassOf(?C, ?I) :-
inf:subClassOf(?C, ?D1), inf:subClassOf(?C, ?D2),
nf:conj(?I, ?D1, ?D2), nf:isSubClass(?I) .

% R_\exists^-:
% If every ?Y has an R-relation to a ?C
% and every ?E is a ?Y,
% then every ?E has an ?R-relation to a ?C
inf:ex(?E, ?R, ?C) :- inf:subClassOf(?E, ?Y), nf:exists(?Y, ?R, ?C) .

% R_\exists^+:
% If every ?E has an ?R-relation to a ?C,
% and every ?C is a ?D,
% and ?R is a subproperty of ?S,
% then every ?E has an ?S-relation to a ?D
% (i.e. every ?E is an ?Y where ?Y is the class of
% of individuals that have an ?S-relation to a ?D)
inf:subClassOf(?E, ?Y) :-
inf:ex(?E, ?R, ?C), inf:subClassOf(?C, ?D), nf:subProp(?R, ?S),
nf:exists(?Y, ?S, ?D), nf:isSubClass(?Y) .

% R_\circ:
% If ?E has an ?R1-relation to a ?C and ?C has an ?R2-relation to a ?D,
% and ?R1 is a subproperty of ?S1 and ?R2 is a subproperty of ?S2
% then ?E has an ?S-relation to a ?D
inf:ex(?E, ?S, ?D) :-
inf:ex(?E, ?R1, ?C), inf:ex(?C, ?R2, ?D),
nf:subProp(?R1, ?S1), nf:subProp(?R2, ?S2),
nf:subPropChain(?S1, ?S2, ?S) .

% R_\sqsubseteq: Transitive closure of the subclass-of relation
inf:subClassOf(?C,?E) :- inf:subClassOf(?C,?D), nf:subClassOf(?D,?E) .

% R_\bot: If every ?E has an R-relation to a ?C but ?C is empty, then ?E is also empty
inf:subClassOf(?E, "<http://www.w3.org/2002/07/owl#Nothing>") :-
inf:ex(?E,?R,?C), inf:subClassOf(?C,"<http://www.w3.org/2002/07/owl#Nothing>") .

%%% Extract final results for main classes

mainSubClassOf(?A,?B) :-
inf:subClassOf(?A,?B), nf:isMainClass(?A), nf:isMainClass(?B) .

@export mainSubClassOf :- csv{compression="gzip"} .
182 changes: 99 additions & 83 deletions examples/owl-el/from-owl-rdf/owl-rdf-preprocessing.rls
Original file line number Diff line number Diff line change
@@ -1,89 +1,105 @@
%%% Implements EL reasoning based on the (optimized) encoding described here:
%%% https://link.springer.com/article/10.1007/s10817-013-9296-3
%%% The description of rules at the bottom reference the names from Figure 3.
%%% This ruleset normalises an EL ontology in OWL/RDF encoding.
%%% Unsupported OWL EL features include: oneOf, allDisjoint.
%%% The encoding used for property chains is also slightly antique.
%%%
%%% The ruleset computes facts for the following predicates:
%%% nf:isMainClass(?C): ?C is an "interesting" class (not just an auxiliary class expression)
%%% nf:isSubClass(?C): ?C occurs in a subclass position (i.e., negatively)
%%% nf:conj(?C,?D1,?D2): ?C is the conjunction of ?D1 and ?D2
%%% nf:exists(?C,?P,?D): ?C is the existential restriction of property ?P with values from ?D
%%% nf:subClassOf(?C,?D): ?C is syntactically specified to be a subclass of ?D
%%% nf:subPropChain(?S1,?S2,?R): there was a role chain axiom ?S1 o ?S2 -> ?R
%%% nf:subProp(?R,?S): ?R is a subproperty of ?S (directly or indirectly)

@prefix nf: <http://rulewerk.semantic-web.org/normalForm/> .
@prefix inf: <http://rulewerk.semantic-web.org/inferred/> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix sct: <http://www.ihtsdo.org/owlname#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix genid: <https://rulewerk.semantic-web.org/.well-known/genid/> .

%%% This file assumes a normalized owl ontology to be present

% nf:isMainClass(?C): ?C is an "interesting" class (not just an auxiliary class expression)
@import nf:isMainClass :- csv { resource = "../data/isMainClass.csv.gz" } .
% nf:isSubClass(?C): ?C occurs in a subclass position (i.e., negatively)
@import nf:isSubClass :- csv { resource = "../data/isSubClass.csv.gz" } .
% nf:conj(?C, ?D1, ?D1): ?C is the conjunction of ?D1 and ?D2
@import nf:conj :- csv { resource = "../data/conj.csv.gz" } .
% nf:exists(?E, ?R, ?C): Every ?E has an ?R-relation to a ?C
@import nf:exists :- csv { resource = "../data/exists.csv.gz" } .
% nf:subClassOf(?C, ?D): Every member of ?C is a member of ?D
@import nf:subClassOf :- csv { resource = "../data/subClassOf.csv.gz" } .
% nf:subPropChain(?S1, ?S2, ?S): Roles ?S1 composed with ?S2 are a subproperty of the role ?S
@import nf:subPropChain :- csv { resource = "../data/subPropChain.csv.gz" } .
% nf:subProp(?R, ?S): Role ?R is a subproperty of the role ?S
@import nf:subProp :- csv { resource = "../data/subProp.csv.gz" } .

%%% Inference rules

% Start classification for all named classes
inf:init(?C) :- nf:isMainClass(?C) .
% R_init
inf:init(?C) :- inf:ex(?E, ?R, ?C) .

% R_0: Every class is a sub class of itself
inf:subClassOf(?C, ?C) :- inf:init(?C) .
% R_\top: Every class is a sub class of owl:Thing
inf:subClassOf(?C, "<http://www.w3.org/2002/07/owl#Thing>") :- nf:isMainClass(?C) .

% R_\sqcap^-:
% If ?C is contained in the intersection of ?D1 and ?D1,
% then ?C is contained in ?D1 and ?C is contained in ?D2.
inf:subClassOf(?C,?D1), inf:subClassOf(?C,?D2) :- inf:subClassOf(?C,?Y), nf:conj(?Y,?D1,?D2) .

% R_\sqcap^+:
% If ?C is contained in ?D1 and ?D2,
% then ?C is contained in the intersection ?I of ?D1 ?D2
inf:subClassOf(?C, ?I) :-
inf:subClassOf(?C, ?D1), inf:subClassOf(?C, ?D2),
nf:conj(?I, ?D1, ?D2), nf:isSubClass(?I) .

% R_\exists^-:
% If every ?Y has an R-relation to a ?C
% and every ?E is a ?Y,
% then every ?E has an ?R-relation to a ?C
inf:ex(?E, ?R, ?C) :- inf:subClassOf(?E, ?Y), nf:exists(?Y, ?R, ?C) .

% R_\exists^+:
% If every ?E has an ?R-relation to a ?C,
% and every ?C is a ?D,
% and ?R is a subproperty of ?S,
% then every ?E has an ?S-relation to a ?D
% (i.e. every ?E is an ?Y where ?Y is the class of
% of individuals that have an ?S-relation to a ?D)
inf:subClassOf(?E, ?Y) :-
inf:ex(?E, ?R, ?C), inf:subClassOf(?C, ?D), nf:subProp(?R, ?S),
nf:exists(?Y, ?S, ?D), nf:isSubClass(?Y) .

% R_\circ:
% If ?E has an ?R1-relation to a ?C and ?C has an ?R2-relation to a ?D,
% and ?R1 is a subproperty of ?S1 and ?R2 is a subproperty of ?S2
% then ?E has an ?S-relation to a ?D
inf:ex(?E, ?S, ?D) :-
inf:ex(?E, ?R1, ?C), inf:ex(?C, ?R2, ?D),
nf:subProp(?R1, ?S1), nf:subProp(?R2, ?S2),
nf:subPropChain(?S1, ?S2, ?S) .

% R_\sqsubseteq: Transitive closure of the subclass-of relation
inf:subClassOf(?C,?E) :- inf:subClassOf(?C,?D), nf:subClassOf(?D,?E) .

% R_\bot: If every ?E has an R-relation to a ?C but ?C is empty, then ?E is also empty
inf:subClassOf(?E, "<http://www.w3.org/2002/07/owl#Nothing>") :-
inf:ex(?E,?R,?C), inf:subClassOf(?C,"<http://www.w3.org/2002/07/owl#Nothing>") .

%%% Extract final results for main classes

mainSubClassOf(?A,?B) :-
inf:subClassOf(?A,?B), nf:isMainClass(?A), nf:isMainClass(?B) .
@import TRIPLE :- rdf { resource = "galen-el.nt.gz" } .

%%% Mark classes:
ClassObject(owl:someValuesFrom) .
ClassObject(rdf:first) .
ClassObject(rdfs:subClassOf) .
ClassObject(owl:equivalentClass) .
ClassSubject(rdfs:subClassOf) .
ClassSubject(owl:equivalentClass) .
class(?O) :- TRIPLE(?X, ?P, ?O), ClassObject(?P) .
class(?X) :- TRIPLE(?X, ?P, ?O), ClassSubject(?P) .

%%% Distinguish auxiliary class expressions from primary classes:

% Mark auxiliary existential role restrictions:
synEx(?Y,?P,?X), auxClass(?X) :- TRIPLE(?X, owl:someValuesFrom, ?Y), TRIPLE(?X, owl:onProperty, ?P) .

%%% Mark auxiliary conjunctions:

next(?L1,?L2) :- TRIPLE(?L1,rdf:rest,?L2) .
first(?L1) :- TRIPLE(?X, owl:intersectionOf, ?L1) .
nonfirst(?L2) :- first(?L1), next(?L1,?L2) .
nonfirst(?L2) :- nonfirst(?L1), next(?L1,?L2) .
last(?Ln) :- next(?Ln,rdf:nil) .
nonlast(?L) :- next(?L,?Ln), last(?Ln) .
nonlast(?L1) :- next(?L1,?L2), nonlast(?L2) .
in(?L,?C) :- TRIPLE(?L,rdf:first,?C) .

%%% Mark conjunctions:
synConj(?X,?C1,?C2), auxClass(?X) :-
TRIPLE(?X, owl:intersectionOf, ?L1), next(?L1,?L2), last(?L2), in(?L1,?C1), in(?L2,?C2) .
synConj(?X,?C1,?L2), auxClass(?X) :-
TRIPLE(?X, owl:intersectionOf, ?L1), next(?L1,?L2), nonlast(?L2), in(?L1,?C1) .
synConj(?L1,?C1,?L2), auxClass(?L1) :-
nonfirst(?L1), next(?L1,?L2), nonlast(?L2), in(?L1,?C1) .
synConj(?L1,?C1,?C2), auxClass(?L1) :-
nonfirst(?L1), next(?L1,?L2), last(?L2), in(?L1,?C1), in(?L2,?C2) .

%%% The other classes are "main classes" that are not normalised:
nf:isMainClass(?X) :- class(?X), ~auxClass(?X) .

%%% Normalise auxiliary nested class expressions:
repOf(?X,?X) :- nf:isMainClass(?X) . % keep main classes unchanged
synExRep(?X,?P,?Rep) :- synEx(?Y,?P,?X), repOf(?Y,?Rep) .
nf:exists(!New,?P,?Rep) :- synExRep(?X,?P,?Rep) .
repOf(?X,?N) :- synExRep(?X,?P,?Rep), nf:exists(?N,?P,?Rep) .
% nf:exists(!New,?P,?Rep) :- synEx(?Y,?P,?X), repOf(?Y,?Rep) .
% repOf(?X,?N) :- synEx(?Y,?P), repOf(?Y,?Rep), nf:exists(?N,?P,?Rep) .
nf:conj(!New,?R1,?R2) :- synConj(?X,?C1,?C2), repOf(?C1,?R1), repOf(?C2,?R2) .
repOf(?X,?N) :- synConj(?X,?C1,?C2), repOf(?C1,?R1), repOf(?C2,?R2), nf:conj(?N,?R1,?R2) .


%%% Extract old-style property chains:
nf:subPropChain(?S,?T,?R), nf:subProp(?R,?R) :-
TRIPLE(?L,rdfs:subPropertyOf,?R), TRIPLE(?L,owl:propertyChain,?L1),
in(?L1,?S), next(?L1,?L2), in(?L2,?T) .

%%% Initialise subsumption axioms:
prepareSco(?X,?Y) :- TRIPLE(?X, rdfs:subClassOf, ?Y) .
prepareSco(?X,?Y), prepareSco(?Y,?X) :- TRIPLE(?X, owl:equivalentClass, ?Y) .
nf:subClassOf(?RX,?RY), nf:isSubClass(?RX) :- prepareSco(?X,?Y), repOf(?X,?RX), repOf(?Y,?RY) .

%%% Initialise disjointness:
nf:subClassOf(!C,owl:Nothing), nf:conj(!C,?X,?Y), nf:isSubClass(!C), nf:isSubClass(?X), nf:isSubClass(?Y)
:- TRIPLE(?X,owl:disjointWith,?Y) .

%%% Mark classes in subclass position recursively:
nf:isSubClass(?D) :- nf:exists(?C, ?P, ?D), nf:isSubClass(?C) .
nf:isSubClass(?C1), nf:isSubClass(?C2) :- nf:conj(?X, ?C1, ?C2), nf:isSubClass(?X) .

%%% Precompute role hierarchy:
directSubProp(?R,?S) :- TRIPLE(?R,rdfs:subPropertyOf,?S) .
% Initialise role hierarchy only for roles in subclass positions:
nf:subProp(?P,?P) :- nf:exists(?C,?P,?D), nf:isSubClass(?C) .
nf:subProp(?R,?T) :- nf:subProp(?R,?S), directSubProp(?S,?T) .

@export nf:isMainClass :- csv{format=(any), compression="gzip", resource="isMainClass.csv.gz"} .
@export nf:isSubClass :- csv{format=(any), compression="gzip", resource="isSubClass.csv.gz"} .
@export nf:conj :- csv{format=(any, any, any), compression="gzip", resource="conj.csv.gz"} .
@export nf:exists :- csv{format=(any, any, any), compression="gzip", resource="exists.csv.gz"} .
@export nf:subClassOf :- csv{format=(any, any), compression="gzip", resource="subClassOf.csv.gz"} .
@export nf:subPropChain :- csv{format=(any, any, any), compression="gzip", resource="subPropChain.csv.gz"} .
@export nf:subProp :- csv{format=(any, any), compression="gzip", resource="subProp.csv.gz"} .

0 comments on commit 28b40e9

Please sign in to comment.