Hi. I'm Mark Wallace. In my role as a an Ontologist and Software Architect, I am continually working with new and fun semantic technologies. Be it RDF/OWL, Triple-stores, Semantic Wikis, or Text Extraction, I am learning more all the time and want to share my experiences in hopes of helping others along with these technologies. I hope post a new article every month or two, so check back in every so often to see what’s cooking!

Wednesday, December 22, 2010

Custom Rules for Jena Reasoner

Here is an example of creating a custom RDFS++ reasoner using Jena 2.6.2. By RDFS++, I mean the following key rules in RDFS, which are:

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> .

$

Sunday, May 30, 2010

Speaking at SemTech 2010

I'll be speaking this year at SemTech 2010. The full list of presentations is available online.

My one-hour talk is Tuesday, June 22, at 5pm. It's part of the Ontology Design and Engineering track, in the Technical-Advanced category, and is entitled "Rapid Prototyping with the Jena Command Line Utilities".

It should be a fun talk that includes demonstrations of how to use the utilities for file conversion (see previous post), RDF file merging, SPARQL queries, and filtering triples. There is also some leave-behind code in the slides so you can write your own Jena inference utility.

I hope to see you there!

Monday, May 24, 2010

Using Jena to convert RDF/OWL file formats

As a way of getting started with this blog, let me show how to use the Jena Utilities to convert your RDF/OWL files from one format to another.

On the Web, most OWL and RDF content are formatted as as RDF/XML. This format works great for the web, and most OWL/RDF tools support this as the native format. However, it is less human-readable than other formats such as Turtle.

For me, Turtle is the easiest form to read, and the quickest format for generating test data when I am prototyping. But not all tools support Turtle format.

But that's OK! If you want to easily convert from RDF/XML to Turtle and back, you can use the freely available Jena utilities to do this. These are command line utilities, so you run them from a command window.

First: Download Jena. The latest version at the time of this writing is version 2.6.2. Unpack it anywhere - though I usually make sure the directory path has no spaces in it. E.g., let's say you unpack it on a Windows system under C:\Programs, making the Jena top level folder the C:\Programs\Jena-2.6.2 folder.

Second: Set up your Java classpath, e.g.:


set CLASSPATH=
set CLASSPATH=%CLASSPATH%;C:\Programs\Jena-2.6.2\lib\arq-extra.jar
set CLASSPATH=%CLASSPATH%;C:\Programs\Jena-2.6.2\lib\arq.jar
set CLASSPATH=%CLASSPATH%;C:\Programs\Jena-2.6.2\lib\icu4j_3_4.jar
set CLASSPATH=%CLASSPATH%;C:\Programs\Jena-2.6.2\lib\iri.jar
set CLASSPATH=%CLASSPATH%;C:\Programs\Jena-2.6.2\lib\jena.jar
set CLASSPATH=%CLASSPATH%;C:\Programs\Jena-2.6.2\lib\jenatest.jar
set CLASSPATH=%CLASSPATH%;C:\Programs\Jena-2.6.2\lib\json.jar
set CLASSPATH=%CLASSPATH%;C:\Programs\Jena-2.6.2\lib\junit-4.5.jar
set CLASSPATH=%CLASSPATH%;C:\Programs\Jena-2.6.2\lib\log4j-1.2.12.jar
set CLASSPATH=%CLASSPATH%;C:\Programs\Jena-2.6.2\lib\lucene-core-2.3.1.jar
set CLASSPATH=%CLASSPATH%;C:\Programs\Jena-2.6.2\lib\slf4j-api-1.5.6.jar
set CLASSPATH=%CLASSPATH%;C:\Programs\Jena-2.6.2\lib\slf4j-log4j12-1.5.6.jar
set CLASSPATH=%CLASSPATH%;C:\Programs\Jena-2.6.2\lib\stax-api-1.0.jar
set CLASSPATH=%CLASSPATH%;C:\Programs\Jena-2.6.2\lib\wstx-asl-3.0.0.jar
set CLASSPATH=%CLASSPATH%;C:\Programs\Jena-2.6.2\lib\xercesImpl.jar


Third: Convert stuff! E.g. I have the file vehicle.owl, with these contents:


<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:e="http://example.org/ex#"
xmlns:owl="http://www.w3.org/2002/07/owl#"
xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
<rdfs:Class rdf:about="http://example.org/ex#Vehicle"/>
<e:Civic rdf:about="http://example.org/ex#Civic_8717383">
<e:hasVin>1H0243098430987</e:hasVin>
</e:Civic>
<rdfs:Property rdf:about="http://example.org/ex#hasVin"/>
<rdf:Description rdf:about="http://example.org/ex#HondaCar">
<rdfs:subClassOf>
<rdf:Description rdf:about="http://example.org/ex#Car">
<rdfs:subClassOf rdf:resource="http://example.org/ex#Vehicle"/>
</rdf:Description>
</rdfs:subClassOf>
</rdf:Description>
<rdf:Description rdf:about="http://example.org/ex#Civic">
<rdfs:subClassOf rdf:resource="http://example.org/ex#HondaCar"/>
</rdf:Description>
</rdf:RDF>


To convert it, I use the Jena rdfcat program, as follows:

java jena.rdfcat -out ttl vehicle.owl


And it prints this this out:

@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix e: <http://example.org/ex#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .

e:Civic_8717383
a e:Civic ;
e:hasVin "1H0243098430987" .

e:Vehicle
a rdfs:Class .

e:HondaCar
rdfs:subClassOf e:Car .

e:hasVin
a rdfs:Property .

e:Car
rdfs:subClassOf e:Vehicle .

e:Civic
rdfs:subClassOf e:HondaCar .


I can redirect this to a file for safe keeping:

java jena.rdfcat -out ttl vehicle.owl > vehicle.ttl


Now I have my turtle file! I can view and edit it as I see fit. Then, if I need to publish back to RDF/XML format, I simply use the rdfcat utility again, but with no "-out" option:

java jena.rdfcat vehicle.ttl > vehicle-new.owl


That's it. If this was new to you, post a comment and let me know if it helped.

Wednesday, April 28, 2010

Hello, World!

Hello all you semantics-focused technology buffs out there!

I'm Mark Wallace, and I plan use this blog to write about different Semantic Applications I am working on.

In my role as Principal Engineer, Semantic Applications at Modus Operandi, I am continually working with new and fun semantic technologies. Be it RDF/OWL, Triple-stores, Semantic Wikis, or Text Extraction, I am learning more all the time and want to share my experiences in hopes of helping others along with these technologies.

I hope to be posting a new article about every week or two, so check back every so often and see what's cooking!

Followers