Repository: incubator-rya Updated Branches: refs/heads/master 30475023d -> 85caccf41
RYA-297 Added owl:equivalentClass inference. Closes #184. If A and B are equivalent classes, then A is a subclass of B and B is a subclass of A. Handled the same way as owl:equivalentProperty. Project: http://git-wip-us.apache.org/repos/asf/incubator-rya/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-rya/commit/85caccf4 Tree: http://git-wip-us.apache.org/repos/asf/incubator-rya/tree/85caccf4 Diff: http://git-wip-us.apache.org/repos/asf/incubator-rya/diff/85caccf4 Branch: refs/heads/master Commit: 85caccf410ac53a8f6b21ef8bab9949cfa21728a Parents: 3047502 Author: Jesse Hatfield <[email protected]> Authored: Tue Jul 25 17:08:31 2017 -0400 Committer: Caleb Meier <[email protected]> Committed: Wed Aug 16 16:09:13 2017 -0700 ---------------------------------------------------------------------- .../inference/InferenceEngine.java | 84 +++++++++----------- .../inference/SubClassOfVisitor.java | 2 +- .../inference/InferenceEngineTest.java | 79 ++++++++++++++++++ .../rdftriplestore/inference/InferenceIT.java | 37 +++++++++ 4 files changed, 156 insertions(+), 46 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/85caccf4/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 2c2ba62..d0ff51b 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 @@ -127,22 +127,12 @@ public class InferenceEngine { public void refreshGraph() throws InferenceEngineException { ValueFactory vf = ValueFactoryImpl.getInstance(); try { + CloseableIteration<Statement, QueryEvaluationException> iter; //get all subclassof Graph graph = TinkerGraph.open(); - CloseableIteration<Statement, QueryEvaluationException> iter = RyaDAOHelper.query(ryaDAO, null, - RDFS.SUBCLASSOF, null, conf); - try { - while (iter.hasNext()) { - String edgeName = RDFS.SUBCLASSOF.stringValue(); - Statement st = iter.next(); - addStatementEdge(graph, edgeName, st); - } - } finally { - if (iter != null) { - iter.close(); - } - } - + addPredicateEdges(RDFS.SUBCLASSOF, Direction.OUT, graph, RDFS.SUBCLASSOF.stringValue()); + //equivalentClass is the same as subClassOf both ways + addPredicateEdges(OWL.EQUIVALENTCLASS, Direction.BOTH, graph, RDFS.SUBCLASSOF.stringValue()); // Add unions to the subclass graph: if c owl:unionOf LIST(c1, c2, ... cn), then any // instances of c1, c2, ... or cn are also instances of c, meaning c is a superclass // of all the rest. @@ -189,41 +179,12 @@ public class InferenceEngine { iter.close(); } } - subClassOfGraph = graph; //TODO: Should this be synchronized? graph = TinkerGraph.open(); - - iter = RyaDAOHelper.query(ryaDAO, null, - RDFS.SUBPROPERTYOF, null, conf); - try { - while (iter.hasNext()) { - String edgeName = RDFS.SUBPROPERTYOF.stringValue(); - Statement st = iter.next(); - addStatementEdge(graph, edgeName, st); - } - } finally { - if (iter != null) { - iter.close(); - } - } - + addPredicateEdges(RDFS.SUBPROPERTYOF, Direction.OUT, graph, RDFS.SUBPROPERTYOF.stringValue()); //equiv property really is the same as a subPropertyOf both ways - iter = RyaDAOHelper.query(ryaDAO, null, OWL.EQUIVALENTPROPERTY, null, conf); - try { - while (iter.hasNext()) { - String edgeName = RDFS.SUBPROPERTYOF.stringValue(); - Statement st = iter.next(); - addStatementEdge(graph, edgeName, st); - //reverse is also true - addStatementEdge(graph, edgeName, new StatementImpl((Resource) st.getObject(), st.getPredicate(), st.getSubject())); - } - } finally { - if (iter != null) { - iter.close(); - } - } - + addPredicateEdges(OWL.EQUIVALENTPROPERTY, Direction.BOTH, graph, RDFS.SUBPROPERTYOF.stringValue()); subPropertyOfGraph = graph; //TODO: Should this be synchronized? iter = RyaDAOHelper.query(ryaDAO, null, RDF.TYPE, OWL.SYMMETRICPROPERTY, conf); @@ -417,6 +378,39 @@ public class InferenceEngine { } } + /** + * Query for all triples involving a given predicate and add corresponding edges to a + * {@link Graph} in one or both directions. + * @param predicate Find all connections via this predicate URI + * @param dir Direction of interest: for a matching triple, if {@link Direction#OUT} add an edge + * from subject to object; if {@link Direction#IN} add an edge from object to subject; + * if {@link Direction#BOTH} add both. + * @param graph A TinkerPop graph + * @param edgeName Label that will be given to all added edges + * @throws QueryEvaluationException + */ + private void addPredicateEdges(URI predicate, Direction dir, Graph graph, String edgeName) + throws QueryEvaluationException { + CloseableIteration<Statement, QueryEvaluationException> iter = RyaDAOHelper.query(ryaDAO, + null, predicate, null, conf); + try { + while (iter.hasNext()) { + Statement st = iter.next(); + if (Direction.OUT.equals(dir) || Direction.BOTH.equals(dir)) { + addStatementEdge(graph, edgeName, st); + } + if (Direction.IN.equals(dir) || Direction.BOTH.equals(dir)) { + addStatementEdge(graph, edgeName, new StatementImpl((Resource) st.getObject(), + st.getPredicate(), st.getSubject())); + } + } + } finally { + if (iter != null) { + iter.close(); + } + } + } + private void refreshPropertyRestrictions() throws QueryEvaluationException { // Get a set of all property restrictions of any type CloseableIteration<Statement, QueryEvaluationException> iter = RyaDAOHelper.query(ryaDAO, null, OWL.ONPROPERTY, null, conf); http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/85caccf4/sail/src/main/java/org/apache/rya/rdftriplestore/inference/SubClassOfVisitor.java ---------------------------------------------------------------------- diff --git a/sail/src/main/java/org/apache/rya/rdftriplestore/inference/SubClassOfVisitor.java b/sail/src/main/java/org/apache/rya/rdftriplestore/inference/SubClassOfVisitor.java index 89d3b7b..7fb1b4a 100644 --- a/sail/src/main/java/org/apache/rya/rdftriplestore/inference/SubClassOfVisitor.java +++ b/sail/src/main/java/org/apache/rya/rdftriplestore/inference/SubClassOfVisitor.java @@ -68,7 +68,7 @@ public class SubClassOfVisitor extends AbstractInferVisitor { String s = UUID.randomUUID().toString(); Var typeVar = new Var(s); FixedStatementPattern fsp = new FixedStatementPattern(typeVar, new Var("c-" + s, RDFS.SUBCLASSOF), objVar, conVar); - fsp.statements.add(new NullableStatementImpl(subclassof_uri, RDFS.SUBCLASSOF, subclassof_uri)); + parents.add(subclassof_uri); for (URI u : parents) { fsp.statements.add(new NullableStatementImpl(u, RDFS.SUBCLASSOF, subclassof_uri)); } http://git-wip-us.apache.org/repos/asf/incubator-rya/blob/85caccf4/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 05adbc8..58aeb88 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 @@ -18,6 +18,7 @@ package org.apache.rya.rdftriplestore.inference; * under the License. */ +import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.Map; @@ -86,6 +87,84 @@ public class InferenceEngineTest extends TestCase { } @Test + public void testSubClassGraph() throws Exception { + String insert = "INSERT DATA { GRAPH <http://updated/test> {\n" + + " <urn:A> rdfs:subClassOf <urn:C> . \n" + + " <urn:B> rdfs:subClassOf <urn:C> . \n" + + " <urn:C> rdfs:subClassOf <urn:D> . \n" + + " <urn:E> owl:equivalentClass <urn:D> . \n" + + " <urn:E> rdfs:subClassOf <urn:G> . \n" + + " <urn:Z> a owl:Class . \n" + + " <urn:F> owl:equivalentClass <urn:G> . \n" + + "}}"; + conn.prepareUpdate(QueryLanguage.SPARQL, insert).execute(); + inferenceEngine.refreshGraph(); + Graph graph = inferenceEngine.getSubClassOfGraph(); + URI a = vf.createURI("urn:A"); + URI b = vf.createURI("urn:B"); + URI c = vf.createURI("urn:C"); + URI d = vf.createURI("urn:D"); + URI e = vf.createURI("urn:E"); + URI f = vf.createURI("urn:F"); + URI g = vf.createURI("urn:G"); + URI z = vf.createURI("urn:Z"); + URI missing = vf.createURI("urn:Missing"); + Set<URI> empty = new HashSet<>(); + Set<URI> belowLevel2 = new HashSet<>(Arrays.asList(new URI[] { a, b })); + Set<URI> belowLevel3 = new HashSet<>(Arrays.asList(new URI[] { a, b, c, d, e })); + Set<URI> belowLevel4 = new HashSet<>(Arrays.asList(new URI[] { a, b, c, d, e, f, g })); + Assert.assertEquals(empty, inferenceEngine.findParents(graph, a)); + Assert.assertEquals(empty, inferenceEngine.findParents(graph, b)); + Assert.assertEquals(empty, inferenceEngine.findParents(graph, z)); + Assert.assertEquals(empty, inferenceEngine.findParents(graph, missing)); + Assert.assertEquals(belowLevel2, inferenceEngine.findParents(graph, c)); + Assert.assertEquals(belowLevel3, inferenceEngine.findParents(graph, d)); + Assert.assertEquals(belowLevel3, inferenceEngine.findParents(graph, e)); + Assert.assertEquals(belowLevel4, inferenceEngine.findParents(graph, f)); + Assert.assertEquals(belowLevel4, inferenceEngine.findParents(graph, g)); + } + + @Test + public void testSubPropertyGraph() throws Exception { + String insert = "INSERT DATA { GRAPH <http://updated/test> {\n" + + " <urn:p> rdfs:subPropertyOf <urn:q> . \n" + + " <urn:p> rdfs:subPropertyOf <urn:r> . \n" + + " <urn:r> owl:equivalentProperty <urn:s> . \n" + + " <urn:q> rdfs:subPropertyOf <urn:t> . \n" + + " <urn:t> rdfs:subPropertyOf <urn:u> . \n" + + " <urn:s> rdfs:subPropertyOf <urn:u> . \n" + + " <urn:v> owl:equivalentProperty <urn:u> . \n" + + " <urn:w> a owl:FunctionalProperty . \n" + + "}}"; + conn.prepareUpdate(QueryLanguage.SPARQL, insert).execute(); + inferenceEngine.refreshGraph(); + Graph graph = inferenceEngine.getSubPropertyOfGraph(); + URI p = vf.createURI("urn:p"); + URI q = vf.createURI("urn:q"); + URI r = vf.createURI("urn:r"); + URI s = vf.createURI("urn:s"); + URI t = vf.createURI("urn:t"); + URI u = vf.createURI("urn:u"); + URI v = vf.createURI("urn:v"); + URI w = vf.createURI("urn:w"); + URI missing = vf.createURI("urn:Missing"); + Set<URI> empty = new HashSet<>(); + Set<URI> belowQ = new HashSet<>(Arrays.asList(new URI[] { p })); + Set<URI> belowR = new HashSet<>(Arrays.asList(new URI[] { p, r, s })); + Set<URI> belowT = new HashSet<>(Arrays.asList(new URI[] { p, q })); + Set<URI> belowU = new HashSet<>(Arrays.asList(new URI[] { p, q, r, s, t, u, v })); + Assert.assertEquals(empty, inferenceEngine.findParents(graph, p)); + Assert.assertEquals(empty, inferenceEngine.findParents(graph, w)); + Assert.assertEquals(empty, inferenceEngine.findParents(graph, missing)); + Assert.assertEquals(belowQ, inferenceEngine.findParents(graph, q)); + Assert.assertEquals(belowR, inferenceEngine.findParents(graph, r)); + Assert.assertEquals(belowR, inferenceEngine.findParents(graph, s)); + Assert.assertEquals(belowT, inferenceEngine.findParents(graph, t)); + Assert.assertEquals(belowU, inferenceEngine.findParents(graph, u)); + Assert.assertEquals(belowU, inferenceEngine.findParents(graph, v)); + } + + @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/85caccf4/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 d3f2faf..6824751 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 @@ -35,6 +35,7 @@ import org.junit.Test; import org.openrdf.model.Value; import org.openrdf.model.ValueFactory; import org.openrdf.model.impl.ValueFactoryImpl; +import org.openrdf.model.vocabulary.FOAF; import org.openrdf.query.BindingSet; import org.openrdf.query.QueryLanguage; import org.openrdf.query.QueryResultHandlerException; @@ -105,6 +106,42 @@ public class InferenceIT extends TestCase { } @Test + public void testSubClassInferenceQuery() throws Exception { + final String ontology = "INSERT DATA { GRAPH <http://updated/test> {\n" + + " <urn:Agent> owl:equivalentClass <http://dbpedia.org/ontology/Agent> . \n" + + " <urn:Person> rdfs:subClassOf <urn:Agent> . \n" + + " [ owl:equivalentClass <http://schema.org/Person> ] rdfs:subClassOf <http://dbpedia.org/ontology/Agent> . \n" + + " <" + FOAF.PERSON.stringValue() + "> owl:equivalentClass <http://dbpedia.org/ontology/Person> . \n" + + " <" + FOAF.PERSON.stringValue() + "> owl:equivalentClass <urn:Person> . \n" + + " <http://dbpedia.org/ontology/Engineer> rdfs:subClassOf <http://dbpedia.org/ontology/Person> . \n" + + " <http://dbpedia.org/ontology/Engineer> rdfs:subClassOf <http://example.org/Person> . \n" + + " <http://dbpedia.org/ontology/Engineer> owl:equivalentClass <http://www.wikidata.org/entity/Q81096> . \n" + + "}}"; + final String instances = "INSERT DATA { GRAPH <http://updated/test> {\n" + + " <urn:Alice> a <http://schema.org/Person> . \n" + + " <urn:Bob> a <http://www.wikidata.org/entity/Q81096> . \n" + + " <urn:Carol> a <http://example.org/Person> . \n" + + " <urn:Dan> a <http://example.org/Engineer> . \n" + + " <urn:Eve> a <urn:Agent> . \n" + + "}}"; + final String query = "SELECT ?x { GRAPH <http://updated/test> { ?x a <urn:Agent> } } \n"; + conn.prepareUpdate(QueryLanguage.SPARQL, ontology).execute(); + inferenceEngine.refreshGraph(); + conn.prepareUpdate(QueryLanguage.SPARQL, instances).execute(); + conn.prepareTupleQuery(QueryLanguage.SPARQL, query).evaluate(resultHandler); + Set<Value> expected = new HashSet<>(); + expected.add(vf.createURI("urn:Alice")); + expected.add(vf.createURI("urn:Bob")); + expected.add(vf.createURI("urn:Eve")); + Set<Value> returned = new HashSet<>(); + for (BindingSet bs : solutions) { + returned.add(bs.getBinding("x").getValue()); + } + Assert.assertEquals(expected, returned); + Assert.assertEquals(expected.size(), solutions.size()); + } + + @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"
