diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml new file mode 100644 index 00000000..b6690e05 --- /dev/null +++ b/.github/workflows/ruff.yml @@ -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 \ No newline at end of file diff --git a/README.md b/README.md index 56c1d08d..a7ca34b0 100644 --- a/README.md +++ b/README.md @@ -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) @@ -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 ?s_1 . ?s_1 a . ?x a . } } + +# 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 ?s_1 . ?s_1 a . ?x a . } } + +# 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 @@ -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() ``` @@ -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", @@ -108,7 +123,7 @@ adaptor.infer_axioms_and_save(output_path="KGs/Family/inferred_family-benchmark_ "InferredSubClassAxiomGenerator", "InferredInverseObjectPropertiesAxiomGenerator", "InferredEquivalentClassAxiomGenerator"]) -adaptor.stopJVM() +stopJVM() ``` diff --git a/docs/conf.py b/docs/conf.py index 55a57422..dedc20f6 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -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 diff --git a/docs/usage/main.md b/docs/usage/main.md index fa94d25d..7692a88f 100644 --- a/docs/usage/main.md +++ b/docs/usage/main.md @@ -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) diff --git a/docs/usage/ontologies.md b/docs/usage/ontologies.md index 098eaa2f..73d2477d 100644 --- a/docs/usage/ontologies.md +++ b/docs/usage/ontologies.md @@ -78,7 +78,7 @@ 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 @@ -86,9 +86,8 @@ 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 @@ -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. @@ -129,7 +128,7 @@ 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 @@ -137,7 +136,7 @@ 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. @@ -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 @@ -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: ```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. ```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. diff --git a/docs/usage/owlapi_adaptor.md b/docs/usage/owlapi_adaptor.md index 3cfb288a..ded4b81e 100644 --- a/docs/usage/owlapi_adaptor.md +++ b/docs/usage/owlapi_adaptor.md @@ -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. diff --git a/examples/comparing_adaptor_reasoners.py b/examples/comparing_adaptor_reasoners.py index d0c1c50a..25222601 100644 --- a/examples/comparing_adaptor_reasoners.py +++ b/examples/comparing_adaptor_reasoners.py @@ -1,8 +1,7 @@ from owlapy.owl_property import OWLObjectProperty -from owlapy.owlapi_adaptor import OWLAPIAdaptor +from owlapy.owl_reasoner import SyncReasoner from owlapy.iri import IRI -from owlapy.class_expression import OWLClass, OWLObjectIntersectionOf, OWLObjectAllValuesFrom, OWLObjectComplementOf -from owlapy.providers import owl_datatype_min_exclusive_restriction +from owlapy.class_expression import OWLClass, OWLObjectAllValuesFrom, OWLObjectComplementOf import time ontology_location = "../KGs/Carcinogenesis/carcinogenesis.owl" @@ -11,21 +10,21 @@ 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')), +for rsn in ["HermiT", "Pellet", "JFact", "Openllet"]: + reasoner = SyncReasoner(ontology_location, rsn) + # TODO AB: needs a more complex class expression to show the specific differences of the reasoners + 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)) + if rsn == "HermiT": + i1 = set(reasoner.instances(ce)) + elif rsn == "Pellet": + i2 = set(reasoner.instances(ce)) + elif rsn == "JFact": + i3 = set(reasoner.instances(ce)) + elif rsn == "Openllet": + i4 = set(reasoner.instances(ce)) print("Hermit-Pellet:") [print(_) for _ in i1-i2] diff --git a/examples/ontology_modification.py b/examples/ontology_modification.py index 87d6e1ce..577e2c1d 100644 --- a/examples/ontology_modification.py +++ b/examples/ontology_modification.py @@ -1,5 +1,5 @@ from owlapy.class_expression import OWLClass -from owlapy.owl_axiom import OWLDeclarationAxiom, OWLClassAxiom, OWLClassAssertionAxiom +from owlapy.owl_axiom import OWLDeclarationAxiom, OWLClassAssertionAxiom from owlapy.owl_individual import OWLNamedIndividual from owlapy.owl_ontology_manager import OntologyManager from owlapy.iri import IRI @@ -10,7 +10,7 @@ # Load the 'father' ontology using a new ontology manager. manager = OntologyManager() -onto = manager.load_ontology(IRI.create(f'file://../KGs/Family/father.owl')) +onto = manager.load_ontology(IRI.create('file://../KGs/Family/father.owl')) # Let's see what classes does this ontology has [print(_) for _ in onto.classes_in_signature()] @@ -18,8 +18,8 @@ # Create a new class new_class = OWLClass(IRI.create('http://example.com/father#child')) -# Add a declaration axiom for this class using ontology manager -manager.add_axiom(ontology=onto, axiom=OWLDeclarationAxiom(new_class)) +# Add a declaration axiom for this class +onto.add_axiom(axiom=OWLDeclarationAxiom(new_class)) # Check whether the new class is added in the signature of the ontology print("------------------------") @@ -27,7 +27,7 @@ # Add an individual of type child in the ontology new_ind = OWLNamedIndividual('http://example.com/father#lulu') -manager.add_axiom(onto, OWLClassAssertionAxiom(new_ind, new_class)) +onto.add_axiom(OWLClassAssertionAxiom(new_ind, new_class)) # Check if Lulu is added @@ -35,7 +35,7 @@ [print(_) for _ in onto.individuals_in_signature()] # Save the modified ontology locally (otherwise the changes will be lost) -manager.save_ontology(ontology=onto, document_iri=IRI.create("file:/../KGs/Family/father_modified.owl")) +onto.save(document_iri=IRI.create("file:/../KGs/Family/father_modified.owl")) # NOTE: using the same name will overwrite the current file with the new one. diff --git a/examples/ontology_reasoning.py b/examples/ontology_reasoning.py index c48f655b..dd12db7f 100644 --- a/examples/ontology_reasoning.py +++ b/examples/ontology_reasoning.py @@ -4,7 +4,7 @@ from owlapy.owl_individual import OWLNamedIndividual from owlapy.owl_ontology_manager import OntologyManager from owlapy.owl_property import OWLDataProperty, OWLObjectProperty -from owlapy.owl_reasoner import OntologyReasoner +from owlapy.owl_reasoner import OntologyReasoner, FastInstanceCheckerReasoner data_file = '../KGs/Test/test_ontology.owl' NS = 'http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#' @@ -61,7 +61,7 @@ e = OWLNamedIndividual(IRI(NS, "e")) g = OWLNamedIndividual(IRI(NS, "g")) m = OWLNamedIndividual(IRI(NS, "m")) -l = OWLNamedIndividual(IRI(NS, "l")) +l = OWLNamedIndividual(IRI(NS, "l")) # noqa: E741 n = OWLNamedIndividual(IRI(NS, "n")) o = OWLNamedIndividual(IRI(NS, "o")) p = OWLNamedIndividual(IRI(NS, "p")) @@ -93,11 +93,11 @@ J = OWLClass(IRI(NS, 'J')) K = OWLClass(IRI(NS, 'K')) H = OWLClass(IRI(NS, 'H')) -I = OWLClass(IRI(NS, 'I')) +I = OWLClass(IRI(NS, 'I')) # noqa: E741 L = OWLClass(IRI(NS, 'L')) M = OWLClass(IRI(NS, 'M')) N = OWLClass(IRI(NS, 'N')) -O = OWLClass(IRI(NS, 'O')) +O = OWLClass(IRI(NS, 'O')) # noqa: E741 P = OWLClass(IRI(NS, 'P')) Q = OWLClass(IRI(NS, 'Q')) R = OWLClass(IRI(NS, 'R')) @@ -117,20 +117,20 @@ r1T = OWLObjectSomeValuesFrom(property=r1, filler=OWLClass(IRI('http://www.w3.org/2002/07/owl#', 'Thing'))) manager = OntologyManager() -onto = manager.load_ontology(IRI.create(f'file://' + data_file)) +onto = manager.load_ontology(IRI.create('file://' + data_file)) -manager.add_axiom(onto, OWLEquivalentObjectPropertiesAxiom([r6, r5])) -manager.add_axiom(onto, OWLEquivalentObjectPropertiesAxiom([r5, r6])) -manager.add_axiom(onto, OWLObjectPropertyDomainAxiom(r1, ST)) +onto.add_axiom(OWLEquivalentObjectPropertiesAxiom([r6, r5])) +onto.add_axiom(OWLEquivalentObjectPropertiesAxiom([r5, r6])) +onto.add_axiom(OWLObjectPropertyDomainAxiom(r1, ST)) -manager.add_axiom(onto, OWLSubClassOfAxiom(R, r5Q)) -manager.add_axiom(onto, OWLSubClassOfAxiom(ST, U)) +onto.add_axiom(OWLSubClassOfAxiom(R, r5Q)) +onto.add_axiom(OWLSubClassOfAxiom(ST, U)) base_reasoner = OntologyReasoner(onto) # ---------------------------------------- Reasoning ---------------------------------------- -reasoner = FastInstanceCheckerReasoner(onto) +reasoner = FastInstanceCheckerReasoner(onto, base_reasoner) # Instances t1 = list(reasoner.instances(N)) diff --git a/examples/owl_reasoners.py b/examples/owl_reasoners.py new file mode 100644 index 00000000..90584e24 --- /dev/null +++ b/examples/owl_reasoners.py @@ -0,0 +1,124 @@ +""" A script to show that OWL Reasoners return the same retrieval results in different runtimes """ +import time + +from owlapy.class_expression import OWLClass, OWLObjectSomeValuesFrom, OWLObjectAllValuesFrom +from owlapy.owl_ontology_manager import OntologyManager +from owlapy.owl_reasoner import SyncReasoner +from owlapy.utils import concept_reducer_properties + +import matplotlib.pyplot as plt +import seaborn as sns +import numpy as np + +ontology_path = "../KGs/Family/family-benchmark_rich_background.owl" + +owl_reasoners = dict() +owl_reasoners["HermiT"] = SyncReasoner(ontology=ontology_path, reasoner="HermiT") +owl_reasoners["Pellet"] = SyncReasoner(ontology=ontology_path, reasoner="Pellet") +owl_reasoners["JFact"] = SyncReasoner(ontology=ontology_path, reasoner="JFact") +owl_reasoners["Openllet"] = SyncReasoner(ontology=ontology_path, reasoner="Openllet") +onto = OntologyManager().load_ontology(ontology_path) +c: OWLClass +################################################################### +# GENERATE ALCQ CONCEPTS TO EVALUATE RETRIEVAL PERFORMANCES +# (3) R: Extract object properties. +object_properties = {i for i in onto.object_properties_in_signature()} +# (4) R⁻: Inverse of object properties. +object_properties_inverse = {i.get_inverse_property() for i in object_properties} +# (5) R*: R UNION R⁻. +object_properties_and_inverse = object_properties.union(object_properties_inverse) +# (6) NC: Named owl concepts. +nc = {i for i in onto.classes_in_signature()} +# (7) NC⁻: Complement of NC. +nnc = {i.get_object_complement_of() for i in nc} +# (8) \exist r. C s.t. C \in NC and r \in R* . +exist_nc = concept_reducer_properties( + concepts=nc, + properties=object_properties_and_inverse, + cls=OWLObjectSomeValuesFrom) +# (9) \forall r. C s.t. C \in NC and r \in R* . +forall_nc = concept_reducer_properties( + concepts=nc, + properties=object_properties_and_inverse, + cls=OWLObjectAllValuesFrom, +) + + +def eval_reasoners(iter_owl_exp, mapping): + print("Number of expressions:", len(iter_owl_exp)) + results = dict() + runtime_results = dict() + for c in iter_owl_exp: + for name_i, reasoner_i in mapping.items(): + start_time_i = time.time() + result_reasoner_i = {i.str for i in reasoner_i.instances(c)} + runtime_results.setdefault(name_i, []).append(time.time() - start_time_i) + for name_j, reasoner_j in mapping.items(): + if name_i == name_j: + continue # Skip self-comparison + start_time_j = time.time() + result_reasoner_j = {i.str for i in reasoner_j.instances(c)} + runtime_results.setdefault(name_i, []).append(time.time() - start_time_j) + # Compute intersection and union + size_intersection = len(result_reasoner_i.intersection(result_reasoner_j)) + size_of_union = len(result_reasoner_i.union(result_reasoner_j)) + + # Compute Jaccard similarity + sim = 1.0 if size_of_union == 0 else size_intersection / size_of_union + results.setdefault(name_i, {}).setdefault(name_j, []).append(sim) + # Calculate average runtime for each reasoner + average_runtime = {name: sum(times) / len(times) for name, times in runtime_results.items()} + return results, average_runtime + + +def plot_runtimes(average_runtime, title='Average Runtime of OWL Reasoners'): + # Plotting the bar plot + plt.bar(average_runtime.keys(), average_runtime.values(), color='skyblue') + plt.title(title) + plt.ylabel('Average Runtime (seconds)') + plt.xticks(rotation=45) + max_rt = max(average_runtime.values()) + plt.ylim(0, max_rt * 1.1) + plt.tight_layout() + # Display the plot + plt.show() + + +def plot_similarity_btw_reasoners(results): + # Compute average Jaccard similarity for each pair of reasoners + average_jaccard = {name_i: {name_j: sum(sim_list) / len(sim_list) for name_j, sim_list in comparisons.items()} for + name_i, comparisons in results.items()} + + # Convert the dictionary into a matrix for heatmap plotting + reasoners = list(owl_reasoners.keys()) + matrix = np.zeros((len(reasoners), len(reasoners))) + + for i, name_i in enumerate(reasoners): + for j, name_j in enumerate(reasoners): + if name_i == name_j: + matrix[i][j] = 1.0 + else: + matrix[i][j] = average_jaccard.get(name_i, {}).get(name_j, 0.0) + + # Plotting the heatmap + sns.heatmap(matrix, xticklabels=reasoners, yticklabels=reasoners, annot=True, cmap="coolwarm", cbar=True, vmin=0, + vmax=1) + plt.title('Jaccard Similarity Between OWL Reasoners') + plt.xlabel('Reasoner') + plt.ylabel('Reasoner') + plt.show() + + +# EVAL Named Concepts +similarity_results, average_runtime_owl_reasoners = eval_reasoners(nc, owl_reasoners) +plot_similarity_btw_reasoners(similarity_results) +plot_runtimes(average_runtime_owl_reasoners, title="Avg Runtime of Reasoners on Named Concepts") +# EVAL Negated Concepts +similarity_results, average_runtime_owl_reasoners = eval_reasoners(nnc, owl_reasoners) +plot_similarity_btw_reasoners(similarity_results) +plot_runtimes(average_runtime_owl_reasoners, title="Avg Runtime of Reasoners on Negated Named Concepts") + +# EVAL Exist R. NC +similarity_results, average_runtime_owl_reasoners = eval_reasoners(exist_nc, owl_reasoners) +plot_similarity_btw_reasoners(similarity_results) +plot_runtimes(average_runtime_owl_reasoners, title="Avg Runtime of Reasoners on OWLObjectSomeValuesFrom") diff --git a/examples/using_owlapi_adaptor.py b/examples/using_owlapi_adaptor.py index 44d82039..88a6b405 100644 --- a/examples/using_owlapi_adaptor.py +++ b/examples/using_owlapi_adaptor.py @@ -1,13 +1,14 @@ -from owlapy.owlapi_adaptor import OWLAPIAdaptor +from owlapy.owl_reasoner import SyncReasoner from owlapy.iri import IRI from owlapy.class_expression import OWLClass, OWLObjectIntersectionOf +from owlapy.static_funcs import stopJVM ontology_location = "../KGs/Family/family-benchmark_rich_background.owl" -# Start an adaptor session and perform your operations. -adaptor = OWLAPIAdaptor(ontology_location, "HermiT") +# Create an SyncReasoner +reasoner = SyncReasoner(ontology_location, "HermiT") # Check ontology consistency -print(f"Is the given ontology consistent? --> {adaptor.has_consistent_ontology()}") +print(f"Is the given ontology consistent? --> {reasoner.has_consistent_ontology()}") # Construct an owlapy class expression brother = OWLClass(IRI.create("http://www.benchmark.org/family#Brother")) @@ -15,19 +16,19 @@ brother_and_father = OWLObjectIntersectionOf([brother, father]) # Find individual belonging to that class expression -instances = adaptor.instances(brother_and_father) +instances = reasoner.instances(brother_and_father) print("----------------------") print("Individuals that are brother and father at the same time:") [print(_) for _ in instances] # Map the class expression from owlapy to owlapi -py_to_pi = adaptor.mapper.map_(brother_and_father) +py_to_pi = reasoner.mapper.map_(brother_and_father) # Map the class expression from owlapi to owlapy -pi_to_py = adaptor.mapper.map_(py_to_pi) +pi_to_py = reasoner.mapper.map_(py_to_pi) print("----------------------") print(f"Owlapy ce: {pi_to_py}") # Stop the JVM to free the associated resources. -adaptor.stopJVM() # or jpype.shutdownJVM() +stopJVM() # or jpype.shutdownJVM() diff --git a/owlapy/__init__.py b/owlapy/__init__.py index f408d9d5..4f6b6692 100644 --- a/owlapy/__init__.py +++ b/owlapy/__init__.py @@ -1,4 +1,6 @@ -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.2.1' +from .render import (owl_expression_to_dl as owl_expression_to_dl, + owl_expression_to_manchester as owl_expression_to_manchester) +from .parser import (dl_to_owl_expression as dl_to_owl_expression, + manchester_to_owl_expression as manchester_to_owl_expression) +from .converter import owl_expression_to_sparql as owl_expression_to_sparql +__version__ = '1.3.0' diff --git a/owlapy/abstracts/__init__.py b/owlapy/abstracts/__init__.py new file mode 100644 index 00000000..473f64d3 --- /dev/null +++ b/owlapy/abstracts/__init__.py @@ -0,0 +1,4 @@ +from .abstract_owl_ontology_manager import OWLOntologyManager, OWLOntologyChange, OWLOntology +from .abstract_owl_reasoner import OWLReasoner, OWLReasonerEx + +__all__ = ['OWLOntologyManager', 'OWLOntologyChange', 'OWLOntology', 'OWLReasoner', 'OWLReasonerEx'] diff --git a/owlapy/abstracts/abstract_owl_ontology.py b/owlapy/abstracts/abstract_owl_ontology.py new file mode 100644 index 00000000..2b8b5172 --- /dev/null +++ b/owlapy/abstracts/abstract_owl_ontology.py @@ -0,0 +1,185 @@ +from abc import ABCMeta, abstractmethod +from typing import Final, Iterable, Union, Optional, TypeVar + +from owlapy.class_expression import OWLClass +from owlapy.iri import IRI +from owlapy.owl_axiom import OWLEquivalentClassesAxiom, OWLClassAxiom, OWLDataPropertyDomainAxiom, \ + OWLDataPropertyRangeAxiom, OWLObjectPropertyDomainAxiom, OWLObjectPropertyRangeAxiom, OWLAxiom +from owlapy.owl_individual import OWLNamedIndividual +from owlapy.owl_object import OWLObject +from owlapy.owl_property import OWLDataProperty, OWLObjectProperty + +_M = TypeVar('_M', bound='OWLOntologyManager') # noqa: F821 +_OI = TypeVar('_OI', bound='OWLOntologyID') # noqa: F821 + + +class OWLOntology(OWLObject, metaclass=ABCMeta): + """Represents an OWL 2 Ontology in the OWL 2 specification. + + An OWLOntology consists of a possibly empty set of OWLAxioms and a possibly empty set of OWLAnnotations. + An ontology can have an ontology IRI which can be used to identify the ontology. If it has an ontology IRI then + it may also have an ontology version IRI. Since OWL 2, an ontology need not have an ontology IRI. (See the OWL 2 + Structural Specification). + + An ontology cannot be modified directly. Changes must be applied via its OWLOntologyManager. + """ + __slots__ = () + type_index: Final = 1 + + @abstractmethod + def classes_in_signature(self) -> Iterable[OWLClass]: + """Gets the classes in the signature of this object. + + Returns: + Classes in the signature of this object. + """ + pass + + @abstractmethod + def data_properties_in_signature(self) -> Iterable[OWLDataProperty]: + """Get the data properties that are in the signature of this object. + + Returns: + Data properties that are in the signature of this object. + """ + pass + + @abstractmethod + def object_properties_in_signature(self) -> Iterable[OWLObjectProperty]: + """A convenience method that obtains the object properties that are in the signature of this object. + + Returns: + Object properties that are in the signature of this object. + """ + pass + + @abstractmethod + def individuals_in_signature(self) -> Iterable[OWLNamedIndividual]: + """A convenience method that obtains the individuals that are in the signature of this object. + + Returns: + Individuals that are in the signature of this object. + """ + pass + + @abstractmethod + def equivalent_classes_axioms(self, c: OWLClass) -> Iterable[OWLEquivalentClassesAxiom]: + """ Gets all of the equivalent axioms in this ontology that contain the specified class as an operand. + + Args: + c: The class for which the EquivalentClasses axioms should be retrieved. + + Returns: + EquivalentClasses axioms contained in this ontology. + """ + pass + + @abstractmethod + def general_class_axioms(self) -> Iterable[OWLClassAxiom]: + """Get the general class axioms of this ontology. This includes SubClass axioms with a complex class expression + as the sub class and EquivalentClass axioms and DisjointClass axioms with only complex class expressions. + + Returns: + General class axioms contained in this ontology. + """ + pass + + @abstractmethod + def data_property_domain_axioms(self, property: OWLDataProperty) -> Iterable[OWLDataPropertyDomainAxiom]: + """Gets the OWLDataPropertyDomainAxiom objects where the property is equal to the specified property. + + Args: + property: The property which is equal to the property of the retrieved axioms. + + Returns: + The axioms matching the search. + """ + pass + + @abstractmethod + def data_property_range_axioms(self, property: OWLDataProperty) -> Iterable[OWLDataPropertyRangeAxiom]: + """Gets the OWLDataPropertyRangeAxiom objects where the property is equal to the specified property. + + Args: + property: The property which is equal to the property of the retrieved axioms. + + Returns: + The axioms matching the search. + """ + pass + + @abstractmethod + def object_property_domain_axioms(self, property: OWLObjectProperty) -> Iterable[OWLObjectPropertyDomainAxiom]: + """Gets the OWLObjectPropertyDomainAxiom objects where the property is equal to the specified property. + + Args: + property: The property which is equal to the property of the retrieved axioms. + + Returns: + The axioms matching the search. + """ + pass + + @abstractmethod + def object_property_range_axioms(self, property: OWLObjectProperty) -> Iterable[OWLObjectPropertyRangeAxiom]: + """Gets the OWLObjectPropertyRangeAxiom objects where the property is equal to the specified property. + + Args: + property: The property which is equal to the property of the retrieved axioms. + + Returns: + The axioms matching the search. + """ + pass + + @abstractmethod + def get_owl_ontology_manager(self) -> _M: + """Gets the manager that manages this ontology.""" + pass + + @abstractmethod + def get_ontology_id(self) -> _OI: + """Gets the OWLOntologyID belonging to this object. + + Returns: + The OWLOntologyID. + """ + pass + + def is_anonymous(self) -> bool: + """Check whether this ontology does contain an IRI or not.""" + return self.get_ontology_id().is_anonymous() + + @abstractmethod + def add_axiom(self, axiom: Union[OWLAxiom, Iterable[OWLAxiom]]): + """Add the specified axiom/axioms to the ontology. + + Args: + axiom: Can be a single axiom or a collection of axioms. + + Returns: + Nothing. + """ + pass + + @abstractmethod + def remove_axiom(self, axiom: Union[OWLAxiom, Iterable[OWLAxiom]]): + """Removes the specified axiom/axioms to the ontology. + + Args: + axiom: Can be a single axiom or a collection of axioms. + + Returns: + Nothing. + """ + pass + + def save(self, document_iri: Optional[IRI] = None): + """Saves this ontology, using its IRI to determine where/how the ontology should be + saved. + + Args: + document_iri: Whether you want to save in a different location. + + """ + pass diff --git a/owlapy/abstracts/abstract_owl_ontology_manager.py b/owlapy/abstracts/abstract_owl_ontology_manager.py new file mode 100644 index 00000000..891b29a8 --- /dev/null +++ b/owlapy/abstracts/abstract_owl_ontology_manager.py @@ -0,0 +1,70 @@ +from abc import ABCMeta, abstractmethod +from typing import Union + +from owlapy.abstracts.abstract_owl_ontology import OWLOntology +from owlapy.iri import IRI + + +class OWLOntologyChange(metaclass=ABCMeta): + """Represents an ontology change.""" + __slots__ = () + + _ont: OWLOntology + + @abstractmethod + def __init__(self, ontology: OWLOntology): + self._ont = ontology + + def get_ontology(self) -> OWLOntology: + """Gets the ontology that the change is/was applied to. + + Returns: + The ontology that the change is applicable to. + """ + return self._ont + + +class OWLOntologyManager(metaclass=ABCMeta): + """An OWLOntologyManager manages a set of ontologies. It is the main point for creating, loading and accessing + ontologies.""" + + @abstractmethod + def create_ontology(self, iri: Union[str, IRI]) -> OWLOntology: + """Creates a new (empty) ontology that that has the specified ontology IRI (and no version IRI). + + Args: + iri: The IRI of the ontology to be created, can also be a string. + + Returns: + The newly created ontology. + """ + pass + + @abstractmethod + def load_ontology(self, iri: Union[IRI, str]) -> OWLOntology: + """Loads an ontology that is assumed to have the specified ontology IRI as its IRI or version IRI. The ontology + IRI will be mapped to an ontology document IRI. + + Args: + iri: The IRI that identifies the ontology, can also be a string. + It is expected that the ontology will also have this IRI + (although the OWL API should tolerate situations where this is not the case). + + Returns: + The OWLOntology representation of the ontology that was loaded. + """ + pass + + @abstractmethod + def apply_change(self, change: OWLOntologyChange): + """A convenience method that applies just one change to an ontology. When this method is used through an + OWLOntologyManager implementation, the instance used should be the one that the ontology returns through the + get_owl_ontology_manager() call. + + Args: + change: The change to be applied. + + Raises: + ChangeApplied.UNSUCCESSFULLY: if the change was not applied successfully. + """ + pass diff --git a/owlapy/abstracts/abstract_owl_reasoner.py b/owlapy/abstracts/abstract_owl_reasoner.py new file mode 100644 index 00000000..1ed1e01a --- /dev/null +++ b/owlapy/abstracts/abstract_owl_reasoner.py @@ -0,0 +1,471 @@ +"""OWL Reasoner""" +from abc import ABCMeta, abstractmethod +from inspect import signature +from typing import Iterable +import logging + +from owlapy.class_expression import OWLClassExpression +from owlapy.class_expression import OWLClass +from owlapy.owl_data_ranges import OWLDataRange +from owlapy.owl_object import OWLEntity +from owlapy.abstracts.abstract_owl_ontology import OWLOntology +from owlapy.owl_property import OWLObjectPropertyExpression, OWLDataProperty, OWLObjectProperty +from owlapy.owl_individual import OWLNamedIndividual +from owlapy.owl_literal import OWLLiteral + +logger = logging.getLogger(__name__) + + +class OWLReasoner(metaclass=ABCMeta): + """An OWLReasoner reasons over a set of axioms (the set of reasoner axioms) that is based on the imports closure of + a particular ontology - the "root" ontology.""" + __slots__ = () + + @abstractmethod + def __init__(self, ontology: OWLOntology): + pass + + @abstractmethod + def data_property_domains(self, pe: OWLDataProperty, direct: bool = False) -> Iterable[OWLClassExpression]: + """Gets the class expressions that are the direct or indirect domains of this property with respect to the + imports closure of the root ontology. + + Args: + pe: The property expression whose domains are to be retrieved. + direct: Specifies if the direct domains should be retrieved (True), or if all domains should be retrieved + (False). + + Returns: + :Let N = equivalent_classes(DataSomeValuesFrom(pe rdfs:Literal)). If direct is True: then if N is not + empty then the return value is N, else the return value is the result of + super_classes(DataSomeValuesFrom(pe rdfs:Literal), true). If direct is False: then the result of + super_classes(DataSomeValuesFrom(pe rdfs:Literal), false) together with N if N is non-empty. + (Note, rdfs:Literal is the top datatype). + """ + pass + + @abstractmethod + def object_property_domains(self, pe: OWLObjectProperty, direct: bool = False) -> Iterable[OWLClassExpression]: + """Gets the class expressions that are the direct or indirect domains of this property with respect to the + imports closure of the root ontology. + + Args: + pe: The property expression whose domains are to be retrieved. + direct: Specifies if the direct domains should be retrieved (True), or if all domains should be retrieved + (False). + + Returns: + :Let N = equivalent_classes(ObjectSomeValuesFrom(pe owl:Thing)). If direct is True: then if N is not empty + then the return value is N, else the return value is the result of + super_classes(ObjectSomeValuesFrom(pe owl:Thing), true). If direct is False: then the result of + super_classes(ObjectSomeValuesFrom(pe owl:Thing), false) together with N if N is non-empty. + """ + pass + + @abstractmethod + def object_property_ranges(self, pe: OWLObjectProperty, direct: bool = False) -> Iterable[OWLClassExpression]: + """Gets the class expressions that are the direct or indirect ranges of this property with respect to the + imports closure of the root ontology. + + Args: + pe: The property expression whose ranges are to be retrieved. + direct: Specifies if the direct ranges should be retrieved (True), or if all ranges should be retrieved + (False). + + Returns: + :Let N = equivalent_classes(ObjectSomeValuesFrom(ObjectInverseOf(pe) owl:Thing)). If direct is True: then + if N is not empty then the return value is N, else the return value is the result of + super_classes(ObjectSomeValuesFrom(ObjectInverseOf(pe) owl:Thing), true). If direct is False: then + the result of super_classes(ObjectSomeValuesFrom(ObjectInverseOf(pe) owl:Thing), false) together with N + if N is non-empty. + """ + pass + + @abstractmethod + def equivalent_classes(self, ce: OWLClassExpression) -> Iterable[OWLClassExpression]: + """Gets the class expressions that are equivalent to the specified class expression with respect to the set of + reasoner axioms. + + Args: + ce: The class expression whose equivalent classes are to be retrieved. + + Returns: + All class expressions C where the root ontology imports closure entails EquivalentClasses(ce C). If ce is + not a class name (i.e. it is an anonymous class expression) and there are no such classes C then there will + be no result. If ce is unsatisfiable with respect to the set of reasoner axioms then owl:Nothing, i.e. the + bottom node, will be returned. + """ + pass + + @abstractmethod + def disjoint_classes(self, ce: OWLClassExpression) -> Iterable[OWLClassExpression]: + """Gets the class expressions that are disjoint with specified class expression with respect to the set of + reasoner axioms. + + Args: + ce: The class expression whose disjoint classes are to be retrieved. + + Returns: + All class expressions D where the set of reasoner axioms entails EquivalentClasses(D ObjectComplementOf(ce)) + or StrictSubClassOf(D ObjectComplementOf(ce)). + """ + pass + + @abstractmethod + def different_individuals(self, ind: OWLNamedIndividual) -> Iterable[OWLNamedIndividual]: + """Gets the individuals that are different from the specified individual with respect to the set of + reasoner axioms. + + Args: + ind: The individual whose different individuals are to be retrieved. + + Returns: + All individuals x where the set of reasoner axioms entails DifferentIndividuals(ind x). + """ + pass + + @abstractmethod + def same_individuals(self, ind: OWLNamedIndividual) -> Iterable[OWLNamedIndividual]: + """Gets the individuals that are the same as the specified individual with respect to the set of + reasoner axioms. + + Args: + ind: The individual whose same individuals are to be retrieved. + + Returns: + All individuals x where the root ontology imports closure entails SameIndividual(ind x). + """ + pass + + @abstractmethod + def equivalent_object_properties(self, op: OWLObjectPropertyExpression) -> Iterable[OWLObjectPropertyExpression]: + """Gets the simplified object properties that are equivalent to the specified object property with respect + to the set of reasoner axioms. + + Args: + op: The object property whose equivalent object properties are to be retrieved. + + Returns: + All simplified object properties e where the root ontology imports closure entails + EquivalentObjectProperties(op e). If op is unsatisfiable with respect to the set of reasoner axioms + then owl:bottomDataProperty will be returned. + """ + pass + + @abstractmethod + def equivalent_data_properties(self, dp: OWLDataProperty) -> Iterable[OWLDataProperty]: + """Gets the data properties that are equivalent to the specified data property with respect to the set of + reasoner axioms. + + Args: + dp: The data property whose equivalent data properties are to be retrieved. + + Returns: + All data properties e where the root ontology imports closure entails EquivalentDataProperties(dp e). + If dp is unsatisfiable with respect to the set of reasoner axioms then owl:bottomDataProperty will + be returned. + """ + pass + + @abstractmethod + def data_property_values(self, e: OWLEntity, pe: OWLDataProperty) \ + -> Iterable['OWLLiteral']: + """Gets the data property values for the specified entity and data property expression. + + Args: + e: The owl entity (usually an individual) that is the subject of the data property values. + pe: The data property expression whose values are to be retrieved for the specified entity. + + Note: Can be used to get values, for example, of 'label' property of owl entities such as classes and properties + too (not only individuals). + + Returns: + A set of OWLLiterals containing literals such that for each literal l in the set, the set of reasoner + axioms entails DataPropertyAssertion(pe ind l). + """ + pass + + @abstractmethod + def object_property_values(self, ind: OWLNamedIndividual, pe: OWLObjectPropertyExpression) \ + -> Iterable[OWLNamedIndividual]: + """Gets the object property values for the specified individual and object property expression. + + Args: + ind: The individual that is the subject of the object property values. + pe: The object property expression whose values are to be retrieved for the specified individual. + + Returns: + The named individuals such that for each individual j, the set of reasoner axioms entails + ObjectPropertyAssertion(pe ind j). + """ + pass + + @abstractmethod + def instances(self, ce: OWLClassExpression, direct: bool = False) -> Iterable[OWLNamedIndividual]: + """Gets the individuals which are instances of the specified class expression. + + Args: + ce: The class expression whose instances are to be retrieved. + direct: Specifies if the direct instances should be retrieved (True), or if all instances should be + retrieved (False). + + Returns: + If direct is True, each named individual j where the set of reasoner axioms entails + DirectClassAssertion(ce, j). If direct is False, each named individual j where the set of reasoner axioms + entails ClassAssertion(ce, j). If ce is unsatisfiable with respect to the set of reasoner axioms then + nothing returned. + """ + pass + + @abstractmethod + def sub_classes(self, ce: OWLClassExpression, direct: bool = False) \ + -> Iterable[OWLClassExpression]: + """Gets the set of named classes that are the strict (potentially direct) subclasses of the specified class + expression with respect to the reasoner axioms. + + Args: + ce: The class expression whose strict (direct) subclasses are to be retrieved. + direct: Specifies if the direct subclasses should be retrieved (True) or if the all subclasses + (descendant) classes should be retrieved (False). + + Returns: + If direct is True, each class C where reasoner axioms entails DirectSubClassOf(C, ce). If direct is False, + each class C where reasoner axioms entails StrictSubClassOf(C, ce). If ce is equivalent to owl:Nothing then + nothing will be returned. + """ + pass + + @abstractmethod + def disjoint_object_properties(self, op: OWLObjectPropertyExpression) -> Iterable[OWLObjectPropertyExpression]: + """Gets the simplified object properties that are disjoint with the specified object property with respect + to the set of reasoner axioms. + + Args: + op: The object property whose disjoint object properties are to be retrieved. + + Returns: + All simplified object properties e where the root ontology imports closure entails + EquivalentObjectProperties(e ObjectPropertyComplementOf(op)) or + StrictSubObjectPropertyOf(e ObjectPropertyComplementOf(op)). + """ + pass + + @abstractmethod + def disjoint_data_properties(self, dp: OWLDataProperty) -> Iterable[OWLDataProperty]: + """Gets the data properties that are disjoint with the specified data property with respect + to the set of reasoner axioms. + + Args: + dp: The data property whose disjoint data properties are to be retrieved. + + Returns: + All data properties e where the root ontology imports closure entails + EquivalentDataProperties(e DataPropertyComplementOf(dp)) or + StrictSubDataPropertyOf(e DataPropertyComplementOf(dp)). + """ + pass + + @abstractmethod + def sub_data_properties(self, dp: OWLDataProperty, direct: bool = False) -> Iterable[OWLDataProperty]: + """Gets the set of named data properties that are the strict (potentially direct) subproperties of the + specified data property expression with respect to the imports closure of the root ontology. + + Args: + dp: The data property whose strict (direct) subproperties are to be retrieved. + direct: Specifies if the direct subproperties should be retrieved (True) or if the all subproperties + (descendants) should be retrieved (False). + + Returns: + If direct is True, each property P where the set of reasoner axioms entails DirectSubDataPropertyOf(P, pe). + If direct is False, each property P where the set of reasoner axioms entails + StrictSubDataPropertyOf(P, pe). If pe is equivalent to owl:bottomDataProperty then nothing will be + returned. + """ + pass + + @abstractmethod + def super_data_properties(self, dp: OWLDataProperty, direct: bool = False) -> Iterable[OWLDataProperty]: + """Gets the stream of data properties that are the strict (potentially direct) super properties of the + specified data property with respect to the imports closure of the root ontology. + + Args: + dp (OWLDataProperty): The data property whose super properties are to be retrieved. + direct (bool): Specifies if the direct super properties should be retrieved (True) or if the all + super properties (ancestors) should be retrieved (False). + + Returns: + Iterable of super properties. + """ + pass + + @abstractmethod + def sub_object_properties(self, op: OWLObjectPropertyExpression, direct: bool = False) \ + -> Iterable[OWLObjectPropertyExpression]: + """Gets the stream of simplified object property expressions that are the strict (potentially direct) + subproperties of the specified object property expression with respect to the imports closure of the root + ontology. + + Args: + op: The object property expression whose strict (direct) subproperties are to be retrieved. + direct: Specifies if the direct subproperties should be retrieved (True) or if the all subproperties + (descendants) should be retrieved (False). + + Returns: + If direct is True, simplified object property expressions, such that for each simplified object property + expression, P, the set of reasoner axioms entails DirectSubObjectPropertyOf(P, pe). + If direct is False, simplified object property expressions, such that for each simplified object property + expression, P, the set of reasoner axioms entails StrictSubObjectPropertyOf(P, pe). + If pe is equivalent to owl:bottomObjectProperty then nothing will be returned. + """ + pass + + @abstractmethod + def super_object_properties(self, op: OWLObjectPropertyExpression, direct: bool = False) \ + -> Iterable[OWLObjectPropertyExpression]: + """Gets the stream of object properties that are the strict (potentially direct) super properties of the + specified object property with respect to the imports closure of the root ontology. + + Args: + op (OWLObjectPropertyExpression): The object property expression whose super properties are to be + retrieved. + direct (bool): Specifies if the direct super properties should be retrieved (True) or if the all + super properties (ancestors) should be retrieved (False). + + Returns: + Iterable of super properties. + """ + pass + + @abstractmethod + def types(self, ind: OWLNamedIndividual, direct: bool = False) -> Iterable[OWLClass]: + """Gets the named classes which are (potentially direct) types of the specified named individual. + + Args: + ind: The individual whose types are to be retrieved. + direct: Specifies if the direct types should be retrieved (True), or if all types should be retrieved + (False). + + Returns: + If direct is True, each named class C where the set of reasoner axioms entails + DirectClassAssertion(C, ind). If direct is False, each named class C where the set of reasoner axioms + entails ClassAssertion(C, ind). + """ + pass + + @abstractmethod + def get_root_ontology(self) -> OWLOntology: + """Gets the "root" ontology that is loaded into this reasoner. The reasoner takes into account the axioms in + this ontology and its import's closure.""" + pass + + @abstractmethod + def super_classes(self, ce: OWLClassExpression, direct: bool = False) \ + -> Iterable[OWLClassExpression]: + """Gets the stream of named classes that are the strict (potentially direct) super classes of the specified + class expression with respect to the imports closure of the root ontology. + + Args: + ce: The class expression whose strict (direct) super classes are to be retrieved. + direct: Specifies if the direct super classes should be retrieved (True) or if the all super classes + (ancestors) classes should be retrieved (False). + + Returns: + If direct is True, each class C where the set of reasoner axioms entails DirectSubClassOf(ce, C). + If direct is False, each class C where set of reasoner axioms entails StrictSubClassOf(ce, C). + If ce is equivalent to owl:Thing then nothing will be returned. + """ + pass + + +class OWLReasonerEx(OWLReasoner, metaclass=ABCMeta): + """Extra convenience methods for OWL Reasoners""" + + # default + def data_property_ranges(self, pe: OWLDataProperty, direct: bool = False) -> Iterable[OWLDataRange]: + """Gets the data ranges that are the direct or indirect ranges of this property with respect to the imports + closure of the root ontology. + + Args: + pe: The property expression whose ranges are to be retrieved. + direct: Specifies if the direct ranges should be retrieved (True), or if all ranges should be retrieved + (False). + + Returns: + """ + for ax in self.get_root_ontology().data_property_range_axioms(pe): + yield ax.get_range() + if not direct: + logger.warning("indirect not implemented") + + # default + def all_data_property_values(self, pe: OWLDataProperty, direct: bool = True) -> Iterable[OWLLiteral]: + """Gets all values for the given data property expression that appear in the knowledge base. + + Args: + pe: The data property expression whose values are to be retrieved + direct: Specifies if only the direct values of the data property pe should be retrieved (True), or if + the values of sub properties of pe should be taken into account (False). + + Returns: + A set of OWLLiterals containing literals such that for each literal l in the set, the set of reasoner + axioms entails DataPropertyAssertion(pe ind l) for any ind. + """ + onto = self.get_root_ontology() + has_direct = "direct" in str(signature(self.data_property_values)) + for ind in onto.individuals_in_signature(): + if has_direct: + dpv = self.data_property_values(ind, pe, direct) + else: + dpv = self.data_property_values(ind, pe) + for lit in dpv: + yield lit + + # default + def ind_data_properties(self, ind: OWLNamedIndividual, direct: bool = True) -> Iterable[OWLDataProperty]: + """Gets all data properties for the given individual that appear in the knowledge base. + + Args: + ind: The named individual whose data properties are to be retrieved + direct: Specifies if the direct data properties should be retrieved (True), or if all + data properties should be retrieved (False), so that sub properties are taken into account. + + Returns: + All data properties pe where the set of reasoner axioms entails DataPropertyAssertion(pe ind l) + for atleast one l. + """ + onto = self.get_root_ontology() + has_direct = "direct" in str(signature(self.data_property_values)) + for dp in onto.data_properties_in_signature(): + try: + if has_direct: + next(iter(self.data_property_values(ind, dp, direct))) + else: + next(iter(self.data_property_values(ind, dp))) + yield dp + except StopIteration: + pass + + # default + def ind_object_properties(self, ind: OWLNamedIndividual, direct: bool = True) -> Iterable[OWLObjectProperty]: + """Gets all object properties for the given individual that appear in the knowledge base. + + Args: + ind: The named individual whose object properties are to be retrieved + direct: Specifies if the direct object properties should be retrieved (True), or if all + object properties should be retrieved (False), so that sub properties are taken into account. + + Returns: + All data properties pe where the set of reasoner axioms entails ObjectPropertyAssertion(pe ind ind2) + for atleast one ind2. + """ + onto = self.get_root_ontology() + has_direct = "direct" in str(signature(self.object_property_values)) + for op in onto.object_properties_in_signature(): + try: + if has_direct: + next(iter(self.object_property_values(ind, op, direct))) + else: + next(iter(self.object_property_values(ind, op))) + yield op + except StopIteration: + pass diff --git a/owlapy/class_expression/__init__.py b/owlapy/class_expression/__init__.py index f4e8d56c..5ea67ee2 100644 --- a/owlapy/class_expression/__init__.py +++ b/owlapy/class_expression/__init__.py @@ -34,8 +34,20 @@ OWLDataExactCardinality, OWLObjectOneOf ) + +__all__ = ['OWLClassExpression', 'OWLAnonymousClassExpression', 'OWLBooleanClassExpression', 'OWLObjectComplementOf', + 'OWLNaryBooleanClassExpression', 'OWLObjectUnionOf', 'OWLObjectIntersectionOf', 'OWLRestriction', + 'OWLQuantifiedRestriction', 'OWLObjectCardinalityRestriction', 'OWLObjectHasSelf', 'OWLObjectHasValue', + 'OWLQuantifiedDataRestriction', 'OWLObjectSomeValuesFrom', 'OWLObjectAllValuesFrom', + 'OWLDatatypeRestriction', 'OWLFacet', 'OWLQuantifiedObjectRestriction', 'OWLObjectRestriction', + 'OWLHasValueRestriction', 'OWLDataRestriction', 'OWLCardinalityRestriction', 'OWLFacetRestriction', + 'OWLObjectMinCardinality', 'OWLObjectMaxCardinality', 'OWLObjectExactCardinality', 'OWLDataSomeValuesFrom', + 'OWLDataAllValuesFrom', 'OWLDataHasValue', 'OWLClass', 'OWLDataMinCardinality', 'OWLDataMaxCardinality', + 'OWLDataExactCardinality', 'OWLObjectOneOf', 'OWLDataOneOf', 'OWLDataCardinalityRestriction'] + from typing import Final from ..vocab import OWLRDFVocabulary OWLThing: Final = OWLClass(OWLRDFVocabulary.OWL_THING.iri) #: : :The OWL Class corresponding to owl:Thing OWLNothing: Final = OWLClass(OWLRDFVocabulary.OWL_NOTHING.iri) #: : :The OWL Class corresponding to owl:Nothing + diff --git a/owlapy/class_expression/nary_boolean_expression.py b/owlapy/class_expression/nary_boolean_expression.py index 1a417341..0cd2a683 100644 --- a/owlapy/class_expression/nary_boolean_expression.py +++ b/owlapy/class_expression/nary_boolean_expression.py @@ -25,7 +25,7 @@ def __repr__(self): return f'{type(self).__name__}({repr(self._operands)})' def __eq__(self, other): - if type(other) == type(self): + if type(other) is type(self): return self._operands == other._operands return NotImplemented diff --git a/owlapy/class_expression/owl_class.py b/owlapy/class_expression/owl_class.py index c006e481..fd821f45 100644 --- a/owlapy/class_expression/owl_class.py +++ b/owlapy/class_expression/owl_class.py @@ -52,6 +52,9 @@ def is_owl_nothing(self) -> bool: return self._is_nothing def get_object_complement_of(self) -> OWLObjectComplementOf: + # TODO: CD: get_object_complement_of is not correct term. + # TODO: CD : we might want to use get_complement_of instead + # documented in parent return OWLObjectComplementOf(self) diff --git a/owlapy/class_expression/restriction.py b/owlapy/class_expression/restriction.py index 04e7a4bc..2ccc7248 100644 --- a/owlapy/class_expression/restriction.py +++ b/owlapy/class_expression/restriction.py @@ -163,7 +163,7 @@ def __repr__(self): f"property={repr(self.get_property())},{self.get_cardinality()},filler={repr(self.get_filler())})" def __eq__(self, other): - if type(other) == type(self): + if type(other) is type(self): return self._property == other._property \ and self._cardinality == other._cardinality \ and self._filler == other._filler @@ -335,7 +335,7 @@ def get_property(self) -> OWLObjectPropertyExpression: return self._property def __eq__(self, other): - if type(other) == type(self): + if type(other) is type(self): return self._property == other._property return NotImplemented @@ -429,7 +429,7 @@ def __hash__(self): return hash(self._values) def __eq__(self, other): - if type(other) == type(self): + if type(other) is type(self): return self._values == other._values return NotImplemented @@ -492,7 +492,7 @@ def __repr__(self): f"property={repr(self.get_property())},{self.get_cardinality()},filler={repr(self.get_filler())})" def __eq__(self, other): - if type(other) == type(self): + if type(other) is type(self): return self._property == other._property \ and self._cardinality == other._cardinality \ and self._filler == other._filler @@ -746,7 +746,7 @@ def __hash__(self): return hash(self._values) def __eq__(self, other): - if type(other) == type(self): + if type(other) is type(self): return self._values == other._values return NotImplemented diff --git a/owlapy/converter.py b/owlapy/converter.py index fb7ee3f4..8a8a2f8f 100644 --- a/owlapy/converter.py +++ b/owlapy/converter.py @@ -508,7 +508,7 @@ def _(self, ce: OWLObjectOneOf): self.append(",") assert isinstance(ind, OWLNamedIndividual) self.append(f"<{ind.to_string_id()}>") - self.append(f" ) )") + self.append(" ) )") @process.register def _(self, ce: OWLDataSomeValuesFrom): @@ -573,7 +573,7 @@ def _(self, node: OWLDataOneOf): self.append(",") if value: self.append(self.render(value)) - self.append(f" ) ) ") + self.append(" ) ) ") @process.register def _(self, node: OWLDatatypeRestriction): @@ -624,10 +624,10 @@ def as_query(self, q = [f"VALUES {root_variable} {{ "] for x in values: q.append(f"<{x.to_string_id()}>") - q.append(f"}} . ") + q.append("} . ") qs.extend(q) qs.extend(tp) - qs.append(f" }}") + qs.append(" }") query = "\n".join(qs) diff --git a/owlapy/jar_dependencies/jgrapht-core-1.2.0.jar b/owlapy/jar_dependencies/jgrapht-core-1.2.0.jar new file mode 100644 index 00000000..9f943dd1 Binary files /dev/null and b/owlapy/jar_dependencies/jgrapht-core-1.2.0.jar differ diff --git a/owlapy/meta_classes.py b/owlapy/meta_classes.py index b4874863..6186a4c8 100644 --- a/owlapy/meta_classes.py +++ b/owlapy/meta_classes.py @@ -13,7 +13,7 @@ class HasIRI(metaclass=ABCMeta): @property @abstractmethod - def iri(self) -> 'IRI': + def iri(self): """Gets the IRI of this object. Returns: diff --git a/owlapy/owl_annotation.py b/owlapy/owl_annotation.py index 7c9ea565..9d4a14c9 100644 --- a/owlapy/owl_annotation.py +++ b/owlapy/owl_annotation.py @@ -1,7 +1,6 @@ """OWL Annotations""" from abc import ABCMeta from .owl_object import OWLObject -from typing import Optional class OWLAnnotationObject(OWLObject, metaclass=ABCMeta): @@ -9,10 +8,10 @@ class OWLAnnotationObject(OWLObject, metaclass=ABCMeta): __slots__ = () # noinspection PyMethodMayBeStatic - def as_iri(self) -> Optional['IRI']: + def as_iri(self): """ Returns: - if the value is an IRI, return it. Return Mone otherwise. + if the value is an IRI, return it. Return None otherwise. """ return None @@ -43,7 +42,7 @@ def is_literal(self) -> bool: return False # noinspection PyMethodMayBeStatic - def as_literal(self) -> Optional['OWLLiteral']: + def as_literal(self): """ Returns: if the value is a literal, returns it. Return None otherwise diff --git a/owlapy/owl_axiom.py b/owlapy/owl_axiom.py index 2792738c..b1c46c2f 100644 --- a/owlapy/owl_axiom.py +++ b/owlapy/owl_axiom.py @@ -2,7 +2,7 @@ from abc import ABCMeta, abstractmethod from itertools import combinations -from typing import TypeVar, List, Optional, Iterable, Generic, Final, Union +from typing import TypeVar, List, Optional, Iterable, Generic, Union from .owl_property import OWLDataPropertyExpression, OWLObjectPropertyExpression from .owl_object import OWLObject, OWLEntity from .owl_datatype import OWLDatatype, OWLDataRange @@ -14,9 +14,9 @@ from owlapy.owl_annotation import OWLAnnotationSubject, OWLAnnotationValue from .owl_literal import OWLLiteral -_C = TypeVar('_C', bound='OWLObject') #: -_P = TypeVar('_P', bound='OWLPropertyExpression') #: -_R = TypeVar('_R', bound='OWLPropertyRange') #: +_C = TypeVar('_C', bound='OWLObject') # noqa: F821 +_P = TypeVar('_P', bound='OWLPropertyExpression') # noqa: F821 +_R = TypeVar('_R', bound='OWLPropertyRange') # noqa: F821 class OWLAxiom(OWLObject, metaclass=ABCMeta): diff --git a/owlapy/owl_data_ranges.py b/owlapy/owl_data_ranges.py index 3ad248f5..bcead840 100644 --- a/owlapy/owl_data_ranges.py +++ b/owlapy/owl_data_ranges.py @@ -41,7 +41,7 @@ def __repr__(self): return f'{type(self).__name__}({repr(self._operands)})' def __eq__(self, other): - if type(other) == type(self): + if type(other) is type(self): return self._operands == other._operands return NotImplemented diff --git a/owlapy/owl_hierarchy.py b/owlapy/owl_hierarchy.py index 108e35db..3cc2b8be 100644 --- a/owlapy/owl_hierarchy.py +++ b/owlapy/owl_hierarchy.py @@ -9,7 +9,7 @@ from owlapy.meta_classes import HasIRI from owlapy.owl_literal import OWLTopObjectProperty, OWLBottomObjectProperty, OWLTopDataProperty, OWLBottomDataProperty from owlapy.owl_property import OWLObjectProperty, OWLDataProperty -from owlapy.owl_reasoner import OWLReasoner +from owlapy.abstracts.abstract_owl_reasoner import OWLReasoner _S = TypeVar('_S', bound=HasIRI) #: _U = TypeVar('_U', bound='AbstractHierarchy') #: diff --git a/owlapy/owl_object.py b/owlapy/owl_object.py index c2ccbc80..b90e9888 100644 --- a/owlapy/owl_object.py +++ b/owlapy/owl_object.py @@ -1,7 +1,9 @@ """OWL Base classes""" from abc import abstractmethod, ABCMeta from .meta_classes import HasIRI +from typing import TypeVar +_I = TypeVar('_I', bound='IRI') # noqa: F821 class OWLObject(metaclass=ABCMeta): """Base interface for OWL objects""" @@ -68,7 +70,7 @@ class OWLNamedObject(OWLObject, HasIRI, metaclass=ABCMeta): IRI as its name.""" __slots__ = () - _iri: 'IRI' + _iri: _I def __eq__(self, other): if type(other) is type(self): diff --git a/owlapy/owl_ontology.py b/owlapy/owl_ontology.py index 9e9d2f6f..fdf7acbf 100644 --- a/owlapy/owl_ontology.py +++ b/owlapy/owl_ontology.py @@ -1,16 +1,14 @@ """OWL Ontology""" -from abc import ABCMeta, abstractmethod -from functools import singledispatchmethod -from itertools import chain +from functools import singledispatchmethod, singledispatch +from itertools import chain, islice, combinations +import types from types import MappingProxyType -from typing import Iterable, TypeVar, Final, Optional, Union +from typing import Iterable, TypeVar, Final, Optional, Union, cast import logging import owlready2 from pandas import Timedelta from owlapy import namespaces -from owlapy.owl_axiom import OWLEquivalentClassesAxiom, OWLClassAxiom, OWLDataPropertyDomainAxiom, \ - OWLDataPropertyRangeAxiom, OWLObjectPropertyDomainAxiom, OWLObjectPropertyRangeAxiom, OWLSubClassOfAxiom, \ - OWLAnnotationProperty +from owlapy.abstracts.abstract_owl_ontology import OWLOntology from owlapy.owl_data_ranges import OWLDataRange, OWLDataComplementOf, OWLDataUnionOf, OWLDataIntersectionOf from owlapy.owl_datatype import OWLDatatype from owlapy.owl_individual import OWLNamedIndividual, OWLIndividual @@ -22,11 +20,23 @@ OWLObjectIntersectionOf, OWLObjectSomeValuesFrom, OWLObjectAllValuesFrom, OWLObjectOneOf, OWLObjectExactCardinality, \ OWLObjectMaxCardinality, OWLObjectMinCardinality, OWLObjectHasValue, OWLDataSomeValuesFrom, OWLDataAllValuesFrom, \ OWLDataExactCardinality, OWLDataMaxCardinality, OWLDataMinCardinality, OWLDataHasValue, OWLDataOneOf, \ - OWLDatatypeRestriction, OWLRestriction, OWLObjectRestriction, OWLDataRestriction, OWLFacetRestriction + OWLDatatypeRestriction, OWLRestriction, OWLObjectRestriction, OWLDataRestriction, OWLFacetRestriction, \ + OWLNaryBooleanClassExpression, OWLQuantifiedObjectRestriction, OWLQuantifiedDataRestriction from owlapy.owl_property import OWLDataProperty, OWLObjectProperty, OWLPropertyExpression, OWLObjectInverseOf, \ - OWLObjectPropertyExpression, OWLDataPropertyExpression + OWLObjectPropertyExpression, OWLDataPropertyExpression, OWLProperty from datetime import date, datetime - +from owlready2 import destroy_entity, AllDisjoint, AllDifferent, GeneralClassAxiom +from owlapy.owl_axiom import (OWLObjectPropertyRangeAxiom, OWLAxiom, OWLSubClassOfAxiom, OWLEquivalentClassesAxiom, \ + OWLDisjointUnionAxiom, OWLAnnotationAssertionAxiom, OWLAnnotationProperty, OWLSubPropertyAxiom, \ + OWLPropertyRangeAxiom, OWLClassAssertionAxiom, OWLDeclarationAxiom, OWLObjectPropertyAssertionAxiom, \ + OWLSymmetricObjectPropertyAxiom, OWLTransitiveObjectPropertyAxiom, OWLPropertyDomainAxiom, \ + OWLAsymmetricObjectPropertyAxiom, OWLDataPropertyCharacteristicAxiom, OWLFunctionalDataPropertyAxiom, \ + OWLReflexiveObjectPropertyAxiom, OWLDataPropertyAssertionAxiom, OWLFunctionalObjectPropertyAxiom, \ + OWLObjectPropertyCharacteristicAxiom, OWLIrreflexiveObjectPropertyAxiom, OWLInverseFunctionalObjectPropertyAxiom, \ + OWLDisjointDataPropertiesAxiom, OWLDisjointObjectPropertiesAxiom, OWLEquivalentDataPropertiesAxiom, \ + OWLEquivalentObjectPropertiesAxiom, OWLInverseObjectPropertiesAxiom, OWLNaryPropertyAxiom, OWLNaryIndividualAxiom, \ + OWLDifferentIndividualsAxiom, OWLDisjointClassesAxiom, OWLSameIndividualAxiom, OWLClassAxiom, + OWLDataPropertyDomainAxiom, OWLDataPropertyRangeAxiom, OWLObjectPropertyDomainAxiom) from owlapy.vocab import OWLFacet logger = logging.getLogger(__name__) @@ -45,7 +55,9 @@ _VERSION_IRI: Final = IRI.create(namespaces.OWL, "versionIRI") -_M = TypeVar('_M', bound='OWLOntologyManager') +_M = TypeVar('_M', bound='OWLOntologyManager') # noqa: F821 +_OM = TypeVar('_OM', bound='OntologyManager') # noqa: F821 +_SM = TypeVar('_SM', bound='SyncOntologyManager') # noqa: F821 class OWLOntologyID: @@ -111,152 +123,668 @@ def __eq__(self, other): return NotImplemented -class OWLOntology(OWLObject, metaclass=ABCMeta): - """Represents an OWL 2 Ontology in the OWL 2 specification. - - An OWLOntology consists of a possibly empty set of OWLAxioms and a possibly empty set of OWLAnnotations. - An ontology can have an ontology IRI which can be used to identify the ontology. If it has an ontology IRI then - it may also have an ontology version IRI. Since OWL 2, an ontology need not have an ontology IRI. (See the OWL 2 - Structural Specification). - - An ontology cannot be modified directly. Changes must be applied via its OWLOntologyManager. +def _check_expression(expr: OWLObject, ontology: OWLOntology, world: owlready2.namespace.World): """ - __slots__ = () - type_index: Final = 1 - - @abstractmethod - def classes_in_signature(self) -> Iterable[OWLClass]: - """Gets the classes in the signature of this object. - - Returns: - Classes in the signature of this object. - """ - pass - - @abstractmethod - def data_properties_in_signature(self) -> Iterable[OWLDataProperty]: - """Get the data properties that are in the signature of this object. - - Returns: - Data properties that are in the signature of this object. - """ - pass - - @abstractmethod - def object_properties_in_signature(self) -> Iterable[OWLObjectProperty]: - """A convenience method that obtains the object properties that are in the signature of this object. + @TODO:CD: Documentation + Creates all entities (individuals, classes, properties) that appear in the given (complex) class expression + and do not exist in the given ontology yet - Returns: - Object properties that are in the signature of this object. - """ - pass - - @abstractmethod - def individuals_in_signature(self) -> Iterable[OWLNamedIndividual]: - """A convenience method that obtains the individuals that are in the signature of this object. - - Returns: - Individuals that are in the signature of this object. - """ - pass - - @abstractmethod - def equivalent_classes_axioms(self, c: OWLClass) -> Iterable[OWLEquivalentClassesAxiom]: - """ Gets all of the equivalent axioms in this ontology that contain the specified class as an operand. - - Args: - c: The class for which the EquivalentClasses axioms should be retrieved. - - Returns: - EquivalentClasses axioms contained in this ontology. - """ - pass - - @abstractmethod - def general_class_axioms(self) -> Iterable[OWLClassAxiom]: - """Get the general class axioms of this ontology. This includes SubClass axioms with a complex class expression - as the sub class and EquivalentClass axioms and DisjointClass axioms with only complex class expressions. - - Returns: - General class axioms contained in this ontology. - """ - pass - - @abstractmethod - def data_property_domain_axioms(self, property: OWLDataProperty) -> Iterable[OWLDataPropertyDomainAxiom]: - """Gets the OWLDataPropertyDomainAxiom objects where the property is equal to the specified property. - - Args: - property: The property which is equal to the property of the retrieved axioms. - - Returns: - The axioms matching the search. - """ - pass - - @abstractmethod - def data_property_range_axioms(self, property: OWLDataProperty) -> Iterable[OWLDataPropertyRangeAxiom]: - """Gets the OWLDataPropertyRangeAxiom objects where the property is equal to the specified property. - - Args: - property: The property which is equal to the property of the retrieved axioms. - - Returns: - The axioms matching the search. - """ - pass - - @abstractmethod - def object_property_domain_axioms(self, property: OWLObjectProperty) -> Iterable[OWLObjectPropertyDomainAxiom]: - """Gets the OWLObjectPropertyDomainAxiom objects where the property is equal to the specified property. + """ + if isinstance(expr, (OWLClass, OWLProperty, OWLNamedIndividual,)): + _add_axiom(OWLDeclarationAxiom(expr), ontology, world) + elif isinstance(expr, (OWLNaryBooleanClassExpression, OWLObjectComplementOf, OWLObjectOneOf,)): + for op in expr.operands(): + _check_expression(op, ontology, world) + elif isinstance(expr, (OWLQuantifiedObjectRestriction, OWLObjectHasValue,)): + _check_expression(expr.get_property(), ontology, world) + _check_expression(expr.get_filler(), ontology, world) + elif isinstance(expr, OWLObjectInverseOf): + _check_expression(expr.get_named_property(), ontology, world) + _check_expression(expr.get_inverse_property(), ontology, world) + elif isinstance(expr, (OWLQuantifiedDataRestriction, OWLDataHasValue,)): + _check_expression(expr.get_property(), ontology, world) + elif not isinstance(expr, OWLObject): + raise ValueError(f'({expr}) is not an OWLObject.') + + +@singledispatch +def _add_axiom(axiom: OWLAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + raise NotImplementedError(f'Axiom type {axiom} is not implemented yet.') + + +@_add_axiom.register +def _(axiom: OWLDeclarationAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) + + entity = axiom.get_entity() + with ont_x: + entity_x = world[entity.to_string_id()] + # Entity already exists + if entity_x is not None: + return + + thing_x: owlready2.entity.ThingClass = conv.map_concept(OWLThing) + if isinstance(entity, OWLClass): + if entity.is_owl_thing() or entity.is_owl_nothing(): + return + entity_x = types.new_class(name=entity.iri.get_remainder(), bases=(thing_x,)) + elif isinstance(entity, OWLIndividual): + entity_x = thing_x(entity.iri.get_remainder()) + elif isinstance(entity, OWLObjectProperty): + entity_x = types.new_class(name=entity.iri.get_remainder(), bases=(owlready2.ObjectProperty,)) + elif isinstance(entity, OWLDataProperty): + entity_x = types.new_class(name=entity.iri.get_remainder(), bases=(owlready2.DatatypeProperty,)) + elif isinstance(entity, OWLAnnotationProperty): + entity_x = types.new_class(name=entity.iri.get_remainder(), bases=(owlready2.AnnotationProperty,)) + else: + raise ValueError(f'Cannot add ({entity}). Not an atomic class, property, or individual.') + entity_x.namespace = ont_x.get_namespace(entity.iri.get_namespace()) + entity_x.namespace.world._refactor(entity_x.storid, entity_x.iri) + + +@_add_axiom.register +def _(axiom: OWLClassAssertionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) + + individual = axiom.get_individual() + cls_ = axiom.get_class_expression() + _check_expression(cls_, ontology, world) + _add_axiom(OWLDeclarationAxiom(individual), ontology, world) + with ont_x: + cls_x = conv.map_concept(cls_) + ind_x = conv._to_owlready2_individual(individual) + thing_x = conv.map_concept(OWLThing) + if thing_x in ind_x.is_a: + ind_x.is_a.remove(thing_x) + ind_x.is_a.append(cls_x) + + +@_add_axiom.register +def _(axiom: OWLObjectPropertyAssertionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) + + subject = axiom.get_subject() + property_ = axiom.get_property() + object_ = axiom.get_object() + _add_axiom(OWLDeclarationAxiom(subject), ontology, world) + _add_axiom(OWLDeclarationAxiom(property_), ontology, world) + _add_axiom(OWLDeclarationAxiom(object_), ontology, world) + with ont_x: + subject_x = conv._to_owlready2_individual(subject) + property_x = conv._to_owlready2_property(property_) + object_x = conv._to_owlready2_individual(object_) + property_x[subject_x].append(object_x) + + +@_add_axiom.register +def _(axiom: OWLDataPropertyAssertionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) + + subject = axiom.get_subject() + property_ = axiom.get_property() + _add_axiom(OWLDeclarationAxiom(subject), ontology, world) + _add_axiom(OWLDeclarationAxiom(property_), ontology, world) + with ont_x: + subject_x = conv._to_owlready2_individual(subject) + property_x = conv._to_owlready2_property(property_) + property_x[subject_x].append(axiom.get_object().to_python()) + + +@_add_axiom.register +def _(axiom: OWLSubClassOfAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) + + sub_class = axiom.get_sub_class() + super_class = axiom.get_super_class() + + _check_expression(sub_class, ontology, world) + _check_expression(super_class, ontology, world) + with ont_x: + thing_x = conv.map_concept(OWLThing) + sub_class_x = conv.map_concept(sub_class) + super_class_x = conv.map_concept(super_class) + if isinstance(sub_class, OWLClass): + if thing_x in sub_class_x.is_a: + sub_class_x.is_a.remove(thing_x) + else: + # Currently owlready2 seems to expect that we make a new GeneralClassAxiom object each time. + # Another option would be to check whether a GeneralClassAxiom with the sub_class_x already exists and just + # add the super_class_x to its is_a attribute + sub_class_x = GeneralClassAxiom(sub_class_x) + sub_class_x.is_a.append(super_class_x) + + +# TODO: Update as soon as owlready2 adds support for EquivalentClasses general class axioms +@_add_axiom.register +def _(axiom: OWLEquivalentClassesAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x = conv.map_object(ontology) + + assert axiom.contains_named_equivalent_class(), 'Owlready2 does not support general' \ + 'class axioms for equivalent classes.' + for ce in axiom.class_expressions(): + _check_expression(ce, ontology, world) + with ont_x: + for ce_1, ce_2 in combinations(axiom.class_expressions(), 2): + assert ce_1 is not None, f"ce_1 cannot be None: {ce_1}, {type(ce_1)}" + assert ce_2 is not None, f"ce_2_x cannot be None: {ce_2}, {type(ce_2)}" + + ce_1_x = conv.map_concept(ce_1) + ce_2_x = conv.map_concept(ce_2) + try: + assert ce_1_x is not None, f"ce_1_x cannot be None: {ce_1_x}, {type(ce_1_x)}" + assert ce_2_x is not None, f"ce_2_x cannot be None: {ce_2_x}, {type(ce_2_x)}" + except AssertionError: + print("function of ToOwlready2.map_concept() returns None") + print(ce_1, ce_1_x) + print(ce_2, ce_2_x) + print("Axiom:", axiom) + print("Temporary solution is reinitializing ce_1_x=ce_2_x\n\n") + ce_1_x=ce_2_x + + if isinstance(ce_1_x, owlready2.ThingClass): + ce_1_x.equivalent_to.append(ce_2_x) + if isinstance(ce_2_x, owlready2.ThingClass): + ce_2_x.equivalent_to.append(ce_1_x) + + +@_add_axiom.register +def _(axiom: OWLDisjointClassesAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.Ontology = conv.map_object(ontology) + + for cls_ in axiom.class_expressions(): + _check_expression(cls_, ontology, world) + with ont_x: + # TODO: If the first element in the list is a complex class expression owlready2 is bugged + # and creates an AllDifferent axiom + AllDisjoint(list(map(conv.map_concept, axiom.class_expressions()))) + + +@_add_axiom.register +def _(axiom: OWLDisjointUnionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.Ontology = conv.map_object(ontology) + + assert isinstance(axiom.get_owl_class(), OWLClass), f'({axiom.get_owl_class()}) is not a named class.' + _add_axiom(OWLDeclarationAxiom(axiom.get_owl_class()), ontology, world) + for cls_ in axiom.get_class_expressions(): + _check_expression(cls_, ontology, world) + with ont_x: + cls_x = conv.map_concept(axiom.get_owl_class()) + cls_x.disjoint_unions.append(list(map(conv.map_concept, axiom.get_class_expressions()))) + + +@_add_axiom.register +def _(axiom: OWLAnnotationAssertionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.Ontology = conv.map_object(ontology) + + prop_x = conv.map_object(axiom.get_property()) + if prop_x is None: + with ont_x: + prop_x: owlready2.annotation.AnnotationPropertyClass = cast( + owlready2.AnnotationProperty, + types.new_class( + name=axiom.get_property().iri.get_remainder(), + bases=(owlready2.AnnotationProperty,))) + prop_x.namespace = ont_x.get_namespace(axiom.get_property().iri.get_namespace()) + sub_x = world[axiom.get_subject().as_iri().as_str()] + assert sub_x is not None, f'{axiom.get_subject} not found in {ontology}' + with ont_x: + if axiom.get_value().is_literal(): + literal = axiom.get_value().as_literal() + setattr(sub_x, prop_x.python_name, literal.to_python()) + else: + o_x = world[axiom.get_value().as_iri().as_str()] + assert o_x is not None, f'{axiom.get_value()} not found in {ontology}' + setattr(sub_x, prop_x.python_name, o_x) + + +@_add_axiom.register +def _(axiom: OWLNaryIndividualAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.Ontology = conv.map_object(ontology) + + for ind in axiom.individuals(): + _add_axiom(OWLDeclarationAxiom(ind), ontology, world) + with ont_x: + if isinstance(axiom, OWLSameIndividualAxiom): + for idx, ind in enumerate(axiom.individuals()): + ind_x = conv._to_owlready2_individual(ind) + for ind_2 in islice(axiom.individuals(), idx + 1, None): + ind_2_x = conv._to_owlready2_individual(ind_2) + ind_x.equivalent_to.append(ind_2_x) + elif isinstance(axiom, OWLDifferentIndividualsAxiom): + AllDifferent(list(map(conv._to_owlready2_individual, axiom.individuals()))) + else: + raise ValueError(f'OWLNaryIndividualAxiom ({axiom}) is not defined.') + + +@_add_axiom.register +def _(axiom: OWLSubPropertyAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.Ontology = conv.map_object(ontology) + + sub_property = axiom.get_sub_property() + super_property = axiom.get_super_property() + _add_axiom(OWLDeclarationAxiom(sub_property), ontology, world) + _add_axiom(OWLDeclarationAxiom(super_property), ontology, world) + with ont_x: + sub_property_x = conv._to_owlready2_property(sub_property) + super_property_x = conv._to_owlready2_property(super_property) + sub_property_x.is_a.append(super_property_x) + + +@_add_axiom.register +def _(axiom: OWLPropertyDomainAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.Ontology = conv.map_object(ontology) + + property_ = axiom.get_property() + domain = axiom.get_domain() + _add_axiom(OWLDeclarationAxiom(property_), ontology, world) + _check_expression(domain, ontology, world) + with ont_x: + property_x = conv._to_owlready2_property(property_) + domain_x = conv.map_concept(domain) + property_x.domain.append(domain_x) + + +@_add_axiom.register +def _(axiom: OWLPropertyRangeAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.Ontology = conv.map_object(ontology) + + property_ = axiom.get_property() + range_ = axiom.get_range() + _add_axiom(OWLDeclarationAxiom(property_), ontology, world) + if isinstance(axiom, OWLObjectPropertyRangeAxiom): + _check_expression(range_, ontology, world) + with ont_x: + property_x = conv._to_owlready2_property(property_) + range_x = conv.map_concept(range_) if isinstance(axiom, OWLObjectPropertyRangeAxiom) \ + else conv.map_datarange(range_) + property_x.range.append(range_x) + + +@_add_axiom.register +def _(axiom: OWLNaryPropertyAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.Ontology = conv.map_object(ontology) + + for property_ in axiom.properties(): + _add_axiom(OWLDeclarationAxiom(property_), ontology, world) + with ont_x: + if isinstance(axiom, (OWLEquivalentObjectPropertiesAxiom, OWLEquivalentDataPropertiesAxiom,)): + for idx, property_ in enumerate(axiom.properties()): + property_x = conv._to_owlready2_property(property_) + for property_2 in islice(axiom.properties(), idx + 1, None): + property_2_x = conv._to_owlready2_property(property_2) + property_x.equivalent_to.append(property_2_x) + elif isinstance(axiom, (OWLDisjointObjectPropertiesAxiom, OWLDisjointDataPropertiesAxiom,)): + AllDisjoint(list(map(conv._to_owlready2_property, axiom.properties()))) + elif isinstance(axiom, OWLInverseObjectPropertiesAxiom): + property_first_x = conv._to_owlready2_property(axiom.get_first_property()) + property_second_x = conv._to_owlready2_property(axiom.get_second_property()) + if property_second_x.inverse_property is not None: + property_second_x.inverse_property = None + property_first_x.inverse_property = property_second_x + else: + raise ValueError(f'OWLNaryPropertyAxiom ({axiom}) is not defined.') + + +@_add_axiom.register +def _(axiom: OWLObjectPropertyCharacteristicAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.Ontology = conv.map_object(ontology) + + property_ = axiom.get_property() + _add_axiom(OWLDeclarationAxiom(property_), ontology, world) + with ont_x: + property_x = conv._to_owlready2_property(property_) + if isinstance(axiom, OWLFunctionalObjectPropertyAxiom): + property_x.is_a.append(owlready2.FunctionalProperty) + elif isinstance(axiom, OWLAsymmetricObjectPropertyAxiom): + property_x.is_a.append(owlready2.AsymmetricProperty) + elif isinstance(axiom, OWLInverseFunctionalObjectPropertyAxiom): + property_x.is_a.append(owlready2.InverseFunctionalProperty) + elif isinstance(axiom, OWLIrreflexiveObjectPropertyAxiom): + property_x.is_a.append(owlready2.IrreflexiveProperty) + elif isinstance(axiom, OWLReflexiveObjectPropertyAxiom): + property_x.is_a.append(owlready2.ReflexiveProperty) + elif isinstance(axiom, OWLSymmetricObjectPropertyAxiom): + property_x.is_a.append(owlready2.SymmetricProperty) + elif isinstance(axiom, OWLTransitiveObjectPropertyAxiom): + property_x.is_a.append(owlready2.TransitiveProperty) + else: + raise ValueError(f'ObjectPropertyCharacteristicAxiom ({axiom}) is not defined.') - Args: - property: The property which is equal to the property of the retrieved axioms. - Returns: - The axioms matching the search. - """ - pass +@_add_axiom.register +def _(axiom: OWLDataPropertyCharacteristicAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.Ontology = conv.map_object(ontology) - @abstractmethod - def object_property_range_axioms(self, property: OWLObjectProperty) -> Iterable[OWLObjectPropertyRangeAxiom]: - """Gets the OWLObjectPropertyRangeAxiom objects where the property is equal to the specified property. + property_ = axiom.get_property() + _add_axiom(OWLDeclarationAxiom(property_), ontology, world) + with ont_x: + property_x = conv._to_owlready2_property(property_) + if isinstance(axiom, OWLFunctionalDataPropertyAxiom): + property_x.is_a.append(owlready2.FunctionalProperty) + else: + raise ValueError(f'DataPropertyCharacteristicAxiom ({axiom}) is not defined.') + + +@singledispatch +def _remove_axiom(axiom: OWLAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + raise NotImplementedError(f'Axiom type {axiom} is not implemented yet.') + + +@_remove_axiom.register +def _(axiom: OWLDeclarationAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) + with ont_x: + entity_x = world[axiom.get_entity().to_string_id()] + if entity_x is not None: + # TODO: owlready2 seems to be bugged for properties here + destroy_entity(entity_x) + + +@_remove_axiom.register +def _(axiom: OWLClassAssertionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) + + with ont_x: + cls_x = conv.map_concept(axiom.get_class_expression()) + ind_x = conv._to_owlready2_individual(axiom.get_individual()) + if cls_x is None or ind_x is None: + return + if cls_x in ind_x.is_a: + ind_x.is_a.remove(cls_x) + elif isinstance(axiom.get_class_expression(), OWLClass): + ont_x._del_obj_triple_spo(ind_x.storid, owlready2.rdf_type, cls_x.storid) + + +@_remove_axiom.register +def _(axiom: OWLObjectPropertyAssertionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) + + with ont_x: + subject_x = conv._to_owlready2_individual(axiom.get_subject()) + property_x = conv._to_owlready2_property(axiom.get_property()) + object_x = conv._to_owlready2_individual(axiom.get_object()) + if all([subject_x, property_x, object_x]) and object_x in property_x[subject_x]: + property_x[subject_x].remove(object_x) + + +@_remove_axiom.register +def _(axiom: OWLDataPropertyAssertionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) + + with ont_x: + subject_x = conv._to_owlready2_individual(axiom.get_subject()) + property_x = conv._to_owlready2_property(axiom.get_property()) + object_ = axiom.get_object().to_python() + if subject_x is not None and property_x is not None and object_ in property_x[subject_x]: + property_x[subject_x].remove(object_) + + +@_remove_axiom.register +def _(axiom: OWLSubClassOfAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) + sub_class = axiom.get_sub_class() + super_class = axiom.get_super_class() + + with ont_x: + sub_class_x = conv.map_concept(sub_class) + super_class_x = conv.map_concept(super_class) + if sub_class_x is None or super_class_x is None: + return + + if isinstance(sub_class, OWLClass): + if super_class_x in sub_class_x.is_a: + sub_class_x.is_a.remove(super_class_x) + elif isinstance(axiom.get_sub_class(), OWLClass) and isinstance(axiom.get_super_class(), OWLClass): + ont_x._del_obj_triple_spo(sub_class_x.storid, owlready2.rdfs_subclassof, super_class_x.storid) + else: + for ca in ont_x.general_class_axioms(): + if ca.left_side == sub_class_x and super_class_x in ca.is_a: + ca.is_a.remove(super_class_x) + + +# TODO: Update as soons as owlready2 adds support for EquivalentClasses general class axioms +@_remove_axiom.register +def _(axiom: OWLEquivalentClassesAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x = conv.map_object(ontology) + + if not axiom.contains_named_equivalent_class(): + return + + with ont_x: + ces_x = list(map(conv.map_concept, axiom.class_expressions())) + if len(ces_x) < 2 or not all(ces_x): + return + + for ce_1_x, ce_2_x in combinations(ces_x, 2): + if isinstance(ce_2_x, owlready2.ThingClass) and ce_1_x in ce_2_x.equivalent_to: + ce_2_x.equivalent_to.remove(ce_1_x) + if isinstance(ce_1_x, owlready2.ThingClass) and ce_2_x in ce_1_x.equivalent_to: + ce_1_x.equivalent_to.remove(ce_2_x) + + +@_remove_axiom.register +def _(axiom: OWLDisjointClassesAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.Ontology = conv.map_object(ontology) + + with ont_x: + class_expressions_x = set(map(conv.map_concept, axiom.class_expressions())) + if len(class_expressions_x) < 2 or not all(class_expressions_x): + return + for disjoints_x in ont_x.disjoint_classes(): + if set(disjoints_x.entities) == class_expressions_x: + del disjoints_x.entities[:-1] + break + + +@_remove_axiom.register +def _(axiom: OWLDisjointUnionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.Ontology = conv.map_object(ontology) + assert isinstance(axiom.get_owl_class(), OWLClass), f'({axiom.get_owl_class()}) is not a named class.' + + with ont_x: + cls_x = conv.map_concept(axiom.get_owl_class()) + union_expressions_x = set(map(conv.map_concept, axiom.get_class_expressions())) + if cls_x is not None and all(union_expressions_x): + for union_x in cls_x.disjoint_unions: + if union_expressions_x == set(union_x): + cls_x.disjoint_unions.remove(union_x) + break + + +@_remove_axiom.register +def _(axiom: OWLAnnotationAssertionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.Ontology = conv.map_object(ontology) + + sub_x = world[axiom.get_subject().as_iri().as_str()] + if sub_x is None: + return + name = axiom.get_property().iri.get_remainder() + with ont_x: + if axiom.get_value().is_literal(): + o_x = axiom.get_value().as_literal().to_python() + else: + o_x = world[axiom.get_value().as_iri().as_str()] + + value = getattr(sub_x, name, None) + if value is not None and o_x in value: + value.remove(o_x) + + +@_remove_axiom.register +def _(axiom: OWLNaryIndividualAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.Ontology = conv.map_object(ontology) + + with ont_x: + individuals_x = list(map(conv._to_owlready2_individual, axiom.individuals())) + if len(individuals_x) < 2 or not all(individuals_x): + return + if isinstance(axiom, OWLSameIndividualAxiom): + if set(individuals_x[1:-1]) <= set(individuals_x[0].INDIRECT_equivalent_to): + for individual_1_x, individual_2_x in combinations(individuals_x, 2): + if individual_1_x in individual_2_x.equivalent_to: + individual_2_x.equivalent_to.remove(individual_1_x) + if individual_2_x in individual_1_x.equivalent_to: + individual_1_x.equivalent_to.remove(individual_2_x) + elif isinstance(axiom, OWLDifferentIndividualsAxiom): + individuals_x = set(individuals_x) + for different_x in ont_x.different_individuals(): + if set(different_x.entities) == individuals_x: + del different_x.entities[:-1] + break + else: + raise ValueError(f'OWLNaryIndividualAxiom ({axiom}) is not defined.') - Args: - property: The property which is equal to the property of the retrieved axioms. - Returns: - The axioms matching the search. - """ - pass +@_remove_axiom.register +def _(axiom: OWLSubPropertyAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) - @abstractmethod - def get_owl_ontology_manager(self) -> _M: - """Gets the manager that manages this ontology.""" - pass + with ont_x: + sub_property_x = conv._to_owlready2_property(axiom.get_sub_property()) + super_property_x = conv._to_owlready2_property(axiom.get_super_property()) + if sub_property_x is None or super_property_x is None: + return + if super_property_x in sub_property_x.is_a: + sub_property_x.is_a.remove(super_property_x) + else: + ont_x._del_obj_triple_spo(sub_property_x.storid, owlready2.rdfs_subpropertyof, super_property_x.storid) + + +@_remove_axiom.register +def _(axiom: OWLPropertyDomainAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.Ontology = conv.map_object(ontology) + + with ont_x: + property_x = conv._to_owlready2_property(axiom.get_property()) + domain_x = conv.map_concept(axiom.get_domain()) + if domain_x is not None and property_x is not None and domain_x in property_x.domain: + property_x.domain.remove(domain_x) + + +@_remove_axiom.register +def _(axiom: OWLPropertyRangeAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.Ontology = conv.map_object(ontology) + + with ont_x: + property_x = conv._to_owlready2_property(axiom.get_property()) + range_x = conv.map_concept(axiom.get_range()) \ + if isinstance(axiom, OWLObjectPropertyRangeAxiom) else conv.map_datarange(axiom.get_range()) + if range_x is not None and property_x is not None and range_x in property_x.range: + property_x.range.remove(range_x) + + +@_remove_axiom.register +def _(axiom: OWLNaryPropertyAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.Ontology = conv.map_object(ontology) + + with ont_x: + properties_x = list(map(conv._to_owlready2_property, axiom.properties())) + if len(properties_x) < 2 or not all(properties_x): + return + if isinstance(axiom, (OWLEquivalentObjectPropertiesAxiom, OWLEquivalentDataPropertiesAxiom,)): + # Check if all equivalent properties are defined in the ontology + if set(properties_x[1:-1]) <= set(properties_x[0].INDIRECT_equivalent_to): + for property_1_x, property_2_x in combinations(properties_x, 2): + if property_1_x in property_2_x.equivalent_to: + property_2_x.equivalent_to.remove(property_1_x) + if property_2_x in property_1_x.equivalent_to: + property_1_x.equivalent_to.remove(property_2_x) + elif isinstance(axiom, (OWLDisjointObjectPropertiesAxiom, OWLDisjointDataPropertiesAxiom,)): + properties_x = set(properties_x) + for disjoints_x in ont_x.disjoint_properties(): + if set(disjoints_x.entities) == properties_x: + del disjoints_x.entities[:-1] + break + elif isinstance(axiom, OWLInverseObjectPropertiesAxiom): + if len(properties_x) != 2: + return + first = properties_x[0] + second = properties_x[1] + if first.inverse_property == second and second.inverse_property == first: + first.inverse_property = None + else: + raise ValueError(f'OWLNaryPropertyAxiom ({axiom}) is not defined.') + + +@_remove_axiom.register +def _(axiom: OWLObjectPropertyCharacteristicAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.Ontology = conv.map_object(ontology) + + with ont_x: + property_x = conv._to_owlready2_property(axiom.get_property()) + if property_x is None: + return + + if isinstance(axiom, OWLFunctionalObjectPropertyAxiom) and owlready2.FunctionalProperty in property_x.is_a: + property_x.is_a.remove(owlready2.FunctionalProperty) + elif isinstance(axiom, OWLAsymmetricObjectPropertyAxiom) and owlready2.AsymmetricProperty in property_x.is_a: + property_x.is_a.remove(owlready2.AsymmetricProperty) + elif isinstance(axiom, OWLInverseFunctionalObjectPropertyAxiom) \ + and owlready2.InverseFunctionalProperty in property_x.is_a: + property_x.is_a.remove(owlready2.InverseFunctionalProperty) + elif isinstance(axiom, OWLIrreflexiveObjectPropertyAxiom) and owlready2.IrreflexiveProperty in property_x.is_a: + property_x.is_a.remove(owlready2.IrreflexiveProperty) + elif isinstance(axiom, OWLReflexiveObjectPropertyAxiom) and owlready2.ReflexiveProperty in property_x.is_a: + property_x.is_a.remove(owlready2.ReflexiveProperty) + elif isinstance(axiom, OWLSymmetricObjectPropertyAxiom) and owlready2.SymmetricProperty in property_x.is_a: + property_x.is_a.remove(owlready2.SymmetricProperty) + elif isinstance(axiom, OWLTransitiveObjectPropertyAxiom) and owlready2.TransitiveProperty in property_x.is_a: + property_x.is_a.remove(owlready2.TransitiveProperty) + else: + raise ValueError(f'OWLObjectPropertyCharacteristicAxiom ({axiom}) is not defined.') - @abstractmethod - def get_ontology_id(self) -> OWLOntologyID: - """Gets the OWLOntologyID belonging to this object. - Returns: - The OWLOntologyID. - """ - pass +@_remove_axiom.register +def _(axiom: OWLDataPropertyCharacteristicAxiom, ontology: OWLOntology, world: owlready2.namespace.World): + conv = ToOwlready2(world) + ont_x: owlready2.Ontology = conv.map_object(ontology) - def is_anonymous(self) -> bool: - """Check whether this ontology does contain an IRI or not.""" - return self.get_ontology_id().is_anonymous() + with ont_x: + property_x = conv._to_owlready2_property(axiom.get_property()) + if property_x is not None and isinstance(axiom, OWLFunctionalDataPropertyAxiom) \ + and owlready2.FunctionalProperty in property_x.is_a: + property_x.is_a.remove(owlready2.FunctionalProperty) class Ontology(OWLOntology): __slots__ = '_manager', '_iri', '_world', '_onto' - _manager: 'OntologyManager' + _manager: _OM _onto: owlready2.Ontology _world: owlready2.World - def __init__(self, manager: 'OntologyManager', ontology_iri: IRI, load: bool): + def __init__(self, manager: _OM, ontology_iri: IRI, load: bool): """Represents an Ontology in Ontolearn. Args: @@ -300,7 +828,7 @@ def general_class_axioms(self) -> Iterable[OWLClassAxiom]: yield from (OWLSubClassOfAxiom(_parse_concept_to_owlapy(ca.left_side), _parse_concept_to_owlapy(c)) for c in ca.is_a) - def get_owl_ontology_manager(self) -> 'OntologyManager': + def get_owl_ontology_manager(self) -> _OM: return self._manager def get_ontology_id(self) -> OWLOntologyID: @@ -371,12 +899,38 @@ def object_property_range_axioms(self, pe: OWLObjectProperty) -> Iterable[OWLObj logger.warning("Construct %s not implemented at %s", rng, pe) pass # XXX TODO + def add_axiom(self, axiom: Union[OWLAxiom, Iterable[OWLAxiom]]): + if isinstance(axiom, OWLAxiom): + _add_axiom(axiom, self, self._world) + else: + for ax in axiom: + _add_axiom(ax, self, self._world) + + def remove_axiom(self, axiom: Union[OWLAxiom, Iterable[OWLAxiom]]): + if isinstance(axiom, OWLAxiom): + _remove_axiom(axiom, self, self._world) + else: + for ax in axiom: + _remove_axiom(ax, self, self._world) + + def save(self, document_iri: Optional[IRI] = None): + ont_x: owlready2.namespace.Ontology = self._world.get_ontology( + self.get_ontology_id().get_ontology_iri().as_str() + ) + if document_iri is None: + document_iri = self._iri + if document_iri.get_namespace().startswith('file:/'): + filename = document_iri.as_str()[len('file:/'):] + ont_x.save(file=filename) + else: + raise NotImplementedError("Couldn't save because the namespace of current ontology's IRI does not start with **file:/**") + def get_original_iri(self): """Get the IRI argument that was used to create this ontology.""" return self._iri def __eq__(self, other): - if type(other) == type(self): + if type(other) is type(self): return self._onto.loaded == other._onto.loaded and self._onto.base_iri == other._onto.base_iri return NotImplemented @@ -387,6 +941,93 @@ def __repr__(self): return f'Ontology({IRI.create(self._onto.base_iri)}, {self._onto.loaded})' +class SyncOntology(OWLOntology): + + def __init__(self, manager: _SM, path: Union[IRI, str], new: bool = False): + from owlapy.owlapi_mapper import OWLAPIMapper + from java.io import File + from java.util.stream import Stream + from org.semanticweb.owlapi.model import IRI as owlapi_IRI + self.manager = manager + self.path = path + self.new = new + if isinstance(path, IRI): + file_path = path.str + else: + file_path = path + if new: # create new ontology + if isinstance(path, IRI): + self.owlapi_ontology = manager.get_owlapi_manager().createOntology(Stream.empty(), + owlapi_IRI.create(path.str)) + else: + raise NotImplementedError("Cant initialize a new ontology using path. Use IRI instead") + else: # means we are loading an existing ontology + self.owlapi_ontology = manager.get_owlapi_manager().loadOntologyFromOntologyDocument(File(file_path)) + self.mapper = OWLAPIMapper(self) + + def classes_in_signature(self) -> Iterable[OWLClass]: + return self.mapper.map_(self.owlapi_ontology.getClassesInSignature()) + + def data_properties_in_signature(self) -> Iterable[OWLDataProperty]: + return self.mapper.map_(self.owlapi_ontology.getDataPropertiesInSignature()) + + def object_properties_in_signature(self) -> Iterable[OWLObjectProperty]: + return self.mapper.map_(self.owlapi_ontology.getObjectPropertiesInSignature()) + + def individuals_in_signature(self) -> Iterable[OWLNamedIndividual]: + return self.mapper.map_(self.owlapi_ontology.getIndividualsInSignature()) + + def equivalent_classes_axioms(self, c: OWLClass) -> Iterable[OWLEquivalentClassesAxiom]: + return self.mapper.map_(self.owlapi_ontology.getEquivalentClassesAxioms(self.mapper.map_(c))) + + def general_class_axioms(self) -> Iterable[OWLClassAxiom]: + return self.mapper.map_(self.owlapi_ontology.getGeneralClassAxioms()) + + def data_property_domain_axioms(self, property: OWLDataProperty) -> Iterable[OWLDataPropertyDomainAxiom]: + return self.mapper.map_(self.owlapi_ontology.getDataPropertyDomainAxioms(self.mapper.map_(property))) + + def data_property_range_axioms(self, property: OWLDataProperty) -> Iterable[OWLDataPropertyRangeAxiom]: + return self.mapper.map_(self.owlapi_ontology.getDataPropertyRangeAxioms(self.mapper.map_(property))) + + def object_property_domain_axioms(self, property: OWLObjectProperty) -> Iterable[OWLObjectPropertyDomainAxiom]: + return self.mapper.map_(self.owlapi_ontology.getObjectPropertyDomainAxioms(self.mapper.map_(property))) + + def object_property_range_axioms(self, property: OWLObjectProperty) -> Iterable[OWLObjectPropertyRangeAxiom]: + return self.mapper.map_(self.owlapi_ontology.getObjectPropertyRangeAxioms(self.mapper.map_(property))) + + def get_owl_ontology_manager(self) -> _M: + return self.manager + + def get_owlapi_ontology(self): + return self.owlapi_ontology + + def get_ontology_id(self) -> OWLOntologyID: + return self.mapper.map_(self.owlapi_ontology.getOntologyID()) + + def add_axiom(self, axiom: Union[OWLAxiom, Iterable[OWLAxiom]]): + if isinstance(axiom, OWLAxiom): + self.owlapi_ontology.addAxiom(self.mapper.map_(axiom)) + else: + self.owlapi_ontology.addAxioms(self.mapper.map_(axiom)) + + def remove_axiom(self, axiom: Union[OWLAxiom, Iterable[OWLAxiom]]): + if isinstance(axiom, OWLAxiom): + self.owlapi_ontology.removeAxiom(self.mapper.map_(axiom)) + else: + self.owlapi_ontology.removeAxioms(self.mapper.map_(axiom)) + + def __eq__(self, other): + if isinstance(other, SyncOntology): + return other.owlapi_ontology.getOntologyID().equals(other.owlapi_ontology.getOntologyID()) + return False + + def __hash__(self): + return int(self.owlapi_ontology.getOntologyID().hashCode()) + + def __repr__(self): + return f'SyncOntology({self.manager}, {self.path}, {self.new})' + + OWLREADY2_FACET_KEYS = MappingProxyType({ OWLFacet.MIN_INCLUSIVE: "min_inclusive", OWLFacet.MIN_EXCLUSIVE: "min_exclusive", @@ -400,6 +1041,7 @@ def __repr__(self): OWLFacet.FRACTION_DIGITS: "fraction_digits" }) + class ToOwlready2: __slots__ = '_world' @@ -732,19 +1374,19 @@ def _(self, p: owlready2.ConstrainedDatatype) -> OWLDatatypeRestriction: @map_datarange.register def _(self, type_: type) -> OWLDatatype: - if type_ == bool: + if type_ is bool: return BooleanOWLDatatype - elif type_ == float: + elif type_ is float: return DoubleOWLDatatype - elif type_ == int: + elif type_ is int: return IntegerOWLDatatype - elif type_ == str: + elif type_ is str: return StringOWLDatatype - elif type_ == date: + elif type_ is date: return DateOWLDatatype - elif type_ == datetime: + elif type_ is datetime: return DateTimeOWLDatatype - elif type_ == Timedelta: + elif type_ is Timedelta: return DurationOWLDatatype else: raise ValueError(type_) diff --git a/owlapy/owl_ontology_manager.py b/owlapy/owl_ontology_manager.py index 587ddb5d..0f384f05 100644 --- a/owlapy/owl_ontology_manager.py +++ b/owlapy/owl_ontology_manager.py @@ -1,128 +1,14 @@ -from abc import ABCMeta, abstractmethod -from functools import singledispatch -from itertools import islice, combinations -import types -from typing import cast +from typing import Union +import jpype import owlready2 +from owlapy.abstracts.abstract_owl_ontology_manager import OWLOntologyChange, OWLOntologyManager from owlapy.iri import IRI from owlapy.meta_classes import HasIRI -from owlapy.owl_object import OWLObject -from owlready2 import destroy_entity, AllDisjoint, AllDifferent, GeneralClassAxiom -from owlapy.class_expression import OWLThing, OWLClass, \ - OWLQuantifiedDataRestriction, OWLDataHasValue, OWLNaryBooleanClassExpression, OWLObjectOneOf, OWLObjectComplementOf, \ - OWLObjectHasValue, OWLQuantifiedObjectRestriction -from owlapy.owl_axiom import OWLObjectPropertyRangeAxiom, OWLAxiom, OWLSubClassOfAxiom, OWLEquivalentClassesAxiom, \ - OWLDisjointUnionAxiom, OWLAnnotationAssertionAxiom, OWLAnnotationProperty, OWLSubPropertyAxiom, \ - OWLPropertyRangeAxiom, OWLClassAssertionAxiom, OWLDeclarationAxiom, OWLObjectPropertyAssertionAxiom, \ - OWLSymmetricObjectPropertyAxiom, OWLTransitiveObjectPropertyAxiom, OWLPropertyDomainAxiom, \ - OWLAsymmetricObjectPropertyAxiom, OWLDataPropertyCharacteristicAxiom, OWLFunctionalDataPropertyAxiom, \ - OWLReflexiveObjectPropertyAxiom, OWLDataPropertyAssertionAxiom, OWLFunctionalObjectPropertyAxiom, \ - OWLObjectPropertyCharacteristicAxiom, OWLIrreflexiveObjectPropertyAxiom, OWLInverseFunctionalObjectPropertyAxiom, \ - OWLDisjointDataPropertiesAxiom, OWLDisjointObjectPropertiesAxiom, OWLEquivalentDataPropertiesAxiom, \ - OWLEquivalentObjectPropertiesAxiom, OWLInverseObjectPropertiesAxiom, OWLNaryPropertyAxiom, OWLNaryIndividualAxiom, \ - OWLDifferentIndividualsAxiom, OWLDisjointClassesAxiom, OWLSameIndividualAxiom -from owlapy.owl_individual import OWLNamedIndividual, OWLIndividual -from owlapy.owl_ontology import OWLOntology, Ontology, ToOwlready2 -from owlapy.owl_property import OWLDataProperty, OWLObjectInverseOf, OWLObjectProperty, \ - OWLProperty -from typing import Union - -class OWLOntologyChange(metaclass=ABCMeta): - """Represents an ontology change.""" - __slots__ = () - - _ont: OWLOntology - - @abstractmethod - def __init__(self, ontology: OWLOntology): - self._ont = ontology - - def get_ontology(self) -> OWLOntology: - """Gets the ontology that the change is/was applied to. - - Returns: - The ontology that the change is applicable to. - """ - return self._ont - - -class OWLOntologyManager(metaclass=ABCMeta): - """An OWLOntologyManager manages a set of ontologies. It is the main point for creating, loading and accessing - ontologies.""" - - @abstractmethod - def create_ontology(self, iri: IRI) -> OWLOntology: - """Creates a new (empty) ontology that that has the specified ontology IRI (and no version IRI). - - Args: - iri: The IRI of the ontology to be created. - - Returns: - The newly created ontology, or if an ontology with the specified IRI already exists then this existing - ontology will be returned. - """ - pass - - @abstractmethod - def load_ontology(self, iri: IRI) -> OWLOntology: - """Loads an ontology that is assumed to have the specified ontology IRI as its IRI or version IRI. The ontology - IRI will be mapped to an ontology document IRI. - - Args: - iri: The IRI that identifies the ontology. It is expected that the ontology will also have this IRI - (although the OWL API should tolerate situations where this is not the case). - - Returns: - The OWLOntology representation of the ontology that was loaded. - """ - pass - - @abstractmethod - def apply_change(self, change: OWLOntologyChange): - """A convenience method that applies just one change to an ontology. When this method is used through an - OWLOntologyManager implementation, the instance used should be the one that the ontology returns through the - get_owl_ontology_manager() call. - - Args: - change: The change to be applied. - - Raises: - ChangeApplied.UNSUCCESSFULLY: if the change was not applied successfully. - """ - pass - - @abstractmethod - def add_axiom(self, ontology: OWLOntology, axiom: OWLAxiom): - """A convenience method that adds a single axiom to an ontology. - - Args: - ontology: The ontology to add the axiom to. - axiom: The axiom to be added. - """ - pass - - @abstractmethod - def remove_axiom(self, ontology: OWLOntology, axiom: OWLAxiom): - """A convenience method that removes a single axiom from an ontology. - - Args: - ontology: The ontology to remove the axiom from. - axiom: The axiom to be removed. - """ - pass - - @abstractmethod - def save_ontology(self, ontology: OWLOntology, document_iri: IRI): - """Saves the specified ontology, using the specified document IRI to determine where/how the ontology should be - saved. - - Args: - ontology: The ontology to be saved. - document_iri: The document IRI where the ontology should be saved to. - """ - pass +from owlapy.owl_ontology import Ontology, SyncOntology +from owlapy.abstracts.abstract_owl_ontology import OWLOntology +from owlapy.static_funcs import startJVM class OWLImportsDeclaration(HasIRI): @@ -177,660 +63,6 @@ def get_import_declaration(self) -> OWLImportsDeclaration: return self._declaration -@singledispatch -def _add_axiom(axiom: OWLAxiom): - raise NotImplementedError(f'Axiom type {axiom} is not implemented yet.') - - -@_add_axiom.register -def _(axiom: OWLDeclarationAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) - - entity = axiom.get_entity() - with ont_x: - entity_x = world[entity.to_string_id()] - # Entity already exists - if entity_x is not None: - return - - thing_x: owlready2.entity.ThingClass = conv.map_concept(OWLThing) - if isinstance(entity, OWLClass): - if entity.is_owl_thing() or entity.is_owl_nothing(): - return - entity_x = types.new_class(name=entity.iri.get_remainder(), bases=(thing_x,)) - elif isinstance(entity, OWLIndividual): - entity_x = thing_x(entity.iri.get_remainder()) - elif isinstance(entity, OWLObjectProperty): - entity_x = types.new_class(name=entity.iri.get_remainder(), bases=(owlready2.ObjectProperty,)) - elif isinstance(entity, OWLDataProperty): - entity_x = types.new_class(name=entity.iri.get_remainder(), bases=(owlready2.DatatypeProperty,)) - elif isinstance(entity, OWLAnnotationProperty): - entity_x = types.new_class(name=entity.iri.get_remainder(), bases=(owlready2.AnnotationProperty,)) - else: - raise ValueError(f'Cannot add ({entity}). Not an atomic class, property, or individual.') - entity_x.namespace = ont_x.get_namespace(entity.iri.get_namespace()) - entity_x.namespace.world._refactor(entity_x.storid, entity_x.iri) - - -@_add_axiom.register -def _(axiom: OWLClassAssertionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) - - individual = axiom.get_individual() - cls_ = axiom.get_class_expression() - _check_expression(cls_, ontology, world) - _add_axiom(OWLDeclarationAxiom(individual), ontology, world) - with ont_x: - cls_x = conv.map_concept(cls_) - ind_x = conv._to_owlready2_individual(individual) - thing_x = conv.map_concept(OWLThing) - if thing_x in ind_x.is_a: - ind_x.is_a.remove(thing_x) - ind_x.is_a.append(cls_x) - - -@_add_axiom.register -def _(axiom: OWLObjectPropertyAssertionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) - - subject = axiom.get_subject() - property_ = axiom.get_property() - object_ = axiom.get_object() - _add_axiom(OWLDeclarationAxiom(subject), ontology, world) - _add_axiom(OWLDeclarationAxiom(property_), ontology, world) - _add_axiom(OWLDeclarationAxiom(object_), ontology, world) - with ont_x: - subject_x = conv._to_owlready2_individual(subject) - property_x = conv._to_owlready2_property(property_) - object_x = conv._to_owlready2_individual(object_) - property_x[subject_x].append(object_x) - - -@_add_axiom.register -def _(axiom: OWLDataPropertyAssertionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) - - subject = axiom.get_subject() - property_ = axiom.get_property() - _add_axiom(OWLDeclarationAxiom(subject), ontology, world) - _add_axiom(OWLDeclarationAxiom(property_), ontology, world) - with ont_x: - subject_x = conv._to_owlready2_individual(subject) - property_x = conv._to_owlready2_property(property_) - property_x[subject_x].append(axiom.get_object().to_python()) - - -@_add_axiom.register -def _(axiom: OWLSubClassOfAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) - - sub_class = axiom.get_sub_class() - super_class = axiom.get_super_class() - - _check_expression(sub_class, ontology, world) - _check_expression(super_class, ontology, world) - with ont_x: - thing_x = conv.map_concept(OWLThing) - sub_class_x = conv.map_concept(sub_class) - super_class_x = conv.map_concept(super_class) - if isinstance(sub_class, OWLClass): - if thing_x in sub_class_x.is_a: - sub_class_x.is_a.remove(thing_x) - else: - # Currently owlready2 seems to expect that we make a new GeneralClassAxiom object each time. - # Another option would be to check whether a GeneralClassAxiom with the sub_class_x already exists and just - # add the super_class_x to its is_a attribute - sub_class_x = GeneralClassAxiom(sub_class_x) - sub_class_x.is_a.append(super_class_x) - - -# TODO: Update as soon as owlready2 adds support for EquivalentClasses general class axioms -@_add_axiom.register -def _(axiom: OWLEquivalentClassesAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x = conv.map_object(ontology) - - assert axiom.contains_named_equivalent_class(), 'Owlready2 does not support general' \ - 'class axioms for equivalent classes.' - for ce in axiom.class_expressions(): - _check_expression(ce, ontology, world) - with ont_x: - for ce_1, ce_2 in combinations(axiom.class_expressions(), 2): - assert ce_1 is not None, f"ce_1 cannot be None: {ce_1}, {type(ce_1)}" - assert ce_2 is not None, f"ce_2_x cannot be None: {ce_2}, {type(ce_2)}" - - ce_1_x = conv.map_concept(ce_1) - ce_2_x = conv.map_concept(ce_2) - try: - assert ce_1_x is not None, f"ce_1_x cannot be None: {ce_1_x}, {type(ce_1_x)}" - assert ce_2_x is not None, f"ce_2_x cannot be None: {ce_2_x}, {type(ce_2_x)}" - except AssertionError: - print("function of ToOwlready2.map_concept() returns None") - print(ce_1, ce_1_x) - print(ce_2, ce_2_x) - print("Axiom:", axiom) - print("Temporary solution is reinitializing ce_1_x=ce_2_x\n\n") - ce_1_x=ce_2_x - - if isinstance(ce_1_x, owlready2.ThingClass): - ce_1_x.equivalent_to.append(ce_2_x) - if isinstance(ce_2_x, owlready2.ThingClass): - ce_2_x.equivalent_to.append(ce_1_x) - - -@_add_axiom.register -def _(axiom: OWLDisjointClassesAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.Ontology = conv.map_object(ontology) - - for cls_ in axiom.class_expressions(): - _check_expression(cls_, ontology, world) - with ont_x: - # TODO: If the first element in the list is a complex class expression owlready2 is bugged - # and creates an AllDifferent axiom - AllDisjoint(list(map(conv.map_concept, axiom.class_expressions()))) - - -@_add_axiom.register -def _(axiom: OWLDisjointUnionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.Ontology = conv.map_object(ontology) - - assert isinstance(axiom.get_owl_class(), OWLClass), f'({axiom.get_owl_class()}) is not a named class.' - _add_axiom(OWLDeclarationAxiom(axiom.get_owl_class()), ontology, world) - for cls_ in axiom.get_class_expressions(): - _check_expression(cls_, ontology, world) - with ont_x: - cls_x = conv.map_concept(axiom.get_owl_class()) - cls_x.disjoint_unions.append(list(map(conv.map_concept, axiom.get_class_expressions()))) - - -@_add_axiom.register -def _(axiom: OWLAnnotationAssertionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.Ontology = conv.map_object(ontology) - - prop_x = conv.map_object(axiom.get_property()) - if prop_x is None: - with ont_x: - prop_x: owlready2.annotation.AnnotationPropertyClass = cast( - owlready2.AnnotationProperty, - types.new_class( - name=axiom.get_property().iri.get_remainder(), - bases=(owlready2.AnnotationProperty,))) - prop_x.namespace = ont_x.get_namespace(axiom.get_property().iri.get_namespace()) - sub_x = world[axiom.get_subject().as_iri().as_str()] - assert sub_x is not None, f'{axiom.get_subject} not found in {ontology}' - with ont_x: - if axiom.get_value().is_literal(): - literal = axiom.get_value().as_literal() - setattr(sub_x, prop_x.python_name, literal.to_python()) - else: - o_x = world[axiom.get_value().as_iri().as_str()] - assert o_x is not None, f'{axiom.get_value()} not found in {ontology}' - setattr(sub_x, prop_x.python_name, o_x) - - -@_add_axiom.register -def _(axiom: OWLNaryIndividualAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.Ontology = conv.map_object(ontology) - - for ind in axiom.individuals(): - _add_axiom(OWLDeclarationAxiom(ind), ontology, world) - with ont_x: - if isinstance(axiom, OWLSameIndividualAxiom): - for idx, ind in enumerate(axiom.individuals()): - ind_x = conv._to_owlready2_individual(ind) - for ind_2 in islice(axiom.individuals(), idx + 1, None): - ind_2_x = conv._to_owlready2_individual(ind_2) - ind_x.equivalent_to.append(ind_2_x) - elif isinstance(axiom, OWLDifferentIndividualsAxiom): - AllDifferent(list(map(conv._to_owlready2_individual, axiom.individuals()))) - else: - raise ValueError(f'OWLNaryIndividualAxiom ({axiom}) is not defined.') - - -@_add_axiom.register -def _(axiom: OWLSubPropertyAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.Ontology = conv.map_object(ontology) - - sub_property = axiom.get_sub_property() - super_property = axiom.get_super_property() - _add_axiom(OWLDeclarationAxiom(sub_property), ontology, world) - _add_axiom(OWLDeclarationAxiom(super_property), ontology, world) - with ont_x: - sub_property_x = conv._to_owlready2_property(sub_property) - super_property_x = conv._to_owlready2_property(super_property) - sub_property_x.is_a.append(super_property_x) - - -@_add_axiom.register -def _(axiom: OWLPropertyDomainAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.Ontology = conv.map_object(ontology) - - property_ = axiom.get_property() - domain = axiom.get_domain() - _add_axiom(OWLDeclarationAxiom(property_), ontology, world) - _check_expression(domain, ontology, world) - with ont_x: - property_x = conv._to_owlready2_property(property_) - domain_x = conv.map_concept(domain) - property_x.domain.append(domain_x) - - -@_add_axiom.register -def _(axiom: OWLPropertyRangeAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.Ontology = conv.map_object(ontology) - - property_ = axiom.get_property() - range_ = axiom.get_range() - _add_axiom(OWLDeclarationAxiom(property_), ontology, world) - if isinstance(axiom, OWLObjectPropertyRangeAxiom): - _check_expression(range_, ontology, world) - with ont_x: - property_x = conv._to_owlready2_property(property_) - range_x = conv.map_concept(range_) if isinstance(axiom, OWLObjectPropertyRangeAxiom) \ - else conv.map_datarange(range_) - property_x.range.append(range_x) - - -@_add_axiom.register -def _(axiom: OWLNaryPropertyAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.Ontology = conv.map_object(ontology) - - for property_ in axiom.properties(): - _add_axiom(OWLDeclarationAxiom(property_), ontology, world) - with ont_x: - if isinstance(axiom, (OWLEquivalentObjectPropertiesAxiom, OWLEquivalentDataPropertiesAxiom,)): - for idx, property_ in enumerate(axiom.properties()): - property_x = conv._to_owlready2_property(property_) - for property_2 in islice(axiom.properties(), idx + 1, None): - property_2_x = conv._to_owlready2_property(property_2) - property_x.equivalent_to.append(property_2_x) - elif isinstance(axiom, (OWLDisjointObjectPropertiesAxiom, OWLDisjointDataPropertiesAxiom,)): - AllDisjoint(list(map(conv._to_owlready2_property, axiom.properties()))) - elif isinstance(axiom, OWLInverseObjectPropertiesAxiom): - property_first_x = conv._to_owlready2_property(axiom.get_first_property()) - property_second_x = conv._to_owlready2_property(axiom.get_second_property()) - if property_second_x.inverse_property is not None: - property_second_x.inverse_property = None - property_first_x.inverse_property = property_second_x - else: - raise ValueError(f'OWLNaryPropertyAxiom ({axiom}) is not defined.') - - -@_add_axiom.register -def _(axiom: OWLObjectPropertyCharacteristicAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.Ontology = conv.map_object(ontology) - - property_ = axiom.get_property() - _add_axiom(OWLDeclarationAxiom(property_), ontology, world) - with ont_x: - property_x = conv._to_owlready2_property(property_) - if isinstance(axiom, OWLFunctionalObjectPropertyAxiom): - property_x.is_a.append(owlready2.FunctionalProperty) - elif isinstance(axiom, OWLAsymmetricObjectPropertyAxiom): - property_x.is_a.append(owlready2.AsymmetricProperty) - elif isinstance(axiom, OWLInverseFunctionalObjectPropertyAxiom): - property_x.is_a.append(owlready2.InverseFunctionalProperty) - elif isinstance(axiom, OWLIrreflexiveObjectPropertyAxiom): - property_x.is_a.append(owlready2.IrreflexiveProperty) - elif isinstance(axiom, OWLReflexiveObjectPropertyAxiom): - property_x.is_a.append(owlready2.ReflexiveProperty) - elif isinstance(axiom, OWLSymmetricObjectPropertyAxiom): - property_x.is_a.append(owlready2.SymmetricProperty) - elif isinstance(axiom, OWLTransitiveObjectPropertyAxiom): - property_x.is_a.append(owlready2.TransitiveProperty) - else: - raise ValueError(f'ObjectPropertyCharacteristicAxiom ({axiom}) is not defined.') - - -@_add_axiom.register -def _(axiom: OWLDataPropertyCharacteristicAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.Ontology = conv.map_object(ontology) - - property_ = axiom.get_property() - _add_axiom(OWLDeclarationAxiom(property_), ontology, world) - with ont_x: - property_x = conv._to_owlready2_property(property_) - if isinstance(axiom, OWLFunctionalDataPropertyAxiom): - property_x.is_a.append(owlready2.FunctionalProperty) - else: - raise ValueError(f'DataPropertyCharacteristicAxiom ({axiom}) is not defined.') - - -@singledispatch -def _remove_axiom(axiom: OWLAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - raise NotImplementedError(f'Axiom type {axiom} is not implemented yet.') - - -@_remove_axiom.register -def _(axiom: OWLDeclarationAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) - with ont_x: - entity_x = world[axiom.get_entity().to_string_id()] - if entity_x is not None: - # TODO: owlready2 seems to be bugged for properties here - destroy_entity(entity_x) - - -@_remove_axiom.register -def _(axiom: OWLClassAssertionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) - - with ont_x: - cls_x = conv.map_concept(axiom.get_class_expression()) - ind_x = conv._to_owlready2_individual(axiom.get_individual()) - if cls_x is None or ind_x is None: - return - if cls_x in ind_x.is_a: - ind_x.is_a.remove(cls_x) - elif isinstance(axiom.get_class_expression(), OWLClass): - ont_x._del_obj_triple_spo(ind_x.storid, owlready2.rdf_type, cls_x.storid) - - -@_remove_axiom.register -def _(axiom: OWLObjectPropertyAssertionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) - - with ont_x: - subject_x = conv._to_owlready2_individual(axiom.get_subject()) - property_x = conv._to_owlready2_property(axiom.get_property()) - object_x = conv._to_owlready2_individual(axiom.get_object()) - if all([subject_x, property_x, object_x]) and object_x in property_x[subject_x]: - property_x[subject_x].remove(object_x) - - -@_remove_axiom.register -def _(axiom: OWLDataPropertyAssertionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) - - with ont_x: - subject_x = conv._to_owlready2_individual(axiom.get_subject()) - property_x = conv._to_owlready2_property(axiom.get_property()) - object_ = axiom.get_object().to_python() - if subject_x is not None and property_x is not None and object_ in property_x[subject_x]: - property_x[subject_x].remove(object_) - - -@_remove_axiom.register -def _(axiom: OWLSubClassOfAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) - sub_class = axiom.get_sub_class() - super_class = axiom.get_super_class() - - with ont_x: - sub_class_x = conv.map_concept(sub_class) - super_class_x = conv.map_concept(super_class) - if sub_class_x is None or super_class_x is None: - return - - if isinstance(sub_class, OWLClass): - if super_class_x in sub_class_x.is_a: - sub_class_x.is_a.remove(super_class_x) - elif isinstance(axiom.get_sub_class(), OWLClass) and isinstance(axiom.get_super_class(), OWLClass): - ont_x._del_obj_triple_spo(sub_class_x.storid, owlready2.rdfs_subclassof, super_class_x.storid) - else: - for ca in ont_x.general_class_axioms(): - if ca.left_side == sub_class_x and super_class_x in ca.is_a: - ca.is_a.remove(super_class_x) - - -# TODO: Update as soons as owlready2 adds support for EquivalentClasses general class axioms -@_remove_axiom.register -def _(axiom: OWLEquivalentClassesAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x = conv.map_object(ontology) - - if not axiom.contains_named_equivalent_class(): - return - - with ont_x: - ces_x = list(map(conv.map_concept, axiom.class_expressions())) - if len(ces_x) < 2 or not all(ces_x): - return - - for ce_1_x, ce_2_x in combinations(ces_x, 2): - if isinstance(ce_2_x, owlready2.ThingClass) and ce_1_x in ce_2_x.equivalent_to: - ce_2_x.equivalent_to.remove(ce_1_x) - if isinstance(ce_1_x, owlready2.ThingClass) and ce_2_x in ce_1_x.equivalent_to: - ce_1_x.equivalent_to.remove(ce_2_x) - - -@_remove_axiom.register -def _(axiom: OWLDisjointClassesAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.Ontology = conv.map_object(ontology) - - with ont_x: - class_expressions_x = set(map(conv.map_concept, axiom.class_expressions())) - if len(class_expressions_x) < 2 or not all(class_expressions_x): - return - for disjoints_x in ont_x.disjoint_classes(): - if set(disjoints_x.entities) == class_expressions_x: - del disjoints_x.entities[:-1] - break - - -@_remove_axiom.register -def _(axiom: OWLDisjointUnionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.Ontology = conv.map_object(ontology) - assert isinstance(axiom.get_owl_class(), OWLClass), f'({axiom.get_owl_class()}) is not a named class.' - - with ont_x: - cls_x = conv.map_concept(axiom.get_owl_class()) - union_expressions_x = set(map(conv.map_concept, axiom.get_class_expressions())) - if cls_x is not None and all(union_expressions_x): - for union_x in cls_x.disjoint_unions: - if union_expressions_x == set(union_x): - cls_x.disjoint_unions.remove(union_x) - break - - -@_remove_axiom.register -def _(axiom: OWLAnnotationAssertionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.Ontology = conv.map_object(ontology) - - sub_x = world[axiom.get_subject().as_iri().as_str()] - if sub_x is None: - return - name = axiom.get_property().iri.get_remainder() - with ont_x: - if axiom.get_value().is_literal(): - o_x = axiom.get_value().as_literal().to_python() - else: - o_x = world[axiom.get_value().as_iri().as_str()] - - value = getattr(sub_x, name, None) - if value is not None and o_x in value: - value.remove(o_x) - - -@_remove_axiom.register -def _(axiom: OWLNaryIndividualAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.Ontology = conv.map_object(ontology) - - with ont_x: - individuals_x = list(map(conv._to_owlready2_individual, axiom.individuals())) - if len(individuals_x) < 2 or not all(individuals_x): - return - if isinstance(axiom, OWLSameIndividualAxiom): - if set(individuals_x[1:-1]) <= set(individuals_x[0].INDIRECT_equivalent_to): - for individual_1_x, individual_2_x in combinations(individuals_x, 2): - if individual_1_x in individual_2_x.equivalent_to: - individual_2_x.equivalent_to.remove(individual_1_x) - if individual_2_x in individual_1_x.equivalent_to: - individual_1_x.equivalent_to.remove(individual_2_x) - elif isinstance(axiom, OWLDifferentIndividualsAxiom): - individuals_x = set(individuals_x) - for different_x in ont_x.different_individuals(): - if set(different_x.entities) == individuals_x: - del different_x.entities[:-1] - break - else: - raise ValueError(f'OWLNaryIndividualAxiom ({axiom}) is not defined.') - - -@_remove_axiom.register -def _(axiom: OWLSubPropertyAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) - - with ont_x: - sub_property_x = conv._to_owlready2_property(axiom.get_sub_property()) - super_property_x = conv._to_owlready2_property(axiom.get_super_property()) - if sub_property_x is None or super_property_x is None: - return - if super_property_x in sub_property_x.is_a: - sub_property_x.is_a.remove(super_property_x) - else: - ont_x._del_obj_triple_spo(sub_property_x.storid, owlready2.rdfs_subpropertyof, super_property_x.storid) - - -@_remove_axiom.register -def _(axiom: OWLPropertyDomainAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.Ontology = conv.map_object(ontology) - - with ont_x: - property_x = conv._to_owlready2_property(axiom.get_property()) - domain_x = conv.map_concept(axiom.get_domain()) - if domain_x is not None and property_x is not None and domain_x in property_x.domain: - property_x.domain.remove(domain_x) - - -@_remove_axiom.register -def _(axiom: OWLPropertyRangeAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.Ontology = conv.map_object(ontology) - - with ont_x: - property_x = conv._to_owlready2_property(axiom.get_property()) - range_x = conv.map_concept(axiom.get_range()) \ - if isinstance(axiom, OWLObjectPropertyRangeAxiom) else conv.map_datarange(axiom.get_range()) - if range_x is not None and property_x is not None and range_x in property_x.range: - property_x.range.remove(range_x) - - -@_remove_axiom.register -def _(axiom: OWLNaryPropertyAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.Ontology = conv.map_object(ontology) - - with ont_x: - properties_x = list(map(conv._to_owlready2_property, axiom.properties())) - if len(properties_x) < 2 or not all(properties_x): - return - if isinstance(axiom, (OWLEquivalentObjectPropertiesAxiom, OWLEquivalentDataPropertiesAxiom,)): - # Check if all equivalent properties are defined in the ontology - if set(properties_x[1:-1]) <= set(properties_x[0].INDIRECT_equivalent_to): - for property_1_x, property_2_x in combinations(properties_x, 2): - if property_1_x in property_2_x.equivalent_to: - property_2_x.equivalent_to.remove(property_1_x) - if property_2_x in property_1_x.equivalent_to: - property_1_x.equivalent_to.remove(property_2_x) - elif isinstance(axiom, (OWLDisjointObjectPropertiesAxiom, OWLDisjointDataPropertiesAxiom,)): - properties_x = set(properties_x) - for disjoints_x in ont_x.disjoint_properties(): - if set(disjoints_x.entities) == properties_x: - del disjoints_x.entities[:-1] - break - elif isinstance(axiom, OWLInverseObjectPropertiesAxiom): - if len(properties_x) != 2: - return - first = properties_x[0] - second = properties_x[1] - if first.inverse_property == second and second.inverse_property == first: - first.inverse_property = None - else: - raise ValueError(f'OWLNaryPropertyAxiom ({axiom}) is not defined.') - - -@_remove_axiom.register -def _(axiom: OWLObjectPropertyCharacteristicAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.Ontology = conv.map_object(ontology) - - with ont_x: - property_x = conv._to_owlready2_property(axiom.get_property()) - if property_x is None: - return - - if isinstance(axiom, OWLFunctionalObjectPropertyAxiom) and owlready2.FunctionalProperty in property_x.is_a: - property_x.is_a.remove(owlready2.FunctionalProperty) - elif isinstance(axiom, OWLAsymmetricObjectPropertyAxiom) and owlready2.AsymmetricProperty in property_x.is_a: - property_x.is_a.remove(owlready2.AsymmetricProperty) - elif isinstance(axiom, OWLInverseFunctionalObjectPropertyAxiom) \ - and owlready2.InverseFunctionalProperty in property_x.is_a: - property_x.is_a.remove(owlready2.InverseFunctionalProperty) - elif isinstance(axiom, OWLIrreflexiveObjectPropertyAxiom) and owlready2.IrreflexiveProperty in property_x.is_a: - property_x.is_a.remove(owlready2.IrreflexiveProperty) - elif isinstance(axiom, OWLReflexiveObjectPropertyAxiom) and owlready2.ReflexiveProperty in property_x.is_a: - property_x.is_a.remove(owlready2.ReflexiveProperty) - elif isinstance(axiom, OWLSymmetricObjectPropertyAxiom) and owlready2.SymmetricProperty in property_x.is_a: - property_x.is_a.remove(owlready2.SymmetricProperty) - elif isinstance(axiom, OWLTransitiveObjectPropertyAxiom) and owlready2.TransitiveProperty in property_x.is_a: - property_x.is_a.remove(owlready2.TransitiveProperty) - else: - raise ValueError(f'OWLObjectPropertyCharacteristicAxiom ({axiom}) is not defined.') - - -@_remove_axiom.register -def _(axiom: OWLDataPropertyCharacteristicAxiom, ontology: OWLOntology, world: owlready2.namespace.World): - conv = ToOwlready2(world) - ont_x: owlready2.Ontology = conv.map_object(ontology) - - with ont_x: - property_x = conv._to_owlready2_property(axiom.get_property()) - if property_x is not None and isinstance(axiom, OWLFunctionalDataPropertyAxiom) \ - and owlready2.FunctionalProperty in property_x.is_a: - property_x.is_a.remove(owlready2.FunctionalProperty) - - -def _check_expression(expr: OWLObject, ontology: OWLOntology, world: owlready2.namespace.World): - """ - @TODO:CD: Documentation - Creates all entities (individuals, classes, properties) that appear in the given (complex) class expression - and do not exist in the given ontology yet - - """ - if isinstance(expr, (OWLClass, OWLProperty, OWLNamedIndividual,)): - _add_axiom(OWLDeclarationAxiom(expr), ontology, world) - elif isinstance(expr, (OWLNaryBooleanClassExpression, OWLObjectComplementOf, OWLObjectOneOf,)): - for op in expr.operands(): - _check_expression(op, ontology, world) - elif isinstance(expr, (OWLQuantifiedObjectRestriction, OWLObjectHasValue,)): - _check_expression(expr.get_property(), ontology, world) - _check_expression(expr.get_filler(), ontology, world) - elif isinstance(expr, OWLObjectInverseOf): - _check_expression(expr.get_named_property(), ontology, world) - _check_expression(expr.get_inverse_property(), ontology, world) - elif isinstance(expr, (OWLQuantifiedDataRestriction, OWLDataHasValue,)): - _check_expression(expr.get_property(), ontology, world) - elif not isinstance(expr, OWLObject): - raise ValueError(f'({expr}) is not an OWLObject.') - - class OntologyManager(OWLOntologyManager): __slots__ = '_world' @@ -849,16 +81,16 @@ def __init__(self, world_store=None): else: self._world = owlready2.World(filename=world_store) - def create_ontology(self, iri:Union[str,IRI]=None) -> 'Ontology': + def create_ontology(self, iri: Union[str, IRI] = None) -> Ontology: if isinstance(iri, str): - iri=IRI.create(iri) + iri = IRI.create(iri) else: assert isinstance(iri, IRI), "iri either must be string or an instance of IRI Class" return Ontology(self, iri, load=False) - def load_ontology(self, iri: Union[str,IRI]=None) -> 'Ontology': + def load_ontology(self, iri: Union[IRI, str] = None) -> Ontology: if isinstance(iri, str): - iri=IRI.create(iri) + iri = IRI.create(iri) else: assert isinstance(iri, IRI), "iri either must be string or an instance of IRI Class" return Ontology(self, iri, load=True) @@ -873,24 +105,33 @@ def apply_change(self, change: OWLOntologyChange): # TODO XXX raise NotImplementedError - def add_axiom(self, ontology: OWLOntology, axiom: OWLAxiom): - _add_axiom(axiom, ontology, self._world) - - def remove_axiom(self, ontology: OWLOntology, axiom: OWLAxiom): - _remove_axiom(axiom, ontology, self._world) - - def save_ontology(self, ontology: OWLOntology, document_iri: IRI): - ont_x: owlready2.namespace.Ontology = self._world.get_ontology( - ontology.get_ontology_id().get_ontology_iri().as_str() - ) - if document_iri.get_namespace().startswith('file:/'): - filename = document_iri.as_str()[len('file:/'):] - ont_x.save(file=filename) - else: - raise NotImplementedError("Couldn't save because the namespace of document_iri does not start with **file:/**") - def save_world(self): """Saves the actual state of the quadstore in the SQLite3 file. """ self._world.save() + +class SyncOntologyManager(OWLOntologyManager): + + # WARN: Do not move local imports to top of the module + def __init__(self): + if not jpype.isJVMStarted(): + startJVM() + from org.semanticweb.owlapi.apibinding import OWLManager + self.owlapi_manager = OWLManager.createOWLOntologyManager() + + def create_ontology(self, iri: Union[IRI, str]) -> SyncOntology: + if isinstance(iri, str): + iri = IRI.create(iri) + else: + assert isinstance(iri, IRI), "iri either must be string or an instance of IRI Class" + return SyncOntology(self, iri, new=True) + + def load_ontology(self, iri: Union[IRI, str]) -> SyncOntology: + return SyncOntology(self, iri, new=False) + + def get_owlapi_manager(self): + return self.owlapi_manager + + def apply_change(self, change: OWLOntologyChange): + raise NotImplementedError() diff --git a/owlapy/owl_reasoner.py b/owlapy/owl_reasoner.py index 318ad2c9..45d02bca 100644 --- a/owlapy/owl_reasoner.py +++ b/owlapy/owl_reasoner.py @@ -1,490 +1,39 @@ """OWL Reasoner""" import operator -from abc import ABCMeta, abstractmethod +import logging +import owlready2 + from collections import defaultdict from functools import singledispatchmethod, reduce from itertools import chain, repeat from types import MappingProxyType, FunctionType -from typing import DefaultDict, Iterable, Dict, Mapping, Set, Type, TypeVar, Optional, FrozenSet -import logging - -import owlready2 +from typing import DefaultDict, Iterable, Dict, Mapping, Set, Type, TypeVar, Optional, FrozenSet, List, Union from owlapy.class_expression import OWLClassExpression, OWLObjectSomeValuesFrom, OWLObjectUnionOf, \ OWLObjectIntersectionOf, OWLObjectComplementOf, OWLObjectAllValuesFrom, OWLObjectOneOf, OWLObjectHasValue, \ OWLObjectMinCardinality, OWLObjectMaxCardinality, OWLObjectExactCardinality, OWLObjectCardinalityRestriction, \ OWLDataSomeValuesFrom, OWLDataOneOf, OWLDatatypeRestriction, OWLFacetRestriction, OWLDataHasValue, \ OWLDataAllValuesFrom -from owlapy.class_expression import OWLClass -from owlapy.iri import IRI -from owlapy.owl_axiom import OWLSubClassOfAxiom -from owlapy.owl_data_ranges import OWLDataRange, OWLDataComplementOf, OWLDataUnionOf, OWLDataIntersectionOf -from owlapy.owl_datatype import OWLDatatype -from owlapy.owl_object import OWLEntity -from owlapy.owl_ontology import OWLOntology, Ontology, _parse_concept_to_owlapy -from owlapy.owl_ontology_manager import OntologyManager -from owlapy.owl_property import OWLObjectPropertyExpression, OWLDataProperty, OWLObjectProperty, OWLObjectInverseOf, \ - OWLPropertyExpression, OWLDataPropertyExpression -from owlapy.owl_individual import OWLNamedIndividual -from owlapy.owl_literal import OWLLiteral -from owlapy.owlapi_adaptor import OWLAPIAdaptor -from owlapy.utils import LRUCache - -logger = logging.getLogger(__name__) - -_P = TypeVar('_P', bound=OWLPropertyExpression) - -# TODO:CD:The name of the classes defined with metaclass=ABCMeta should reflect that -# TODO:CD: An instance cannot be created from those classes. -# TODO:CD: We should move those Abstract Base Classes into a respective package, e.g. -# TODO:CD: owlapy/abstract_owl_reasoner/abstract_owl_reasoner.py should contain OWLReasoner and OWLReasonerEx -class OWLReasoner(metaclass=ABCMeta): - """An OWLReasoner reasons over a set of axioms (the set of reasoner axioms) that is based on the imports closure of - a particular ontology - the "root" ontology.""" - __slots__ = () - - @abstractmethod - def __init__(self, ontology: OWLOntology): - pass - - @abstractmethod - def data_property_domains(self, pe: OWLDataProperty, direct: bool = False) -> Iterable[OWLClassExpression]: - """Gets the class expressions that are the direct or indirect domains of this property with respect to the - imports closure of the root ontology. - - Args: - pe: The property expression whose domains are to be retrieved. - direct: Specifies if the direct domains should be retrieved (True), or if all domains should be retrieved - (False). - - Returns: - :Let N = equivalent_classes(DataSomeValuesFrom(pe rdfs:Literal)). If direct is True: then if N is not - empty then the return value is N, else the return value is the result of - super_classes(DataSomeValuesFrom(pe rdfs:Literal), true). If direct is False: then the result of - super_classes(DataSomeValuesFrom(pe rdfs:Literal), false) together with N if N is non-empty. - (Note, rdfs:Literal is the top datatype). - """ - pass - - @abstractmethod - def object_property_domains(self, pe: OWLObjectProperty, direct: bool = False) -> Iterable[OWLClassExpression]: - """Gets the class expressions that are the direct or indirect domains of this property with respect to the - imports closure of the root ontology. - - Args: - pe: The property expression whose domains are to be retrieved. - direct: Specifies if the direct domains should be retrieved (True), or if all domains should be retrieved - (False). - - Returns: - :Let N = equivalent_classes(ObjectSomeValuesFrom(pe owl:Thing)). If direct is True: then if N is not empty - then the return value is N, else the return value is the result of - super_classes(ObjectSomeValuesFrom(pe owl:Thing), true). If direct is False: then the result of - super_classes(ObjectSomeValuesFrom(pe owl:Thing), false) together with N if N is non-empty. - """ - pass - - @abstractmethod - def object_property_ranges(self, pe: OWLObjectProperty, direct: bool = False) -> Iterable[OWLClassExpression]: - """Gets the class expressions that are the direct or indirect ranges of this property with respect to the - imports closure of the root ontology. - - Args: - pe: The property expression whose ranges are to be retrieved. - direct: Specifies if the direct ranges should be retrieved (True), or if all ranges should be retrieved - (False). - - Returns: - :Let N = equivalent_classes(ObjectSomeValuesFrom(ObjectInverseOf(pe) owl:Thing)). If direct is True: then - if N is not empty then the return value is N, else the return value is the result of - super_classes(ObjectSomeValuesFrom(ObjectInverseOf(pe) owl:Thing), true). If direct is False: then - the result of super_classes(ObjectSomeValuesFrom(ObjectInverseOf(pe) owl:Thing), false) together with N - if N is non-empty. - """ - pass - - @abstractmethod - def equivalent_classes(self, ce: OWLClassExpression, only_named: bool = True) -> Iterable[OWLClassExpression]: - """Gets the class expressions that are equivalent to the specified class expression with respect to the set of - reasoner axioms. - - Args: - ce: The class expression whose equivalent classes are to be retrieved. - only_named: Whether to only retrieve named equivalent classes or also complex class expressions. - - Returns: - All class expressions C where the root ontology imports closure entails EquivalentClasses(ce C). If ce is - not a class name (i.e. it is an anonymous class expression) and there are no such classes C then there will - be no result. If ce is unsatisfiable with respect to the set of reasoner axioms then owl:Nothing, i.e. the - bottom node, will be returned. - """ - pass - - @abstractmethod - def disjoint_classes(self, ce: OWLClassExpression, only_named: bool = True) -> Iterable[OWLClassExpression]: - """Gets the class expressions that are disjoint with specified class expression with respect to the set of - reasoner axioms. - - Args: - ce: The class expression whose disjoint classes are to be retrieved. - only_named: Whether to only retrieve named disjoint classes or also complex class expressions. - - Returns: - All class expressions D where the set of reasoner axioms entails EquivalentClasses(D ObjectComplementOf(ce)) - or StrictSubClassOf(D ObjectComplementOf(ce)). - """ - pass - - @abstractmethod - def different_individuals(self, ind: OWLNamedIndividual) -> Iterable[OWLNamedIndividual]: - """Gets the individuals that are different from the specified individual with respect to the set of - reasoner axioms. - - Args: - ind: The individual whose different individuals are to be retrieved. - - Returns: - All individuals x where the set of reasoner axioms entails DifferentIndividuals(ind x). - """ - pass - - @abstractmethod - def same_individuals(self, ind: OWLNamedIndividual) -> Iterable[OWLNamedIndividual]: - """Gets the individuals that are the same as the specified individual with respect to the set of - reasoner axioms. - - Args: - ind: The individual whose same individuals are to be retrieved. - - Returns: - All individuals x where the root ontology imports closure entails SameIndividual(ind x). - """ - pass - - @abstractmethod - def equivalent_object_properties(self, op: OWLObjectPropertyExpression) -> Iterable[OWLObjectPropertyExpression]: - """Gets the simplified object properties that are equivalent to the specified object property with respect - to the set of reasoner axioms. - - Args: - op: The object property whose equivalent object properties are to be retrieved. - - Returns: - All simplified object properties e where the root ontology imports closure entails - EquivalentObjectProperties(op e). If op is unsatisfiable with respect to the set of reasoner axioms - then owl:bottomDataProperty will be returned. - """ - pass - - @abstractmethod - def equivalent_data_properties(self, dp: OWLDataProperty) -> Iterable[OWLDataProperty]: - """Gets the data properties that are equivalent to the specified data property with respect to the set of - reasoner axioms. - - Args: - dp: The data property whose equivalent data properties are to be retrieved. - - Returns: - All data properties e where the root ontology imports closure entails EquivalentDataProperties(dp e). - If dp is unsatisfiable with respect to the set of reasoner axioms then owl:bottomDataProperty will - be returned. - """ - pass - - @abstractmethod - def data_property_values(self, e: OWLEntity, pe: OWLDataProperty, direct: bool = True) \ - -> Iterable['OWLLiteral']: - """Gets the data property values for the specified entity and data property expression. - - Args: - e: The owl entity (usually an individual) that is the subject of the data property values. - pe: The data property expression whose values are to be retrieved for the specified entity. - direct: Specifies if the direct values should be retrieved (True), or if all values should be retrieved - (False), so that sub properties are taken into account. - - Note: Can be used to get values, for example, of 'label' property of owl entities such as classes and properties - too (not only individuals). - - Returns: - A set of OWLLiterals containing literals such that for each literal l in the set, the set of reasoner - axioms entails DataPropertyAssertion(pe ind l). - """ - pass - - @abstractmethod - def object_property_values(self, ind: OWLNamedIndividual, pe: OWLObjectPropertyExpression, direct: bool = True) \ - -> Iterable[OWLNamedIndividual]: - """Gets the object property values for the specified individual and object property expression. - - Args: - ind: The individual that is the subject of the object property values. - pe: The object property expression whose values are to be retrieved for the specified individual. - direct: Specifies if the direct values should be retrieved (True), or if all values should be retrieved - (False), so that sub properties are taken into account. - - Returns: - The named individuals such that for each individual j, the set of reasoner axioms entails - ObjectPropertyAssertion(pe ind j). - """ - pass - - @abstractmethod - def instances(self, ce: OWLClassExpression, direct: bool = False) -> Iterable[OWLNamedIndividual]: - """Gets the individuals which are instances of the specified class expression. - - Args: - ce: The class expression whose instances are to be retrieved. - direct: Specifies if the direct instances should be retrieved (True), or if all instances should be - retrieved (False). - - Returns: - If direct is True, each named individual j where the set of reasoner axioms entails - DirectClassAssertion(ce, j). If direct is False, each named individual j where the set of reasoner axioms - entails ClassAssertion(ce, j). If ce is unsatisfiable with respect to the set of reasoner axioms then - nothing returned. - """ - pass - - @abstractmethod - def sub_classes(self, ce: OWLClassExpression, direct: bool = False, only_named: bool = True) \ - -> Iterable[OWLClassExpression]: - """Gets the set of named classes that are the strict (potentially direct) subclasses of the specified class - expression with respect to the reasoner axioms. - - Args: - ce: The class expression whose strict (direct) subclasses are to be retrieved. - direct: Specifies if the direct subclasses should be retrieved (True) or if the all subclasses - (descendant) classes should be retrieved (False). - only_named: Whether to only retrieve named sub-classes or also complex class expressions. - - Returns: - If direct is True, each class C where reasoner axioms entails DirectSubClassOf(C, ce). If direct is False, - each class C where reasoner axioms entails StrictSubClassOf(C, ce). If ce is equivalent to owl:Nothing then - nothing will be returned. - """ - pass - - @abstractmethod - def disjoint_object_properties(self, op: OWLObjectPropertyExpression) -> Iterable[OWLObjectPropertyExpression]: - """Gets the simplified object properties that are disjoint with the specified object property with respect - to the set of reasoner axioms. - - Args: - op: The object property whose disjoint object properties are to be retrieved. - - Returns: - All simplified object properties e where the root ontology imports closure entails - EquivalentObjectProperties(e ObjectPropertyComplementOf(op)) or - StrictSubObjectPropertyOf(e ObjectPropertyComplementOf(op)). - """ - pass - - @abstractmethod - def disjoint_data_properties(self, dp: OWLDataProperty) -> Iterable[OWLDataProperty]: - """Gets the data properties that are disjoint with the specified data property with respect - to the set of reasoner axioms. - - Args: - dp: The data property whose disjoint data properties are to be retrieved. - - Returns: - All data properties e where the root ontology imports closure entails - EquivalentDataProperties(e DataPropertyComplementOf(dp)) or - StrictSubDataPropertyOf(e DataPropertyComplementOf(dp)). - """ - pass - - @abstractmethod - def sub_data_properties(self, dp: OWLDataProperty, direct: bool = False) -> Iterable[OWLDataProperty]: - """Gets the set of named data properties that are the strict (potentially direct) subproperties of the - specified data property expression with respect to the imports closure of the root ontology. - - Args: - dp: The data property whose strict (direct) subproperties are to be retrieved. - direct: Specifies if the direct subproperties should be retrieved (True) or if the all subproperties - (descendants) should be retrieved (False). - - Returns: - If direct is True, each property P where the set of reasoner axioms entails DirectSubDataPropertyOf(P, pe). - If direct is False, each property P where the set of reasoner axioms entails - StrictSubDataPropertyOf(P, pe). If pe is equivalent to owl:bottomDataProperty then nothing will be - returned. - """ - pass - - @abstractmethod - def super_data_properties(self, dp: OWLDataProperty, direct: bool = False) -> Iterable[OWLDataProperty]: - """Gets the stream of data properties that are the strict (potentially direct) super properties of the - specified data property with respect to the imports closure of the root ontology. - - Args: - dp (OWLDataProperty): The data property whose super properties are to be retrieved. - direct (bool): Specifies if the direct super properties should be retrieved (True) or if the all - super properties (ancestors) should be retrieved (False). - - Returns: - Iterable of super properties. - """ - pass - - @abstractmethod - def sub_object_properties(self, op: OWLObjectPropertyExpression, direct: bool = False) \ - -> Iterable[OWLObjectPropertyExpression]: - """Gets the stream of simplified object property expressions that are the strict (potentially direct) - subproperties of the specified object property expression with respect to the imports closure of the root - ontology. - - Args: - op: The object property expression whose strict (direct) subproperties are to be retrieved. - direct: Specifies if the direct subproperties should be retrieved (True) or if the all subproperties - (descendants) should be retrieved (False). - - Returns: - If direct is True, simplified object property expressions, such that for each simplified object property - expression, P, the set of reasoner axioms entails DirectSubObjectPropertyOf(P, pe). - If direct is False, simplified object property expressions, such that for each simplified object property - expression, P, the set of reasoner axioms entails StrictSubObjectPropertyOf(P, pe). - If pe is equivalent to owl:bottomObjectProperty then nothing will be returned. - """ - pass - - @abstractmethod - def super_object_properties(self, op: OWLObjectPropertyExpression, direct: bool = False) \ - -> Iterable[OWLObjectPropertyExpression]: - """Gets the stream of object properties that are the strict (potentially direct) super properties of the - specified object property with respect to the imports closure of the root ontology. - - Args: - op (OWLObjectPropertyExpression): The object property expression whose super properties are to be - retrieved. - direct (bool): Specifies if the direct super properties should be retrieved (True) or if the all - super properties (ancestors) should be retrieved (False). - - Returns: - Iterable of super properties. - """ - pass - - @abstractmethod - def types(self, ind: OWLNamedIndividual, direct: bool = False) -> Iterable[OWLClass]: - """Gets the named classes which are (potentially direct) types of the specified named individual. - - Args: - ind: The individual whose types are to be retrieved. - direct: Specifies if the direct types should be retrieved (True), or if all types should be retrieved - (False). - - Returns: - If direct is True, each named class C where the set of reasoner axioms entails - DirectClassAssertion(C, ind). If direct is False, each named class C where the set of reasoner axioms - entails ClassAssertion(C, ind). - """ - pass - - @abstractmethod - def get_root_ontology(self) -> OWLOntology: - """Gets the "root" ontology that is loaded into this reasoner. The reasoner takes into account the axioms in - this ontology and its import's closure.""" - pass - - @abstractmethod - def super_classes(self, ce: OWLClassExpression, direct: bool = False, only_named: bool = True) \ - -> Iterable[OWLClassExpression]: - """Gets the stream of named classes that are the strict (potentially direct) super classes of the specified - class expression with respect to the imports closure of the root ontology. - - Args: - ce: The class expression whose strict (direct) super classes are to be retrieved. - direct: Specifies if the direct super classes should be retrieved (True) or if the all super classes - (ancestors) classes should be retrieved (False). - only_named: Whether to only retrieve named super classes or also complex class expressions. - - Returns: - If direct is True, each class C where the set of reasoner axioms entails DirectSubClassOf(ce, C). - If direct is False, each class C where set of reasoner axioms entails StrictSubClassOf(ce, C). - If ce is equivalent to owl:Thing then nothing will be returned. - """ - pass -class OWLReasonerEx(OWLReasoner, metaclass=ABCMeta): - """Extra convenience methods for OWL Reasoners""" - - # default - def data_property_ranges(self, pe: OWLDataProperty, direct: bool = False) -> Iterable[OWLDataRange]: - """Gets the data ranges that are the direct or indirect ranges of this property with respect to the imports - closure of the root ontology. - - Args: - pe: The property expression whose ranges are to be retrieved. - direct: Specifies if the direct ranges should be retrieved (True), or if all ranges should be retrieved - (False). - - Returns: - """ - for ax in self.get_root_ontology().data_property_range_axioms(pe): - yield ax.get_range() - if not direct: - logger.warning("indirect not implemented") - # TODO: - - # default - def all_data_property_values(self, pe: OWLDataProperty, direct: bool = True) -> Iterable[OWLLiteral]: - """Gets all values for the given data property expression that appear in the knowledge base. - - Args: - pe: The data property expression whose values are to be retrieved - direct: Specifies if only the direct values of the data property pe should be retrieved (True), or if - the values of sub properties of pe should be taken into account (False). - - Returns: - A set of OWLLiterals containing literals such that for each literal l in the set, the set of reasoner - axioms entails DataPropertyAssertion(pe ind l) for any ind. - """ - onto = self.get_root_ontology() - for ind in onto.individuals_in_signature(): - for lit in self.data_property_values(ind, pe, direct): - yield lit - - # default - def ind_data_properties(self, ind: OWLNamedIndividual, direct: bool = True) -> Iterable[OWLDataProperty]: - """Gets all data properties for the given individual that appear in the knowledge base. - - Args: - ind: The named individual whose data properties are to be retrieved - direct: Specifies if the direct data properties should be retrieved (True), or if all - data properties should be retrieved (False), so that sub properties are taken into account. +from owlapy.class_expression import OWLClass +from owlapy.iri import IRI +from owlapy.owl_axiom import OWLAxiom, OWLSubClassOfAxiom +from owlapy.owl_data_ranges import OWLDataRange, OWLDataComplementOf, OWLDataUnionOf, OWLDataIntersectionOf +from owlapy.owl_datatype import OWLDatatype +from owlapy.owl_object import OWLEntity +from owlapy.owl_ontology import Ontology, _parse_concept_to_owlapy, SyncOntology +from owlapy.abstracts.abstract_owl_ontology import OWLOntology +from owlapy.owl_ontology_manager import SyncOntologyManager +from owlapy.owl_property import OWLObjectPropertyExpression, OWLDataProperty, OWLObjectProperty, OWLObjectInverseOf, \ + OWLPropertyExpression, OWLDataPropertyExpression +from owlapy.owl_individual import OWLNamedIndividual +from owlapy.owl_literal import OWLLiteral +from owlapy.utils import LRUCache +from owlapy.abstracts.abstract_owl_reasoner import OWLReasoner, OWLReasonerEx - Returns: - All data properties pe where the set of reasoner axioms entails DataPropertyAssertion(pe ind l) - for atleast one l. - """ - onto = self.get_root_ontology() - for dp in onto.data_properties_in_signature(): - try: - next(iter(self.data_property_values(ind, dp, direct))) - yield dp - except StopIteration: - pass - - # default - def ind_object_properties(self, ind: OWLNamedIndividual, direct: bool = True) -> Iterable[OWLObjectProperty]: - """Gets all object properties for the given individual that appear in the knowledge base. +logger = logging.getLogger(__name__) - Args: - ind: The named individual whose object properties are to be retrieved - direct: Specifies if the direct object properties should be retrieved (True), or if all - object properties should be retrieved (False), so that sub properties are taken into account. +_P = TypeVar('_P', bound=OWLPropertyExpression) - Returns: - All data properties pe where the set of reasoner axioms entails ObjectPropertyAssertion(pe ind ind2) - for atleast one ind2. - """ - onto = self.get_root_ontology() - for op in onto.object_properties_in_signature(): - try: - next(iter(self.object_property_values(ind, op, direct))) - yield op - except StopIteration: - pass class OntologyReasoner(OWLReasonerEx): __slots__ = '_ontology', '_world' @@ -1598,86 +1147,519 @@ def _retrieve_triples(self, pe: OWLPropertyExpression) -> Iterable: class SyncReasoner(OWLReasonerEx): - def __init__(self, ontology_path: str, reasoner="HermiT"): + def __init__(self, ontology: Union[SyncOntology, str], reasoner="HermiT"): """ OWL reasoner that syncs to other reasoners like HermiT,Pellet,etc. Args: - ontology_path: Path of ontology that should be used by the reasoner. - reasoner: Choose from (case-sensitive): ["HermiT", "Pellet", "JFact", "Openllet"]. Default: "HermiT". + ontology(SyncOntology): Ontology that will be used by this reasoner. + reasoner: Name of the reasoner. Possible values (case-sensitive): ["HermiT", "Pellet", "JFact", "Openllet", + "Structural"]. + Default: "HermiT". """ - self.manager = OntologyManager() - self.ontology = self.manager.load_ontology(IRI.create("file://" + ontology_path)) - super().__init__(self.ontology) - self.adaptor = OWLAPIAdaptor(ontology_path, reasoner) + assert reasoner in ["HermiT", "Pellet", "JFact", "Openllet", "Structural"], \ + (f"'{reasoner}' is not implemented. Available reasoners: ['HermiT', 'Pellet', 'JFact', 'Openllet', " + f"'Structural']. " + f"This field is case sensitive.") + if isinstance(ontology, SyncOntology): + self.manager = ontology.manager + self.ontology = ontology + elif isinstance(ontology, str): + self.manager = SyncOntologyManager() + self.ontology = self.manager.load_ontology(ontology) + + self._owlapi_manager = self.manager.get_owlapi_manager() + self._owlapi_ontology = self.ontology.get_owlapi_ontology() + # super().__init__(self.ontology) + self.mapper = self.ontology.mapper + from org.semanticweb.owlapi.util import (InferredClassAssertionAxiomGenerator, InferredSubClassAxiomGenerator, + InferredEquivalentClassAxiomGenerator, + InferredDisjointClassesAxiomGenerator, + InferredEquivalentDataPropertiesAxiomGenerator, + InferredEquivalentObjectPropertyAxiomGenerator, + InferredInverseObjectPropertiesAxiomGenerator, + InferredSubDataPropertyAxiomGenerator, + InferredSubObjectPropertyAxiomGenerator, + InferredDataPropertyCharacteristicAxiomGenerator, + InferredObjectPropertyCharacteristicAxiomGenerator) + + self.inference_types_mapping = {"InferredClassAssertionAxiomGenerator": InferredClassAssertionAxiomGenerator(), + "InferredSubClassAxiomGenerator": InferredSubClassAxiomGenerator(), + "InferredDisjointClassesAxiomGenerator": InferredDisjointClassesAxiomGenerator(), + "InferredEquivalentClassAxiomGenerator": InferredEquivalentClassAxiomGenerator(), + "InferredInverseObjectPropertiesAxiomGenerator": InferredInverseObjectPropertiesAxiomGenerator(), + "InferredEquivalentDataPropertiesAxiomGenerator": InferredEquivalentDataPropertiesAxiomGenerator(), + "InferredEquivalentObjectPropertyAxiomGenerator": InferredEquivalentObjectPropertyAxiomGenerator(), + "InferredSubDataPropertyAxiomGenerator": InferredSubDataPropertyAxiomGenerator(), + "InferredSubObjectPropertyAxiomGenerator": InferredSubObjectPropertyAxiomGenerator(), + "InferredDataPropertyCharacteristicAxiomGenerator": InferredDataPropertyCharacteristicAxiomGenerator(), + "InferredObjectPropertyCharacteristicAxiomGenerator": InferredObjectPropertyCharacteristicAxiomGenerator(), + } + + # () Create a reasoner using the ontology + if reasoner == "HermiT": + from org.semanticweb.HermiT import ReasonerFactory + self._owlapi_reasoner = ReasonerFactory().createReasoner(self._owlapi_ontology) + assert self._owlapi_reasoner.getReasonerName() == "HermiT" + elif reasoner == "JFact": + from uk.ac.manchester.cs.jfact import JFactFactory + self._owlapi_reasoner = JFactFactory().createReasoner(self._owlapi_ontology) + elif reasoner == "Pellet": + from openllet.owlapi import PelletReasonerFactory + self._owlapi_reasoner = PelletReasonerFactory().createReasoner(self._owlapi_ontology) + elif reasoner == "Openllet": + from openllet.owlapi import OpenlletReasonerFactory + self._owlapi_reasoner = OpenlletReasonerFactory().getInstance().createReasoner(self._owlapi_ontology) + elif reasoner == "Structural": + from org.semanticweb.owlapi.reasoner.structural import StructuralReasonerFactory + self._owlapi_reasoner = StructuralReasonerFactory().createReasoner(self._owlapi_ontology) + else: + raise NotImplementedError("Not implemented") - def instances(self, ce: OWLClassExpression, direct: bool = False) -> Iterable[OWLNamedIndividual]: - yield from self.adaptor.instances(ce, direct) + def instances(self, ce: OWLClassExpression, direct=False) -> List[OWLNamedIndividual]: + """ + Get the instances for a given class expression using HermiT. - def data_property_domains(self, pe: OWLDataProperty, direct: bool = False) -> Iterable[OWLClassExpression]: - yield from self.adaptor.data_property_domains(pe, direct) + Args: + ce (OWLClassExpression): The class expression in OWLAPY format. + direct (bool): Whether to get direct instances or not. Defaults to False. - def object_property_domains(self, pe: OWLObjectProperty, direct: bool = False) -> Iterable[OWLClassExpression]: - yield from self.adaptor.object_property_domains(pe, direct) + Returns: + list: A list of individuals classified by the given class expression. + """ + inds = self._owlapi_reasoner.getInstances(self.mapper.map_(ce), direct).getFlattened() + return [self.mapper.map_(ind) for ind in inds] - def object_property_ranges(self, pe: OWLObjectProperty, direct: bool = False) -> Iterable[OWLClassExpression]: - yield from self.adaptor.object_property_ranges(pe, direct) + def equivalent_classes(self, ce: OWLClassExpression) -> List[OWLClassExpression]: + """ + Gets the set of named classes that are equivalent to the specified class expression with + respect to the set of reasoner axioms. - def equivalent_classes(self, ce: OWLClassExpression, only_named: bool = True) -> Iterable[OWLClassExpression]: - yield from self.adaptor.equivalent_classes(ce) + Args: + ce (OWLClassExpression): The class expression whose equivalent classes are to be retrieved. - def disjoint_classes(self, ce: OWLClassExpression, only_named: bool = True) -> Iterable[OWLClassExpression]: - yield from self.adaptor.disjoint_classes(ce) + Returns: + Equivalent classes of the given class expression. + """ + classes = self._owlapi_reasoner.getEquivalentClasses(self.mapper.map_(ce)).getEntities() + yield from [self.mapper.map_(cls) for cls in classes] - def different_individuals(self, ind: OWLNamedIndividual) -> Iterable[OWLNamedIndividual]: - yield from self.adaptor.different_individuals(ind) + def disjoint_classes(self, ce: OWLClassExpression) -> List[OWLClassExpression]: + """ + Gets the classes that are disjoint with the specified class expression. - def same_individuals(self, ind: OWLNamedIndividual) -> Iterable[OWLNamedIndividual]: - yield from self.adaptor.same_individuals(ind) + Args: + ce (OWLClassExpression): The class expression whose disjoint classes are to be retrieved. - def data_property_values(self, e: OWLEntity, pe: OWLDataProperty, direct: bool = True) -> Iterable[OWLLiteral]: - yield from self.adaptor.data_property_values(e, pe) + Returns: + Disjoint classes of the given class expression. + """ + classes = self._owlapi_reasoner.getDisjointClasses(self.mapper.map_(ce)).getFlattened() + yield from [self.mapper.map_(cls) for cls in classes] - def object_property_values(self, ind: OWLNamedIndividual, pe: OWLObjectPropertyExpression, direct: bool = False) -> \ - Iterable[OWLNamedIndividual]: - yield from self.adaptor.object_property_values(ind, pe) + def sub_classes(self, ce: OWLClassExpression, direct=False) -> List[OWLClassExpression]: + """ + Gets the set of named classes that are the strict (potentially direct) subclasses of the + specified class expression with respect to the reasoner axioms. - def sub_classes(self, ce: OWLClassExpression, direct: bool = False, only_named: bool = True) -> Iterable[ - OWLClassExpression]: - yield from self.adaptor.sub_classes(ce, direct) + Args: + ce (OWLClassExpression): The class expression whose strict (direct) subclasses are to be retrieved. + direct (bool, optional): Specifies if the direct subclasses should be retrieved (True) or if + all subclasses (descendant) classes should be retrieved (False). Defaults to False. + Returns: + The subclasses of the given class expression depending on `direct` field. + """ + classes = self._owlapi_reasoner.getSubClasses(self.mapper.map_(ce), direct).getFlattened() + yield from [self.mapper.map_(cls) for cls in classes] - def super_classes(self, ce: OWLClassExpression, direct: bool = False, only_named: bool = True) -> Iterable[ - OWLClassExpression]: - yield from self.adaptor.super_classes(ce, direct) + def super_classes(self, ce: OWLClassExpression, direct=False) -> List[OWLClassExpression]: + """ + Gets the stream of named classes that are the strict (potentially direct) super classes of + the specified class expression with respect to the imports closure of the root ontology. - def equivalent_object_properties(self, op: OWLObjectPropertyExpression) -> Iterable[OWLObjectPropertyExpression]: - yield from self.adaptor.equivalent_object_properties(op) + Args: + ce (OWLClassExpression): The class expression whose strict (direct) subclasses are to be retrieved. + direct (bool, optional): Specifies if the direct superclasses should be retrieved (True) or if + all superclasses (descendant) classes should be retrieved (False). Defaults to False. - def equivalent_data_properties(self, dp: OWLDataProperty) -> Iterable[OWLDataProperty]: - yield from self.adaptor.equivalent_data_properties(dp) + Returns: + The subclasses of the given class expression depending on `direct` field. + """ + classes = self._owlapi_reasoner.getSuperClasses(self.mapper.map_(ce), direct).getFlattened() + yield from [self.mapper.map_(cls) for cls in classes] - def disjoint_object_properties(self, op: OWLObjectPropertyExpression) -> Iterable[OWLObjectPropertyExpression]: - yield from self.adaptor.disjoint_object_properties(op) + def data_property_domains(self, p: OWLDataProperty, direct: bool = False): + """Gets the class expressions that are the direct or indirect domains of this property with respect to the + imports closure of the root ontology. - def disjoint_data_properties(self, dp: OWLDataProperty) -> Iterable[OWLDataProperty]: - yield from self.adaptor.disjoint_data_properties(dp) + Args: + p: The property expression whose domains are to be retrieved. + direct: Specifies if the direct domains should be retrieved (True), or if all domains should be retrieved + (False). - def super_data_properties(self, dp: OWLDataProperty, direct: bool = False) -> Iterable[OWLDataProperty]: - yield from self.adaptor.super_data_properties(dp, direct) + Returns: + :Let N = equivalent_classes(DataSomeValuesFrom(pe rdfs:Literal)). If direct is True: then if N is not + empty then the return value is N, else the return value is the result of + super_classes(DataSomeValuesFrom(pe rdfs:Literal), true). If direct is False: then the result of + super_classes(DataSomeValuesFrom(pe rdfs:Literal), false) together with N if N is non-empty. + (Note, rdfs:Literal is the top datatype). + """ + yield from [self.mapper.map_(ce) for ce in + self._owlapi_reasoner.getDataPropertyDomains(self.mapper.map_(p), direct).getFlattened()] - def sub_data_properties(self, dp: OWLDataProperty, direct: bool = False) -> Iterable[OWLDataProperty]: - yield from self.adaptor.sub_data_properties(dp, direct) + def object_property_domains(self, p: OWLObjectProperty, direct: bool = False): + """Gets the class expressions that are the direct or indirect domains of this property with respect to the + imports closure of the root ontology. + + Args: + p: The property expression whose domains are to be retrieved. + direct: Specifies if the direct domains should be retrieved (True), or if all domains should be retrieved + (False). - def super_object_properties(self, op: OWLObjectPropertyExpression, direct: bool = False) -> Iterable[ - OWLObjectPropertyExpression]: - yield from self.adaptor.super_object_properties(op, direct) + Returns: + :Let N = equivalent_classes(ObjectSomeValuesFrom(pe owl:Thing)). If direct is True: then if N is not empty + then the return value is N, else the return value is the result of + super_classes(ObjectSomeValuesFrom(pe owl:Thing), true). If direct is False: then the result of + super_classes(ObjectSomeValuesFrom(pe owl:Thing), false) together with N if N is non-empty. + """ + yield from [self.mapper.map_(ce) for ce in + self._owlapi_reasoner.getObjectPropertyDomains(self.mapper.map_(p), direct).getFlattened()] - def sub_object_properties(self, op: OWLObjectPropertyExpression, direct: bool = False) -> Iterable[ - OWLObjectPropertyExpression]: - yield from self.adaptor.sub_object_properties(op, direct) + def object_property_ranges(self, p: OWLObjectProperty, direct: bool = False): + """Gets the class expressions that are the direct or indirect ranges of this property with respect to the + imports closure of the root ontology. - def types(self, ind: OWLNamedIndividual, direct: bool = False) -> Iterable[OWLClass]: - yield from self.adaptor.types(ind, direct) + Args: + p: The property expression whose ranges are to be retrieved. + direct: Specifies if the direct ranges should be retrieved (True), or if all ranges should be retrieved + (False). + + Returns: + :Let N = equivalent_classes(ObjectSomeValuesFrom(ObjectInverseOf(pe) owl:Thing)). If direct is True: then + if N is not empty then the return value is N, else the return value is the result of + super_classes(ObjectSomeValuesFrom(ObjectInverseOf(pe) owl:Thing), true). If direct is False: then + the result of super_classes(ObjectSomeValuesFrom(ObjectInverseOf(pe) owl:Thing), false) together with N + if N is non-empty. + """ + yield from [self.mapper.map_(ce) for ce in + self._owlapi_reasoner.getObjectPropertyRanges(self.mapper.map_(p), direct).getFlattened()] + + def sub_object_properties(self, p: OWLObjectProperty, direct: bool = False): + """Gets the stream of simplified object property expressions that are the strict (potentially direct) + subproperties of the specified object property expression with respect to the imports closure of the root + ontology. + + Args: + p: The object property expression whose strict (direct) subproperties are to be retrieved. + direct: Specifies if the direct subproperties should be retrieved (True) or if the all subproperties + (descendants) should be retrieved (False). + + Returns: + If direct is True, simplified object property expressions, such that for each simplified object property + expression, P, the set of reasoner axioms entails DirectSubObjectPropertyOf(P, pe). + If direct is False, simplified object property expressions, such that for each simplified object property + expression, P, the set of reasoner axioms entails StrictSubObjectPropertyOf(P, pe). + If pe is equivalent to owl:bottomObjectProperty then nothing will be returned. + """ + yield from [self.mapper.map_(pe) for pe in + self._owlapi_reasoner.getSubObjectProperties(self.mapper.map_(p), direct).getFlattened()] + + def super_object_properties(self, p: OWLObjectProperty, direct: bool = False): + """Gets the stream of object properties that are the strict (potentially direct) super properties of the + specified object property with respect to the imports closure of the root ontology. + + Args: + p (OWLObjectPropertyExpression): The object property expression whose super properties are to be + retrieved. + direct (bool): Specifies if the direct super properties should be retrieved (True) or if the all + super properties (ancestors) should be retrieved (False). + + Returns: + Iterable of super properties. + """ + yield from [self.mapper.map_(pe) for pe in + self._owlapi_reasoner.getSuperObjectProperties(self.mapper.map_(p), direct).getFlattened()] + + def sub_data_properties(self, p: OWLDataProperty, direct: bool = False): + """Gets the set of named data properties that are the strict (potentially direct) subproperties of the + specified data property expression with respect to the imports closure of the root ontology. + + Args: + p: The data property whose strict (direct) subproperties are to be retrieved. + direct: Specifies if the direct subproperties should be retrieved (True) or if the all subproperties + (descendants) should be retrieved (False). + + Returns: + If direct is True, each property P where the set of reasoner axioms entails DirectSubDataPropertyOf(P, pe). + If direct is False, each property P where the set of reasoner axioms entails + StrictSubDataPropertyOf(P, pe). If pe is equivalent to owl:bottomDataProperty then nothing will be + returned. + """ + yield from [self.mapper.map_(pe) for pe in + self._owlapi_reasoner.getSubDataProperties(self.mapper.map_(p), direct).getFlattened()] + + def super_data_properties(self, p: OWLDataProperty, direct: bool = False): + """Gets the stream of data properties that are the strict (potentially direct) super properties of the + specified data property with respect to the imports closure of the root ontology. + + Args: + p (OWLDataProperty): The data property whose super properties are to be retrieved. + direct (bool): Specifies if the direct super properties should be retrieved (True) or if the all + super properties (ancestors) should be retrieved (False). + + Returns: + Iterable of super properties. + """ + yield from [self.mapper.map_(pe) for pe in + self._owlapi_reasoner.getSuperDataProperties(self.mapper.map_(p), direct).getFlattened()] + + def different_individuals(self, i: OWLNamedIndividual): + """Gets the individuals that are different from the specified individual with respect to the set of + reasoner axioms. + + Args: + i: The individual whose different individuals are to be retrieved. + + Returns: + All individuals x where the set of reasoner axioms entails DifferentIndividuals(ind x). + """ + yield from [self.mapper.map_(ind) for ind in + self._owlapi_reasoner.getDifferentIndividuals(self.mapper.map_(i)).getFlattened()] + + def same_individuals(self, i: OWLNamedIndividual): + """Gets the individuals that are the same as the specified individual with respect to the set of + reasoner axioms. + + Args: + i: The individual whose same individuals are to be retrieved. + + Returns: + All individuals x where the root ontology imports closure entails SameIndividual(ind x). + """ + yield from [self.mapper.map_(ind) for ind in + self.mapper.to_list(self._owlapi_reasoner.sameIndividuals(self.mapper.map_(i)))] + + def equivalent_object_properties(self, p: OWLObjectProperty): + """Gets the simplified object properties that are equivalent to the specified object property with respect + to the set of reasoner axioms. + + Args: + p: The object property whose equivalent object properties are to be retrieved. + + Returns: + All simplified object properties e where the root ontology imports closure entails + EquivalentObjectProperties(op e). If op is unsatisfiable with respect to the set of reasoner axioms + then owl:bottomDataProperty will be returned. + """ + yield from [self.mapper.map_(pe) for pe in + self.mapper.to_list(self._owlapi_reasoner.equivalentObjectProperties(self.mapper.map_(p)))] + + def equivalent_data_properties(self, p: OWLDataProperty): + """Gets the data properties that are equivalent to the specified data property with respect to the set of + reasoner axioms. + + Args: + p: The data property whose equivalent data properties are to be retrieved. + + Returns: + All data properties e where the root ontology imports closure entails EquivalentDataProperties(dp e). + If dp is unsatisfiable with respect to the set of reasoner axioms then owl:bottomDataProperty will + be returned. + """ + yield from [self.mapper.map_(pe) for pe in + self.mapper.to_list(self._owlapi_reasoner.getEquivalentDataProperties(self.mapper.map_(p)))] + + def object_property_values(self, i: OWLNamedIndividual, p: OWLObjectProperty): + """Gets the object property values for the specified individual and object property expression. + + Args: + i: The individual that is the subject of the object property values. + p: The object property expression whose values are to be retrieved for the specified individual. + + Returns: + The named individuals such that for each individual j, the set of reasoner axioms entails + ObjectPropertyAssertion(pe ind j). + """ + yield from [self.mapper.map_(ind) for ind in + self._owlapi_reasoner.getObjectPropertyValues(self.mapper.map_(i), self.mapper.map_(p)).getFlattened()] + + def data_property_values(self, e: OWLEntity, p: OWLDataProperty): + """Gets the data property values for the specified entity and data property expression. + + Args: + e: The entity (usually an individual) that is the subject of the data property values. + p: The data property expression whose values are to be retrieved for the specified individual. + + Returns: + A set of OWLLiterals containing literals such that for each literal l in the set, the set of reasoner + axioms entails DataPropertyAssertion(pe ind l). + """ + yield from [self.mapper.map_(literal) for literal in + self.mapper.to_list(self._owlapi_reasoner.dataPropertyValues(self.mapper.map_(e), self.mapper.map_(p)))] + + def disjoint_object_properties(self, p: OWLObjectProperty): + """Gets the simplified object properties that are disjoint with the specified object property with respect + to the set of reasoner axioms. + + Args: + p: The object property whose disjoint object properties are to be retrieved. + + Returns: + All simplified object properties e where the root ontology imports closure entails + EquivalentObjectProperties(e ObjectPropertyComplementOf(op)) or + StrictSubObjectPropertyOf(e ObjectPropertyComplementOf(op)). + """ + yield from [self.mapper.map_(pe) for pe in + self._owlapi_reasoner.getDisjointObjectProperties(self.mapper.map_(p)).getFlattened()] + + def disjoint_data_properties(self, p: OWLDataProperty): + """Gets the data properties that are disjoint with the specified data property with respect + to the set of reasoner axioms. + + Args: + p: The data property whose disjoint data properties are to be retrieved. + + Returns: + All data properties e where the root ontology imports closure entails + EquivalentDataProperties(e DataPropertyComplementOf(dp)) or + StrictSubDataPropertyOf(e DataPropertyComplementOf(dp)). + """ + yield from [self.mapper.map_(pe) for pe in + self._owlapi_reasoner.getDisjointDataProperties(self.mapper.map_(p)).getFlattened()] + + def types(self, i: OWLNamedIndividual, direct: bool = False): + """Gets the named classes which are (potentially direct) types of the specified named individual. + + Args: + i: The individual whose types are to be retrieved. + direct: Specifies if the direct types should be retrieved (True), or if all types should be retrieved + (False). + + Returns: + If direct is True, each named class C where the set of reasoner axioms entails + DirectClassAssertion(C, ind). If direct is False, each named class C where the set of reasoner axioms + entails ClassAssertion(C, ind). + """ + yield from [self.mapper.map_(ind) for ind in + self._owlapi_reasoner.getTypes(self.mapper.map_(i), direct).getFlattened()] + + def has_consistent_ontology(self) -> bool: + """ + Check if the used ontology is consistent. + + Returns: + bool: True if the ontology used by this reasoner is consistent, False otherwise. + """ + return self._owlapi_reasoner.isConsistent() + + def infer_axioms(self, inference_types: list[str]) -> Iterable[OWLAxiom]: + """ + Infer the specified inference type of axioms for the ontology managed by this instance's reasoner and + return them. + + Args: + inference_types: Axiom inference types: Avaliable options (can set more than 1): + ["InferredClassAssertionAxiomGenerator", "InferredSubClassAxiomGenerator", + "InferredDisjointClassesAxiomGenerator", "InferredEquivalentClassAxiomGenerator", + "InferredEquivalentDataPropertiesAxiomGenerator","InferredEquivalentObjectPropertyAxiomGenerator", + "InferredInverseObjectPropertiesAxiomGenerator","InferredSubDataPropertyAxiomGenerator", + "InferredSubObjectPropertyAxiomGenerator","InferredDataPropertyCharacteristicAxiomGenerator", + "InferredObjectPropertyCharacteristicAxiomGenerator" + ] + + Returns: + Iterable of inferred axioms. + """ + from java.util import ArrayList + from org.semanticweb.owlapi.util import InferredOntologyGenerator + + generators = ArrayList() + for i in inference_types: + if java_object := self.inference_types_mapping.get(i, None): + generators.add(java_object) + iog = InferredOntologyGenerator(self._owlapi_reasoner, generators) + inferred_axioms = list(iog.getAxiomGenerators()) + for ia in inferred_axioms: + for axiom in ia.createAxioms(self._owlapi_manager.getOWLDataFactory(), self._owlapi_reasoner): + yield self.mapper.map_(axiom) + + def infer_axioms_and_save(self, output_path: str = None, output_format: str = None, inference_types: list[str] = None): + """ + Generates inferred axioms for the ontology managed by this instance's reasoner and saves them to a file. + This function uses the OWL API to generate inferred class assertion axioms based on the ontology and reasoner + associated with this instance. The inferred axioms are saved to the specified output file in the desired format. + + Args: + output_path : The name of the file where the inferred axioms will be saved. + output_format : The format in which to save the inferred axioms. Supported formats are: + - "ttl" or "turtle" for Turtle format + - "rdf/xml" for RDF/XML format + - "owl/xml" for OWL/XML format + If not specified, the format of the original ontology is used. + inference_types: Axiom inference types: Avaliable options (can set more than 1): + ["InferredClassAssertionAxiomGenerator", "InferredSubClassAxiomGenerator", + "InferredDisjointClassesAxiomGenerator", "InferredEquivalentClassAxiomGenerator", + "InferredEquivalentDataPropertiesAxiomGenerator","InferredEquivalentObjectPropertyAxiomGenerator", + "InferredInverseObjectPropertiesAxiomGenerator","InferredSubDataPropertyAxiomGenerator", + "InferredSubObjectPropertyAxiomGenerator","InferredDataPropertyCharacteristicAxiomGenerator", + "InferredObjectPropertyCharacteristicAxiomGenerator" + ] + + Returns: + None (the file is saved to the specified directory) + """ + from java.io import File, FileOutputStream + from java.util import ArrayList + from org.semanticweb.owlapi.util import InferredOntologyGenerator + from org.semanticweb.owlapi.formats import TurtleDocumentFormat, RDFXMLDocumentFormat, OWLXMLDocumentFormat + if output_format == "ttl" or output_format == "turtle": + document_format = TurtleDocumentFormat() + elif output_format == "rdf/xml": + document_format = RDFXMLDocumentFormat() + elif output_format == "owl/xml": + document_format = OWLXMLDocumentFormat() + else: + document_format = self._owlapi_manager.getOntologyFormat(self._owlapi_ontology) + generators = ArrayList() + + for i in inference_types: + if java_object := self.inference_types_mapping.get(i, None): + generators.add(java_object) + iog = InferredOntologyGenerator(self._owlapi_reasoner, generators) + inferred_axioms_ontology = self._owlapi_manager.createOntology() + iog.fillOntology(self._owlapi_manager.getOWLDataFactory(), inferred_axioms_ontology) + inferred_ontology_file = File(output_path).getAbsoluteFile() + output_stream = FileOutputStream(inferred_ontology_file) + self._owlapi_manager.saveOntology(inferred_axioms_ontology, document_format, output_stream) + + def generate_and_save_inferred_class_assertion_axioms(self, output="temp.ttl", output_format: str = None): + """ + Generates inferred class assertion axioms for the ontology managed by this instance's reasoner and saves them + to a file. This function uses the OWL API to generate inferred class assertion axioms based on the ontology + and reasoner associated with this instance. The inferred axioms are saved to the specified output file in + the desired format. + Parameters: + ----------- + output : str, optional + The name of the file where the inferred axioms will be saved. Default is "temp.ttl". + output_format : str, optional + The format in which to save the inferred axioms. Supported formats are: + - "ttl" or "turtle" for Turtle format + - "rdf/xml" for RDF/XML format + - "owl/xml" for OWL/XML format + If not specified, the format of the original ontology is used. + Notes: + ------ + - The function supports saving in multiple formats: Turtle, RDF/XML, and OWL/XML. + - The inferred axioms are generated using the reasoner associated with this instance and the OWL API's + InferredClassAssertionAxiomGenerator. + - The inferred axioms are added to a new ontology which is then saved in the specified format. + Example: + -------- + >>> instance.generate_and_save_inferred_class_assertion_axioms(output="inferred_axioms.ttl", format="ttl") + This will save the inferred class assertion axioms to the file "inferred_axioms.ttl" in Turtle format. + """ + self.infer_axioms_and_save(output, output_format, ["InferredClassAssertionAxiomGenerator"]) def get_root_ontology(self) -> OWLOntology: - return self.ontology \ No newline at end of file + return self.ontology diff --git a/owlapy/owlapi_adaptor.py b/owlapy/owlapi_adaptor.py deleted file mode 100644 index 856cef2b..00000000 --- a/owlapy/owlapi_adaptor.py +++ /dev/null @@ -1,584 +0,0 @@ -"""Owlapi Adaptor - -Part of the docstrings are taken directly from owlapi -""" -import jpype.imports -import os -import pkg_resources - -from owlapy.class_expression import OWLClassExpression -from owlapy.owl_axiom import OWLAxiom -from owlapy.owl_individual import OWLNamedIndividual -from owlapy.owl_object import OWLEntity -from owlapy.owl_property import OWLDataProperty, OWLObjectProperty -from typing import List, Iterable - - -def to_list(stream_obj): - """Converts Java Stream object to Python list""" - return stream_obj.collect(jpype.JClass("java.util.stream.Collectors").toList()) - - -class OWLAPIAdaptor: - """ - A class to interface with the OWL API using the HermiT reasoner, enabling ontology management, - reasoning, and parsing class expressions in Manchester OWL Syntax. - - Attributes: - path (str): The file path to the ontology. - name_reasoner (str): The reasoner to be used, default is "HermiT". - manager: The OWL ontology manager. - ontology: The loaded OWL ontology. - reasoner: Choose from (case-sensitive): ["HermiT", "Pellet", "JFact", "Openllet"]. Default: "HermiT". - """ - - 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. - 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", "Pellet", "JFact", "Openllet"], \ - (f"'{name_reasoner}' is not implemented. Available reasoners: ['HermiT', 'Pellet', 'JFact', 'Openllet']. " - f"This field is case sensitive.") - self.name_reasoner = name_reasoner - # 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 reasoner for the loaded ontology - self.reasoner = None - # () For mapping entities/expressions from/to owlapi - self.mapper = None - # () 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 _setup(self): - """ - Start the JVM if not already, import necessary OWL API dependencies, and initialize attributes. - """ - if not jpype.isJVMStarted(): - self._startJVM() - - # Imports - from owlapy.owlapi_mapper import OWLAPIMapper - from org.semanticweb.owlapi.apibinding import OWLManager - from java.io import File - from org.semanticweb.owlapi.util import (InferredClassAssertionAxiomGenerator, InferredSubClassAxiomGenerator, - InferredEquivalentClassAxiomGenerator, - InferredDisjointClassesAxiomGenerator, - InferredEquivalentDataPropertiesAxiomGenerator, - InferredEquivalentObjectPropertyAxiomGenerator, - InferredInverseObjectPropertiesAxiomGenerator, - InferredSubDataPropertyAxiomGenerator, - InferredSubObjectPropertyAxiomGenerator, - InferredDataPropertyCharacteristicAxiomGenerator, - InferredObjectPropertyCharacteristicAxiomGenerator) - - self.inference_types_mapping = {"InferredClassAssertionAxiomGenerator": InferredClassAssertionAxiomGenerator(), - "InferredSubClassAxiomGenerator": InferredSubClassAxiomGenerator(), - "InferredDisjointClassesAxiomGenerator": InferredDisjointClassesAxiomGenerator(), - "InferredEquivalentClassAxiomGenerator": InferredEquivalentClassAxiomGenerator(), - "InferredInverseObjectPropertiesAxiomGenerator": InferredInverseObjectPropertiesAxiomGenerator(), - "InferredEquivalentDataPropertiesAxiomGenerator": InferredEquivalentDataPropertiesAxiomGenerator(), - "InferredEquivalentObjectPropertyAxiomGenerator": InferredEquivalentObjectPropertyAxiomGenerator(), - "InferredSubDataPropertyAxiomGenerator": InferredSubDataPropertyAxiomGenerator(), - "InferredSubObjectPropertyAxiomGenerator": InferredSubObjectPropertyAxiomGenerator(), - "InferredDataPropertyCharacteristicAxiomGenerator": InferredDataPropertyCharacteristicAxiomGenerator(), - "InferredObjectPropertyCharacteristicAxiomGenerator": InferredObjectPropertyCharacteristicAxiomGenerator(), - } - - 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") - - # () Manager is needed to load an ontology - self.manager = OWLManager.createOWLOntologyManager() - # () Load a local ontology using the manager - self.ontology = self.manager.loadOntologyFromOntologyDocument(File(self.path)) - self.mapper = OWLAPIMapper(self.ontology) - - # () Create a reasoner for the loaded ontology - if self.name_reasoner == "HermiT": - self.reasoner = ReasonerFactory().createReasoner(self.ontology) - assert self.reasoner.getReasonerName() == "HermiT" - 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) - - def has_consistent_ontology(self) -> bool: - """ - Check if the used ontology is consistent. - - Returns: - bool: True if the ontology is consistent, False otherwise. - """ - return self.reasoner.isConsistent() - - def instances(self, ce: OWLClassExpression, direct=False) -> List[OWLNamedIndividual]: - """ - Get the instances for a given class expression using HermiT. - - Args: - ce (OWLClassExpression): The class expression in OWLAPY format. - direct (bool): Whether to get direct instances or not. Defaults to False. - - Returns: - list: A list of individuals classified by the given class expression. - """ - inds = self.reasoner.getInstances(self.mapper.map_(ce), direct).getFlattened() - return [self.mapper.map_(ind) for ind in inds] - - def equivalent_classes(self, ce: OWLClassExpression) -> List[OWLClassExpression]: - """ - Gets the set of named classes that are equivalent to the specified class expression with - respect to the set of reasoner axioms. - - Args: - ce (OWLClassExpression): The class expression whose equivalent classes are to be retrieved. - - Returns: - Equivalent classes of the given class expression. - """ - classes = self.reasoner.getEquivalentClasses(self.mapper.map_(ce)).getEntities() - yield from [self.mapper.map_(cls) for cls in classes] - - def disjoint_classes(self, ce: OWLClassExpression) -> List[OWLClassExpression]: - """ - Gets the classes that are disjoint with the specified class expression. - - Args: - ce (OWLClassExpression): The class expression whose disjoint classes are to be retrieved. - - Returns: - Disjoint classes of the given class expression. - """ - classes = self.reasoner.getDisjointClasses(self.mapper.map_(ce)).getFlattened() - yield from [self.mapper.map_(cls) for cls in classes] - - def sub_classes(self, ce: OWLClassExpression, direct=False) -> List[OWLClassExpression]: - """ - Gets the set of named classes that are the strict (potentially direct) subclasses of the - specified class expression with respect to the reasoner axioms. - - Args: - ce (OWLClassExpression): The class expression whose strict (direct) subclasses are to be retrieved. - direct (bool, optional): Specifies if the direct subclasses should be retrieved (True) or if - all subclasses (descendant) classes should be retrieved (False). Defaults to False. - Returns: - The subclasses of the given class expression depending on `direct` field. - """ - classes = self.reasoner.getSubClasses(self.mapper.map_(ce), direct).getFlattened() - yield from [self.mapper.map_(cls) for cls in classes] - - def super_classes(self, ce: OWLClassExpression, direct=False) -> List[OWLClassExpression]: - """ - Gets the stream of named classes that are the strict (potentially direct) super classes of - the specified class expression with respect to the imports closure of the root ontology. - - Args: - ce (OWLClassExpression): The class expression whose strict (direct) subclasses are to be retrieved. - direct (bool, optional): Specifies if the direct superclasses should be retrieved (True) or if - all superclasses (descendant) classes should be retrieved (False). Defaults to False. - - Returns: - The subclasses of the given class expression depending on `direct` field. - """ - classes = self.reasoner.getSuperClasses(self.mapper.map_(ce), direct).getFlattened() - yield from [self.mapper.map_(cls) for cls in classes] - - def data_property_domains(self, p: OWLDataProperty, direct: bool = False): - """Gets the class expressions that are the direct or indirect domains of this property with respect to the - imports closure of the root ontology. - - Args: - p: The property expression whose domains are to be retrieved. - direct: Specifies if the direct domains should be retrieved (True), or if all domains should be retrieved - (False). - - Returns: - :Let N = equivalent_classes(DataSomeValuesFrom(pe rdfs:Literal)). If direct is True: then if N is not - empty then the return value is N, else the return value is the result of - super_classes(DataSomeValuesFrom(pe rdfs:Literal), true). If direct is False: then the result of - super_classes(DataSomeValuesFrom(pe rdfs:Literal), false) together with N if N is non-empty. - (Note, rdfs:Literal is the top datatype). - """ - yield from [self.mapper.map_(ce) for ce in - self.reasoner.getDataPropertyDomains(self.mapper.map_(p), direct).getFlattened()] - - def object_property_domains(self, p: OWLObjectProperty, direct: bool = False): - """Gets the class expressions that are the direct or indirect domains of this property with respect to the - imports closure of the root ontology. - - Args: - p: The property expression whose domains are to be retrieved. - direct: Specifies if the direct domains should be retrieved (True), or if all domains should be retrieved - (False). - - Returns: - :Let N = equivalent_classes(ObjectSomeValuesFrom(pe owl:Thing)). If direct is True: then if N is not empty - then the return value is N, else the return value is the result of - super_classes(ObjectSomeValuesFrom(pe owl:Thing), true). If direct is False: then the result of - super_classes(ObjectSomeValuesFrom(pe owl:Thing), false) together with N if N is non-empty. - """ - yield from [self.mapper.map_(ce) for ce in - self.reasoner.getObjectPropertyDomains(self.mapper.map_(p), direct).getFlattened()] - - def object_property_ranges(self, p: OWLObjectProperty, direct: bool = False): - """Gets the class expressions that are the direct or indirect ranges of this property with respect to the - imports closure of the root ontology. - - Args: - p: The property expression whose ranges are to be retrieved. - direct: Specifies if the direct ranges should be retrieved (True), or if all ranges should be retrieved - (False). - - Returns: - :Let N = equivalent_classes(ObjectSomeValuesFrom(ObjectInverseOf(pe) owl:Thing)). If direct is True: then - if N is not empty then the return value is N, else the return value is the result of - super_classes(ObjectSomeValuesFrom(ObjectInverseOf(pe) owl:Thing), true). If direct is False: then - the result of super_classes(ObjectSomeValuesFrom(ObjectInverseOf(pe) owl:Thing), false) together with N - if N is non-empty. - """ - yield from [self.mapper.map_(ce) for ce in - self.reasoner.getObjectPropertyRanges(self.mapper.map_(p), direct).getFlattened()] - - def sub_object_properties(self, p: OWLObjectProperty, direct: bool = False): - """Gets the stream of simplified object property expressions that are the strict (potentially direct) - subproperties of the specified object property expression with respect to the imports closure of the root - ontology. - - Args: - p: The object property expression whose strict (direct) subproperties are to be retrieved. - direct: Specifies if the direct subproperties should be retrieved (True) or if the all subproperties - (descendants) should be retrieved (False). - - Returns: - If direct is True, simplified object property expressions, such that for each simplified object property - expression, P, the set of reasoner axioms entails DirectSubObjectPropertyOf(P, pe). - If direct is False, simplified object property expressions, such that for each simplified object property - expression, P, the set of reasoner axioms entails StrictSubObjectPropertyOf(P, pe). - If pe is equivalent to owl:bottomObjectProperty then nothing will be returned. - """ - yield from [self.mapper.map_(pe) for pe in - self.reasoner.getSubObjectProperties(self.mapper.map_(p), direct).getFlattened()] - - def super_object_properties(self, p: OWLObjectProperty, direct: bool = False): - """Gets the stream of object properties that are the strict (potentially direct) super properties of the - specified object property with respect to the imports closure of the root ontology. - - Args: - p (OWLObjectPropertyExpression): The object property expression whose super properties are to be - retrieved. - direct (bool): Specifies if the direct super properties should be retrieved (True) or if the all - super properties (ancestors) should be retrieved (False). - - Returns: - Iterable of super properties. - """ - yield from [self.mapper.map_(pe) for pe in - self.reasoner.getSuperObjectProperties(self.mapper.map_(p), direct).getFlattened()] - - def sub_data_properties(self, p: OWLDataProperty, direct: bool = False): - """Gets the set of named data properties that are the strict (potentially direct) subproperties of the - specified data property expression with respect to the imports closure of the root ontology. - - Args: - p: The data property whose strict (direct) subproperties are to be retrieved. - direct: Specifies if the direct subproperties should be retrieved (True) or if the all subproperties - (descendants) should be retrieved (False). - - Returns: - If direct is True, each property P where the set of reasoner axioms entails DirectSubDataPropertyOf(P, pe). - If direct is False, each property P where the set of reasoner axioms entails - StrictSubDataPropertyOf(P, pe). If pe is equivalent to owl:bottomDataProperty then nothing will be - returned. - """ - yield from [self.mapper.map_(pe) for pe in - self.reasoner.getSubDataProperties(self.mapper.map_(p), direct).getFlattened()] - - def super_data_properties(self, p: OWLDataProperty, direct: bool = False): - """Gets the stream of data properties that are the strict (potentially direct) super properties of the - specified data property with respect to the imports closure of the root ontology. - - Args: - p (OWLDataProperty): The data property whose super properties are to be retrieved. - direct (bool): Specifies if the direct super properties should be retrieved (True) or if the all - super properties (ancestors) should be retrieved (False). - - Returns: - Iterable of super properties. - """ - yield from [self.mapper.map_(pe) for pe in - self.reasoner.getSuperDataProperties(self.mapper.map_(p), direct).getFlattened()] - - def different_individuals(self, i: OWLNamedIndividual): - """Gets the individuals that are different from the specified individual with respect to the set of - reasoner axioms. - - Args: - i: The individual whose different individuals are to be retrieved. - - Returns: - All individuals x where the set of reasoner axioms entails DifferentIndividuals(ind x). - """ - yield from [self.mapper.map_(ind) for ind in - self.reasoner.getDifferentIndividuals(self.mapper.map_(i)).getFlattened()] - - def same_individuals(self, i: OWLNamedIndividual): - """Gets the individuals that are the same as the specified individual with respect to the set of - reasoner axioms. - - Args: - i: The individual whose same individuals are to be retrieved. - - Returns: - All individuals x where the root ontology imports closure entails SameIndividual(ind x). - """ - yield from [self.mapper.map_(ind) for ind in - to_list(self.reasoner.sameIndividuals(self.mapper.map_(i)))] - - def equivalent_object_properties(self, p: OWLObjectProperty): - """Gets the simplified object properties that are equivalent to the specified object property with respect - to the set of reasoner axioms. - - Args: - p: The object property whose equivalent object properties are to be retrieved. - - Returns: - All simplified object properties e where the root ontology imports closure entails - EquivalentObjectProperties(op e). If op is unsatisfiable with respect to the set of reasoner axioms - then owl:bottomDataProperty will be returned. - """ - yield from [self.mapper.map_(pe) for pe in - to_list(self.reasoner.equivalentObjectProperties(self.mapper.map_(p)))] - - def equivalent_data_properties(self, p: OWLDataProperty): - """Gets the data properties that are equivalent to the specified data property with respect to the set of - reasoner axioms. - - Args: - p: The data property whose equivalent data properties are to be retrieved. - - Returns: - All data properties e where the root ontology imports closure entails EquivalentDataProperties(dp e). - If dp is unsatisfiable with respect to the set of reasoner axioms then owl:bottomDataProperty will - be returned. - """ - yield from [self.mapper.map_(pe) for pe in - to_list(self.reasoner.getEquivalentDataProperties(self.mapper.map_(p)))] - - def object_property_values(self, i: OWLNamedIndividual, p: OWLObjectProperty): - """Gets the object property values for the specified individual and object property expression. - - Args: - i: The individual that is the subject of the object property values. - p: The object property expression whose values are to be retrieved for the specified individual. - - Returns: - The named individuals such that for each individual j, the set of reasoner axioms entails - ObjectPropertyAssertion(pe ind j). - """ - yield from [self.mapper.map_(ind) for ind in - self.reasoner.getObjectPropertyValues(self.mapper.map_(i), self.mapper.map_(p)).getFlattened()] - - def data_property_values(self, e: OWLEntity, p: OWLDataProperty): - """Gets the data property values for the specified entity and data property expression. - - Args: - e: The entity (usually an individual) that is the subject of the data property values. - p: The data property expression whose values are to be retrieved for the specified individual. - - Returns: - A set of OWLLiterals containing literals such that for each literal l in the set, the set of reasoner - axioms entails DataPropertyAssertion(pe ind l). - """ - yield from [self.mapper.map_(literal) for literal in - to_list(self.reasoner.dataPropertyValues(self.mapper.map_(e), self.mapper.map_(p)))] - - def disjoint_object_properties(self, p: OWLObjectProperty): - """Gets the simplified object properties that are disjoint with the specified object property with respect - to the set of reasoner axioms. - - Args: - p: The object property whose disjoint object properties are to be retrieved. - - Returns: - All simplified object properties e where the root ontology imports closure entails - EquivalentObjectProperties(e ObjectPropertyComplementOf(op)) or - StrictSubObjectPropertyOf(e ObjectPropertyComplementOf(op)). - """ - yield from [self.mapper.map_(pe) for pe in - self.reasoner.getDisjointObjectProperties(self.mapper.map_(p)).getFlattened()] - - def disjoint_data_properties(self, p: OWLDataProperty): - """Gets the data properties that are disjoint with the specified data property with respect - to the set of reasoner axioms. - - Args: - p: The data property whose disjoint data properties are to be retrieved. - - Returns: - All data properties e where the root ontology imports closure entails - EquivalentDataProperties(e DataPropertyComplementOf(dp)) or - StrictSubDataPropertyOf(e DataPropertyComplementOf(dp)). - """ - yield from [self.mapper.map_(pe) for pe in - self.reasoner.getDisjointDataProperties(self.mapper.map_(p)).getFlattened()] - - def types(self, i: OWLNamedIndividual, direct: bool = False): - """Gets the named classes which are (potentially direct) types of the specified named individual. - - Args: - i: The individual whose types are to be retrieved. - direct: Specifies if the direct types should be retrieved (True), or if all types should be retrieved - (False). - - Returns: - If direct is True, each named class C where the set of reasoner axioms entails - DirectClassAssertion(C, ind). If direct is False, each named class C where the set of reasoner axioms - entails ClassAssertion(C, ind). - """ - yield from [self.mapper.map_(ind) for ind in - self.reasoner.getTypes(self.mapper.map_(i), direct).getFlattened()] - - def infer_axioms(self, inference_types: list[str]) -> Iterable[OWLAxiom]: - """ - Infer the specified inference type of axioms for the ontology managed by this instance's reasoner and - return them. - - Args: - inference_types: Axiom inference types: Avaliable options (can set more than 1): - ["InferredClassAssertionAxiomGenerator", "InferredSubClassAxiomGenerator", - "InferredDisjointClassesAxiomGenerator", "InferredEquivalentClassAxiomGenerator", - "InferredEquivalentDataPropertiesAxiomGenerator","InferredEquivalentObjectPropertyAxiomGenerator", - "InferredInverseObjectPropertiesAxiomGenerator","InferredSubDataPropertyAxiomGenerator", - "InferredSubObjectPropertyAxiomGenerator","InferredDataPropertyCharacteristicAxiomGenerator", - "InferredObjectPropertyCharacteristicAxiomGenerator" - ] - - Returns: - Iterable of inferred axioms. - """ - from java.util import ArrayList - from org.semanticweb.owlapi.util import InferredOntologyGenerator - - generators = ArrayList() - for i in inference_types: - if java_object := self.inference_types_mapping.get(i, None): - generators.add(java_object) - iog = InferredOntologyGenerator(self.reasoner, generators) - inferred_axioms = list(iog.getAxiomGenerators()) - for ia in inferred_axioms: - for axiom in ia.createAxioms(self.manager.getOWLDataFactory(), self.reasoner): - yield self.mapper.map_(axiom) - - def infer_axioms_and_save(self, output_path: str = None, output_format: str = None, inference_types: list[str] = None): - """ - Generates inferred axioms for the ontology managed by this instance's reasoner and saves them to a file. - This function uses the OWL API to generate inferred class assertion axioms based on the ontology and reasoner - associated with this instance. The inferred axioms are saved to the specified output file in the desired format. - - Args: - output_path : The name of the file where the inferred axioms will be saved. - output_format : The format in which to save the inferred axioms. Supported formats are: - - "ttl" or "turtle" for Turtle format - - "rdf/xml" for RDF/XML format - - "owl/xml" for OWL/XML format - If not specified, the format of the original ontology is used. - inference_types: Axiom inference types: Avaliable options (can set more than 1): - ["InferredClassAssertionAxiomGenerator", "InferredSubClassAxiomGenerator", - "InferredDisjointClassesAxiomGenerator", "InferredEquivalentClassAxiomGenerator", - "InferredEquivalentDataPropertiesAxiomGenerator","InferredEquivalentObjectPropertyAxiomGenerator", - "InferredInverseObjectPropertiesAxiomGenerator","InferredSubDataPropertyAxiomGenerator", - "InferredSubObjectPropertyAxiomGenerator","InferredDataPropertyCharacteristicAxiomGenerator", - "InferredObjectPropertyCharacteristicAxiomGenerator" - ] - - Returns: - None (the file is saved to the specified directory) - """ - from java.io import File, FileOutputStream - from java.util import ArrayList - from org.semanticweb.owlapi.util import InferredOntologyGenerator - from org.semanticweb.owlapi.formats import TurtleDocumentFormat, RDFXMLDocumentFormat, OWLXMLDocumentFormat - if output_format == "ttl" or output_format == "turtle": - document_format = TurtleDocumentFormat() - elif output_format == "rdf/xml": - document_format = RDFXMLDocumentFormat() - elif output_format == "owl/xml": - document_format = OWLXMLDocumentFormat() - else: - document_format = self.manager.getOntologyFormat(self.ontology) - generators = ArrayList() - - for i in inference_types: - if java_object := self.inference_types_mapping.get(i, None): - generators.add(java_object) - iog = InferredOntologyGenerator(self.reasoner, generators) - inferred_axioms_ontology = self.manager.createOntology() - iog.fillOntology(self.manager.getOWLDataFactory(), inferred_axioms_ontology) - inferred_ontology_file = File(output_path).getAbsoluteFile() - output_stream = FileOutputStream(inferred_ontology_file) - self.manager.saveOntology(inferred_axioms_ontology, document_format, output_stream) - - def generate_and_save_inferred_class_assertion_axioms(self, output="temp.ttl", output_format: str = None): - """ - Generates inferred class assertion axioms for the ontology managed by this instance's reasoner and saves them to a file. - This function uses the OWL API to generate inferred class assertion axioms based on the ontology and reasoner - associated with this instance. The inferred axioms are saved to the specified output file in the desired format. - Parameters: - ----------- - output : str, optional - The name of the file where the inferred axioms will be saved. Default is "temp.ttl". - output_format : str, optional - The format in which to save the inferred axioms. Supported formats are: - - "ttl" or "turtle" for Turtle format - - "rdf/xml" for RDF/XML format - - "owl/xml" for OWL/XML format - If not specified, the format of the original ontology is used. - Notes: - ------ - - The function supports saving in multiple formats: Turtle, RDF/XML, and OWL/XML. - - The inferred axioms are generated using the reasoner associated with this instance and the OWL API's - InferredClassAssertionAxiomGenerator. - - The inferred axioms are added to a new ontology which is then saved in the specified format. - Example: - -------- - >>> instance.generate_inferred_class_assertion_axioms(output="inferred_axioms.ttl", format="ttl") - This will save the inferred class assertion axioms to the file "inferred_axioms.ttl" in Turtle format. - """ - self.infer_axioms_and_save(output, output_format, ["InferredClassAssertionAxiomGenerator"]) diff --git a/owlapy/owlapi_mapper.py b/owlapy/owlapi_mapper.py index 9e2ec046..efbd9ac8 100644 --- a/owlapy/owlapi_mapper.py +++ b/owlapy/owlapi_mapper.py @@ -1,9 +1,7 @@ from functools import singledispatchmethod -from typing import Iterable - +from typing import Iterable, TypeVar import jpype.imports -import owlapy.owl_ontology from owlapy import owl_expression_to_manchester, manchester_to_owl_expression from owlapy.class_expression import OWLClassExpression, OWLDataOneOf, OWLFacetRestriction, OWLDatatypeRestriction from owlapy.iri import IRI @@ -23,62 +21,62 @@ from owlapy.owl_datatype import OWLDatatype from owlapy.owl_individual import OWLNamedIndividual from owlapy.owl_literal import OWLLiteral +from owlapy.owl_ontology import OWLOntologyID from owlapy.owl_property import OWLObjectProperty, OWLDataProperty +from owlapy.static_funcs import startJVM from owlapy.vocab import OWLFacet -if jpype.isJVMStarted(): - from org.semanticweb.owlapi.model import IRI as owlapi_IRI - from org.semanticweb.owlapi.vocab import OWLFacet as owlapi_OWLFacet - from org.semanticweb.owlapi.manchestersyntax.parser import ManchesterOWLSyntaxClassExpressionParser - from org.semanticweb.owlapi.manchestersyntax.renderer import ManchesterOWLSyntaxOWLObjectRendererImpl - from org.semanticweb.owlapi.util import BidirectionalShortFormProviderAdapter, SimpleShortFormProvider - from org.semanticweb.owlapi.expression import ShortFormEntityChecker - from java.util import HashSet, ArrayList, List, Set - from java.util.stream import Stream - from uk.ac.manchester.cs.owl.owlapi import (OWLAnonymousClassExpressionImpl, OWLCardinalityRestrictionImpl, - OWLClassExpressionImpl, OWLClassImpl, OWLDataAllValuesFromImpl, - OWLDataCardinalityRestrictionImpl, OWLDataExactCardinalityImpl, - OWLDataHasValueImpl, OWLDataMaxCardinalityImpl, OWLDataUnionOfImpl, - OWLDataMinCardinalityImpl, OWLDataSomeValuesFromImpl, - OWLNaryBooleanClassExpressionImpl, OWLObjectAllValuesFromImpl, - OWLObjectCardinalityRestrictionImpl, OWLObjectComplementOfImpl, - OWLObjectExactCardinalityImpl, OWLObjectHasSelfImpl, - OWLObjectHasValueImpl, OWLObjectIntersectionOfImpl, - OWLObjectMaxCardinalityImpl, OWLObjectMinCardinalityImpl, - OWLObjectOneOfImpl, OWLObjectSomeValuesFromImpl, OWLNaryDataRangeImpl, - OWLObjectUnionOfImpl, OWLQuantifiedDataRestrictionImpl, - OWLQuantifiedObjectRestrictionImpl, OWLQuantifiedRestrictionImpl, - OWLValueRestrictionImpl, OWLLiteralImplBoolean, OWLLiteralImplString, - OWLLiteralImplDouble, OWLLiteralImplFloat, OWLLiteralImplInteger, - OWLDisjointClassesAxiomImpl, OWLDeclarationAxiomImpl, OWLAnnotationImpl, - OWLAnnotationPropertyImpl, OWLClassAssertionAxiomImpl, - OWLDataPropertyAssertionAxiomImpl, OWLDataPropertyDomainAxiomImpl, - OWLDataPropertyRangeAxiomImpl, OWLEquivalentClassesAxiomImpl, - OWLEquivalentDataPropertiesAxiomImpl, OWLDataIntersectionOfImpl, - OWLEquivalentObjectPropertiesAxiomImpl, OWLDataOneOfImpl, - OWLObjectPropertyDomainAxiomImpl, OWLObjectPropertyRangeAxiomImpl, - OWLObjectPropertyAssertionAxiomImpl, OWLDisjointDataPropertiesAxiomImpl, - OWLDisjointObjectPropertiesAxiomImpl, OWLHasKeyAxiomImpl, - OWLSubClassOfAxiomImpl, OWLSubDataPropertyOfAxiomImpl, - OWLSubObjectPropertyOfAxiomImpl, OWLAsymmetricObjectPropertyAxiomImpl, - OWLDatatypeDefinitionAxiomImpl, OWLDatatypeImpl, OWLObjectPropertyImpl, - OWLDataPropertyImpl, OWLNamedIndividualImpl, OWLDisjointUnionAxiomImpl, - OWLDifferentIndividualsAxiomImpl, OWLFunctionalDataPropertyAxiomImpl, - OWLFunctionalObjectPropertyAxiomImpl, OWLSameIndividualAxiomImpl, - OWLInverseFunctionalObjectPropertyAxiomImpl, OWLDataComplementOfImpl, - OWLInverseObjectPropertiesAxiomImpl,OWLReflexiveObjectPropertyAxiomImpl, - OWLIrreflexiveObjectPropertyAxiomImpl, OWLAnnotationAssertionAxiomImpl, - OWLNegativeDataPropertyAssertionAxiomImpl, OWLFacetRestrictionImpl, - OWLNegativeObjectPropertyAssertionAxiomImpl, OWLDatatypeRestrictionImpl, - OWLSymmetricObjectPropertyAxiomImpl, - OWLTransitiveObjectPropertyAxiomImpl, - OWLAnnotationPropertyDomainAxiomImpl, - OWLAnnotationPropertyRangeAxiomImpl, - OWLSubAnnotationPropertyOfAxiomImpl - ) - -else: - raise ImportError("Jpype JVM is not started! Tip: Import OWLAPIMapper after JVM has started") +if not jpype.isJVMStarted(): + startJVM() +from org.semanticweb.owlapi.model import IRI as owlapi_IRI, OWLOntologyID as owlapi_OWLOntologyID +from org.semanticweb.owlapi.vocab import OWLFacet as owlapi_OWLFacet +from org.semanticweb.owlapi.manchestersyntax.parser import ManchesterOWLSyntaxClassExpressionParser +from org.semanticweb.owlapi.manchestersyntax.renderer import ManchesterOWLSyntaxOWLObjectRendererImpl +from org.semanticweb.owlapi.util import BidirectionalShortFormProviderAdapter, SimpleShortFormProvider +from org.semanticweb.owlapi.expression import ShortFormEntityChecker +from java.util import HashSet, ArrayList, List, Set, LinkedHashSet, Optional +from java.util.stream import Stream +from uk.ac.manchester.cs.owl.owlapi import (OWLAnonymousClassExpressionImpl, OWLCardinalityRestrictionImpl, + OWLClassExpressionImpl, OWLClassImpl, OWLDataAllValuesFromImpl, + OWLDataCardinalityRestrictionImpl, OWLDataExactCardinalityImpl, + OWLDataHasValueImpl, OWLDataMaxCardinalityImpl, OWLDataUnionOfImpl, + OWLDataMinCardinalityImpl, OWLDataSomeValuesFromImpl, + OWLNaryBooleanClassExpressionImpl, OWLObjectAllValuesFromImpl, + OWLObjectCardinalityRestrictionImpl, OWLObjectComplementOfImpl, + OWLObjectExactCardinalityImpl, OWLObjectHasSelfImpl, + OWLObjectHasValueImpl, OWLObjectIntersectionOfImpl, + OWLObjectMaxCardinalityImpl, OWLObjectMinCardinalityImpl, + OWLObjectOneOfImpl, OWLObjectSomeValuesFromImpl, OWLNaryDataRangeImpl, + OWLObjectUnionOfImpl, OWLQuantifiedDataRestrictionImpl, + OWLQuantifiedObjectRestrictionImpl, OWLQuantifiedRestrictionImpl, + OWLValueRestrictionImpl, OWLLiteralImplBoolean, OWLLiteralImplString, + OWLLiteralImplDouble, OWLLiteralImplFloat, OWLLiteralImplInteger, + OWLDisjointClassesAxiomImpl, OWLDeclarationAxiomImpl, OWLAnnotationImpl, + OWLAnnotationPropertyImpl, OWLClassAssertionAxiomImpl, + OWLDataPropertyAssertionAxiomImpl, OWLDataPropertyDomainAxiomImpl, + OWLDataPropertyRangeAxiomImpl, OWLEquivalentClassesAxiomImpl, + OWLEquivalentDataPropertiesAxiomImpl, OWLDataIntersectionOfImpl, + OWLEquivalentObjectPropertiesAxiomImpl, OWLDataOneOfImpl, + OWLObjectPropertyDomainAxiomImpl, OWLObjectPropertyRangeAxiomImpl, + OWLObjectPropertyAssertionAxiomImpl, OWLDisjointDataPropertiesAxiomImpl, + OWLDisjointObjectPropertiesAxiomImpl, OWLHasKeyAxiomImpl, + OWLSubClassOfAxiomImpl, OWLSubDataPropertyOfAxiomImpl, + OWLSubObjectPropertyOfAxiomImpl, OWLAsymmetricObjectPropertyAxiomImpl, + OWLDatatypeDefinitionAxiomImpl, OWLDatatypeImpl, OWLObjectPropertyImpl, + OWLDataPropertyImpl, OWLNamedIndividualImpl, OWLDisjointUnionAxiomImpl, + OWLDifferentIndividualsAxiomImpl, OWLFunctionalDataPropertyAxiomImpl, + OWLFunctionalObjectPropertyAxiomImpl, OWLSameIndividualAxiomImpl, + OWLInverseFunctionalObjectPropertyAxiomImpl, OWLDataComplementOfImpl, + OWLInverseObjectPropertiesAxiomImpl,OWLReflexiveObjectPropertyAxiomImpl, + OWLIrreflexiveObjectPropertyAxiomImpl, OWLAnnotationAssertionAxiomImpl, + OWLNegativeDataPropertyAssertionAxiomImpl, OWLFacetRestrictionImpl, + OWLNegativeObjectPropertyAssertionAxiomImpl, OWLDatatypeRestrictionImpl, + OWLSymmetricObjectPropertyAxiomImpl, + OWLTransitiveObjectPropertyAxiomImpl, + OWLAnnotationPropertyDomainAxiomImpl, + OWLAnnotationPropertyRangeAxiomImpl, + OWLSubAnnotationPropertyOfAxiomImpl + ) def init(the_class): @@ -89,16 +87,16 @@ def init(the_class): return globals().get(cls_name + "Impl") +_SO = TypeVar('_SO', bound='SyncOntology') # noqa: F821 + + class OWLAPIMapper: - def __init__(self, ontology=None): - # TODO: CD: Please use class type of ontology - # TODO: CD: if ontology is None, then we should throw an exception with a useful information - # assert isinstance(ontology, OWLAPIMapper) - self.ontology = ontology - self.manager = ontology.getOWLOntologyManager() + def __init__(self, ontology: _SO): + self.manager = ontology.manager.get_owlapi_manager() + self.ontology = ontology.get_owlapi_ontology() - # () Get the name space. + # () Get the name space. (used for rendering class expressions) self.namespace = self.ontology.getOntologyID().getOntologyIRI().orElse(None) if self.namespace is not None: self.namespace = str(self.namespace) @@ -107,7 +105,7 @@ def __init__(self, ontology=None): else: self.namespace = "http://www.anonymous.org/anonymous#" - # () Create a manchester parser and a renderer. + # () Create a manchester parser and a renderer using the given ontology. ontology_set = HashSet() ontology_set.add(self.ontology) bidi_provider = BidirectionalShortFormProviderAdapter(self.manager, ontology_set, SimpleShortFormProvider()) @@ -466,8 +464,32 @@ def _(self, e): return OWLInverseObjectPropertiesAxiom(self.map_(e.getFirstProperty()), self.map_(e.getSecondProperty()), self.map_(e.annotationsAsList())) + @map_.register(OWLOntologyID) + def _(self, e): + if e.get_ontology_iri(): + i1 = self.map_(e.get_ontology_iri()) + else: + i1 = None + if e.get_version_iri(): + i2 = self.map_(e.get_version_iri()) + else: + i2 = None + return owlapi_OWLOntologyID(i1, i2) + + @map_.register(owlapi_OWLOntologyID) + def _(self, e): + return OWLOntologyID(self.map_(e.getOntologyIRI()), self.map_(e.getVersionIRI())) + + @map_.register(Optional) + def _(self, e): + if bool(e.isPresent()): + return self.map_(e.get()) + else: + return None + @map_.register(List) @map_.register(Set) + @map_.register(LinkedHashSet) def _(self, e): python_list = list() casted_list = list(e) @@ -484,3 +506,8 @@ def _(self, e): for item in e: java_list.add(self.map_(item)) return java_list + + @staticmethod + def to_list(stream_obj): + """Converts Java Stream object to Python list""" + return stream_obj.collect(jpype.JClass("java.util.stream.Collectors").toList()) diff --git a/owlapy/providers.py b/owlapy/providers.py index d0d99c78..d9390089 100644 --- a/owlapy/providers.py +++ b/owlapy/providers.py @@ -39,7 +39,7 @@ def owl_datatype_min_max_exclusive_restriction(min_: Restriction_Literals, max_ = float(max_) if isinstance(max_, float) and isinstance(min_, int): min_ = float(min_) - assert type(min_) == type(max_) + assert type(min_) is type(max_) r_min = OWLFacetRestriction(OWLFacet.MIN_EXCLUSIVE, min_) r_max = OWLFacetRestriction(OWLFacet.MAX_EXCLUSIVE, max_) @@ -54,7 +54,7 @@ def owl_datatype_min_max_inclusive_restriction(min_: Restriction_Literals, max_ = float(max_) if isinstance(max_, float) and isinstance(min_, int): min_ = float(min_) - assert type(min_) == type(max_) + assert type(min_) is type(max_) r_min = OWLFacetRestriction(OWLFacet.MIN_INCLUSIVE, min_) r_max = OWLFacetRestriction(OWLFacet.MAX_INCLUSIVE, max_) diff --git a/owlapy/render.py b/owlapy/render.py index a8bb5a8e..0e30bb6d 100644 --- a/owlapy/render.py +++ b/owlapy/render.py @@ -7,10 +7,10 @@ from owlapy import namespaces from .iri import IRI -from .owl_individual import OWLNamedIndividual, OWLIndividual +from .owl_individual import OWLNamedIndividual from .owl_literal import OWLLiteral from .owl_object import OWLObjectRenderer, OWLEntity, OWLObject -from .owl_property import OWLObjectInverseOf, OWLPropertyExpression, OWLDataProperty, OWLObjectProperty +from .owl_property import OWLObjectInverseOf, OWLPropertyExpression, OWLDataProperty from .class_expression import OWLClassExpression, OWLBooleanClassExpression, OWLClass, OWLObjectSomeValuesFrom, \ OWLObjectAllValuesFrom, OWLObjectUnionOf, OWLObjectIntersectionOf, OWLObjectComplementOf, OWLObjectMinCardinality, \ OWLObjectExactCardinality, OWLObjectMaxCardinality, OWLObjectHasSelf, OWLDataSomeValuesFrom, OWLDataAllValuesFrom, \ @@ -20,8 +20,7 @@ from .owl_data_ranges import OWLNaryDataRange, OWLDataComplementOf, OWLDataUnionOf, OWLDataIntersectionOf from .class_expression import OWLObjectHasValue, OWLFacetRestriction, OWLDatatypeRestriction, OWLObjectOneOf from .owl_datatype import OWLDatatype -from .owl_reasoner import OWLReasoner -from typing import Union, Tuple +from .abstracts.abstract_owl_reasoner import OWLReasoner import requests import warnings import abc diff --git a/owlapy/static_funcs.py b/owlapy/static_funcs.py index 4fd0a764..228aa050 100644 --- a/owlapy/static_funcs.py +++ b/owlapy/static_funcs.py @@ -3,6 +3,9 @@ import subprocess import platform import shutil +import jpype +import jpype.imports +import pkg_resources # NOTE: Static functions closely related with owl classes should be placed in utils.py not here @@ -37,3 +40,20 @@ def download_external_files(ftp_link: str): subprocess.run(['unzip', file_name]) os.remove(os.path.join(os.getcwd(), file_name)) shutil.move(current_dir, root_dir) + + +def startJVM(): + """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() -> None: + """Detaches the thread from Java packages and shuts down the java virtual machine hosted by jpype.""" + if jpype.isJVMStarted(): + jpype.detachThreadFromJVM() + jpype.shutdownJVM() diff --git a/owlapy/utils.py b/owlapy/utils.py index 945da589..6a81a8fc 100644 --- a/owlapy/utils.py +++ b/owlapy/utils.py @@ -14,12 +14,66 @@ OWLDataAllValuesFrom, OWLDataSomeValuesFrom, OWLObjectAllValuesFrom, \ OWLDataOneOf, OWLObjectIntersectionOf, \ OWLDataCardinalityRestriction, OWLNaryBooleanClassExpression, OWLObjectUnionOf, \ - OWLObjectHasValue, OWLDatatypeRestriction, OWLFacetRestriction, OWLObjectOneOf + OWLObjectHasValue, OWLDatatypeRestriction, OWLFacetRestriction, OWLObjectOneOf, OWLQuantifiedObjectRestriction from .owl_data_ranges import OWLDataComplementOf, OWLDataUnionOf, OWLDataIntersectionOf, OWLNaryDataRange, OWLDataRange, \ OWLPropertyRange from .owl_object import OWLObject from .owl_datatype import OWLDatatype +def concept_reducer(concepts:Iterable, opt:Callable): + """ + Reduces a set of concepts by applying a binary operation to each pair of concepts. + + Args: + concepts (set): A set of concepts to be reduced. + opt (function): A binary function that takes a pair of concepts and returns a single concept. + + Returns: + set: A set containing the results of applying the binary operation to each pair of concepts. + + Example: + >>> concepts = {1, 2, 3} + >>> opt = lambda x: x[0] + x[1] + >>> concept_reducer(concepts, opt) + {2, 3, 4, 5, 6} + + Note: + The operation `opt` should be commutative and associative to ensure meaningful reduction in the context of set operations. + """ + result = set() + for i in concepts: + for j in concepts: + result.add(opt((i, j))) + return result + +def concept_reducer_properties( + concepts: Iterable, properties, cls: Callable = None, cardinality: int = 2 +) -> Iterable[Union[OWLQuantifiedObjectRestriction, OWLObjectCardinalityRestriction]]: + """ + Map a set of owl concepts and a set of properties into OWL Restrictions + + Args: + concepts: + properties: + cls (Callable): An owl Restriction class + cardinality: A positive Integer + + Returns: List of OWL Restrictions + + """ + assert isinstance(concepts, Iterable), "Concepts must be an Iterable" + assert isinstance(properties, Iterable), "properties must be an Iterable" + assert isinstance(cls, Callable), "cls must be an Callable" + assert cardinality > 0 + result = set() + for i in concepts: + for j in properties: + if cls == OWLObjectMinCardinality or cls == OWLObjectMaxCardinality: + result.add(cls(cardinality=cardinality, property=j, filler=i)) + continue + result.add(cls(j, i)) + return result + class OWLClassExpressionLengthMetric: """Length calculation of OWLClassExpression diff --git a/setup.py b/setup.py index 80b77624..b04bd1b3 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name="owlapy", description="OWLAPY is a Python Framework for creating and manipulating OWL Ontologies.", - version="1.2.1", + version="1.3.0", packages=find_packages(), include_package_data=True, package_data={ diff --git a/tests/test_owlapi_adaptor.py b/tests/test_owlapi_adaptor.py index 5f6c1975..e69de29b 100644 --- a/tests/test_owlapi_adaptor.py +++ b/tests/test_owlapi_adaptor.py @@ -1,124 +0,0 @@ -import os -import unittest - -from jpype import JDouble - -from owlapy.class_expression import OWLClass, OWLDataSomeValuesFrom, OWLObjectIntersectionOf -from owlapy.iri import IRI -from owlapy.owl_axiom import OWLDisjointClassesAxiom, OWLDeclarationAxiom, OWLClassAssertionAxiom -from owlapy.owl_individual import OWLNamedIndividual -from owlapy.owl_ontology_manager import OntologyManager -from owlapy.owl_property import OWLDataProperty -from owlapy.owlapi_adaptor import OWLAPIAdaptor -from owlapy.providers import owl_datatype_min_inclusive_restriction - -from owlapy.class_expression import OWLClassExpression -from owlapy.owl_ontology_manager import OntologyManager -from owlapy.owlapi_adaptor import OWLAPIAdaptor - - -class TestOwlapiAdaptor(unittest.TestCase): - ns = "http://dl-learner.org/mutagenesis#" - ontology_path = "KGs/Mutagenesis/mutagenesis.owl" - nitrogen38 = OWLClass(IRI.create(ns, "Nitrogen-38")) - charge = OWLDataProperty(IRI.create(ns, "charge")) - has_charge_more_than_0_85 = OWLDataSomeValuesFrom(charge, owl_datatype_min_inclusive_restriction(0.85)) - ce = OWLObjectIntersectionOf([nitrogen38, has_charge_more_than_0_85]) - adaptor = OWLAPIAdaptor(ontology_path) - - def test_named_concepts(self): - - ontology_path = "KGs/Family/family-benchmark_rich_background.owl" - - # Available OWL Reasoners: 'HermiT', 'Pellet', 'JFact', 'Openllet' - owl_reasoners = dict() - owl_reasoners["HermiT"] = OWLAPIAdaptor(path=ontology_path, name_reasoner="HermiT") - owl_reasoners["Pellet"] = OWLAPIAdaptor(path=ontology_path, name_reasoner="Pellet") - owl_reasoners["JFact"] = OWLAPIAdaptor(path=ontology_path, name_reasoner="JFact") - owl_reasoners["Openllet"] = OWLAPIAdaptor(path=ontology_path, name_reasoner="Openllet") - - onto = OntologyManager().load_ontology(ontology_path) - - def compute_agreements(i: OWLClassExpression, verbose=False): - if verbose: - print(f"Computing agreements between Reasoners on {i}...") - retrieval_result = None - flag = False - for k, reasoner in owl_reasoners.items(): - if retrieval_result: - flag = retrieval_result == {_.str for _ in reasoner.instances(i)} - else: - retrieval_result = {_.str for _ in reasoner.instances(i)} - return flag - # Agreement between instances over - for i in onto.classes_in_signature(): - assert compute_agreements(i, True) - for k, reasoner in owl_reasoners.items(): - reasoner.stopJVM() - - def test_consistency_check(self): - self.assertEqual(self.adaptor.has_consistent_ontology(), True) - - def test_inconsistency_check(self): - manager = OntologyManager() - onto = manager.load_ontology(IRI.create(self.ontology_path)) - - carbon230 = OWLClass(IRI.create(self.ns, "Carbon-230")) - axiom = OWLDisjointClassesAxiom([self.nitrogen38, carbon230]) - manager.add_axiom(onto, axiom) - new_individual = OWLNamedIndividual(IRI.create(self.ns, "testIndividual")) - manager.add_axiom(onto, OWLDeclarationAxiom(new_individual)) - manager.add_axiom(onto, OWLClassAssertionAxiom(new_individual, self.nitrogen38)) - manager.add_axiom(onto, OWLClassAssertionAxiom(new_individual, carbon230)) - - manager.save_ontology(onto, IRI.create("file:/test.owl")) - adaptor1 = OWLAPIAdaptor("test.owl") - self.assertEqual(adaptor1.has_consistent_ontology(), False) - os.remove("test.owl") - - def test_instances_retrieval(self): - instances = self.adaptor.instances(self.ce) - expected = [OWLNamedIndividual(IRI('http://dl-learner.org/mutagenesis#', 'd141_10')), - OWLNamedIndividual(IRI('http://dl-learner.org/mutagenesis#', 'd195_12')), - OWLNamedIndividual(IRI('http://dl-learner.org/mutagenesis#', 'd144_10')), - OWLNamedIndividual(IRI('http://dl-learner.org/mutagenesis#', 'd147_11')), - OWLNamedIndividual(IRI('http://dl-learner.org/mutagenesis#', 'e18_9')), - OWLNamedIndividual(IRI('http://dl-learner.org/mutagenesis#', 'd175_17')), - OWLNamedIndividual(IRI('http://dl-learner.org/mutagenesis#', 'e16_9'))] - # Assert equal without considering the order - for instance in instances: - self.assertIn(instance, expected) - self.assertEqual(len(list(instances)), len(expected)) - - def test_conversion(self): - # construct the class expression in owlapi - from org.semanticweb.owlapi.model import IRI as IRIowlapi, OWLClass, OWLObjectProperty - from org.semanticweb.owlapi.vocab import OWLFacet - - nitrogenIRI = IRIowlapi.create(self.ns + "Nitrogen-38") - charge_iri = IRIowlapi.create(self.ns + "charge") - - data_factory = self.adaptor.manager.getOWLDataFactory() - nitrogen_class = data_factory.getOWLClass(nitrogenIRI) - - charge_property = data_factory.getOWLDataProperty(charge_iri) - double_datatype = data_factory.getDoubleOWLDatatype() - facet_restriction = data_factory.getOWLFacetRestriction(OWLFacet.MIN_INCLUSIVE, JDouble(0.85)) - datatype_restriction = data_factory.getOWLDatatypeRestriction(double_datatype, facet_restriction) - some_values_from = data_factory.getOWLDataSomeValuesFrom(charge_property, datatype_restriction) - - class_expression = data_factory.getOWLObjectIntersectionOf(nitrogen_class, some_values_from) - - # compare them with the adaptor converted expression - ce_converted = self.adaptor.mapper.map_(self.ce) - print(ce_converted) - print(class_expression) - self.assertEqual(class_expression, ce_converted) - - # convert back to owlapy and check for equality - ce_1 = self.adaptor.mapper.map_(class_expression) - ce_2 = self.adaptor.mapper.map_(ce_converted) - - self.assertEqual(ce_1, ce_2) - self.assertEqual(ce_1, self.ce) - self.assertEqual(ce_2, self.ce) diff --git a/tests/test_owlapy_fastinstancechecker.py b/tests/test_owlapy_fastinstancechecker.py index 48fd6647..ea0e061b 100644 --- a/tests/test_owlapy_fastinstancechecker.py +++ b/tests/test_owlapy_fastinstancechecker.py @@ -9,17 +9,15 @@ from owlapy.owl_axiom import OWLSubDataPropertyOfAxiom, OWLInverseObjectPropertiesAxiom, OWLSubObjectPropertyOfAxiom from owlapy.owl_data_ranges import OWLDataComplementOf, OWLDataIntersectionOf, OWLDataUnionOf from owlapy.owl_individual import OWLNamedIndividual -from owlapy.owl_literal import DoubleOWLDatatype, OWLLiteral, DurationOWLDatatype +from owlapy.owl_literal import DoubleOWLDatatype, OWLLiteral from owlapy.owl_ontology_manager import OntologyManager from owlapy.owl_property import OWLObjectInverseOf, OWLObjectProperty, OWLDataProperty from owlready2.prop import DataProperty -from pandas import Timedelta from owlapy.owl_reasoner import FastInstanceCheckerReasoner, OntologyReasoner -from owlapy.providers import owl_datatype_min_exclusive_restriction, \ - owl_datatype_min_max_inclusive_restriction, owl_datatype_min_max_exclusive_restriction, \ - owl_datatype_max_exclusive_restriction, owl_datatype_max_inclusive_restriction +from owlapy.providers import owl_datatype_min_max_inclusive_restriction, owl_datatype_min_max_exclusive_restriction, \ + owl_datatype_max_inclusive_restriction class Owlapy_FastInstanceChecker_Test(unittest.TestCase): @@ -241,78 +239,62 @@ def test_data_properties(self): OWLNamedIndividual(IRI(NS, 'd180'))}) self.assertEqual(inst, target_inst) - # def test_data_properties_time(self): - # NS = "http://example.com/father#" - # mgr = OWLOntologyManager_Owlready2() - # onto = mgr.load_ontology(IRI.create("file://KGs/Family/father.owl")) - # - # with onto._onto: - # class birthDate(DataProperty): - # range = [date] - # - # class birthDateTime(DataProperty): - # range = [datetime] - # - # class age(DataProperty): - # range = [Timedelta] - # - # onto._onto.markus.birthDate = [date(year=1990, month=10, day=2)] - # onto._onto.markus.birthDateTime = [datetime(year=1990, month=10, day=2, hour=10, minute=20, second=5)] - # onto._onto.markus.age = [Timedelta(days=11315, hours=10, minutes=2)] - # - # onto._onto.anna.birthDate = [date(year=1995, month=6, day=10)] - # onto._onto.anna.birthDateTime = [datetime(year=1995, month=6, day=10, hour=2, minute=10)] - # onto._onto.anna.age = [Timedelta(days=9490, hours=4)] - # - # onto._onto.heinz.birthDate = [date(year=1986, month=6, day=10)] - # onto._onto.heinz.birthDateTime = [datetime(year=1986, month=6, day=10, hour=10, second=10)] - # onto._onto.heinz.age = [Timedelta(days=12775, hours=14, seconds=40)] - # - # onto._onto.michelle.birthDate = [date(year=2000, month=1, day=4)] - # onto._onto.michelle.birthDateTime = [datetime(year=2000, month=1, day=4, minute=4, second=10)] - # onto._onto.michelle.age = [Timedelta(days=7665, hours=1, milliseconds=11)] - # - # onto._onto.martin.birthDate = [date(year=1999, month=3, day=1)] - # onto._onto.martin.birthDateTime = [datetime(year=1999, month=3, day=2, hour=20, minute=2, second=30)] - # onto._onto.martin.age = [Timedelta(days=8030, minutes=2)] - # - # birth_date = OWLDataProperty(IRI(NS, 'birthDate')) - # birth_date_time = OWLDataProperty(IRI(NS, 'birthDateTime')) - # age_ = OWLDataProperty(IRI(NS, 'age')) - # markus = OWLNamedIndividual(IRI(NS, 'markus')) - # anna = OWLNamedIndividual(IRI(NS, 'anna')) - # heinz = OWLNamedIndividual(IRI(NS, 'heinz')) - # michelle = OWLNamedIndividual(IRI(NS, 'michelle')) - # martin = OWLNamedIndividual(IRI(NS, 'martin')) - # - # base_reasoner = OWLReasoner_Owlready2(onto) - # reasoner = OWLReasoner_FastInstanceChecker(onto, base_reasoner=base_reasoner) - # - # restriction = owl_datatype_min_max_exclusive_restriction(date(year=1995, month=6, day=12), - # date(year=1999, month=3, day=2)) - # inst = frozenset(reasoner.instances(OWLDataSomeValuesFrom(property=birth_date, - # filler=restriction))) - # target_inst = frozenset({martin}) - # self.assertEqual(inst, target_inst) - # - # inst = frozenset(reasoner.instances(OWLDataSomeValuesFrom(property=birth_date, - # filler=OWLDataComplementOf(restriction)))) - # target_inst = frozenset({michelle, anna, heinz, markus}) - # self.assertEqual(inst, target_inst) - # - # restriction = owl_datatype_max_inclusive_restriction(datetime(year=1990, month=10, day=2, hour=10, - # minute=20, second=5)) - # inst = frozenset(reasoner.instances(OWLDataSomeValuesFrom(property=birth_date_time, - # filler=restriction))) - # target_inst = frozenset({markus, heinz}) - # self.assertEqual(inst, target_inst) - # - # restriction_min = owl_datatype_min_exclusive_restriction(Timedelta(days=8030, minutes=1)) - # restriction_max = owl_datatype_max_exclusive_restriction(Timedelta(days=9490, hours=4, nanoseconds=1)) - # filler = OWLDataIntersectionOf([restriction_min, restriction_max, DurationOWLDatatype]) - # inst = frozenset(reasoner.instances(OWLDataSomeValuesFrom(property=age_, filler=filler))) - # target_inst = frozenset({anna, martin}) - # self.assertEqual(inst, target_inst) + def test_data_properties_time(self): + NS = "http://example.com/father#" + mgr = OntologyManager() + onto = mgr.load_ontology(IRI.create("file://KGs/Family/father.owl")) + + with onto._onto: + class birthDate(DataProperty): + range = [date] + + class birthDateTime(DataProperty): + range = [datetime] + + onto._onto.markus.birthDate = [date(year=1990, month=10, day=2)] + onto._onto.markus.birthDateTime = [datetime(year=1990, month=10, day=2, hour=10, minute=20, second=5)] + + onto._onto.anna.birthDate = [date(year=1995, month=6, day=10)] + onto._onto.anna.birthDateTime = [datetime(year=1995, month=6, day=10, hour=2, minute=10)] + + onto._onto.heinz.birthDate = [date(year=1986, month=6, day=10)] + onto._onto.heinz.birthDateTime = [datetime(year=1986, month=6, day=10, hour=10, second=10)] + + onto._onto.michelle.birthDate = [date(year=2000, month=1, day=4)] + onto._onto.michelle.birthDateTime = [datetime(year=2000, month=1, day=4, minute=4, second=10)] + + onto._onto.martin.birthDate = [date(year=1999, month=3, day=1)] + onto._onto.martin.birthDateTime = [datetime(year=1999, month=3, day=2, hour=20, minute=2, second=30)] + + birth_date = OWLDataProperty(IRI(NS, 'birthDate')) + birth_date_time = OWLDataProperty(IRI(NS, 'birthDateTime')) + markus = OWLNamedIndividual(IRI(NS, 'markus')) + anna = OWLNamedIndividual(IRI(NS, 'anna')) + heinz = OWLNamedIndividual(IRI(NS, 'heinz')) + michelle = OWLNamedIndividual(IRI(NS, 'michelle')) + martin = OWLNamedIndividual(IRI(NS, 'martin')) + + base_reasoner = OntologyReasoner(onto) + reasoner = FastInstanceCheckerReasoner(onto, base_reasoner=base_reasoner) + + restriction = owl_datatype_min_max_exclusive_restriction(date(year=1995, month=6, day=12), + date(year=1999, month=3, day=2)) + inst = frozenset(reasoner.instances(OWLDataSomeValuesFrom(property=birth_date, + filler=restriction))) + target_inst = frozenset({martin}) + self.assertEqual(inst, target_inst) + + inst = frozenset(reasoner.instances(OWLDataSomeValuesFrom(property=birth_date, + filler=OWLDataComplementOf(restriction)))) + target_inst = frozenset({michelle, anna, heinz, markus}) + self.assertEqual(inst, target_inst) + + restriction = owl_datatype_max_inclusive_restriction(datetime(year=1990, month=10, day=2, hour=10, + minute=20, second=5)) + inst = frozenset(reasoner.instances(OWLDataSomeValuesFrom(property=birth_date_time, + filler=restriction))) + target_inst = frozenset({markus, heinz}) + self.assertEqual(inst, target_inst) def test_sub_property_inclusion(self): ns = "http://dl-learner.org/mutagenesis#" @@ -326,8 +308,8 @@ def test_sub_property_inclusion(self): super_has_structure = OWLObjectProperty(IRI(ns, 'superHasStucture')) charge = OWLDataProperty(IRI(ns, 'charge')) super_charge = OWLDataProperty(IRI.create(ns, 'super_charge')) - mgr.add_axiom(onto, OWLSubObjectPropertyOfAxiom(has_structure, super_has_structure)) - mgr.add_axiom(onto, OWLSubDataPropertyOfAxiom(charge, super_charge)) + onto.add_axiom(OWLSubObjectPropertyOfAxiom(has_structure, super_has_structure)) + onto.add_axiom(OWLSubDataPropertyOfAxiom(charge, super_charge)) # sub_property = True base_reasoner = OntologyReasoner(onto) @@ -357,8 +339,8 @@ def test_sub_property_inclusion(self): individuals = frozenset(reasoner.instances(ce)) self.assertEqual(len(individuals), 0) - mgr.remove_axiom(onto, OWLSubObjectPropertyOfAxiom(has_structure, super_has_structure)) - mgr.remove_axiom(onto, OWLSubDataPropertyOfAxiom(charge, super_charge)) + onto.remove_axiom(OWLSubObjectPropertyOfAxiom(has_structure, super_has_structure)) + onto.remove_axiom(OWLSubDataPropertyOfAxiom(charge, super_charge)) def test_inverse(self): ns = "http://example.com/father#" @@ -367,7 +349,7 @@ def test_inverse(self): has_child = OWLObjectProperty(IRI(ns, 'hasChild')) has_child_inverse = OWLObjectProperty(IRI.create(ns, 'hasChild_inverse')) - mgr.add_axiom(onto, OWLInverseObjectPropertiesAxiom(has_child, has_child_inverse)) + onto.add_axiom(OWLInverseObjectPropertiesAxiom(has_child, has_child_inverse)) parents = {OWLNamedIndividual(IRI.create(ns, 'anna')), OWLNamedIndividual(IRI.create(ns, 'martin')), @@ -384,13 +366,13 @@ def test_inverse(self): self.assertEqual(parents_expr, parents) self.assertEqual(parents_expr_inverse, parents) # Removal not needed, just for completeness - mgr.remove_axiom(onto, OWLInverseObjectPropertiesAxiom(has_child, has_child_inverse)) + onto.remove_axiom(OWLInverseObjectPropertiesAxiom(has_child, has_child_inverse)) # test sub properties super_has_child = OWLObjectProperty(IRI(ns, 'super_hasChild')) - mgr.add_axiom(onto, OWLSubObjectPropertyOfAxiom(has_child, super_has_child)) + onto.add_axiom(OWLSubObjectPropertyOfAxiom(has_child, super_has_child)) super_has_child_inverse = OWLObjectProperty(IRI(ns, 'super_hasChild_inverse')) - mgr.add_axiom(onto, OWLInverseObjectPropertiesAxiom(super_has_child, super_has_child_inverse)) + onto.add_axiom(OWLInverseObjectPropertiesAxiom(super_has_child, super_has_child_inverse)) # False (sub properties not taken into account) expr = OWLObjectSomeValuesFrom(super_has_child, OWLThing) @@ -409,8 +391,8 @@ def test_inverse(self): self.assertEqual(parents_expr, parents) self.assertEqual(parents_expr_inverse, parents) - mgr.remove_axiom(onto, OWLSubObjectPropertyOfAxiom(has_child, super_has_child)) - mgr.remove_axiom(onto, OWLInverseObjectPropertiesAxiom(super_has_child, super_has_child_inverse)) + onto.remove_axiom(OWLSubObjectPropertyOfAxiom(has_child, super_has_child)) + onto.remove_axiom(OWLInverseObjectPropertiesAxiom(super_has_child, super_has_child_inverse)) if __name__ == '__main__': diff --git a/tests/test_owlapy_ontology_management.py b/tests/test_owlapy_ontology_management.py index fefbf0c4..96a3ed9f 100644 --- a/tests/test_owlapy_ontology_management.py +++ b/tests/test_owlapy_ontology_management.py @@ -45,9 +45,9 @@ def test_equivalent_classes(self): compound = OWLClass(IRI(ns, 'Compound')) has_atom = OWLObjectProperty(IRI(ns, 'hasAtom')) - mgr.add_axiom(onto, OWLEquivalentClassesAxiom([OWLObjectUnionOf([atom, bond]), compound, atom, bond])) - mgr.add_axiom(onto, OWLEquivalentClassesAxiom([bond, benzene])) - mgr.add_axiom(onto, OWLEquivalentClassesAxiom([bond, OWLObjectSomeValuesFrom(has_atom, ball3)])) + onto.add_axiom(OWLEquivalentClassesAxiom([OWLObjectUnionOf([atom, bond]), compound, atom, bond])) + onto.add_axiom(OWLEquivalentClassesAxiom([bond, benzene])) + onto.add_axiom(OWLEquivalentClassesAxiom([bond, OWLObjectSomeValuesFrom(has_atom, ball3)])) classes = frozenset({atom, compound, bond}) target_classes = frozenset(reasoner.equivalent_classes(benzene, only_named=True)) @@ -87,9 +87,9 @@ def test_sub_classes(self): benzene = OWLClass(IRI(ns, 'Benzene')) has_atom = OWLObjectProperty(IRI(ns, 'hasAtom')) - mgr.add_axiom(onto, OWLSubClassOfAxiom(benzene, OWLObjectUnionOf([bond, bond]))) - mgr.add_axiom(onto, OWLSubClassOfAxiom(OWLObjectUnionOf([bond, bond]), ball3)) - mgr.add_axiom(onto, OWLSubClassOfAxiom(ball3, OWLObjectSomeValuesFrom(has_atom, ball3))) + onto.add_axiom(OWLSubClassOfAxiom(benzene, OWLObjectUnionOf([bond, bond]))) + onto.add_axiom(OWLSubClassOfAxiom(OWLObjectUnionOf([bond, bond]), ball3)) + onto.add_axiom(OWLSubClassOfAxiom(ball3, OWLObjectSomeValuesFrom(has_atom, ball3))) # Named class # Direct, only named @@ -149,9 +149,9 @@ def test_super_classes(self): ring_structure = OWLClass(IRI(ns, 'RingStructure')) has_atom = OWLObjectProperty(IRI(ns, 'hasAtom')) - mgr.add_axiom(onto, OWLSubClassOfAxiom(OWLObjectSomeValuesFrom(has_atom, ball3), benzene)) - mgr.add_axiom(onto, OWLSubClassOfAxiom(benzene, OWLObjectUnionOf([bond, bond]))) - mgr.add_axiom(onto, OWLSubClassOfAxiom(OWLObjectUnionOf([bond, bond]), ball3)) + onto.add_axiom(OWLSubClassOfAxiom(OWLObjectSomeValuesFrom(has_atom, ball3), benzene)) + onto.add_axiom(OWLSubClassOfAxiom(benzene, OWLObjectUnionOf([bond, bond]))) + onto.add_axiom(OWLSubClassOfAxiom(OWLObjectUnionOf([bond, bond]), ball3)) # Named class # Direct, only named @@ -255,7 +255,7 @@ def test_object_values(self): heinz = OWLNamedIndividual(IRI(ns, 'heinz')) has_child = OWLObjectProperty(IRI.create(ns, 'hasChild')) super_has_child = OWLObjectProperty(IRI.create(ns, 'super_hasChild')) - mgr.add_axiom(onto, OWLSubObjectPropertyOfAxiom(has_child, super_has_child)) + onto.add_axiom(OWLSubObjectPropertyOfAxiom(has_child, super_has_child)) kids = frozenset(reasoner.object_property_values(stefan, has_child)) target_kids = frozenset({markus}) @@ -275,12 +275,12 @@ def test_object_values(self): # test inverse has_child_inverse = OWLObjectProperty(IRI.create(ns, 'hasChild_inverse')) - mgr.add_axiom(onto, OWLInverseObjectPropertiesAxiom(has_child, has_child_inverse)) + onto.add_axiom(OWLInverseObjectPropertiesAxiom(has_child, has_child_inverse)) parents = frozenset(reasoner.object_property_values(markus, OWLObjectInverseOf(has_child), direct=True)) target_parents = frozenset({stefan}) self.assertEqual(target_parents, parents) # Remove again for completeness, would not be necessary - mgr.remove_axiom(onto, OWLInverseObjectPropertiesAxiom(has_child, has_child_inverse)) + onto.remove_axiom(OWLInverseObjectPropertiesAxiom(has_child, has_child_inverse)) # test inverse with sub property # Setup: @@ -288,7 +288,7 @@ def test_object_values(self): # hasChild inverseOf hasChild_inverse # super_hasChild inverseOf super_hasChild_inverse super_has_child_inverse = OWLObjectProperty(IRI.create(ns, 'super_hasChild_inverse')) - mgr.add_axiom(onto, OWLInverseObjectPropertiesAxiom(super_has_child, super_has_child_inverse)) + onto.add_axiom(OWLInverseObjectPropertiesAxiom(super_has_child, super_has_child_inverse)) parents = frozenset(reasoner.object_property_values(stefan, OWLObjectInverseOf(super_has_child_inverse), @@ -301,8 +301,8 @@ def test_object_values(self): direct=False)) target_parents = frozenset({markus}) self.assertEqual(target_parents, parents) - mgr.remove_axiom(onto, OWLInverseObjectPropertiesAxiom(super_has_child, super_has_child_inverse)) - mgr.remove_axiom(onto, OWLSubObjectPropertyOfAxiom(has_child, super_has_child)) + onto.remove_axiom(OWLInverseObjectPropertiesAxiom(super_has_child, super_has_child_inverse)) + onto.remove_axiom(OWLSubObjectPropertyOfAxiom(has_child, super_has_child)) def test_data_values(self): ns = "http://dl-learner.org/mutagenesis#" @@ -314,7 +314,7 @@ def test_data_values(self): d100_1 = OWLNamedIndividual(IRI.create(ns, 'd100_1')) charge = OWLDataProperty(IRI.create(ns, 'charge')) super_charge = OWLDataProperty(IRI.create(ns, 'super_charge')) - mgr.add_axiom(onto, OWLSubDataPropertyOfAxiom(charge, super_charge)) + onto.add_axiom(OWLSubDataPropertyOfAxiom(charge, super_charge)) values = frozenset(reasoner.data_property_values(d100_1, charge)) targets = frozenset({OWLLiteral(-0.128)}) @@ -332,7 +332,7 @@ def test_data_values(self): values = frozenset(reasoner.data_property_values(d100_1, super_charge, direct=True)) targets = frozenset() self.assertEqual(targets, values) - mgr.remove_axiom(onto, OWLSubDataPropertyOfAxiom(charge, super_charge)) + onto.remove_axiom(OWLSubDataPropertyOfAxiom(charge, super_charge)) def test_all_data_values(self): ns = "http://dl-learner.org/mutagenesis#" @@ -343,7 +343,7 @@ def test_all_data_values(self): charge = OWLDataProperty(IRI.create(ns, 'charge')) super_charge = OWLDataProperty(IRI.create(ns, 'super_charge')) - mgr.add_axiom(onto, OWLSubDataPropertyOfAxiom(charge, super_charge)) + onto.add_axiom(OWLSubDataPropertyOfAxiom(charge, super_charge)) values = frozenset(reasoner.all_data_property_values(charge, direct=True)) self.assertEqual(529, len(values)) @@ -353,7 +353,7 @@ def test_all_data_values(self): values = frozenset(reasoner.all_data_property_values(super_charge, direct=False)) self.assertEqual(529, len(values)) - mgr.remove_axiom(onto, OWLSubDataPropertyOfAxiom(charge, super_charge)) + onto.remove_axiom(OWLSubDataPropertyOfAxiom(charge, super_charge)) def test_ind_object_properties(self): ns = "http://example.com/father#" @@ -365,7 +365,7 @@ def test_ind_object_properties(self): stefan = OWLNamedIndividual(IRI.create(ns, 'stefan')) has_child = OWLObjectProperty(IRI.create(ns, 'hasChild')) super_has_child = OWLObjectProperty(IRI.create(ns, 'super_hasChild')) - mgr.add_axiom(onto, OWLSubObjectPropertyOfAxiom(has_child, super_has_child)) + onto.add_axiom(OWLSubObjectPropertyOfAxiom(has_child, super_has_child)) properties = frozenset(reasoner.ind_object_properties(stefan, direct=True)) target_properties = frozenset({has_child}) @@ -374,7 +374,7 @@ def test_ind_object_properties(self): properties = frozenset(reasoner.ind_object_properties(stefan, direct=False)) target_properties = frozenset({has_child, super_has_child}) self.assertEqual(target_properties, properties) - mgr.remove_axiom(onto, OWLSubObjectPropertyOfAxiom(has_child, super_has_child)) + onto.remove_axiom(OWLSubObjectPropertyOfAxiom(has_child, super_has_child)) def test_ind_data_properties(self): ns = "http://dl-learner.org/mutagenesis#" @@ -386,7 +386,7 @@ def test_ind_data_properties(self): d100_1 = OWLNamedIndividual(IRI.create(ns, 'd100_1')) charge = OWLDataProperty(IRI.create(ns, 'charge')) super_charge = OWLDataProperty(IRI.create(ns, 'super_charge')) - mgr.add_axiom(onto, OWLSubDataPropertyOfAxiom(charge, super_charge)) + onto.add_axiom(OWLSubDataPropertyOfAxiom(charge, super_charge)) properties = frozenset(reasoner.ind_data_properties(d100_1, direct=True)) target_properties = frozenset({charge}) @@ -395,7 +395,7 @@ def test_ind_data_properties(self): properties = frozenset(reasoner.ind_data_properties(d100_1, direct=False)) target_properties = frozenset({charge, super_charge}) self.assertEqual(target_properties, properties) - mgr.remove_axiom(onto, OWLSubDataPropertyOfAxiom(charge, super_charge)) + onto.remove_axiom(OWLSubDataPropertyOfAxiom(charge, super_charge)) def test_add_remove_axiom(self): ns = "http://example.com/father#" @@ -423,159 +423,159 @@ def test_add_remove_axiom(self): aerial_animal = OWLClass(IRI(ns, 'aerialAnimal')) self.assertNotIn(sister, list(onto.classes_in_signature())) - mgr.add_axiom(onto, OWLClassAssertionAxiom(anna, sister)) + onto.add_axiom(OWLClassAssertionAxiom(anna, sister)) self.assertIn(sister, list(onto.classes_in_signature())) self.assertIn(anna, list(reasoner.instances(sister))) self.assertIn(sister, list(reasoner.types(anna))) - mgr.remove_axiom(onto, OWLClassAssertionAxiom(anna, sister)) + onto.remove_axiom(OWLClassAssertionAxiom(anna, sister)) self.assertNotIn(anna, list(reasoner.instances(sister))) self.assertNotIn(sister, list(reasoner.types(anna))) self.assertNotIn(michelle, list(reasoner.instances(sister))) - mgr.add_axiom(onto, OWLClassAssertionAxiom(michelle, sister)) + onto.add_axiom(OWLClassAssertionAxiom(michelle, sister)) self.assertIn(michelle, list(reasoner.instances(sister))) self.assertIn(sister, list(reasoner.types(michelle))) - mgr.remove_axiom(onto, OWLClassAssertionAxiom(michelle, sister)) + onto.remove_axiom(OWLClassAssertionAxiom(michelle, sister)) self.assertNotIn(michelle, list(reasoner.instances(sister))) self.assertNotIn(sister, list(reasoner.types(michelle))) self.assertFalse(list(reasoner.object_property_values(michelle, has_child))) - mgr.add_axiom(onto, OWLObjectPropertyAssertionAxiom(michelle, has_child, anna)) + onto.add_axiom(OWLObjectPropertyAssertionAxiom(michelle, has_child, anna)) self.assertIn(anna, list(reasoner.object_property_values(michelle, has_child))) - mgr.remove_axiom(onto, OWLObjectPropertyAssertionAxiom(michelle, has_child, anna)) + onto.remove_axiom(OWLObjectPropertyAssertionAxiom(michelle, has_child, anna)) self.assertNotIn(anna, list(reasoner.object_property_values(michelle, has_child))) - mgr.remove_axiom(onto, OWLObjectPropertyAssertionAxiom(michelle, has_child, anna)) + onto.remove_axiom(OWLObjectPropertyAssertionAxiom(michelle, has_child, anna)) self.assertNotIn(has_sibling, list(onto.object_properties_in_signature())) self.assertNotIn(marius, list(onto.individuals_in_signature())) - mgr.add_axiom(onto, OWLObjectPropertyAssertionAxiom(marius, has_sibling, michelle)) + onto.add_axiom(OWLObjectPropertyAssertionAxiom(marius, has_sibling, michelle)) self.assertIn(has_sibling, list(onto.object_properties_in_signature())) self.assertIn(marius, list(onto.individuals_in_signature())) self.assertIn(michelle, list(reasoner.object_property_values(marius, has_sibling))) - mgr.remove_axiom(onto, OWLObjectPropertyAssertionAxiom(marius, has_sibling, michelle)) + onto.remove_axiom(OWLObjectPropertyAssertionAxiom(marius, has_sibling, michelle)) self.assertNotIn(michelle, list(reasoner.object_property_values(marius, has_sibling))) self.assertNotIn(age, list(onto.data_properties_in_signature())) - mgr.add_axiom(onto, OWLDataPropertyAssertionAxiom(markus, age, OWLLiteral(30))) + onto.add_axiom(OWLDataPropertyAssertionAxiom(markus, age, OWLLiteral(30))) self.assertIn(age, list(onto.data_properties_in_signature())) self.assertIn(OWLLiteral(30), list(reasoner.data_property_values(markus, age))) - mgr.remove_axiom(onto, OWLDataPropertyAssertionAxiom(markus, age, OWLLiteral(30))) + onto.remove_axiom(OWLDataPropertyAssertionAxiom(markus, age, OWLLiteral(30))) self.assertNotIn(OWLLiteral(30), list(reasoner.data_property_values(markus, age))) self.assertNotIn(OWLLiteral(31), list(reasoner.data_property_values(anna, age))) - mgr.add_axiom(onto, OWLDataPropertyAssertionAxiom(anna, age, OWLLiteral(31))) + onto.add_axiom(OWLDataPropertyAssertionAxiom(anna, age, OWLLiteral(31))) self.assertIn(OWLLiteral(31), list(reasoner.data_property_values(anna, age))) - mgr.remove_axiom(onto, OWLDataPropertyAssertionAxiom(anna, age, OWLLiteral(31))) + onto.remove_axiom(OWLDataPropertyAssertionAxiom(anna, age, OWLLiteral(31))) self.assertNotIn(OWLLiteral(31), list(reasoner.data_property_values(anna, age))) self.assertNotIn(brother, list(onto.classes_in_signature())) self.assertNotIn(animal, list(onto.classes_in_signature())) self.assertNotIn(aerial_animal, list(onto.classes_in_signature())) - mgr.add_axiom(onto, OWLSubClassOfAxiom(brother, male)) - mgr.add_axiom(onto, OWLSubClassOfAxiom(aerial_animal, animal)) + onto.add_axiom(OWLSubClassOfAxiom(brother, male)) + onto.add_axiom(OWLSubClassOfAxiom(aerial_animal, animal)) self.assertIn(brother, list(reasoner.sub_classes(male))) self.assertIn(aerial_animal, list(reasoner.sub_classes(animal))) self.assertIn(male, list(reasoner.super_classes(brother))) self.assertIn(animal, list(reasoner.super_classes(aerial_animal))) - mgr.remove_axiom(onto, OWLSubClassOfAxiom(brother, male)) + onto.remove_axiom(OWLSubClassOfAxiom(brother, male)) self.assertNotIn(brother, list(reasoner.sub_classes(male))) self.assertNotIn(male, list(reasoner.super_classes(brother))) self.assertNotIn(has_sibling, list(reasoner.sub_object_properties(has_child))) - mgr.add_axiom(onto, OWLSubObjectPropertyOfAxiom(has_sibling, has_child)) + onto.add_axiom(OWLSubObjectPropertyOfAxiom(has_sibling, has_child)) self.assertIn(has_sibling, list(reasoner.sub_object_properties(has_child))) - mgr.remove_axiom(onto, OWLSubObjectPropertyOfAxiom(has_sibling, has_child)) + onto.remove_axiom(OWLSubObjectPropertyOfAxiom(has_sibling, has_child)) self.assertNotIn(has_sibling, list(reasoner.sub_object_properties(has_child))) self.assertNotIn(OWLObjectUnionOf([person, person_sibling]), list(reasoner.object_property_domains(has_sibling))) - mgr.add_axiom(onto, OWLObjectPropertyDomainAxiom(has_sibling, OWLObjectUnionOf([person, person_sibling]))) + onto.add_axiom(OWLObjectPropertyDomainAxiom(has_sibling, OWLObjectUnionOf([person, person_sibling]))) self.assertIn(OWLObjectUnionOf([person, person_sibling]), list(reasoner.object_property_domains(has_sibling, direct=True))) - mgr.remove_axiom(onto, OWLObjectPropertyDomainAxiom(has_sibling, OWLObjectUnionOf([person, person_sibling]))) + onto.remove_axiom(OWLObjectPropertyDomainAxiom(has_sibling, OWLObjectUnionOf([person, person_sibling]))) self.assertNotIn(OWLObjectUnionOf([person, person_sibling]), list(reasoner.object_property_domains(has_sibling, direct=True))) self.assertNotIn(sister, list(reasoner.object_property_ranges(has_sibling))) - mgr.add_axiom(onto, OWLObjectPropertyRangeAxiom(has_sibling, sister)) + onto.add_axiom(OWLObjectPropertyRangeAxiom(has_sibling, sister)) self.assertIn(sister, list(reasoner.object_property_ranges(has_sibling))) - mgr.remove_axiom(onto, OWLObjectPropertyRangeAxiom(has_sibling, sister)) + onto.remove_axiom(OWLObjectPropertyRangeAxiom(has_sibling, sister)) self.assertNotIn(sister, list(reasoner.object_property_ranges(has_sibling))) self.assertNotIn(person, list(reasoner.data_property_domains(age))) - mgr.add_axiom(onto, OWLDataPropertyDomainAxiom(age, person)) + onto.add_axiom(OWLDataPropertyDomainAxiom(age, person)) self.assertIn(person, list(reasoner.data_property_domains(age))) - mgr.remove_axiom(onto, OWLDataPropertyDomainAxiom(age, person)) + onto.remove_axiom(OWLDataPropertyDomainAxiom(age, person)) self.assertNotIn(person, list(reasoner.data_property_domains(age))) self.assertFalse(list(reasoner.data_property_ranges(age))) - mgr.add_axiom(onto, OWLDataPropertyRangeAxiom(age, OWLDataUnionOf([IntegerOWLDatatype, DateOWLDatatype]))) + onto.add_axiom(OWLDataPropertyRangeAxiom(age, OWLDataUnionOf([IntegerOWLDatatype, DateOWLDatatype]))) self.assertIn(OWLDataUnionOf([IntegerOWLDatatype, DateOWLDatatype]), list(reasoner.data_property_ranges(age))) - mgr.remove_axiom(onto, OWLDataPropertyRangeAxiom(age, OWLDataUnionOf([IntegerOWLDatatype, DateOWLDatatype]))) + onto.remove_axiom(OWLDataPropertyRangeAxiom(age, OWLDataUnionOf([IntegerOWLDatatype, DateOWLDatatype]))) self.assertNotIn(OWLDataUnionOf([IntegerOWLDatatype, DateOWLDatatype]), list(reasoner.data_property_ranges(age))) self.assertFalse(list(reasoner.equivalent_classes(brother))) - mgr.add_axiom(onto, OWLEquivalentClassesAxiom([brother, male])) + onto.add_axiom(OWLEquivalentClassesAxiom([brother, male])) self.assertIn(male, list(reasoner.equivalent_classes(brother))) - mgr.remove_axiom(onto, OWLEquivalentClassesAxiom([brother, male])) + onto.remove_axiom(OWLEquivalentClassesAxiom([brother, male])) self.assertNotIn(male, list(reasoner.equivalent_classes(brother))) self.assertFalse(list(reasoner.equivalent_object_properties(has_child))) - mgr.add_axiom(onto, OWLEquivalentObjectPropertiesAxiom([has_child, has_sibling])) + onto.add_axiom(OWLEquivalentObjectPropertiesAxiom([has_child, has_sibling])) self.assertIn(has_sibling, list(reasoner.equivalent_object_properties(has_child))) - mgr.remove_axiom(onto, OWLEquivalentObjectPropertiesAxiom([has_child, has_sibling])) + onto.remove_axiom(OWLEquivalentObjectPropertiesAxiom([has_child, has_sibling])) self.assertNotIn(has_sibling, list(reasoner.equivalent_object_properties(has_child))) self.assertFalse(list(reasoner.equivalent_data_properties(age))) - mgr.add_axiom(onto, OWLEquivalentDataPropertiesAxiom([age, test1])) + onto.add_axiom(OWLEquivalentDataPropertiesAxiom([age, test1])) self.assertIn(test1, list(reasoner.equivalent_data_properties(age))) - mgr.remove_axiom(onto, OWLEquivalentDataPropertiesAxiom([age, test1])) + onto.remove_axiom(OWLEquivalentDataPropertiesAxiom([age, test1])) self.assertNotIn(test1, list(reasoner.equivalent_data_properties(age))) self.assertFalse(list(reasoner.same_individuals(markus))) - mgr.add_axiom(onto, OWLSameIndividualAxiom([markus, anna, person1])) + onto.add_axiom(OWLSameIndividualAxiom([markus, anna, person1])) self.assertEqual({anna, person1}, set(reasoner.same_individuals(markus))) - mgr.remove_axiom(onto, OWLSameIndividualAxiom([markus, anna, person1])) + onto.remove_axiom(OWLSameIndividualAxiom([markus, anna, person1])) self.assertFalse(set(reasoner.same_individuals(markus))) self.assertFalse(list(reasoner.disjoint_classes(brother))) self.assertFalse(list(reasoner.disjoint_classes(person))) - mgr.add_axiom(onto, OWLDisjointClassesAxiom([brother, sister, aerial_animal])) + onto.add_axiom(OWLDisjointClassesAxiom([brother, sister, aerial_animal])) self.assertEqual({sister, aerial_animal}, set(reasoner.disjoint_classes(brother))) - mgr.remove_axiom(onto, OWLDisjointClassesAxiom([brother, sister, aerial_animal])) + onto.remove_axiom(OWLDisjointClassesAxiom([brother, sister, aerial_animal])) self.assertFalse(set(reasoner.disjoint_classes(brother))) - mgr.add_axiom(onto, OWLDisjointClassesAxiom([person, animal])) + onto.add_axiom(OWLDisjointClassesAxiom([person, animal])) self.assertEqual({animal, aerial_animal}, set(reasoner.disjoint_classes(person))) - mgr.remove_axiom(onto, OWLDisjointClassesAxiom([person, animal])) + onto.remove_axiom(OWLDisjointClassesAxiom([person, animal])) self.assertFalse(set(reasoner.disjoint_classes(person))) self.assertFalse(list(reasoner.disjoint_object_properties(has_sibling))) self.assertFalse(list(reasoner.disjoint_object_properties(has_child))) - mgr.add_axiom(onto, OWLDisjointObjectPropertiesAxiom([has_child, has_sibling])) + onto.add_axiom(OWLDisjointObjectPropertiesAxiom([has_child, has_sibling])) self.assertIn(has_sibling, set(reasoner.disjoint_object_properties(has_child))) self.assertIn(has_child, set(reasoner.disjoint_object_properties(has_sibling))) - mgr.remove_axiom(onto, OWLDisjointObjectPropertiesAxiom([has_child, has_sibling])) + onto.remove_axiom(OWLDisjointObjectPropertiesAxiom([has_child, has_sibling])) self.assertNotIn(has_sibling, set(reasoner.disjoint_object_properties(has_child))) self.assertNotIn(has_child, set(reasoner.disjoint_object_properties(has_sibling))) self.assertFalse(list(reasoner.disjoint_data_properties(age))) self.assertFalse(list(reasoner.disjoint_data_properties(test1))) - mgr.add_axiom(onto, OWLDisjointDataPropertiesAxiom([age, test1])) + onto.add_axiom(OWLDisjointDataPropertiesAxiom([age, test1])) self.assertIn(test1, set(reasoner.disjoint_data_properties(age))) self.assertIn(age, set(reasoner.disjoint_data_properties(test1))) - mgr.remove_axiom(onto, OWLDisjointDataPropertiesAxiom([age, test1])) + onto.remove_axiom(OWLDisjointDataPropertiesAxiom([age, test1])) self.assertNotIn(test1, set(reasoner.disjoint_data_properties(age))) self.assertNotIn(age, set(reasoner.disjoint_data_properties(test1))) self.assertFalse(list(reasoner.different_individuals(markus))) self.assertFalse(list(reasoner.different_individuals(michelle))) - mgr.add_axiom(onto, OWLDifferentIndividualsAxiom([markus, michelle])) - mgr.add_axiom(onto, OWLDifferentIndividualsAxiom([markus, anna, marius])) + onto.add_axiom(OWLDifferentIndividualsAxiom([markus, michelle])) + onto.add_axiom(OWLDifferentIndividualsAxiom([markus, anna, marius])) self.assertEqual({michelle, anna, marius}, set(reasoner.different_individuals(markus))) self.assertEqual({markus}, set(reasoner.different_individuals(michelle))) - mgr.remove_axiom(onto, OWLDifferentIndividualsAxiom([markus, michelle])) - mgr.remove_axiom(onto, OWLDifferentIndividualsAxiom([markus, anna, marius])) + onto.remove_axiom(OWLDifferentIndividualsAxiom([markus, michelle])) + onto.remove_axiom(OWLDifferentIndividualsAxiom([markus, anna, marius])) self.assertFalse(set(reasoner.different_individuals(markus))) self.assertFalse(set(reasoner.different_individuals(michelle))) diff --git a/tests/test_owlapy_owl2sparql_converter.py b/tests/test_owlapy_owl2sparql_converter.py index f943f0e0..b240cb78 100644 --- a/tests/test_owlapy_owl2sparql_converter.py +++ b/tests/test_owlapy_owl2sparql_converter.py @@ -11,10 +11,11 @@ from owlapy.owl_ontology_manager import OntologyManager from owlapy.owl_reasoner import OntologyReasoner, FastInstanceCheckerReasoner from owlapy.parser import DLSyntaxParser -from owlapy.converter import Owl2SparqlConverter from rdflib import Graph +from owlapy.converter import Owl2SparqlConverter + -PATH_FAMILY = '../KGs/Family/family-benchmark_rich_background.owl' +PATH_FAMILY = 'KGs/Family/family-benchmark_rich_background.owl' # checks whether all individuals returned by the reasoner are found in results generated by the sparql query @@ -92,164 +93,164 @@ def test_as_query(self): # )""" self.assertEqual(query, query_t) # add assertion here - # def test_Single(self): - # # rdf graph - using rdflib - # family_rdf_graph = Graph() - # family_rdf_graph.parse(location=PATH_FAMILY) - # # knowledge base - using OWLReasoner - # mgr = OntologyManager() - # onto = mgr.load_ontology(IRI.create(PATH_FAMILY)) - # base_reasoner = Reasoner(onto) - # family_kb_reasoner = FastInstanceCheckerReasoner(onto, base_reasoner=base_reasoner, negation_default=True) - # - # ce_str = "Brother" - # ce_parsed = DLSyntaxParser(namespace="http://www.benchmark.org/family#").parse_expression(expression_str=ce_str) - # actual_query = Owl2SparqlConverter().as_query(root_variable=self._root_var_, ce=ce_parsed, count=False, - # values=None, named_individuals=True) - # expected_query = """SELECT DISTINCT ?x - # WHERE { - # ?x . - # }""" - # - # sparql_results_actual = family_rdf_graph.query(actual_query) - # sparql_results_expected = family_rdf_graph.query(expected_query) - # reasoner_results = set(family_kb_reasoner.instances(ce_parsed)) - # - # self.assertEqual(len(sparql_results_actual), len(sparql_results_expected)) - # self.assertEqual(len(sparql_results_actual), len(reasoner_results)) - # self.assertTrue(check_reasoner_instances_in_sparql_results(sparql_results_actual, reasoner_results)) - # - # ce_str = "Male" - # ce_parsed = DLSyntaxParser(namespace="http://www.benchmark.org/family#").parse_expression(expression_str=ce_str) - # actual_query = Owl2SparqlConverter().as_query(root_variable=self._root_var_, ce=ce_parsed, count=False, - # values=None, named_individuals=True) - # expected_query = """SELECT DISTINCT ?x - # WHERE { - # ?x . - # }""" - # - # sparql_results_actual = family_rdf_graph.query(actual_query) - # sparql_results_expected = family_rdf_graph.query(expected_query) - # reasoner_results = set(family_kb_reasoner.instances(ce_parsed)) - # - # self.assertEqual(len(sparql_results_actual), len(sparql_results_expected)) - # self.assertEqual(len(sparql_results_actual), len(reasoner_results)) - # self.assertTrue(check_reasoner_instances_in_sparql_results(sparql_results_actual, reasoner_results)) - # - # def test_Intersection(self): - # # rdf graph - using rdflib - # family_rdf_graph = Graph() - # family_rdf_graph.parse(location=PATH_FAMILY) - # # knowledge base - using OWLReasoner - # mgr = OntologyManager() - # onto = mgr.load_ontology(IRI.create(PATH_FAMILY)) - # base_reasoner = Reasoner(onto) - # family_kb_reasoner = FastInstanceCheckerReasoner(onto, base_reasoner=base_reasoner, negation_default=True) - # - # ce_str = "Brother ⊓ Father" - # ce_parsed = DLSyntaxParser(namespace="http://www.benchmark.org/family#").parse_expression(expression_str=ce_str) - # actual_query = Owl2SparqlConverter().as_query(root_variable=self._root_var_, ce=ce_parsed, count=False, - # values=None, named_individuals=True) - # expected_query = """SELECT DISTINCT ?x - # WHERE { - # ?x . - # ?x . - # }""" - # - # sparql_results_actual = family_rdf_graph.query(actual_query) - # sparql_results_expected = family_rdf_graph.query(expected_query) - # reasoner_results = set(family_kb_reasoner.instances(ce_parsed)) - # - # self.assertEqual(len(sparql_results_actual), len(sparql_results_expected)) - # self.assertEqual(len(sparql_results_actual), len(reasoner_results)) - # self.assertTrue(check_reasoner_instances_in_sparql_results(sparql_results_actual, reasoner_results)) - # - # def test_Union(self): - # # rdf graph - using rdflib - # family_rdf_graph = Graph() - # family_rdf_graph.parse(location=PATH_FAMILY) - # # knowledge base - using OWLReasoner - # mgr = OntologyManager() - # onto = mgr.load_ontology(IRI.create(PATH_FAMILY)) - # base_reasoner = Reasoner(onto) - # family_kb_reasoner = FastInstanceCheckerReasoner(onto, base_reasoner=base_reasoner, negation_default=True) - # - # ce_str = "Sister ⊔ Mother" - # ce_parsed = DLSyntaxParser(namespace="http://www.benchmark.org/family#").parse_expression(expression_str=ce_str) - # actual_query = Owl2SparqlConverter().as_query(root_variable=self._root_var_, ce=ce_parsed, count=False, - # values=None, named_individuals=True) - # expected_query = """SELECT DISTINCT ?x - # WHERE { - # { ?x . } - # UNION - # { ?x . } - # }""" - # - # sparql_results_actual = family_rdf_graph.query(actual_query) - # sparql_results_expected = family_rdf_graph.query(expected_query) - # reasoner_results = set(family_kb_reasoner.instances(ce_parsed)) - # - # self.assertEqual(len(sparql_results_actual), len(sparql_results_expected)) - # self.assertEqual(len(sparql_results_actual), len(reasoner_results)) - # self.assertTrue(check_reasoner_instances_in_sparql_results(sparql_results_actual, reasoner_results)) - # - # def test_Complement(self): - # # rdf graph - using rdflib - # family_rdf_graph = Graph() - # family_rdf_graph.parse(location=PATH_FAMILY) - # # knowledge base - using OWLReasoner - # mgr = OntologyManager() - # onto = mgr.load_ontology(IRI.create(PATH_FAMILY)) - # base_reasoner = Reasoner(onto) - # family_kb_reasoner = FastInstanceCheckerReasoner(onto, base_reasoner=base_reasoner, negation_default=True) - # - # ce_str = "¬Mother" - # ce_parsed = DLSyntaxParser(namespace="http://www.benchmark.org/family#").parse_expression(expression_str=ce_str) - # actual_query = Owl2SparqlConverter().as_query(root_variable=self._root_var_, ce=ce_parsed, count=False, - # values=None, named_individuals=True) - # expected_query = """SELECT DISTINCT ?x - # WHERE { - # ?x a . - # ?x ?p ?o . - # FILTER NOT EXISTS { ?x a . } - # }""" - # - # sparql_results_actual = family_rdf_graph.query(actual_query) - # sparql_results_expected = family_rdf_graph.query(expected_query) - # reasoner_results = set(family_kb_reasoner.instances(ce_parsed)) - # - # self.assertEqual(len(sparql_results_actual), len(sparql_results_expected)) - # self.assertEqual(len(sparql_results_actual), len(reasoner_results)) - # self.assertTrue(check_reasoner_instances_in_sparql_results(sparql_results_actual, reasoner_results)) - # - # def test_Exists(self): - # # rdf graph - using rdflib - # family_rdf_graph = Graph() - # family_rdf_graph.parse(location=PATH_FAMILY) - # # knowledge base - using OWLReasoner - # mgr = OntologyManager() - # onto = mgr.load_ontology(IRI.create(PATH_FAMILY)) - # base_reasoner = Reasoner(onto) - # family_kb_reasoner = FastInstanceCheckerReasoner(onto, base_reasoner=base_reasoner, negation_default=True) - # - # ce_str = "∃hasChild.Male" - # ce_parsed = DLSyntaxParser(namespace="http://www.benchmark.org/family#").parse_expression(expression_str=ce_str) - # actual_query = Owl2SparqlConverter().as_query(root_variable=self._root_var_, ce=ce_parsed, count=False, - # values=None, named_individuals=True) - # expected_query = """SELECT DISTINCT ?x - # WHERE { - # ?x ?s . - # ?s a . - # }""" - # - # sparql_results_actual = family_rdf_graph.query(actual_query) - # sparql_results_expected = family_rdf_graph.query(expected_query) - # reasoner_results = set(family_kb_reasoner.instances(ce_parsed)) - # - # self.assertEqual(len(sparql_results_actual), len(sparql_results_expected)) - # self.assertEqual(len(sparql_results_actual), len(reasoner_results)) - # self.assertTrue(check_reasoner_instances_in_sparql_results(sparql_results_actual, reasoner_results)) - # + def test_Single(self): + # rdf graph - using rdflib + family_rdf_graph = Graph() + family_rdf_graph.parse(location=PATH_FAMILY) + # knowledge base - using OWLReasoner + mgr = OntologyManager() + onto = mgr.load_ontology(IRI.create(PATH_FAMILY)) + base_reasoner = OntologyReasoner(onto) + family_kb_reasoner = FastInstanceCheckerReasoner(onto, base_reasoner=base_reasoner, negation_default=True) + + ce_str = "Brother" + ce_parsed = DLSyntaxParser(namespace="http://www.benchmark.org/family#").parse_expression(expression_str=ce_str) + actual_query = Owl2SparqlConverter().as_query(root_variable=self._root_var_, ce=ce_parsed, count=False, + values=None, named_individuals=True) + expected_query = """SELECT DISTINCT ?x + WHERE { + ?x . + }""" + + sparql_results_actual = family_rdf_graph.query(actual_query) + sparql_results_expected = family_rdf_graph.query(expected_query) + reasoner_results = set(family_kb_reasoner.instances(ce_parsed)) + + self.assertEqual(len(sparql_results_actual), len(sparql_results_expected)) + self.assertEqual(len(sparql_results_actual), len(reasoner_results)) + self.assertTrue(check_reasoner_instances_in_sparql_results(sparql_results_actual, reasoner_results)) + + ce_str = "Male" + ce_parsed = DLSyntaxParser(namespace="http://www.benchmark.org/family#").parse_expression(expression_str=ce_str) + actual_query = Owl2SparqlConverter().as_query(root_variable=self._root_var_, ce=ce_parsed, count=False, + values=None, named_individuals=True) + expected_query = """SELECT DISTINCT ?x + WHERE { + ?x . + }""" + + sparql_results_actual = family_rdf_graph.query(actual_query) + sparql_results_expected = family_rdf_graph.query(expected_query) + reasoner_results = set(family_kb_reasoner.instances(ce_parsed)) + + self.assertEqual(len(sparql_results_actual), len(sparql_results_expected)) + self.assertEqual(len(sparql_results_actual), len(reasoner_results)) + self.assertTrue(check_reasoner_instances_in_sparql_results(sparql_results_actual, reasoner_results)) + + def test_Intersection(self): + # rdf graph - using rdflib + family_rdf_graph = Graph() + family_rdf_graph.parse(location=PATH_FAMILY) + # knowledge base - using OWLReasoner + mgr = OntologyManager() + onto = mgr.load_ontology(IRI.create(PATH_FAMILY)) + base_reasoner = OntologyReasoner(onto) + family_kb_reasoner = FastInstanceCheckerReasoner(onto, base_reasoner=base_reasoner, negation_default=True) + + ce_str = "Brother ⊓ Father" + ce_parsed = DLSyntaxParser(namespace="http://www.benchmark.org/family#").parse_expression(expression_str=ce_str) + actual_query = Owl2SparqlConverter().as_query(root_variable=self._root_var_, ce=ce_parsed, count=False, + values=None, named_individuals=True) + expected_query = """SELECT DISTINCT ?x + WHERE { + ?x . + ?x . + }""" + + sparql_results_actual = family_rdf_graph.query(actual_query) + sparql_results_expected = family_rdf_graph.query(expected_query) + reasoner_results = set(family_kb_reasoner.instances(ce_parsed)) + + self.assertEqual(len(sparql_results_actual), len(sparql_results_expected)) + self.assertEqual(len(sparql_results_actual), len(reasoner_results)) + self.assertTrue(check_reasoner_instances_in_sparql_results(sparql_results_actual, reasoner_results)) + + def test_Union(self): + # rdf graph - using rdflib + family_rdf_graph = Graph() + family_rdf_graph.parse(location=PATH_FAMILY) + # knowledge base - using OWLReasoner + mgr = OntologyManager() + onto = mgr.load_ontology(IRI.create(PATH_FAMILY)) + base_reasoner = OntologyReasoner(onto) + family_kb_reasoner = FastInstanceCheckerReasoner(onto, base_reasoner=base_reasoner, negation_default=True) + + ce_str = "Sister ⊔ Mother" + ce_parsed = DLSyntaxParser(namespace="http://www.benchmark.org/family#").parse_expression(expression_str=ce_str) + actual_query = Owl2SparqlConverter().as_query(root_variable=self._root_var_, ce=ce_parsed, count=False, + values=None, named_individuals=True) + expected_query = """SELECT DISTINCT ?x + WHERE { + { ?x . } + UNION + { ?x . } + }""" + + sparql_results_actual = family_rdf_graph.query(actual_query) + sparql_results_expected = family_rdf_graph.query(expected_query) + reasoner_results = set(family_kb_reasoner.instances(ce_parsed)) + + self.assertEqual(len(sparql_results_actual), len(sparql_results_expected)) + self.assertEqual(len(sparql_results_actual), len(reasoner_results)) + self.assertTrue(check_reasoner_instances_in_sparql_results(sparql_results_actual, reasoner_results)) + + def test_Complement(self): + # rdf graph - using rdflib + family_rdf_graph = Graph() + family_rdf_graph.parse(location=PATH_FAMILY) + # knowledge base - using OWLReasoner + mgr = OntologyManager() + onto = mgr.load_ontology(IRI.create(PATH_FAMILY)) + base_reasoner = OntologyReasoner(onto) + family_kb_reasoner = FastInstanceCheckerReasoner(onto, base_reasoner=base_reasoner, negation_default=True) + + ce_str = "¬Mother" + ce_parsed = DLSyntaxParser(namespace="http://www.benchmark.org/family#").parse_expression(expression_str=ce_str) + actual_query = Owl2SparqlConverter().as_query(root_variable=self._root_var_, ce=ce_parsed, count=False, + values=None, named_individuals=True) + expected_query = """SELECT DISTINCT ?x + WHERE { + ?x a . + ?x ?p ?o . + FILTER NOT EXISTS { ?x a . } + }""" + + sparql_results_actual = family_rdf_graph.query(actual_query) + sparql_results_expected = family_rdf_graph.query(expected_query) + reasoner_results = set(family_kb_reasoner.instances(ce_parsed)) + + self.assertEqual(len(sparql_results_actual), len(sparql_results_expected)) + self.assertEqual(len(sparql_results_actual), len(reasoner_results)) + self.assertTrue(check_reasoner_instances_in_sparql_results(sparql_results_actual, reasoner_results)) + + def test_Exists(self): + # rdf graph - using rdflib + family_rdf_graph = Graph() + family_rdf_graph.parse(location=PATH_FAMILY) + # knowledge base - using OWLReasoner + mgr = OntologyManager() + onto = mgr.load_ontology(IRI.create(PATH_FAMILY)) + base_reasoner = OntologyReasoner(onto) + family_kb_reasoner = FastInstanceCheckerReasoner(onto, base_reasoner=base_reasoner, negation_default=True) + + ce_str = "∃hasChild.Male" + ce_parsed = DLSyntaxParser(namespace="http://www.benchmark.org/family#").parse_expression(expression_str=ce_str) + actual_query = Owl2SparqlConverter().as_query(root_variable=self._root_var_, ce=ce_parsed, count=False, + values=None, named_individuals=True) + expected_query = """SELECT DISTINCT ?x + WHERE { + ?x ?s . + ?s a . + }""" + + sparql_results_actual = family_rdf_graph.query(actual_query) + sparql_results_expected = family_rdf_graph.query(expected_query) + reasoner_results = set(family_kb_reasoner.instances(ce_parsed)) + + self.assertEqual(len(sparql_results_actual), len(sparql_results_expected)) + self.assertEqual(len(sparql_results_actual), len(reasoner_results)) + self.assertTrue(check_reasoner_instances_in_sparql_results(sparql_results_actual, reasoner_results)) + # def test_ForAll(self): # # rdf graph - using rdflib # family_rdf_graph = Graph() @@ -257,7 +258,7 @@ def test_as_query(self): # # knowledge base - using OWLReasoner # mgr = OntologyManager() # onto = mgr.load_ontology(IRI.create(PATH_FAMILY)) - # base_reasoner = Reasoner(onto) + # base_reasoner = OntologyReasoner(onto) # family_kb_reasoner = FastInstanceCheckerReasoner(onto, base_reasoner=base_reasoner, negation_default=True) # # ce_str = "∀hasChild.Male" @@ -307,7 +308,7 @@ def test_as_query(self): # # knowledge base - using OWLReasoner # mgr = OntologyManager() # onto = mgr.load_ontology(IRI.create(PATH_FAMILY)) - # base_reasoner = Reasoner(onto) + # base_reasoner = OntologyReasoner(onto) # family_kb_reasoner = FastInstanceCheckerReasoner(onto, base_reasoner=base_reasoner, negation_default=True) # # ce_str = "∀hasChild.Male" @@ -342,7 +343,7 @@ def test_as_query(self): # # knowledge base - using OWLReasoner # mgr = OntologyManager() # onto = mgr.load_ontology(IRI.create(PATH_FAMILY)) - # base_reasoner = Reasoner(onto) + # base_reasoner = OntologyReasoner(onto) # family_kb_reasoner = FastInstanceCheckerReasoner(onto, base_reasoner=base_reasoner, negation_default=True) # # concepts = [ @@ -373,7 +374,7 @@ def test_as_query(self): # # knowledge base - using OWLReasoner # mgr = OntologyManager() # onto = mgr.load_ontology(IRI.create(PATH_FAMILY)) - # base_reasoner = Reasoner(onto) + # base_reasoner = OntologyReasoner(onto) # family_kb_reasoner = FastInstanceCheckerReasoner(onto, base_reasoner=base_reasoner, negation_default=True) # # concepts = [ diff --git a/tests/test_sync_ontology.py b/tests/test_sync_ontology.py new file mode 100644 index 00000000..42c7727e --- /dev/null +++ b/tests/test_sync_ontology.py @@ -0,0 +1,112 @@ +import unittest + +from owlapy.class_expression import OWLClass, OWLObjectIntersectionOf, OWLObjectSomeValuesFrom +from owlapy.iri import IRI +from owlapy.owl_axiom import OWLEquivalentClassesAxiom +from owlapy.owl_individual import OWLNamedIndividual +from owlapy.owl_ontology import OWLOntologyID +from owlapy.owl_ontology_manager import SyncOntologyManager +from owlapy.owl_property import OWLDataProperty, OWLObjectProperty + +NS = "http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#" + +# ==== Individuals ==== +a = OWLNamedIndividual(IRI(NS, "a")) +b = OWLNamedIndividual(IRI(NS, "b")) +c = OWLNamedIndividual(IRI(NS, "c")) +d = OWLNamedIndividual(IRI(NS, "d")) +e = OWLNamedIndividual(IRI(NS, "e")) +f = OWLNamedIndividual(IRI(NS, "f")) +g = OWLNamedIndividual(IRI(NS, "g")) +h = OWLNamedIndividual(IRI(NS, "h")) +m = OWLNamedIndividual(IRI(NS, "m")) +l = OWLNamedIndividual(IRI(NS, "l")) # noqa: E741 +n = OWLNamedIndividual(IRI(NS, "n")) +o = OWLNamedIndividual(IRI(NS, "o")) +p = OWLNamedIndividual(IRI(NS, "p")) +q = OWLNamedIndividual(IRI(NS, "q")) +r = OWLNamedIndividual(IRI(NS, "r")) +s = OWLNamedIndividual(IRI(NS, "s")) +ind1 = OWLNamedIndividual(IRI(NS, "ind1")) + +# ==== Object Properties ==== +r1 = OWLObjectProperty(IRI(NS, "r1")) +r2 = OWLObjectProperty(IRI(NS, "r2")) +r3 = OWLObjectProperty(IRI(NS, "r3")) +r4 = OWLObjectProperty(IRI(NS, "r4")) +r5 = OWLObjectProperty(IRI(NS, "r5")) +r6 = OWLObjectProperty(IRI(NS, "r6")) +r7 = OWLObjectProperty(IRI(NS, "r7")) + +# ==== Data Properties ==== + +dp1 = OWLDataProperty(IRI(NS, "dp1")) +dp2 = OWLDataProperty(IRI(NS, "dp2")) +dp3 = OWLDataProperty(IRI(NS, "dp3")) + +# ==== Classes ==== + +A = OWLClass(IRI(NS, 'A')) +B = OWLClass(IRI(NS, 'B')) +C = OWLClass(IRI(NS, 'C')) +AB = OWLClass(IRI(NS, 'AB')) +D = OWLClass(IRI(NS, 'D')) +E = OWLClass(IRI(NS, 'E')) +F = OWLClass(IRI(NS, 'F')) +G = OWLClass(IRI(NS, 'G')) +J = OWLClass(IRI(NS, 'J')) +K = OWLClass(IRI(NS, 'K')) +H = OWLClass(IRI(NS, 'H')) +I = OWLClass(IRI(NS, 'I')) # noqa: E741 +L = OWLClass(IRI(NS, 'L')) +M = OWLClass(IRI(NS, 'M')) +N = OWLClass(IRI(NS, 'N')) +O = OWLClass(IRI(NS, 'O')) # noqa: E741 +P = OWLClass(IRI(NS, 'P')) +Q = OWLClass(IRI(NS, 'Q')) +R = OWLClass(IRI(NS, 'R')) +S = OWLClass(IRI(NS, 'S')) +T = OWLClass(IRI(NS, 'T')) +U = OWLClass(IRI(NS, 'U')) + + +class TestSyncReasoner(unittest.TestCase): + + ontology_path = "KGs/Test/test_ontology.owl" + manager = SyncOntologyManager() + onto = manager.load_ontology(ontology_path) + + # NOTE AB: The name of "assertCountEqual" may be misleading,but it's essentially an order-insensitive "assertEqual". + + def test_classes_in_signature(self): + self.assertCountEqual(list(self.onto.classes_in_signature()), [A, AB, B, C, D, E, F, G, H, I, J, K, L, M, N, O, + P, Q, R, S, T, U]) + + def test_data_properties_in_signature(self): + self.assertCountEqual(list(self.onto.data_properties_in_signature()), [dp1, dp2, dp3]) + + def test_object_properties_in_signature(self): + self.assertCountEqual(list(self.onto.object_properties_in_signature()), [r1, r2, r3, r4, r5, r6, r7]) + + def test_individuals_in_signature(self): + self.assertCountEqual(list(self.onto.individuals_in_signature()), [a, b, c, d, e, f, g, h, m, l, n, o, p, q, r, + s, ind1]) + + def test_equivalent_classes_axiom(self): + eq1 = OWLEquivalentClassesAxiom([N, Q]) + eq2 = OWLEquivalentClassesAxiom([F, OWLObjectSomeValuesFrom(property=r2, filler=G)]) + eq3 = OWLEquivalentClassesAxiom([AB, OWLObjectIntersectionOf((A, B))]) + aeq = set() + for cls in self.onto.classes_in_signature(): + ea = set(self.onto.equivalent_classes_axioms(cls)) + aeq.update(ea) + self.assertCountEqual(aeq, {eq1, eq2, eq3}) + + def test_get_ontology_id(self): + onto_id = OWLOntologyID(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/', 'untitled-ontology-11'), + None) + self.assertEqual(self.onto.get_ontology_id(), onto_id) + + def test__eq__(self): + onto2 = self.manager.load_ontology(self.ontology_path) + self.assertTrue(self.onto.__eq__(onto2)) diff --git a/tests/test_sync_reasoner.py b/tests/test_sync_reasoner.py new file mode 100644 index 00000000..2e32d217 --- /dev/null +++ b/tests/test_sync_reasoner.py @@ -0,0 +1,353 @@ +import os +import unittest + +from jpype import JDouble +from owlapy.class_expression import OWLClass, OWLDataSomeValuesFrom, OWLObjectIntersectionOf, OWLNothing, OWLThing, \ + OWLClassExpression +from owlapy.iri import IRI +from owlapy.owl_axiom import OWLDisjointClassesAxiom, OWLDeclarationAxiom, OWLClassAssertionAxiom, OWLSubClassOfAxiom, \ + OWLEquivalentClassesAxiom, OWLSubDataPropertyOfAxiom, OWLSubObjectPropertyOfAxiom +from owlapy.owl_individual import OWLNamedIndividual +from owlapy.owl_literal import OWLBottomObjectProperty, OWLTopObjectProperty, OWLBottomDataProperty, OWLTopDataProperty, \ + OWLLiteral +from owlapy.owl_ontology_manager import OntologyManager +from owlapy.owl_property import OWLDataProperty, OWLObjectProperty +from owlapy.owl_reasoner import SyncReasoner +from owlapy.providers import owl_datatype_min_inclusive_restriction + +NS = 'http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#' + +a = OWLNamedIndividual(IRI(NS, "a")) +b = OWLNamedIndividual(IRI(NS, "b")) +c = OWLNamedIndividual(IRI(NS, "c")) +d = OWLNamedIndividual(IRI(NS, "d")) +e = OWLNamedIndividual(IRI(NS, "e")) +g = OWLNamedIndividual(IRI(NS, "g")) +m = OWLNamedIndividual(IRI(NS, "m")) +l = OWLNamedIndividual(IRI(NS, "l")) # noqa: E741 +n = OWLNamedIndividual(IRI(NS, "n")) +o = OWLNamedIndividual(IRI(NS, "o")) +p = OWLNamedIndividual(IRI(NS, "p")) +q = OWLNamedIndividual(IRI(NS, "q")) +r = OWLNamedIndividual(IRI(NS, "r")) +s = OWLNamedIndividual(IRI(NS, "s")) +ind1 = OWLNamedIndividual(IRI(NS, "ind1")) + +r1 = OWLObjectProperty(IRI(NS, "r1")) +r2 = OWLObjectProperty(IRI(NS, "r2")) +r3 = OWLObjectProperty(IRI(NS, "r3")) +r4 = OWLObjectProperty(IRI(NS, "r4")) +r5 = OWLObjectProperty(IRI(NS, "r5")) +r6 = OWLObjectProperty(IRI(NS, "r6")) +r7 = OWLObjectProperty(IRI(NS, "r7")) + +dp1 = OWLDataProperty(IRI(NS, "dp1")) +dp2 = OWLDataProperty(IRI(NS, "dp2")) +dp3 = OWLDataProperty(IRI(NS, "dp3")) + +A = OWLClass(IRI(NS, 'A')) +B = OWLClass(IRI(NS, 'B')) +C = OWLClass(IRI(NS, 'C')) +AB = OWLClass(IRI(NS, 'AB')) +D = OWLClass(IRI(NS, 'D')) +E = OWLClass(IRI(NS, 'E')) +F = OWLClass(IRI(NS, 'F')) +G = OWLClass(IRI(NS, 'G')) +J = OWLClass(IRI(NS, 'J')) +K = OWLClass(IRI(NS, 'K')) +H = OWLClass(IRI(NS, 'H')) +I = OWLClass(IRI(NS, 'I')) # noqa: E741 +L = OWLClass(IRI(NS, 'L')) +M = OWLClass(IRI(NS, 'M')) +N = OWLClass(IRI(NS, 'N')) +O = OWLClass(IRI(NS, 'O')) # noqa: E741 +P = OWLClass(IRI(NS, 'P')) +Q = OWLClass(IRI(NS, 'Q')) +R = OWLClass(IRI(NS, 'R')) +S = OWLClass(IRI(NS, 'S')) +T = OWLClass(IRI(NS, 'T')) +U = OWLClass(IRI(NS, 'U')) +reasoner2 = SyncReasoner("KGs/Test/test_ontology.owl") + +class TestSyncReasoner(unittest.TestCase): + ns = "http://dl-learner.org/mutagenesis#" + ontology_path = "KGs/Mutagenesis/mutagenesis.owl" + nitrogen38 = OWLClass(IRI.create(ns, "Nitrogen-38")) + compound = OWLClass(IRI.create(ns, "Compound")) + atom = OWLClass(IRI.create(ns, "Atom")) + charge = OWLDataProperty(IRI.create(ns, "charge")) + hasAtom = OWLObjectProperty(IRI.create(ns, "hasAtom")) + d100_25 = OWLNamedIndividual(IRI.create(ns, "d100_25")) + has_charge_more_than_0_85 = OWLDataSomeValuesFrom(charge, owl_datatype_min_inclusive_restriction(0.85)) + ce = OWLObjectIntersectionOf([nitrogen38, has_charge_more_than_0_85]) + reasoner = SyncReasoner(ontology_path) + + def test_consistency_check(self): + self.assertEqual(self.reasoner.has_consistent_ontology(), True) + + def test_named_concepts(self): + + ontology_path = "KGs/Family/family-benchmark_rich_background.owl" + + # Available OWL Reasoners: 'HermiT', 'Pellet', 'JFact', 'Openllet' + owl_reasoners = dict() + owl_reasoners["HermiT"] = SyncReasoner(ontology=ontology_path, reasoner="HermiT") + owl_reasoners["Pellet"] = SyncReasoner(ontology=ontology_path, reasoner="Pellet") + owl_reasoners["JFact"] = SyncReasoner(ontology=ontology_path, reasoner="JFact") + owl_reasoners["Openllet"] = SyncReasoner(ontology=ontology_path, reasoner="Openllet") + + onto = OntologyManager().load_ontology(ontology_path) + + def compute_agreements(i: OWLClassExpression, verbose=False): + if verbose: + print(f"Computing agreements between Reasoners on {i}...") + retrieval_result = None + flag = False + for __, reasoner in owl_reasoners.items(): + if retrieval_result: + flag = retrieval_result == {_.str for _ in reasoner.instances(i)} + else: + retrieval_result = {_.str for _ in reasoner.instances(i)} + return flag + + # Agreement between instances over + for i in onto.classes_in_signature(): + assert compute_agreements(i, True) + + def test_inconsistency_check(self): + manager = OntologyManager() + onto = manager.load_ontology(IRI.create(self.ontology_path)) + + carbon230 = OWLClass(IRI.create(self.ns, "Carbon-230")) + axiom = OWLDisjointClassesAxiom([self.nitrogen38, carbon230]) + onto.add_axiom(axiom) + new_individual = OWLNamedIndividual(IRI.create(self.ns, "testIndividual")) + onto.add_axiom(OWLDeclarationAxiom(new_individual)) + onto.add_axiom(OWLClassAssertionAxiom(new_individual, self.nitrogen38)) + onto.add_axiom(OWLClassAssertionAxiom(new_individual, carbon230)) + + onto.save(IRI.create("file:/test.owl")) + reasoner = SyncReasoner("test.owl") + self.assertEqual(reasoner.has_consistent_ontology(), False) + os.remove("test.owl") + + def test_instances_retrieval(self): + instances = self.reasoner.instances(self.ce) + expected = [OWLNamedIndividual(IRI('http://dl-learner.org/mutagenesis#', 'd141_10')), + OWLNamedIndividual(IRI('http://dl-learner.org/mutagenesis#', 'd195_12')), + OWLNamedIndividual(IRI('http://dl-learner.org/mutagenesis#', 'd144_10')), + OWLNamedIndividual(IRI('http://dl-learner.org/mutagenesis#', 'd147_11')), + OWLNamedIndividual(IRI('http://dl-learner.org/mutagenesis#', 'e18_9')), + OWLNamedIndividual(IRI('http://dl-learner.org/mutagenesis#', 'd175_17')), + OWLNamedIndividual(IRI('http://dl-learner.org/mutagenesis#', 'e16_9'))] + # Assert equal without considering the order + for instance in instances: + self.assertIn(instance, expected) + self.assertEqual(len(list(instances)), len(expected)) + + def test_conversion(self): + # construct the class expression in owlapi + from org.semanticweb.owlapi.model import IRI as IRIowlapi + from org.semanticweb.owlapi.vocab import OWLFacet + + nitrogenIRI = IRIowlapi.create(self.ns + "Nitrogen-38") + charge_iri = IRIowlapi.create(self.ns + "charge") + + data_factory = self.reasoner.manager.get_owlapi_manager().getOWLDataFactory() + nitrogen_class = data_factory.getOWLClass(nitrogenIRI) + + charge_property = data_factory.getOWLDataProperty(charge_iri) + double_datatype = data_factory.getDoubleOWLDatatype() + facet_restriction = data_factory.getOWLFacetRestriction(OWLFacet.MIN_INCLUSIVE, JDouble(0.85)) + datatype_restriction = data_factory.getOWLDatatypeRestriction(double_datatype, facet_restriction) + some_values_from = data_factory.getOWLDataSomeValuesFrom(charge_property, datatype_restriction) + + class_expression = data_factory.getOWLObjectIntersectionOf(nitrogen_class, some_values_from) + + # compare them with the converted expression + ce_converted = self.reasoner.mapper.map_(self.ce) + print(ce_converted) + print(class_expression) + self.assertEqual(class_expression, ce_converted) + + # convert back to owlapy and check for equality + ce_1 = self.reasoner.mapper.map_(class_expression) + ce_2 = self.reasoner.mapper.map_(ce_converted) + + self.assertEqual(ce_1, ce_2) + self.assertEqual(ce_1, self.ce) + self.assertEqual(ce_2, self.ce) + + def test_equivalent_classes(self): + self.assertCountEqual(list(reasoner2.equivalent_classes(N)), [N, Q]) + + def test_disjoint_classes(self): + self.assertCountEqual(list(reasoner2.disjoint_classes(L)), [M, OWLNothing]) + + def test_sub_classes(self): + self.assertCountEqual(list(reasoner2.sub_classes(P)), [O, OWLNothing]) + + def test_super_classes(self): + self.assertCountEqual(list(reasoner2.super_classes(O)), [P, OWLThing]) + + def test_object_property_domains(self): + self.assertCountEqual(list(self.reasoner.object_property_domains(self.hasAtom, False)), [self.compound, OWLThing]) + self.assertCountEqual(list(self.reasoner.object_property_domains(self.hasAtom, True)), [self.compound]) + + def test_object_property_ranges(self): + self.assertCountEqual(list(reasoner2.object_property_ranges(r1, False)), [OWLThing, G]) + self.assertCountEqual(list(reasoner2.object_property_ranges(r1, True)), [G]) + + def test_sub_object_properties(self): + self.assertCountEqual(list(reasoner2.sub_object_properties(r1, False)), [r2, OWLBottomObjectProperty]) + self.assertCountEqual(list(reasoner2.sub_object_properties(r1, True)), [r2]) + + def test_super_object_properties(self): + self.assertCountEqual(list(reasoner2.super_object_properties(r2, False)), [r1, OWLTopObjectProperty]) + self.assertCountEqual(list(reasoner2.super_object_properties(r2, True)), [r1]) + + def test_sub_data_properties(self): + self.assertCountEqual(list(reasoner2.sub_data_properties(dp1, False)), [dp2, OWLBottomDataProperty]) + self.assertCountEqual(list(reasoner2.sub_data_properties(dp1, True)), [dp2]) + + def test_super_data_properties(self): + self.assertCountEqual(list(reasoner2.super_data_properties(dp2, False)), [dp1, OWLTopDataProperty]) + self.assertCountEqual(list(reasoner2.super_data_properties(dp2, True)), [dp1]) + + def test_different_individuals(self): + self.assertCountEqual(list(reasoner2.different_individuals(l)), [m]) + self.assertCountEqual(list(reasoner2.different_individuals(m)), [l]) + + def test_object_property_values(self): + self.assertCountEqual(list(reasoner2.object_property_values(n, r3)), [q]) + self.assertCountEqual(list(reasoner2.object_property_values(n, r4)), [l, q]) + + def test_data_property_values(self): + self.assertCountEqual(list(self.reasoner.data_property_values(self.d100_25, self.charge)), [OWLLiteral(0.332)]) + + def test_disjoint_object_properties(self): + self.assertCountEqual(list(reasoner2.disjoint_object_properties(r5)), [r1, r2, OWLBottomObjectProperty]) + self.assertCountEqual(list(reasoner2.disjoint_object_properties(r1)), [r5, OWLBottomObjectProperty]) + self.assertCountEqual(list(reasoner2.disjoint_object_properties(r2)), [r5, OWLBottomObjectProperty]) + + def test_disjoint_data_properties(self): + self.assertCountEqual(list(reasoner2.disjoint_data_properties(dp1)), [dp3, OWLBottomDataProperty]) + self.assertCountEqual(list(reasoner2.disjoint_data_properties(dp3)), [dp1,dp2, OWLBottomDataProperty]) + + def test_types(self): + self.assertCountEqual(list(reasoner2.types(c)), [I, J, K, OWLThing]) + + def test_infer_axiom(self): + + self.assertCountEqual(list(reasoner2.infer_axioms(["InferredClassAssertionAxiomGenerator", "InferredSubClassAxiomGenerator", + "InferredDisjointClassesAxiomGenerator", "InferredEquivalentClassAxiomGenerator", + "InferredEquivalentDataPropertiesAxiomGenerator","InferredEquivalentObjectPropertyAxiomGenerator", + "InferredInverseObjectPropertiesAxiomGenerator","InferredSubDataPropertyAxiomGenerator", + "InferredSubObjectPropertyAxiomGenerator","InferredDataPropertyCharacteristicAxiomGenerator", + "InferredObjectPropertyCharacteristicAxiomGenerator" + ])), [ OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','f')),class_expression=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','l')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','L')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','m')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','M')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','a')),class_expression=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','e')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','C')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','s')),class_expression=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','n')),class_expression=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','p')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','P')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','a')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','AB')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','q')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','Q')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','a')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','B')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','n')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','N')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','c')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','K')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','e')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','A')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','ind1')),class_expression=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','d')),class_expression=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','o')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','O')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','c')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','I')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','r')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','R')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','q')),class_expression=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','l')),class_expression=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','s')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','S')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','f')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','E')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','g')),class_expression=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','b')),class_expression=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','n')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','Q')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','e')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','B')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','a')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','C')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','o')),class_expression=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','o')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','P')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','ind1')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','H')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','a')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','A')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','e')),class_expression=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','ind1')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','F')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','s')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','T')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','r')),class_expression=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','e')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','AB')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','m')),class_expression=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','h')),class_expression=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','d')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','D')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','c')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','J')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','b')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','B')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','c')),class_expression=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','d')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','B')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','p')),class_expression=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','q')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','N')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','g')),class_expression=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','G')),annotations=[]), + OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','AB')),super_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','A')),annotations=[]), + OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','I')),super_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','K')),annotations=[]), + OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','N')),super_class=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','Q')),super_class=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','A')),super_class=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','D')),super_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','B')),annotations=[]), + OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','AB')),super_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','C')),annotations=[]), + OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','K')),super_class=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','B')),super_class=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','G')),super_class=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','O')),super_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','P')),annotations=[]), + OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','L')),super_class=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','S')),super_class=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','F')),super_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','H')),annotations=[]), + OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','AB')),super_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','B')),annotations=[]), + OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','E')),super_class=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','J')),super_class=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','U')),super_class=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','I')),super_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','J')),annotations=[]), + OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','T')),super_class=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','P')),super_class=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','C')),super_class=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','H')),super_class=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','M')),super_class=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','R')),super_class=OWLClass(IRI('http://www.w3.org/2002/07/owl#','Thing')),annotations=[]), + OWLDisjointClassesAxiom([OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','D')), OWLClass(IRI('http://www.w3.org/2002/07/owl#','Nothing'))],[]), + OWLDisjointClassesAxiom([OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','Q')), OWLClass(IRI('http://www.w3.org/2002/07/owl#','Nothing'))],[]), + OWLDisjointClassesAxiom([OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','J')), OWLClass(IRI('http://www.w3.org/2002/07/owl#','Nothing'))],[]), + OWLDisjointClassesAxiom([OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','E')), OWLClass(IRI('http://www.w3.org/2002/07/owl#','Nothing'))],[]), + OWLDisjointClassesAxiom([OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','K')), OWLClass(IRI('http://www.w3.org/2002/07/owl#','Nothing'))],[]), + OWLDisjointClassesAxiom([OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','L')), OWLClass(IRI('http://www.w3.org/2002/07/owl#','Nothing'))],[]), + OWLDisjointClassesAxiom([OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','R')), OWLClass(IRI('http://www.w3.org/2002/07/owl#','Nothing'))],[]), + OWLDisjointClassesAxiom([OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','AB')), OWLClass(IRI('http://www.w3.org/2002/07/owl#','Nothing'))],[]), + OWLDisjointClassesAxiom([OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','S')), OWLClass(IRI('http://www.w3.org/2002/07/owl#','Nothing'))],[]), + OWLDisjointClassesAxiom([OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','M')), OWLClass(IRI('http://www.w3.org/2002/07/owl#','Nothing'))],[]), + OWLDisjointClassesAxiom([OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','F')), OWLClass(IRI('http://www.w3.org/2002/07/owl#','Nothing'))],[]), + OWLDisjointClassesAxiom([OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','A')), OWLClass(IRI('http://www.w3.org/2002/07/owl#','Nothing'))],[]), + OWLDisjointClassesAxiom([OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','G')), OWLClass(IRI('http://www.w3.org/2002/07/owl#','Nothing'))],[]), + OWLDisjointClassesAxiom([OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','T')), OWLClass(IRI('http://www.w3.org/2002/07/owl#','Nothing'))],[]), + OWLDisjointClassesAxiom([OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','H')), OWLClass(IRI('http://www.w3.org/2002/07/owl#','Nothing'))],[]), + OWLDisjointClassesAxiom([OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','N')), OWLClass(IRI('http://www.w3.org/2002/07/owl#','Nothing'))],[]), + OWLDisjointClassesAxiom([OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','U')), OWLClass(IRI('http://www.w3.org/2002/07/owl#','Nothing'))],[]), + OWLDisjointClassesAxiom([OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','L')), OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','M'))],[]), + OWLDisjointClassesAxiom([OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','I')), OWLClass(IRI('http://www.w3.org/2002/07/owl#','Nothing'))],[]), + OWLDisjointClassesAxiom([OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','B')), OWLClass(IRI('http://www.w3.org/2002/07/owl#','Nothing'))],[]), + OWLDisjointClassesAxiom([OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','O')), OWLClass(IRI('http://www.w3.org/2002/07/owl#','Nothing'))],[]), + OWLDisjointClassesAxiom([OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','P')), OWLClass(IRI('http://www.w3.org/2002/07/owl#','Nothing'))],[]), + OWLDisjointClassesAxiom([OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','C')), OWLClass(IRI('http://www.w3.org/2002/07/owl#','Nothing'))],[]), + OWLEquivalentClassesAxiom([OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','N')), OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','Q'))],[]), + OWLSubDataPropertyOfAxiom(sub_property=OWLDataProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','dp2')),super_property=OWLDataProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','dp1')),annotations=[]), + OWLSubDataPropertyOfAxiom(sub_property=OWLDataProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','dp3')),super_property=OWLDataProperty(IRI('http://www.w3.org/2002/07/owl#','topDataProperty')),annotations=[]), + OWLSubDataPropertyOfAxiom(sub_property=OWLDataProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','dp1')),super_property=OWLDataProperty(IRI('http://www.w3.org/2002/07/owl#','topDataProperty')),annotations=[]), + OWLSubObjectPropertyOfAxiom(sub_property=OWLObjectProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','r5')),super_property=OWLObjectProperty(IRI('http://www.w3.org/2002/07/owl#','topObjectProperty')),annotations=[]), + OWLSubObjectPropertyOfAxiom(sub_property=OWLObjectProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','r2')),super_property=OWLObjectProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','r1')),annotations=[]), + OWLSubObjectPropertyOfAxiom(sub_property=OWLObjectProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','r7')),super_property=OWLObjectProperty(IRI('http://www.w3.org/2002/07/owl#','topObjectProperty')),annotations=[]), + OWLSubObjectPropertyOfAxiom(sub_property=OWLObjectProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','r4')),super_property=OWLObjectProperty(IRI('http://www.w3.org/2002/07/owl#','topObjectProperty')),annotations=[]), + OWLSubObjectPropertyOfAxiom(sub_property=OWLObjectProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','r1')),super_property=OWLObjectProperty(IRI('http://www.w3.org/2002/07/owl#','topObjectProperty')),annotations=[]), + OWLSubObjectPropertyOfAxiom(sub_property=OWLObjectProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','r3')),super_property=OWLObjectProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','r4')),annotations=[]), + OWLSubObjectPropertyOfAxiom(sub_property=OWLObjectProperty(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','r6')),super_property=OWLObjectProperty(IRI('http://www.w3.org/2002/07/owl#','topObjectProperty')),annotations=[])]) \ No newline at end of file