In my application I need to add named individuals to an ontology. At a later point I need to be able to retrieve these named individuals and determine their inferred types, but for some reason I am not able to retrieve their types. I get either an exception or an empty set depending on the OWL reasoner I am using.
Here is a self contained example illustrating the problem:
package owl.api.test.StandaloneOWLNamedIndividualRetrievalv5;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Set;
import java.util.stream.Collectors;
import org.semanticweb.HermiT.ReasonerFactory;
import org.semanticweb.owlapi.apibinding.OWLManager;
import org.semanticweb.owlapi.model.IRI;
import org.semanticweb.owlapi.model.OWLClass;
import org.semanticweb.owlapi.model.OWLClassAssertionAxiom;
import org.semanticweb.owlapi.model.OWLDataFactory;
import org.semanticweb.owlapi.model.OWLIndividual;
import org.semanticweb.owlapi.model.OWLOntology;
import org.semanticweb.owlapi.model.OWLOntologyManager;
import org.semanticweb.owlapi.model.OWLOntologyStorageException;
import org.semanticweb.owlapi.model.PrefixManager;
import org.semanticweb.owlapi.model.parameters.ChangeApplied;
import org.semanticweb.owlapi.reasoner.NodeSet;
import org.semanticweb.owlapi.reasoner.OWLReasoner;
import org.semanticweb.owlapi.reasoner.OWLReasonerFactory;
import org.semanticweb.owlapi.search.EntitySearcher;
import org.semanticweb.owlapi.util.DefaultPrefixManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.Marker;
import org.slf4j.MarkerFactory;
import openllet.owlapi.OpenlletReasonerFactory;
import uk.ac.manchester.cs.jfact.JFactFactory;
public class App {
private static Logger logger = LoggerFactory
.getLogger(owl.api.test.StandaloneOWLNamedIndividualRetrievalv5.App.class);
// Why This Failure marker
private static final Marker WTF_MARKER = MarkerFactory.getMarker("WTF");
public static void main(String[] args) {
try {
// Setup physical IRI for storing ontology
Path path = Paths.get(".").toAbsolutePath().normalize();
IRI loadDocumentIRI = IRI.create("file:" + path.toFile().getAbsolutePath() + "/SimpleOntology.owl");
logger.trace("documentIRI=" + loadDocumentIRI);
IRI saveDocumentIRI = IRI.create("file:" + path.toFile().getAbsolutePath() + "/SimpleOntologyUpdated.owl");
logger.trace("documentIRI=" + saveDocumentIRI);
// Initialize
OWLOntologyManager manager = OWLManager.createOWLOntologyManager();
OWLDataFactory dataFactory = manager.getOWLDataFactory();
OWLOntology ontology = manager.loadOntologyFromOntologyDocument(loadDocumentIRI);
// OWLReasonerFactory reasonerFactory = new JFactFactory();
OWLReasonerFactory reasonerFactory = new ReasonerFactory();
// OWLReasonerFactory reasonerFactory = OpenlletReasonerFactory.getInstance();
OWLReasoner reasoner = reasonerFactory.createReasoner(ontology);
PrefixManager pm = new DefaultPrefixManager(ontology.getOntologyID().getOntologyIRI().get().getIRIString());
// Get references to a new named individual and an existing class
OWLIndividual individual = dataFactory.getOWLNamedIndividual("#ind1", pm);
OWLClass owlClass = dataFactory.getOWLClass("#ClassB", pm);
// Create class assertion axiom
OWLClassAssertionAxiom classAssertionAxiom = dataFactory.getOWLClassAssertionAxiom(owlClass, individual);
// Add class assertion axiom to ontology
ChangeApplied changeApplied = manager.addAxiom(ontology, classAssertionAxiom);
logger.trace("ChangeApplied = " + changeApplied);
if (changeApplied.equals(ChangeApplied.SUCCESSFULLY)) {
try {
manager.saveOntology(ontology, saveDocumentIRI);
} catch (OWLOntologyStorageException e) {
logger.error(e.getMessage());
}
}
// Now try to retrieve the individual
logger.trace(
"Trying to retrieve individual = " + classAssertionAxiom.getIndividual().asOWLNamedIndividual());
Set<Object> classExpressionTypes = EntitySearcher.getTypes(classAssertionAxiom.getIndividual(), ontology)
.collect(Collectors.toSet());
logger.trace("Individual = " + classAssertionAxiom.getIndividual() + " has types based on EntitySearcher "
+ classExpressionTypes);
NodeSet<OWLClass> types = reasoner.getTypes(classAssertionAxiom.getIndividual().asOWLNamedIndividual(),
false);
logger.trace("Individual = " + classAssertionAxiom.getIndividual()
+ " has types based on reasoner.getTypes " + types);
} catch (Throwable t) {
logger.error(WTF_MARKER, t.getMessage(), t);
}
}
}
Here is the Simple ontology I use to test against:
<?xml version="1.0"?>
<rdf:RDF xmlns="http://www.semanticweb.org/2017/simple#"
xml:base="http://www.semanticweb.org/2017/simple"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:owl="http://www.w3.org/2002/07/owl#"
xmlns:xml="http://www.w3.org/XML/1998/namespace"
xmlns:xsd="http://www.w3.org/2001/XMLSchema#"
xmlns:rdfs="http://www.w3.org/2000/01/rdf-schema#">
<owl:Ontology rdf:about="http://www.semanticweb.org/2017/simple"/>
<owl:Class rdf:about="http://www.semanticweb.org/2017/simple#ClassA"/>
<owl:Class rdf:about="http://www.semanticweb.org/2017/simple#ClassB">
<rdfs:subClassOf rdf:resource="http://www.semanticweb.org/2017/simple#ClassA"/>
</owl:Class>
</rdf:RDF>
After running this code I expect that it will determine that the types of individual ind1
are Thing
, ClassA
and ClassB
.
Thinking that this problem is related to perhaps to a specific OWL reasoner, I have tried using JFact
, HermiT
and Openllet
. JFact
throws a NullPointerException
, HermiT
only returns owl:Thing
and Openllet
nothing. However, when I save the changes to the ontology to file and reload it, I can find the inferred types of the individual I have added using any of these reasoners.
I have tested this with versions 5.1.2 and 4.5.0 of the OWL API. I have also tried calling reasoner.precomputeInferences()
even though the documentation states that this is not necessary, but it made no difference.
reasonerFactory.createReasoner(ontology)
creates a buffering reasoner, i.e. it has to be synchronized manually after you changed the ontology.
More details from the Javadoc:
Ontology Change Management (Buffering and Non-Buffering Modes)
At creation time, an
OWLReasoner
will load the axioms in the root ontology imports closure. It will attach itself as a listener to theOWLOntologyManager
that manages the root ontology. The reasoner will listen to anyOWLOntologyChanges
and respond appropriately to them before answering any queries. If theBufferingMode
of the reasoner (the answer togetBufferingMode()
isBufferingMode.NON_BUFFERING
) the ontology changes are processed by the reasoner immediately so that any queries asked after the changes are answered with respect to the changed ontologies. If the BufferingMode of the reasoner isBufferingMode.BUFFERING
then ontology changes are stored in a buffer and are only taken into consideration when the buffer is flushed with theflush()
method. When reasoning, axioms in the root ontology imports closure, minus the axioms returned by thegetPendingAxiomAdditions()
method, plus the axioms returned by thegetPendingAxiomRemovals()
are taken into consideration. Note that there is no guarantee that the reasoner implementation will respond to changes in an incremental (and efficient manner) manner.
Two options:
reasoner.flush()
before you're asking the reasoner for inferences.reasonerFactory.createNonBufferingReasoner(ontology)