Skip to content

Commit

Permalink
merge TripleStoreKnowledgeBase and TripleStore
Browse files Browse the repository at this point in the history
  • Loading branch information
Jean-KOUAGOU committed Nov 25, 2024
1 parent 5db17e3 commit 15147ab
Show file tree
Hide file tree
Showing 5 changed files with 110 additions and 123 deletions.
8 changes: 4 additions & 4 deletions docs/usage/06_concept_learners.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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.
Expand Down
4 changes: 2 additions & 2 deletions examples/concept_learning_via_triplestore_example.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

"""
Expand All @@ -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)
Expand Down
4 changes: 2 additions & 2 deletions ontolearn/executor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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__)
Expand Down Expand Up @@ -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)

Expand Down
208 changes: 96 additions & 112 deletions ontolearn/triple_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,7 +291,6 @@ def __repr__(self):


class TripleStoreReasoner(AbstractOWLReasoner):
__slots__ = "ontology"

def __init__(self, ontology: TripleStoreOntology):
self.ontology = ontology
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand All @@ -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)
9 changes: 6 additions & 3 deletions tests/test_triplestore.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()])
Expand All @@ -53,12 +52,16 @@ 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)
sparql = Owl2SparqlConverter().as_query("?x", h)
assert sparql
else:
pass
"""

if __name__ == "__main__":
test_triplestore = TestTriplestore()
test_triplestore.test_remote_triplestore_dbpedia_tdl()

0 comments on commit 15147ab

Please sign in to comment.