Search code examples
apiowlontology

Cannot retrieve inferences for named individual added via OWL API without reloading ontology


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.


Solution

  • 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 the OWLOntologyManager that manages the root ontology. The reasoner will listen to any OWLOntologyChanges and respond appropriately to them before answering any queries. If the BufferingMode of the reasoner (the answer to getBufferingMode() is BufferingMode.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 is BufferingMode.BUFFERING then ontology changes are stored in a buffer and are only taken into consideration when the buffer is flushed with the flush() method. When reasoning, axioms in the root ontology imports closure, minus the axioms returned by the getPendingAxiomAdditions() method, plus the axioms returned by the getPendingAxiomRemovals() 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:

    1. Call reasoner.flush() before you're asking the reasoner for inferences.
    2. Create a non-buffering reasoner, i.e. use reasonerFactory.createNonBufferingReasoner(ontology)