Working with Redland RDF and a DIG Reasoner

(by Lara Spendier, adaptions by rho)

RDF::Redland is the Perl wrapper around the Redland RDF libraries. With it you can read, write and query RDF graphs.

While Redland supports SPARQL to a large degree, there is no support for OWL, or any other kind of reasoning. This is where RDF::Redland::DIG kicks in: It is an extension to exchange information with a DIG reasoner, i.e. one which can be reached via the DIG protocol.

The following gives an overview over the API. To learn more you may want to consult the RDF::Redland::DIG manpages for the reasoner itself, or RDF::Redland::DIG::KB for the knowledge base hosted inside the reasoner.

Test Ontology

Before we can start with using the reasoning services, we have to create an RDF::Redland::Model to host the data. In the following, we create a simple test ontology inspired by the Protege pizza ontology.

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

Pizza            a    owl:Class.
PizzaTopping     a    owl:Class.

CheesePizza      a    owl:Class;
                 rdfs:subClassOf Pizza.
ProsciuttoPizza  a    owl:Class;
                 rdfs:subClassOf Pizza.

CheeseTopping    a    owl:Class;
                 rdfs:subClassOf PizzaTopping.
HamTopping       a    owl:Class;
                 rdfs:subClassOf PizzaTopping.
TomatoTopping    a    owl:Class;
                 rdfs:subclassOf PizzaTopping.
 
hasIngredient    a             owl:ObjectProperty;
                 owl:inverseOf isIngredientOf.
hasTopping       rdfs:subPropertyOf hasIngredient;
                 owl:inverseOf isToppingOf;
                 rdfs:domain   Pizza;
                 rdfs:range    PizzaTopping.
isIngredientOf   a             owl:ObjectProperty;
                 owl:inverseOf hasIngredient.
isToppingOf      rdfs:subPropertyOf isIngredientOf;
                 owl:inverseOf hasTopping;
                 rdfs:domain   PizzaTopping;
                 rdfs:range    Pizza.

It lists Pizza and PizzaToppings as top-level concepts. From these it creates a small class tree of pizzas and possible toppings. Also a small hierarchy of properties is added, together with the usual inverse variations.

Apart from the ontological information, we can also add some instance data to play with:

Margharita       a            CheesePizza;
                 hasTopping   MozarellaTopping.
MozarellaTopping a            CheeseTopping;
                 isToppingOf  Margharita.

The Model

If we assume that the above ontology and instance data is stored in an RDF file pizza.n3, the we can load this file into a model:

use RDF::Redland::Parser;
my $parser = new RDF::Redland::Parser (undef, "application/rdf+xml")
    or die "Failed to find parser";

use RDF::Redland::Storage;  
my $storage = new RDF::Redland::Storage (
                "hashes",
                "test",
                "new='yes', hash-type='bdb', dir='/where/ever/'")
    or die "Failed to create RDF::Redland::Storage";

use RDF::Redland::Model;
my $model   = new RDF::Redland::Model ($storage, "")
    or die "Failed to create RDF::Redland::Model for storage";
 
my $uri = new RDF::Redland::URI ("file:///where/ever/pizza.n3");
$parser->parse_into_model ($uri, $uri, $model);

This boilerplate code is pretty common. Again, you may want to consult a detailed step-by-step tutorial for RDF::Redland.

The Reasoner and KB Handles

A DIG reasoner is a separate process, popular choices being pellet, Fact++ (RacerPro would be a commercial alternative). As the communication occurs via HTTP, we have to specify the URL when we create a handle to the reasoner:

use RDF::Redland::DIG;  
my $digreasoner = new RDF::Redland::DIG ( "http://localhost:8081" )
                  or die "Failed to create RDF::Redland::DIG";

From then on we are able to create our own knowledge base. This can be easily done by requesting such from our $digreasoner:

my $kb = $digreasoner->kb;

This way, we can create as many knowledge bases as we need.

