rdfs:range, rdfs:domain, rdfs:subClassOf, rdfs:subPropertyOf
and the addition of these lightweight but useful OWL rules:
owl:inversOf, owl:TransitiveProperty, owl:sameAs
The code uses Jena GenericRuleReasonser. This example was done with Jena 2.6.2.
Here is the code to the generic inference (ginfer) program:
$ type ginfer.java
import com.hp.hpl.jena.rdf.model.*;
import com.hp.hpl.jena.reasoner.*;
import com.hp.hpl.jena.vocabulary.*;
import com.hp.hpl.jena.reasoner.rulesys.*;
/** Read RDF XML from standard in; infer and write to standard out. */
class ginfer {
public static void main (String args[]) {
// Create an empty model.
Model model = ModelFactory.createDefaultModel();
// Read the RDF/XML on standard in.
model.read(System.in, null);
// Create a simple RDFS++ Reasoner.
StringBuilder sb = new StringBuilder();
sb.append("[rdfs2: (?x ?p ?y), (?p rdfs:domain ?c) -> (?x rdf:type ?c)] ");
sb.append("[rdfs3: (?x ?p ?y), (?p rdfs:range ?c) -> (?y rdf:type ?c)] ");
sb.append("[rdfs6: (?a ?p ?b), (?p rdfs:subPropertyOf ?q) -> (?a ?q ?b)] ");
sb.append("[rdfs5: (?x rdfs:subPropertyOf ?y), (?y rdfs:subPropertyOf ?z) -> (?x rdfs:subPropertyOf ?z)] ");
sb.append("[rdfs9: (?x rdfs:subClassOf ?y), (?a rdf:type ?x) -> (?a rdf:type ?y)] ");
sb.append("[rdfs11: (?x rdfs:subClassOf ?y), (?y rdfs:subClassOf ?z) -> (?x rdfs:subClassOf ?z)] ");
sb.append("[owlinv: (?x ?p ?y), (?p owl:inverseOf ?q) -> (?y ?q ?x)] ");
sb.append("[owlinv2: (?p owl:inverseOf ?q) -> (?q owl:inverseOf ?p)] ");
sb.append("[owltra: (?x ?p ?y), (?y ?p ?z), (?p rdf:type owl:TransitiveProperty) -> (?x ?p ?z)] ");
sb.append("[owlsam: (?x ?p ?y), (?x owl:sameAs ?z) -> (?z ?p ?y)] ");
sb.append("[owlsam2: (?x owl:sameAs ?y) -> (?y owl:sameAs ?x)] ");
Reasoner reasoner = new GenericRuleReasoner(Rule.parseRules(sb.toString()));
// Create inferred model using the reasoner and write it out.
InfModel inf = ModelFactory.createInfModel(reasoner, model);
inf.write(System.out);
}
}
Here is some data for demonstration.
$ type data.ttl
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix demo: <http://example.com/demo#> .
demo:Person a owl:Class.
demo:hasAncestor rdfs:range demo:Person ; rdfs:domain demo:Person .
demo:parentOf rdfs:subPropertyOf demo:ancestorOf ; owl:inverseOf demo:childOf .
demo:ancestorOf owl:inverseOf demo:hasAncestor ; a owl:TransitiveProperty .
demo:Trilby demo:parentOf demo:MarkB .
demo:Mark demo:parentOf demo:Elizabeth .
demo:MarkB owl:sameAs demo:Mark .
Here we use the jena.rdfcat program to convert the before and after reasoning data to a sorted N-Triples format so we can compare the two.
$ java jena.rdfcat -out ntriples data.ttl | sort >before.nt
$ java jena.rdfcat data.ttl | java ginfer | java jena.rdfcat -out ntriples -x - | sort >after.nt
And here is the comparison. Everything shown is a triple that was not in the original data, but was inferred by executing the rules.
$ diff before.nt after.nt
2a3,9
> <http://example.com/demo#childOf> <http://www.w3.org/2002/07/owl#inverseOf> <http://example.com/demo#parentOf> .
> <http://example.com/demo#Elizabeth> <http://example.com/demo#childOf> <http://example.com/demo#Mark> .
> <http://example.com/demo#Elizabeth> <http://example.com/demo#childOf> <http://example.com/demo#MarkB> .
> <http://example.com/demo#Elizabeth> <http://example.com/demo#hasAncestor> <http://example.com/demo#Mark> .
> <http://example.com/demo#Elizabeth> <http://example.com/demo#hasAncestor> <http://example.com/demo#MarkB> .
> <http://example.com/demo#Elizabeth> <http://example.com/demo#hasAncestor> <http://example.com/demo#Trilby> .
> <http://example.com/demo#Elizabeth> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.com/demo#Person> .
4a12,15
> <http://example.com/demo#hasAncestor> <http://www.w3.org/2002/07/owl#inverseOf> <http://example.com/demo#ancestorOf> .
> <http://example.com/demo#Mark> <http://example.com/demo#ancestorOf> <http://example.com/demo#Elizabeth> .
> <http://example.com/demo#Mark> <http://example.com/demo#childOf> <http://example.com/demo#Trilby> .
> <http://example.com/demo#Mark> <http://example.com/demo#hasAncestor> <http://example.com/demo#Trilby> .
5a17,24
> <http://example.com/demo#Mark> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.com/demo#Person> .
> <http://example.com/demo#Mark> <http://www.w3.org/2002/07/owl#sameAs> <http://example.com/demo#Mark> .
> <http://example.com/demo#Mark> <http://www.w3.org/2002/07/owl#sameAs> <http://example.com/demo#MarkB> .
> <http://example.com/demo#MarkB> <http://example.com/demo#ancestorOf> <http://example.com/demo#Elizabeth> .
> <http://example.com/demo#MarkB> <http://example.com/demo#childOf> <http://example.com/demo#Trilby> .
> <http://example.com/demo#MarkB> <http://example.com/demo#hasAncestor> <http://example.com/demo#Trilby> .
> <http://example.com/demo#MarkB> <http://example.com/demo#parentOf> <http://example.com/demo#Elizabeth> .
> <http://example.com/demo#MarkB> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.com/demo#Person> .
6a26
> <http://example.com/demo#MarkB> <http://www.w3.org/2002/07/owl#sameAs> <http://example.com/demo#MarkB> .
9a30,33
> <http://example.com/demo#Trilby> <http://example.com/demo#ancestorOf> <http://example.com/demo#Elizabeth> .
> <http://example.com/demo#Trilby> <http://example.com/demo#ancestorOf> <http://example.com/demo#Mark> .
> <http://example.com/demo#Trilby> <http://example.com/demo#ancestorOf> <http://example.com/demo#MarkB> .
> <http://example.com/demo#Trilby> <http://example.com/demo#parentOf> <http://example.com/demo#Mark> .
10a35
> <http://example.com/demo#Trilby> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.com/demo#Person> .
$