Posted:August 31, 2020

CWPK #26: Introduction to Knowledge Graph Reasoners

Two Standards Come Pre-packaged with Owlready2

We introduce OWL (knowledge graph) reasoners in this installment of the Cooking with Python and KBpedia series. A reasoner has two purposes. First, based on deductive reasoning, a reasoner can infer new class and property assignments that are logically entailed by the assertions in an ontology (that is, from its axioms) but not otherwise explicitly stated. Once inferred, these additional assignments can be written to an inferred version of the ontology for faster lookups and analysis. Second, reasoners can evaluate the stated axioms to determine if the ontology is consistent or satisfiable. This second purpose is a key step when building or modifying a knowledge graph to ensure that illogical assertions are not introduced into the system. Reasoners thus often have explanation routines that point out where the inconsistencies or problems occur, thus helping the analyst to fix the errors before committing to productive use. In later installments we will focus especially on these coherency tests when we discuss the build procedures for KBpedia.

Consistency is when none of the assertions (axioms) in the knowledge graph contradicts another one. If there is a contradiction the graph is termed to be inconsistent. Satisfiability individually evaluates the classes in the graph and checks to see if they can have instances without contradicting other asserted axioms. Unsatisfiability indicates there is a conflicting or missing assignment that needs to be corrected. It is a particularly useful check when there are disjoint assertions made between classes, one of the design pillars of KBpedia.

Owlready2 is distributed with two OWL reasoners:

  • HermiT, developed by the department of Computer Science of the University of Oxford, and
  • Pellet, a reasoner developed specifically to support the OWL language.

Both HermiT and Pellet are written in Java, so require access to a JVM on your system. If you have difficulty running these systems it is likely because you: 1) do not have a recent version of Java installed on your system; or 2) do not have a proper PATH statement in your environmental variables to find the Java executable. If you encounter such problems, please consult third-party sources to get Java properly configured for your system before continuing with this installment.

Test Ontology

To make sure that your system is configured properly, go ahead and shift+enter or Run this cell that enters a small example ontology from the owlready2 documentation:

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)

Then, run the HermiT reasoner with the single command:

sync_reasoner()
* Owlready2 * Running HermiT...
java -Xmx2000M -cp C:\1-PythonProjects\Python\lib\site-packages\owlready2\hermit;C:\1-PythonProjects\Python\lib\site-packages\owlready2\hermit\HermiT.jar org.semanticweb.HermiT.cli.CommandLine -c -O -D -I file:///C:/Users/mike/AppData/Local/Temp/tmpbnxf7755
* Owlready2 * HermiT took 0.4851553440093994 seconds
* Owlready * Reparenting onto.drug2: {onto.Drug} => {onto.DrugAssociation}
* Owlready * Reparenting onto.drug1: {onto.Drug} => {onto.SingleActivePrincipleDrug}
* Owlready * Reparenting onto.drug3: {onto.Drug} => {onto.Placebo}
* Owlready * (NB: only changes on entities loaded in Python are shown, other changes are done but not listed)

The feedback you get to screen should indicate that you are ‘Reparenting’ the three drugs from one class (Drug) to their appropriate sublasses. By the way, you could also place this argument in the command to turn off the debug reports to screen: $ sync_reasoner(debug = 0).

You can also confirm this move for drug2:

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

And, then, in the next three cells, confirm how you took those three drugs:

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

And, last, in the next two cells discover if any inconsistent classes remain (they do not), which is equivalent to a class being assigned to the Nothing class in OWL:

list(default_world.inconsistent_classes())
[]
if Nothing in Drug.equivalent_to:
       print("Drug is inconsistent!")

General Load Method

OK, so now we see the HermiT reasoner is configured properly and working, we are now ready to test our KBpedia knowledge graph. Go ahead and select Kernel → Restart & Clear Output from the main menu to begin the next activities from a clean slate.

Then execute what has become our standard load procedure:

