diff --git a/README.md b/README.md index a7ca34b0..352faa24 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.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) +[![Pypi](https://img.shields.io/badge/pypi-1.3.1-blue)](https://pypi.org/project/owlapy/1.3.1/) +[![Docs](https://img.shields.io/badge/documentation-1.3.1-yellow)](https://dice-group.github.io/owlapy/usage/main.html) ![OWLAPY](docs/_static/images/owlapy_logo.png) @@ -25,11 +25,10 @@ pip3 install owlapy ```shell # To download RDF knowledge graphs wget https://files.dice-research.org/projects/Ontolearn/KGs.zip -O ./KGs.zip && unzip KGs.zip -pytest -p no:warnings -x # Running 102 tests takes ~ 1 min +pytest -p no:warnings -x # Running 142 tests ~ 30 secs ``` -## Usage - +## Examples ### Creating OWL Class Expressions
Click me! @@ -91,7 +90,7 @@ from owlapy.static_funcs import stopJVM ontology_path = "KGs/Family/family-benchmark_rich_background.owl" # Available OWL Reasoners: 'HermiT', 'Pellet', 'JFact', 'Openllet' -reasoner = SyncReasoner(ontology = ontology_path, reasoner="Pellet") +sync_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(): diff --git a/docs/conf.py b/docs/conf.py index dedc20f6..2c38198a 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -13,7 +13,7 @@ project = 'OWLAPY' author = 'Ontolearn Team' -release = '1.3.0' +release = '1.3.1' # -- 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 7692a88f..2cf8cac5 100644 --- a/docs/usage/main.md +++ b/docs/usage/main.md @@ -1,6 +1,6 @@ # About owlapy -**Version:** owlapy 1.3.0 +**Version:** owlapy 1.3.1 **GitHub repository:** [https://github.com/dice-group/owlapy](https://github.com/dice-group/owlapy) diff --git a/examples/comparing_adaptor_reasoners.py b/examples/comparing_adaptor_reasoners.py deleted file mode 100644 index 25222601..00000000 --- a/examples/comparing_adaptor_reasoners.py +++ /dev/null @@ -1,45 +0,0 @@ -from owlapy.owl_property import OWLObjectProperty -from owlapy.owl_reasoner import SyncReasoner -from owlapy.iri import IRI -from owlapy.class_expression import OWLClass, OWLObjectAllValuesFrom, OWLObjectComplementOf -import time -ontology_location = "../KGs/Carcinogenesis/carcinogenesis.owl" - -i1 = set() -i2 = set() -i3 = set() -i4 = set() - -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 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] -time.sleep(10) -print("Hermit-JFact:") -[print(_) for _ in i1-i3] -time.sleep(10) -print("Hermit-Openllet:") -[print(_) for _ in i1-i4] -time.sleep(10) -print("Pellet-JFact:") -[print(_) for _ in i2-i3] -time.sleep(10) -print("Pellet-Openllet:") -[print(_) for _ in i2-i4] -time.sleep(10) -print("JFact-Openllet:") -[print(_) for _ in i3-i4] \ No newline at end of file diff --git a/examples/ontology_engineering.py b/examples/ontology_engineering.py new file mode 100644 index 00000000..d404f2a0 --- /dev/null +++ b/examples/ontology_engineering.py @@ -0,0 +1,51 @@ +from owlapy.class_expression import OWLClass +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 +from owlapy.static_funcs import download_external_files +# (1) Download the datasets if KGs does not exist. +download_external_files("https://files.dice-research.org/projects/Ontolearn/KGs.zip") +# (2) Load the father ontology using a new ontology manager. +onto = OntologyManager().load_ontology(path='file://../KGs/Family/father.owl') +# (3) Iterate over defined OWL classes, object properties. +print("OWL Classes:") +for c in onto.classes_in_signature(): + print(c) +print("\nOWL Properties:") +for p in onto.properties_in_signature(): + print(p) +print("\nOWL Individuals:") +for i in onto.individuals_in_signature(): + print(i) +print("\nOWL Class Axioms:") +for a in onto.general_class_axioms(): + print(a) +""" +@TODO: Will be implemented. +for a in onto.tbox_axioms(): + print(a) +for a in onto.abox_axioms_between_individuals(): + print(a) +for a in onto.abox_axioms_between_individuals_and_classes(): + print(a) +""" +# (4) Create a new class (father) +father = OWLClass(IRI.create('http://example.com/father#child')) +# (5) Add a declaration axiom for this class, +onto.add_axiom(axiom=OWLDeclarationAxiom(father)) +# (6) Check whether the newly defined class is in the signature. +assert father in [ c for c in onto.classes_in_signature()] +# (7) Iterate over owl individuals +print("\nOWL Individuals:") +for i in onto.individuals_in_signature(): + print(i) +# (8) Create an owl individual and used it in an axiom. +cdemir = OWLNamedIndividual('http://example.com/father#cdemir') +onto.add_axiom(OWLClassAssertionAxiom(cdemir, father)) +# (9) Check whether cdemir is in the signature. +assert cdemir in [ c for c in onto.individuals_in_signature()] +# (10) Save the modified ontology locally. +onto.save(path="babo.owl",rdf_format = "rdfxml") + + diff --git a/examples/ontology_modification.py b/examples/ontology_modification.py deleted file mode 100644 index 577e2c1d..00000000 --- a/examples/ontology_modification.py +++ /dev/null @@ -1,46 +0,0 @@ -from owlapy.class_expression import OWLClass -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 -from owlapy.static_funcs import download_external_files - -# Download the datasets if KGs does not exist. -download_external_files("https://files.dice-research.org/projects/Ontolearn/KGs.zip") - -# Load the 'father' ontology using a new ontology manager. -manager = OntologyManager() -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()] - -# Create a new class -new_class = OWLClass(IRI.create('http://example.com/father#child')) - -# 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("------------------------") -[print(_) for _ in onto.classes_in_signature()] - -# Add an individual of type child in the ontology -new_ind = OWLNamedIndividual('http://example.com/father#lulu') -onto.add_axiom(OWLClassAssertionAxiom(new_ind, new_class)) - -# Check if Lulu is added - -print("----------------------") -[print(_) for _ in onto.individuals_in_signature()] - -# Save the modified ontology locally (otherwise the changes will be lost) -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. - - -""" -You can also remove axioms by using manage.remove_axiom. -There are countless axioms which you can add or remove from an ontology. -Check them here: https://dice-group.github.io/owlapy/autoapi/owlapy/owl_axiom/index.html -""" \ No newline at end of file diff --git a/examples/ontology_reasoning_retrieval.py b/examples/ontology_reasoning_retrieval.py new file mode 100644 index 00000000..414de2b0 --- /dev/null +++ b/examples/ontology_reasoning_retrieval.py @@ -0,0 +1,39 @@ +""" + +KB = { (A subclass B), (B subclass C), (x type A) } + +KB = { (A subclass B), (B subclass C), (x type A), (x type B), (x type C) } + +Missing types are inferred due to subclass hierarchy. +""" +from owlapy.class_expression import OWLClass +from owlapy.owl_axiom import OWLDeclarationAxiom, OWLClassAssertionAxiom, OWLSubClassOfAxiom +from owlapy.owl_ontology_manager import OntologyManager +from owlapy.owl_individual import OWLNamedIndividual +from owlapy.iri import IRI +from owlapy.owl_reasoner import SyncReasoner +# () Define a base IRI. +base_iri = IRI(namespace="https://github.com/dice-group/owlapy#") +# () Create an empty ontology. +onto = OntologyManager().create_ontology(iri=base_iri) +# () Define classes and individuals. +A = OWLClass(iri=base_iri.get_namespace() + "A") +B = OWLClass(iri=base_iri.get_namespace() + "B") +C = OWLClass(iri=base_iri.get_namespace() + "C") +x = OWLNamedIndividual(iri=base_iri.get_namespace() + "x") +# () Add axioms. +onto.add_axiom([OWLDeclarationAxiom(A), + OWLDeclarationAxiom(B), + OWLDeclarationAxiom(C), + OWLDeclarationAxiom(x), + OWLSubClassOfAxiom(A,B), + OWLSubClassOfAxiom(B,C), + OWLClassAssertionAxiom(x,A)]) +# () Save axioms [ (A subclass B), (B subclass C), (x type A) ]. +onto.save("new_ontology.owl") +# () Initialize reasoner. +reasoner = SyncReasoner(ontology="new_ontology.owl", reasoner="Pellet") +# () Infer instances. +for i in reasoner.ontology.classes_in_signature(): + print(f"Retrieve {i}:",end=" ") + print(" ".join( [_.str for _ in reasoner.instances(i)])) \ No newline at end of file diff --git a/examples/ontology_reasoning_retrieval_agreements.py b/examples/ontology_reasoning_retrieval_agreements.py new file mode 100644 index 00000000..ca66d1e3 --- /dev/null +++ b/examples/ontology_reasoning_retrieval_agreements.py @@ -0,0 +1,33 @@ +from owlapy.owl_reasoner import SyncReasoner +from owlapy import OntologyManager +from owlapy.class_expression import OWLClassExpression +from typing import Dict +ontology_path = "../KGs/Family/family-benchmark_rich_background.owl" +# () Load ontology +onto = OntologyManager().load_ontology(ontology_path) + +# () Initialize Reasoners +reasoners = dict() +reasoners["HermiT"] = SyncReasoner(ontology=ontology_path, reasoner="HermiT") +reasoners["Pellet"] = SyncReasoner(ontology=ontology_path, reasoner="Pellet") +reasoners["JFact"] = SyncReasoner(ontology=ontology_path, reasoner="JFact") +reasoners["Openllet"] = SyncReasoner(ontology=ontology_path, reasoner="Openllet") + +def compute_agreements(owl_reasoners:Dict[str,SyncReasoner], expression: OWLClassExpression, verbose=False): + if verbose: + print(f"Computing agreements between Reasoners on {expression}...",end="\t") + retrieval_result = None + flag = False + for __, reasoner in owl_reasoners.items(): + if retrieval_result: + flag = retrieval_result == {_.str for _ in reasoner.instances(expression)} + else: + retrieval_result = {_.str for _ in reasoner.instances(expression)} + if verbose: + print(f"Successful:{flag}") + return flag + +# () Iterate over named classes +for c in onto.classes_in_signature(): + # reasoners must agree + assert compute_agreements(reasoners, c, True) \ No newline at end of file diff --git a/examples/owl_reasoners.py b/examples/ontology_reasoning_retrieval_agreements_and_runtimes.py similarity index 56% rename from examples/owl_reasoners.py rename to examples/ontology_reasoning_retrieval_agreements_and_runtimes.py index 90584e24..6f6df662 100644 --- a/examples/owl_reasoners.py +++ b/examples/ontology_reasoning_retrieval_agreements_and_runtimes.py @@ -1,58 +1,28 @@ """ 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.class_expression import (OWLClass, OWLObjectSomeValuesFrom, OWLObjectAllValuesFrom, + OWLObjectIntersectionOf, OWLObjectUnionOf) from owlapy.owl_ontology_manager import OntologyManager from owlapy.owl_reasoner import SyncReasoner -from owlapy.utils import concept_reducer_properties - +from owlapy.utils import concept_reducer_properties, concept_reducer +from owlapy import owl_expression_to_dl import matplotlib.pyplot as plt import seaborn as sns import numpy as np +from tqdm import tqdm -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)) +def eval_reasoners(iter_owl_exp, mapping,info:str=""): results = dict() runtime_results = dict() - for c in iter_owl_exp: + for c in (tqdm_bar:=tqdm(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) + + tqdm_bar.set_description_str(f"{owl_expression_to_dl(c)}\t") + for name_j, reasoner_j in mapping.items(): if name_i == name_j: continue # Skip self-comparison @@ -108,17 +78,57 @@ def plot_similarity_btw_reasoners(results): plt.ylabel('Reasoner') plt.show() +ontology_path = "../KGs/Family/father.owl" -# EVAL Named Concepts +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 +# () C: OWL Class. +c = {i for i in onto.classes_in_signature()} +similarity_results, average_runtime_owl_reasoners = eval_reasoners(c, owl_reasoners) +plot_similarity_btw_reasoners(similarity_results) +plot_runtimes(average_runtime_owl_reasoners, title="Avg Runtime of Reasoners on Classes") + +# () Negated OWL CLasses. +nc = {i.get_object_complement_of() for i in c} 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_runtimes(average_runtime_owl_reasoners, title="Avg Runtime of Reasoners on Negated Classes") +# () Intersection of OWL Classes +intersections_classes = concept_reducer(c, opt=OWLObjectIntersectionOf) +similarity_results, average_runtime_owl_reasoners = eval_reasoners(intersections_classes, owl_reasoners) plot_similarity_btw_reasoners(similarity_results) -plot_runtimes(average_runtime_owl_reasoners, title="Avg Runtime of Reasoners on Negated Named Concepts") +plot_runtimes(average_runtime_owl_reasoners, title="Avg Runtime of Reasoners on Intersection of Classes") +# () Union of OWL Classes +unions_classes = concept_reducer(c, opt=OWLObjectUnionOf) +similarity_results, average_runtime_owl_reasoners = eval_reasoners(unions_classes, owl_reasoners) +plot_similarity_btw_reasoners(similarity_results) +plot_runtimes(average_runtime_owl_reasoners, title="Avg Runtime of Reasoners on Union of Classes") +# () Object Property Restrictions - Existential Quantification +object_properties = {i for i in onto.object_properties_in_signature()} +exist_c = concept_reducer_properties(concepts=c, properties=object_properties, cls=OWLObjectSomeValuesFrom) +similarity_results, average_runtime_owl_reasoners = eval_reasoners(exist_c, owl_reasoners) +plot_similarity_btw_reasoners(similarity_results) +plot_runtimes(average_runtime_owl_reasoners, title="Avg Runtime of Reasoners on Existential Quantifiers") +# () Object Property Restrictions - Universal Quantification +forall_c = concept_reducer_properties(concepts=c, properties=object_properties, cls=OWLObjectAllValuesFrom) +similarity_results, average_runtime_owl_reasoners = eval_reasoners(forall_c, owl_reasoners) +plot_similarity_btw_reasoners(similarity_results) +plot_runtimes(average_runtime_owl_reasoners, title="Avg Runtime of Reasoners on Universal Quantifiers") -# EVAL Exist R. NC -similarity_results, average_runtime_owl_reasoners = eval_reasoners(exist_nc, owl_reasoners) +# () Object Property Restrictions - Existential Quantification +inverse_object_properties = {i.get_inverse_property() for i in onto.object_properties_in_signature()} +exist_inverse_c = concept_reducer_properties(concepts=c, properties=inverse_object_properties, cls=OWLObjectSomeValuesFrom) +similarity_results, average_runtime_owl_reasoners = eval_reasoners(exist_inverse_c, owl_reasoners) +plot_similarity_btw_reasoners(similarity_results) +plot_runtimes(average_runtime_owl_reasoners, title="Avg Runtime of Reasoners on Existential Quantifiers with inverse properties") +# () Object Property Restrictions - Universal Quantification +forall_inverse_c = concept_reducer_properties(concepts=c, properties=inverse_object_properties, cls=OWLObjectAllValuesFrom) +similarity_results, average_runtime_owl_reasoners = eval_reasoners(forall_inverse_c, owl_reasoners) plot_similarity_btw_reasoners(similarity_results) -plot_runtimes(average_runtime_owl_reasoners, title="Avg Runtime of Reasoners on OWLObjectSomeValuesFrom") +plot_runtimes(average_runtime_owl_reasoners, title="Avg Runtime of Reasoners on Universal Quantifiers with inverse properties") + diff --git a/examples/parse_expressions.py b/examples/parse_expressions.py index 2a205b64..5d9a6037 100644 --- a/examples/parse_expressions.py +++ b/examples/parse_expressions.py @@ -1,11 +1,9 @@ -from owlapy import dl_to_owl_expression, manchester_to_owl_expression - +from owlapy import dl_to_owl_expression, manchester_to_owl_expression, owl_expression_to_sparql # Define the namespace of your ontology namespace = "http://example.com/family#" - -# Convert dl or manchester expressions (as string) to owl expressions. +# Map a description logic concept into OWLClassExpression object. print(dl_to_owl_expression("∃ hasChild.male", namespace)) - +# Map an OWL class expression in the manchester syntax to into OWLClassExpression object . print(manchester_to_owl_expression("female and (hasChild max 2 person)", namespace)) - -# It's that simple :) +# Map a description logic concept into OWLClassExpression object and then to SPARQL query +print(owl_expression_to_sparql(dl_to_owl_expression("∃ hasChild.male", namespace))) \ No newline at end of file diff --git a/examples/quality_of_owl_ce_via_sparql.py b/examples/quality_of_owl_ce_via_sparql.py new file mode 100644 index 00000000..ea9603f5 --- /dev/null +++ b/examples/quality_of_owl_ce_via_sparql.py @@ -0,0 +1,23 @@ +""" +The confusion matrix indicating the quality of an OWL Class Expression can be computed through SPARQL. +By this, we avoid the process of retrieving instances of an OWL Class Expression, hence, accelerate the learning process +""" +from owlapy import owl_expression_to_sparql_with_confusion_matrix +from owlapy.owl_individual import OWLNamedIndividual +from owlapy.class_expression import OWLClass +import requests + +pos={OWLNamedIndividual('http://dbpedia.org/resource/George_Montagu_(naturalist)'), OWLNamedIndividual('http://dbpedia.org/resource/Andrei_Monin'), OWLNamedIndividual('http://dbpedia.org/resource/Joe_Bastardi')} +neg={OWLNamedIndividual('http://dbpedia.org/resource/James_M._Bower'), OWLNamedIndividual('http://dbpedia.org/resource/Shirley_Meng'), OWLNamedIndividual('http://dbpedia.org/resource/Betsy_Weatherhead')} +response = requests.post("https://dbpedia-2022-12.data.dice-research.org/sparql", data={"query": owl_expression_to_sparql_with_confusion_matrix(expression=OWLClass('http://dbpedia.org/ontology/Person'),positive_examples=pos,negative_examples=neg)}) +for res in response.json()["results"]["bindings"]: + for k,v in res.items(): + print(k,eval(v["value"])) + + +""" +tp 3.0 +fn 0.0 +fp 3.0 +tn 0.0 +""" \ No newline at end of file diff --git a/owlapy/__init__.py b/owlapy/__init__.py index 4f6b6692..2b3878c9 100644 --- a/owlapy/__init__.py +++ b/owlapy/__init__.py @@ -1,6 +1,13 @@ -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' +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, owl_expression_to_sparql_with_confusion_matrix +from .owl_ontology_manager import OntologyManager + +__version__ = '1.3.1' + +__all__ = [ + 'owl_expression_to_dl', 'owl_expression_to_manchester', + 'dl_to_owl_expression', 'manchester_to_owl_expression', + 'owl_expression_to_sparql', 'owl_expression_to_sparql_with_confusion_matrix', + 'OntologyManager' +] \ No newline at end of file diff --git a/owlapy/abstracts/__init__.py b/owlapy/abstracts/__init__.py index 473f64d3..49b5a7ab 100644 --- a/owlapy/abstracts/__init__.py +++ b/owlapy/abstracts/__init__.py @@ -1,4 +1,5 @@ -from .abstract_owl_ontology_manager import OWLOntologyManager, OWLOntologyChange, OWLOntology -from .abstract_owl_reasoner import OWLReasoner, OWLReasonerEx +from .abstract_owl_ontology_manager import AbstractOWLOntologyManager, AbstractOWLOntologyChange, AbstractOWLOntology +from .abstract_owl_reasoner import AbstractOWLReasoner, AbstractOWLReasonerEx -__all__ = ['OWLOntologyManager', 'OWLOntologyChange', 'OWLOntology', 'OWLReasoner', 'OWLReasonerEx'] +__all__ = ['AbstractOWLOntologyManager', 'AbstractOWLOntologyChange', 'AbstractOWLOntology', 'AbstractOWLReasoner', + 'AbstractOWLReasonerEx'] diff --git a/owlapy/abstracts/abstract_owl_ontology.py b/owlapy/abstracts/abstract_owl_ontology.py index 2b8b5172..1aa6715d 100644 --- a/owlapy/abstracts/abstract_owl_ontology.py +++ b/owlapy/abstracts/abstract_owl_ontology.py @@ -13,7 +13,7 @@ _OI = TypeVar('_OI', bound='OWLOntologyID') # noqa: F821 -class OWLOntology(OWLObject, metaclass=ABCMeta): +class AbstractOWLOntology(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. diff --git a/owlapy/abstracts/abstract_owl_ontology_manager.py b/owlapy/abstracts/abstract_owl_ontology_manager.py index 891b29a8..2b90afb8 100644 --- a/owlapy/abstracts/abstract_owl_ontology_manager.py +++ b/owlapy/abstracts/abstract_owl_ontology_manager.py @@ -1,21 +1,21 @@ from abc import ABCMeta, abstractmethod from typing import Union -from owlapy.abstracts.abstract_owl_ontology import OWLOntology +from owlapy.abstracts.abstract_owl_ontology import AbstractOWLOntology from owlapy.iri import IRI -class OWLOntologyChange(metaclass=ABCMeta): +class AbstractOWLOntologyChange(metaclass=ABCMeta): """Represents an ontology change.""" __slots__ = () - _ont: OWLOntology + _ont: AbstractOWLOntology @abstractmethod - def __init__(self, ontology: OWLOntology): + def __init__(self, ontology: AbstractOWLOntology): self._ont = ontology - def get_ontology(self) -> OWLOntology: + def get_ontology(self) -> AbstractOWLOntology: """Gets the ontology that the change is/was applied to. Returns: @@ -24,12 +24,12 @@ def get_ontology(self) -> OWLOntology: return self._ont -class OWLOntologyManager(metaclass=ABCMeta): +class AbstractOWLOntologyManager(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: + def create_ontology(self, iri: Union[str, IRI]) -> AbstractOWLOntology: """Creates a new (empty) ontology that that has the specified ontology IRI (and no version IRI). Args: @@ -41,7 +41,7 @@ def create_ontology(self, iri: Union[str, IRI]) -> OWLOntology: pass @abstractmethod - def load_ontology(self, iri: Union[IRI, str]) -> OWLOntology: + def load_ontology(self, iri: Union[IRI, str]) -> AbstractOWLOntology: """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. @@ -56,7 +56,7 @@ def load_ontology(self, iri: Union[IRI, str]) -> OWLOntology: pass @abstractmethod - def apply_change(self, change: OWLOntologyChange): + def apply_change(self, change: AbstractOWLOntologyChange): """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. diff --git a/owlapy/abstracts/abstract_owl_reasoner.py b/owlapy/abstracts/abstract_owl_reasoner.py index 1ed1e01a..987c3f2a 100644 --- a/owlapy/abstracts/abstract_owl_reasoner.py +++ b/owlapy/abstracts/abstract_owl_reasoner.py @@ -8,7 +8,7 @@ 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.abstracts.abstract_owl_ontology import AbstractOWLOntology from owlapy.owl_property import OWLObjectPropertyExpression, OWLDataProperty, OWLObjectProperty from owlapy.owl_individual import OWLNamedIndividual from owlapy.owl_literal import OWLLiteral @@ -16,13 +16,13 @@ logger = logging.getLogger(__name__) -class OWLReasoner(metaclass=ABCMeta): +class AbstractOWLReasoner(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): + def __init__(self, ontology: AbstractOWLOntology): pass @abstractmethod @@ -201,13 +201,14 @@ def object_property_values(self, ind: OWLNamedIndividual, pe: OWLObjectPropertyE pass @abstractmethod - def instances(self, ce: OWLClassExpression, direct: bool = False) -> Iterable[OWLNamedIndividual]: + def instances(self, ce: OWLClassExpression, direct: bool = False, timeout: int = 1000) -> 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). + timeout: Time limit in seconds until results must be returned, else empty set is returned. Returns: If direct is True, each named individual j where the set of reasoner axioms entails @@ -353,7 +354,7 @@ def types(self, ind: OWLNamedIndividual, direct: bool = False) -> Iterable[OWLCl pass @abstractmethod - def get_root_ontology(self) -> OWLOntology: + def get_root_ontology(self) -> AbstractOWLOntology: """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 @@ -377,7 +378,7 @@ class expression with respect to the imports closure of the root ontology. pass -class OWLReasonerEx(OWLReasoner, metaclass=ABCMeta): +class AbstractOWLReasonerEx(AbstractOWLReasoner, metaclass=ABCMeta): """Extra convenience methods for OWL Reasoners""" # default diff --git a/owlapy/converter.py b/owlapy/converter.py index 8a8a2f8f..019aac03 100644 --- a/owlapy/converter.py +++ b/owlapy/converter.py @@ -211,7 +211,7 @@ def current_variable(self): # each overload of the method is responsible for processing a different type of class expressions (e.g., ⊔ or ⊓) @singledispatchmethod def process(self, ce: OWLClassExpression): - raise NotImplementedError(ce) + raise NotImplementedError(f"We cannot create SPARQL query based on the following owl class {ce}") # an overload of process function # this overload is responsible for handling single concepts (e.g., Brother) @@ -607,6 +607,7 @@ def as_query(self, count: bool = False, values: Optional[Iterable[OWLNamedIndividual]] = None, named_individuals: bool = False) -> str: + assert isinstance(ce,OWLClassExpression), f"ce must be an instance of OWLClassExpression. Currently {type(ce)}" # root variable: the variable that will be projected # ce: the class expression to be transformed to a SPARQL query # for_all_de_morgan: true -> ¬(∃r.¬C), false -> (∀r.C) @@ -634,6 +635,61 @@ def as_query(self, parseQuery(query) return query + def as_confusion_matrix_query(self, + root_variable: str, + ce: OWLClassExpression, + positive_examples: Iterable[OWLNamedIndividual], + negative_examples: Iterable[OWLNamedIndividual], + for_all_de_morgan: bool = True, + named_individuals: bool = False) -> str: + # get the graph pattern corresponding to the provided class expression (ce) + graph_pattern_str = "".join(self.convert(root_variable, + ce, + for_all_de_morgan=for_all_de_morgan, + named_individuals=named_individuals)) + # preparation for the final query + + # required to compute false negatives + number_of_positive_examples = 0 + # required to compute true negatives + number_of_negative_examples = 0 + # string representation of the positive examples (to be passed to the first VALUES clause) + positive_examples_as_str = "" + # iterate over the positive examples + for positive_example in positive_examples: + number_of_positive_examples += 1 + positive_examples_as_str += f"<{positive_example.to_string_id()}> " + assert (len(positive_examples_as_str) > 0) + + # string representation of the positive examples (to be passed to the first VALUES clause) + negative_examples_as_str = "" + # iterate over the negative examples + for negative_example in negative_examples: + number_of_negative_examples += 1 + negative_examples_as_str += f"<{negative_example.to_string_id()}> " + assert(len(negative_examples_as_str) > 0) + + # create the sparql query + sparql_str= f""" + PREFIX xsd: + SELECT * WHERE {{ + {{ + SELECT (COUNT(DISTINCT {root_variable}) as ?tp) (({number_of_positive_examples} - COUNT(DISTINCT {root_variable})) as ?fn) WHERE {{ + VALUES {root_variable} {{ {positive_examples_as_str} }} + {graph_pattern_str} + }} + }} + {{ + SELECT (COUNT(DISTINCT {root_variable}) as ?fp) (({number_of_negative_examples} - COUNT(DISTINCT {root_variable})) as ?tn) WHERE {{ + VALUES {root_variable} {{ {negative_examples_as_str} }} + {graph_pattern_str} + }} + }} + }} + """ + parseQuery(sparql_str) + return sparql_str + converter = Owl2SparqlConverter() @@ -656,3 +712,30 @@ def owl_expression_to_sparql(expression: OWLClassExpression = None, assert expression is not None, "expression cannot be None" return converter.as_query(root_variable, expression, count=False, values=values, named_individuals=named_individuals, for_all_de_morgan=for_all_de_morgan) + + +def owl_expression_to_sparql_with_confusion_matrix(expression: OWLClassExpression, + positive_examples: Optional[Iterable[OWLNamedIndividual]], + negative_examples: Optional[Iterable[OWLNamedIndividual]], + root_variable: str = "?x", + for_all_de_morgan: bool = True, + named_individuals: bool = False) -> str: + """Convert an OWL Class Expression (https://www.w3.org/TR/owl2-syntax/#Class_Expressions) into a SPARQL query + root variable: the variable that will be projected + expression: the class expression to be transformed to a SPARQL query + positive_examples: positive examples from a class expression problem + negative_examples: positive examples from a class expression problem + for_all_de_morgan: if set to True, the SPARQL mapping will use the mapping containing the nested FILTER NOT EXISTS + patterns for the universal quantifier (¬(∃r.¬C)), instead of the counting query + named_individuals: if set to True, the generated SPARQL query will return only entities + that are instances of owl:NamedIndividual + """ + assert expression is not None, "expression cannot be None" + assert positive_examples is not None, "positive examples cannot be None" + assert negative_examples is not None, "negative examples cannot be None" + return converter.as_confusion_matrix_query(root_variable, + expression, + positive_examples=positive_examples, + negative_examples=negative_examples, + named_individuals=named_individuals, + for_all_de_morgan=for_all_de_morgan) diff --git a/owlapy/iri.py b/owlapy/iri.py index c7234414..66166bb4 100644 --- a/owlapy/iri.py +++ b/owlapy/iri.py @@ -39,7 +39,7 @@ class IRI(OWLAnnotationSubject, OWLAnnotationValue, metaclass=_meta_IRI): _namespace: str _remainder: str - def __init__(self, namespace: Union[str, Namespaces], remainder: str): + def __init__(self, namespace: Union[str, Namespaces], remainder: str=""): if isinstance(namespace, Namespaces): namespace = namespace.ns else: @@ -89,7 +89,7 @@ def create(string, remainder=None) -> 'IRI': return IRI(string[0:index], string[index:]) def __repr__(self): - return f"IRI({repr(self._namespace)},{repr(self._remainder)})" + return f"IRI({repr(self._namespace)}, {repr(self._remainder)})" def __eq__(self, other): if type(other) is type(self): diff --git a/owlapy/jar_dependencies/animal-sniffer-annotations-1.14.jar b/owlapy/jar_dependencies/animal-sniffer-annotations-1.14.jar deleted file mode 100644 index fb76acf7..00000000 Binary files a/owlapy/jar_dependencies/animal-sniffer-annotations-1.14.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/commons-codec-1.10.jar b/owlapy/jar_dependencies/commons-codec-1.10.jar deleted file mode 100644 index 1d7417c4..00000000 Binary files a/owlapy/jar_dependencies/commons-codec-1.10.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/error_prone_annotations-2.0.18.jar b/owlapy/jar_dependencies/error_prone_annotations-2.0.18.jar deleted file mode 100644 index fa549b4d..00000000 Binary files a/owlapy/jar_dependencies/error_prone_annotations-2.0.18.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/fluent-hc-4.5.5.jar b/owlapy/jar_dependencies/fluent-hc-4.5.5.jar deleted file mode 100644 index f1495c76..00000000 Binary files a/owlapy/jar_dependencies/fluent-hc-4.5.5.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/httpclient-4.5.2.jar b/owlapy/jar_dependencies/httpclient-4.5.2.jar deleted file mode 100644 index 701609fc..00000000 Binary files a/owlapy/jar_dependencies/httpclient-4.5.2.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/httpclient-cache-4.5.2.jar b/owlapy/jar_dependencies/httpclient-cache-4.5.2.jar deleted file mode 100644 index a6f5d317..00000000 Binary files a/owlapy/jar_dependencies/httpclient-cache-4.5.2.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/httpclient-osgi-4.5.5.jar b/owlapy/jar_dependencies/httpclient-osgi-4.5.5.jar deleted file mode 100644 index 9d28c6e1..00000000 Binary files a/owlapy/jar_dependencies/httpclient-osgi-4.5.5.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/httpcore-4.4.4.jar b/owlapy/jar_dependencies/httpcore-4.4.4.jar deleted file mode 100644 index ac4a8773..00000000 Binary files a/owlapy/jar_dependencies/httpcore-4.4.4.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/httpcore-nio-4.4.5.jar b/owlapy/jar_dependencies/httpcore-nio-4.4.5.jar deleted file mode 100644 index 18452862..00000000 Binary files a/owlapy/jar_dependencies/httpcore-nio-4.4.5.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/httpcore-osgi-4.4.5.jar b/owlapy/jar_dependencies/httpcore-osgi-4.4.5.jar deleted file mode 100644 index 98bd96d9..00000000 Binary files a/owlapy/jar_dependencies/httpcore-osgi-4.4.5.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/httpmime-4.5.5.jar b/owlapy/jar_dependencies/httpmime-4.5.5.jar deleted file mode 100644 index 4d3ee66f..00000000 Binary files a/owlapy/jar_dependencies/httpmime-4.5.5.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/j2objc-annotations-1.1.jar b/owlapy/jar_dependencies/j2objc-annotations-1.1.jar deleted file mode 100644 index 4b6f1274..00000000 Binary files a/owlapy/jar_dependencies/j2objc-annotations-1.1.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/jackson-annotations-2.9.7.jar b/owlapy/jar_dependencies/jackson-annotations-2.9.7.jar deleted file mode 100644 index 46f56b26..00000000 Binary files a/owlapy/jar_dependencies/jackson-annotations-2.9.7.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/jackson-core-2.9.7.jar b/owlapy/jar_dependencies/jackson-core-2.9.7.jar deleted file mode 100644 index fa46a7f6..00000000 Binary files a/owlapy/jar_dependencies/jackson-core-2.9.7.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/jackson-databind-2.9.7.jar b/owlapy/jar_dependencies/jackson-databind-2.9.7.jar deleted file mode 100644 index 76d50b4f..00000000 Binary files a/owlapy/jar_dependencies/jackson-databind-2.9.7.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/jaxb-api-2.3.0.jar b/owlapy/jar_dependencies/jaxb-api-2.3.0.jar deleted file mode 100644 index 0817c083..00000000 Binary files a/owlapy/jar_dependencies/jaxb-api-2.3.0.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/jsonld-java-0.12.0.jar b/owlapy/jar_dependencies/jsonld-java-0.12.0.jar deleted file mode 100644 index 6e01fdbc..00000000 Binary files a/owlapy/jar_dependencies/jsonld-java-0.12.0.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/jsr305-3.0.2.jar b/owlapy/jar_dependencies/jsr305-3.0.2.jar deleted file mode 100644 index 59222d9c..00000000 Binary files a/owlapy/jar_dependencies/jsr305-3.0.2.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/rdf4j-rio-binary-2.3.2.jar b/owlapy/jar_dependencies/rdf4j-rio-binary-2.3.2.jar deleted file mode 100644 index 41627364..00000000 Binary files a/owlapy/jar_dependencies/rdf4j-rio-binary-2.3.2.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/rdf4j-rio-datatypes-2.3.2.jar b/owlapy/jar_dependencies/rdf4j-rio-datatypes-2.3.2.jar deleted file mode 100644 index a62ee6a0..00000000 Binary files a/owlapy/jar_dependencies/rdf4j-rio-datatypes-2.3.2.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/rdf4j-rio-jsonld-2.3.2.jar b/owlapy/jar_dependencies/rdf4j-rio-jsonld-2.3.2.jar deleted file mode 100644 index 2097340d..00000000 Binary files a/owlapy/jar_dependencies/rdf4j-rio-jsonld-2.3.2.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/rdf4j-rio-languages-2.3.2.jar b/owlapy/jar_dependencies/rdf4j-rio-languages-2.3.2.jar deleted file mode 100644 index 0930d578..00000000 Binary files a/owlapy/jar_dependencies/rdf4j-rio-languages-2.3.2.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/rdf4j-rio-n3-2.3.2.jar b/owlapy/jar_dependencies/rdf4j-rio-n3-2.3.2.jar deleted file mode 100644 index f16d6e7a..00000000 Binary files a/owlapy/jar_dependencies/rdf4j-rio-n3-2.3.2.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/rdf4j-rio-nquads-2.3.2.jar b/owlapy/jar_dependencies/rdf4j-rio-nquads-2.3.2.jar deleted file mode 100644 index 79e69e83..00000000 Binary files a/owlapy/jar_dependencies/rdf4j-rio-nquads-2.3.2.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/rdf4j-rio-ntriples-2.3.2.jar b/owlapy/jar_dependencies/rdf4j-rio-ntriples-2.3.2.jar deleted file mode 100644 index 94c7b3c0..00000000 Binary files a/owlapy/jar_dependencies/rdf4j-rio-ntriples-2.3.2.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/rdf4j-rio-rdfjson-2.3.2.jar b/owlapy/jar_dependencies/rdf4j-rio-rdfjson-2.3.2.jar deleted file mode 100644 index 8965f1ec..00000000 Binary files a/owlapy/jar_dependencies/rdf4j-rio-rdfjson-2.3.2.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/rdf4j-rio-rdfxml-2.3.2.jar b/owlapy/jar_dependencies/rdf4j-rio-rdfxml-2.3.2.jar deleted file mode 100644 index e8f48b74..00000000 Binary files a/owlapy/jar_dependencies/rdf4j-rio-rdfxml-2.3.2.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/rdf4j-rio-trig-2.3.2.jar b/owlapy/jar_dependencies/rdf4j-rio-trig-2.3.2.jar deleted file mode 100644 index 9751b3bc..00000000 Binary files a/owlapy/jar_dependencies/rdf4j-rio-trig-2.3.2.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/rdf4j-rio-trix-2.3.2.jar b/owlapy/jar_dependencies/rdf4j-rio-trix-2.3.2.jar deleted file mode 100644 index 4a3b921e..00000000 Binary files a/owlapy/jar_dependencies/rdf4j-rio-trix-2.3.2.jar and /dev/null differ diff --git a/owlapy/jar_dependencies/rdf4j-rio-turtle-2.3.2.jar b/owlapy/jar_dependencies/rdf4j-rio-turtle-2.3.2.jar deleted file mode 100644 index 14d03302..00000000 Binary files a/owlapy/jar_dependencies/rdf4j-rio-turtle-2.3.2.jar and /dev/null differ diff --git a/owlapy/owl_axiom.py b/owlapy/owl_axiom.py index b1c46c2f..6c10ea5d 100644 --- a/owlapy/owl_axiom.py +++ b/owlapy/owl_axiom.py @@ -989,8 +989,8 @@ def __hash__(self): return hash((self._subject, self._property, self._object, *self._annotations)) def __repr__(self): - return f'{type(self).__name__}(subject={self._subject},property={self._property},' \ - f'object={self._object},annotation={self._annotations})' + return f'{type(self).__name__}(subject={self._subject},property_={self._property},' \ + f'object_={self._object},annotations={self._annotations})' class OWLObjectPropertyAssertionAxiom(OWLPropertyAssertionAxiom[OWLObjectPropertyExpression, OWLIndividual]): diff --git a/owlapy/owl_hierarchy.py b/owlapy/owl_hierarchy.py index 3cc2b8be..3517ebf9 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.abstracts.abstract_owl_reasoner import OWLReasoner +from owlapy.abstracts.abstract_owl_reasoner import AbstractOWLReasoner _S = TypeVar('_S', bound=HasIRI) #: _U = TypeVar('_U', bound='AbstractHierarchy') #: @@ -40,20 +40,20 @@ def __init__(self, factory: Type[_S], hierarchy_down: Iterable[Tuple[_S, Iterabl ... @overload - def __init__(self, factory: Type[_S], reasoner: OWLReasoner): + def __init__(self, factory: Type[_S], reasoner: AbstractOWLReasoner): ... @abstractmethod def __init__(self, factory: Type[_S], arg): self._Type = factory - if isinstance(arg, OWLReasoner): + if isinstance(arg, AbstractOWLReasoner): hier_down_gen = self._hierarchy_down_generator(arg) self._init(hier_down_gen) else: self._init(arg) @abstractmethod - def _hierarchy_down_generator(self, reasoner: OWLReasoner) -> Iterable[Tuple[_S, Iterable[_S]]]: + def _hierarchy_down_generator(self, reasoner: AbstractOWLReasoner) -> Iterable[Tuple[_S, Iterable[_S]]]: """Generate the suitable downwards hierarchy based on the reasoner.""" pass @@ -263,7 +263,7 @@ def get_top_entity(cls) -> OWLClass: def get_bottom_entity(cls) -> OWLClass: return OWLNothing - def _hierarchy_down_generator(self, reasoner: OWLReasoner) -> Iterable[Tuple[OWLClass, Iterable[OWLClass]]]: + def _hierarchy_down_generator(self, reasoner: AbstractOWLReasoner) -> Iterable[Tuple[OWLClass, Iterable[OWLClass]]]: yield from ((_, reasoner.sub_classes(_, direct=True)) for _ in reasoner.get_root_ontology().classes_in_signature()) @@ -280,7 +280,7 @@ def is_subclass_of(self, subclass: OWLClass, superclass: OWLClass) -> bool: def __init__(self, hierarchy_down: Iterable[Tuple[OWLClass, Iterable[OWLClass]]]): ... @overload - def __init__(self, reasoner: OWLReasoner): ... + def __init__(self, reasoner: AbstractOWLReasoner): ... def __init__(self, arg): super().__init__(OWLClass, arg) @@ -296,7 +296,7 @@ def get_top_entity(cls) -> OWLObjectProperty: def get_bottom_entity(cls) -> OWLObjectProperty: return OWLBottomObjectProperty - def _hierarchy_down_generator(self, reasoner: OWLReasoner) \ + def _hierarchy_down_generator(self, reasoner: AbstractOWLReasoner) \ -> Iterable[Tuple[OWLObjectProperty, Iterable[OWLObjectProperty]]]: return ((_, map(lambda _: cast(OWLObjectProperty, _), filter(lambda _: isinstance(_, OWLObjectProperty), @@ -328,7 +328,7 @@ def most_special_roles(self) -> Iterable[OWLObjectProperty]: def __init__(self, hierarchy_down: Iterable[Tuple[OWLObjectProperty, Iterable[OWLObjectProperty]]]): ... @overload - def __init__(self, reasoner: OWLReasoner): ... + def __init__(self, reasoner: AbstractOWLReasoner): ... def __init__(self, arg): super().__init__(OWLObjectProperty, arg) @@ -344,7 +344,7 @@ def get_top_entity(cls) -> OWLDataProperty: def get_bottom_entity(cls) -> OWLDataProperty: return OWLBottomDataProperty - def _hierarchy_down_generator(self, reasoner: OWLReasoner) \ + def _hierarchy_down_generator(self, reasoner: AbstractOWLReasoner) \ -> Iterable[Tuple[OWLDataProperty, Iterable[OWLDataProperty]]]: return ((_, reasoner.sub_data_properties(_, direct=True)) for _ in reasoner.get_root_ontology().data_properties_in_signature()) @@ -374,7 +374,7 @@ def most_special_roles(self) -> Iterable[OWLDataProperty]: def __init__(self, hierarchy_down: Iterable[Tuple[OWLDataProperty, Iterable[OWLDataProperty]]]): ... @overload - def __init__(self, reasoner: OWLReasoner): ... + def __init__(self, reasoner: AbstractOWLReasoner): ... def __init__(self, arg): super().__init__(OWLDataProperty, arg) diff --git a/owlapy/owl_object.py b/owlapy/owl_object.py index b90e9888..02894034 100644 --- a/owlapy/owl_object.py +++ b/owlapy/owl_object.py @@ -88,8 +88,6 @@ def __hash__(self): def __repr__(self): return f"{type(self).__name__}({repr(self._iri)})" - pass - class OWLEntity(OWLNamedObject, metaclass=ABCMeta): """Represents Entities in the OWL 2 Specification.""" diff --git a/owlapy/owl_ontology.py b/owlapy/owl_ontology.py index fdf7acbf..683a947b 100644 --- a/owlapy/owl_ontology.py +++ b/owlapy/owl_ontology.py @@ -8,7 +8,7 @@ import owlready2 from pandas import Timedelta from owlapy import namespaces -from owlapy.abstracts.abstract_owl_ontology import OWLOntology +from owlapy.abstracts.abstract_owl_ontology import AbstractOWLOntology from owlapy.owl_data_ranges import OWLDataRange, OWLDataComplementOf, OWLDataUnionOf, OWLDataIntersectionOf from owlapy.owl_datatype import OWLDatatype from owlapy.owl_individual import OWLNamedIndividual, OWLIndividual @@ -38,6 +38,7 @@ OWLDifferentIndividualsAxiom, OWLDisjointClassesAxiom, OWLSameIndividualAxiom, OWLClassAxiom, OWLDataPropertyDomainAxiom, OWLDataPropertyRangeAxiom, OWLObjectPropertyDomainAxiom) from owlapy.vocab import OWLFacet +import os logger = logging.getLogger(__name__) @@ -123,7 +124,7 @@ def __eq__(self, other): return NotImplemented -def _check_expression(expr: OWLObject, ontology: OWLOntology, world: owlready2.namespace.World): +def _check_expression(expr: OWLObject, ontology: AbstractOWLOntology, world: owlready2.namespace.World): """ @TODO:CD: Documentation Creates all entities (individuals, classes, properties) that appear in the given (complex) class expression @@ -148,12 +149,12 @@ def _check_expression(expr: OWLObject, ontology: OWLOntology, world: owlready2.n @singledispatch -def _add_axiom(axiom: OWLAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _add_axiom(axiom: OWLAxiom, ontology: AbstractOWLOntology, 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): +def _(axiom: OWLDeclarationAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) @@ -184,7 +185,7 @@ def _(axiom: OWLDeclarationAxiom, ontology: OWLOntology, world: owlready2.namesp @_add_axiom.register -def _(axiom: OWLClassAssertionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _(axiom: OWLClassAssertionAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) @@ -202,7 +203,7 @@ def _(axiom: OWLClassAssertionAxiom, ontology: OWLOntology, world: owlready2.nam @_add_axiom.register -def _(axiom: OWLObjectPropertyAssertionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _(axiom: OWLObjectPropertyAssertionAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) @@ -220,7 +221,7 @@ def _(axiom: OWLObjectPropertyAssertionAxiom, ontology: OWLOntology, world: owlr @_add_axiom.register -def _(axiom: OWLDataPropertyAssertionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _(axiom: OWLDataPropertyAssertionAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) @@ -235,7 +236,7 @@ def _(axiom: OWLDataPropertyAssertionAxiom, ontology: OWLOntology, world: owlrea @_add_axiom.register -def _(axiom: OWLSubClassOfAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _(axiom: OWLSubClassOfAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) @@ -261,7 +262,7 @@ def _(axiom: OWLSubClassOfAxiom, ontology: OWLOntology, world: owlready2.namespa # 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): +def _(axiom: OWLEquivalentClassesAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x = conv.map_object(ontology) @@ -294,7 +295,7 @@ def _(axiom: OWLEquivalentClassesAxiom, ontology: OWLOntology, world: owlready2. @_add_axiom.register -def _(axiom: OWLDisjointClassesAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _(axiom: OWLDisjointClassesAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.Ontology = conv.map_object(ontology) @@ -307,7 +308,7 @@ def _(axiom: OWLDisjointClassesAxiom, ontology: OWLOntology, world: owlready2.na @_add_axiom.register -def _(axiom: OWLDisjointUnionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _(axiom: OWLDisjointUnionAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.Ontology = conv.map_object(ontology) @@ -321,7 +322,7 @@ def _(axiom: OWLDisjointUnionAxiom, ontology: OWLOntology, world: owlready2.name @_add_axiom.register -def _(axiom: OWLAnnotationAssertionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _(axiom: OWLAnnotationAssertionAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.Ontology = conv.map_object(ontology) @@ -347,7 +348,7 @@ def _(axiom: OWLAnnotationAssertionAxiom, ontology: OWLOntology, world: owlready @_add_axiom.register -def _(axiom: OWLNaryIndividualAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _(axiom: OWLNaryIndividualAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.Ontology = conv.map_object(ontology) @@ -367,7 +368,7 @@ def _(axiom: OWLNaryIndividualAxiom, ontology: OWLOntology, world: owlready2.nam @_add_axiom.register -def _(axiom: OWLSubPropertyAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _(axiom: OWLSubPropertyAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.Ontology = conv.map_object(ontology) @@ -382,7 +383,7 @@ def _(axiom: OWLSubPropertyAxiom, ontology: OWLOntology, world: owlready2.namesp @_add_axiom.register -def _(axiom: OWLPropertyDomainAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _(axiom: OWLPropertyDomainAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.Ontology = conv.map_object(ontology) @@ -397,7 +398,7 @@ def _(axiom: OWLPropertyDomainAxiom, ontology: OWLOntology, world: owlready2.nam @_add_axiom.register -def _(axiom: OWLPropertyRangeAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _(axiom: OWLPropertyRangeAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.Ontology = conv.map_object(ontology) @@ -414,7 +415,7 @@ def _(axiom: OWLPropertyRangeAxiom, ontology: OWLOntology, world: owlready2.name @_add_axiom.register -def _(axiom: OWLNaryPropertyAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _(axiom: OWLNaryPropertyAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.Ontology = conv.map_object(ontology) @@ -440,7 +441,7 @@ def _(axiom: OWLNaryPropertyAxiom, ontology: OWLOntology, world: owlready2.names @_add_axiom.register -def _(axiom: OWLObjectPropertyCharacteristicAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _(axiom: OWLObjectPropertyCharacteristicAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.Ontology = conv.map_object(ontology) @@ -467,7 +468,7 @@ def _(axiom: OWLObjectPropertyCharacteristicAxiom, ontology: OWLOntology, world: @_add_axiom.register -def _(axiom: OWLDataPropertyCharacteristicAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _(axiom: OWLDataPropertyCharacteristicAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.Ontology = conv.map_object(ontology) @@ -482,12 +483,12 @@ def _(axiom: OWLDataPropertyCharacteristicAxiom, ontology: OWLOntology, world: o @singledispatch -def _remove_axiom(axiom: OWLAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _remove_axiom(axiom: OWLAxiom, ontology: AbstractOWLOntology, 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): +def _(axiom: OWLDeclarationAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) with ont_x: @@ -498,7 +499,7 @@ def _(axiom: OWLDeclarationAxiom, ontology: OWLOntology, world: owlready2.namesp @_remove_axiom.register -def _(axiom: OWLClassAssertionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _(axiom: OWLClassAssertionAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) @@ -514,7 +515,7 @@ def _(axiom: OWLClassAssertionAxiom, ontology: OWLOntology, world: owlready2.nam @_remove_axiom.register -def _(axiom: OWLObjectPropertyAssertionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _(axiom: OWLObjectPropertyAssertionAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) @@ -527,7 +528,7 @@ def _(axiom: OWLObjectPropertyAssertionAxiom, ontology: OWLOntology, world: owlr @_remove_axiom.register -def _(axiom: OWLDataPropertyAssertionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _(axiom: OWLDataPropertyAssertionAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) @@ -540,7 +541,7 @@ def _(axiom: OWLDataPropertyAssertionAxiom, ontology: OWLOntology, world: owlrea @_remove_axiom.register -def _(axiom: OWLSubClassOfAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _(axiom: OWLSubClassOfAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) sub_class = axiom.get_sub_class() @@ -565,7 +566,7 @@ def _(axiom: OWLSubClassOfAxiom, ontology: OWLOntology, world: owlready2.namespa # 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): +def _(axiom: OWLEquivalentClassesAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x = conv.map_object(ontology) @@ -585,7 +586,7 @@ def _(axiom: OWLEquivalentClassesAxiom, ontology: OWLOntology, world: owlready2. @_remove_axiom.register -def _(axiom: OWLDisjointClassesAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _(axiom: OWLDisjointClassesAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.Ontology = conv.map_object(ontology) @@ -600,7 +601,7 @@ def _(axiom: OWLDisjointClassesAxiom, ontology: OWLOntology, world: owlready2.na @_remove_axiom.register -def _(axiom: OWLDisjointUnionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _(axiom: OWLDisjointUnionAxiom, ontology: AbstractOWLOntology, 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.' @@ -616,7 +617,7 @@ def _(axiom: OWLDisjointUnionAxiom, ontology: OWLOntology, world: owlready2.name @_remove_axiom.register -def _(axiom: OWLAnnotationAssertionAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _(axiom: OWLAnnotationAssertionAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.Ontology = conv.map_object(ontology) @@ -636,7 +637,7 @@ def _(axiom: OWLAnnotationAssertionAxiom, ontology: OWLOntology, world: owlready @_remove_axiom.register -def _(axiom: OWLNaryIndividualAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _(axiom: OWLNaryIndividualAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.Ontology = conv.map_object(ontology) @@ -662,7 +663,7 @@ def _(axiom: OWLNaryIndividualAxiom, ontology: OWLOntology, world: owlready2.nam @_remove_axiom.register -def _(axiom: OWLSubPropertyAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _(axiom: OWLSubPropertyAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.namespace.Ontology = conv.map_object(ontology) @@ -678,7 +679,7 @@ def _(axiom: OWLSubPropertyAxiom, ontology: OWLOntology, world: owlready2.namesp @_remove_axiom.register -def _(axiom: OWLPropertyDomainAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _(axiom: OWLPropertyDomainAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.Ontology = conv.map_object(ontology) @@ -690,7 +691,7 @@ def _(axiom: OWLPropertyDomainAxiom, ontology: OWLOntology, world: owlready2.nam @_remove_axiom.register -def _(axiom: OWLPropertyRangeAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _(axiom: OWLPropertyRangeAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.Ontology = conv.map_object(ontology) @@ -703,7 +704,7 @@ def _(axiom: OWLPropertyRangeAxiom, ontology: OWLOntology, world: owlready2.name @_remove_axiom.register -def _(axiom: OWLNaryPropertyAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _(axiom: OWLNaryPropertyAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.Ontology = conv.map_object(ontology) @@ -737,7 +738,7 @@ def _(axiom: OWLNaryPropertyAxiom, ontology: OWLOntology, world: owlready2.names @_remove_axiom.register -def _(axiom: OWLObjectPropertyCharacteristicAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _(axiom: OWLObjectPropertyCharacteristicAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.Ontology = conv.map_object(ontology) @@ -766,7 +767,7 @@ def _(axiom: OWLObjectPropertyCharacteristicAxiom, ontology: OWLOntology, world: @_remove_axiom.register -def _(axiom: OWLDataPropertyCharacteristicAxiom, ontology: OWLOntology, world: owlready2.namespace.World): +def _(axiom: OWLDataPropertyCharacteristicAxiom, ontology: AbstractOWLOntology, world: owlready2.namespace.World): conv = ToOwlready2(world) ont_x: owlready2.Ontology = conv.map_object(ontology) @@ -777,7 +778,7 @@ def _(axiom: OWLDataPropertyCharacteristicAxiom, ontology: OWLOntology, world: o property_x.is_a.remove(owlready2.FunctionalProperty) -class Ontology(OWLOntology): +class Ontology(AbstractOWLOntology): __slots__ = '_manager', '_iri', '_world', '_onto' _manager: _OM @@ -800,6 +801,9 @@ def __init__(self, manager: _OM, ontology_iri: IRI, load: bool): onto = onto.load() self._onto = onto + def __len__(self) -> int: + return len([t for t in self._onto.get_triples()]) + def classes_in_signature(self) -> Iterable[OWLClass]: for c in self._onto.classes(): yield OWLClass(IRI.create(c.iri)) @@ -812,16 +816,31 @@ def object_properties_in_signature(self) -> Iterable[OWLObjectProperty]: for op in self._onto.object_properties(): yield OWLObjectProperty(IRI.create(op.iri)) + def properties_in_signature(self) -> Iterable[OWLProperty]: + yield from self.object_properties_in_signature() + yield from self.data_properties_in_signature() + def individuals_in_signature(self) -> Iterable[OWLNamedIndividual]: for i in self._onto.individuals(): yield OWLNamedIndividual(IRI.create(i.iri)) + def tbox_axioms(self)->Iterable: + # @TODO: CD: Return all information between owl classes, e.g. subclass or disjoint + raise NotImplementedError("will be implemented in future") + def abox_axioms_between_individuals(self)->Iterable: + # @TODO: CD: Return all information between owl_individuals, i.e., triples with object properties + raise NotImplementedError("will be implemented in future") + def abox_axioms_between_individuals_and_classes(self)->Iterable: + # @TODO: CD: Return all type information about individuals, i.e., individual type Class + raise NotImplementedError("will be implemented in future") + + # @TODO:CD:Unsure it is working def equivalent_classes_axioms(self, c: OWLClass) -> Iterable[OWLEquivalentClassesAxiom]: c_x: owlready2.ThingClass = self._world[c.str] # TODO: Should this also return EquivalentClasses general class axioms? Compare to java owlapi for ec_x in c_x.equivalent_to: yield OWLEquivalentClassesAxiom([c, _parse_concept_to_owlapy(ec_x)]) - + # @TODO:CD:Unsure it is working def general_class_axioms(self) -> Iterable[OWLClassAxiom]: # TODO: At the moment owlready2 only supports SubClassOf general class axioms. (18.02.2023) for ca in self._onto.general_class_axioms(): @@ -913,17 +932,27 @@ def remove_axiom(self, axiom: Union[OWLAxiom, Iterable[OWLAxiom]]): 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) + def save(self, path: Union[str,IRI] = None, inplace:bool=False, rdf_format = "rdfxml"): + # convert it into str. + if isinstance(path, IRI): + path = path.as_str() + # Sanity checking + if inplace is False: + assert isinstance(path,str), f"path must be string if inplace is set to False. Current path is {type(path)}" + # Get the current ontology defined in the world. + ont_x:owlready2.namespace.Ontology + ont_x = self._world.get_ontology(self.get_ontology_id().get_ontology_iri().as_str()) + + if inplace: + if os.path.exists(self._iri.as_str()): + print(f"Saving {self} inplace...") + ont_x.save(file=self._iri.as_str(), format=rdf_format) + else: + print(f"Saving {self} inplace with name of demo.owl...") + self._world.get_ontology(self.get_ontology_id().get_ontology_iri().as_str()).save(file="demo.owl") else: - raise NotImplementedError("Couldn't save because the namespace of current ontology's IRI does not start with **file:/**") + print(f"Saving {path}..") + ont_x.save(file=path,format=rdf_format) def get_original_iri(self): """Get the IRI argument that was used to create this ontology.""" @@ -938,10 +967,10 @@ def __hash__(self): return hash(self._onto.base_iri) def __repr__(self): - return f'Ontology({IRI.create(self._onto.base_iri)}, {self._onto.loaded})' + return f'Ontology({self._onto.base_iri}, loaded:{self._onto.loaded})' -class SyncOntology(OWLOntology): +class SyncOntology(AbstractOWLOntology): def __init__(self, manager: _SM, path: Union[IRI, str], new: bool = False): from owlapy.owlapi_mapper import OWLAPIMapper @@ -995,6 +1024,48 @@ def object_property_domain_axioms(self, property: OWLObjectProperty) -> Iterable def object_property_range_axioms(self, property: OWLObjectProperty) -> Iterable[OWLObjectPropertyRangeAxiom]: return self.mapper.map_(self.owlapi_ontology.getObjectPropertyRangeAxioms(self.mapper.map_(property))) + def _get_imports_enum(self, include_imports_closure: bool): + from org.semanticweb.owlapi.model.parameters import Imports + if include_imports_closure: + imports = Imports.INCLUDED + else: + imports = Imports.EXCLUDED + return imports + + def get_signature(self, include_imports_closure: bool = True): + """Gets the entities that are in the signature of this ontology. + + Args: + include_imports_closure: Whether to include/exclude imports from searches. + + Returns: + Entities in signature. + """ + return self.mapper.map_(self.owlapi_ontology.getSignature(self._get_imports_enum(include_imports_closure))) + + def get_abox_axioms(self, include_imports_closure: bool = True) -> Iterable[OWLAxiom]: + """Get all ABox axioms. + + Args: + include_imports_closure: Whether to include/exclude imports from searches. + + Returns: + ABox axioms. + """ + + return self.mapper.map_(self.owlapi_ontology.getABoxAxioms(self._get_imports_enum(include_imports_closure))) + + def get_tbox_axioms(self, include_imports_closure: bool = True) -> Iterable[OWLAxiom]: + """Get all TBox axioms. + + Args: + include_imports_closure: Whether to include/exclude imports from searches. + + Returns: + TBox axioms. + """ + return self.mapper.map_(self.owlapi_ontology.getTBoxAxioms(self._get_imports_enum(include_imports_closure))) + def get_owl_ontology_manager(self) -> _M: return self.manager @@ -1065,7 +1136,7 @@ def _(self, ce: OWLClassExpression) -> Union[owlready2.ClassConstruct, owlready2 return self.map_concept(ce) @map_object.register - def _(self, ont: OWLOntology) -> owlready2.namespace.Ontology: + def _(self, ont: AbstractOWLOntology) -> owlready2.namespace.Ontology: return self._world.get_ontology( ont.get_ontology_id().get_ontology_iri().as_str() ) diff --git a/owlapy/owl_ontology_manager.py b/owlapy/owl_ontology_manager.py index 0f384f05..4d58d58a 100644 --- a/owlapy/owl_ontology_manager.py +++ b/owlapy/owl_ontology_manager.py @@ -3,11 +3,11 @@ import jpype import owlready2 -from owlapy.abstracts.abstract_owl_ontology_manager import OWLOntologyChange, OWLOntologyManager +from owlapy.abstracts.abstract_owl_ontology_manager import AbstractOWLOntologyChange, AbstractOWLOntologyManager from owlapy.iri import IRI from owlapy.meta_classes import HasIRI from owlapy.owl_ontology import Ontology, SyncOntology -from owlapy.abstracts.abstract_owl_ontology import OWLOntology +from owlapy.abstracts.abstract_owl_ontology import AbstractOWLOntology from owlapy.static_funcs import startJVM @@ -41,11 +41,11 @@ def str(self) -> str: return self._iri.as_str() -class AddImport(OWLOntologyChange): +class AddImport(AbstractOWLOntologyChange): """Represents an ontology change where an import statement is added to an ontology.""" __slots__ = '_ont', '_declaration' - def __init__(self, ontology: OWLOntology, import_declaration: OWLImportsDeclaration): + def __init__(self, ontology: AbstractOWLOntology, import_declaration: OWLImportsDeclaration): """ Args: ontology: The ontology to which the change is to be applied. @@ -63,7 +63,7 @@ def get_import_declaration(self) -> OWLImportsDeclaration: return self._declaration -class OntologyManager(OWLOntologyManager): +class OntologyManager(AbstractOWLOntologyManager): __slots__ = '_world' _world: owlready2.namespace.World @@ -88,14 +88,15 @@ def create_ontology(self, iri: Union[str, IRI] = None) -> Ontology: 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[IRI, str] = None) -> Ontology: - if isinstance(iri, str): - iri = IRI.create(iri) + def load_ontology(self, path: Union[IRI, str] = None) -> Ontology: + if isinstance(path, str): + path_iri = IRI.create(path) else: - assert isinstance(iri, IRI), "iri either must be string or an instance of IRI Class" - return Ontology(self, iri, load=True) + assert isinstance(path, IRI), "iri either must be string or an instance of IRI Class" + path_iri=path + return Ontology(self, path_iri, load=True) - def apply_change(self, change: OWLOntologyChange): + def apply_change(self, change: AbstractOWLOntologyChange): if isinstance(change, AddImport): ont_x: owlready2.namespace.Ontology = self._world.get_ontology( change.get_ontology().get_ontology_id().get_ontology_iri().as_str()) @@ -111,7 +112,11 @@ def save_world(self): self._world.save() -class SyncOntologyManager(OWLOntologyManager): +class SyncOntologyManager(AbstractOWLOntologyManager): + """ + Create OWLManager in Python + https://owlcs.github.io/owlapi/apidocs_5/org/semanticweb/owlapi/apibinding/OWLManager.html + """ # WARN: Do not move local imports to top of the module def __init__(self): @@ -133,5 +138,5 @@ def load_ontology(self, iri: Union[IRI, str]) -> SyncOntology: def get_owlapi_manager(self): return self.owlapi_manager - def apply_change(self, change: OWLOntologyChange): + def apply_change(self, change: AbstractOWLOntologyChange): raise NotImplementedError() diff --git a/owlapy/owl_reasoner.py b/owlapy/owl_reasoner.py index 45d02bca..5dd6998b 100644 --- a/owlapy/owl_reasoner.py +++ b/owlapy/owl_reasoner.py @@ -21,21 +21,19 @@ 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.abstracts.abstract_owl_ontology import AbstractOWLOntology 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 - +from owlapy.utils import LRUCache, run_with_timeout +from owlapy.abstracts.abstract_owl_reasoner import AbstractOWLReasoner, AbstractOWLReasonerEx logger = logging.getLogger(__name__) _P = TypeVar('_P', bound=OWLPropertyExpression) - -class OntologyReasoner(OWLReasonerEx): +class OntologyReasoner(AbstractOWLReasonerEx): __slots__ = '_ontology', '_world' # TODO: CD: We will remove owlready2 from owlapy _ontology: Ontology @@ -198,7 +196,7 @@ def object_property_values(self, ind: OWLNamedIndividual, pe: OWLObjectPropertyE else: raise NotImplementedError(pe) - def instances(self, ce: OWLClassExpression, direct: bool = False) -> Iterable[OWLNamedIndividual]: + def _instances(self, ce: OWLClassExpression, direct: bool = False) -> Iterable[OWLNamedIndividual]: if direct: if isinstance(ce, OWLClass): c_x: owlready2.ThingClass = self._world[ce.str] @@ -226,6 +224,9 @@ def instances(self, ce: OWLClassExpression, direct: bool = False) -> Iterable[OW else: raise NotImplementedError("instances for complex class expressions not implemented", ce) + def instances(self, ce: OWLClassExpression, direct: bool = False, timeout: int = 1000): + return run_with_timeout(self._instances, timeout, (ce, direct)) + def _sub_classes_recursive(self, ce: OWLClassExpression, seen_set: Set, only_named: bool = True) \ -> Iterable[OWLClassExpression]: @@ -576,11 +577,11 @@ def types(self, ind: OWLNamedIndividual, direct: bool = False) -> Iterable[OWLCl # infer_data_property_values=infer_data_property_values, # debug=debug) - def get_root_ontology(self) -> OWLOntology: + def get_root_ontology(self) -> AbstractOWLOntology: return self._ontology -class FastInstanceCheckerReasoner(OWLReasonerEx): +class FastInstanceCheckerReasoner(AbstractOWLReasonerEx): """Tries to check instances fast (but maybe incomplete).""" __slots__ = '_ontology', '_base_reasoner', \ '_ind_set', '_cls_to_ind', \ @@ -591,8 +592,8 @@ class FastInstanceCheckerReasoner(OWLReasonerEx): '_negation_default', \ '__warned' - _ontology: OWLOntology - _base_reasoner: OWLReasoner + _ontology: AbstractOWLOntology + _base_reasoner: AbstractOWLReasoner _cls_to_ind: Dict[OWLClass, FrozenSet[OWLNamedIndividual]] # Class => individuals _has_prop: Mapping[Type[_P], LRUCache[_P, FrozenSet[OWLNamedIndividual]]] # Type => Property => individuals _ind_set: FrozenSet[OWLNamedIndividual] @@ -612,7 +613,7 @@ class FastInstanceCheckerReasoner(OWLReasonerEx): _negation_default: bool _sub_properties: bool - def __init__(self, ontology: OWLOntology, base_reasoner: OWLReasoner, *, + def __init__(self, ontology: AbstractOWLOntology, base_reasoner: AbstractOWLReasoner, *, property_cache: bool = True, negation_default: bool = True, sub_properties: bool = False): """Fast instance checker. @@ -690,7 +691,7 @@ def object_property_values(self, ind: OWLNamedIndividual, pe: OWLObjectPropertyE -> Iterable[OWLNamedIndividual]: yield from self._base_reasoner.object_property_values(ind, pe, direct) - def instances(self, ce: OWLClassExpression, direct: bool = False) -> Iterable[OWLNamedIndividual]: + def _instances(self, ce: OWLClassExpression, direct: bool = False) -> Iterable[OWLNamedIndividual]: if direct: if not self.__warned & 2: logger.warning("direct not implemented") @@ -698,6 +699,9 @@ def instances(self, ce: OWLClassExpression, direct: bool = False) -> Iterable[OW temp = self._find_instances(ce) yield from temp + def instances(self, ce: OWLClassExpression, direct: bool = False, timeout: int = 1000): + return run_with_timeout(self._instances, timeout, (ce, direct)) + def sub_classes(self, ce: OWLClassExpression, direct: bool = False, only_named: bool = True) \ -> Iterable[OWLClassExpression]: yield from self._base_reasoner.sub_classes(ce, direct=direct, only_named=only_named) @@ -734,7 +738,7 @@ def sub_object_properties(self, op: OWLObjectPropertyExpression, direct: bool = -> Iterable[OWLObjectPropertyExpression]: yield from self._base_reasoner.sub_object_properties(op=op, direct=direct) - def get_root_ontology(self) -> OWLOntology: + def get_root_ontology(self) -> AbstractOWLOntology: return self._ontology def _lazy_cache_obj_prop(self, pe: OWLObjectPropertyExpression) -> None: @@ -1145,7 +1149,7 @@ def _retrieve_triples(self, pe: OWLPropertyExpression) -> Iterable: yield from relations -class SyncReasoner(OWLReasonerEx): +class SyncReasoner(AbstractOWLReasonerEx): def __init__(self, ontology: Union[SyncOntology, str], reasoner="HermiT"): """ @@ -1165,8 +1169,10 @@ def __init__(self, ontology: Union[SyncOntology, str], reasoner="HermiT"): self.manager = ontology.manager self.ontology = ontology elif isinstance(ontology, str): + # https://owlcs.github.io/owlapi/apidocs_5/org/semanticweb/owlapi/apibinding/OWLManager.html self.manager = SyncOntologyManager() - self.ontology = self.manager.load_ontology(ontology) + # OWLOntology + self.ontology = self.manager.load_ontology(iri=ontology) self._owlapi_manager = self.manager.get_owlapi_manager() self._owlapi_ontology = self.ontology.get_owlapi_ontology() @@ -1216,7 +1222,9 @@ def __init__(self, ontology: Union[SyncOntology, str], reasoner="HermiT"): else: raise NotImplementedError("Not implemented") - def instances(self, ce: OWLClassExpression, direct=False) -> List[OWLNamedIndividual]: + self.reasoner = reasoner + + def _instances(self, ce: OWLClassExpression, direct=False) -> Set[OWLNamedIndividual]: """ Get the instances for a given class expression using HermiT. @@ -1225,10 +1233,16 @@ def instances(self, ce: OWLClassExpression, direct=False) -> List[OWLNamedIndivi direct (bool): Whether to get direct instances or not. Defaults to False. Returns: - list: A list of individuals classified by the given class expression. + set: A set 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] + mapped_ce = self.mapper.map_(ce) + instances = self._owlapi_reasoner.getInstances(mapped_ce, direct) + flattended_instances = instances.getFlattened() + assert str(type(flattended_instances)) == "" + return {self.mapper.map_(ind) for ind in flattended_instances} + + def instances(self, ce: OWLClassExpression, direct: bool = False, timeout: int = 1000): + return run_with_timeout(self._instances, timeout, (ce, direct)) def equivalent_classes(self, ce: OWLClassExpression) -> List[OWLClassExpression]: """ @@ -1626,11 +1640,12 @@ def infer_axioms_and_save(self, output_path: str = None, output_format: str = No 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) + # CD: No need to create a new ontology + # inferred_axioms_ontology = self._owlapi_manager.createOntology() + iog.fillOntology(self._owlapi_manager.getOWLDataFactory(), self._owlapi_ontology) + self._owlapi_manager.saveOntology(self._owlapi_ontology, + document_format, + FileOutputStream(File(output_path).getAbsoluteFile())) def generate_and_save_inferred_class_assertion_axioms(self, output="temp.ttl", output_format: str = None): """ @@ -1661,5 +1676,34 @@ def generate_and_save_inferred_class_assertion_axioms(self, output="temp.ttl", o """ self.infer_axioms_and_save(output, output_format, ["InferredClassAssertionAxiomGenerator"]) - def get_root_ontology(self) -> OWLOntology: + def is_entailed(self, axiom: OWLAxiom) -> bool: + """A convenience method that determines if the specified axiom is entailed by the set of reasoner axioms. + + Args: + axiom: The axiom to check for entailment. + + Return: + True if the axiom is entailed by the reasoner axioms and False otherwise. + """ + return bool(self._owlapi_reasoner.isEntailed(self.mapper.map_(axiom))) + + def is_satisfiable(self, ce: OWLClassExpression) -> bool: + """A convenience method that determines if the specified class expression is satisfiable with respect + to the reasoner axioms. + + Args: + ce: The class expression to check for satisfiability. + + Return: + True if the class expression is satisfiable by the reasoner axioms and False otherwise. + """ + + return bool(self._owlapi_reasoner.isSatisfiable(self.mapper.map_(ce))) + + def unsatisfiable_classes(self): + """A convenience method that obtains the classes in the signature of the root ontology that are + unsatisfiable.""" + return self.mapper.map_(self._owlapi_reasoner.unsatisfiableClasses()) + + def get_root_ontology(self) -> AbstractOWLOntology: return self.ontology diff --git a/owlapy/owlapi_mapper.py b/owlapy/owlapi_mapper.py index efbd9ac8..b72c940b 100644 --- a/owlapy/owlapi_mapper.py +++ b/owlapy/owlapi_mapper.py @@ -110,6 +110,8 @@ def __init__(self, ontology: _SO): ontology_set.add(self.ontology) bidi_provider = BidirectionalShortFormProviderAdapter(self.manager, ontology_set, SimpleShortFormProvider()) entity_checker = ShortFormEntityChecker(bidi_provider) + bidi_provider.add(self.manager.getOWLDataFactory().getOWLNothing()) + bidi_provider.add(self.manager.getOWLDataFactory().getOWLThing()) self.parser = ManchesterOWLSyntaxClassExpressionParser(self.manager.getOWLDataFactory(), entity_checker) self.renderer = ManchesterOWLSyntaxOWLObjectRendererImpl() @@ -507,6 +509,11 @@ def _(self, e): java_list.add(self.map_(item)) return java_list + @map_.register(Stream) + def _(self, e): + for en in self.to_list(e): + yield self.map_(en) + @staticmethod def to_list(stream_obj): """Converts Java Stream object to Python list""" diff --git a/owlapy/render.py b/owlapy/render.py index 0e30bb6d..dd71dc03 100644 --- a/owlapy/render.py +++ b/owlapy/render.py @@ -20,7 +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 .abstracts.abstract_owl_reasoner import OWLReasoner +from .abstracts.abstract_owl_reasoner import AbstractOWLReasoner import requests import warnings import abc @@ -78,7 +78,7 @@ def translating_short_form_provider(e: OWLEntity, reasoner, rules: dict[str:str] label_iri = "http://www.w3.org/2000/01/rdf-schema#label" def get_label(entity, r, predicate=label_iri): - if isinstance(r, OWLReasoner): + if isinstance(r, AbstractOWLReasoner): values = list(r.data_property_values(entity, OWLDataProperty(predicate))) if values: return str(values[0].get_literal()) diff --git a/owlapy/scripts/run.py b/owlapy/scripts/run.py new file mode 100644 index 00000000..c517b276 --- /dev/null +++ b/owlapy/scripts/run.py @@ -0,0 +1,54 @@ +import argparse +from owlapy.owl_reasoner import SyncReasoner + +inference_types = ["InferredClassAssertionAxiomGenerator", + "InferredSubClassAxiomGenerator", + "InferredDisjointClassesAxiomGenerator", + "InferredEquivalentClassAxiomGenerator", + "InferredEquivalentDataPropertiesAxiomGenerator", + "InferredEquivalentObjectPropertyAxiomGenerator", + "InferredInverseObjectPropertiesAxiomGenerator", + "InferredSubDataPropertyAxiomGenerator", + "InferredSubObjectPropertyAxiomGenerator", + "InferredDataPropertyCharacteristicAxiomGenerator", + "InferredObjectPropertyCharacteristicAxiomGenerator" + ] + + +def get_default_arguments(description=None): + parser = argparse.ArgumentParser(add_help=False) + parser.add_argument("--path_ontology", type=str, default="KGs/Family/family-benchmark_rich_background.owl", + help="The path of a folder containing the ontology" + ",e.g., KGs/Family/family-benchmark_rich_background.owl.") + parser.add_argument("--inference_types", type=str, default="all", + nargs='+', + choices=inference_types + ["all"], + help="The type of axioms that you want to infer. This argument accepts multiple values." + "You can use 'all' to infer all of them.") + + parser.add_argument("--out_ontology", type=str, default="inferred_axioms_ontology.owl", + help="Path of a file to save the output ontology containing the inferred axioms.") + parser.add_argument("--output_type", type=str, default=None, choices=["ttl", "rdf/xml", "owl/xml"], + help="Filetype of the output ontology.") + + if description is None: + return parser.parse_args() + return parser.parse_args(description) + + +def main(): + + args = get_default_arguments() + sync_reasoner = SyncReasoner(ontology=args.path_ontology) + if "all" in args.inference_types: + it = inference_types + else: + it = args.inference_types + sync_reasoner.infer_axioms_and_save(output_path=args.out_ontology, + output_format=args.output_type, + inference_types=it) + print("\nFinished inferring axioms \nOutput filename: '{}'".format(args.out_ontology)) + + +if __name__ == '__main__': + main() diff --git a/owlapy/utils.py b/owlapy/utils.py index 6a81a8fc..ea293916 100644 --- a/owlapy/utils.py +++ b/owlapy/utils.py @@ -20,6 +20,17 @@ from .owl_object import OWLObject from .owl_datatype import OWLDatatype +import concurrent.futures +def run_with_timeout(func, timeout, args=(), **kwargs): + with concurrent.futures.ThreadPoolExecutor() as executor: + future = executor.submit(func, *args, **kwargs) + try: + result = future.result(timeout=timeout) + return result + except concurrent.futures.TimeoutError: + return set() + + def concept_reducer(concepts:Iterable, opt:Callable): """ Reduces a set of concepts by applying a binary operation to each pair of concepts. diff --git a/setup.py b/setup.py index b04bd1b3..14e869cc 100644 --- a/setup.py +++ b/setup.py @@ -5,12 +5,10 @@ setup( name="owlapy", description="OWLAPY is a Python Framework for creating and manipulating OWL Ontologies.", - version="1.3.0", + version="1.3.1", packages=find_packages(), include_package_data=True, - package_data={ - 'owlapy': ['jar_dependencies/*.jar'], - }, + package_data={'owlapy': ['jar_dependencies/*.jar'],}, install_requires=[ "pandas>=1.5.0", "requests>=2.32.3", @@ -19,7 +17,8 @@ "pytest>=8.1.1", "sortedcontainers>=2.4.0", "owlready2>=0.40", - "JPype1>=1.5.0"], + "JPype1>=1.5.0", + "tqdm>=4.66.5"], author='Caglar Demir', author_email='caglardemir8@gmail.com', url='https://github.com/dice-group/owlapy', @@ -28,6 +27,7 @@ "License :: OSI Approved :: MIT License", "Topic :: Scientific/Engineering"], python_requires='>=3.10.13', + entry_points={"console_scripts": ["owlapy=owlapy.scripts.run:main"]}, long_description=long_description, long_description_content_type="text/markdown", ) diff --git a/tests/test_owlapy_command.py b/tests/test_owlapy_command.py new file mode 100644 index 00000000..88387b16 --- /dev/null +++ b/tests/test_owlapy_command.py @@ -0,0 +1,98 @@ +import subprocess +import sys +import unittest + +from owlapy.class_expression import OWLClass, OWLNothing +from owlapy.iri import IRI +from owlapy.owl_axiom import OWLSymmetricObjectPropertyAxiom, OWLReflexiveObjectPropertyAxiom +from owlapy.owl_ontology_manager import SyncOntologyManager +from owlapy.owl_property import OWLObjectProperty +from owlapy.owl_reasoner import SyncReasoner + + +class TestOwlapyCommand(unittest.TestCase): + + def test_owlapy_entry_point(self): + result = subprocess.run([sys.executable, + '-m', + 'owlapy.scripts.run', + '--path_ontology', + 'KGs/Family/family-benchmark_rich_background.owl' + ]) + + self.assertEqual(result.returncode, 0) + + onto = SyncOntologyManager().load_ontology("inferred_axioms_ontology.owl") + + ops = onto.object_properties_in_signature() + self.assertEqual(list(ops), [OWLObjectProperty(IRI('http://www.benchmark.org/family#', 'hasChild')), + OWLObjectProperty(IRI('http://www.benchmark.org/family#', 'hasParent')), + OWLObjectProperty(IRI('http://www.benchmark.org/family#', 'hasSibling')), + OWLObjectProperty(IRI('http://www.benchmark.org/family#', 'married')), + OWLObjectProperty(IRI('http://www.w3.org/2002/07/owl#', 'topObjectProperty'))]) + + reasoner = SyncReasoner(onto) + mapper = reasoner.mapper + from org.semanticweb.owlapi.model import AxiomType + symetrical_axioms = mapper.map_( + onto.get_owlapi_ontology().getAxioms(AxiomType.SYMMETRIC_OBJECT_PROPERTY).stream()) + + reflexive_axioms = mapper.map_( + onto.get_owlapi_ontology().getAxioms(AxiomType.REFLEXIVE_OBJECT_PROPERTY).stream()) + + self.assertEqual(list(symetrical_axioms)[0], + OWLSymmetricObjectPropertyAxiom(OWLObjectProperty(IRI('http://www.w3.org/2002/07/owl#', + 'topObjectProperty')), [])) + + self.assertEqual(list(reflexive_axioms)[0], + OWLReflexiveObjectPropertyAxiom(OWLObjectProperty(IRI('http://www.w3.org/2002/07/owl#', + 'topObjectProperty')), [])) + + classes = onto.classes_in_signature() + self.assertEqual(list(classes), [OWLClass(IRI('http://www.benchmark.org/family#', 'Brother')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Child')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Daughter')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Father')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Female')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Grandchild')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Granddaughter')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Grandfather')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Grandmother')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Grandparent')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Grandson')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Male')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Mother')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Parent')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Person')), + OWLClass(IRI('http://www.benchmark.org/family#', 'PersonWithASibling')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Sister')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Son')), + OWLClass(IRI('http://www.w3.org/2002/07/owl#', 'Nothing')), + OWLClass(IRI('http://www.w3.org/2002/07/owl#', 'Thing'))]) + + disjoint_of_owl_nothing = reasoner.disjoint_classes(OWLNothing) + self.assertCountEqual(list(disjoint_of_owl_nothing), + [OWLClass(IRI('http://www.benchmark.org/family#', 'Grandchild')), + OWLClass(IRI('http://www.w3.org/2002/07/owl#', 'Thing')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Person')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Grandparent')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Grandfather')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Male')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Son')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Grandson')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Female')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Granddaughter')), + OWLClass(IRI('http://www.benchmark.org/family#', 'PersonWithASibling')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Sister')), + OWLClass(IRI('http://www.w3.org/2002/07/owl#', 'Nothing')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Grandmother')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Daughter')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Brother')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Mother')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Child')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Father')), + OWLClass(IRI('http://www.benchmark.org/family#', 'Parent'))]) + + len_inds = len(list(onto.individuals_in_signature())) + self.assertEqual(len_inds, 202) + diff --git a/tests/test_owlapy_owl2sparql_converter.py b/tests/test_owlapy_owl2sparql_converter.py index b240cb78..477e3769 100644 --- a/tests/test_owlapy_owl2sparql_converter.py +++ b/tests/test_owlapy_owl2sparql_converter.py @@ -1,23 +1,18 @@ import unittest - import rdflib.plugins.sparql.sparql - - from owlapy.class_expression import OWLObjectSomeValuesFrom, OWLThing, \ OWLObjectMaxCardinality, OWLObjectMinCardinality, OWLObjectIntersectionOf from owlapy.iri import IRI from owlapy.owl_property import OWLObjectProperty - from owlapy.owl_ontology_manager import OntologyManager from owlapy.owl_reasoner import OntologyReasoner, FastInstanceCheckerReasoner from owlapy.parser import DLSyntaxParser from rdflib import Graph from owlapy.converter import Owl2SparqlConverter - +from owlapy import owl_expression_to_sparql_with_confusion_matrix 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 def check_reasoner_instances_in_sparql_results(sparql_results: rdflib.query.Result, reasoner_results: set) -> bool: @@ -37,7 +32,7 @@ def check_reasoner_instances_in_sparql_results(sparql_results: rdflib.query.Resu return True -class Test_Owl2SparqlConverter(unittest.TestCase): +class TestOwl2SparqlConverter(unittest.TestCase): _root_var_ = '?x' maxDiff = None @@ -408,6 +403,42 @@ def test_Exists(self): # # self.assertEqual(len(sparql_results_actual), len(reasoner_results)) # # self.assertTrue(check_reasoner_instances_in_sparql_results(sparql_results_actual, reasoner_results)) + def test_ConfusionMatrixQuery(self): + # rdf graph - using rdflib + family_rdf_graph = Graph() + family_rdf_graph.parse(location=PATH_FAMILY) + + ce_str = "Brother" + ce_parsed = DLSyntaxParser(namespace="http://www.benchmark.org/family#").parse_expression(expression_str=ce_str) + + positive_examples = [ + DLSyntaxParser(namespace="http://www.benchmark.org/family#").parse_expression(expression_str="F10M173"), + DLSyntaxParser(namespace="http://www.benchmark.org/family#").parse_expression(expression_str="F10M183"), + ] + + negative_examples = [ + DLSyntaxParser(namespace="http://www.benchmark.org/family#").parse_expression(expression_str="F10M184"), + DLSyntaxParser(namespace="http://www.benchmark.org/family#").parse_expression(expression_str="F10F179"), + ] + + query_with_confusion_matrix = Owl2SparqlConverter().as_confusion_matrix_query(root_variable=self._root_var_, + ce=ce_parsed, + positive_examples=positive_examples, + negative_examples=negative_examples, + for_all_de_morgan=True, + named_individuals=True) + + assert owl_expression_to_sparql_with_confusion_matrix(root_variable=self._root_var_, + expression=ce_parsed, + positive_examples=positive_examples, + negative_examples=negative_examples, + for_all_de_morgan=True, + named_individuals=True) + sparql_results = family_rdf_graph.query(query_with_confusion_matrix) + self.assertEqual(int(sparql_results.bindings[0]["tp"]), 2) + self.assertEqual(int(sparql_results.bindings[0]["fn"]), 0) + self.assertEqual(int(sparql_results.bindings[0]["fp"]), 1) + self.assertEqual(int(sparql_results.bindings[0]["tn"]), 1) if __name__ == '__main__': unittest.main() diff --git a/tests/test_reasoner_alc_father_ontology.py b/tests/test_reasoner_alc_father_ontology.py new file mode 100644 index 00000000..8b3bc5e9 --- /dev/null +++ b/tests/test_reasoner_alc_father_ontology.py @@ -0,0 +1,59 @@ +from owlapy.owl_reasoner import SyncReasoner +from owlapy.class_expression import OWLClass, OWLObjectSomeValuesFrom, OWLObjectAllValuesFrom +from owlapy.owl_individual import OWLNamedIndividual +from owlapy.iri import IRI +from owlapy.owl_property import OWLObjectProperty + + +class TestHermitFather: + def test_readme(self): + + ontology_path = "KGs/Family/father.owl" + hermit = SyncReasoner(ontology=ontology_path, reasoner="HermiT") + + # Thing + thing = OWLClass(IRI('http://www.w3.org/2002/07/owl#', 'Thing')) + # Nothing + nothing = OWLClass(IRI('http://www.w3.org/2002/07/owl#', 'Nothing')) + + # Person OWL Class + person = eval("OWLClass(IRI('http://example.com/father#', 'person'))") + # Female OWL CLass + female = eval("OWLClass(IRI('http://example.com/father#', 'female'))") + # hasChild object property + hasChild = OWLObjectProperty(IRI('http://example.com/father#', 'hasChild')) + + # Sanity checking = Things + assert hermit.instances(thing) == {OWLNamedIndividual(IRI('http://example.com/father#', 'anna')), OWLNamedIndividual(IRI('http://example.com/father#', 'martin')), OWLNamedIndividual(IRI('http://example.com/father#', 'heinz')), OWLNamedIndividual(IRI('http://example.com/father#', 'stefan')), OWLNamedIndividual(IRI('http://example.com/father#', 'michelle')), OWLNamedIndividual(IRI('http://example.com/father#', 'markus'))} + # De Morgen Rules : Thing \equiv \neg Bottom + assert hermit.instances(thing) == hermit.instances(nothing.get_object_complement_of()) + + # Sanity checking = \exist hasChild Thing. + assert hermit.instances(OWLObjectSomeValuesFrom(property=hasChild, filler=thing)) == eval( + "{OWLNamedIndividual(IRI('http://example.com/father#', 'markus')), OWLNamedIndividual(IRI('http://example.com/father#', 'martin')), OWLNamedIndividual(IRI('http://example.com/father#', 'stefan')), OWLNamedIndividual(IRI('http://example.com/father#', 'anna'))}") + # Sanity checking \exist hasChild Person. + assert hermit.instances(OWLObjectSomeValuesFrom(property=hasChild, filler=person)) == eval( + "{OWLNamedIndividual(IRI('http://example.com/father#', 'markus')), OWLNamedIndividual(IRI('http://example.com/father#', 'martin')), OWLNamedIndividual(IRI('http://example.com/father#', 'stefan')), OWLNamedIndividual(IRI('http://example.com/father#', 'anna'))}") + # \exist hasChild a person = \exist hasChild a thing + assert hermit.instances(OWLObjectSomeValuesFrom(property=hasChild, filler=thing)) == hermit.instances( + OWLObjectSomeValuesFrom(property=hasChild, filler=person)) + + # Sanity checking: \exist hasChild a female. + assert hermit.instances(OWLObjectSomeValuesFrom(property=hasChild, filler=female)) == eval( + "{OWLNamedIndividual(IRI('http://example.com/father#', 'markus'))}") + # Question: hasChild something that hasChild a female. + # Answer: stefan: (stefan haschild markus) and (markus haschild anna) + assert hermit.instances(OWLObjectSomeValuesFrom(property=hasChild, + filler=OWLObjectSomeValuesFrom(property=hasChild, + filler=female))) == eval( + "{OWLNamedIndividual(IRI('http://example.com/father#', 'stefan'))}") + # De morgen rule: \neg \exist r \neg T = \forall r T + c = thing + forall_r_c = OWLObjectAllValuesFrom(hasChild, c) + neg_exist_r_neg_c = OWLObjectSomeValuesFrom(hasChild, c.get_object_complement_of()).get_object_complement_of() + assert hermit.instances(neg_exist_r_neg_c) == hermit.instances(forall_r_c) + # De morgen rule: \neg \exist r \neg bottom = \forall r bottom + c = nothing + forall_r_c = OWLObjectAllValuesFrom(hasChild, c) + neg_exist_r_neg_c = OWLObjectSomeValuesFrom(hasChild, c.get_object_complement_of()).get_object_complement_of() + assert hermit.instances(neg_exist_r_neg_c) == hermit.instances(forall_r_c) \ No newline at end of file diff --git a/tests/test_sync_ontology.py b/tests/test_sync_ontology.py index 42c7727e..c35d7462 100644 --- a/tests/test_sync_ontology.py +++ b/tests/test_sync_ontology.py @@ -1,8 +1,9 @@ import unittest -from owlapy.class_expression import OWLClass, OWLObjectIntersectionOf, OWLObjectSomeValuesFrom +from owlapy.class_expression import OWLClass, OWLObjectIntersectionOf, OWLObjectSomeValuesFrom, OWLObjectComplementOf from owlapy.iri import IRI -from owlapy.owl_axiom import OWLEquivalentClassesAxiom +from owlapy.owl_axiom import OWLEquivalentClassesAxiom, OWLClassAssertionAxiom, OWLObjectPropertyAssertionAxiom, \ + OWLSubClassOfAxiom, OWLObjectPropertyRangeAxiom, OWLObjectPropertyDomainAxiom from owlapy.owl_individual import OWLNamedIndividual from owlapy.owl_ontology import OWLOntologyID from owlapy.owl_ontology_manager import SyncOntologyManager @@ -69,6 +70,9 @@ T = OWLClass(IRI(NS, 'T')) U = OWLClass(IRI(NS, 'U')) +father_onto_path = "KGs/Family/father.owl" +father_manager = SyncOntologyManager() +father_onto = father_manager.load_ontology(father_onto_path) class TestSyncReasoner(unittest.TestCase): @@ -110,3 +114,39 @@ def test_get_ontology_id(self): def test__eq__(self): onto2 = self.manager.load_ontology(self.ontology_path) self.assertTrue(self.onto.__eq__(onto2)) + + def test_get_signature(self): + self.assertCountEqual(father_onto.get_signature(), + [OWLClass(IRI('http://example.com/father#', 'female')), + OWLClass(IRI('http://example.com/father#', 'male')), + OWLClass(IRI('http://example.com/father#', 'person')), + OWLClass(IRI('http://www.w3.org/2002/07/owl#', 'Thing')), + OWLObjectProperty(IRI('http://example.com/father#', 'hasChild')), + OWLNamedIndividual(IRI('http://example.com/father#', 'anna')), + OWLNamedIndividual(IRI('http://example.com/father#', 'heinz')), + OWLNamedIndividual(IRI('http://example.com/father#', 'markus')), + OWLNamedIndividual(IRI('http://example.com/father#', 'martin')), + OWLNamedIndividual(IRI('http://example.com/father#', 'michelle')), + OWLNamedIndividual(IRI('http://example.com/father#', 'stefan'))]) + + def test_get_abox(self): + self.assertCountEqual(father_onto.get_abox_axioms(), + [OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://example.com/father#', 'martin')),class_expression=OWLClass(IRI('http://example.com/father#', 'male')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://example.com/father#', 'markus')),class_expression=OWLClass(IRI('http://example.com/father#', 'male')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://example.com/father#', 'michelle')),class_expression=OWLClass(IRI('http://example.com/father#', 'female')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://example.com/father#', 'heinz')),class_expression=OWLClass(IRI('http://example.com/father#', 'male')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://example.com/father#', 'stefan')),class_expression=OWLClass(IRI('http://example.com/father#', 'male')),annotations=[]), + OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://example.com/father#', 'anna')),class_expression=OWLClass(IRI('http://example.com/father#', 'female')),annotations=[]), + OWLObjectPropertyAssertionAxiom(subject=OWLNamedIndividual(IRI('http://example.com/father#', 'anna')),property_=OWLObjectProperty(IRI('http://example.com/father#', 'hasChild')),object_=OWLNamedIndividual(IRI('http://example.com/father#', 'heinz')),annotations=[]), + OWLObjectPropertyAssertionAxiom(subject=OWLNamedIndividual(IRI('http://example.com/father#', 'stefan')),property_=OWLObjectProperty(IRI('http://example.com/father#', 'hasChild')),object_=OWLNamedIndividual(IRI('http://example.com/father#', 'markus')),annotations=[]), + OWLObjectPropertyAssertionAxiom(subject=OWLNamedIndividual(IRI('http://example.com/father#', 'markus')),property_=OWLObjectProperty(IRI('http://example.com/father#', 'hasChild')),object_=OWLNamedIndividual(IRI('http://example.com/father#', 'anna')),annotations=[]), + OWLObjectPropertyAssertionAxiom(subject=OWLNamedIndividual(IRI('http://example.com/father#', 'martin')),property_=OWLObjectProperty(IRI('http://example.com/father#', 'hasChild')),object_=OWLNamedIndividual(IRI('http://example.com/father#', 'heinz')),annotations=[])]) + + def test_get_tbox(self): + self.assertCountEqual(father_onto.get_tbox_axioms(), + [OWLEquivalentClassesAxiom([OWLClass(IRI('http://example.com/father#', 'male')), OWLObjectComplementOf(OWLClass(IRI('http://example.com/father#', 'female')))],[]), + OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://example.com/father#', 'female')),super_class=OWLClass(IRI('http://example.com/father#', 'person')),annotations=[]), + OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://example.com/father#', 'male')),super_class=OWLClass(IRI('http://example.com/father#', 'person')),annotations=[]), + OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://example.com/father#', 'person')),super_class=OWLClass(IRI('http://www.w3.org/2002/07/owl#', 'Thing')),annotations=[]), + OWLObjectPropertyRangeAxiom(OWLObjectProperty(IRI('http://example.com/father#', 'hasChild')),OWLClass(IRI('http://example.com/father#', 'person')),[]), + OWLObjectPropertyDomainAxiom(OWLObjectProperty(IRI('http://example.com/father#', 'hasChild')),OWLClass(IRI('http://example.com/father#', 'person')),[])]) diff --git a/tests/test_sync_reasoner.py b/tests/test_sync_reasoner.py index 2e32d217..f45cb8a8 100644 --- a/tests/test_sync_reasoner.py +++ b/tests/test_sync_reasoner.py @@ -3,7 +3,7 @@ from jpype import JDouble from owlapy.class_expression import OWLClass, OWLDataSomeValuesFrom, OWLObjectIntersectionOf, OWLNothing, OWLThing, \ - OWLClassExpression + OWLClassExpression, OWLObjectSomeValuesFrom from owlapy.iri import IRI from owlapy.owl_axiom import OWLDisjointClassesAxiom, OWLDeclarationAxiom, OWLClassAssertionAxiom, OWLSubClassOfAxiom, \ OWLEquivalentClassesAxiom, OWLSubDataPropertyOfAxiom, OWLSubObjectPropertyOfAxiom @@ -126,7 +126,7 @@ def test_inconsistency_check(self): onto.add_axiom(OWLClassAssertionAxiom(new_individual, self.nitrogen38)) onto.add_axiom(OWLClassAssertionAxiom(new_individual, carbon230)) - onto.save(IRI.create("file:/test.owl")) + onto.save("test.owl") reasoner = SyncReasoner("test.owl") self.assertEqual(reasoner.has_consistent_ontology(), False) os.remove("test.owl") @@ -350,4 +350,21 @@ def test_infer_axiom(self): 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 + 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=[])]) + + def test_entailment(self): + self.assertTrue(reasoner2.is_entailed(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=[]))) + self.assertFalse(reasoner2.is_entailed(OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','B')),super_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','D')),annotations=[]))) + self.assertFalse(reasoner2.is_entailed(OWLSubClassOfAxiom(sub_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','C')),super_class=OWLClass(IRI('http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#','G')),annotations=[]))) + + def test_satisfiability(self): + ST = OWLObjectIntersectionOf([S, T]) + LM = OWLObjectIntersectionOf([L, M]) + r7E = OWLObjectSomeValuesFrom(property=r7, filler=E) + self.assertTrue(reasoner2.is_satisfiable(ST)) + self.assertTrue(reasoner2.is_satisfiable(r7E)) + self.assertFalse(reasoner2.is_satisfiable(LM)) + + def test_unsatisfiability(self): + self.assertEqual(list(reasoner2.unsatisfiable_classes()), [OWLNothing]) + self.assertNotEquals(list(reasoner2.unsatisfiable_classes()), [OWLThing])