Introduction
Before we begin the tutorial there are few things to keep in mind.
- The reasoner supports a subset of the full RDFS and OWL constructs (see below).
- The tutorial examples below are in Lisp. The AllegroGraph Learning Center is the best place to find Java examples of using the reasoner and much, much more.
- This tutorial is written for people who know how to get around in the IDE or in Emacs. You can load this file in a buffer and start executing its forms from top to bottom.
- The Lisp program 'reasoner-tutorial.lisp' contains all of the illustrative examples below. You may want to open this in your development environment and follow along by evaluating its forms as you go. The program is in the 'tutorial-files' sub-directory of the AllegroGraph installation directory.
Reasoning Supported.
We support all the RDFS and OWL reasoning that you can do with the predicates below.
- rdf:type
- rdfs:domain
- rdfs:range
- rdfs:subClassOf
- rdfs:subPropertyOf
- owl:inverseOf
- owl:sameAs
- owl:TransitiveProperty
We also support owl:hasValue reasoning. Its use is covered in a separate tutorial.
Before you look up the semantics of these predicates in the W3C documentation you may want to first examine the examples in the tutorial below. Note that for efficiency's sake, our RDFS++ reasoner will not make every possible inference given a triple-store's ground triples. 1 We are constantly improving our inference algorithms and will continue to extend the reasoner's reach. If there are particular inferences that are important to you and AllegroGraph is not making them, please let us know so that we can work together to reach a solution.
Changes between AllegroGraph 2.x and 3.0
In AllegroGraph 2.x, reasoning was applied at the individual query level. Every triple-store supported a single kind of reasoning (RDFS++) which could be turned on for some queries and turned off for others. AllegroGraph 3.0's new architecture provides much greater flexibility in composing multiple triple-stores and reasoning engines in whatever shape is needed for a particular application. Given this flexibility, the simple "this query has reasoning and this one does not" no longer makes sense. Instead, you should create the triple-stores that you need to perform each different kind of query (use federate-triple-stores, encapsulate-triple-store, and db-apply-reasoner to build each store) and run your queries against them. For example, suppose we create a triple-store and add two triples (if you not familiar with the !-notation, see the section of the reference-guide on future-parts):
> (setf ground-ts (create-triple-store "sample"))
#<triple-db /home/gwking/sample, open @
#x13d41812>
> (register-namespace "ex" "http://www.franz.com/simple#")
"http://www.franz.com/simple#"
> (add-triple !ex:jans !ex:owns !ex:birra)
1
> (add-triple !ex:jans !owl:sameAs !ex:jannes)
2
We'll make sure that the triples are in there and use enable-print-decoded so that the rest of the example is easier to read:
> (print-triples *db* :format :concise)
<1: ex:jans ex:owns ex:birrabirra>
<2: ex:jans owl:sameAs ex:jannesjannes>
; No value
> (enable-print-decoded t)
t
If we ask for all of the triples whose predicate is !ex:owns
, we get back the single ground triple.
> (get-triples-list :p !ex:owns)
(<jans owns birra>)
nil
If we want to use AllegroGraph's RDFS++ reasoner, we call apply-rdfs++-reasoner
. If called with no additional parameters, this will encapsulate the current triple-store (i.e., *db*) with a reasoning-triple-store whose reasoner is of type rdfs++-reasoner. It will also bind *db* to this new store. The return value of the call makes it clear what has happened.
> (setf inferred-ts (apply-rdfs++-reasoner))
#<reasoning-triple-store
sample, inner #<triple-db /home/gwking/sample, open @ #x13d41812>
@ #x140a26f2>
Now if we make the same call to get-triples-list
, we get two results back. One ground triple and one that has been inferred.
> (get-triples-list :p !ex:owns)
(<jans owns birra> <jannes owns birra>)
Note that we can specify which triple-store to query using the :db
argument. If we ask for the triples from ground-ts
, that is what we will get. We could also have accomplished the same thing by using the reasoning-triple-store's inner-triple-store:
> (get-triples-list :p !ex:owns :db ground-ts)
(<jans owns birra>)
> (get-triples-list :p !ex:owns :db (inner-triple-store *db*))
(<jans owns birra>)
Functions discussed in this tutorial.
This tutorial discusses: make-tutorial-store
, apply-rdfs++-reasoner
, inner-triple-store
and get-triples-list
. The first is just a convenience function to create a fresh triple-store in a temporary directory.
2
Close any current triple-store and create a new empty one in a temporary directory. Make-tutorial-store takes two keyword parameters:
:temporary-directory
- the directory where the store will be created. If it is not supplied, then it will get its value from (ag-property
temporary-directory
).:use-reasoner
- If true (the default), then the new triple-store will use RDFS++ reasoning. If nil, then the triple-store will have no reasoning.
The new triple-store will be bound to *db*
and is also returned by make-tutorial-store
.
As discussed above, apply-rdfs++-reasoner
wraps a triple-store in a reasoning-triple-store and uses the rdfs++-reasoner as the reasoning class.
Return a new reasoning-triple-store that encapsulates db
and adds an rdfs++-reasoner.
db
- the triple-store to encapsulate. The defaults to the value of *db*.name
- the name to give the encapsulated-triple-store. Defaults to the current db-name of thedb
with rdfs++- prepended.
You can query a reasoning-triple-store almost anywhere that you can use a regular triple-store. SPARQL, our family of Prolog select functors, get-triples and get-triples-list all know how to work with a reasoning-triple-store.
It's helpful to be able to quickly print out the results of a query. For this purpose, we define a new function ptl
(for print-triples-list):
(defun ptl (s p o)
(print-triples (get-triples :s s :p p :o o) :format :concise))
As you can see, it simply passes the s-p-o
pattern to get-triples
and then uses print-triples
to display them in an easy to read form.
example: (ptl !ex:Jans !ex:has nil)
Finally, the reasoner introduces a new Prolog functor
(q ?s ?p ?o)
that is used like q- in prolog clauses. The difference is that q-
will work on the inner-triple-store whereas q
will work with the actual *db* (obviously, if *db* is a non-encapsulated-store, then q-
and q
will be the same.)
Example: Assume that *db* is a reasoning-triple-store, then using q-
...
(select (?x)
(q- ?x !cyc:isa !cyc:Terrorist))
This will find all the triples that literally have the predicate !cyc:isa
and the object !cyc:Terrorist
.
This next query does the same thing but uses the reasoner and might return more triples.
(select (?x)
(q ?x !cyc:isa !cyc:Terrorist))
Getting Ready
The examples below assume that the following code has been evaluated in your Lisp session. So please make sure you do so in your session before continuing.
(require :agraph)
(in-package :triple-store-user)
;; short for print-triples-list
(defun ptl (s p o)
(print-triples (get-triples :s s :p p :o o) :format :concise))
(register-namespace
"ex" "http://www.franz.com/example#" :errorp nil)
Reasoner Examples
Inverse of
(make-tutorial-store)
(add-triple !ex:jans !ex:owns !ex:birra)
(add-triple !ex:owned-by !owl:inverseOf !ex:owns)
(add-triple !ex:has !owl:inverseOf !ex:owned-by)
(ptl !ex:birra !ex:owned-by nil)
(ptl nil !ex:owned-by nil)
(ptl nil !ex:owned-by !ex:jans)
(ptl !ex:jans !ex:has nil)
(ptl nil !ex:has nil)
(ptl nil !ex:has !ex:birra)
(ptl nil nil nil)
; this will return nothing
; because it works on only the triples explicitly
; added to the triple-store
(select (?x)
(q- !ex:birra !ex:owned-by ?x))
; this will return something
; because it works on inferred triples
(select (?x)
(q !ex:birra !ex:owned-by ?x))
Sub-property of
(make-tutorial-store)
(add-triple !ex:jans !ex:has-pet !ex:birra)
(add-triple !ex:has-pet !rdfs:subPropertyOf !ex:owns)
(add-triple !ex:birra !ex:friend-of !ex:samira)
(ptl !ex:jans !ex:owns !ex:birra)
(ptl !ex:jans !ex:owns nil)
(ptl nil !ex:owns !ex:birra)
(ptl !ex:jans !ex:has-pet !ex:birra)
(ptl !ex:jans !ex:has-pet nil)
(ptl nil !ex:has-pet !ex:birra)
(select (?x ?y)
(q !ex:jans !ex:owns ?x)
(q ?x !ex:friend-of ?y))
inverse and sub properties
(make-tutorial-store)
(add-triple !ex:jans !ex:has-pet !ex:birra)
(add-triple !ex:owned-by !owl:inverseOf !ex:owns)
(add-triple !ex:has !owl:inverseOf !ex:owned-by)
(add-triple !ex:has-pet !rdfs:subPropertyOf !ex:owns)
(add-triple !ex:pet-of !owl:inverseOf !ex:has-pet)
;; direct triples
(ptl !ex:jans !ex:has-pet !ex:birra)
(ptl nil !ex:has-pet !ex:birra)
(ptl !ex:jans !ex:has-pet nil)
;; inverse of !ex:has-pet
(ptl !ex:birra !ex:pet-of !ex:jans)
(ptl nil !ex:pet-of !ex:jans)
(ptl !ex:birra !ex:pet-of nil)
;; subproperty
(ptl !ex:jans !ex:owns !ex:birra)
(ptl !ex:jans !ex:owns nil)
(ptl nil !ex:owns !ex:birra)
;; inverse of subproperty
(ptl !ex:birra !ex:owned-by !ex:jans)
(ptl nil !ex:owned-by !ex:jans)
(ptl !ex:birra !ex:owned-by nil)
;; inverse of inverse
(ptl !ex:jans !ex:has !ex:birra)
(ptl nil !ex:has !ex:birra)
(ptl !ex:jans !ex:has nil)
Same-as
(make-tutorial-store)
(add-triple !ex:jans !ex:owns !ex:birra)
(add-triple !ex:jans !owl:sameAs !ex:jannes)
(add-triple !ex:aasman !owl:sameAs !ex:jannes)
(add-triple !ex:birra !owl:sameAs !ex:son-of-samira)
(ptl !ex:aasman !ex:owns !ex:son-of-samira)
(ptl !ex:aasman !ex:owns nil)
(ptl nil !ex:owns !ex:son-of-samira)
(ptl nil !ex:owns nil)
Same-as, inverse, and sub-properties
(make-tutorial-store)
(add-triple !ex:jans !ex:has-pet !ex:birra)
(add-triple !ex:owned-by !owl:inverseOf !ex:owns)
(add-triple !ex:has !owl:inverseOf !ex:owned-by)
(add-triple !ex:has-pet !rdfs:subPropertyOf !ex:owns)
(add-triple !ex:pet-of !owl:inverseOf !ex:has-pet)
(add-triple !ex:birra !ex:age !ex:twelve)
(add-triple !ex:jans !owl:sameAs !ex:jannes)
(add-triple !ex:aasman !owl:sameAs !ex:jannes)
(add-triple !ex:birra !owl:sameAs !ex:son-of-samira)
;; direct triples
(ptl !ex:aasman !ex:has-pet !ex:son-of-samira)
(ptl nil !ex:has-pet !ex:son-of-samira)
(ptl !ex:aasman !ex:has-pet nil)
;; inverse of !ex:owns
(ptl !ex:son-of-samira !ex:pet-of !ex:aasman)
(ptl nil !ex:pet-of !ex:aasman)
(ptl !ex:son-of-samira !ex:pet-of nil)
;; inverse of inverse
(ptl !ex:aasman !ex:has !ex:son-of-samira)
(ptl nil !ex:has !ex:son-of-samira)
(ptl !ex:aasman !ex:has nil)
;; subproperty
(ptl !ex:aasman !ex:owns !ex:son-of-samira)
(ptl !ex:aasman !ex:owns nil)
(ptl nil !ex:owns !ex:son-of-samira)
;; inverse of subproperty
(ptl !ex:son-of-samira !ex:owned-by !ex:aasman)
(ptl nil !ex:owned-by !ex:aasman)
(ptl !ex:son-of-samira !ex:owned-by nil)
;; what to do with this?
(ptl nil nil nil)
;; but what if predicate is unknown?
(ptl !ex:jans nil !ex:birra) ;; this returns only one valid result
;; what should i do here, find all the
;; predicates defined for !ex:aasman (and the sames)
;; and then try them all?
(ptl !ex:aasman nil !ex:birra)
(ptl !ex:aasman nil nil)
type with sub-class
(make-tutorial-store)
(add-triple !ex:mammal !rdfs:subClassOf !ex:animal)
(add-triple !ex:human !rdfs:subClassOf !ex:mammal)
(add-triple !ex:man !rdfs:subClassOf !ex:human)
(add-triple !ex:jans !rdf:type !ex:man)
(add-triple !ex:jans !owl:sameAs !ex:jannes)
(add-triple !ex:aasman !owl:sameAs !ex:jannes)
(ptl !ex:jans !rdf:type !ex:man)
(ptl !ex:jans !rdf:type !ex:human)
(ptl !ex:jans !rdf:type nil)
(ptl !ex:aasman !rdf:type !ex:man)
(ptl !ex:aasman !rdf:type !ex:human)
(ptl !ex:aasman !rdf:type nil)
(ptl nil !rdf:type !ex:man)
(ptl nil !rdf:type !ex:human)
(ptl nil !rdf:type nil)
type with range
(make-tutorial-store)
(add-triple !ex:jans !ex:has-pet !ex:birra)
(add-triple !ex:has-pet !rdfs:range !ex:pet)
(add-triple !ex:pet !rdfs:subClassOf !ex:mammal)
(add-triple !ex:fatcat !owl:sameAs !ex:birra)
(ptl !ex:birra !rdf:type !ex:pet)
(ptl !ex:birra !rdf:type nil)
(ptl nil !rdf:type !ex:pet)
(ptl !ex:birra !rdf:type !ex:mammal)
(ptl !ex:fatcat !rdf:type !ex:mammal)
type with domain
(make-tutorial-store)
(add-triple !ex:jans !ex:has-pet !ex:birra)
(add-triple !ex:has-pet !rdfs:domain !ex:human)
(add-triple !ex:human !rdfs:subClassOf !ex:mammal)
(add-triple !ex:jans !owl:sameAs !ex:aasman)
(ptl !ex:jans !rdf:type !ex:human)
(ptl !ex:jans !rdf:type nil)
(ptl nil !rdf:type !ex:human)
;; not returning all solutions..
(ptl nil !rdf:type nil)
Transitivity with Same As
(make-tutorial-store)
(add-triple !ex:contains !rdf:type !owl:TransitiveProperty)
(add-triple !ex:usa !ex:contains !ex:california)
(add-triple !ex:golden-state !ex:contains !ex:contra-costa)
(add-triple !ex:contra-costa !ex:contains !ex:moraga)
(add-triple !ex:usa !owl:sameAs !ex:uncle-sam)
(add-triple !ex:moraga !owl:sameAs !ex:mytown)
(add-triple !ex:california !owl:sameAs !ex:golden-state)
(ptl !ex:usa !ex:contains !ex:moraga)
(ptl !ex:uncle-sam !ex:contains !ex:mytown)
(ptl !ex:golden-state !ex:contains !ex:moraga)
(ptl !ex:california !ex:contains !ex:moraga)
(ptl !ex:california !ex:contains !ex:mytown)
(ptl !ex:usa !ex:contains nil)
(ptl !ex:uncle-sam !ex:contains nil)
(ptl nil !ex:contains !ex:moraga)
(ptl nil !ex:contains !ex:mytown)
Footnotes
- In particular, some non-rdf:type and rdfs:subClassOf reasoning involving chains of transitive predicates will not be followed unless the predicate is supplied as part of the query. ↩
-
The only thing to be careful of is that the directory is chosen by the built-in Allegro Common Lisp function sys:temporary-directory. It is possible for this to return a directory into which you do not have write access. In this case,
make-tutorial-store
will generate an error. The solution is to either alter the value of the ag-property :temporary-directory or to supply your own directory as the optional argument ofmake-tutorial-store
. ↩