Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge TripleStoreKnowledgeBase and TripleStore #497

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is incorrect. We cannot have TripleStoreOntology, TripleStoreReasoner, and TripleStoreReasonerOntology, can we ?

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)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to print

assert h
assert DLSyntaxObjectRenderer().render(h)
save_owl_class_expressions(h)
sparql = Owl2SparqlConverter().as_query("?x", h)
assert sparql
else:
pass
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

pass is problematic. This is not a really test

"""

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove
if name == "main":
test_triplestore = TestTriplestore()
test_triplestore.test_remote_triplestore_dbpedia_tdl()

No need these three lines.

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

Loading