SemWeb: Querying

SemWeb Documentation

# Inferencing: RDFS and the Euler Proof Engine

SemWeb provides two inferencing classes in the `SemWeb.Inference` namespace: `RDFS`, which implements a subset of RDFS reasoning, and `Euler` which implements general rule-based reasoning based on the Euler proof mechanism.

The two reasoning engines work similarly. They are initialized with axioms and then perform reasoning on an arbitrary target data model. For the `RDFS` class, the axioms are an RDFS schema that contains subClassOf relations between classes and subPropertyOf relations between properties. For the `Euler` class, the axioms are rules of entailment (and possibly some basic instance data). The data model contains an arbitrarily large data set that the axioms are applied to in order to answer some question about the data.

The best way to use these `Reasoner` classes is to apply them to a `Store` with the `AddReasoner` method. When this is done, the `Contains`, `Select`, and `Query` methods on the store will make use of the reasoner applied.

## RDFS Reasoning

SemWeb's `RDFS` class provides limited RDFS reasoning over a data model. See the API documentation for a complete list of what reasoning the class provides.

To use the class, create an instance of it and then load in RDF schemas.

    RDFS engine = new RDFS();
    engine.LoadSchema(RdfReader.LoadFromUri(new Uri("http://xmlns.com/foaf/0.1/index.rdf")));

Besides passing it an `RdfReader`, you may also pass it any `SelectableSource` with RDFS schema statements, such as a `MemoryStore` that you might construct with your own schema. You can call `LoadSchema` any number of times.

Then apply the `RDFS` instance to a `Store` object.

    dataModel.AddReasoner(engine);

The `Contains`, `Select`, and `Query` methods on the store will make use of the RDFS reasoner and will return anything entailed by the instance data in the data model and the schema.

For instance, if the dataModel contains the following:

    :me rdf:type foaf:Person .
    :you rdf:type foaf:Person .
    :me foaf:name "John Doe" .
    :you foaf:name "Sam Smith" .

and you load in the FOAF schema, then the following will result

    dataModel.Contains(new Statement(me, rdfType, foafAgent)) => Returns True

    dataModel.SelectSubjects(rdfType, foafAgent)) => Returns [ me , you ]

    dataModel.Select(new Statement(null, rdfsLabel, null))
     => me rdfsLabel "John Doe"
     => you rdfsLabel "Sam Smith"

Note that when RDFS returns statements that were found by applying a subproperty closure, the returned statements do not show the subproperty actually in the instance data, but rather the property that was provided in the call to `Select`. Think of it this way: Select does not return statements in the data model that are relavent to the template, but rather it returns all possible statements that are entailed by the data model and schema that match the template.

A complete example of using the `RDFS` class is below:

    // This example demonstrates basic RDFS reasoning.

    using System;
    using System.IO;

    using SemWeb;
    using SemWeb.Inference;

    public class EulerTest {

        public static void Main() {
            // Create the instance data

            MemoryStore dataModel = new MemoryStore();

            BNode me = new BNode("me");
            BNode you = new BNode("you");

            Entity rdfType = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type";
            Entity rdfsLabel= "http://www.w3.org/2000/01/rdf-schema#label";
            Entity foafPerson = "http://xmlns.com/foaf/0.1/Person";
            Entity foafAgent = "http://xmlns.com/foaf/0.1/Agent";
            Entity foafName = "http://xmlns.com/foaf/0.1/name";

            dataModel.Add(new Statement(me, rdfType, foafPerson));
            dataModel.Add(new Statement(you, rdfType, foafPerson));
            dataModel.Add(new Statement(me, foafName, (Literal)"John Doe"));
            dataModel.Add(new Statement(you, foafName, (Literal)"Sam Smith"));

            // Create the RDFS engine and apply it to the data model.

            RDFS engine = new RDFS();
            engine.LoadSchema(RdfReader.LoadFromUri(new Uri("http://xmlns.com/foaf/0.1/index.rdf")));

            dataModel.AddReasoner(engine);

            // Query the data model

            // Ask for who are typed as Agents.  Note that the people are
            // typed as foaf:Person, and the schema asserts that foaf:Person
            // is a subclass of foaf:Agent.
            Console.WriteLine("Who are Agents?");
            foreach (Entity r in dataModel.SelectSubjects(rdfType, foafAgent))
                Console.WriteLine("\t" + r);

            // Ask for the rdfs:labels of everyone.  Note that the data model
            // has foaf:names for the people, and the schema asserts that
            // foaf:name is a subproperty of rdfs:label.
            Console.WriteLine("People's labels:");
            foreach (Statement s in dataModel.Select(new Statement(null, rdfsLabel, null)))
                Console.WriteLine("\t" + s);
        }

    }

