javaintellij-ideaowlowl-apiowlready

Explanations in Consistent OWL Ontologies


I want to programmatically get explanations for inferred axioms in consistent ontologies, in a similar manner that one can do in the Protégé UI. I cannot find any straightforward way. I have found the owlexplanation repo, but I cannot for the life of me solve the dependency issues to set up the owlexplanation environment. I have also browsed the javadoc of owlapi regarding explanations (to avoid the other repo altogether), but I don't see anything useful beyond what I can already see browsing the Java source code.

I have thought of simply negating the inferred axiom, to get explanations through inconsistencies, but I would prefer something cleaner, and I am not sure this approach is correct anyway.

Other (possibly) useful context:

I would appreciate any insight or tips.

UPDATE Jan 6, 2022:

I decided to try once more with the owlexplanation code with a clean head so here is where I am at:

UPDATE Jan 8, 2022: (Trying @Ignazio's answer)

I created a new IntelliJ project, and added the Maven dependencies @Ignazio mentioned (plus some others like slf4j etc) and I got a working example (I think). Moving to my main project (using JPype), I had to manually download some .jars to include in the classpath (as maven can't be used here). These are the ones downloaded so far:

caffeine-3.0.5.jar         hppcrt-0.7.5.jar    org.semanticweb.hermit-1.4.5.519.jar  slf4j-api-1.7.32.jar
commons-rdf-api-0.5.0.jar  javax.inject-1.jar  owlapi-distribution-5.1.9.jar         slf4j-nop-1.7.32.jar
google-collect-1.0.jar     owlexplanation-5.0.0.jar

Next, a NullPointerException is thrown when trying to use loadOntologyFromOntologyDocument(). I have tried re-downloading the jars as proposed here, but the Exception remains. Could it be that some .jar is missing? I downloaded them based on the thrown NoClassDefFoundError that would occur.

This occurs with a common pizza.owl file that it is otherwise fully working.

EDIT: I used mvn dependency:copy-dependencies -DoutputDirectory=OUTPUT_DIR to get the dependencies and by using OUTPUT_DIR as the classpath, the NullPointerException is gone, so it seems I was indeed missing some .jar file.

For the record, I then got other issues (gen.getExplanations() was throwing a NoSuchMethodError error), but I have no more time for debugging this. I will ditch JPype, however convenient it is, and simply call Java from Python with subprocess. These are (I guess) Jpype problems, so I am accepting Ignazio's answer as it solved my Java/OWL API/owlexplanation side of things.


Solution

  • You're not just using the projects but actually building them from scratch, which requires more setup than using the published artifacts.

    Shortcut that uses Maven available jars (via Maven Central, although other public repositories should do just as well)

    Java code:

        import java.io.File;
        import java.util.Set;
        import java.util.function.Supplier;
        
        import org.junit.Test;
        import org.semanticweb.HermiT.ReasonerFactory;
        import org.semanticweb.owl.explanation.api.Explanation;
        import org.semanticweb.owl.explanation.api.ExplanationGenerator;
        import org.semanticweb.owl.explanation.api.ExplanationGeneratorFactory;
        import org.semanticweb.owl.explanation.api.ExplanationProgressMonitor;
        import org.semanticweb.owl.explanation.impl.blackbox.Configuration;
        import org.semanticweb.owl.explanation.impl.blackbox.DivideAndConquerContractionStrategy;
        import org.semanticweb.owl.explanation.impl.blackbox.EntailmentCheckerFactory;
        import org.semanticweb.owl.explanation.impl.blackbox.InitialEntailmentCheckStrategy;
        import org.semanticweb.owl.explanation.impl.blackbox.StructuralTypePriorityExpansionStrategy;
        import org.semanticweb.owl.explanation.impl.blackbox.checker.BlackBoxExplanationGeneratorFactory;
        import org.semanticweb.owl.explanation.impl.blackbox.checker.SatisfiabilityEntailmentCheckerFactory;
        import org.semanticweb.owlapi.apibinding.OWLManager;
        import org.semanticweb.owlapi.model.OWLAxiom;
        import org.semanticweb.owlapi.model.OWLOntology;
        import org.semanticweb.owlapi.model.OWLOntologyManager;
        import org.semanticweb.owlapi.reasoner.OWLReasonerFactory;
        
        public class CheckOntology {
        
            @Test
            public void should() throws Exception {
        
                OWLOntologyManager m = OWLManager.createOWLOntologyManager();
                OWLOntology o = m.loadOntologyFromOntologyDocument(new File("pizza.owl"));
        
                OWLReasonerFactory rf = new ReasonerFactory(); // Get hold of a reasoner factory
        
                // Create the explanation generator factory which uses reasoners provided by the specified
                // reasoner factory
        
                ExplanationGeneratorFactory<OWLAxiom> genFac =
                    createExplanationGeneratorFactory(rf, null, OWLManager::createOWLOntologyManager);
        
                // Now create the actual explanation generator for our ontology
                ExplanationGenerator<OWLAxiom> gen = genFac.createExplanationGenerator(o);
        
        
                // Ask for explanations for some entailment
                // Get a reference to the axiom that represents the entailment that we want explanation for
                
                // this will just run the explanations for all axioms
                o.logicalAxioms().forEach(e -> explain(e, gen));
            }
        
            void explain(OWLAxiom entailment, ExplanationGenerator<OWLAxiom> gen) {
                // Get our explanations. Ask for a maximum of 5.
                try {
                    Set<Explanation<OWLAxiom>> expl = gen.getExplanations(entailment, 5);
                    System.out.println("CheckOntology.explain() " + entailment);
                    expl.forEach(System.out::println);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        // this method replicates code existing in the owlexplanation project; it's needed because the factories in owlexplanation do not set InitialEntailmentCheckStrategy correctly
            public static ExplanationGeneratorFactory<OWLAxiom> createExplanationGeneratorFactory(
                OWLReasonerFactory reasonerFactory, ExplanationProgressMonitor<OWLAxiom> progressMonitor,
                Supplier<OWLOntologyManager> m) {
                EntailmentCheckerFactory<OWLAxiom> checker =
                    new SatisfiabilityEntailmentCheckerFactory(reasonerFactory, m);
                Configuration<OWLAxiom> config = new Configuration<>(checker,
                    new StructuralTypePriorityExpansionStrategy<OWLAxiom>(
                        InitialEntailmentCheckStrategy.PERFORM, m),
                    new DivideAndConquerContractionStrategy<OWLAxiom>(), progressMonitor, m);
                return new BlackBoxExplanationGeneratorFactory<>(config);
            }
        }
    

    Maven configuration:

    4.0.0 blah blah 0.0.1-SNAPSHOT blah junit junit 4.12 net.sourceforge.owlapi org.semanticweb.hermit 1.4.5.519 net.sourceforge.owlapi owlexplanation 5.0.0

    HermiT is available on Maven Central via release builds I've made - the patch version refers to the OWLAPI version it was built with. Here 1.4.5.519 means HermiT was built with OWLAPI 5.1.9. owlexplanation 5.0.0 was built with OWLAPI 5, so it's the same major OWLAPI version for the project.

    This code sample doesn't do a lot because it just looks for explanations of asserted axioms. Often the explanation is just the axiom itself, as it's asserted. Varying the chosen axiom, or using axioms not asserted in the ontology, should give you the explanations you are after.