Which environment? The specific load routine you should choose below depends on whether you are using the online MyBinder service (the ‘raw’ version) or local files. The example below is based on using local files (though replace with your own local directory specification). If loading from MyBinder, replace with the lines that are commented (#) out.
main = 'C:/1-PythonProjects/kbpedia/sandbox/kbpedia_reference_concepts.owl'
# main = 'https://raw.githubusercontent.com/Cognonto/CWPK/master/sandbox/builds/ontologies/kbpedia_reference_concepts.owl'
skos_file = 'http://www.w3.org/2004/02/skos/core' 
kko_file = 'C:/1-PythonProjects/kbpedia/sandbox/kko.owl'
# kko_file = 'https://raw.githubusercontent.com/Cognonto/CWPK/master/sandbox/builds/ontologies/kko.owl'

from owlready2 import *
world = World()
kb = world.get_ontology(main).load()
rc = kb.get_namespace('http://kbpedia.org/kko/rc/')

skos = world.get_ontology(skos_file).load()
kb.imported_ontologies.append(skos)

kko = world.get_ontology(kko_file).load()
kb.imported_ontologies.append(kko)

HermiT Reasoner

We again invoke the HermiT reasoner:

sync_reasoner()
* Owlready2 * Running HermiT...
java -Xmx2000M -cp C:\1-PythonProjects\Python\lib\site-packages\owlready2\hermit;C:\1-PythonProjects\Python\lib\site-packages\owlready2\hermit\HermiT.jar org.semanticweb.HermiT.cli.CommandLine -c -O -D -I file:///C:/Users/mike/AppData/Local/Temp/tmpxglvdub2
* Owlready2 * HermiT took 0.42046189308166504 seconds
* Owlready * (NB: only changes on entities loaded in Python are shown, other changes are done but not listed)

There is also an argument to infer_property_values = True:

sync_reasoner(infer_property_values = True)
* Owlready2 * Running HermiT...
java -Xmx2000M -cp C:\1-PythonProjects\Python\lib\site-packages\owlready2\hermit;C:\1-PythonProjects\Python\lib\site-packages\owlready2\hermit\HermiT.jar org.semanticweb.HermiT.cli.CommandLine -c -O -D -I file:///C:/Users/mike/AppData/Local/Temp/tmpxkc92ws4 -Y
* Owlready2 * HermiT took 0.416165828704834 seconds
* Owlready * (NB: only changes on entities loaded in Python are shown, other changes are done but not listed)

We see that the ontology is consistent, which we can confirm with this additional command:

list(world.inconsistent_classes())
[]

Pellet Reasoner

The second of our reasoners, Pellet, operates under a similar set of arguments. We invoke Pellet through the modified reasoner command:.

sync_reasoner_pellet()
* Owlready2 * Running Pellet...
java -Xmx2000M -cp C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\antlr-3.2.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\antlr-runtime-3.2.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\aterm-java-1.6.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\commons-codec-1.6.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\httpclient-4.2.3.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\httpcore-4.2.2.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\jcl-over-slf4j-1.6.4.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\jena-arq-2.10.0.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\jena-core-2.10.0.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\jena-iri-0.9.5.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\jena-tdb-0.10.0.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\jgrapht-jdk1.5.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\log4j-1.2.16.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\owlapi-distribution-3.4.3-bin.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\pellet-2.3.1.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\slf4j-api-1.6.4.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\slf4j-log4j12-1.6.4.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\xercesImpl-2.10.0.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\xml-apis-1.4.01.jar pellet.Pellet realize --loader Jena --input-format N-Triples --ignore-imports C:\Users\mike\AppData\Local\Temp\tmp7_rotl4_
* Owlready2 * Pellet took 1.017836093902588 seconds
* Owlready * (NB: only changes on entities loaded in Python are shown, other changes are done but not listed)

Pellet, too, is configured to run in a debug mode. If you wish, you may turn it off with $ sync_reasoner(debug = 0).

Like HermiT we can also infer_property_values. But, different than HermiT, we may also infer_data_property_values = True using Pellet:

sync_reasoner_pellet(infer_property_values = True, infer_data_property_values = True)
* Owlready2 * Running Pellet...
java -Xmx2000M -cp C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\antlr-3.2.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\antlr-runtime-3.2.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\aterm-java-1.6.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\commons-codec-1.6.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\httpclient-4.2.3.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\httpcore-4.2.2.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\jcl-over-slf4j-1.6.4.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\jena-arq-2.10.0.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\jena-core-2.10.0.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\jena-iri-0.9.5.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\jena-tdb-0.10.0.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\jgrapht-jdk1.5.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\log4j-1.2.16.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\owlapi-distribution-3.4.3-bin.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\pellet-2.3.1.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\slf4j-api-1.6.4.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\slf4j-log4j12-1.6.4.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\xercesImpl-2.10.0.jar;C:\1-PythonProjects\Python\lib\site-packages\owlready2\pellet\xml-apis-1.4.01.jar pellet.Pellet realize --loader Jena --input-format N-Triples --infer-prop-values --infer-data-prop-values --ignore-imports C:\Users\mike\AppData\Local\Temp\tmpcr2dw8yi
* Owlready2 * Pellet took 0.6863009929656982 seconds
* Owlready * (NB: only changes on entities loaded in Python are shown, other changes are done but not listed)
list(world.inconsistent_classes())
[]

SWRL

As long as we are introducing these capabilities, we should also mention that Owlready2 also supports the use of SWRL (the Semantic Web Rule Language) “if . . . then” type statements. To the best of my knowledge, Owlready2 supports all of the standard SWRL constructs. It is also possible to mix Python and OWL code together, but that, too, is a topic we will not be addressing further in this CWPK series.

Save and Exit

When we are finished with our tests, we can File → Save and Checkpoint, Rename our output file, or specify it at the command line:

kb.save(file = 'files/kbpedia_reference_concepts-pellet.owl', format = 'rdfxml')

Additional Documentation

Here are links to appropriate Owlready2 documentation:

NOTE: This article is part of the Cooking with Python and KBpedia series. See the CWPK listing for other articles in the series. KBpedia has its own Web site.
NOTE: This CWPK installment is available both as an online interactive file or as a direct download to use locally. Make sure and pick the correct installment number. For the online interactive option, pick the *.ipynb file. It may take a bit of time for the interactive option to load.
I am at best an amateur with Python. There are likely more efficient methods for coding these steps than what I provide. I encourage you to experiment — which is part of the fun of Python — and to notify me should you make improvements.

Schema.org Markup

headline:
CWPK #26: Introduction to Knowledge Graph Reasoners

alternativeHeadline:
Two Standards Come Pre-packaged with Owlready2

author:

image:
https://www.mkbergman.com/wp-content/uploads/2020/07/cooking-with-kbpedia-785.png

description:
We introduce two OWL (knowledge graph) reasoners in this CWPK installment, Pellet and HermiT. A reasoner has two purposes: 1) based on deductive reasoning, it can infer new class and property assignments that are logically entailed by the assertions in an ontology, but not explicitly stated; 2) it can evaluate if the ontology is logically consistent or satisfiable.

articleBody:
see above

datePublished:

Leave a Reply

Your email address will not be published. Required fields are marked *