## General reasoning with the Euler engine

SemWeb has an adaptation of the Euler proof mechanism by Jos De Roo in the class `Euler` in the namespace `SemWeb.Inference`.

To use the class, create an instance of it and pass it a `StatementSource` with the rules of inference:

    string rules =
       "@prefix : . " +
       "{ ?a :oneway ?b } => { ?a :path ?b } . " +
       "{ ?a :path ?b . ?b :path ?c . } => { ?a :path ?c } . ";

    Euler engine = new Euler(new N3Reader(new StringReader(rules)));

Then form the question to be proved in the form a statement:

    Statement question = new Statement(paris, path, nantes);

One way to ask the question is to have the Euler engine return an array of proofs.

    Proof[] proofs = engine.Prove(dataModel, new Statement[] { question });
    foreach (Proof p in proofs)
       Console.WriteLine(p.ToString());

If it can't find a proof, a zero-length array is returned.

Alternatively, you may add the engine to a `Store` so that its `Contains`, `Select`, and `Query` methods make use of reasoning. With reasoning, the `Contains` method returns not just whether the statement is directly in the data model, but also whether it can be proved to be entailed by the data model.

    dataModel.AddReasoner(engine);
    Console.WriteLine("Euler Says the Question is: " + dataModel.Contains(question));

A complete example for using `Euler` is below:

    // This example demonstrates general reasoning with
    // the Euler engine based on Jos De Roo's Euler proof
    // mechanism.  The example is based on the "graph"
    // example from Euler.

    using System;
    using System.IO;

    using SemWeb;
    using SemWeb.Inference;

    public class EulerTest {

        public static void Main() {
            // Create the instance data

            MemoryStore dataModel = new MemoryStore();

            BNode paris = new BNode("paris");
            BNode orleans = new BNode("orleans");
            BNode chartres = new BNode("chartres");
            BNode amiens = new BNode("amiens");
            BNode blois = new BNode("blois");
            BNode bourges = new BNode("bourges");
            BNode tours = new BNode("tours");
            BNode lemans = new BNode("lemans");
            BNode angers = new BNode("angers");
            BNode nantes = new BNode("nantes");

            Entity oneway = new Entity("http://www.agfa.com/w3c/euler/graph.axiom#oneway");
            Entity path = new Entity("http://www.agfa.com/w3c/euler/graph.axiom#path");

            dataModel.Add(new Statement(paris, oneway, orleans));
            dataModel.Add(new Statement(paris, oneway, chartres));
            dataModel.Add(new Statement(paris, oneway, amiens));
            dataModel.Add(new Statement(orleans, oneway, blois));
            dataModel.Add(new Statement(orleans, oneway, bourges));
            dataModel.Add(new Statement(blois, oneway, tours));
            dataModel.Add(new Statement(chartres, oneway, lemans));
            dataModel.Add(new Statement(lemans, oneway, angers));
            dataModel.Add(new Statement(lemans, oneway, tours));
            dataModel.Add(new Statement(angers, oneway, nantes));

            // Create the inference rules by reading them from a N3 string.

            string rules =
                "@prefix : <http://www.agfa.com/w3c/euler/graph.axiom#>.\n" +
                "\n" +
                "{ ?a :oneway ?b } => { ?a :path ?b } .\n" +
                "{ ?a :path ?b . ?b :path ?c . } => { ?a :path ?c } .\n";

            // Create our question in the form of a statement to test.

            Statement question = new Statement(paris, path, nantes);

            // Create the Euler engine

            Euler engine = new Euler(new N3Reader(new StringReader(rules)));

            // First Method of Inference:
            // Ask the engine whether there is a path from paris to nantes.
            // The Prove method will return a list of proofs, or an empty
            // array if it could not find a proof.

            foreach (Proof p in engine.Prove(dataModel, new Statement[] { question })) {
                Console.WriteLine(p.ToString());
            }

            // Second Method of Inference:
            // Apply the engine to the data model and then use the data
            // model's Contains method to see if the statement is "in"
            // the model + reasoning.

            dataModel.AddReasoner(engine);

            Console.WriteLine("Euler Says the Question is: " + dataModel.Contains(question));

        }

    }