Clone wiki

dotnetrdf / UserGuide / Inference and Reasoning

Home > User Guide > Inference and Reasoning

Inference and Reasoning

Inference and Reasoning are mechanisms whereby an application can discover additional information that is not explicitly stated in the initial data. There are various scenarios when you might want to use this and having this capability in the library provides a powerful feature to end users. Inference and reasoning in dotNetRDF is currently based on the IInferenceEngine interface which allows for both static and dynamic reasoners i.e. those that use a fixed set of rules and those that create their rules dynamically based on the input data.

The IInferenceEngine interface

The IInferenceEngine interface has two main methods which implementers need to implement in order to integrate your own reasoners into dotNetRDF. The first of these is the Initialise(IGraph g) method which is used to input graphs to the reasoner which define the schema/rules that the reasoner should follow. The reasoner can process and interpret this Graph in any way it wishes in order to generate the rules that it will use when actually applying inference to a Graph.

The second method is the Apply() method which applies inference to a Graph outputting the inferred triples into either the same graph or to another graph. For implementers this method is where the core logic of the reasoner will be located (or at least called from).

Existing Implementations

As part of the library three types of reasoners are provided currently - an RDFS reasoner, a SKOS reasoner and a simple N3 Rules reasoner.

RDFS Reasoner

RDFS is an RDF vocabulary for expressing schemas for RDF data specified by the W3C. It allows for the definition of class and property hierarchies and specifying things like the domains and ranges of properties. We provide both a StaticRdfsReasoner which has to be initialised with one/more schemas and uses only those to make inferences and a RdfsReasoner which is dynamic in that every Graph you apply reasoning to can extend the set of rules that the reasoner uses.

The RDFS reasoner does not apply the full range of possible RDFS based inferencing but does do the following:

  • Asserts additional type triples for anything which has a type which is a sub-class of another type
  • Asserts additional triples where the property (predicate) is a sub-property of another property
  • Asserts additional type triples based on the domain and range of properties

Consider the following example schema (schema.ttl):

@prefix rdfs: <> .
@prefix : <> .

:Vehicle a rdfs:Class .
:Car rdfs:subClassOf :Vehicle .
:SportsCar rdfs:subClassOf :Car .

And the following example data (data.ttl):

@prefix ex: <> .
@prefix : <> .

:FordFiesta a ex:Car .
:AudiA8 a ex:Car .
:FerrariEnzo a ex:SportsCar .

If you were to add the data into a graph and ask it for things which are cars then it would only give you back :FordFiesta and :AudiA8 despite the fact that :FerrariEnzo is also a car. If we apply an RDFS reasoner to the data using the schema given and then ask the same question we'd get back all three things e.g.

using System;
using VDS.RDF;
using VDS.RDF.Query.Parsing;
using VDS.RDF.Query.Inference;

public class RdfsReasoningExample
	public static void Main(String[] args)
		//First we want to load our data and schema into Graphs
		Graph data = new Graph();
		FileLoader.Load(data, "data.ttl");
		Graph schema = new Graph();
		FileLoader.Load(schema, "schema.ttl");

		//Now we ask for things which are cars from our data Graph
		IUriNode rdfType = data.CreateUriNode(new Uri(RdfSpecsHelper.RdfType));
		IUriNode car = data.CreateUriNode("ex:Car");
		foreach (Triple t in data.GetTriplesWithPredicateObject(rdfType, car))
		//This will result in the triples defining the type for :FordFiesta
		//and :AudiA8 being printed
		//BUT without inference we don't know that :FerrariEnzo is a car

		//So now we'll go ahead and apply inference
		StaticRdfsReasoner reasoner = new StaticRdfsReasoner();

		//Now we ask for things which are cars again
		foreach (Triple t in data.GetTriplesWithPredicateObject(rdfType, car))
		//This time it will have printed :FerrariEnzo as well as it will have inferred that anything
		//which is of type ex:SportsCar is also of type ex:Car

SKOS Reasoner

SKOS is another RDF vocabulary specified by the W3C which is intended for use in defining taxonomies for classifying data. The SKOS reasoner included in the library is a simple concept hierarchy reasoner which can infer additional triples where the subject has an object which is a skos:Concept in the taxonomy by following skos:narrower and skos:broader links as appropriate.

As with RDFS there is a StaticSkosReasoner and a dynamic variant called SkosReasoner.

Consider the following classification of vehicles based on the earlier examples (taxonomy.ttl):

@prefix skos: <>
@prefix : <> .

:Vehicle a skos:Concept .
:Vehicle skos:narrower :Car .
:Car skos:narrower :SportsCar .

And the following data (data2.ttl):

@prefix ex: <> .
@prefix : <> .

:FordFiesta ex:vehicleType ex:Car .
:AudiA8 ex:vehicleType ex:Car .
:FerrariEnzo ex:vehicleType ex:SportsCar .

As seen in the RDFS example without inference we don't automatically know that anything which stated it was related to the concept :SportsCar is also related to the concept :Car but by applying SKOS concept hierarchy reasoning we can do this.

Simple N3 Rules Reasoner

The SimpleN3RulesReasoner is a reasoner that is able to apply simple N3 Rules. The reasoner must be initialised with a Graph that has been parsed from an input N3 file in order to contain any rules. A simple rule is expressed as follows:

{ ?x a ex:Car } => { ?x a ex:Vehicle }

The above rule would match anything that is a ex:Car and assert that it is also a ex:Vehicle.

Any rule which is composed of two graph literals and connected via => or <= can be processed and applied. Note that nested graph literals are not currently supported.

Additionally if you use @forsome or @forall to define tokens as being variables these will be processed e.g.

@forall :x .
{ :x a ex:Car } => { :x a ex:Vehicle }

The above is equivalent to the previous example since we've used a @forall directive to specify that :x is a variable. Generally speaking specifying variables directly is always preferred.

Using Inference with Triple Stores

The library also provides an IInferencingTripleStore interface which extends the basic ITripleStore interface with methods which allow for the attachment of reasoners (instances of IInferenceEngine implementations) to a Triple Store. Reasoning when used in this sense is static in that inference is applied only at certain points:

  • When you add a reasoner the IInferencingTripleStore the implementations in the library will apply the reasoner to all existing Graphs in the Store
  • When you add a new Graph to the Store the reasoner will be applied to that Graph

Current implementations store the inferred information in a special Graph inside the Triple Store so that the existing Graphs are not themselves altered - there is no guarantee/requirement that 3rd party implementations of this interface will do this. Also there is a limitation in that inferences will not be made when a graph changes or is removed so you can have some data over which no inferences have been made or some data which is inferred from data you've removed from your Store.

Native Stores and Inferencing

Currently there is no formal API support in the library for using inference with 3rd party stores, primarily because this feature is not available in many stores and there is not currently a standardised mechanism for specifying that you wish to use inference with a store.

However some stores such as StarDog do support reasoning when making queries, see Triple Store Integration for more information.

Reasoners and SPARQL Endpoints

Please see Configuration API - Reasoners for information on how to specify reasoner configuration and how to attach them to HTTP Handlers.