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

v1.3.0 merge #65

Merged
merged 48 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
ef501ef
changed OWLAPIMapper constructor
alkidbaci Aug 20, 2024
0029053
added JPype start and stop functions
alkidbaci Aug 20, 2024
847432a
added SyncOntologyManager and SyncOntology
alkidbaci Aug 20, 2024
e8a9687
OWLAPIAdaptor disbanded, logic moved to SyncReasoner
alkidbaci Aug 20, 2024
35ecec0
updated test to latest changes
alkidbaci Aug 20, 2024
78d9aef
SyncReasoner optional init using path
alkidbaci Aug 20, 2024
518ad88
Updated test/examples using SyncReasoner
alkidbaci Aug 20, 2024
b0bf90c
Ontology can be loaded using path as string also
alkidbaci Aug 20, 2024
f83f130
Added mapping for 'OntologyID' and 'Optional'
alkidbaci Aug 21, 2024
b2d18d8
Implemented methods of SyncOntology
alkidbaci Aug 21, 2024
9fe79f9
fixed new ontology creation
alkidbaci Aug 22, 2024
8dbf219
added test for sync ontology
alkidbaci Aug 22, 2024
790f68d
add ruff workflow
alkidbaci Aug 26, 2024
417a114
Updates based on ruff's linting report
alkidbaci Aug 26, 2024
265f15f
Linting related changes
alkidbaci Aug 26, 2024
e826b10
Fixed converter and some of its tests
alkidbaci Aug 26, 2024
b85448e
updated path of ontology
alkidbaci Aug 26, 2024
2c31466
added tests for sync reasoner
alkidbaci Aug 27, 2024
8529011
updated path
alkidbaci Aug 27, 2024
10b27fe
added jgrapht java package
alkidbaci Aug 27, 2024
94f6e71
updated data_property_domains test
alkidbaci Aug 27, 2024
f5fdc1a
Merge branch 'develop' into ruff-integration
alkidbaci Aug 27, 2024
96f8c28
set paths to relative
alkidbaci Aug 27, 2024
8adea84
removed domain test
alkidbaci Aug 27, 2024
8ad0f48
fixed test
alkidbaci Aug 28, 2024
6d9b483
moved add/remove axiom and save methods to Ontology class
alkidbaci Aug 28, 2024
9ef50ea
updated references of add/remove axiom and save methods
alkidbaci Aug 28, 2024
ed6ceb5
updated README
alkidbaci Aug 28, 2024
99315fd
A script to show that OWL Reasoners return the same retrieval results…
Demirrr Aug 30, 2024
c29c235
two helper functions are edded
Demirrr Aug 30, 2024
5579798
A Todo added for the owl class
Demirrr Aug 30, 2024
31ad8dd
updated abstract and base classes
alkidbaci Sep 2, 2024
4ad2677
updated documentation for owlapi synchronization
alkidbaci Sep 3, 2024
f38f579
Merge remote-tracking branch 'origin/develop' into ruff-integration
alkidbaci Sep 4, 2024
6200ad4
added test from develop
alkidbaci Sep 4, 2024
5bca8f8
updated example
alkidbaci Sep 4, 2024
d0abb5d
Merge pull request #62 from dice-group/ruff-integration
alkidbaci Sep 4, 2024
c12c3a7
moved reasoner abstract classes to new module
alkidbaci Sep 4, 2024
e48c25d
removed optional arguments from abstract methods of OWLReasoner
alkidbaci Sep 9, 2024
7016b29
flexible 'direct' argument
alkidbaci Sep 9, 2024
aada550
flexible 'direct' argument for ind_data_properties
alkidbaci Sep 9, 2024
1afb0fa
Moved abstract classes to 'abstracts' directory
alkidbaci Sep 9, 2024
4e30719
Fixed circular imports
alkidbaci Sep 9, 2024
8ecb9eb
Added structural reasoner
alkidbaci Sep 9, 2024
25f0848
turned 'abstracts' into a package
alkidbaci Sep 10, 2024
612beb9
new release
alkidbaci Sep 10, 2024
007563b
Merge pull request #64 from dice-group/base_changes
alkidbaci Sep 10, 2024
d463825
version increase
alkidbaci Sep 10, 2024
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
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
Loading