From 15147abc8596d4dd8bf647b130e690727e0c13be Mon Sep 17 00:00:00 2001 From: Jean-KOUAGOU Date: Mon, 25 Nov 2024 16:22:58 +0100 Subject: [PATCH] merge TripleStoreKnowledgeBase and TripleStore --- docs/usage/06_concept_learners.md | 8 +- ...oncept_learning_via_triplestore_example.py | 4 +- ontolearn/executor.py | 4 +- ontolearn/triple_store.py | 208 ++++++++---------- tests/test_triplestore.py | 9 +- 5 files changed, 110 insertions(+), 123 deletions(-) diff --git a/docs/usage/06_concept_learners.md b/docs/usage/06_concept_learners.md index 7c4bb382..02592431 100644 --- a/docs/usage/06_concept_learners.md +++ b/docs/usage/06_concept_learners.md @@ -272,7 +272,7 @@ image that contain the tree representation of that class expression. --------------------------------------------------------------------------------------- -## Use Triplestore Knowledge Base +## Use Triplestore as Knowledge Base Instead of going through nodes using expensive computation resources why not just make use of the efficient approach of querying a triplestore using SPARQL queries. We have brought this @@ -282,13 +282,13 @@ Let's see what it takes to make use of it. First of all you need a server which should host the triplestore for your ontology. If you don't already have one, see [Loading and Launching a Triplestore](#loading-and-launching-a-triplestore) below. -Now you can simply initialize a `TripleStoreKnowledgeBase` object that will server as an input for your desired +Now you can simply initialize a `TripleStore` object that will server as an input for your desired concept learner as follows: ```python -from ontolearn.triple_store import TripleStoreKnowledgeBase +from ontolearn.triple_store import TripleStore -kb = TripleStoreKnowledgeBase("http://your_domain/some_path/sparql") +kb = TripleStore("http://your_domain/some_path/sparql") ``` Notice that the triplestore endpoint is the only argument that you need to pass. diff --git a/examples/concept_learning_via_triplestore_example.py b/examples/concept_learning_via_triplestore_example.py index bd0aabb8..d31d4a44 100644 --- a/examples/concept_learning_via_triplestore_example.py +++ b/examples/concept_learning_via_triplestore_example.py @@ -5,7 +5,7 @@ from ontolearn.learning_problem import PosNegLPStandard from owlapy.owl_individual import IRI, OWLNamedIndividual from ontolearn.refinement_operators import ModifiedCELOERefinement -from ontolearn.triple_store import TripleStoreKnowledgeBase +from ontolearn.triple_store import TripleStore """ @@ -23,7 +23,7 @@ """ # Create a knowledge base object for the Family dataset using the URL address of the triplestore host only -kb = TripleStoreKnowledgeBase("http://localhost:3030/family/sparql") +kb = TripleStore("http://localhost:3030/family/sparql") # Define the model heur = CELOEHeuristic(expansionPenaltyFactor=0.05, startNodeBonus=1.0, nodeRefinementPenalty=0.01) diff --git a/ontolearn/executor.py b/ontolearn/executor.py index 60adbad4..1e243196 100644 --- a/ontolearn/executor.py +++ b/ontolearn/executor.py @@ -47,7 +47,7 @@ from ontolearn.learning_problem import PosNegLPStandard from ontolearn.refinement_operators import ModifiedCELOERefinement from ontolearn.metrics import Accuracy, F1, Recall, Precision, WeightedAccuracy -from ontolearn.triple_store import TripleStoreKnowledgeBase +from ontolearn.triple_store import TripleStore from ontolearn.value_splitter import BinningValueSplitter, EntropyValueSplitter logger = logging.getLogger(__name__) @@ -200,7 +200,7 @@ def execute(args): # pragma: no cover learner_type = models[args.model] optargs = {} if args.sparql_endpoint: - kb = TripleStoreKnowledgeBase(args.sparql_endpoint) + kb = TripleStore(args.sparql_endpoint) else: kb = KnowledgeBase(path=args.knowledge_base_path) diff --git a/ontolearn/triple_store.py b/ontolearn/triple_store.py index 3e4726e0..d50bd350 100644 --- a/ontolearn/triple_store.py +++ b/ontolearn/triple_store.py @@ -291,7 +291,6 @@ def __repr__(self): class TripleStoreReasoner(AbstractOWLReasoner): - __slots__ = "ontology" def __init__(self, ontology: TripleStoreOntology): self.ontology = ontology @@ -714,83 +713,6 @@ def is_isolated(self): pass -class TripleStoreKnowledgeBase(KnowledgeBase): - url: str - ontology: TripleStoreOntology - reasoner: TripleStoreReasoner - - def __init__(self, url: str=None): - assert url is not None, "url must be string" - self.url = url - self.ontology = TripleStoreOntology(url) - self.reasoner = TripleStoreReasoner(self.ontology) - super().__init__( ontology=self.ontology, reasoner=self.reasoner, load_class_hierarchy=False) - - def get_direct_sub_concepts(self, concept: OWLClass) -> Iterable[OWLClass]: - assert isinstance(concept, OWLClass) - yield from self.reasoner.sub_classes(concept, direct=True) - - def get_direct_parents(self, concept: OWLClassExpression) -> Iterable[OWLClass]: - assert isinstance(concept, OWLClass) - yield from self.reasoner.super_classes(concept, direct=True) - - def get_all_direct_sub_concepts(self, concept: OWLClassExpression) -> Iterable[OWLClassExpression]: - assert isinstance(concept, OWLClass) - yield from self.reasoner.sub_classes(concept, direct=True) - - def get_all_sub_concepts(self, concept: OWLClassExpression) -> Iterable[OWLClassExpression]: - assert isinstance(concept, OWLClass) - yield from self.reasoner.sub_classes(concept, direct=False) - - def get_concepts(self) -> Iterable[OWLClass]: - yield from self.ontology.classes_in_signature() - - @property - def concepts(self) -> Iterable[OWLClass]: - yield from self.ontology.classes_in_signature() - - def contains_class(self, concept: OWLClassExpression) -> bool: - assert isinstance(concept, OWLClass) - return concept in self.ontology.classes_in_signature() - - def most_general_object_properties( - self, *, domain: OWLClassExpression, inverse: bool = False) -> Iterable[OWLObjectProperty]: - assert isinstance(domain, OWLClassExpression) - func: Callable - func = ( - self.get_object_property_ranges - if inverse - else self.get_object_property_domains - ) - - inds_domain = self.individuals_set(domain) - for prop in self.ontology.object_properties_in_signature(): - if domain.is_owl_thing() or inds_domain <= self.individuals_set(func(prop)): - yield prop - - @property - def object_properties(self) -> Iterable[OWLObjectProperty]: - yield from self.ontology.object_properties_in_signature() - - def get_object_properties(self) -> Iterable[OWLObjectProperty]: - yield from self.ontology.object_properties_in_signature() - - @property - def data_properties(self) -> Iterable[OWLDataProperty]: - yield from self.ontology.data_properties_in_signature() - - def get_data_properties( - self, ranges: Set[OWLDatatype] = None - ) -> Iterable[OWLDataProperty]: - - if ranges is not None: - for dp in self.ontology.data_properties_in_signature(): - if self.get_data_property_ranges(dp) & ranges: - yield dp - else: - yield from self.ontology.data_properties_in_signature() - - ####################################################################################################################### # See https://github.com/dice-group/Ontolearn/issues/451 for the decision behind this seperation @@ -981,21 +903,89 @@ def domain_of_double_data_properties(self, prop: OWLDataProperty): query = f"{rdf_prefix}\n{rdfs_prefix}\n{xsd_prefix}SELECT DISTINCT ?x WHERE {{?x <{prop.str}> ?z}}" for binding in self.query(query).json()["results"]["bindings"]: yield OWLNamedIndividual(binding["x"]["value"]) -class TripleStore: + + +class TripleStore(KnowledgeBase): url: str - def __init__(self, reasoner=None, url: str = None): - - if reasoner is None: - assert url is not None, f"Reasoner:{reasoner} and url of a triplestore {url} cannot be both None." - self.g = TripleStoreReasonerOntology(url=url) - else: - self.g = reasoner - self.ontology = self.g - self.reasoner = self.g + ontology: TripleStoreOntology + reasoner: TripleStoreReasoner + def __init__(self, url: str=None): + assert url is not None, "url must be string" + self.g = TripleStoreReasonerOntology(url=url) + self.url = url + self.ontology = TripleStoreOntology(url) + self.reasoner = TripleStoreReasoner(self.ontology) + super().__init__( ontology=self.ontology, reasoner=self.reasoner, load_class_hierarchy=False) + def __str__(self): return f"TripleStore:{self.g}" + def get_direct_sub_concepts(self, concept: OWLClass) -> Iterable[OWLClass]: + assert isinstance(concept, OWLClass) + yield from self.reasoner.sub_classes(concept, direct=True) + + def get_direct_parents(self, concept: OWLClassExpression) -> Iterable[OWLClass]: + assert isinstance(concept, OWLClass) + yield from self.reasoner.super_classes(concept, direct=True) + + def get_all_direct_sub_concepts(self, concept: OWLClassExpression) -> Iterable[OWLClassExpression]: + assert isinstance(concept, OWLClass) + yield from self.reasoner.sub_classes(concept, direct=True) + + def get_all_sub_concepts(self, concept: OWLClassExpression) -> Iterable[OWLClassExpression]: + assert isinstance(concept, OWLClass) + yield from self.reasoner.sub_classes(concept, direct=False) + + def get_concepts(self) -> Iterable[OWLClass]: + yield from self.ontology.classes_in_signature() + + @property + def concepts(self) -> Iterable[OWLClass]: + yield from self.ontology.classes_in_signature() + + def contains_class(self, concept: OWLClassExpression) -> bool: + assert isinstance(concept, OWLClass) + return concept in self.ontology.classes_in_signature() + + def most_general_object_properties( + self, *, domain: OWLClassExpression, inverse: bool = False) -> Iterable[OWLObjectProperty]: + assert isinstance(domain, OWLClassExpression) + func: Callable + func = ( + self.get_object_property_ranges + if inverse + else self.get_object_property_domains + ) + + inds_domain = self.individuals_set(domain) + for prop in self.ontology.object_properties_in_signature(): + if domain.is_owl_thing() or inds_domain <= self.individuals_set(func(prop)): + yield prop + + @property + def object_properties(self) -> Iterable[OWLObjectProperty]: + yield from self.ontology.object_properties_in_signature() + + def get_object_properties(self) -> Iterable[OWLObjectProperty]: + yield from self.ontology.object_properties_in_signature() + + @property + def data_properties(self) -> Iterable[OWLDataProperty]: + yield from self.ontology.data_properties_in_signature() + + def get_data_properties( + self, ranges: Set[OWLDatatype] = None + ) -> Iterable[OWLDataProperty]: + + if ranges is not None: + for dp in self.ontology.data_properties_in_signature(): + if self.get_data_property_ranges(dp) & ranges: + yield dp + else: + yield from self.ontology.data_properties_in_signature() + + def __abox_expression(self, individual: OWLNamedIndividual) -> Generator[ Union[ OWLClass, @@ -1126,31 +1116,25 @@ def abox(self, individual: OWLNamedIndividual, mode: str = "native"): def are_owl_concept_disjoint(self, c: OWLClass, cc: OWLClass) -> bool: assert isinstance(c, OWLClass) and isinstance(cc, OWLClass) - return self.reasoner.are_owl_concept_disjoint(c, cc) - - def get_object_properties(self): - yield from self.reasoner.object_properties_in_signature() + return self.g.are_owl_concept_disjoint(c, cc) - def get_data_properties(self): - yield from self.reasoner.data_properties_in_signature() - - def get_concepts(self) -> OWLClass: - yield from self.reasoner.classes_in_signature() + def get_concepts(self) -> Generator[OWLClass, None, None]: + yield from self.ontology.classes_in_signature() - def get_classes_in_signature(self) -> OWLClass: - yield from self.reasoner.classes_in_signature() + def get_classes_in_signature(self) -> Generator[OWLClass, None, None]: + yield from self.ontology.classes_in_signature() - def get_most_general_classes(self): - yield from self.reasoner.most_general_classes() + def get_most_general_classes(self) -> Generator[OWLClass, None, None]: + yield from self.g.most_general_classes() def get_boolean_data_properties(self): - yield from self.reasoner.boolean_data_properties() + yield from self.g.boolean_data_properties() def get_double_data_properties(self): - yield from self.reasoner.double_data_properties() + yield from self.g.double_data_properties() def get_range_of_double_data_properties(self, prop: OWLDataProperty): - yield from self.reasoner.range_of_double_data_properties(prop) + yield from self.g.range_of_double_data_properties(prop) def individuals( self, @@ -1166,29 +1150,29 @@ def individuals( """ if concept is None or concept.is_owl_thing(): - yield from self.reasoner.individuals_in_signature() + yield from self.ontology.individuals_in_signature() else: - yield from self.reasoner.instances(concept, named_individuals=named_individuals) + yield from self.g.instances(concept, named_individuals=named_individuals) def get_types(self, ind: OWLNamedIndividual, direct: True) -> Generator[OWLClass, None, None]: if not direct: raise NotImplementedError("Inferring indirect types not available") - return self.reasoner.get_type_individuals(ind.str) + return self.g.get_type_individuals(ind.str) def get_all_sub_concepts(self, concept: OWLClass, direct=True): - yield from self.reasoner.subconcepts(concept, direct) + yield from self.g.subconcepts(concept, direct) def classes_in_signature(self): - yield from self.reasoner.classes_in_signature() + yield from self.ontology.classes_in_signature() def get_direct_parents(self, c: OWLClass): - yield from self.reasoner.get_direct_parents(c) + yield from self.g.get_direct_parents(c) def most_general_named_concepts(self): - yield from self.reasoner.most_general_named_concepts() + yield from self.g.most_general_named_concepts() def least_general_named_concepts(self): - yield from self.reasoner.least_general_named_concepts() + yield from self.g.least_general_named_concepts() def query(self, sparql: str): yield from self.g.query(sparql_query=sparql) diff --git a/tests/test_triplestore.py b/tests/test_triplestore.py index 626a5fb2..e8c2d7ba 100644 --- a/tests/test_triplestore.py +++ b/tests/test_triplestore.py @@ -40,8 +40,7 @@ def test_local_triplestore_family_tdl(self): break """ def test_remote_triplestore_dbpedia_tdl(self): - """ - url = "http://dice-dbpedia.cs.upb.de:9080/sparql" + url = "https://dbpedia.data.dice-research.org/sparql" kb = TripleStore(url=url) # Check whether there is a connection num_object_properties = len([i for i in kb.get_object_properties()]) @@ -53,6 +52,7 @@ def test_remote_triplestore_dbpedia_tdl(self): typed_neg = set(map(OWLNamedIndividual, map(IRI.create, examples["negative_examples"]))) lp = PosNegLPStandard(pos=typed_pos, neg=typed_neg) h = model.fit(learning_problem=lp).best_hypotheses() + print(h) assert h assert DLSyntaxObjectRenderer().render(h) save_owl_class_expressions(h) @@ -60,5 +60,8 @@ def test_remote_triplestore_dbpedia_tdl(self): assert sparql else: pass - """ + +if __name__ == "__main__": + test_triplestore = TestTriplestore() + test_triplestore.test_remote_triplestore_dbpedia_tdl()