-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
5962860
commit 28b40e9
Showing
2 changed files
with
172 additions
and
85 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"} . | ||
|