io.konig.core.OwlReasoner Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of konig-core Show documentation
Show all versions of konig-core Show documentation
A library for core classes (Graph, Vertex, Edge, etc.)
package io.konig.core;
import java.util.ArrayList;
/*
* #%L
* konig-core
* %%
* Copyright (C) 2015 - 2016 Gregory McFall
* %%
* Licensed 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.
* #L%
*/
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.openrdf.model.Resource;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.model.vocabulary.OWL;
import org.openrdf.model.vocabulary.RDF;
import org.openrdf.model.vocabulary.RDFS;
import org.openrdf.model.vocabulary.XMLSchema;
import io.konig.core.impl.RdfUtil;
import io.konig.core.vocab.Konig;
import io.konig.core.vocab.OwlVocab;
import io.konig.core.vocab.SH;
import io.konig.core.vocab.Schema;
import io.konig.core.vocab.XSD;
public class OwlReasoner {
private Graph graph;
private Map equivalentClassMap;
private Map datatypeMap;
private boolean inferredClassesFromSubclass;
public OwlReasoner(Graph graph) {
this.graph = graph;
}
public Graph getGraph() {
return graph;
}
public List owlClassList() {
return graph.v(OWL.CLASS).in(RDF.TYPE).toVertexList();
}
/**
* Ensure that every member within a set of equivalent classes contains
* an owl:equivalentClass property whose value is the preferred class.
*/
private void buildEquivalentClasses() {
if (equivalentClassMap == null) {
inferClassFromSubclassOf();
List list = graph.v(OWL.CLASS)
.union(RDFS.CLASS).union(RDFS.DATATYPE).union(Schema.DataType).union(RDFS.LITERAL)
.distinct().in(RDF.TYPE).distinct().toVertexList();
equivalentClassMap = new HashMap<>();
assembleEquivalenceClasses(list, equivalentClassMap);
}
}
public void inferRdfPropertiesFromPropertyConstraints(Graph sink) {
if (sink == null) {
sink = graph;
}
List shapeList = graph.v(SH.Shape).in(RDF.TYPE).toVertexList();
for (Vertex shape : shapeList) {
URI targetClass = shape.getURI(SH.targetClass);
List constraintList = shape.asTraversal().out(SH.property).toVertexList();
for (Vertex p : constraintList) {
Vertex property = p.getVertex(SH.predicate);
URI predicate = p.getURI(SH.predicate);
if (predicate != null) {
URI propertyType = property.getURI(RDF.TYPE);
URI range = property.getURI(RDFS.RANGE);
URI domain = property.getURI(RDFS.RANGE);
if (domain != null) {
targetClass = null;
}
URI datatype = p.getURI(SH.datatype);
if (datatype != null) {
URI type = propertyType==null ? OWL.DATATYPEPROPERTY : null;
edge(sink, predicate, RDF.TYPE, type);
edge(sink, predicate, Schema.domainIncludes, targetClass);
edge(sink, predicate, Schema.rangeIncludes, datatype);
} else {
URI valueClass = range==null ? p.getURI(SH.valueClass) : null;
URI type = propertyType==null ? OWL.OBJECTPROPERTY : null;
if (valueClass != null) {
edge(sink, predicate, RDF.TYPE, type);
edge(sink, predicate, Schema.domainIncludes, targetClass);
edge(sink, predicate, Schema.rangeIncludes, valueClass);
} else {
Vertex valueShape = p.getVertex(SH.shape);
if (valueShape != null) {
valueClass = valueShape.getURI(SH.targetClass);
if (valueClass != null) {
edge(sink, predicate, RDF.TYPE, type);
edge(sink, predicate, Schema.domainIncludes, targetClass);
edge(sink, predicate, Schema.rangeIncludes, valueClass);
} else {
edge(sink, predicate, RDF.TYPE, type);
edge(sink, predicate, Schema.domainIncludes, targetClass);
}
} else {
edge(sink, predicate, RDF.TYPE, type);
edge(sink, predicate, Schema.domainIncludes, targetClass);
}
}
}
}
}
}
}
private void edge(Graph sink, Resource subject, URI predicate, Value object) {
if (sink!=null && subject!=null && predicate!=null && object!=null) {
sink.edge(subject, predicate, object);
}
}
private void assembleEquivalenceClasses(List list, Map map) {
for (Vertex owlClass : list) {
analyzeEquivalentClasses(owlClass, map);
}
}
/**
* Compute the transitive closure of the equivalent classes of the specified owlClass
* @param owlClass The class from which the transitive closure will be computed
* @param map A map from the id for an owlClass to the EquivalenceClass to which it belongs.
*/
private void analyzeEquivalentClasses(Vertex owlClass, Map map) {
String key = owlClass.getId().stringValue();
EquivalenceClass e = map.get(key);
if (e == null) {
e = new EquivalenceClass();
map.put(key, e);
addMembers(e, owlClass, map);
}
}
private void addMembers(EquivalenceClass e, Vertex owlClass, Map map) {
Set set = e.getMembers();
set.add(owlClass);
List list = owlClass.asTraversal().out(OWL.EQUIVALENTCLASS).distinct().toVertexList();
for (Vertex v : list) {
String otherKey = v.getId().stringValue();
EquivalenceClass other = map.get(otherKey);
if (other == null) {
map.put(otherKey, e);
set.add(v);
addMembers(e, v, map);
} else if (other != e) {
map.put(otherKey, e);
e.addAll(other);
}
}
}
private static class EquivalenceClass {
private Set members = new HashSet<>();
private Vertex preferredEntity;
private boolean computedPreferredEntity;
public Vertex getPreferredEntity(URI preferredType) throws AmbiguousPreferredClassException {
if (!computedPreferredEntity) {
computedPreferredEntity = true;
for (Vertex v : members) {
if (v.hasProperty(RDF.TYPE, preferredType)) {
if (preferredEntity == null) {
preferredEntity = v;
} else {
throw new AmbiguousPreferredClassException(members);
}
}
}
}
return preferredEntity;
}
public Set getMembers() {
return members;
}
public void addAll(EquivalenceClass other) {
members.addAll(other.getMembers());
}
}
/**
* Identify the preferred class from the set of classes equivalent to a given class.
* @param owlClass The given OWL class that may or may not have equivalent classes.
* @return If there are classes equivalent to the given owlClass, then return the
* member from the set of equivalent classes that contains the kol:isPreferredClass
* with the value 'true'.
* @throws AmbiguousPreferredClassException
*/
public Vertex preferredClass(Vertex owlClass) throws AmbiguousPreferredClassException {
buildEquivalentClasses();
EquivalenceClass e = equivalentClassMap.get(owlClass.getId().stringValue());
if (e != null) {
Vertex result = e.getPreferredEntity(Konig.PreferredClass);
if (result != null) {
owlClass = result;
}
}
return owlClass;
}
public boolean isDatatype(Resource id) {
if (id instanceof URI) {
URI uri = (URI) id;
if (XMLSchema.NAMESPACE.equals(uri.getNamespace())) {
return true;
}
}
Vertex v = graph.getVertex(id);
if (v != null) {
boolean truth = !v.asTraversal().hasValue(RDF.TYPE, RDFS.DATATYPE).toVertexList().isEmpty();
if (truth) {
return true;
}
// TODO: apply other kinds of inference
}
return false;
}
public Set valueType(Vertex predicate) {
Set set = new HashSet<>();
set.addAll(predicate.asTraversal().out(RDFS.RANGE).toUriSet());
set.addAll(predicate.asTraversal().in(SH.predicate).out(SH.datatype).toUriSet());
set.addAll(predicate.asTraversal().in(SH.predicate).out(SH.valueClass).toUriSet());
set.addAll(predicate.asTraversal().in(SH.predicate).out(SH.shape).out(SH.targetClass).toUriSet());
return set;
}
/**
* Compute the least common super datatype between two given datatypes.
* @param aType The first class
* @param bType The second class
* @return The least common super class between the two classes.
*/
public Resource leastCommonSuperDatatype(Resource aType, Resource bType) {
Vertex a = graph.vertex(aType);
Vertex b = graph.vertex(bType);
if (RdfUtil.isSubClassOf(a, bType)) {
return bType;
}
if (RdfUtil.isSubClassOf(b, aType)) {
return aType;
}
Set set = superClasses(a);
List stack = b.asTraversal().out(RDFS.SUBCLASSOF).toVertexList();
for (int i=0; i set = superClasses(a);
List stack = b.asTraversal().out(RDFS.SUBCLASSOF).toVertexList();
for (int i=0; i superClasses(Vertex a) {
Set set = new HashSet<>();
List stack = new ArrayList<>();
stack.addAll(equivalentClasses(a.getId()));
for (int i=0; i next = w.asTraversal().out(RDFS.SUBCLASSOF).toVertexList();
for (Vertex v : next) {
Set eq = equivalentClasses(v.getId());
for (Vertex u : eq) {
if (!stack.contains(u)) {
stack.add(u);
if (u.getId() instanceof URI) {
set.add(u.getId().stringValue());
}
}
}
}
}
return set;
}
/**
* Check whether one type is a subClassOf another.
* @param a The identifier for one OWL Class
* @param b The identifier for another OWL Class.
* @return True if a and b are equal or a is a subClassOf b (or one of b's ancestors).
*/
public boolean isSubClassOf(Resource a, Resource b) {
if (a.equals(b)) {
return true;
}
Vertex va = graph.getVertex(a);
return RdfUtil.isSubClassOf(va, b);
}
public Set equivalentClasses(Resource owlClass) {
buildEquivalentClasses();
EquivalenceClass e = equivalentClassMap.get(owlClass.stringValue());
if (e == null) {
Vertex v = graph.vertex(owlClass);
Set result = new HashSet<>();
result.add(v);
return result;
}
return e.getMembers();
}
public Vertex preferredClass(Resource owlClass) throws AmbiguousPreferredClassException {
return preferredClass(graph.vertex(owlClass));
}
public URI preferredClassAsURI(URI owlClass) throws AmbiguousPreferredClassException {
return (URI) preferredClass(graph.vertex(owlClass)).getId();
}
public void inferTypeOfSuperClass(Vertex thing) {
Set typeSet = thing.asTraversal().out(RDF.TYPE).toUriSet();
Set superSet = new HashSet<>();
for (URI uri : typeSet) {
getTransitiveClosure(uri, RDFS.SUBCLASSOF, superSet);
}
for (URI superClass : superSet) {
graph.edge(thing.getId(), RDF.TYPE, superClass);
}
}
public void getTransitiveClosure(Resource source, URI predicate, Set sink) {
Vertex v = graph.getVertex(source);
if (v != null) {
Set out = v.outProperty(predicate);
for (Edge e : out) {
Value object = e.getObject();
if (object instanceof URI) {
URI uri = (URI) object;
if (!sink.contains(uri)) {
sink.add(uri);
getTransitiveClosure(uri, predicate, sink);
}
}
}
}
}
public URI mostSpecificType(Iterable collection, URI filter) {
URI best = null;
for (URI candidate : collection) {
if (filter != null && !candidate.equals(filter) && !isSubClassOf(candidate, filter)) {
continue;
}
if (best == null) {
best = candidate;
} else if (isSubClassOf(candidate, best)) {
best = candidate;
}
}
return best;
}
public void inferClassFromSubclassOf() {
if (!inferredClassesFromSubclass) {
inferredClassesFromSubclass = true;
List list = new ArrayList<>(graph);
for (Edge e : list) {
URI predicate = e.getPredicate();
if (predicate.equals(RDFS.SUBCLASSOF)) {
graph.edge(e.getSubject(), RDF.TYPE, OWL.CLASS);
graph.edge((Resource) e.getObject(), RDF.TYPE, OWL.CLASS);
}
}
}
}
/**
* Test whether a given Resource is an instance of a given OWL Class.
* This method checks the rdf:type relationship, and infers based on rdfs:subClassOf relationships.
*
* @param subject The Resource whose type is to be tested.
* @param owlClass The OWL Class to be matched
* @return True if the subject is an instance of the specified owlClass and false otherwise.
*/
public boolean instanceOf(Resource subject, URI owlClass) {
Vertex v = graph.getVertex(subject);
if (v != null) {
Set typeSet = v.asTraversal().out(RDF.TYPE).toUriSet();
for (URI type : typeSet) {
if (type.equals(owlClass)) {
return true;
}
}
Set superTypeSet = new HashSet<>();
for (URI type : typeSet) {
getTransitiveClosure(type, RDFS.SUBCLASSOF, superTypeSet);
}
for (URI superType : superTypeSet) {
if (superType.equals(owlClass)) {
return true;
}
}
}
return false;
}
public boolean isRealNumber(URI owlClass) {
return
XMLSchema.DECIMAL.equals(owlClass) ||
XMLSchema.DOUBLE.equals(owlClass) ||
XMLSchema.FLOAT.equals(owlClass) ||
Schema.Float.equals(owlClass) ||
Schema.Number.equals(owlClass);
}
public boolean isBooleanType(URI owlClass) {
return
XMLSchema.BOOLEAN.equals(owlClass) ||
Schema.Boolean.equals(owlClass);
}
public boolean isPlainLiteral(URI owlClass) {
return
XMLSchema.STRING.equals(owlClass) ||
Schema.Text.equals(owlClass);
}
public boolean isIntegerDatatype(URI owlClass) {
return
XMLSchema.BYTE.equals(owlClass) ||
XMLSchema.INT.equals(owlClass) ||
XMLSchema.INTEGER.equals(owlClass) ||
XMLSchema.LONG.equals(owlClass) ||
XMLSchema.NEGATIVE_INTEGER.equals(owlClass) ||
XMLSchema.NON_NEGATIVE_INTEGER.equals(owlClass) ||
XMLSchema.NON_POSITIVE_INTEGER.equals(owlClass) ||
XMLSchema.SHORT.equals(owlClass) ||
XMLSchema.UNSIGNED_BYTE.equals(owlClass) ||
XMLSchema.UNSIGNED_INT.equals(owlClass) ||
XMLSchema.UNSIGNED_LONG.equals(owlClass) ||
XMLSchema.UNSIGNED_SHORT.equals(owlClass) ||
Schema.Integer.equals(owlClass);
}
public boolean isEnumerationClass(URI owlClass) {
return graph.v(owlClass).hasValue(RDFS.SUBCLASSOF, Schema.Enumeration).size()>0;
}
public DatatypeRestriction datatypeRestriction(URI datatype) {
getDatatypeMap();
DatatypeRestriction result = datatypeMap.get(datatype.stringValue());
if (result == null) {
result = createDatatypeRestriction(datatype);
datatypeMap.put(datatype.stringValue(), result);
}
return result;
}
private DatatypeRestriction createDatatypeRestriction(URI datatype) {
Vertex v = graph.vertex(datatype);
DatatypeRestriction r = new DatatypeRestriction();
r.setOnDatatype(asURI(v.getValue(OwlVocab.onDatatype)));
Vertex withRestrictions = v.vertexValue(OwlVocab.withRestrictions);
if (withRestrictions != null) {
List list = withRestrictions.asList();
for (Value value : list) {
if (value instanceof Resource) {
Vertex w = graph.vertex((Resource)value);
Double maxExclusive = w.doubleValue(XSD.maxExclusive);
Double maxInclusive = w.doubleValue(XSD.maxInclusive);
Integer maxLength = w.integerValue(XSD.maxLength);
Double minExclusive = w.doubleValue(XSD.minExclusive);
Double minInclusive = w.doubleValue(XSD.minInclusive);
String pattern = w.stringValue(XSD.pattern);
if (maxExclusive != null) {
r.setMaxExclusive(maxExclusive);
}
if (maxInclusive != null) {
r.setMaxInclusive(maxInclusive);
}
if (maxLength != null) {
r.setMaxLength(maxLength);
}
if (minExclusive != null) {
r.setMinExclusive(minExclusive);
}
if (minInclusive != null) {
r.setMinInclusive(minInclusive);
}
if (pattern != null) {
r.setPattern(pattern);
}
}
}
}
return r;
}
private URI asURI(Value value) {
return value instanceof URI ? (URI)value : null;
}
private Map getDatatypeMap() {
if (datatypeMap == null) {
datatypeMap = new HashMap<>();
}
return datatypeMap;
}
}