You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@rya.apache.org by ca...@apache.org on 2017/08/19 02:04:24 UTC

incubator-rya git commit: RYA-295 owl:allValuesFrom inference. Closes #201.

Repository: incubator-rya
Updated Branches:
  refs/heads/master 0d80871ff -> 2e88f1050


RYA-295 owl:allValuesFrom inference. Closes #201.


Project: http://git-wip-us.apache.org/repos/asf/incubator-rya/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-rya/commit/2e88f105
Tree: http://git-wip-us.apache.org/repos/asf/incubator-rya/tree/2e88f105
Diff: http://git-wip-us.apache.org/repos/asf/incubator-rya/diff/2e88f105

Branch: refs/heads/master
Commit: 2e88f1050b095ade37586562398cd02e162da639
Parents: 0d80871
Author: Jesse Hatfield <je...@parsons.com>
Authored: Tue Aug 8 15:58:10 2017 -0400
Committer: Caleb Meier <ca...@parsons.com>
Committed: Fri Aug 18 19:03:46 2017 -0700

----------------------------------------------------------------------
 .../src/main/java/MongoRyaDirectExample.java    |  52 ++++++++
 .../RdfCloudTripleStoreConnection.java          |   2 +
 .../inference/AllValuesFromVisitor.java         | 112 ++++++++++++++++
 .../inference/InferenceEngine.java              |  81 +++++++++++-
 .../inference/AllValuesFromVisitorTest.java     | 128 +++++++++++++++++++
 .../inference/InferenceEngineTest.java          |  31 +++++
 .../rdftriplestore/inference/InferenceIT.java   |  37 ++++++
 7 files changed, 440 insertions(+), 3 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/2e88f105/extras/indexingExample/src/main/java/MongoRyaDirectExample.java
----------------------------------------------------------------------
diff --git a/extras/indexingExample/src/main/java/MongoRyaDirectExample.java b/extras/indexingExample/src/main/java/MongoRyaDirectExample.java
index 32de913..1294a44 100644
--- a/extras/indexingExample/src/main/java/MongoRyaDirectExample.java
+++ b/extras/indexingExample/src/main/java/MongoRyaDirectExample.java
@@ -26,6 +26,7 @@ import org.apache.log4j.Logger;
 import org.openrdf.model.Namespace;
 import org.openrdf.model.URI;
 import org.openrdf.model.ValueFactory;
+import org.openrdf.model.vocabulary.OWL;
 import org.openrdf.model.vocabulary.RDF;
 import org.openrdf.model.vocabulary.RDFS;
 import org.openrdf.query.BindingSet;
@@ -98,6 +99,7 @@ public class MongoRyaDirectExample {
             	testInfer(conn, sail);
             	testPropertyChainInference(conn, sail);
             	testPropertyChainInferenceAltRepresentation(conn, sail);
+            	testAllValuesFromInference(conn, sail);
             }
 
             log.info("TIME: " + (System.currentTimeMillis() - start) / 1000.);
@@ -446,6 +448,56 @@ public class MongoRyaDirectExample {
     	log.info("Result count : " + resultHandler.getCount());
 
     }
