Reasoning

OWL reasoners can be used to check the consistency of an ontology, and to deduce new fact in the ontology, typically be reclassing Individuals to new Classes, and Classes to new superclasses, depending on their relations.

Several OWL reasoners exist; Owlready2 includes:

  • a modified version of the HermiT reasoner, developed by the department of Computer Science of the University of Oxford, and released under the LGPL licence.

  • a modified version of the Pellet reasoner, released under the AGPL licence.

HermiT and Pellet are written in Java, and thus you need a Java Vitual Machine to perform reasoning in Owlready2.

HermiT is used by default.

Configuration

Under Linux, Owlready should automatically find Java.

Under windows, you may need to configure the location of the Java interpreter, as follows:

>>> from owlready2 import *
>>> import owlready2
>>> owlready2.JAVA_EXE = "C:\\path\\to\\java.exe"

Setting up everything

Before performing reasoning, you need to create all Classes, Properties and Instances, and to ensure that restrictions and disjointnesses / differences have been defined too.

Here is an example creating a ‘reasoning-ready’ ontology:

>>> from owlready2 import *

>>> onto = get_ontology("http://test.org/onto.owl")

>>> with onto:
...     class Drug(Thing):
...         def take(self): print("I took a drug")

...     class ActivePrinciple(Thing):
...         pass

...     class has_for_active_principle(Drug >> ActivePrinciple):
...         python_name = "active_principles"

...     class Placebo(Drug):
...         equivalent_to = [Drug & Not(has_for_active_principle.some(ActivePrinciple))]
...         def take(self): print("I took a placebo")

...     class SingleActivePrincipleDrug(Drug):
...         equivalent_to = [Drug & has_for_active_principle.exactly(1, ActivePrinciple)]
...         def take(self): print("I took a drug with a single active principle")

...     class DrugAssociation(Drug):
...         equivalent_to = [Drug & has_for_active_principle.min(2, ActivePrinciple)]
...         def take(self): print("I took a drug with %s active principles" % len(self.active_principles))

>>> acetaminophen   = ActivePrinciple("acetaminophen")
>>> amoxicillin     = ActivePrinciple("amoxicillin")
>>> clavulanic_acid = ActivePrinciple("clavulanic_acid")

>>> AllDifferent([acetaminophen, amoxicillin, clavulanic_acid])

>>> drug1 = Drug(active_principles = [acetaminophen])
>>> drug2 = Drug(active_principles = [amoxicillin, clavulanic_acid])
>>> drug3 = Drug(active_principles = [])

>>> close_world(Drug)

Running the reasoner

The reasoner (HermiT) is simply run by calling the sync_reasoner() global function:

>>> sync_reasoner()

By default, sync_reasoner() places all inferred facts in a special ontology, ‘http://inferrences/’. You can control in which ontology the inferred facts are placed using the ‘with ontology’ statement (remember, all triples asserted inside a ‘with ontology’ statement go inside this ontology). For example, for placing all inferred facts in the ‘onto’ ontology:

>>> with onto:
...     sync_reasoner()

This allows saving the ontology with the inferred facts (using onto.save() as usual).

The reasoner can also be limited to some ontologies:

>>> sync_reasoner([onto1, onto2,...])

If you also want to infer object property values, use the “infer_property_values” parameter:

>>> sync_reasoner(infer_property_values = True)

If you want to ignore unsupported datatypes, use the “ignore_unsupported_datatypes” parameter:

>>> sync_reasoner(ignore_unsupported_datatypes = True)

To use Pellet instead of HermiT, just use the sync_reasoner_pellet() function instead.

In addition, Pellet also supports the inference of data property values, using the “infer_data_property_values” parameter:

>>> sync_reasoner_pellet(infer_property_values = True, infer_data_property_values = True)

Results of the automatic classification

Owlready automatically gets the results of the reasoning from HermiT and reclassifies Individuals and Classes, i.e Owlready changes the Classes of Individuals and the superclasses of Classes.

>>> print("drug2 new Classes:", drug2.__class__)
drug2 new Classes: onto.DrugAssociation

>>> drug1.take()
I took a drug with a single active principle

>>> drug2.take()
I took a drug with 2 active principles

>>> drug3.take()
I took a placebo

In this example, drug1, drug2 and drug3 Classes have changed! The reasoner deduced that drug2 is an Association Drug, and that drug3 is a Placebo.

Also notice how the example combines automatic classification of OWL Classes with polymorphism on Python Classes.

Inconsistent classes and ontologies

In case of inconsistent ontology, an OwlReadyInconsistentOntologyError is raised.

Inconcistent classes may occur without making the entire ontology inconsistent, as long as these classes have no individuals. Inconsistent classes are inferred as equivalent to Nothing. They can be obtained as follows:

>>> list(default_world.inconsistent_classes())

In addition, the consistency of a given class can be tested by checking for Nothing in its equivalent classes, as follows:

>>> if Nothing in Drug.equivalent_to:
...       print("Drug is inconsistent!")

Note

To debug an inconsistent ontology the explain command of the Pellet reasoner can provide some useful information. The output of this command is shown if for sync_reasoner_pellet(...) the keyword argument debug has a value >=2 (default is 1). However, note that the additional call to pellet explain might take more time than the reasoning itself.

Querying inferred classification

The .get_parents_of(), .get_instances_of() and .get_children_of() methods of an ontology can be used to query the hierarchical relations, limited to those defined in the given ontology. This is commonly used after reasoning, to obtain the inferred hierarchical relations.

  • .get_parents_of(entity) accepts any entity (Class, property or individual), and returns the superclasses (for a class), the superproperties (for a property), or the classes (for an individual). (NB for obtaining all parents, independently of the ontology they are asserted in, use entity.is_a).

  • .get_instances_of(Class) returns the individuals that are asserted as belonging to the given Class in the ontology. (NB for obtaining all instances, independently of the ontology they are asserted in, use Class.instances()).

  • .get_children_of(entity) returns the subclasses (or subproperties) that are asserted for the given Class or property in the ontology. (NB for obtaining all children, independently of the ontology they are asserted in, use entity.subclasses()).

Here is an example:

>>> inferences = get_ontology("http://test.org/onto_inferences.owl")
>>> with inferences:
...     sync_reasoner()

>>> inferences.get_parents_of(drug1)
[onto.SingleActivePrincipleDrug]

>>> drug1.is_a
[onto.has_for_active_principle.only(OneOf([onto.acetaminophen])), onto.SingleActivePrincipleDrug]