To fill our knowledge base $kb with data we tell it about our $model:

$kb->tell ($model) or die "Tell failed";

After having executed tell, our $model is stored in the knowledge base, we can start with the reasoning. In the following I will describe a few stereotypical methods.

Concept Retrieval

For pure concept retrieval there are three methods available. One of them is the function allRoleNames that returns all roles from our $model:

warn Dumper $kb->allRoleNames;

'hasIngredient',
'hasTopping',
'isIngredientOf',
'isToppingOf'

The methods allConceptNames and allIndividuals work almost identical.

Satisfiability

To check, whether or not the relations between classes are satisfied, there are three methods available:

With unsatisfiable we can retrieve a list of all those concepts, which are not satisfiable. Because there are no unsatisfiable concepts in our pizza ontology, we would get back an empty list:

warn Dumper $kb->unsatisfiable;

<empty result>

We can also query whether or not one concept subsumes another. To do that we have to provide a hash (reference) that contains the classes we want to check:

warn Dumper $kb->subsumes (\ %('Pizza' => ['PizzaTopping', 'CheesePizza']) );

'Pizza' => 'CheesePizza'

The result contains all those classes as values that indeed subsume the concept that is provided as key.

The third method disjoint works the same way.

Concept Hierarchy

There are some methods regarding the hierarchy of concepts. If you want to know, for instance, the parents from a specific concept of our $model, you can simply write:

warn Dumper $kb->parents ('CheesePizza', 'HamTopping');

'CheesePizza' => 'Pizza',
'HamTopping'  => 'PizzaTopping'

You can also request information on children, descendants or ancestors from classes.

Role Hierarchy

Similar to the concept hierarchy methods, there are also role hierarchy functions available. This way you are able to retrieve information regarding the parents, children, ancestors or descendants of a specific role. If we want to know all children for a specific role in our $model, we write:

warn Dumper $kb->rchildren ('isIngredientOf');

'isIngredientOf' => 'isToppingOf'

Queries about Individuals

For retrieving information about the relations between concepts, roles or individuals, there is also a bunch of methods available. We can query the instances from a specific class:

warn Dumper $kb->instances ('CheesePizza')

'CheesePizza' => 'Margharita'

We can retrieve information about individuals that are asserted to a specific (individual,role)-pair with the roleFillers method. All we have to provide is one individual and one role as parameter and in return we get all individuals that are asserted as statement involving this pair:

warn Dumper $kb->roleFillers ('Margharita', 'hasTopping');

'MozarellaTopping'

If we want to know which pairs of individuals are asserted to a given role, we use the method relatedIndividuals:

warn Dumper $kb->relatedIndividuals ('hasTopping');

('Margharita', 'MozarellaTopping')

Variation: Procure your own User Agent

Sometimes you will want to use an HTTP user agent of your own making, be it for testing, caching, etc. In any case it will have to be a subclass of LWP::UserAgent.

If we assume that it is stored in $ua, then you will need to provide it as a additional parameter when the reasoner handle is created:

my $digreasoner = new RDF::Redland::DIG ( "http://localhost:8081",
                                          ua => $ua )
                  or die "Failed to create RDF::Redland::DIG";

Variation: Concept Hierarchy Parameters

There are some alternatives regarding passing of parameters when using the concept and role hierarchy methods.

As demonstrated above, you can provide a specific concept or role as parameter and get all the information you want for those specified concepts or roles. If you want to create a general query on all existing concepts and roles from your model, you can leave the parameter blank.

If you want to have the parents from all concepts of our $model you can write:

warn Dumper $kb->parents;

'Pizza'           => '',
'
PizzaTopping'    => '',
'
CheesePizza'     => 'Pizza',
'
ProsciuttoPizza' => 'Pizza',
'
CheeseTopping'   => 'PizzaTopping',
'
TomatoTopping'   => 'PizzaTopping',
'
HamTopping'      => 'PizzaTopping'

Posted In