Skip to content

Commit

Permalink
Merge pull request #65 from dice-group/develop
Browse files Browse the repository at this point in the history
v1.3.0 merge
  • Loading branch information
alkidbaci authored Sep 10, 2024
2 parents cd38c60 + d463825 commit 9a95b15
Show file tree
Hide file tree
Showing 44 changed files with 3,348 additions and 2,712 deletions.
8 changes: 8 additions & 0 deletions .github/workflows/ruff.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
name: Ruff
on: [push, pull_request]
jobs:
ruff:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: chartboost/ruff-action@v1
53 changes: 34 additions & 19 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# OWLAPY
[![Coverage](https://img.shields.io/badge/coverage-78%25-green)](https://dice-group.github.io/owlapy/usage/further_resources.html#coverage-report)
[![Pypi](https://img.shields.io/badge/pypi-1.2.0-blue)](https://pypi.org/project/owlapy/1.2.0/)
[![Docs](https://img.shields.io/badge/documentation-1.2.0-yellow)](https://dice-group.github.io/owlapy/usage/main.html)
[![Pypi](https://img.shields.io/badge/pypi-1.3.0-blue)](https://pypi.org/project/owlapy/1.3.0/)
[![Docs](https://img.shields.io/badge/documentation-1.3.0-yellow)](https://dice-group.github.io/owlapy/usage/main.html)

![OWLAPY](docs/_static/images/owlapy_logo.png)

Expand Down Expand Up @@ -38,21 +38,34 @@ pytest -p no:warnings -x # Running 102 tests takes ~ 1 min
from owlapy.class_expression import OWLClass, OWLObjectIntersectionOf, OWLObjectSomeValuesFrom
from owlapy.owl_property import OWLObjectProperty
from owlapy import owl_expression_to_sparql, owl_expression_to_dl
from owlapy.owl_ontology_manager import OntologyManager
from owlapy.owl_axiom import OWLDeclarationAxiom, OWLClassAssertionAxiom
from owlapy.owl_individual import OWLNamedIndividual, IRI

# Create the male class
# Using owl classes to create a complex class expression
male = OWLClass("http://example.com/society#male")
# Create an object property using the iri as a string for 'hasChild' property.
hasChild = OWLObjectProperty("http://example.com/society#hasChild")
# Create an existential restrictions
hasChild_male = OWLObjectSomeValuesFrom(hasChild, male)
# Let's make it more complex by intersecting with another class
teacher = OWLClass("http://example.com/society#teacher")
teacher_that_hasChild_male = OWLObjectIntersectionOf([hasChild_male, teacher])
# You can render and print owl class expressions in description logics syntax (and vice-versa)
print(owl_expression_to_dl(teacher_that_hasChild_male))
# (∃ hasChild.male) ⊓ teacher
print(owl_expression_to_sparql(teacher_that_hasChild_male))
# SELECT DISTINCT ?x WHERE { ?x <http://example.com/society#hasChild> ?s_1 . ?s_1 a <http://example.com/society#male> . ?x a <http://example.com/society#teacher> . } }

# You can render and print owl class expressions in Description Logics syntax or convert it to SPARQL for example.
print(owl_expression_to_dl(teacher_that_hasChild_male)) # (∃ hasChild.male) ⊓ teacher
print(owl_expression_to_sparql(teacher_that_hasChild_male)) # SELECT DISTINCT ?x WHERE { ?x <http://example.com/society#hasChild> ?s_1 . ?s_1 a <http://example.com/society#male> . ?x a <http://example.com/society#teacher> . } }

# Create an Ontology, add the axioms and save the Ontology.
manager = OntologyManager()
new_iri = IRI.create("file:/example_ontology.owl")
ontology = manager.create_ontology(new_iri)

john = OWLNamedIndividual("http://example.com/society#john")
male_declaration_axiom = OWLDeclarationAxiom(male)
hasChild_declaration_axiom = OWLDeclarationAxiom(hasChild)
john_declaration_axiom = OWLDeclarationAxiom(john)
john_a_male_assertion_axiom = OWLClassAssertionAxiom(john, male)
ontology.add_axiom([male_declaration_axiom, hasChild_declaration_axiom, john_declaration_axiom, john_a_male_assertion_axiom])
ontology.save()

```

Every OWL object that can be used to classify individuals, is considered a class expression and
Expand All @@ -73,18 +86,19 @@ OWL objects in [owlapy api](https://dice-group.github.io/owlapy/autoapi/owlapy/i

```python
from owlapy.owl_ontology_manager import OntologyManager
from owlapy.owlapi_adaptor import OWLAPIAdaptor
from owlapy.owl_reasoner import SyncReasoner
from owlapy.static_funcs import stopJVM

ontology_path = "KGs/Family/family-benchmark_rich_background.owl"
# Available OWL Reasoners: 'HermiT', 'Pellet', 'JFact', 'Openllet'
owlapi_adaptor = OWLAPIAdaptor(path=ontology_path, name_reasoner="Pellet")
reasoner = SyncReasoner(ontology = ontology_path, reasoner="Pellet")
onto = OntologyManager().load_ontology(ontology_path)
# Iterate over defined owl Classes in the signature
for i in onto.classes_in_signature():
# Performing type inference with Pellet
instances=owlapi_adaptor.instances(i,direct=False)
instances=sync_reasoner.instances(i,direct=False)
print(f"Class:{i}\t Num instances:{len(instances)}")
owlapi_adaptor.stopJVM()
stopJVM()
```

</details>
Expand All @@ -95,11 +109,12 @@ owlapi_adaptor.stopJVM()

An Ontology can be enriched by inferring many different axioms.
```python
from owlapy.owlapi_adaptor import OWLAPIAdaptor
from owlapy.owl_reasoner import SyncReasoner
from owlapy.static_funcs import stopJVM

adaptor = OWLAPIAdaptor(path="KGs/Family/family-benchmark_rich_background.owl", name_reasoner="Pellet")
sync_reasoner = SyncReasoner(ontology="KGs/Family/family-benchmark_rich_background.owl", reasoner="Pellet")
# Infer missing class assertions
adaptor.infer_axioms_and_save(output_path="KGs/Family/inferred_family-benchmark_rich_background.ttl",
sync_reasoner.infer_axioms_and_save(output_path="KGs/Family/inferred_family-benchmark_rich_background.ttl",
output_format="ttl",
inference_types=[
"InferredClassAssertionAxiomGenerator",
Expand All @@ -108,7 +123,7 @@ adaptor.infer_axioms_and_save(output_path="KGs/Family/inferred_family-benchmark_
"InferredSubClassAxiomGenerator",
"InferredInverseObjectPropertiesAxiomGenerator",
"InferredEquivalentClassAxiomGenerator"])
adaptor.stopJVM()
stopJVM()
```

</details>
Expand Down
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 = '1.2.0'
release = '1.3.0'

# -- 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.2.0
**Version:** owlapy 1.3.0

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

Expand Down
31 changes: 14 additions & 17 deletions docs/usage/ontologies.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,17 +78,16 @@ iri = IRI('http://example.com/father#', 'child')
child_class = OWLClass(iri)
child_class_declaration_axiom = OWLDeclarationAxiom(child_class)

manager.add_axiom(onto, child_class_declaration_axiom)
onto.add_axiom(child_class_declaration_axiom)
```
In this example, we added the class 'child' to the _father.owl_ ontology.
Firstly we create an instance of [OWLClass](owlapy.class_expression.owl_class.OWLClass) to represent the concept
of 'child' by using an [IRI](owlapy.iri.IRI).
On the other side, an instance of `IRI` is created by passing two arguments which are
the namespace of the ontology and the remainder 'child'. To declare this new class we need
an axiom of type `OWLDeclarationAxiom`. We simply pass the `child_class` to create an
instance of this axiom. The final step is to add this axiom to the ontology using the
[OWLOntologyManager](owlapy.owl_ontology_manager.OWLOntologyManager). We use the `add_axiom` method
of the `manager` to add into the ontology
instance of this axiom. The final step is to add this axiom to the ontology
We use the `add_axiom` method to add into the ontology
`onto` the axiom `child_class_declaration_axiom`.

#### Add a new Object Property / Data Property
Expand All @@ -105,12 +104,12 @@ from owlapy.owl_property import OWLObjectProperty, OWLDataProperty
# adding the object property 'hasParent'
hasParent_op = OWLObjectProperty(IRI('http://example.com/father#', 'hasParent'))
hasParent_op_declaration_axiom = OWLDeclarationAxiom(hasParent_op)
manager.add_axiom(onto, hasParent_op_declaration_axiom)
onto.add_axiom(hasParent_op_declaration_axiom)

# adding the data property 'hasAge'
hasAge_dp = OWLDataProperty(IRI('http://example.com/father#', 'hasAge'))
hasAge_dp_declaration_axiom = OWLDeclarationAxiom(hasAge_dp)
manager.add_axiom(onto, hasAge_dp_declaration_axiom)
onto.add_axiom(hasAge_dp_declaration_axiom)
```

See the [owlapy](owlapy) for more OWL entities that you can add as a declaration axiom.
Expand All @@ -129,15 +128,15 @@ heinz = individuals[1] # get the 2nd individual in the list which is 'heinz'

class_assertion_axiom = OWLClassAssertionAxiom(heinz, child_class)

manager.add_axiom(onto, class_assertion_axiom)
onto.add_axiom(class_assertion_axiom)
```
We have used the previous method `individuals_in_signature()` to get all the individuals
and converted them to a list, so we can access them by using indexes. In this example, we
want to assert a class axiom for the individual `heinz`.
We have used the class `OWLClassAssertionAxiom`
where the first argument is the 'individual' `heinz` and the second argument is
the 'class_expression'. As the class expression, we used the previously defined class
`child_Class`. Finally, add the axiom by using `add_axiom` method of the [OWLOntologyManager](owlapy.owl_ontology_manager.OWLOntologyManager).
`child_Class`. Finally, add the axiom by using `add_axiom` method of the [OWLOntology](owlapy.owl_ontology.OWLOntology).

Let's show one more example using a `OWLDataPropertyAssertionAxiom` to assign the age of 17 to
heinz.
Expand All @@ -151,7 +150,7 @@ from owlapy.owl_axiom import OWLDataPropertyAssertionAxiom
literal_17 = OWLLiteral(17)
dp_assertion_axiom = OWLDataPropertyAssertionAxiom(heinz, hasAge_dp, literal_17)

manager.add_axiom(onto, dp_assertion_axiom)
onto.add_axiom(dp_assertion_axiom)
```

[OWLLiteral](owlapy.owl_literal.OWLLiteral) is a class that represents the literal values in
Expand All @@ -167,26 +166,24 @@ assertion axioms that you can use.

#### Remove an Axiom

To remove an axiom you can use the `remove_axiom` method of the ontology manager as follows:
To remove an axiom you can use the `remove_axiom` method as follows:

<!--pytest-codeblocks:cont-->
```python
manager.remove_axiom(onto,dp_assertion_axiom)
onto.remove_axiom(dp_assertion_axiom)
```
The first argument is the ontology you want to remove the axiom from and the second
argument is the axiom you want to remove.
The required argument is the axiom/axioms you want to remove.


## Save an Ontology

If you modified an ontology, you may want to save it as a new file. To do this
you can use the `save_ontology` method of the [OWLOntologyManager](owlapy.owl_ontology_manager.OWLOntologyManager).
It requires two arguments, the first is the ontology you want to save and The second
is the IRI of the new ontology.
you can use the `save` method of the [OWLOntology](owlapy.owl_ontology.OWLOntology).
It requires one argument, the IRI of the new ontology.

<!--pytest-codeblocks:cont-->
```python
manager.save_ontology(onto, IRI.create('file:/' + 'test' + '.owl'))
onto.save(IRI.create('file:/' + 'test' + '.owl'))
```
The above line of code will save the ontology `onto` in the file *test.owl* which will be
created in the same directory as the file you are running this code.
Expand Down
119 changes: 79 additions & 40 deletions docs/usage/owlapi_adaptor.md
Original file line number Diff line number Diff line change
@@ -1,62 +1,101 @@
# Owlapi Adaptor
# Owlapi Synchronization

As mentioned earlier, owlapy is loosely based in [owlapi](https://github.com/owlcs/owlapi),
As mentioned earlier, _owlapy_ is loosely based in [_owlapi_](https://github.com/owlcs/owlapi),
a library for ontology modification in java.

We have created [OWLAPIAdaptor](owlapy.owlapi_adaptor.OWLAPIAdaptor),
an adaptor class that facilitates the conversion of
owl class expressions from owlapy to owlapi and vice-versa.
This adaptor is still considered experimental, and it's in the
initial phase of development.
We have created [OWLAPIMapper](owlapy.owlapi_mapper.OWLAPIMapper),
a mapping class that makes possible the conversion of the most
important classes from _owlapy_ to _owlapi_ and vice-versa.


We are able to use owlapi via [Jpype](https://jpype.readthedocs.io/en/latest/),
a python module that provides access to Java via python. To start executing
Java code via jpype, one needs to start the java virtual machine (JVM).
This is automatically done when initializing a `OWLAPIAdaptor` object.
a python module that provides access to Java in python. To start executing
Java code via Jpype, one needs to start the java virtual machine (JVM).
You don't have to worry about it, because if a class is going to use
`OWLAPIMapper` the JVM will start automatically. However, there is the
function `startJVM` of the `static_functions.py` module if you ever need
to start it manually.

## "Sync" Classes

With the addition of the `OWLAPIMapper`, we introduce three new classes:
- [SyncOntologyManager](owlapy.owl_ontology_manager.SyncOntologyManager)
- [SyncOntology](owlapy.owl_ontology.SyncOntology)
- [SyncReasoner](owlapy.owl_reasoner.SyncReasoner)

All the logic of these three classes is handled by _owlapi_ through the
mapper. They inherit from abstract classes already present in owlapy
(`OWLOntologyManager`, `OWLOntology` and `OWLReasoner` respectively) so
the usage is the same as other implementors of these abstract classes.
However, there are also some extra methods, like `infer_axioms` of SyncReasoner
which infers the missing axioms from the given ontology and returns them as `Iterable[OWLAxiom]`.
Make sure to check the API docs to see them all.

To make this guide self-contained, we will go through a simple example
showing how to use this above-mentioned classes:

## Initialization
```python
from owlapy.owl_ontology_manager import SyncOntologyManager
from owlapy.owl_axiom import OWLDeclarationAxiom
from owlapy.class_expression import OWLClass
from owlapy.owl_reasoner import SyncReasoner
from owlapy.static_funcs import stopJVM

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.
# (.) Creat a manager and load the 'father' ontology
manager = SyncOntologyManager()
ontology = manager.load_ontology("KGs/Family/father.owl")

```python
from owlapy.owlapi_adaptor import OWLAPIAdaptor
# (.) Use your ontology as you usually do
# (..) Add a new class
ontology.add_axiom(OWLDeclarationAxiom(OWLClass("http://example.com/father#some_new_class")))
# (..) Print classes in signature
[print(cls) for cls in ontology.classes_in_signature()]

# (.) Create a reasoner and perform some reasoning
reasoner = SyncReasoner(ontology)

# (..) Check ontology consistency
print(f"Is the ontology consistent? Answer: {reasoner.has_consistent_ontology()}")

# (..) Print all male individuals
[print(ind) for ind in reasoner.instances(OWLClass("http://example.com/father#male"))]

# (.) Stop the JVM if you no longer intend to use "Sync" classes
stopJVM()

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

# Stop the JVM
adaptor.stopJVM()
```
This was a simple example using the '_father_' ontology to show
just a small part of what you can do with "Sync" classes.

In the above code snipped, we created an adaptor for the father ontology
by passing the local path of that ontology. Then we print whether
the ontology is consistent or not.
Notice that after we are done using them we can stop
the JVM by either using `jpype.shutdownJVM()` or the static function from the
`static_functions.py` module `stopJVM()`. This will free the resources used by JPype and the java
packages. Once you stop the JVM it cannot be restarted so make sure you do that
when you are done with the owlapi related classes. Stopping the JVM is not
strictly necessary. The resources will be freed once the execution is over, but
in case your code is somehow longer and the "Sync" classes only make up a part of your execution
then you can stop the JVM after it not being needed anymore.

## Notes

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 as long as your JVM is started.
Isn't that awesome!
## Notes

`OWLAPIAdaptor` uses HermiT reasoner by default. You can choose between:
"HermiT", "Pellet", "JFact" and "Openllet".
An important thing to keep in mind is that when starting the JVM
you are able to import and use java classes as you would do in python (thanks to Jpype).
That means that you can play around with owlapi code in python as long
as your JVM is started. Isn't that awesome!

You can use the reasoning method directly from the adaptor but
for classes that require an [OWLReasoner](owlapi.owl_reasoner.OWLReasoner)
you can use [SyncReasoner](https://dice-group.github.io/owlapy/autoapi/owlapy/owl_reasoner/index.html#owlapy.owl_reasoner.SyncReasoner).
`SyncReasoner` uses HermiT reasoner by default. You can choose between:
"HermiT", "Pellet", "JFact" and "Openllet". Although no significant
difference is noticed between these reasoners, they surely differentiate
in specific scenarios. You can check owlapi [Wiki](https://github.com/owlcs/owlapi/wiki) for more references.

_**owlapi version**: 5.1.9_

## Examples

You can check a usage example in the [_examples_](https://github.com/dice-group/owlapy/tree/develop/examples) folder.
You can see usage examples in the [_examples_](https://github.com/dice-group/owlapy/tree/develop/examples) folder.

[Test cases](https://github.com/dice-group/owlapy/tree/develop/tests) for the adaptor can also serve as an example, so you can
check that out as well.
[Test cases](https://github.com/dice-group/owlapy/tree/develop/tests)
can also serve as an example, so you can
check them out as well.
Loading

0 comments on commit 9a95b15

Please sign in to comment.