This section of the user manual will describe the support for inference over RDF graphs that is included within Jena. However, it has not been written. In its place we have some brief notes to assist brave users who wish to experiment with preview releases of this functionality before the documentation is ready.
The Jena2 reasoner subsystem is designed to allow a range of inference engines to be plugged into Jena. Such engines are used to derive additional RDF assertions which are entailed from some base RDF together with any optional ontology information and the axioms and rules associated with the reasoner. The primary use of this mechanism is to support the use of languages such as RDFS and OWL which allow additional facts to be inferred from instance data and class descriptions. However, the machinery is designed to be quite general and, in particular, it includes a generic rule engine that can be used for many RDF processing or transformation tasks.
We will try to use the term inference to refer to the abstract process of deriving additional information and the term reasoner to refer to a specific code object that performs this task. Such usage is arbitrary and if we slip into using equivalent terms like reasoning and inference engine please forgive us.
The overall structure of the inference machinery is illustrated below.
As illustrated the inference machinery is implemented at the level of the Graph
SPI [TODO:cross link to that section]. One or more Graphs containing RDF data
can be attached to a specific reasoner to generate a derived graph of type InfGraph
[TODO: links to javadoc]. The InfGraph acts as if it is a graph containing all
of the triples in the base data graph(s) together with any additional triples
which the reasoner is able to infer. However, many of these triple will be virtual
in the sense that they are only generated in response to access requests.
The derived InfGraph can then be viewed through any of the Jena supported APIs, specifically the general RDF Model API and the various profiles of the Ontology API. The choice of access API, underlying reasoner and physical implementation of the data graphs can all be varied independently.
Included in the Jena distribution are a number of predefined reasoners, at various stages of maturity. In the Jena2 preview 4 distribution the following reasoners are worth noting:
rdfs:subPropertyOf
and rdfs:subClassOf
.For each type of reasoner there is a factory class (which conforms to the interface
ReasonerFactory
) which has one or more instances through which
instances of the Reasoner
can be constructed. The factory instances
can be located by going directly to a known factory class and finding and use
theInstance
() method or by retrieval from a global ReasonerRegistry
which stores factory instances indexed by URI assigned to the reasoner.
In addition, there are convenience methods on the ReasonerRegistry
for locating a prebuilt instance of each of the main reasoners (getTransitiveReasoner
,
getRDFSReasoner
, getOWLReasoner
, getRDFSSimpleReasoner
).
Finally the accessAPIs include convenience support for creating Models or Ontology
models with supporting reasoners. See ModelFactory.createRDFSModel
and [TODO: cross ref to ontology documentation].
The behaviour of many of the reasoners can be configured. To allow arbitrary
configuration information to be passed to reasoners we use RDF to encode the
configuration information. The ReasonerFactory.create
method can
be passed a Jena Resource
object, the properties of that object
will be used to configure the created reasoner.
To simplify the code required for simple cases we also provide a direct Java
method to set a single configuration parameter, Reasoner.setParameter
.
The parameter being set is identified by the URI of the corresponding configuration
property.
For the built in reasoners the available configuration parameters are described
below [TODO: make it so] and are predefined in the ReasonerVocabulary
class.
Once you have an instance of a reasoner it can then be attached to a graph of RDF data to create an inference graph. This can either be done by putting all the RDF data into one Graph/Model or by separating into two components - schema and instance data. For some external reasoners a hard separation may be required. For all of the built in reasoners the separation is arbitrary. The prime value of this separation is the allow some deductions from one set of data (typically some schema definitions) to be efficiently applied to several subsidiary sets of data (typically sets of instance data).
At the SPI level the methods Reasoner.bindSchema
and Reasoner.bind
perform this function. These operations are side-effect free so that a single
reasoner instance can be used to generate arbitrary bound inference graphs without
problems.
At the API level the method ModelFactory.createInfModel
does this.
Finally, having created a inference model then any API operations which access RDF statements will be able to access additional statements which are entailed from the bound data by means of the reasoner.
The reasoners can be described using RDF metadata which can be searched to
locate reasoners with appropriate properties. The calls Reasoner.getCapabilities
and Reasoner.supportsProperty
are used to access this descriptive
metadata.
These initial examples are not designed to illustrate the power of the reasoners but to illustrate the code required to set one up.
Let us first create a Jena model containing the statements that some property "p" is a subproperty of another property "q" and that we have a resource "a" with value "foo" for "p". This could be done by writing an RDF/XML or N3 file and reading that in but we have chosen to use the RDF API:
String NS = "urn:x-hp-jena:eg/"; // Build a trivial example data set Model rdfsExample = ModelFactory.createDefaultModel(); Property p = rdfsExample.createProperty(NS, "p"); Property q = rdfsExample.createProperty(NS, "q"); rdfsExample.add(p, RDFS.subPropertyOf, q); rdfsExample.createResource(NS+"a").addProperty(p, "foo");
Now we can create an inference model which performs RDFS inference over this data by using:
InfModel inf = ModelFactory.createRDFSModel(rdfsExample); // [1]
We can then check that resulting model shows that "a" also has property "q" of value "foo" by virtue of the subPropertyOf entailment:
Resource a = inf.getResource(NS+"a"); System.out.println("Statement: " + a.getProperty(q));
Which prints the output:
Statement: [urn:x-hp-jena:eg/a, urn:x-hp-jena:eg/q, Literal]
Alternatively we could have created an empty inference model and then added in the statements directly to that model.
If we wanted to use a different reasoner which is not available as a convenience method or wanted to configure one we would change line [1].
To create the same set up manually we could replace [1] by:
Reasoner reasoner = RDFSRuleReasonerFactory.theInstance().create(null); InfModel inf = ModelFactory.createInfModel(reasoner, rdfsExample);
Then we could set properties of the reasoner before use. For example, if we were to listStatements on inf Model we would see that it also "includes" all the RDFS axioms, of which there are quite a lot. It is sometimes useful to suppress these and only see the "interesting" entailments. This can be done by setting the processing level parameter by creating a description of a new reasoner configuration and passing that to the factory method:
Resource config = ModelFactory.createDefaultModel() .createResource() .addProperty(ReasonerVocabulary.PROPsetRDFSLevel, "simple"); Reasoner reasoner = RDFSRuleReasonerFactory.theInstance()Create(config); InfModel inf = ModelFactory.createInfModel(reasoner, rdfsExample);
This is a rather long winded way of setting a single parameter, though it can be useful in the cases where you want to store this sort of configuration information in a separate (RDF) configuration file. For hardwired uses the following alternative is often simpler:
Reasoner reasoner = RDFSRuleReasonerFactory.theInstance()Create(null); reasoner.setParameter(ReasonerVocabulary.PROPsetRDFSLevel.getURI(), ReasonerVocabulary.RDFS_SIMPLE); InfModel inf = ModelFactory.createInfModel(reasoner, rdfsExample);
Finally, supposing you have a more complex set of schema information in defined in a Model called schema and you want to apply this schema to several sets of instance data without redoing too many of the same intermediate deductions. This can be done by using the SPI level methods:
Reasoner boundReasoner = reasoner.bindSchema(schema.getGraph()); InfModel inf = ModelFactory.createInfModel(boundReasoner, data);
This creates an new reasoner, independent from the original, which contains the schema data. Any queries to an InfModel created using the boundReasoner will see the schema statements, the data statements and any statements entailed from the combination of the two.
The TransitiveReasoner provides support for storing and traversing class and
property lattices. This implements just the transitive and symmetric
properties of rdfs:subPropertyOf
and rdfs:subClassOf
.
It is not all that exciting on its own but is one of the building blocks used
for the more complex reasoners. The functionality it offers is roughly equivalent
to the hardwired inferences available through the Jena1 DAML API.
It has no configuration options.
The RDSFRuleReasoner implements the RDFS entailment rules by using a combination of the TransitiveReasoner and a generate rule based engine.
The rules are defined in a text file located on the classpath and it is possible to construct new RDFS engines with different rule variants.
The most important configuration parameter is the RDFSLevel parameter illustrated in the example above. The levels are:
[TODO: more detail required on this either here or in a later section and expand on all the cryptic references like rdfD-1]
Note: In the Jena Model API we do not treat bNodes as variables. This enables code to, for example, retrieve the properties of specific bNodes from a Model. In order to determine if Model B is entailed by Model A one would need to translate model B in a query replacing bNodes by query variables and then apply the query to Model A. This will be expensive for large B's.
Given this property that bNodes have real existence as Java objects in a Jena Model, and given that the reasoners are intended to be access through the normal Model API, we decided that implementing the RDFS closure rules that generate bNodes for all literals in the graph would be confusing and pointless. So we didn't.
Similarly, the Jena API allows provides direct means to discover the datatype of a typed literal and reflecting this through the rdfD-1 closure rule is pointless.
The RDFS (and indeed OWL) reasoner is built using a general purpose rule engine. This engine supports forward chaining rules, tabled (aka memoized) backward chaining rules or a hybrid mode in which forward rules can create new backward rules that are used to answer queries.
This engine is available for general use in applications that wish to perform rule based processing of RDF data.
At this release the engine is exposed via the GenericRuleReasoner
and is complete enough to support the RDFS reasoner but is not yet stable. In
particular the backward chaining component is being revised. As the RuleReasoner
stabilizes will generate documentation and sample code for how to use it in
applications. At the moment rules can only be created either programmatically
or via a text format, though in principle it would be possible to define an
RDF encoding for them.
[TODO lots more details]
We also include an early prototype of a rule-based implementation of an OWL/Lite reasoner - OWLFBReasoner. The current implementation is sufficient to pass the relevant core normative WebONT test cases. On such small scale test cases, where the query to the inference graph is a test for a ground triple, performance is good. However, we have concerns over scalability of the current solution and intend to do further performance work and validation before recommending this reasoner for use.
[TODO lots more details]
TODO. Describe:
The Jena reasoner API is intended to support plug in access to appropriate external reasoners and we plan to construct example adapters for one or two openly available reasoners. This work has not yet been done and no external reasoner support is included within this release.
[TODO lots more details]