Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactoring owlapi adaptor #49

Merged
merged 9 commits into from
Jul 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

project = 'OWLAPY'
author = 'Ontolearn Team'
release = '0.1.2'
release = '1.1.1'

# -- General configuration ---------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration
Expand Down
2 changes: 1 addition & 1 deletion docs/usage/main.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# About owlapy

**Version:** owlapy 1.1.0
**Version:** owlapy 1.1.1

**GitHub repository:** [https://github.com/dice-group/owlapy](https://github.com/dice-group/owlapy)

Expand Down
26 changes: 16 additions & 10 deletions docs/usage/owlapi_adaptor.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,21 @@ This is automatically done when initializing a `OWLAPIAdaptor` object.

## Initialization

To use the adaptor you have to initialize using the `with` statement in python.
This way you will know where the JVM session starts and when it closes:
To use the adaptor you have to start the JVM via jpype, which is done automatically
when you create an _OWLAPIAdaptor_ object. After you are finished you can stop
the JVM by either using `jpype.shutdownJVM()` or the static method from the
adaptor `stopJVM()`. This will free the resources used by JPype and the java
packages.

```python
from owlapy.owlapi_adaptor import OWLAPIAdaptor

with OWLAPIAdaptor("KGs/Family/father.owl") as adaptor:
# Use the adaptor
print(f"Is the ontology consistent? {adaptor.has_consistent_ontology()}")
adaptor = OWLAPIAdaptor("KGs/Family/father.owl")
# Use the adaptor
print(f"Is the ontology consistent? {adaptor.has_consistent_ontology()}")

# The JVM will shut down when the thread is no longer used.
# Stop the JVM
adaptor.stopJVM()
```

In the above code snipped, we created an adaptor for the father ontology
Expand All @@ -38,11 +42,13 @@ the ontology is consistent or not.
An important note is that when initialising the adaptor you are basically
starting a JVM in the background, and therefore you are able to import and
use java classes as you would do in python. That means that you can
play around with owlapi code in python. Isn't that awesome!
play around with owlapi code in python as long as your JVM is started.
Isn't that awesome!

`OWLAPIAdaptor` uses HermiT reasoner for methods that require reasoning,
such as `instances`, which returns all individuals belonging to a class
expression.
`OWLAPIAdaptor` uses HermiT reasoner by default. You can choose between:
"HermiT", "Pellet", "JFact" and "Openllet".

_**owlapi version**: 5.1.9_

## Examples

Expand Down
46 changes: 46 additions & 0 deletions examples/comparing_adaptor_reasoners.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
from owlapy.owl_property import OWLObjectProperty
from owlapy.owlapi_adaptor import OWLAPIAdaptor
from owlapy.iri import IRI
from owlapy.class_expression import OWLClass, OWLObjectIntersectionOf, OWLObjectAllValuesFrom, OWLObjectComplementOf
from owlapy.providers import owl_datatype_min_exclusive_restriction
import time
ontology_location = "../KGs/Carcinogenesis/carcinogenesis.owl"

i1 = set()
i2 = set()
i3 = set()
i4 = set()

for reasoner in ["HermiT", "Pellet", "JFact", "Openllet"]:
adaptor = OWLAPIAdaptor(ontology_location, reasoner)

ce = OWLObjectAllValuesFrom(property=OWLObjectProperty(IRI('http://dl-learner.org/carcinogenesis#','hasAtom')),
filler=OWLObjectComplementOf(OWLClass(IRI('http://dl-learner.org/carcinogenesis#',
'Sulfur-75'))))

if reasoner == "HermiT":
i1 = set(adaptor.instances(ce))
elif reasoner == "Pellet":
i2 = set(adaptor.instances(ce))
elif reasoner == "JFact":
i3 = set(adaptor.instances(ce))
elif reasoner == "Openllet":
i4 = set(adaptor.instances(ce))

print("Hermit-Pellet:")
[print(_) for _ in i1-i2]
time.sleep(10)
print("Hermit-JFact:")
[print(_) for _ in i1-i3]
time.sleep(10)
print("Hermit-Openllet:")
[print(_) for _ in i1-i4]
time.sleep(10)
print("Pellet-JFact:")
[print(_) for _ in i2-i3]
time.sleep(10)
print("Pellet-Openllet:")
[print(_) for _ in i2-i4]
time.sleep(10)
print("JFact-Openllet:")
[print(_) for _ in i3-i4]
47 changes: 25 additions & 22 deletions examples/using_owlapi_adaptor.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,29 @@
ontology_location = "../KGs/Family/family-benchmark_rich_background.owl"

# Start an adaptor session and perform your operations.
with OWLAPIAdaptor(ontology_location) as adaptor:
# Check ontology consistency using HermiT
print(f"Is the given ontology consistent? --> {adaptor.has_consistent_ontology()}")

# Construct an owlapy class expression
brother = OWLClass(IRI.create("http://www.benchmark.org/family#Brother"))
father = OWLClass(IRI.create("http://www.benchmark.org/family#Father"))
brother_and_father = OWLObjectIntersectionOf([brother, father])

# Find individual belonging to that class expression (using HermiT behind the scene)
instances = adaptor.instances(brother_and_father)
print("----------------------")
print("Individuals that are brother and father at the same time:")
[print(_) for _ in instances]

# Convert from owlapy to owlapi
py_to_pi = adaptor.convert_to_owlapi(brother_and_father)

# Convert from owlapi to owlapy
pi_to_py = adaptor.convert_from_owlapi(py_to_pi, "http://www.benchmark.org/family#")
print("----------------------")
print(f"Owlapy ce: {pi_to_py}")
adaptor = OWLAPIAdaptor(ontology_location, "HermiT")
# Check ontology consistency
print(f"Is the given ontology consistent? --> {adaptor.has_consistent_ontology()}")

# Construct an owlapy class expression
brother = OWLClass(IRI.create("http://www.benchmark.org/family#Brother"))
father = OWLClass(IRI.create("http://www.benchmark.org/family#Father"))
brother_and_father = OWLObjectIntersectionOf([brother, father])

# Find individual belonging to that class expression
instances = adaptor.instances(brother_and_father)
print("----------------------")
print("Individuals that are brother and father at the same time:")
[print(_) for _ in instances]

# Convert from owlapy to owlapi
py_to_pi = adaptor.convert_to_owlapi(brother_and_father)

# Convert from owlapi to owlapy
pi_to_py = adaptor.convert_from_owlapi(py_to_pi, "http://www.benchmark.org/family#")
print("----------------------")
print(f"Owlapy ce: {pi_to_py}")

# Stop the JVM to free the associated resources.
adaptor.stopJVM() # or jpype.shutdownJVM()

2 changes: 1 addition & 1 deletion owlapy/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .render import owl_expression_to_dl, owl_expression_to_manchester
from .parser import dl_to_owl_expression, manchester_to_owl_expression
from .converter import owl_expression_to_sparql
__version__ = '1.1.0'
__version__ = '1.1.1'
Binary file added owlapy/jar_dependencies/RoaringBitmap-0.6.32.jar
Binary file not shown.
File renamed without changes.
Binary file added owlapy/jar_dependencies/jaxb-api-2.3.0.jar
Binary file not shown.
Binary file added owlapy/jar_dependencies/jfact-5.0.3.jar
Binary file not shown.
Binary file added owlapy/jar_dependencies/openllet-core-2.6.5.jar
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file added owlapy/jar_dependencies/openllet-query-2.6.5.jar
Binary file not shown.
File renamed without changes.
89 changes: 46 additions & 43 deletions owlapy/owlapi_adaptor.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import jpype.imports
import os
import pkg_resources

from owlapy import manchester_to_owl_expression
from owlapy.class_expression import OWLClassExpression
Expand All @@ -8,6 +9,7 @@
from owlapy.render import owl_expression_to_manchester
from typing import List


class OWLAPIAdaptor:
"""
A class to interface with the OWL API using the HermiT reasoner, enabling ontology management,
Expand All @@ -18,72 +20,72 @@ class OWLAPIAdaptor:
name_reasoner (str): The reasoner to be used, default is "HermiT".
manager: The OWL ontology manager.
ontology: The loaded OWL ontology.
reasoner: The HermiT reasoner for the ontology.
reasoner: Choose from (case-sensitive): ["HermiT", "Pellet", "JFact", "Openllet"]. Default: "HermiT".
parser: The Manchester OWL Syntax parser.
renderer: The Manchester OWL Syntax renderer.
__is_open (bool): Flag to check if the JVM is open.
"""
def __init__(self, path: str, name_reasoner: str = "HermiT"):
"""
Initialize the OWLAPIAdaptor with a path to an ontology and a reasoner name.

Args:
path (str): The file path to the ontology.
name_reasoner (str, optional): The reasoner to be used. Defaults to "HermiT".
name_reasoner (str, optional): The reasoner to be used.
Available options are: ['HermiT' (default), 'Pellet', 'JFact', 'Openllet'].

Raises:
AssertionError: If the provided reasoner name is not implemented.
"""
self.path = path
assert name_reasoner in ["HermiT"], f"{name_reasoner} is not implemented. Please use HermiT"
assert name_reasoner in ["HermiT", "Pellet", "JFact", "Openllet"], \
f"'{name_reasoner}' is not implemented. Available reasoners: ['HermiT', 'Pellet', 'JFact', 'Openllet']"
self.name_reasoner = name_reasoner
# Attributes are initialized as JVMStarted is started
# Attributes are initialized as JVM is started
# () Manager is needed to load an ontology
self.manager = None
# () Load a local ontology using the manager
self.ontology = None
# () Create a HermiT reasoner for the loaded ontology
# () Create a reasoner for the loaded ontology
self.reasoner = None
self.parser = None
# () A manchester renderer to render owlapi ce to manchester syntax
self.renderer = None
self.open()
self.__is_open = True

def __exit__(self, exc_type, exc_val, exc_tb):
"""Shuts down the java virtual machine hosted by jpype."""
self.close()

def __enter__(self):
"""
Initialization via the `with` statement.

Returns:
OWLAPIAdaptor: The current instance.
"""
if not self.__is_open:
self.open()
return self
# () Set up the necessary attributes by making use of the java packages
self._setup()

def _startJVM(self):
"""Start the JVM with jar dependencies. This method is called automatically on object initialization, if the
JVM is not started yet."""
# Start a java virtual machine using the dependencies in the respective folder:
jar_folder = pkg_resources.resource_filename('owlapy', 'jar_dependencies')
jar_files = [os.path.join(jar_folder, f) for f in os.listdir(jar_folder) if f.endswith('.jar')]
# Starting JVM.
jpype.startJVM(classpath=jar_files)

def stopJVM(self, *args, **kwargs) -> None:
"""Detaches the thread from Java packages and shuts down the java virtual machine hosted by jpype."""
if jpype.isJVMStarted():
jpype.detachThreadFromJVM()
jpype.shutdownJVM()

def open(self):
def _setup(self):
"""
Start the JVM with jar dependencies, import necessary OWL API dependencies, and initialize attributes.
Start the JVM if not already, import necessary OWL API dependencies, and initialize attributes.
"""
if not jpype.isJVMStarted():
# Start a java virtual machine using the dependencies in the respective folder:
if os.getcwd()[-6:] == "owlapy":
jar_folder = "jar_dependencies"
else:
jar_folder = "../jar_dependencies"
jar_files = [os.path.join(jar_folder, f) for f in os.listdir(jar_folder) if f.endswith('.jar')]
# Starting JVM.
jpype.startJVM(classpath=jar_files)
self._startJVM()

# Import Java classes
from org.semanticweb.owlapi.apibinding import OWLManager
from java.io import File
if self.name_reasoner == "HermiT":
from org.semanticweb.HermiT import ReasonerFactory
elif self.name_reasoner == "Pellet":
from openllet.owlapi import PelletReasonerFactory
elif self.name_reasoner == "JFact":
from uk.ac.manchester.cs.jfact import JFactFactory
elif self.name_reasoner == "Openllet":
from openllet.owlapi import OpenlletReasonerFactory
else:
raise NotImplementedError("Not implemented")

Expand All @@ -97,10 +99,17 @@ def open(self):
self.manager = OWLManager.createOWLOntologyManager()
# () Load a local ontology using the manager
self.ontology = self.manager.loadOntologyFromOntologyDocument(File(self.path))
# () Create a HermiT reasoner for the loaded ontology
self.reasoner = ReasonerFactory().createReasoner(self.ontology)

# () Create a manchester parser and all the necessary attributes for parsing manchester syntax string to owlapi ce
# () Create a reasoner for the loaded ontology
if self.name_reasoner == "HermiT":
self.reasoner = ReasonerFactory().createReasoner(self.ontology)
elif self.name_reasoner == "JFact":
self.reasoner = JFactFactory().createReasoner(self.ontology)
elif self.name_reasoner == "Pellet":
self.reasoner = PelletReasonerFactory().createReasoner(self.ontology)
elif self.name_reasoner == "Openllet":
self.reasoner = OpenlletReasonerFactory().getInstance().createReasoner(self.ontology)

# () Create a manchester parser and all the necessary attributes for parsing a manchester string to owlapi ce
ontology_set = HashSet()
ontology_set.add(self.ontology)
bidi_provider = BidirectionalShortFormProviderAdapter(self.manager, ontology_set, SimpleShortFormProvider())
Expand All @@ -109,12 +118,6 @@ def open(self):
# A manchester renderer to render owlapi ce to manchester syntax
self.renderer = ManchesterOWLSyntaxOWLObjectRendererImpl()

def close(self, *args, **kwargs) -> None:
"""Shuts down the java virtual machine hosted by jpype."""
if jpype.isJVMStarted() and not jpype.isThreadAttachedToJVM():
jpype.shutdownJVM()
self.__is_open = False

def convert_to_owlapi(self, ce: OWLClassExpression):
"""
Converts an OWLAPY class expression to an OWLAPI class expression.
Expand Down
6 changes: 5 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,12 @@
setup(
name="owlapy",
description="OWLAPY is a Python Framework for creating and manipulating OWL Ontologies.",
version="1.1.0",
version="1.1.1",
packages=find_packages(),
include_package_data=True,
package_data={
'owlapy': ['jar_dependencies/*.jar'],
},
install_requires=[
"pandas>=1.5.0",
"rdflib>=6.0.2",
Expand Down
Loading
Loading