Search code examples
owlowl-api

How to efficiently get inferred subclasses in OWL?


I'm using OWLAPI to distinguish between the subclasses that are simply asserted, from the ones that are inferred. My code works fine, but it is really slow:

for(OWLClass clazz : ontology.getClassesInSignature())
    {
        for(OWLClass child: ontology.getClassesInSignature())
            {
                if(reasoner.isSatisfiable(child) )
                {                   
                    NodeSet<OWLClass> subClasses = reasoner.getSubClasses(clazz, true);
                    OWLSubClassOfAxiom subAxiomTest = myModel.factory.getOWLSubClassOfAxiom(child, clazz);

                    if(!ontology.containsAxiom(subAxiomTest) && subClasses.containsEntity(child) && !clazz.isTopEntity())
                    {
                        //do something
                    }
                }
            }
        }

As you can see, the code is slow because I'm comparing each class with all the others for each iteration.

I can't find an easy way as

NodeSet<OWLClass> subClasses = reasoner.getSubClasses(clazz, true);

to get only the inferred ones.

Is there a way more efficient to do that?

EDIT:

Here my solution that greatly improves the performance. If someone has the same issue it could try this:

NodeSet<OWLClass> subClassesDerived = reasoner.getSubClasses(clazz, true);
        for (OWLClass child : subClassesDerived.getFlattened()) {
            if(reasoner.isSatisfiable(child) )
            {
                //Test axiom
                OWLAxiom subAxiomTest = myModel.factory.getOWLSubClassOfAxiom(child, clazz);
                if(!ontology.containsAxiom(subAxiomTest) && subClassesDerived.containsEntity(child) && !clazz.isTopEntity())
                {
                   //subclass derived found - DO SOMETHING
                }
            }
        }

Solution

  • There are a few approaches you can try. One way is to use an inferred axiom generator and set it to generate only subclass axioms. Used with reasoner and ontology, it will get you all inferrable subclass axioms. You can then check which ones appear in the original ontology and which ones do not.

    One thing to note: remove annotations before doing this, and before using younger approach - annotated axioms are equivalent to non annotated axioms but not equal. Leaving the annotations in will cause some false positives.

    You could also improve your algorithm, although the main cost will still be reasoning:

    • Check and skip cases where parent and child are the same class

    • Get the classes equivalent to owl:Nothing from the reasoner - these are all unsatisfiable. Skip them in the loops

    • Same for owl:Thing

    • Instead of getting subclasses for a single level (the true value you're passing in does that) get all subclasses of a class at once and cache those nodes. A number of children will be found there without reasoner calls. (Reasoners cache the class hierarchy, so this one might not be worth much)

    • Your loops will compare a and b, then b and a. If you find a is a superclass of b, don't check if b is a superclass of a.

    • Before creating axioms and calling the reasoner, check with the OWLOntology methods if your classes are asserted to be subclass of each other or equivalent. These are fast operations, relying on hashmap indexes.

    Edit: following on your second version, I would try the following tweaks:

        Node<OWLClass> unsatisfiableClasses = reasoner.getUnsatisfiableClasses();
        Set<OWLClass> visitedClasses = new HashSet<>();
        ontology.classesInSignature().filter(clazz -> !clazz.isBuiltIn())
            .filter(visitedClasses::add).filter(clazz -> !unsatisfiableClasses.contains(clazz))
            .forEach(clazz -> {
                NodeSet<OWLClass> subClassesDerived = reasoner.getSubClasses(clazz, true);
                // for all classes equivalent to clazz, the reasoner will return the same set of
                // results
                Node<OWLClass> equivalentClasses = reasoner.getEquivalentClasses(clazz);
                equivalentClasses.entities().forEach(c -> {
                    visitedClasses.add(c);
                    Set<OWLClassExpression> assertedChildren =
                        ontology.subClassAxiomsForSuperClass(c).map(OWLSubClassOfAxiom::getSubClass)
                            .filter(OWLClassExpression::isNamed).collect(Collectors.toSet());
                    subClassesDerived.entities()
                        .filter(child -> !unsatisfiableClasses.contains(child))
                        .filter(child -> !assertedChildren.contains(child)).forEach(child -> {
                            // subclass derived found - DO SOMETHING
                            // c is the superclass, child is the subclass
                        });
                });
            });