From 79c982d61d62144d674e1ac9eb7acd663284cfd7 Mon Sep 17 00:00:00 2001 From: Alkid Date: Tue, 5 Nov 2024 14:43:03 +0100 Subject: [PATCH 01/24] new release: 1.3.2 --- README.md | 4 +-- docs/conf.py | 2 +- docs/usage/further_resources.md | 46 ++++++++++++++++++--------------- docs/usage/main.md | 2 +- owlapy/__init__.py | 2 +- setup.py | 2 +- 6 files changed, 31 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 352faa24..9eb842a7 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.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) +[![Pypi](https://img.shields.io/badge/pypi-1.3.2-blue)](https://pypi.org/project/owlapy/1.3.2/) +[![Docs](https://img.shields.io/badge/documentation-1.3.2-yellow)](https://dice-group.github.io/owlapy/usage/main.html) ![OWLAPY](docs/_static/images/owlapy_logo.png) diff --git a/docs/conf.py b/docs/conf.py index 2c38198a..9e37f605 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -13,7 +13,7 @@ project = 'OWLAPY' author = 'Ontolearn Team' -release = '1.3.1' +release = '1.3.2' # -- General configuration --------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration diff --git a/docs/usage/further_resources.md b/docs/usage/further_resources.md index 46a55f2a..57df4007 100644 --- a/docs/usage/further_resources.md +++ b/docs/usage/further_resources.md @@ -26,34 +26,38 @@ The coverage report is generated using [coverage.py](https://coverage.readthedoc ``` Name Stmts Miss Cover Missing ---------------------------------------------------------------------------------- -owlapy/__init__.py 4 0 100% -owlapy/class_expression/__init__.py 8 0 100% -owlapy/class_expression/class_expression.py 34 2 94% 58, 62 -owlapy/class_expression/nary_boolean_expression.py 24 0 100% +owlapy/__init__.py 6 0 100% +owlapy/abstracts/__init__.py 3 0 100% +owlapy/abstracts/abstract_owl_ontology.py 16 1 94% 151 +owlapy/abstracts/abstract_owl_ontology_manager.py 10 1 90% 24 +owlapy/abstracts/abstract_owl_reasoner.py 49 10 80% 409-417, 439, 464 +owlapy/class_expression/__init__.py 9 0 100% +owlapy/class_expression/class_expression.py 34 3 91% 58, 62, 103 +owlapy/class_expression/nary_boolean_expression.py 25 0 100% owlapy/class_expression/owl_class.py 33 1 97% 44 -owlapy/class_expression/restriction.py 313 26 92% 41, 49, 68, 71, 89, 170, 245-246, 302, 305, 335, 340, 343, 426, 451, 499, 502, 579-580, 616, 659, 662, 700, 703, 751, 823 -owlapy/converter.py 397 189 52% 52-68, 75-76, 79, 82, 152, 157, 169, 176, 184, 246-257, 264-282, 294, 304-307, 313-359, 366-387, 394-401, 417-420, 431, 451, 460-481, 489-491, 498-511, 515-521, 525-548, 552-555, 559-560, 564-576, 580-587, 591-592, 620, 624-628 -owlapy/iri.py 79 7 91% 54, 69, 82, 97, 128, 133, 150 +owlapy/class_expression/restriction.py 313 27 91% 41, 49, 68, 71, 89, 171, 245-246, 303, 336, 342, 345, 419, 430, 439, 456, 502, 505, 582-583, 620, 663, 666, 706, 709, 758, 830 +owlapy/converter.py 420 174 59% 52-68, 75-76, 79, 82, 152, 157, 169, 176, 184, 277, 294, 304-307, 313-359, 366-387, 394-401, 417-420, 431, 451, 460-481, 489-491, 498-511, 515-521, 525-548, 552-555, 559-560, 564-576, 580-587, 591-592, 621, 625-629 +owlapy/iri.py 79 6 92% 54, 69, 82, 97, 128, 133 owlapy/meta_classes.py 11 0 100% owlapy/namespaces.py 27 3 89% 36, 40, 43 -owlapy/owl_annotation.py 17 4 76% 17, 25, 43, 51 -owlapy/owl_axiom.py 518 157 70% 36, 39, 42, 45, 59, 111-113, 116, 136-138, 141, 144, 147-150, 153, 182-184, 187, 190, 193, 196-200, 203, 253-256, 259-261, 264, 288, 291, 294, 332-335, 338-340, 343, 398-401, 404-406, 409, 533-536, 539, 561-563, 566, 569, 572, 575, 578-581, 584, 620-623, 626, 645-648, 652, 656, 674-675, 683, 692, 695-697, 700, 711, 733-737, 745, 753, 761, 764-766, 769, 786-788, 791, 794, 797-800, 803, 822-824, 827, 830, 833-836, 839, 858-860, 863, 866, 869-872, 875, 905-908, 911, 982-985, 988, 1018, 1044, 1071-1073, 1076, 1091, 1103, 1116, 1129, 1142, 1157, 1172, 1185-1187, 1190, 1208, 1227-1230, 1233, 1254-1257, 1260 +owlapy/owl_annotation.py 16 4 75% 16, 24, 42, 50 +owlapy/owl_axiom.py 519 130 75% 39, 42, 45, 59, 111-113, 116, 136-138, 141, 144, 147-150, 153, 182-184, 187, 190, 193, 196-200, 203, 253-256, 261, 288, 291, 294, 332-335, 338-340, 343, 398-401, 404-406, 409, 536, 561-563, 566, 569, 572, 575, 578-581, 584, 623, 645-648, 652, 656, 674-675, 683, 692, 695-697, 700, 711, 734-738, 746, 754, 762, 765-767, 770, 787-789, 792, 795, 798-801, 804, 823-825, 828, 831, 834-837, 840, 859-861, 864, 867, 870-873, 876, 909, 986, 1019, 1045, 1074, 1077, 1092, 1104, 1117, 1130, 1173, 1186-1188, 1191, 1209 owlapy/owl_data_ranges.py 40 1 98% 46 owlapy/owl_datatype.py 20 2 90% 33-34 owlapy/owl_individual.py 20 1 95% 37 -owlapy/owl_literal.py 286 73 74% 49, 77, 86, 90, 99, 103, 112, 116, 125, 129, 138, 142, 151, 155, 164, 169, 173, 203, 208, 217, 221, 244, 247-249, 252, 258, 262, 288, 293, 302, 306, 311, 323, 329, 332-334, 337, 340, 346, 350, 355, 373, 376-378, 381, 387, 391, 415, 418-420, 423, 429, 433, 454, 459, 462-464, 467, 473, 477, 489-491, 494, 497-499, 502 -owlapy/owl_object.py 27 4 85% 24, 79-81 -owlapy/owl_ontology.py 391 40 90% 86, 97-100, 103, 109-111, 249, 292-295, 304, 312, 329, 341, 345, 358, 371, 376, 379-381, 384, 423, 433, 449-450, 473-474, 553-554, 595, 599, 603, 629, 736, 742, 750 -owlapy/owl_ontology_manager.py 568 167 71% 48, 140, 151, 155, 168-169, 177, 200, 208-211, 312-318, 341-350, 355-376, 396, 466, 469, 474-496, 501-511, 521-527, 539, 542-543, 583, 588-593, 603, 608, 625, 634-645, 650-665, 676, 681, 691, 703, 707, 743, 749, 760, 766, 771-795, 800-807, 825-831, 850, 853, 859-862, 888 +owlapy/owl_literal.py 286 66 77% 49, 77, 86, 90, 99, 103, 112, 116, 125, 129, 138, 142, 151, 155, 164, 169, 173, 203, 208, 217, 221, 244, 247-249, 258, 262, 288, 293, 302, 306, 311, 323, 329, 332-334, 337, 340, 346, 350, 355, 373, 378, 387, 391, 415, 420, 429, 433, 454, 459, 462-464, 467, 473, 477, 489-491, 494, 497-499, 502 +owlapy/owl_object.py 29 4 86% 26, 81-83 +owlapy/owl_ontology.py 1000 222 78% 99, 110-113, 116, 124, 142-148, 171, 179-182, 283-289, 312-321, 326-347, 367, 437, 440, 445-467, 472-482, 492-498, 510, 513-514, 554, 559-564, 574, 579, 596, 605-616, 621-636, 647, 652, 662, 674, 678, 714, 720, 731, 737, 742-766, 771-778, 807, 822-823, 841-844, 853, 861, 878, 890, 894, 907, 920, 928-929, 936-937, 942, 951-956, 963, 966-968, 971, 988, 992-993, 1017, 1020, 1023, 1026, 1029, 1036, 1074, 1083-1086, 1089-1092, 1097, 1100, 1140, 1150, 1166-1167, 1190-1191, 1270-1271, 1312, 1316, 1320, 1346, 1453, 1459, 1467 +owlapy/owl_ontology_manager.py 66 19 71% 26, 37, 41, 54-55, 63, 82, 85-89, 100-103, 112, 129-133 owlapy/owl_property.py 69 11 84% 17, 24, 32, 40, 67, 76, 126, 158, 162, 174, 193 -owlapy/owl_reasoner.py 841 175 79% 452-455, 572, 584-586, 591-597, 604, 653-659, 665-669, 727-734, 760, 795-799, 825-828, 856-858, 860-862, 871, 884-886, 888-890, 897, 902-904, 924, 928-929, 942-944, 965, 1010-1012, 1113, 1121, 1124, 1127, 1130, 1133, 1136, 1139, 1142, 1145, 1160-1162, 1168, 1172, 1175, 1178, 1181, 1184, 1187, 1193, 1196, 1210, 1240-1243, 1251-1290, 1305, 1318-1328, 1353-1356, 1372, 1386, 1456-1460, 1488, 1498-1502, 1510-1514, 1555-1561, 1573, 1632, 1635, 1638, 1641, 1644, 1647, 1650, 1653, 1657, 1661, 1665, 1668, 1671, 1674, 1677, 1680, 1683, 1687, 1691, 1694, 1697 -owlapy/owlapi_adaptor.py 130 65 50% 18, 74-76, 91-96, 110-115, 151-152, 164-165, 179-180, 195-196, 214, 232, 251, 271, 287, 305, 320, 333, 346, 361, 376, 390, 404, 419, 434, 450, 454-483, 511 -owlapy/owlapi_mapper.py 103 14 86% 35, 51, 72, 76, 80, 84, 88, 133-136, 141, 145, 149 -owlapy/parser.py 371 16 96% 316, 327, 400-401, 416, 577, 618, 656, 667, 721, 723, 751-752, 763, 779-780 +owlapy/owl_reasoner.py 845 131 84% 170, 182-184, 189-195, 202, 251-257, 263-265, 308-315, 341, 376-380, 406-409, 437-439, 441-443, 452, 465-467, 469-471, 478, 483-485, 505, 509-510, 523-525, 546, 591-593, 607-609, 627-628, 639-642, 645, 651, 675-684, 696, 701, 705, 753-756, 861-865, 887, 894, 904-908, 916-920, 961-967, 979, 1100-1102, 1202, 1334, 1349, 1364, 1506-1527, 1558, 1590 +owlapy/owlapi_mapper.py 357 62 83% 30, 106, 125-126, 193-200, 206-209, 218, 222, 229, 236, 240, 244, 248, 252, 256, 260, 264, 268, 272, 276, 280, 285, 291, 295, 299, 312, 328, 340, 350, 361, 366, 376, 381, 398, 416, 431, 436, 442, 447, 451, 456, 461, 466, 471-479, 508-509 +owlapy/parser.py 371 12 97% 316, 327, 400-401, 416, 656, 667, 751-752, 763, 779-780 owlapy/providers.py 38 3 92% 41, 54, 56 -owlapy/render.py 290 46 84% 79-114, 143-158, 176, 180, 186, 206, 222, 231, 236, 241, 375, 379, 386, 405, 421, 430, 435, 440 -owlapy/utils.py 766 227 70% 164, 168, 172, 178, 184-188, 192-196, 200, 204, 208, 214, 218, 222, 226, 230, 236, 242, 248, 252, 256, 260, 264-267, 271-274, 278, 285, 300-302, 305-314, 317, 320, 323, 326, 329, 333-339, 343, 354, 358, 362, 366, 370, 374-378, 382-386, 390-394, 398-402, 406, 410, 414-419, 423-428, 432-437, 441, 445, 449-453, 457-461, 465-469, 473-477, 481-485, 489, 493-497, 501, 505-510, 514-519, 523-528, 532, 536-540, 545, 554, 558, 562, 566, 570, 574, 578, 582-587, 591-597, 601, 605, 609, 614, 619, 624, 628, 632, 636, 640, 644-647, 651-654, 658, 662, 666, 671, 676, 681, 685, 736, 740, 746, 748, 751, 753, 796, 852, 866-868, 877, 919-920, 940, 1039, 1044, 1049, 1071, 1075, 1083, 1087, 1092, 1164-1182, 1195-1197, 1202-1206 -owlapy/vocab.py 92 4 96% 32, 35, 113-114 +owlapy/render.py 289 43 85% 78-113, 142-157, 175, 179, 185, 221, 230, 235, 240, 374, 378, 420, 429, 434, 439 +owlapy/static_funcs.py 33 19 42% 21-26, 31-42, 57-59 +owlapy/utils.py 794 304 62% 30-31, 54-58, 75-86, 229, 233, 237, 243, 249-253, 257-261, 265, 269, 273, 279, 283, 287, 291, 295, 301, 307, 313, 317, 321, 325, 329-332, 336-339, 343, 350, 365-367, 370-379, 382, 385, 388, 391, 394, 398-404, 408, 419, 423, 427, 431, 435, 439-443, 447-451, 455-459, 463-467, 471, 475, 479-484, 488-493, 497-502, 506, 510, 514-518, 522-526, 530-534, 538-542, 546-550, 554, 558-562, 566, 570-575, 579-584, 588-593, 597, 601-605, 610, 619, 623, 627, 631, 635, 639, 643, 647-652, 656-662, 666, 670, 674, 679, 684, 689, 693, 697, 701, 705, 709-712, 716-719, 723, 727, 731, 736, 741, 746, 750, 799, 801, 803, 805, 807, 811, 813, 816, 818, 835, 861, 917, 931-933, 941-942, 962, 984-985, 1005, 1042-1043, 1058-1059, 1076-1104, 1109, 1114, 1123-1152, 1157, 1162-1164, 1229-1247, 1260-1262, 1267-1271 +owlapy/vocab.py 92 3 97% 32, 113-114 ---------------------------------------------------------------------------------- -TOTAL 5517 1238 78% +TOTAL 5919 1263 79% ``` \ No newline at end of file diff --git a/docs/usage/main.md b/docs/usage/main.md index 2cf8cac5..36b77c2b 100644 --- a/docs/usage/main.md +++ b/docs/usage/main.md @@ -1,6 +1,6 @@ # About owlapy -**Version:** owlapy 1.3.1 +**Version:** owlapy 1.3.2 **GitHub repository:** [https://github.com/dice-group/owlapy](https://github.com/dice-group/owlapy) diff --git a/owlapy/__init__.py b/owlapy/__init__.py index 2b3878c9..84aec36b 100644 --- a/owlapy/__init__.py +++ b/owlapy/__init__.py @@ -3,7 +3,7 @@ from .converter import owl_expression_to_sparql, owl_expression_to_sparql_with_confusion_matrix from .owl_ontology_manager import OntologyManager -__version__ = '1.3.1' +__version__ = '1.3.2' __all__ = [ 'owl_expression_to_dl', 'owl_expression_to_manchester', diff --git a/setup.py b/setup.py index 14e869cc..bf8d39f5 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name="owlapy", description="OWLAPY is a Python Framework for creating and manipulating OWL Ontologies.", - version="1.3.1", + version="1.3.2", packages=find_packages(), include_package_data=True, package_data={'owlapy': ['jar_dependencies/*.jar'],}, From 208c92536c6e54133ca07174f18c1a5290e6d8cb Mon Sep 17 00:00:00 2001 From: Luke Friedrichs Date: Thu, 7 Nov 2024 22:23:54 +0100 Subject: [PATCH 02/24] changing _values to frozenset to allow for hashing --- owlapy/class_expression/restriction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/owlapy/class_expression/restriction.py b/owlapy/class_expression/restriction.py index 911a718a..cce981a1 100644 --- a/owlapy/class_expression/restriction.py +++ b/owlapy/class_expression/restriction.py @@ -403,7 +403,7 @@ def __init__(self, values: OWLIndividual | Iterable[OWLIndividual]): else: for _ in values: assert isinstance(_, OWLIndividual) - self._values = {i for i in values} + self._values = frozenset(values) def individuals(self) -> Iterable[OWLIndividual]: """Gets the individuals that are in the oneOf. These individuals represent the exact instances (extension) From 13e2ca3ffa85ac8433ba18b921c5c8dd00c9268d Mon Sep 17 00:00:00 2001 From: Luke Friedrichs Date: Fri, 8 Nov 2024 09:46:23 +0100 Subject: [PATCH 03/24] removed slots --- owlapy/owl_reasoner.py | 9 --------- 1 file changed, 9 deletions(-) diff --git a/owlapy/owl_reasoner.py b/owlapy/owl_reasoner.py index 211460d9..fc9cae95 100644 --- a/owlapy/owl_reasoner.py +++ b/owlapy/owl_reasoner.py @@ -36,15 +36,6 @@ class StructuralReasoner(AbstractOWLReasoner): """Tries to check instances fast (but maybe incomplete).""" - __slots__ = '_ontology', '_world', \ - '_ind_set', '_cls_to_ind', \ - '_has_prop', 'class_cache', \ - '_objectsomevalues_cache', '_datasomevalues_cache', '_objectcardinality_cache', \ - '_property_cache', \ - '_obj_prop', '_obj_prop_inv', '_data_prop', \ - '_negation_default', '_sub_properties', \ - '__warned' - _ontology: Ontology _world: owlready2.World _cls_to_ind: Dict[OWLClass, FrozenSet[OWLNamedIndividual]] # Class => individuals From 60e113ff47e666c6dd38b27e79ad9771bfed8208 Mon Sep 17 00:00:00 2001 From: Caglar Demir Date: Fri, 8 Nov 2024 09:58:38 +0100 Subject: [PATCH 04/24] save_owl_class_expressions and test included --- owlapy/util_owl_static_funcs.py | 46 ++++++++++++++++++++++++++++++ tests/test_save_owl_expressions.py | 25 ++++++++++++++++ 2 files changed, 71 insertions(+) create mode 100644 owlapy/util_owl_static_funcs.py create mode 100644 tests/test_save_owl_expressions.py diff --git a/owlapy/util_owl_static_funcs.py b/owlapy/util_owl_static_funcs.py new file mode 100644 index 00000000..3ed34a92 --- /dev/null +++ b/owlapy/util_owl_static_funcs.py @@ -0,0 +1,46 @@ +from .owl_ontology import Ontology +from .owl_ontology_manager import OntologyManager +from typing import List +from .class_expression import OWLClassExpression, OWLClass +from .iri import IRI +from .owl_axiom import OWLEquivalentClassesAxiom + +def save_owl_class_expressions(expressions: OWLClassExpression | List[OWLClassExpression], + path: str = 'predictions', + rdf_format: str = 'rdfxml', + namespace:str=None) -> None: + """ + + """ + + assert isinstance(expressions, OWLClassExpression) or isinstance(expressions[0], + OWLClassExpression), "expressions must be either OWLClassExpression or a list of OWLClassExpression" + assert rdf_format == 'rdfxml', f'Format {rdf_format} not implemented. Please use rdfxml' + + if isinstance(expressions, OWLClassExpression): + expressions = [expressions] + + namespace= 'https://dice-research.org/predictions#' if namespace is None else namespace + assert "#" == namespace[-1], "namespace must end with #" + # () + manager = OntologyManager() + # () + ontology:Ontology = manager.create_ontology(namespace) + # () Iterate over concepts + for th, i in enumerate(expressions): + cls_a = OWLClass(IRI.create(namespace, str(th))) + equivalent_classes_axiom = OWLEquivalentClassesAxiom([cls_a, i]) + try: + ontology.add_axiom(equivalent_classes_axiom) + except AttributeError: + print(traceback.format_exc()) + print("Exception at creating OWLEquivalentClassesAxiom") + print(equivalent_classes_axiom) + print(cls_a) + print(i) + print(expressions) + exit(1) + print(ontology) + ontology.save(path=path, inplace=False, rdf_format=rdf_format) + + # ontology.save(IRI.create(path)) diff --git a/tests/test_save_owl_expressions.py b/tests/test_save_owl_expressions.py new file mode 100644 index 00000000..3beda87b --- /dev/null +++ b/tests/test_save_owl_expressions.py @@ -0,0 +1,25 @@ +from owlapy.util_owl_static_funcs import save_owl_class_expressions +from owlapy.class_expression import OWLClass, OWLObjectIntersectionOf, OWLObjectSomeValuesFrom +from owlapy.owl_property import OWLObjectProperty +from owlapy import owl_expression_to_sparql, owl_expression_to_dl +from owlapy.owl_ontology_manager import OntologyManager +from owlapy.owl_axiom import OWLDeclarationAxiom, OWLClassAssertionAxiom +from owlapy.owl_individual import OWLNamedIndividual, IRI +import rdflib + +class TestRunningExamples: + def test_readme(self): + # Using owl classes to create a complex class expression + male = OWLClass("http://example.com/society#male") + hasChild = OWLObjectProperty("http://example.com/society#hasChild") + hasChild_male = OWLObjectSomeValuesFrom(hasChild, male) + teacher = OWLClass("http://example.com/society#teacher") + teacher_that_hasChild_male = OWLObjectIntersectionOf([hasChild_male, teacher]) + + expressions= [male, teacher_that_hasChild_male] + save_owl_class_expressions(expressions=expressions, + namespace="https://ontolearn.org/predictions#", + path="owl_class_expressions.owl", + rdf_format= 'rdfxml') + g=rdflib.Graph().parse("owl_class_expressions.owl") + assert len(g)==22 \ No newline at end of file From 3f6d2881f0f860b5114ade2ed8bc85d2182c8779 Mon Sep 17 00:00:00 2001 From: Caglar Demir Date: Fri, 8 Nov 2024 10:00:54 +0100 Subject: [PATCH 05/24] docstring added --- owlapy/util_owl_static_funcs.py | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/owlapy/util_owl_static_funcs.py b/owlapy/util_owl_static_funcs.py index 3ed34a92..e8012707 100644 --- a/owlapy/util_owl_static_funcs.py +++ b/owlapy/util_owl_static_funcs.py @@ -10,9 +10,32 @@ def save_owl_class_expressions(expressions: OWLClassExpression | List[OWLClassEx rdf_format: str = 'rdfxml', namespace:str=None) -> None: """ + Saves a set of OWL class expressions to an ontology file in RDF/XML format. - """ + This function takes one or more OWL class expressions, creates an ontology, + and saves the expressions as OWL equivalent class axioms in the specified RDF format. + By default, it saves the file to the specified path using the 'rdfxml' format. + + Args: + expressions (OWLClassExpression | List[OWLClassExpression]): A single or a list of OWL class expressions + to be saved as equivalent class axioms. + path (str, optional): The file path where the ontology will be saved. Defaults to 'predictions'. + rdf_format (str, optional): RDF serialization format for saving the ontology. Currently only + supports 'rdfxml'. Defaults to 'rdfxml'. + namespace (str, optional): The namespace URI used for the ontology. If None, defaults to + 'https://dice-research.org/predictions#'. Must end with '#'. + + Raises: + AssertionError: If `expressions` is neither an OWLClassExpression nor a list of OWLClassExpression. + AssertionError: If `rdf_format` is not 'rdfxml'. + AssertionError: If `namespace` does not end with a '#'. + Example: + >>> from some_module import OWLClassExpression + >>> expr1 = OWLClassExpression("SomeExpression1") + >>> expr2 = OWLClassExpression("SomeExpression2") + >>> save_owl_class_expressions([expr1, expr2], path="my_ontology.owl", rdf_format="rdfxml") + """ assert isinstance(expressions, OWLClassExpression) or isinstance(expressions[0], OWLClassExpression), "expressions must be either OWLClassExpression or a list of OWLClassExpression" assert rdf_format == 'rdfxml', f'Format {rdf_format} not implemented. Please use rdfxml' @@ -40,7 +63,4 @@ def save_owl_class_expressions(expressions: OWLClassExpression | List[OWLClassEx print(i) print(expressions) exit(1) - print(ontology) - ontology.save(path=path, inplace=False, rdf_format=rdf_format) - - # ontology.save(IRI.create(path)) + ontology.save(path=path, inplace=False, rdf_format=rdf_format) \ No newline at end of file From c901ed0ab43ee899026e43ddfc603b728e21f04f Mon Sep 17 00:00:00 2001 From: Caglar Demir Date: Fri, 8 Nov 2024 10:06:28 +0100 Subject: [PATCH 06/24] Ruff is being used with explicit command --- .github/workflows/ruff.yml | 8 -------- .github/workflows/test.yml | 4 ++++ 2 files changed, 4 insertions(+), 8 deletions(-) delete mode 100644 .github/workflows/ruff.yml diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml deleted file mode 100644 index b6690e05..00000000 --- a/.github/workflows/ruff.yml +++ /dev/null @@ -1,8 +0,0 @@ -name: Ruff -on: [push, pull_request] -jobs: - ruff: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: chartboost/ruff-action@v1 \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6bee0285..e08a82a1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,6 +19,10 @@ jobs: python -m pip install --upgrade pip pip install -e . + - name: Lint with ruff + run: | + ruff check owlapy --line-length=200 + - name: Test with pytest run: | wget https://files.dice-research.org/projects/Ontolearn/KGs.zip From 7eaf5be60cbcd780793556c882a4f127e6fe4d5a Mon Sep 17 00:00:00 2001 From: Caglar Demir Date: Fri, 8 Nov 2024 10:08:58 +0100 Subject: [PATCH 07/24] Ruff Linter error fixes --- .github/workflows/ruff.yml | 8 -------- .github/workflows/test.yml | 4 +++- owlapy/util_owl_static_funcs.py | 11 +---------- setup.py | 1 + 4 files changed, 5 insertions(+), 19 deletions(-) delete mode 100644 .github/workflows/ruff.yml diff --git a/.github/workflows/ruff.yml b/.github/workflows/ruff.yml deleted file mode 100644 index b6690e05..00000000 --- a/.github/workflows/ruff.yml +++ /dev/null @@ -1,8 +0,0 @@ -name: Ruff -on: [push, pull_request] -jobs: - ruff: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: chartboost/ruff-action@v1 \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6bee0285..a0e107e9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,7 +18,9 @@ jobs: run: | python -m pip install --upgrade pip pip install -e . - + - name: Lint with ruff + run: | + ruff check owlapy --line-length=200 - name: Test with pytest run: | wget https://files.dice-research.org/projects/Ontolearn/KGs.zip diff --git a/owlapy/util_owl_static_funcs.py b/owlapy/util_owl_static_funcs.py index e8012707..20ee01e5 100644 --- a/owlapy/util_owl_static_funcs.py +++ b/owlapy/util_owl_static_funcs.py @@ -53,14 +53,5 @@ def save_owl_class_expressions(expressions: OWLClassExpression | List[OWLClassEx for th, i in enumerate(expressions): cls_a = OWLClass(IRI.create(namespace, str(th))) equivalent_classes_axiom = OWLEquivalentClassesAxiom([cls_a, i]) - try: - ontology.add_axiom(equivalent_classes_axiom) - except AttributeError: - print(traceback.format_exc()) - print("Exception at creating OWLEquivalentClassesAxiom") - print(equivalent_classes_axiom) - print(cls_a) - print(i) - print(expressions) - exit(1) + ontology.add_axiom(equivalent_classes_axiom) ontology.save(path=path, inplace=False, rdf_format=rdf_format) \ No newline at end of file diff --git a/setup.py b/setup.py index bf8d39f5..71d8ca61 100644 --- a/setup.py +++ b/setup.py @@ -13,6 +13,7 @@ "pandas>=1.5.0", "requests>=2.32.3", "rdflib>=6.0.2", + "ruff>=0.7.2", "parsimonious>=0.8.1", "pytest>=8.1.1", "sortedcontainers>=2.4.0", From fb67bf358fed6a925a54d9465abf98d243c03677 Mon Sep 17 00:00:00 2001 From: Luke Friedrichs Date: Fri, 8 Nov 2024 10:10:21 +0100 Subject: [PATCH 08/24] lazy compute all individuals + remove LRU cache --- owlapy/owl_reasoner.py | 40 ++++++++++------------------------------ 1 file changed, 10 insertions(+), 30 deletions(-) diff --git a/owlapy/owl_reasoner.py b/owlapy/owl_reasoner.py index fc9cae95..7654844d 100644 --- a/owlapy/owl_reasoner.py +++ b/owlapy/owl_reasoner.py @@ -40,13 +40,9 @@ class StructuralReasoner(AbstractOWLReasoner): _world: owlready2.World _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] # ObjectSomeValuesFrom => individuals - _objectsomevalues_cache: LRUCache[OWLClassExpression, FrozenSet[OWLNamedIndividual]] # DataSomeValuesFrom => individuals - _datasomevalues_cache: LRUCache[OWLClassExpression, FrozenSet[OWLNamedIndividual]] # ObjectCardinalityRestriction => individuals - _objectcardinality_cache: LRUCache[OWLClassExpression, FrozenSet[OWLNamedIndividual]] # ObjectProperty => { individual => individuals } _obj_prop: Dict[OWLObjectProperty, Mapping[OWLNamedIndividual, Set[OWLNamedIndividual]]] # ObjectProperty => { individual => individuals } @@ -81,12 +77,6 @@ def __init__(self, ontology: AbstractOWLOntology, *, class_cache: bool = True, self._init() def _init(self, cache_size=128): - - individuals = self._ontology.individuals_in_signature() - self._ind_set = frozenset(individuals) - self._objectsomevalues_cache = LRUCache(maxsize=cache_size) - self._datasomevalues_cache = LRUCache(maxsize=cache_size) - self._objectcardinality_cache = LRUCache(maxsize=cache_size) if self.class_cache: self._cls_to_ind = dict() @@ -96,11 +86,12 @@ def _init(self, cache_size=128): self._data_prop = dict() else: self._has_prop = MappingProxyType({ - OWLDataProperty: LRUCache(maxsize=cache_size), - OWLObjectProperty: LRUCache(maxsize=cache_size), - OWLObjectInverseOf: LRUCache(maxsize=cache_size), + OWLDataProperty: dict(), + OWLObjectProperty: dict(), + OWLObjectInverseOf: dict(), }) + def reset(self): """The reset method shall reset any cached state.""" self._init() @@ -627,7 +618,8 @@ def _lazy_cache_obj_prop(self, pe: OWLObjectPropertyExpression) -> None: opc[s] = set() opc[s] |= {o} else: - for s in self._ind_set: + all_ = frozenset(self._ontology.individuals_in_signature()) + for s in all_: individuals = set(self.object_property_values(s, pe, not self._sub_properties)) if individuals: opc[s] = individuals @@ -667,8 +659,8 @@ def _some_values_subject_index(self, pe: OWLPropertyExpression) -> FrozenSet[OWL func = self.data_property_values else: func = self.object_property_values - - for s in self._ind_set: + all_ = frozenset(self._ontology.individuals_in_signature()) + for s in all_: try: next(iter(func(s, pe, not self._sub_properties))) subs |= {s} @@ -773,9 +765,6 @@ def _(self, ce: OWLObjectIntersectionOf) -> FrozenSet[OWLNamedIndividual]: @_find_instances.register def _(self, ce: OWLObjectSomeValuesFrom) -> FrozenSet[OWLNamedIndividual]: - if ce in self._objectsomevalues_cache: - return self._objectsomevalues_cache[ce] - p = ce.get_property() assert isinstance(p, OWLObjectPropertyExpression) if not self._property_cache and ce.get_filler().is_owl_thing(): @@ -785,13 +774,12 @@ def _(self, ce: OWLObjectSomeValuesFrom) -> FrozenSet[OWLNamedIndividual]: ind = self._find_some_values(p, filler_ind) - self._objectsomevalues_cache[ce] = ind return ind @_find_instances.register def _(self, ce: OWLObjectComplementOf) -> FrozenSet[OWLNamedIndividual]: if self._negation_default: - all_ = self._ind_set + all_ = frozenset(self._ontology.individuals_in_signature()) complement_ind = self._find_instances(ce.get_operand()) return all_ ^ complement_ind else: @@ -827,7 +815,7 @@ def _(self, ce: OWLObjectMinCardinality) -> FrozenSet[OWLNamedIndividual]: @_find_instances.register def _(self, ce: OWLObjectMaxCardinality) -> FrozenSet[OWLNamedIndividual]: - all_ = self._ind_set + all_ = frozenset(self._ontology.individuals_in_signature()) min_ind = self._find_instances(OWLObjectMinCardinality(cardinality=ce.get_cardinality() + 1, property=ce.get_property(), filler=ce.get_filler())) @@ -838,9 +826,6 @@ def _(self, ce: OWLObjectExactCardinality) -> FrozenSet[OWLNamedIndividual]: return self._get_instances_object_card_restriction(ce) def _get_instances_object_card_restriction(self, ce: OWLObjectCardinalityRestriction): - if ce in self._objectcardinality_cache: - return self._objectcardinality_cache[ce] - p = ce.get_property() assert isinstance(p, OWLObjectPropertyExpression) @@ -862,14 +847,10 @@ def _get_instances_object_card_restriction(self, ce: OWLObjectCardinalityRestric ind = self._find_some_values(p, filler_ind, min_count=min_count, max_count=max_count) - self._objectcardinality_cache[ce] = ind return ind @_find_instances.register def _(self, ce: OWLDataSomeValuesFrom) -> FrozenSet[OWLNamedIndividual]: - if ce in self._datasomevalues_cache: - return self._datasomevalues_cache[ce] - pe = ce.get_property() filler = ce.get_filler() assert isinstance(pe, OWLDataProperty) @@ -958,7 +939,6 @@ def include(lv: OWLLiteral): raise ValueError r = frozenset(ind) - self._datasomevalues_cache[ce] = r return r @_find_instances.register From a4ee29930e89794067342a6a4cd256dfe33ec329 Mon Sep 17 00:00:00 2001 From: Luke Friedrichs Date: Fri, 8 Nov 2024 10:21:08 +0100 Subject: [PATCH 09/24] move type annotations into init --- owlapy/owl_reasoner.py | 59 ++++++++++++++++-------------------------- 1 file changed, 22 insertions(+), 37 deletions(-) diff --git a/owlapy/owl_reasoner.py b/owlapy/owl_reasoner.py index 7654844d..6333f2e1 100644 --- a/owlapy/owl_reasoner.py +++ b/owlapy/owl_reasoner.py @@ -36,24 +36,6 @@ class StructuralReasoner(AbstractOWLReasoner): """Tries to check instances fast (but maybe incomplete).""" - _ontology: Ontology - _world: owlready2.World - _cls_to_ind: Dict[OWLClass, FrozenSet[OWLNamedIndividual]] # Class => individuals - _has_prop: Mapping[Type[_P], LRUCache[_P, FrozenSet[OWLNamedIndividual]]] # Type => Property => individuals - # ObjectSomeValuesFrom => individuals - # DataSomeValuesFrom => individuals - # ObjectCardinalityRestriction => individuals - # ObjectProperty => { individual => individuals } - _obj_prop: Dict[OWLObjectProperty, Mapping[OWLNamedIndividual, Set[OWLNamedIndividual]]] - # ObjectProperty => { individual => individuals } - _obj_prop_inv: Dict[OWLObjectProperty, Mapping[OWLNamedIndividual, Set[OWLNamedIndividual]]] - # DataProperty => { individual => literals } - _data_prop: Dict[OWLDataProperty, Mapping[OWLNamedIndividual, Set[OWLLiteral]]] - class_cache: bool - _property_cache: bool - _negation_default: bool - _sub_properties: bool - def __init__(self, ontology: AbstractOWLOntology, *, class_cache: bool = True, property_cache: bool = True, negation_default: bool = True, sub_properties: bool = False): """Fast instance checker. @@ -67,31 +49,34 @@ def __init__(self, ontology: AbstractOWLOntology, *, class_cache: bool = True, """ super().__init__(ontology) assert isinstance(ontology, Ontology) - self._world = ontology._world - self._ontology = ontology - self.class_cache = class_cache - self._property_cache = property_cache - self._negation_default = negation_default - self._sub_properties = sub_properties - self.__warned = 0 + self._world: owlready2.World = ontology._world + self._ontology: Ontology = ontology + self.class_cache: bool = class_cache + self._property_cache: bool = property_cache + self._negation_default: bool = negation_default + self._sub_properties: bool = sub_properties + self.__warned: int = 0 self._init() - def _init(self, cache_size=128): + def _init(self): if self.class_cache: - self._cls_to_ind = dict() + # Class => individuals + self._cls_to_ind: Dict[OWLClass, FrozenSet[OWLNamedIndividual]] = {} if self._property_cache: - self._obj_prop = dict() - self._obj_prop_inv = dict() - self._data_prop = dict() + # ObjectProperty => { individual => individuals } + self._obj_prop: Dict[OWLObjectProperty, Mapping[OWLNamedIndividual, Set[OWLNamedIndividual]]] = dict() + # ObjectProperty => { individual => individuals } + self._obj_prop_inv: Dict[OWLObjectProperty, Mapping[OWLNamedIndividual, Set[OWLNamedIndividual]]] = dict() + # DataProperty => { individual => literals } + self._data_prop: Dict[OWLDataProperty, Mapping[OWLNamedIndividual, Set[OWLLiteral]]] = dict() else: - self._has_prop = MappingProxyType({ - OWLDataProperty: dict(), - OWLObjectProperty: dict(), - OWLObjectInverseOf: dict(), - }) - - + self._has_prop: Mapping[Type[_P], Dict[_P, FrozenSet[OWLNamedIndividual]]] = { + OWLDataProperty: {}, + OWLObjectProperty: {}, + OWLObjectInverseOf: {}, + } + def reset(self): """The reset method shall reset any cached state.""" self._init() From df6b6c1804171d51568ae98b62c355d342335ece Mon Sep 17 00:00:00 2001 From: Luke Friedrichs Date: Fri, 8 Nov 2024 10:25:05 +0100 Subject: [PATCH 10/24] removed LRU import --- owlapy/owl_reasoner.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/owlapy/owl_reasoner.py b/owlapy/owl_reasoner.py index 6333f2e1..2589b91e 100644 --- a/owlapy/owl_reasoner.py +++ b/owlapy/owl_reasoner.py @@ -27,7 +27,7 @@ OWLPropertyExpression, OWLDataPropertyExpression from owlapy.owl_individual import OWLNamedIndividual from owlapy.owl_literal import OWLLiteral -from owlapy.utils import LRUCache, run_with_timeout +from owlapy.utils import run_with_timeout from owlapy.abstracts.abstract_owl_reasoner import AbstractOWLReasoner logger = logging.getLogger(__name__) From 5038f0d3d63c8c34e4488987fde3c3c3d366c02d Mon Sep 17 00:00:00 2001 From: Caglar Demir Date: Fri, 8 Nov 2024 16:42:55 +0100 Subject: [PATCH 11/24] simple tests about hashings are added --- tests/test_hashing.py | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 tests/test_hashing.py diff --git a/tests/test_hashing.py b/tests/test_hashing.py new file mode 100644 index 00000000..b177022c --- /dev/null +++ b/tests/test_hashing.py @@ -0,0 +1,33 @@ +from owlapy.class_expression import OWLClass, OWLObjectIntersectionOf +from owlapy.owl_property import OWLObjectProperty +from owlapy.class_expression import OWLObjectSomeValuesFrom +from owlapy.converter import owl_expression_to_sparql +from owlapy.render import owl_expression_to_dl +from owlapy.iri import IRI +from owlapy.class_expression import OWLObjectUnionOf, OWLObjectIntersectionOf +class TestHashing: + def test_simple(self): + memory = dict() + # An OWL Class can be used as a key in a dictionary. + memory[OWLClass("http://example.com/father#A")] = OWLClass("http://example.com/father#A") + memory[OWLClass("http://example.com/father#B")] = OWLClass("http://example.com/father#B") + memory[OWLClass("http://example.com/father#C")] = OWLClass("http://example.com/father#C") + + unions = set() + intersections = set() + for k, v in memory.items(): + assert k == v + # An OWLObjectUnionOf over two OWL Classes can be added into a set. + unions.add(OWLObjectUnionOf((k, v))) + # Since the order doesn't matter in an OWLObjectUnionOf the following also holds + assert OWLObjectUnionOf((v, k)) in unions + + # An OWLObjectUnionOf over two OWL Classes can be added into a set. + intersections.add(OWLObjectIntersectionOf((k, v))) + # Since the order doesn't matter in an OWLObjectUnionOf the following also holds + assert OWLObjectIntersectionOf((v, k)) in intersections + # OWLObjectUnionOf and OWLObjectIntersectionOf can also be used as keys + for i in unions | intersections: + memory[i]=i + for k, v in memory.items(): + assert k == v From 5e3a9e08b71ad7435a31326124afe8b43876138f Mon Sep 17 00:00:00 2001 From: Caglar Demir Date: Fri, 8 Nov 2024 16:44:12 +0100 Subject: [PATCH 12/24] Raised RuntimeError if two objects not inherting from same abstract class are compared --- owlapy/iri.py | 4 +++- owlapy/owl_object.py | 3 ++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/owlapy/iri.py b/owlapy/iri.py index 66166bb4..af6a3238 100644 --- a/owlapy/iri.py +++ b/owlapy/iri.py @@ -45,6 +45,7 @@ def __init__(self, namespace: Union[str, Namespaces], remainder: str=""): else: assert namespace[-1] in ("/", ":", "#"), "It should be a valid IRI based on /, :, and #" import sys + # https://docs.python.org/3.2/library/sys.html?highlight=sys.intern#sys.intern self._namespace = sys.intern(namespace) self._remainder = remainder @@ -94,7 +95,8 @@ def __repr__(self): def __eq__(self, other): if type(other) is type(self): return self._namespace is other._namespace and self._remainder == other._remainder - return NotImplemented + else: + raise RuntimeError(f"Invalid equality checking:{self} cannot be compared with {other}") def __hash__(self): return hash((self._namespace, self._remainder)) diff --git a/owlapy/owl_object.py b/owlapy/owl_object.py index 02894034..2f89a877 100644 --- a/owlapy/owl_object.py +++ b/owlapy/owl_object.py @@ -75,7 +75,8 @@ class OWLNamedObject(OWLObject, HasIRI, metaclass=ABCMeta): def __eq__(self, other): if type(other) is type(self): return self._iri == other._iri - return NotImplemented + else: + raise RuntimeError(f"Invalid equality checking:{self} cannot be compared with {other}") def __lt__(self, other): if type(other) is type(self): From 1bf6604ba080d15b9a30b9e3d111fc056521d424 Mon Sep 17 00:00:00 2001 From: Caglar Demir Date: Fri, 8 Nov 2024 17:03:45 +0100 Subject: [PATCH 13/24] RuntimeError raised if __eq__ different object types --- owlapy/class_expression/restriction.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/owlapy/class_expression/restriction.py b/owlapy/class_expression/restriction.py index cce981a1..dfe697db 100644 --- a/owlapy/class_expression/restriction.py +++ b/owlapy/class_expression/restriction.py @@ -272,7 +272,8 @@ def __repr__(self): def __eq__(self, other): if type(other) is type(self): return self._filler == other._filler and self._property == other._property - return NotImplemented + else: + raise RuntimeError(f"Invalid equality checking:{self} cannot be compared with {other}") def __hash__(self): return hash((self._filler, self._property)) From 86787928747a190f9632844f0e09c5bd8d516859 Mon Sep 17 00:00:00 2001 From: Caglar Demir Date: Fri, 8 Nov 2024 17:04:03 +0100 Subject: [PATCH 14/24] el concept construct tested via hashing --- tests/test_hashing.py | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/tests/test_hashing.py b/tests/test_hashing.py index b177022c..22b7489e 100644 --- a/tests/test_hashing.py +++ b/tests/test_hashing.py @@ -1,12 +1,19 @@ -from owlapy.class_expression import OWLClass, OWLObjectIntersectionOf from owlapy.owl_property import OWLObjectProperty -from owlapy.class_expression import OWLObjectSomeValuesFrom +from owlapy.class_expression import OWLObjectSomeValuesFrom, OWLObjectAllValuesFrom from owlapy.converter import owl_expression_to_sparql from owlapy.render import owl_expression_to_dl from owlapy.iri import IRI -from owlapy.class_expression import OWLObjectUnionOf, OWLObjectIntersectionOf +from owlapy.class_expression import OWLClass, OWLObjectUnionOf, OWLObjectIntersectionOf + class TestHashing: - def test_simple(self): + + def test_el_description_logic_hash(self): + """ + EL allows complex concepts of the following form: + C := \top | A | C1 u C2 | \existr.C + where A is a concept and r a role name. + For more, refer to https://www.emse.fr/~zimmermann/Teaching/KRR/el.html + """ memory = dict() # An OWL Class can be used as a key in a dictionary. memory[OWLClass("http://example.com/father#A")] = OWLClass("http://example.com/father#A") @@ -22,7 +29,7 @@ def test_simple(self): # Since the order doesn't matter in an OWLObjectUnionOf the following also holds assert OWLObjectUnionOf((v, k)) in unions - # An OWLObjectUnionOf over two OWL Classes can be added into a set. + # This also works for intersections. intersections.add(OWLObjectIntersectionOf((k, v))) # Since the order doesn't matter in an OWLObjectUnionOf the following also holds assert OWLObjectIntersectionOf((v, k)) in intersections @@ -31,3 +38,16 @@ def test_simple(self): memory[i]=i for k, v in memory.items(): assert k == v + + atomic_concepts={OWLClass("http://example.com/father#A"),OWLClass("http://example.com/father#B")} + properties={OWLObjectProperty("http://example.com/society#hasChild")} + memory = dict() + for ac in atomic_concepts: + for op in properties: + # OWLObjectSomeValuesFrom can be used as a key. + memory[OWLObjectSomeValuesFrom(property=op, filler=ac)] = OWLObjectSomeValuesFrom(property=op, filler=ac) + # TODO: https://github.com/dice-group/owlapy/issues/103 + # memory[OWLObjectAllValuesFrom(property=op, filler=ac)] = OWLObjectAllValuesFrom(property=op, filler=ac) + + for k, v in memory.items(): + assert k == v \ No newline at end of file From 254e164f162a3c28f162b5d18bd4b7776bc275bc Mon Sep 17 00:00:00 2001 From: Caglar Demir Date: Fri, 8 Nov 2024 17:20:04 +0100 Subject: [PATCH 15/24] RuntimeErrors are included in all restrictions if types are not matching. Hash functions are taking a tuple where the first item corresponds to the string represenntation of the class --- owlapy/class_expression/restriction.py | 63 ++++++++++++++------------ tests/test_hashing.py | 3 +- 2 files changed, 36 insertions(+), 30 deletions(-) diff --git a/owlapy/class_expression/restriction.py b/owlapy/class_expression/restriction.py index dfe697db..79ed3f8f 100644 --- a/owlapy/class_expression/restriction.py +++ b/owlapy/class_expression/restriction.py @@ -276,7 +276,7 @@ def __eq__(self, other): raise RuntimeError(f"Invalid equality checking:{self} cannot be compared with {other}") def __hash__(self): - return hash((self._filler, self._property)) + return hash(("OWLObjectSomeValuesFrom",self._filler, self._property)) def get_property(self) -> OWLObjectPropertyExpression: # documented in parent @@ -301,10 +301,10 @@ def __eq__(self, other): if type(other) is type(self): return self._filler == other._filler and self._property == other._property else: - return False + raise RuntimeError(f"Invalid equality checking:{self} cannot be compared with {other}") def __hash__(self): - return hash((self._filler, self._property)) + return hash(("OWLObjectAllValuesFrom",self._filler, self._property)) def get_property(self) -> OWLObjectPropertyExpression: # documented in parent @@ -340,10 +340,11 @@ def __eq__(self, other): if type(other) is type(self): return self._property == other._property else: - return False + raise RuntimeError(f"Invalid equality checking:{self} cannot be compared with {other}") + def __hash__(self): - return hash(self._property) + return hash(("OWLObjectHasSelf", self._property)) def __repr__(self): return f'OWLObjectHasSelf({self._property})' @@ -431,13 +432,14 @@ def as_object_union_of(self) -> OWLClassExpression: return OWLObjectUnionOf(map(lambda _: OWLObjectOneOf(_), self.individuals())) def __hash__(self): - return hash(self._values) + return hash(("OWLObjectOneOf", self._values)) def __eq__(self, other): if type(other) is type(self): return self._values == other._values else: - return False + raise RuntimeError(f"Invalid equality checking:{self} cannot be compared with {other}") + def __repr__(self): return f'OWLObjectOneOf({self._values})' @@ -500,10 +502,12 @@ def __eq__(self, other): return self._property == other._property \ and self._cardinality == other._cardinality \ and self._filler == other._filler - return NotImplemented + else: + raise RuntimeError(f"Invalid equality checking:{self} cannot be compared with {other}") + def __hash__(self): - return hash((self._property, self._cardinality, self._filler)) + return hash(("OWLDataCardinalityRestriction",self._property, self._cardinality, self._filler)) class OWLDataMinCardinality(OWLDataCardinalityRestriction): @@ -618,9 +622,10 @@ def __eq__(self, other): if type(other) is type(self): return self._filler == other._filler and self._property == other._property else: - return False + raise RuntimeError(f"Invalid equality checking:{self} cannot be compared with {other}") + def __hash__(self): - return hash((self._filler, self._property)) + return hash(("OWLDataSomeValuesFrom",self._filler, self._property)) def get_property(self) -> OWLDataPropertyExpression: # documented in parent @@ -661,10 +666,10 @@ def __eq__(self, other): if type(other) is type(self): return self._filler == other._filler and self._property == other._property else: - return False + raise RuntimeError(f"Invalid equality checking:{self} cannot be compared with {other}") def __hash__(self): - return hash((self._filler, self._property)) + return hash(("OWLDataAllValuesFrom",self._filler, self._property)) def get_property(self) -> OWLDataPropertyExpression: # documented in parent @@ -707,7 +712,7 @@ def __eq__(self, other): return NotImplemented def __hash__(self): - return hash((self._v, self._property)) + return hash(("OWLDataHasValue",self._v, self._property)) def as_some_values_from(self) -> OWLClassExpression: """A convenience method that obtains this restriction as an existential restriction with a nominal filler. @@ -736,6 +741,18 @@ def __init__(self, values: Union[OWLLiteral, Iterable[OWLLiteral]]): for _ in values: assert isinstance(_, OWLLiteral) self._values = tuple(values) + def __repr__(self): + return f'OWLDataOneOf({self._values})' + + def __hash__(self): + return hash(("OWLDataOneOf",self._values)) + + def __eq__(self, other): + if type(other) is type(self): + return {i for i in self._values} == {j for j in other._values} + else: + raise RuntimeError(f"Invalid equality checking:{self} cannot be compared with {other}") + # TODO:CD: define it as @property as the name of the class method does not correspond to an action def values(self) -> Iterable[OWLLiteral]: """Gets the values that are in the oneOf. @@ -749,18 +766,6 @@ def operands(self) -> Iterable[OWLLiteral]: # documented in parent yield from self.values() - def __hash__(self): - return hash(self._values) - - def __eq__(self, other): - if type(other) is type(self): - return {i for i in self._values} == {j for j in other._values} - else: - return False - - def __repr__(self): - return f'OWLDataOneOf({self._values})' - class OWLDatatypeRestriction(OWLDataRange): """A datatype restriction DatatypeRestriction( DT F1 lt1 ... Fn ltn ) consists of a unary datatype DT and n pairs @@ -828,10 +833,12 @@ def get_facet_value(self) -> 'OWLLiteral': def __eq__(self, other): if type(other) is type(self): return self._facet == other._facet and self._literal == other._literal - return NotImplemented + else: + raise RuntimeError(f"Invalid equality checking:{self} cannot be compared with {other}") + def __hash__(self): - return hash((self._facet, self._literal)) + return hash(("OWLFacetRestriction",self._facet, self._literal)) def __repr__(self): return f'OWLFacetRestriction({self._facet}, {repr(self._literal)})' diff --git a/tests/test_hashing.py b/tests/test_hashing.py index 22b7489e..f30dfc72 100644 --- a/tests/test_hashing.py +++ b/tests/test_hashing.py @@ -46,8 +46,7 @@ def test_el_description_logic_hash(self): for op in properties: # OWLObjectSomeValuesFrom can be used as a key. memory[OWLObjectSomeValuesFrom(property=op, filler=ac)] = OWLObjectSomeValuesFrom(property=op, filler=ac) - # TODO: https://github.com/dice-group/owlapy/issues/103 - # memory[OWLObjectAllValuesFrom(property=op, filler=ac)] = OWLObjectAllValuesFrom(property=op, filler=ac) + memory[OWLObjectAllValuesFrom(property=op, filler=ac)] = OWLObjectAllValuesFrom(property=op, filler=ac) for k, v in memory.items(): assert k == v \ No newline at end of file From f0c5592f737a71593065bb9df79230a22c090532 Mon Sep 17 00:00:00 2001 From: Caglar Demir Date: Fri, 8 Nov 2024 17:28:54 +0100 Subject: [PATCH 16/24] Reverted: __eq__ does not throw runtime error if two objects not beloning to the same class. This is too restrictive --- owlapy/class_expression/restriction.py | 57 ++++++++++---------------- owlapy/owl_object.py | 3 +- 2 files changed, 24 insertions(+), 36 deletions(-) diff --git a/owlapy/class_expression/restriction.py b/owlapy/class_expression/restriction.py index 79ed3f8f..09549c3c 100644 --- a/owlapy/class_expression/restriction.py +++ b/owlapy/class_expression/restriction.py @@ -10,7 +10,7 @@ from ..owl_individual import OWLIndividual from ..owl_datatype import OWLDatatype from ..owl_object import OWLObject -from owlapy.vocab import OWLFacet +from ..vocab import OWLFacet from datetime import datetime, date from pandas import Timedelta @@ -65,7 +65,7 @@ def __init__(self, value: _T): def __eq__(self, other): if type(other) is type(self): return self._v == other._v - return NotImplemented + return False def __hash__(self): return hash(self._v) @@ -273,7 +273,7 @@ def __eq__(self, other): if type(other) is type(self): return self._filler == other._filler and self._property == other._property else: - raise RuntimeError(f"Invalid equality checking:{self} cannot be compared with {other}") + return False def __hash__(self): return hash(("OWLObjectSomeValuesFrom",self._filler, self._property)) @@ -301,7 +301,7 @@ def __eq__(self, other): if type(other) is type(self): return self._filler == other._filler and self._property == other._property else: - raise RuntimeError(f"Invalid equality checking:{self} cannot be compared with {other}") + return False def __hash__(self): return hash(("OWLObjectAllValuesFrom",self._filler, self._property)) @@ -340,7 +340,7 @@ def __eq__(self, other): if type(other) is type(self): return self._property == other._property else: - raise RuntimeError(f"Invalid equality checking:{self} cannot be compared with {other}") + return False def __hash__(self): @@ -430,17 +430,13 @@ def as_object_union_of(self) -> OWLClassExpression: if len(self._values) == 1: return self return OWLObjectUnionOf(map(lambda _: OWLObjectOneOf(_), self.individuals())) - def __hash__(self): return hash(("OWLObjectOneOf", self._values)) - def __eq__(self, other): if type(other) is type(self): return self._values == other._values else: - raise RuntimeError(f"Invalid equality checking:{self} cannot be compared with {other}") - - + return False def __repr__(self): return f'OWLObjectOneOf({self._values})' @@ -488,27 +484,22 @@ def __init__(self, cardinality: int, property: OWLDataPropertyExpression, filler assert isinstance(filler, OWLDataRange), "filler must be an OWLDataRange" super().__init__(cardinality, filler) self._property = property - - def get_property(self) -> OWLDataPropertyExpression: - # documented in parent - return self._property - + def __hash__(self): + return hash(("OWLDataCardinalityRestriction",self._property, self._cardinality, self._filler)) def __repr__(self): return f"{type(self).__name__}(" \ f"property={repr(self.get_property())},{self.get_cardinality()},filler={repr(self.get_filler())})" def __eq__(self, other): if type(other) is type(self): - return self._property == other._property \ - and self._cardinality == other._cardinality \ - and self._filler == other._filler + return (self._property == other._property and self._cardinality == other._cardinality + and self._filler == other._filler) else: - raise RuntimeError(f"Invalid equality checking:{self} cannot be compared with {other}") - - - def __hash__(self): - return hash(("OWLDataCardinalityRestriction",self._property, self._cardinality, self._filler)) + return False + def get_property(self) -> OWLDataPropertyExpression: + # documented in parent + return self._property class OWLDataMinCardinality(OWLDataCardinalityRestriction): """A minimum cardinality expression DataMinCardinality( n DPE DR ) consists of a nonnegative integer n, a data @@ -622,8 +613,7 @@ def __eq__(self, other): if type(other) is type(self): return self._filler == other._filler and self._property == other._property else: - raise RuntimeError(f"Invalid equality checking:{self} cannot be compared with {other}") - + return False def __hash__(self): return hash(("OWLDataSomeValuesFrom",self._filler, self._property)) @@ -666,8 +656,7 @@ def __eq__(self, other): if type(other) is type(self): return self._filler == other._filler and self._property == other._property else: - raise RuntimeError(f"Invalid equality checking:{self} cannot be compared with {other}") - + return False def __hash__(self): return hash(("OWLDataAllValuesFrom",self._filler, self._property)) @@ -709,8 +698,8 @@ def __repr__(self): def __eq__(self, other): if type(other) is type(self): return self._v == other._v and self._property == other._property - return NotImplemented - + else: + return False def __hash__(self): return hash(("OWLDataHasValue",self._v, self._property)) @@ -751,8 +740,7 @@ def __eq__(self, other): if type(other) is type(self): return {i for i in self._values} == {j for j in other._values} else: - raise RuntimeError(f"Invalid equality checking:{self} cannot be compared with {other}") - + return False # TODO:CD: define it as @property as the name of the class method does not correspond to an action def values(self) -> Iterable[OWLLiteral]: """Gets the values that are in the oneOf. @@ -798,8 +786,8 @@ def __eq__(self, other): if type(other) is type(self): return self._type == other._type \ and self._facet_restrictions == other._facet_restrictions - return NotImplemented - + else: + return False def __hash__(self): return hash((self._type, self._facet_restrictions)) @@ -834,8 +822,7 @@ def __eq__(self, other): if type(other) is type(self): return self._facet == other._facet and self._literal == other._literal else: - raise RuntimeError(f"Invalid equality checking:{self} cannot be compared with {other}") - + return False def __hash__(self): return hash(("OWLFacetRestriction",self._facet, self._literal)) diff --git a/owlapy/owl_object.py b/owlapy/owl_object.py index 2f89a877..00032820 100644 --- a/owlapy/owl_object.py +++ b/owlapy/owl_object.py @@ -76,7 +76,8 @@ def __eq__(self, other): if type(other) is type(self): return self._iri == other._iri else: - raise RuntimeError(f"Invalid equality checking:{self} cannot be compared with {other}") + return False + # raise RuntimeError(f"Invalid equality checking:{self} cannot be compared with {other}") def __lt__(self, other): if type(other) is type(self): From aa99ec675a0a860e8fa4602a79f95172a61fb8b9 Mon Sep 17 00:00:00 2001 From: Caglar Demir Date: Fri, 8 Nov 2024 17:31:52 +0100 Subject: [PATCH 17/24] OWLDatatypeRestriction has also updated --- owlapy/class_expression/restriction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/owlapy/class_expression/restriction.py b/owlapy/class_expression/restriction.py index 09549c3c..0e6658a9 100644 --- a/owlapy/class_expression/restriction.py +++ b/owlapy/class_expression/restriction.py @@ -789,7 +789,7 @@ def __eq__(self, other): else: return False def __hash__(self): - return hash((self._type, self._facet_restrictions)) + return hash(("OWLDatatypeRestriction", self._type, self._facet_restrictions)) def __repr__(self): return f'OWLDatatypeRestriction({repr(self._type)}, {repr(self._facet_restrictions)})' From 0dbc2de9a5dcea2d9491d35b4767aeceda22eea0 Mon Sep 17 00:00:00 2001 From: Caglar Demir Date: Fri, 8 Nov 2024 17:35:33 +0100 Subject: [PATCH 18/24] version incremanted --- owlapy/__init__.py | 2 +- setup.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/owlapy/__init__.py b/owlapy/__init__.py index 84aec36b..78a0a08e 100644 --- a/owlapy/__init__.py +++ b/owlapy/__init__.py @@ -3,7 +3,7 @@ from .converter import owl_expression_to_sparql, owl_expression_to_sparql_with_confusion_matrix from .owl_ontology_manager import OntologyManager -__version__ = '1.3.2' +__version__ = '1.3.3' __all__ = [ 'owl_expression_to_dl', 'owl_expression_to_manchester', diff --git a/setup.py b/setup.py index 71d8ca61..e3e8e579 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name="owlapy", description="OWLAPY is a Python Framework for creating and manipulating OWL Ontologies.", - version="1.3.2", + version="1.3.3", packages=find_packages(), include_package_data=True, package_data={'owlapy': ['jar_dependencies/*.jar'],}, From 70aa0abda4726cfdd60d96cfc0857da810d9c374 Mon Sep 17 00:00:00 2001 From: Caglar Demir Date: Fri, 8 Nov 2024 20:48:51 +0100 Subject: [PATCH 19/24] NotImplementedErrors message added --- owlapy/owl_ontology_manager.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/owlapy/owl_ontology_manager.py b/owlapy/owl_ontology_manager.py index 4d58d58a..2df6cdd7 100644 --- a/owlapy/owl_ontology_manager.py +++ b/owlapy/owl_ontology_manager.py @@ -103,8 +103,7 @@ def apply_change(self, change: AbstractOWLOntologyChange): ont_x.imported_ontologies.append( self._world.get_ontology(change.get_import_declaration().str)) else: - # TODO XXX - raise NotImplementedError + raise NotImplementedError("Change is not yet implemented.") def save_world(self): """Saves the actual state of the quadstore in the SQLite3 file. @@ -139,4 +138,4 @@ def get_owlapi_manager(self): return self.owlapi_manager def apply_change(self, change: AbstractOWLOntologyChange): - raise NotImplementedError() + raise NotImplementedError("A change cannot be applied at the moment.") From f2204693a176d0164c717526126606362363b50b Mon Sep 17 00:00:00 2001 From: Caglar Demir Date: Fri, 8 Nov 2024 22:03:13 +0100 Subject: [PATCH 20/24] New example added --- README.md | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) diff --git a/README.md b/README.md index 9eb842a7..f5c2b4e5 100644 --- a/README.md +++ b/README.md @@ -30,6 +30,54 @@ pytest -p no:warnings -x # Running 142 tests ~ 30 secs ## Examples +### Exploring OWL Ontology + +
Click me! + +```python +from owlapy.owl_ontology_manager import SyncOntologyManager + +ontology_path = "KGs/Family/father.owl" +onto = SyncOntologyManager().load_ontology(ontology_path) + +print({owl_class.reminder for owl_class in onto.classes_in_signature()}) +# {'Thing', 'female', 'male', 'person'} + +print({individual.reminder for individual in onto.individuals_in_signature()}) +# {'michelle', 'stefan', 'martin', 'anna', 'heinz', 'markus'} + +print({object_property.reminder for object_property in onto.object_properties_in_signature()}) +# {'hasChild'} + +for owl_subclass_of_axiom in onto.get_tbox_axioms(): + print(owl_subclass_of_axiom) + +# 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')),[]) + + +for axiom in onto.get_abox_axioms(): + print(axiom) + +# OWLClassAssertionAxiom(individual=OWLNamedIndividual(IRI('http://example.com/father#', 'anna')),class_expression=OWLClass(IRI('http://example.com/father#', 'female')),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#', '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#', '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=[]) +# 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=[]) +# 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#', 'anna')),property_=OWLObjectProperty(IRI('http://example.com/father#', 'hasChild')),object_=OWLNamedIndividual(IRI('http://example.com/father#', 'heinz')),annotations=[]) + +``` + +
+ ### Creating OWL Class Expressions
Click me! From de17806e27462d0cd928a1a6c54efcefe5f966c4 Mon Sep 17 00:00:00 2001 From: Caglar Demir Date: Fri, 8 Nov 2024 22:09:46 +0100 Subject: [PATCH 21/24] sub_class and super_class properties are added into OWLSubClassOfAxiom --- owlapy/owl_axiom.py | 10 +++++++++- tests/test_sync_ontology.py | 7 +++++++ tests/test_sync_reasoner.py | 23 ++++++++++++++++++++++- 3 files changed, 38 insertions(+), 2 deletions(-) diff --git a/owlapy/owl_axiom.py b/owlapy/owl_axiom.py index 6c10ea5d..3c0cfd96 100644 --- a/owlapy/owl_axiom.py +++ b/owlapy/owl_axiom.py @@ -523,6 +523,13 @@ def __init__(self, sub_class: OWLClassExpression, super_class: OWLClassExpressio self._super_class = super_class super().__init__(annotations=annotations) + @property + def sub_class(self) -> OWLClassExpression: + return self._sub_class + @property + def super_class(self) -> OWLClassExpression: + return self._super_class + def get_sub_class(self) -> OWLClassExpression: return self._sub_class @@ -533,7 +540,8 @@ def __eq__(self, other): if type(other) is type(self): return self._super_class == other._super_class and self._sub_class == other._sub_class \ and self._annotations == other._annotations - return NotImplemented + else: + return False def __hash__(self): return hash((self._super_class, self._sub_class, *self._annotations)) diff --git a/tests/test_sync_ontology.py b/tests/test_sync_ontology.py index c35d7462..f02c198e 100644 --- a/tests/test_sync_ontology.py +++ b/tests/test_sync_ontology.py @@ -80,6 +80,13 @@ class TestSyncReasoner(unittest.TestCase): manager = SyncOntologyManager() onto = manager.load_ontology(ontology_path) + def test_interface_father_dataset(self): + ontology_path = "KGs/Family/father.owl" + onto = SyncOntologyManager().load_ontology(ontology_path) + assert {owl_class.reminder for owl_class in onto.classes_in_signature()}=={'male', 'female', 'Thing', 'person'} + assert {individual.reminder for individual in onto.individuals_in_signature()}=={'markus', 'anna', 'martin', 'stefan', 'heinz', 'michelle'} + assert {object_property.reminder for object_property in onto.object_properties_in_signature()}=={'hasChild'} + # NOTE AB: The name of "assertCountEqual" may be misleading,but it's essentially an order-insensitive "assertEqual". def test_classes_in_signature(self): diff --git a/tests/test_sync_reasoner.py b/tests/test_sync_reasoner.py index f45cb8a8..9b157457 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, OWLObjectSomeValuesFrom + OWLClassExpression, OWLObjectSomeValuesFrom, OWLObjectOneOf from owlapy.iri import IRI from owlapy.owl_axiom import OWLDisjointClassesAxiom, OWLDeclarationAxiom, OWLClassAssertionAxiom, OWLSubClassOfAxiom, \ OWLEquivalentClassesAxiom, OWLSubDataPropertyOfAxiom, OWLSubObjectPropertyOfAxiom @@ -15,6 +15,7 @@ from owlapy.owl_reasoner import SyncReasoner from owlapy.providers import owl_datatype_min_inclusive_restriction + NS = 'http://www.semanticweb.org/stefan/ontologies/2023/1/untitled-ontology-11#' a = OWLNamedIndividual(IRI(NS, "a")) @@ -82,6 +83,26 @@ class TestSyncReasoner(unittest.TestCase): ce = OWLObjectIntersectionOf([nitrogen38, has_charge_more_than_0_85]) reasoner = SyncReasoner(ontology_path) + def test_father_dataset(self): + ontology_path = "KGs/Family/father.owl" + # Available OWL Reasoners: 'HermiT', 'Pellet', 'JFact', 'Openllet' + owl_reasoners = dict() + owl_reasoners["HermiT"] = SyncReasoner(ontology=ontology_path, reasoner="HermiT") + owl_reasoners["Pellet"] = SyncReasoner(ontology=ontology_path, reasoner="Pellet") + owl_reasoners["JFact"] = SyncReasoner(ontology=ontology_path, reasoner="JFact") + owl_reasoners["Openllet"] = SyncReasoner(ontology=ontology_path, reasoner="Openllet") + + for k, reasoner in owl_reasoners.items(): + exist_haschild_female = OWLObjectSomeValuesFrom( + property=OWLObjectProperty('http://example.com/father#hasChild'), + filler=OWLClass('http://example.com/father#female')) + + exist_haschild_anna = OWLObjectSomeValuesFrom(property=OWLObjectProperty('http://example.com/father#hasChild'), + filler=OWLObjectOneOf( + OWLNamedIndividual('http://example.com/father#anna'))) + assert reasoner.instances(ce=exist_haschild_female) == reasoner.instances(ce=exist_haschild_anna) == { + OWLNamedIndividual('http://example.com/father#markus')} + def test_consistency_check(self): self.assertEqual(self.reasoner.has_consistent_ontology(), True) From 32027f582110fa855f986d48dc9234106c0bd368 Mon Sep 17 00:00:00 2001 From: Caglar Demir Date: Fri, 8 Nov 2024 22:11:40 +0100 Subject: [PATCH 22/24] __repr__ of SyncOntology extended. We should perhaps introduce __str__ --- owlapy/owl_ontology.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/owlapy/owl_ontology.py b/owlapy/owl_ontology.py index d527383e..03a2d4af 100644 --- a/owlapy/owl_ontology.py +++ b/owlapy/owl_ontology.py @@ -126,7 +126,6 @@ def __eq__(self, other): 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 and do not exist in the given ontology yet @@ -1035,7 +1034,6 @@ def _get_imports_enum(self, include_imports_closure: bool): 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. @@ -1045,6 +1043,7 @@ def get_signature(self, include_imports_closure: bool = True): Returns: Entities in signature. """ + # @TODO: CD: Is this class method redundant given that we have the individuals_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]: @@ -1056,7 +1055,6 @@ def get_abox_axioms(self, include_imports_closure: bool = True) -> Iterable[OWLA 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]: @@ -1100,7 +1098,14 @@ def __hash__(self): return int(self.owlapi_ontology.getOntologyID().hashCode()) def __repr__(self): - return f'SyncOntology({self.manager}, {self.path}, {self.new})' + return (f'SyncOntology:' + f'\t|Tbox|={len(self.get_tbox_axioms())}' + f'\t|Abox|={len(self.get_abox_axioms())}' + f'\t|Individuals|={len(self.individuals_in_signature())}' + f'\t|Classes|={len(self.classes_in_signature())}' + f'\t|Object Properties|={len(self.object_properties_in_signature())}' + f'\t|Data Properties|={len(self.data_properties_in_signature())}' + f'\n{self.manager}\tPath:{self.path}\tNew:{self.new}') OWLREADY2_FACET_KEYS = MappingProxyType({ From 1aaf53c168340da9e8bc836d1dca1737ff7706a9 Mon Sep 17 00:00:00 2001 From: Caglar Demir Date: Fri, 8 Nov 2024 22:13:16 +0100 Subject: [PATCH 23/24] __init__ of SyncReasoner is refactored by moving few assigments outside of the class. @property introduced in few places --- owlapy/owl_individual.py | 5 +- owlapy/owl_property.py | 4 ++ owlapy/owl_reasoner.py | 99 ++++++++++++++++++++-------------------- 3 files changed, 57 insertions(+), 51 deletions(-) diff --git a/owlapy/owl_individual.py b/owlapy/owl_individual.py index 3d98ff7b..2e0fd626 100644 --- a/owlapy/owl_individual.py +++ b/owlapy/owl_individual.py @@ -35,11 +35,12 @@ def __init__(self, iri: Union[IRI, str]): self._iri = iri else: self._iri = IRI.create(iri) - @property def iri(self) -> IRI: return self._iri - @property def str(self): return self._iri.as_str() + @property + def reminder(self): + return self._iri.reminder \ No newline at end of file diff --git a/owlapy/owl_property.py b/owlapy/owl_property.py index cef05361..4f3ee257 100644 --- a/owlapy/owl_property.py +++ b/owlapy/owl_property.py @@ -113,6 +113,10 @@ class OWLObjectProperty(OWLObjectPropertyExpression, OWLProperty): _iri: IRI + @property + def reminder(self): + return self._iri.reminder + def get_named_property(self) -> 'OWLObjectProperty': # documented in parent return self diff --git a/owlapy/owl_reasoner.py b/owlapy/owl_reasoner.py index 2589b91e..5069338f 100644 --- a/owlapy/owl_reasoner.py +++ b/owlapy/owl_reasoner.py @@ -1013,53 +1013,9 @@ def __init__(self, ontology: Union[SyncOntology, str], reasoner="HermiT"): self._owlapi_manager = self.manager.get_owlapi_manager() self._owlapi_ontology = self.ontology.get_owlapi_ontology() - # super().__init__(self.ontology) self.mapper = self.ontology.mapper - from org.semanticweb.owlapi.util import (InferredClassAssertionAxiomGenerator, InferredSubClassAxiomGenerator, - InferredEquivalentClassAxiomGenerator, - InferredDisjointClassesAxiomGenerator, - InferredEquivalentDataPropertiesAxiomGenerator, - InferredEquivalentObjectPropertyAxiomGenerator, - InferredInverseObjectPropertiesAxiomGenerator, - InferredSubDataPropertyAxiomGenerator, - InferredSubObjectPropertyAxiomGenerator, - InferredDataPropertyCharacteristicAxiomGenerator, - InferredObjectPropertyCharacteristicAxiomGenerator) - - self.inference_types_mapping = {"InferredClassAssertionAxiomGenerator": InferredClassAssertionAxiomGenerator(), - "InferredSubClassAxiomGenerator": InferredSubClassAxiomGenerator(), - "InferredDisjointClassesAxiomGenerator": InferredDisjointClassesAxiomGenerator(), - "InferredEquivalentClassAxiomGenerator": InferredEquivalentClassAxiomGenerator(), - "InferredInverseObjectPropertiesAxiomGenerator": InferredInverseObjectPropertiesAxiomGenerator(), - "InferredEquivalentDataPropertiesAxiomGenerator": InferredEquivalentDataPropertiesAxiomGenerator(), - "InferredEquivalentObjectPropertyAxiomGenerator": InferredEquivalentObjectPropertyAxiomGenerator(), - "InferredSubDataPropertyAxiomGenerator": InferredSubDataPropertyAxiomGenerator(), - "InferredSubObjectPropertyAxiomGenerator": InferredSubObjectPropertyAxiomGenerator(), - "InferredDataPropertyCharacteristicAxiomGenerator": InferredDataPropertyCharacteristicAxiomGenerator(), - "InferredObjectPropertyCharacteristicAxiomGenerator": InferredObjectPropertyCharacteristicAxiomGenerator(), - } - - # () Create a reasoner using the ontology - if reasoner == "HermiT": - from org.semanticweb.HermiT import ReasonerFactory - self._owlapi_reasoner = ReasonerFactory().createReasoner(self._owlapi_ontology) - assert self._owlapi_reasoner.getReasonerName() == "HermiT" - elif reasoner == "JFact": - from uk.ac.manchester.cs.jfact import JFactFactory - self._owlapi_reasoner = JFactFactory().createReasoner(self._owlapi_ontology) - elif reasoner == "Pellet": - from openllet.owlapi import PelletReasonerFactory - self._owlapi_reasoner = PelletReasonerFactory().createReasoner(self._owlapi_ontology) - elif reasoner == "Openllet": - from openllet.owlapi import OpenlletReasonerFactory - self._owlapi_reasoner = OpenlletReasonerFactory().getInstance().createReasoner(self._owlapi_ontology) - elif reasoner == "Structural": - from org.semanticweb.owlapi.reasoner.structural import StructuralReasonerFactory - self._owlapi_reasoner = StructuralReasonerFactory().createReasoner(self._owlapi_ontology) - else: - raise NotImplementedError("Not implemented") - - self.reasoner = reasoner + self.inference_types_mapping = import_and_include_axioms_generators() + self._owlapi_reasoner = initialize_reasoner(reasoner,self._owlapi_ontology) def _instances(self, ce: OWLClassExpression, direct=False) -> Set[OWLNamedIndividual]: """ @@ -1378,11 +1334,11 @@ def disjoint_data_properties(self, p: OWLDataProperty): yield from [self.mapper.map_(pe) for pe in self._owlapi_reasoner.getDisjointDataProperties(self.mapper.map_(p)).getFlattened()] - def types(self, i: OWLNamedIndividual, direct: bool = False): + def types(self, individual: OWLNamedIndividual, direct: bool = False): """Gets the named classes which are (potentially direct) types of the specified named individual. Args: - i: The individual whose types are to be retrieved. + individual: The individual whose types are to be retrieved. direct: Specifies if the direct types should be retrieved (True), or if all types should be retrieved (False). @@ -1392,7 +1348,7 @@ def types(self, i: OWLNamedIndividual, direct: bool = False): entails ClassAssertion(C, ind). """ yield from [self.mapper.map_(ind) for ind in - self._owlapi_reasoner.getTypes(self.mapper.map_(i), direct).getFlattened()] + self._owlapi_reasoner.getTypes(self.mapper.map_(individual), direct).getFlattened()] def has_consistent_ontology(self) -> bool: """ @@ -1544,3 +1500,48 @@ def unsatisfiable_classes(self): def get_root_ontology(self) -> AbstractOWLOntology: return self.ontology + +def initialize_reasoner(reasoner:str, owlapi_ontology): + # () Create a reasoner using the ontology + if reasoner == "HermiT": + from org.semanticweb.HermiT import ReasonerFactory + owlapi_reasoner = ReasonerFactory().createReasoner(owlapi_ontology) + assert owlapi_reasoner.getReasonerName() == "HermiT" + elif reasoner == "JFact": + from uk.ac.manchester.cs.jfact import JFactFactory + owlapi_reasoner = JFactFactory().createReasoner(owlapi_ontology) + elif reasoner == "Pellet": + from openllet.owlapi import PelletReasonerFactory + owlapi_reasoner = PelletReasonerFactory().createReasoner(owlapi_ontology) + elif reasoner == "Openllet": + from openllet.owlapi import OpenlletReasonerFactory + owlapi_reasoner = OpenlletReasonerFactory().getInstance().createReasoner(owlapi_ontology) + elif reasoner == "Structural": + from org.semanticweb.owlapi.reasoner.structural import StructuralReasonerFactory + owlapi_reasoner = StructuralReasonerFactory().createReasoner(owlapi_ontology) + else: + raise NotImplementedError("Not implemented") + return owlapi_reasoner +def import_and_include_axioms_generators(): + from org.semanticweb.owlapi.util import (InferredClassAssertionAxiomGenerator, InferredSubClassAxiomGenerator, + InferredEquivalentClassAxiomGenerator, + InferredDisjointClassesAxiomGenerator, + InferredEquivalentDataPropertiesAxiomGenerator, + InferredEquivalentObjectPropertyAxiomGenerator, + InferredInverseObjectPropertiesAxiomGenerator, + InferredSubDataPropertyAxiomGenerator, + InferredSubObjectPropertyAxiomGenerator, + InferredDataPropertyCharacteristicAxiomGenerator, + InferredObjectPropertyCharacteristicAxiomGenerator) + + return {"InferredClassAssertionAxiomGenerator": InferredClassAssertionAxiomGenerator(), + "InferredSubClassAxiomGenerator": InferredSubClassAxiomGenerator(), + "InferredDisjointClassesAxiomGenerator": InferredDisjointClassesAxiomGenerator(), + "InferredEquivalentClassAxiomGenerator": InferredEquivalentClassAxiomGenerator(), + "InferredInverseObjectPropertiesAxiomGenerator": InferredInverseObjectPropertiesAxiomGenerator(), + "InferredEquivalentDataPropertiesAxiomGenerator": InferredEquivalentDataPropertiesAxiomGenerator(), + "InferredEquivalentObjectPropertyAxiomGenerator": InferredEquivalentObjectPropertyAxiomGenerator(), + "InferredSubDataPropertyAxiomGenerator": InferredSubDataPropertyAxiomGenerator(), + "InferredSubObjectPropertyAxiomGenerator": InferredSubObjectPropertyAxiomGenerator(), + "InferredDataPropertyCharacteristicAxiomGenerator": InferredDataPropertyCharacteristicAxiomGenerator(), + "InferredObjectPropertyCharacteristicAxiomGenerator": InferredObjectPropertyCharacteristicAxiomGenerator()} From d77a4831695c219cd154f03b46c4a8be804a3559 Mon Sep 17 00:00:00 2001 From: Alkid Date: Mon, 11 Nov 2024 14:14:39 +0100 Subject: [PATCH 24/24] Version increment to 1.3.3 --- README.md | 4 ++-- docs/conf.py | 2 +- docs/usage/main.md | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index f5c2b4e5..2cfcbc91 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.2-blue)](https://pypi.org/project/owlapy/1.3.2/) -[![Docs](https://img.shields.io/badge/documentation-1.3.2-yellow)](https://dice-group.github.io/owlapy/usage/main.html) +[![Pypi](https://img.shields.io/badge/pypi-1.3.3-blue)](https://pypi.org/project/owlapy/1.3.3/) +[![Docs](https://img.shields.io/badge/documentation-1.3.3-yellow)](https://dice-group.github.io/owlapy/usage/main.html) ![OWLAPY](docs/_static/images/owlapy_logo.png) diff --git a/docs/conf.py b/docs/conf.py index 9e37f605..641cf1fb 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -13,7 +13,7 @@ project = 'OWLAPY' author = 'Ontolearn Team' -release = '1.3.2' +release = '1.3.3' # -- 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 36b77c2b..c646825c 100644 --- a/docs/usage/main.md +++ b/docs/usage/main.md @@ -1,6 +1,6 @@ # About owlapy -**Version:** owlapy 1.3.2 +**Version:** owlapy 1.3.3 **GitHub repository:** [https://github.com/dice-group/owlapy](https://github.com/dice-group/owlapy)