+
+    public static void testAllValuesFromInference(SailRepositoryConnection conn, Sail sail) throws MalformedQueryException, RepositoryException,
+    UpdateExecutionException, QueryEvaluationException, TupleQueryResultHandlerException, InferenceEngineException {
+        log.info("Adding Data");
+        String insert = "INSERT DATA\n"
+                + "{ GRAPH <http://updated/test> {\n"
+                + "  <urn:Alice> a <urn:Person> .\n"
+                + "  <urn:Alice> <urn:hasParent> <urn:Bob> .\n"
+                + "  <urn:Carol> <urn:hasParent> <urn:Dan> .\n"
+                + "}}";
+        Update update = conn.prepareUpdate(QueryLanguage.SPARQL, insert);
+        update.execute();
+        final String inferQuery = "select distinct ?x { GRAPH <http://updated/test> { ?x a <urn:Person> }}";
+        final String explicitQuery = "select distinct ?x { GRAPH <http://updated/test> {\n"
+                + "  { ?x a <urn:Person> }\n"
+                + "  UNION {\n"
+                + "    ?y a <urn:Person> .\n"
+                + "    ?y <urn:hasParent> ?x .\n"
+                + "  }\n"
+                + "}}";
+        log.info("Running Explicit Query");
+        CountingResultHandler resultHandler = new CountingResultHandler();
+        TupleQuery tupleQuery = conn.prepareTupleQuery(QueryLanguage.SPARQL, explicitQuery);
+        tupleQuery.evaluate(resultHandler);
+        log.info("Result count : " + resultHandler.getCount());
+        Validate.isTrue(resultHandler.getCount() == 2);
+        log.info("Running Inference-dependent Query");
+        resultHandler.resetCount();
+        tupleQuery = conn.prepareTupleQuery(QueryLanguage.SPARQL, inferQuery);
+        tupleQuery.evaluate(resultHandler);
+        log.info("Result count : " + resultHandler.getCount());
+        Validate.isTrue(resultHandler.getCount() == 1);
+        log.info("Adding owl:allValuesFrom Schema");
+        insert = "PREFIX rdfs: <" + RDFS.NAMESPACE + ">\n"
+                + "PREFIX owl: <" + OWL.NAMESPACE + ">\n"
+                + "INSERT DATA\n"
+                + "{ GRAPH <http://updated/test> {\n"
+                + "  <urn:Person> rdfs:subClassOf [ owl:onProperty <urn:hasParent> ; owl:allValuesFrom <urn:Person> ] ."
+                + "}}";
+        update = conn.prepareUpdate(QueryLanguage.SPARQL, insert);
+        update.execute();
+        log.info("Refreshing InferenceEngine");
+        ((RdfCloudTripleStore) sail).getInferenceEngine().refreshGraph();
+        log.info("Re-running Inference-dependent Query");
+        resultHandler.resetCount();
+        tupleQuery = conn.prepareTupleQuery(QueryLanguage.SPARQL, inferQuery);
+        tupleQuery.evaluate(resultHandler);
+        log.info("Result count : " + resultHandler.getCount());
+        Validate.isTrue(resultHandler.getCount() == 2);
+    }
     
     public static void testInfer(SailRepositoryConnection conn, Sail sail) throws MalformedQueryException, RepositoryException, 
     UpdateExecutionException, QueryEvaluationException, TupleQueryResultHandlerException, InferenceEngineException {

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/2e88f105/sail/src/main/java/org/apache/rya/rdftriplestore/RdfCloudTripleStoreConnection.java
----------------------------------------------------------------------
diff --git a/sail/src/main/java/org/apache/rya/rdftriplestore/RdfCloudTripleStoreConnection.java b/sail/src/main/java/org/apache/rya/rdftriplestore/RdfCloudTripleStoreConnection.java
index 483d4ee..ec93da8 100644
--- a/sail/src/main/java/org/apache/rya/rdftriplestore/RdfCloudTripleStoreConnection.java
+++ b/sail/src/main/java/org/apache/rya/rdftriplestore/RdfCloudTripleStoreConnection.java
@@ -50,6 +50,7 @@ import org.apache.rya.rdftriplestore.evaluation.QueryJoinSelectOptimizer;
 import org.apache.rya.rdftriplestore.evaluation.RdfCloudTripleStoreEvaluationStatistics;
 import org.apache.rya.rdftriplestore.evaluation.RdfCloudTripleStoreSelectivityEvaluationStatistics;
 import org.apache.rya.rdftriplestore.evaluation.SeparateFilterJoinsVisitor;
+import org.apache.rya.rdftriplestore.inference.AllValuesFromVisitor;
 import org.apache.rya.rdftriplestore.inference.DomainRangeVisitor;
 import org.apache.rya.rdftriplestore.inference.HasValueVisitor;
 import org.apache.rya.rdftriplestore.inference.InferenceEngine;
@@ -351,6 +352,7 @@ public class RdfCloudTripleStoreConnection extends SailConnectionBase {
                     ) {
                 try {
                     tupleExpr.visit(new DomainRangeVisitor(queryConf, inferenceEngine));
+                    tupleExpr.visit(new AllValuesFromVisitor(queryConf, inferenceEngine));
                     tupleExpr.visit(new HasValueVisitor(queryConf, inferenceEngine));
                     tupleExpr.visit(new PropertyChainVisitor(queryConf, inferenceEngine));
                     tupleExpr.visit(new TransitivePropertyVisitor(queryConf, inferenceEngine));

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/2e88f105/sail/src/main/java/org/apache/rya/rdftriplestore/inference/AllValuesFromVisitor.java
----------------------------------------------------------------------
diff --git a/sail/src/main/java/org/apache/rya/rdftriplestore/inference/AllValuesFromVisitor.java b/sail/src/main/java/org/apache/rya/rdftriplestore/inference/AllValuesFromVisitor.java
new file mode 100644
index 0000000..9fc297a
--- /dev/null
+++ b/sail/src/main/java/org/apache/rya/rdftriplestore/inference/AllValuesFromVisitor.java
@@ -0,0 +1,112 @@
+package org.apache.rya.rdftriplestore.inference;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+import org.apache.rya.api.RdfCloudTripleStoreConfiguration;
+import org.apache.rya.api.utils.NullableStatementImpl;
+import org.apache.rya.rdftriplestore.utils.FixedStatementPattern;
+import org.openrdf.model.Resource;
+import org.openrdf.model.URI;
+import org.openrdf.model.vocabulary.OWL;
+import org.openrdf.model.vocabulary.RDF;
+import org.openrdf.query.algebra.StatementPattern;
+import org.openrdf.query.algebra.Var;
+
+/**
+ * Expands the query tree to account for any universal class expressions (property restrictions
+ * using owl:allValuesFrom) in the ontology known to the {@link InferenceEngine}.
+ *
+ * Operates on {@link StatementPattern} nodes whose predicate is rdf:type and whose object is a
+ * defined type (not a variable) which is related to an allValuesFrom expression in the ontology.
+ * When applicable, replaces the node with a union of itself and a subtree that matches any
+ * instance that can be inferred to have the type in question via the semantics of
+ * owl:allValuesFrom.
+ *
+ * A universal class expression references a predicate and a value class, and represents the set of
+ * individuals who, for any instance of the predicate, have a value belonging to the value class.
+ * Therefore, the value class should be inferred for any individual which is the object of a triple
+ * with that predicate and with a subject belonging to the class expression. This implication is
+ * similar to rdfs:range except that it only applies when the subject of the triple belongs to a
+ * specific type.
+ *
+ * (Note: Because of OWL's open world assumption, the inference in the other direction can't be
+ * made. That is, when an individual is explicitly declared to have the universally quantified
+ * restriction, then the types of its values can be inferred. But when the universal restriction
+ * isn't explicitly stated, it can't be inferred from the values themselves, because there's no
+ * guarantee that all values are known.)
+ */
+public class AllValuesFromVisitor extends AbstractInferVisitor {
+
+    /**
+     * Creates a new {@link AllValuesFromVisitor}, which is enabled by default.
+     * @param conf The {@link RdfCloudTripleStoreConfiguration}.
+     * @param inferenceEngine The InferenceEngine containing the relevant ontology.
+     */
+    public AllValuesFromVisitor(RdfCloudTripleStoreConfiguration conf, InferenceEngine inferenceEngine) {
+        super(conf, inferenceEngine);
+        include = true;
+    }
+
+    /**
+     * Checks whether the StatementPattern is a type query whose solutions could be inferred
+     * by allValuesFrom inference, and if so, replaces the node with a union of itself and any
+     * possible inference.
+     */
+    @Override
+    protected void meetSP(StatementPattern node) throws Exception {
+        final Var subjVar = node.getSubjectVar();
+        final Var predVar = node.getPredicateVar();
+        final Var objVar = node.getObjectVar();
+        // Only applies to type queries where the type is defined
+        if (predVar != null && RDF.TYPE.equals(predVar.getValue()) && objVar != null && objVar.getValue() instanceof Resource) {
+            final Resource typeToInfer = (Resource) objVar.getValue();
+            Map<Resource, Set<URI>> relevantAvfRestrictions = inferenceEngine.getAllValuesFromByValueType(typeToInfer);
+            if (!relevantAvfRestrictions.isEmpty()) {
+                // We can infer the queried type if, for an allValuesFrom restriction type
+                // associated  with the queried type, some anonymous neighboring node belongs to the
+                // restriction type and has the node in question (subjVar) as a value for the
+                // restriction's property.
+                final Var avfTypeVar = new Var("t-" + UUID.randomUUID());
+                final Var avfPredVar = new Var("p-" + UUID.randomUUID());
+                final Var neighborVar = new Var("n-" + UUID.randomUUID());
+                neighborVar.setAnonymous(true);
+                final StatementPattern membershipPattern = new DoNotExpandSP(neighborVar,
+                        new Var(RDF.TYPE.stringValue(), RDF.TYPE), avfTypeVar);
+                final StatementPattern valuePattern = new StatementPattern(neighborVar, avfPredVar, subjVar);
+                final InferJoin avfPattern = new InferJoin(membershipPattern, valuePattern);
+                // Use a FixedStatementPattern to contain the appropriate (restriction, predicate)
+                // pairs, and check each one against the general pattern.
+                final FixedStatementPattern avfPropertyTypes = new FixedStatementPattern(avfTypeVar,
+                        new Var(OWL.ONPROPERTY.stringValue(), OWL.ONPROPERTY), avfPredVar);
+                for (Resource avfRestrictionType : relevantAvfRestrictions.keySet()) {
+                    for (URI avfProperty : relevantAvfRestrictions.get(avfRestrictionType)) {
+                        avfPropertyTypes.statements.add(new NullableStatementImpl(avfRestrictionType,
+                                OWL.ONPROPERTY, avfProperty));
+                    }
+                }
+                final InferJoin avfInferenceQuery = new InferJoin(avfPropertyTypes, avfPattern);
+                node.replaceWith(new InferUnion(node.clone(), avfInferenceQuery));
+            }
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/2e88f105/sail/src/main/java/org/apache/rya/rdftriplestore/inference/InferenceEngine.java
----------------------------------------------------------------------
diff --git a/sail/src/main/java/org/apache/rya/rdftriplestore/inference/InferenceEngine.java b/sail/src/main/java/org/apache/rya/rdftriplestore/inference/InferenceEngine.java
index 43f00e0..05b847b 100644
--- a/sail/src/main/java/org/apache/rya/rdftriplestore/inference/InferenceEngine.java
+++ b/sail/src/main/java/org/apache/rya/rdftriplestore/inference/InferenceEngine.java
@@ -73,10 +73,11 @@ public class InferenceEngine {
     private Set<URI> symmetricPropertySet;
     private Map<URI, URI> inverseOfMap;
     private Set<URI> transitivePropertySet;
-    private Map<Resource, Map<URI, Value>> hasValueByType;
-    private Map<URI, Map<Resource, Value>> hasValueByProperty;
     private Map<URI, Set<URI>> domainByType;
     private Map<URI, Set<URI>> rangeByType;
+    private Map<Resource, Map<URI, Value>> hasValueByType;
+    private Map<URI, Map<Resource, Value>> hasValueByProperty;
+    private Map<Resource, Map<Resource, URI>> allValuesFromByValueType;
 
     private RyaDAO ryaDAO;
     private RdfCloudTripleStoreConfiguration conf;
@@ -589,8 +590,9 @@ public class InferenceEngine {
                 iter.close();
             }
         }
-        // Query for the hasValue restrictions and add them to the schema
+        // Query for specific types of restriction and add their details to the schema
         refreshHasValueRestrictions(restrictions);
+        refreshAllValuesFromRestrictions(restrictions);
     }
 
     private void refreshHasValueRestrictions(Map<Resource, URI> restrictions) throws QueryEvaluationException {
@@ -621,6 +623,36 @@ public class InferenceEngine {
         }
     }
 
+    private void refreshAllValuesFromRestrictions(Map<Resource, URI> restrictions) throws QueryEvaluationException {
+        allValuesFromByValueType = new HashMap<>();
+        CloseableIteration<Statement, QueryEvaluationException> iter = RyaDAOHelper.query(ryaDAO, null, OWL.ALLVALUESFROM, null, conf);
+        try {
+            while (iter.hasNext()) {
+                Statement st = iter.next();
+                if (restrictions.containsKey(st.getSubject()) && st.getObject() instanceof URI) {
+                    URI property = restrictions.get(st.getSubject());
+                    URI valueClass = (URI) st.getObject();
+                    // Should also be triggered by subclasses of the property restriction
+                    Set<Resource> restrictionClasses = new HashSet<>();
+                    restrictionClasses.add(st.getSubject());
+                    if (st.getSubject() instanceof URI) {
+                        restrictionClasses.addAll(findParents(subClassOfGraph, (URI) st.getSubject()));
+                    }
+                    for (Resource restrictionClass : restrictionClasses) {
+                        if (!allValuesFromByValueType.containsKey(valueClass)) {
+                            allValuesFromByValueType.put(valueClass, new HashMap<>());
+                        }
+                        allValuesFromByValueType.get(valueClass).put(restrictionClass, property);
+                    }
+                }
+            }
+        } finally {
+            if (iter != null) {
+                iter.close();
+            }
+        }
+    }
+
     private static Vertex getVertex(Graph graph, Object id) {
         Iterator<Vertex> it = graph.vertices(id.toString());
         if (it.hasNext()) {
@@ -947,4 +979,47 @@ public class InferenceEngine {
         }
         return properties;
     }
+
+    /**
+     * For a given type, return information about any owl:allValuesFrom restriction that could imply
+     * an individual's membership in that type: If the subject of a triple belongs to the type
+     * associated with the restriction itself, and the predicate is the one referenced by the
+     * restriction, then the object of the triple is implied to have the value type.
+     * @param valueType The type to be inferred, which is the type of the object of the triple, or
+     *      the type from which all values are stated to belong. Takes class hierarchy into account,
+     *      so possible inferences include any ways of inferring subtypes of the value type, and
+     *      subject types that trigger inference include any subtypes of relevant restrictions.
+     *      Also considers property hierarchy, so properties that trigger inference will include
+     *      subproperties of those referenced by relevant restrictions.
+     * @return A map from subject type (a property restriction type or a subtype of one) to the set
+     *      of properties (including any property referenced by such a restriction and all of its
+     *      subproperties) such that for any individual which belongs to the subject type, all
+     *      values it has for any of those properties belong to the value type.
+     */
+    public Map<Resource, Set<URI>> getAllValuesFromByValueType(Resource valueType) {
+        Map<Resource, Set<URI>> implications = new HashMap<>();
+        if (allValuesFromByValueType != null) {
+            // Check for any subtypes which would in turn imply the value type
+            HashSet<Resource> valueTypes = new HashSet<>();
+            valueTypes.add(valueType);
+            if (valueType instanceof URI) {
+                valueTypes.addAll(findParents(subClassOfGraph, (URI) valueType));
+            }
+            for (Resource valueSubType : valueTypes) {
+                if (allValuesFromByValueType.containsKey(valueSubType)) {
+                    Map<Resource, URI> restrictionToProperty = allValuesFromByValueType.get(valueSubType);
+                    for (Resource restrictionType : restrictionToProperty.keySet()) {
+                        if (!implications.containsKey(restrictionType)) {
+                            implications.put(restrictionType, new HashSet<>());
+                        }
+                        URI property = restrictionToProperty.get(restrictionType);
+                        implications.get(restrictionType).add(property);
+                        // Also add subproperties that would in turn imply the property
+                        implications.get(restrictionType).addAll(findParents(subPropertyOfGraph, property));
+                    }
+                }
+            }
+        }
+        return implications;
+    }
 }

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/2e88f105/sail/src/test/java/org/apache/rya/rdftriplestore/inference/AllValuesFromVisitorTest.java
----------------------------------------------------------------------
diff --git a/sail/src/test/java/org/apache/rya/rdftriplestore/inference/AllValuesFromVisitorTest.java b/sail/src/test/java/org/apache/rya/rdftriplestore/inference/AllValuesFromVisitorTest.java
new file mode 100644
index 0000000..d284d57
--- /dev/null
+++ b/sail/src/test/java/org/apache/rya/rdftriplestore/inference/AllValuesFromVisitorTest.java
@@ -0,0 +1,128 @@
+package org.apache.rya.rdftriplestore.inference;
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.rya.accumulo.AccumuloRdfConfiguration;
+import org.apache.rya.api.utils.NullableStatementImpl;
+import org.apache.rya.rdftriplestore.utils.FixedStatementPattern;
+import org.junit.Assert;
+import org.junit.Test;
+import org.openrdf.model.Resource;
+import org.openrdf.model.URI;
+import org.openrdf.model.ValueFactory;
+import org.openrdf.model.impl.ValueFactoryImpl;
+import org.openrdf.model.vocabulary.OWL;
+import org.openrdf.model.vocabulary.RDF;
+import org.openrdf.query.algebra.Join;
+import org.openrdf.query.algebra.Projection;
+import org.openrdf.query.algebra.ProjectionElem;
+import org.openrdf.query.algebra.ProjectionElemList;
+import org.openrdf.query.algebra.StatementPattern;
+import org.openrdf.query.algebra.TupleExpr;
+import org.openrdf.query.algebra.Union;
+import org.openrdf.query.algebra.Var;
+
+public class AllValuesFromVisitorTest {
+    private final AccumuloRdfConfiguration conf = new AccumuloRdfConfiguration();
+    private final ValueFactory vf = new ValueFactoryImpl();
+
+    // Value types
+    private final URI person = vf.createURI("urn:Person");
+    private final URI dog = vf.createURI("urn:Dog");
+    // Predicates
+    private final URI parent = vf.createURI("urn:parent");
+    private final URI relative = vf.createURI("urn:relative");
+    // Restriction types
+    private final URI parentsAreTallPeople = vf.createURI("urn:parentsAreTallPeople");
+    private final URI parentsArePeople = vf.createURI("urn:parentsArePeople");
+    private final URI relativesArePeople = vf.createURI("urn:relativesArePeople");
+    private final URI parentsAreDogs = vf.createURI("urn:parentsAreDogs");
+
+    @Test
+    public void testRewriteTypePattern() throws Exception {
+        // Configure a mock instance engine with an ontology:
+        final InferenceEngine inferenceEngine = mock(InferenceEngine.class);
+        Map<Resource, Set<URI>> personAVF = new HashMap<>();
+        personAVF.put(parentsAreTallPeople, new HashSet<>());
+        personAVF.put(parentsArePeople, new HashSet<>());
+        personAVF.put(relativesArePeople, new HashSet<>());
+        personAVF.get(parentsAreTallPeople).add(parent);
+        personAVF.get(parentsArePeople).add(parent);
+        personAVF.get(relativesArePeople).add(relative);
+        personAVF.get(relativesArePeople).add(parent);
+        Map<Resource, Set<URI>> dogAVF = new HashMap<>();
+        dogAVF.put(parentsAreDogs, new HashSet<>());
+        dogAVF.get(parentsAreDogs).add(parent);
+        when(inferenceEngine.getAllValuesFromByValueType(person)).thenReturn(personAVF);
+        when(inferenceEngine.getAllValuesFromByValueType(dog)).thenReturn(dogAVF);
+        // Query for a specific type and rewrite using the visitor:
+        StatementPattern originalSP = new StatementPattern(new Var("s"), new Var("p", RDF.TYPE), new Var("o", person));
+        final Projection query = new Projection(originalSP, new ProjectionElemList(new ProjectionElem("s", "subject")));
+        query.visit(new AllValuesFromVisitor(conf, inferenceEngine));
+        // Expected structure: a union of two elements: one is equal to the original statement
+        // pattern, and the other one joins a list of predicate/restriction type combinations
+        // with another join querying for values of that predicate for members of that type.
+        Assert.assertTrue(query.getArg() instanceof Union);
+        TupleExpr left = ((Union) query.getArg()).getLeftArg();
+        TupleExpr right = ((Union) query.getArg()).getRightArg();
+        final Join join;
+        if (left instanceof StatementPattern) {
+            Assert.assertEquals(originalSP, left);
+            Assert.assertTrue(right instanceof Join);
+            join = (Join) right;
+        }
+        else {
+            Assert.assertEquals(originalSP, right);
+            Assert.assertTrue(left instanceof Join);
+            join = (Join) left;
+        }
+        Assert.assertTrue(join.getLeftArg() instanceof FixedStatementPattern);
+        Assert.assertTrue(join.getRightArg() instanceof Join);
+        FixedStatementPattern fsp = (FixedStatementPattern) join.getLeftArg();
+        left = ((Join) join.getRightArg()).getLeftArg();
+        right = ((Join) join.getRightArg()).getRightArg();
+        Assert.assertTrue(left instanceof StatementPattern);
+        Assert.assertTrue(right instanceof StatementPattern);
+        // Verify expected predicate/restriction pairs
+        Assert.assertEquals(4, fsp.statements.size());
+        fsp.statements.contains(new NullableStatementImpl(parentsArePeople, OWL.ONPROPERTY, parent));
+        fsp.statements.contains(new NullableStatementImpl(relativesArePeople, OWL.ONPROPERTY, relative));
+        fsp.statements.contains(new NullableStatementImpl(relativesArePeople, OWL.ONPROPERTY, parent));
+        fsp.statements.contains(new NullableStatementImpl(parentsAreTallPeople, OWL.ONPROPERTY, parent));
+        // Verify general pattern for matching instances of each pair: Join on unknown subject; left
+        // triple states it belongs to the restriction while right triple relates it to the original
+        // subject variable by the relevant property. Restriction and property variables are given
+        // by the FixedStatementPattern.
+        StatementPattern leftSP = (StatementPattern) left;
+        StatementPattern rightSP = (StatementPattern) right;
+        Assert.assertEquals(rightSP.getSubjectVar(), leftSP.getSubjectVar());
+        Assert.assertEquals(RDF.TYPE, leftSP.getPredicateVar().getValue());
+        Assert.assertEquals(fsp.getSubjectVar(), leftSP.getObjectVar());
+        Assert.assertEquals(fsp.getObjectVar(), rightSP.getPredicateVar());
+        Assert.assertEquals(originalSP.getSubjectVar(), rightSP.getObjectVar());
+    }
+}

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/2e88f105/sail/src/test/java/org/apache/rya/rdftriplestore/inference/InferenceEngineTest.java
----------------------------------------------------------------------
diff --git a/sail/src/test/java/org/apache/rya/rdftriplestore/inference/InferenceEngineTest.java b/sail/src/test/java/org/apache/rya/rdftriplestore/inference/InferenceEngineTest.java
index 31ea3e8..556413f 100644
--- a/sail/src/test/java/org/apache/rya/rdftriplestore/inference/InferenceEngineTest.java
+++ b/sail/src/test/java/org/apache/rya/rdftriplestore/inference/InferenceEngineTest.java
@@ -225,6 +225,37 @@ public class InferenceEngineTest extends TestCase {
     }
 
     @Test
+    public void testAllValuesFrom() throws Exception {
+        String insert = "INSERT DATA { GRAPH <http://updated/test> {\n"
+                + "  <urn:Dog> owl:onProperty <urn:relative> ; owl:allValuesFrom <urn:Dog> .\n"
+                + "  <urn:Retriever> rdfs:subClassOf <urn:Dog> .\n"
+                + "  <urn:Terrier> rdfs:subClassOf <urn:Dog> .\n"
+                + "  <urn:Terrier> owl:onProperty <urn:relative> ; owl:allValuesFrom <urn:Terrier> .\n"
+                + "  <urn:Cairn_Terrier> rdfs:subClassOf <urn:Terrier> .\n"
+                + "  <urn:parent> rdfs:subPropertyOf <urn:relative> .\n"
+                + "  <urn:Dog> rdfs:subClassOf <urn:Mammal> .\n"
+                + "  <urn:Person> rdfs:subClassOf <urn:Mammal> .\n"
+                + "  <urn:Person> owl:onProperty <urn:relative> ; owl:allValuesFrom <urn:Person> .\n"
+                + "}}";
+        conn.prepareUpdate(QueryLanguage.SPARQL, insert).execute();
+        inferenceEngine.refreshGraph();
+        final Map<Resource, Set<URI>> restrictionsImplyingTerrier = new HashMap<>();
+        final Set<URI> properties = new HashSet<>();
+        properties.add(vf.createURI("urn:parent"));
+        properties.add(vf.createURI("urn:relative"));
+        restrictionsImplyingTerrier.put(vf.createURI("urn:Terrier"), properties);
+        restrictionsImplyingTerrier.put(vf.createURI("urn:Cairn_Terrier"), properties);
+        Assert.assertEquals(restrictionsImplyingTerrier, inferenceEngine.getAllValuesFromByValueType(vf.createURI("urn:Terrier")));
+        final Map<Resource, Set<URI>> restrictionsImplyingDog = new HashMap<>(restrictionsImplyingTerrier);
+        restrictionsImplyingDog.put(vf.createURI("urn:Dog"), properties);
+        restrictionsImplyingDog.put(vf.createURI("urn:Retriever"), properties);
+        Assert.assertEquals(restrictionsImplyingDog, inferenceEngine.getAllValuesFromByValueType(vf.createURI("urn:Dog")));
+        final Map<Resource, Set<URI>> restrictionsImplyingMammal = new HashMap<>(restrictionsImplyingDog);
+        restrictionsImplyingMammal.put(vf.createURI("urn:Person"), properties);
+        Assert.assertEquals(restrictionsImplyingMammal, inferenceEngine.getAllValuesFromByValueType(vf.createURI("urn:Mammal")));
+    }
+
+    @Test
     public void testHasValueGivenProperty() throws Exception {
         String insert = "INSERT DATA { GRAPH <http://updated/test> {\n"
                 + "  <urn:Biped> owl:onProperty <urn:walksUsingLegs>  . \n"

http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/2e88f105/sail/src/test/java/org/apache/rya/rdftriplestore/inference/InferenceIT.java
----------------------------------------------------------------------
diff --git a/sail/src/test/java/org/apache/rya/rdftriplestore/inference/InferenceIT.java b/sail/src/test/java/org/apache/rya/rdftriplestore/inference/InferenceIT.java
index c43c9e0..7d6f7d1 100644
--- a/sail/src/test/java/org/apache/rya/rdftriplestore/inference/InferenceIT.java
+++ b/sail/src/test/java/org/apache/rya/rdftriplestore/inference/InferenceIT.java
@@ -188,6 +188,43 @@ public class InferenceIT extends TestCase {
     }
 
     @Test
+    public void testAllValuesFromQuery() throws Exception {
+        final String ontology = "INSERT DATA { GRAPH <http://updated/test> {\n"
+                + "  <urn:Cairn_Terrier> rdfs:subClassOf <urn:Terrier> .\n"
+                + "  <urn:Terrier> rdfs:subClassOf <urn:Dog> ;\n"
+                + "    owl:onProperty <urn:relative> ;\n"
+                + "    owl:allValuesFrom <urn:Terrier> .\n"
+                + "  <urn:Dog> rdfs:subClassOf [\n"
+                + "    owl:onProperty <urn:portrays> ; owl:allValuesFrom <urn:FictionalDog>\n"
+                + "  ] .\n"
+                + "  <urn:parent> rdfs:subPropertyOf <urn:relative> .\n"
+                + "}}";
+        conn.prepareUpdate(QueryLanguage.SPARQL, ontology).execute();
+        inferenceEngine.refreshGraph();
+        final String instances = "INSERT DATA { GRAPH <http://updated/test> {\n"
+                + "  <urn:Rommy> a <urn:Cairn_Terrier> .\n"
+                + "  <urn:Rommy> <urn:parent> <urn:Terry> .\n"
+                + "  <urn:Terry> <urn:portrays> <urn:Toto> .\n"
+                + "}}";
+        conn.prepareUpdate(QueryLanguage.SPARQL, instances).execute();
+        conn.prepareTupleQuery(QueryLanguage.SPARQL, "SELECT ?x { ?x a <urn:Dog> }").evaluate(resultHandler);
+        Assert.assertEquals(2, solutions.size());
+        Set<Value> answers = new HashSet<>();
+        for (BindingSet solution : solutions) {
+            answers.add(solution.getBinding("x").getValue());
+        }
+        Assert.assertTrue(answers.contains(vf.createURI("urn:Terry")));
+        Assert.assertTrue(answers.contains(vf.createURI("urn:Rommy")));
+        // If allValuesFrom inference were applied recursively, this triple wouldn't be needed:
+        conn.prepareUpdate(QueryLanguage.SPARQL, "INSERT DATA { GRAPH <http://updated/test> {\n"
+                + "  <urn:Terry> a <urn:Cairn_Terrier> .\n"
+                + "}}").execute();
+        conn.prepareTupleQuery(QueryLanguage.SPARQL, "SELECT ?x { ?x a <urn:FictionalDog> }").evaluate(resultHandler);
+        Assert.assertEquals(1, solutions.size());
+        Assert.assertEquals(vf.createURI("urn:Toto"), solutions.get(0).getBinding("x").getValue());
+    }
+
+    @Test
     public void testHasValueTypeQuery() throws Exception {
         final String ontology = "INSERT DATA { GRAPH <http://updated/test> {\n"
                 + "  <urn:Biped> owl:onProperty <urn:walksOnLegs>  ; owl:hasValue \"2\"^^<xsd:integer> . \n"