# -----------------------------------------------------------------------------
# MIT License
#
# Copyright (c) 2024 Ontolearn Team
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in all
# copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
# -----------------------------------------------------------------------------
"""Triple store representations."""
import logging
import re
from itertools import chain
from typing import Iterable, Set, Optional, Generator, Union, Tuple, Callable, FrozenSet
import requests
from owlapy.class_expression import *
from owlapy.class_expression import OWLThing
from owlapy.iri import IRI
from owlapy.owl_axiom import (
OWLObjectPropertyRangeAxiom,
OWLObjectPropertyDomainAxiom,
OWLDataPropertyRangeAxiom,
OWLDataPropertyDomainAxiom,
OWLClassAxiom,
OWLEquivalentClassesAxiom, OWLAxiom,
)
from owlapy.owl_datatype import OWLDatatype
from owlapy.owl_individual import OWLNamedIndividual
from owlapy.owl_literal import OWLLiteral, BooleanOWLDatatype, DoubleOWLDatatype, NUMERIC_DATATYPES, TIME_DATATYPES
from owlapy.owl_ontology import OWLOntologyID
from owlapy.abstracts import AbstractOWLOntology, AbstractOWLReasoner
from owlapy.owl_property import (
OWLDataProperty,
OWLObjectPropertyExpression,
OWLObjectInverseOf,
OWLObjectProperty,
OWLProperty, OWLDataPropertyExpression,
)
from requests import Response
from requests.exceptions import RequestException, JSONDecodeError
from owlapy.converter import Owl2SparqlConverter
from ontolearn.abstracts import AbstractKnowledgeBase
# import traceback
from collections import Counter
logger = logging.getLogger(__name__)
rdfs_prefix = "PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#>\n "
owl_prefix = "PREFIX owl: <http://www.w3.org/2002/07/owl#>\n "
rdf_prefix = "PREFIX rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#>\n "
xsd_prefix = "PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>\n"
[docs]
def is_valid_url(url) -> bool:
"""
Check the validity of a URL.
Args:
url (str): The url to validate.
Returns:
True if url is not None, and it passes the regex check.
"""
regex = re.compile(
r"^https?://" # http:// or https://
r"(?:(?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\.)+[A-Z]{2,6}\.?|" # domain...
r"localhost|" # localhost...
r"\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})" # ...or ip
r"(?::\d+)?" # optional port
r"(?:/?|[/?]\S+)$",
re.IGNORECASE,
)
return url is not None and regex.search(url)
[docs]
def peek(generator):
"""Peeks the generator and returns the first element and the generator. Used to check whether the generator is
empty by checking if the first element is None.
Note: This is more efficiently than converting the generator to set and checking the len()"""
try:
first = next(generator)
except StopIteration:
return None
return first, chain([first], generator)
[docs]
def send_http_request_to_ts_and_fetch_results(triplestore_address: str, query: str, return_type: Callable):
"""
Execute the SPARQL query in the given triplestore_address and return the result as the given return_type.
Args:
triplestore_address (str): The triplestore address where the query will be executed.
query (str): SPARQL query where the root variable should be '?x'.
return_type (Callable): OWLAPY class as type. e.g. OWLClass, OWLNamedIndividual, etc.
Returns:
Generator containing the results of the query as the given type.
"""
try:
response = requests.post(triplestore_address, data={"query": query})
except RequestException as e:
raise RequestException(
f"Make sure the server is running on the `triplestore_address` = '{triplestore_address}'"
f". Check the error below:"
f"\n -->Error: {e}"
)
try:
if return_type == OWLLiteral:
yield from unwrap(response)
else:
yield from [return_type(i) for i in unwrap(response) if i is not None]
# return [return_type(IRI.create(i['x']['value'])) for i in
# response.json()['results']['bindings']]
except JSONDecodeError as e:
raise JSONDecodeError(
f"Something went wrong with decoding JSON from the response. Check for typos in "
f"the `triplestore_address` = '{triplestore_address}' otherwise the error is likely "
f"caused by an internal issue. \n -->Error: {e}"
)
[docs]
def unwrap(result: Response):
json = result.json()
vars_ = list(json["head"]["vars"])
for b in json["results"]["bindings"]:
val = []
for v in vars_:
if b[v]["type"] == "uri":
val.append(IRI.create(b[v]["value"]))
elif b[v]["type"] == "bnode":
continue
elif b[v]["type"] == "literal" and "datatype" in b[v]:
val.append(OWLLiteral(b[v]["value"], OWLDatatype(IRI.create(b[v]["datatype"]))))
elif b[v]["type"] == "literal" and "datatype" not in b[v]:
continue
elif b[v]["type"] == "literal" and "datatype" in b[v]:
val.append(OWLLiteral(b[v]["value"], OWLDatatype(IRI.create(b[v]["datatype"]))))
elif b[v]["type"] == "literal" and "datatype" not in b[v]:
continue
else:
raise NotImplementedError(f"Seems like this kind of data is not handled: {b[v]}")
if len(val) == 1:
yield val.pop()
else:
yield None
[docs]
def suf(direct: bool):
"""Put the star for rdfs properties depending on direct param"""
return " " if direct else "* "
[docs]
class TripleStoreOntology(AbstractOWLOntology):
def __init__(self, triplestore_address: str):
assert is_valid_url(triplestore_address), (
"You should specify a valid URL in the following argument: "
"'triplestore_address' of class `TripleStore`")
self.url = triplestore_address
[docs]
def classes_in_signature(self) -> Iterable[OWLClass]:
query = owl_prefix + "SELECT DISTINCT ?x WHERE {?x a owl:Class.}"
yield from send_http_request_to_ts_and_fetch_results(self.url, query, OWLClass)
[docs]
def data_properties_in_signature(self) -> Iterable[OWLDataProperty]:
query = owl_prefix + "SELECT DISTINCT ?x\n " + "WHERE {?x a owl:DatatypeProperty.}"
yield from send_http_request_to_ts_and_fetch_results(self.url, query, OWLDataProperty)
[docs]
def object_properties_in_signature(self) -> Iterable[OWLObjectProperty]:
query = owl_prefix + "SELECT DISTINCT ?x\n " + "WHERE {?x a owl:ObjectProperty.}"
yield from send_http_request_to_ts_and_fetch_results(self.url, query, OWLObjectProperty)
# def individuals_in_signature(self) -> Generator[OWLNamedIndividual, None, None]:
# TODO AB: <<TO BE DECIDED>> this or the implementation down below
# TODO AB: owl:Class is not an individual!?
# TODO AB: Why the return type is Generator[OWLNamedIndividual, None, None]? It does not adhere to
# individuals_in_signature method of AbstractOWLOntology.
# # owl:OWLNamedIndividual is often missing: Perhaps we should add union as well
# query = (
# owl_prefix + "SELECT DISTINCT ?x\n " + "WHERE {?x a ?y. ?y a owl:Class.}"
# )
# for binding in self.query(query).json()["results"]["bindings"]:
# yield OWLNamedIndividual(binding["x"]["value"])
[docs]
def individuals_in_signature(self) -> Iterable[OWLNamedIndividual]:
# TODO AB: Maybe extend this method to check for implicit individuals (idea: check for ?x a owl:Thing and
# exclude everything that is not a class, property, etc.)
query = owl_prefix + "SELECT DISTINCT ?x\n " + "WHERE {?x a owl:NamedIndividual.}"
yield from send_http_request_to_ts_and_fetch_results(self.url, query, OWLNamedIndividual)
[docs]
def equivalent_classes_axioms(self, c: OWLClass) -> Iterable[OWLEquivalentClassesAxiom]:
query = (owl_prefix + "SELECT DISTINCT ?x" + "WHERE { ?x owl:equivalentClass " + f"<{c.str}>."
+ "FILTER(?x != " + f"<{c.str}>)}}")
for cls in send_http_request_to_ts_and_fetch_results(self.url, query, OWLClass):
yield OWLEquivalentClassesAxiom([c, cls])
[docs]
def general_class_axioms(self) -> Iterable[OWLClassAxiom]:
# doc strings inherited from abstract method in base class
raise NotImplementedError("Currently, ")
[docs]
def data_property_domain_axioms(self, pe: OWLDataProperty) -> Iterable[OWLDataPropertyDomainAxiom]:
first_element, domains = peek(self.get_property_domains(pe))
if first_element is None:
yield OWLDataPropertyDomainAxiom(pe, OWLThing)
else:
for dom in domains:
yield OWLDataPropertyDomainAxiom(pe, dom)
[docs]
def data_property_range_axioms(self, pe: OWLDataProperty) -> Iterable[OWLDataPropertyRangeAxiom]:
query = f"{rdfs_prefix}SELECT DISTINCT ?x WHERE {{ <{pe.str}> rdfs:range ?x. }}"
for rng in send_http_request_to_ts_and_fetch_results(self.url, query, OWLDatatype):
yield OWLDataPropertyRangeAxiom(pe, rng)
[docs]
def object_property_domain_axioms(
self, pe: OWLObjectProperty
) -> Iterable[OWLObjectPropertyDomainAxiom]:
first_element, domains = peek(self.get_property_domains(pe))
if first_element is None:
yield OWLObjectPropertyDomainAxiom(pe, OWLThing)
else:
for dom in domains:
yield OWLObjectPropertyDomainAxiom(pe, dom)
[docs]
def object_property_range_axioms(self, pe: OWLObjectProperty) -> Iterable[OWLObjectPropertyRangeAxiom]:
query = rdfs_prefix + "SELECT ?x WHERE { " + f"<{pe.str}>" + " rdfs:range ?x. }"
first_element, ranges = peek(send_http_request_to_ts_and_fetch_results(self.url, query, OWLClass))
if first_element is None:
yield OWLObjectPropertyRangeAxiom(pe, OWLThing)
else:
for rng in ranges:
yield OWLObjectPropertyRangeAxiom(pe, rng)
[docs]
def get_property_domains(self, pe: OWLProperty) -> Set:
if isinstance(pe, OWLObjectProperty) or isinstance(pe, OWLDataProperty):
query = rdfs_prefix + "SELECT ?x WHERE { " + f"<{pe.str}>" + " rdfs:domain ?x. }"
yield from send_http_request_to_ts_and_fetch_results(self.url, query, OWLClass)
else:
raise NotImplementedError
[docs]
def get_owl_ontology_manager(self):
# no manager for this kind of Ontology
# doc strings inherited from abstract method in base class
pass
[docs]
def get_ontology_id(self) -> OWLOntologyID:
# doc strings inherited from abstract method in base class
# query = (rdf_prefix + owl_prefix +
# "SELECT ?ontologyIRI WHERE { ?ontology rdf:type owl:Ontology . ?ontology rdf:about ?ontologyIRI .}")
# return list(get_results_from_ts(self.url, query, OWLOntologyID)).pop()
raise NotImplementedError
[docs]
def add_axiom(self, axiom: Union[OWLAxiom, Iterable[OWLAxiom]]):
"""Cant modify a triplestore ontology. Implemented because of the base class."""
pass
[docs]
def remove_axiom(self, axiom: Union[OWLAxiom, Iterable[OWLAxiom]]):
"""Cant modify a triplestore ontology. Implemented because of the base class."""
pass
[docs]
def __eq__(self, other):
if isinstance(other, type(self)):
return self.url == other.url
return NotImplemented
[docs]
def __hash__(self):
return hash(self.url)
[docs]
def __repr__(self):
return f"TripleStoreOntology({self.url})"
[docs]
class TripleStoreReasoner(AbstractOWLReasoner):
def __init__(self, ontology: TripleStoreOntology):
self.ontology = ontology
self.url = self.ontology.url
self._owl2sparql_converter = Owl2SparqlConverter()
[docs]
def query(self, sparql_query: str):
return requests.Session().post(self.url, data={"query": sparql_query})
[docs]
def data_property_domains(self, pe: OWLDataProperty, direct: bool = False) -> Iterable[OWLClassExpression]:
domains = {d.get_domain() for d in self.ontology.data_property_domain_axioms(pe)}
sub_domains = set(chain.from_iterable([self.sub_classes(d) for d in domains]))
yield from domains - sub_domains
if not direct:
yield from sub_domains
[docs]
def object_property_domains(self, pe: OWLObjectProperty, direct: bool = False) -> Iterable[OWLClassExpression]:
domains = {
d.get_domain() for d in self.ontology.object_property_domain_axioms(pe)
}
sub_domains = set(chain.from_iterable([self.sub_classes(d) for d in domains]))
yield from domains - sub_domains
if not direct:
yield from sub_domains
[docs]
def object_property_ranges(self, pe: OWLObjectProperty, direct: bool = False) -> Iterable[OWLClassExpression]:
ranges = {r.get_range() for r in self.ontology.object_property_range_axioms(pe)}
sub_ranges = set(chain.from_iterable([self.sub_classes(d) for d in ranges]))
yield from ranges - sub_ranges
if not direct:
yield from sub_ranges
[docs]
def data_property_ranges(self, pe: OWLDataProperty, direct: bool = True) -> Iterable[OWLClassExpression]:
if direct:
yield from [r.get_range() for r in self.ontology.data_property_range_axioms(pe)]
else:
# hierarchy of data types is not considered.
return NotImplemented()
[docs]
def equivalent_classes(self, ce: OWLClassExpression, only_named: bool = True) -> Iterable[OWLClassExpression]:
if only_named:
if isinstance(ce, OWLClass):
query = (owl_prefix + "SELECT DISTINCT ?x " + "WHERE { {?x owl:equivalentClass " + f"<{ce.str}>.}}"
+ "UNION {" + f"<{ce.str}>" + " owl:equivalentClass ?x.}" + "FILTER(?x != " + f"<{ce.str}>)}}")
yield from send_http_request_to_ts_and_fetch_results(self.url, query, OWLClass)
else:
logger.info(msg=f"Equivalent classes for complex class expressions is not implemented\t{ce}")
# raise NotImplementedError(f"Equivalent classes for complex class expressions is not implemented\t{ce}")
yield from {}
else:
raise NotImplementedError("Finding equivalent complex classes is not implemented")
[docs]
def disjoint_classes(self, ce: OWLClassExpression, only_named: bool = True) -> Iterable[OWLClassExpression]:
if only_named:
if isinstance(ce, OWLClass):
query = owl_prefix + " SELECT DISTINCT ?x " + "WHERE { " + f"<{ce.str}>" + " owl:disjointWith ?x .}"
yield from send_http_request_to_ts_and_fetch_results(self.url, query, OWLClass)
else:
raise NotImplementedError(
"Disjoint classes for complex class expressions is not implemented"
)
else:
raise NotImplementedError(
"Finding disjoint complex classes is not implemented"
)
[docs]
def different_individuals(self, ind: OWLNamedIndividual) -> Iterable[OWLNamedIndividual]:
query = (owl_prefix + rdf_prefix + "SELECT DISTINCT ?x \n"
+ "WHERE{ ?allDifferent owl:distinctMembers/rdf:rest*/rdf:first ?x.\n"
+ "?allDifferent owl:distinctMembers/rdf:rest*/rdf:first" + f"<{ind.str}>" + ".\n"
+ "FILTER(?x != " + f"<{ind.str}>" + ")}")
yield from send_http_request_to_ts_and_fetch_results(self.url, query, OWLNamedIndividual)
[docs]
def same_individuals(self, ind: OWLNamedIndividual) -> Iterable[OWLNamedIndividual]:
query = (owl_prefix + "SELECT DISTINCT ?x WHERE {{ ?x owl:sameAs " + f"<{ind.str}>" + " .}"
+ "UNION { " + f"<{ind.str}>" + " owl:sameAs ?x.}}")
yield from send_http_request_to_ts_and_fetch_results(self.url, query, OWLNamedIndividual)
[docs]
def equivalent_object_properties(self, op: OWLObjectPropertyExpression) -> Iterable[OWLObjectPropertyExpression]:
if isinstance(op, OWLObjectProperty):
query = (owl_prefix + "SELECT DISTINCT ?x " + "WHERE { {?x owl:equivalentProperty " + f"<{op.str}>.}}"
+ "UNION {" + f"<{op.str}>" + " owl:equivalentProperty ?x.}" + "FILTER(?x != " + f"<{op.str}>)}}")
yield from send_http_request_to_ts_and_fetch_results(self.url, query, OWLObjectProperty)
elif isinstance(op, OWLObjectInverseOf):
query = (owl_prefix + "SELECT DISTINCT ?x "
+ "WHERE { ?inverseProperty owl:inverseOf " + f"<{op.get_inverse().str}> ."
+ " {?x owl:equivalentProperty ?inverseProperty .}"
+ "UNION { ?inverseProperty owl:equivalentClass ?x.} FILTER(?x != ?inverseProperty }>)}")
yield from send_http_request_to_ts_and_fetch_results(self.url, query, OWLObjectProperty)
[docs]
def equivalent_data_properties(self, dp: OWLDataProperty) -> Iterable[OWLDataProperty]:
query = (owl_prefix + "SELECT DISTINCT ?x" + "WHERE { {?x owl:equivalentProperty " + f"<{dp.str}>.}}"
+ "UNION {" + f"<{dp.str}>" + " owl:equivalentProperty ?x.}" + "FILTER(?x != " + f"<{dp.str}>)}}")
yield from send_http_request_to_ts_and_fetch_results(self.url, query, OWLDataProperty)
[docs]
def data_property_values(self, ind: OWLNamedIndividual, pe: OWLDataProperty, direct: bool = True) \
-> Iterable[OWLLiteral]:
query = "SELECT ?x WHERE { " + f"<{ind.str}> " + f"<{pe.str}>" + " ?x . }"
yield from send_http_request_to_ts_and_fetch_results(self.url, query, OWLLiteral)
if not direct:
for prop in self.sub_data_properties(pe):
yield from self.data_property_values(ind, prop, True)
[docs]
def object_property_values(self, ind: OWLNamedIndividual, pe: OWLObjectPropertyExpression, direct: bool = True) \
-> Iterable[OWLNamedIndividual]:
if isinstance(pe, OWLObjectProperty):
query = "SELECT ?x WHERE { " + f"<{ind.str}> " + f"<{pe.str}>" + " ?x . }"
yield from send_http_request_to_ts_and_fetch_results(self.url, query, OWLNamedIndividual)
elif isinstance(pe, OWLObjectInverseOf):
query = (owl_prefix + "SELECT ?x WHERE { ?inverseProperty owl:inverseOf "
+ f"<{pe.get_inverse().str}>." + f"<{ind.str}> ?inverseProperty ?x . }}")
yield from send_http_request_to_ts_and_fetch_results(self.url, query, OWLNamedIndividual)
if not direct:
for prop in self.sub_object_properties(pe):
yield from self.object_property_values(ind, prop, True)
[docs]
def flush(self) -> None:
pass
# def instances(self, expression: OWLClassExpression, named_individuals: bool = False) \
# -> Generator[OWLNamedIndividual, None, None]:
# TODO AB: <<TO BE DECIDED>> this or the implementation down below
# TODO AB: Why the return type is Generator[OWLNamedIndividual, None, None]?
# It does not adhere to the return type of `instances` of AbstractOWLReasoner.
# assert isinstance(expression, OWLClassExpression)
# try:
# sparql_query = owl_expression_to_sparql(expression=expression,
# named_individuals=named_individuals)
#
# except Exception as exc:
# print(f"Error at converting {expression} into sparql")
# traceback.print_exception(exc)
# print(f"Error at converting {expression} into sparql")
# raise RuntimeError("Couldn't convert")
# try:
# # TODO:Be aware of the implicit inference of x being OWLNamedIndividual!
# for binding in self.query(sparql_query).json()["results"]["bindings"]:
# yield OWLNamedIndividual(binding["x"]["value"])
# except:
# print(self.query(sparql_query).text)
# raise RuntimeError
[docs]
def instances(self, ce: OWLClassExpression, direct: bool = False, seen_set: Set = None) \
-> Iterable[OWLNamedIndividual]:
if not seen_set:
seen_set = set()
seen_set.add(ce)
ce_to_sparql = self._owl2sparql_converter.as_query("?x", ce)
if not direct:
ce_to_sparql = ce_to_sparql.replace(
"?x a ",
"?x a ?some_cls. \n ?some_cls <http://www.w3.org/2000/01/rdf-schema#subClassOf>* ",
)
yield from send_http_request_to_ts_and_fetch_results(self.url, ce_to_sparql, OWLNamedIndividual)
if not direct:
for cls in self.equivalent_classes(ce):
if cls not in seen_set:
seen_set.add(cls)
yield from self.instances(cls, direct, seen_set)
[docs]
def sub_classes(self, ce: OWLClassExpression, direct: bool = False, only_named: bool = True) \
-> Iterable[OWLClassExpression]:
if not only_named:
raise NotImplementedError("Finding anonymous subclasses not implemented")
if isinstance(ce, OWLClass):
query = rdfs_prefix + "SELECT ?x WHERE { ?x rdfs:subClassOf" + suf(direct) + f"<{ce.str}>" + ". }"
results = list(send_http_request_to_ts_and_fetch_results(self.url, query, OWLClass))
if ce in results:
# TODO AB: Should we remove ce?
results.remove(ce)
yield from results
else:
raise NotImplementedError(
"Subclasses of complex classes retrieved via triple store is not implemented"
)
# query = "PREFIX rdfs: <http://www.w3.org/2000/01/rdf-schema#> " \
# "SELECT DISTINCT ?x WHERE { ?x rdfs:subClassOf" + suf(direct) + " ?c. \n" \
# "?s a ?c . \n"
# ce_to_sparql_statements = self._owl2sparql_converter.convert("?s", ce)
# for s in ce_to_sparql_statements:
# query = query + s + "\n"
# query = query + "}"
# yield from get_results_from_ts(self._triplestore_address, query, OWLClass)
[docs]
def super_classes(self, ce: OWLClassExpression, direct: bool = False, only_named: bool = True) \
-> Iterable[OWLClassExpression]:
if not only_named:
raise NotImplementedError("Finding anonymous superclasses not implemented")
if isinstance(ce, OWLClass):
if ce == OWLThing:
return []
query = rdfs_prefix + "SELECT ?x WHERE { " + f"<{ce.str}>" + " rdfs:subClassOf" + suf(direct) + "?x. }"
results = list(send_http_request_to_ts_and_fetch_results(self.url, query, OWLClass))
if ce in results:
results.remove(ce)
if (not direct and OWLThing not in results) or len(results) == 0:
results.append(OWLThing)
yield from results
else:
raise NotImplementedError(
"Superclasses of complex classes retrieved via triple store is not "
"implemented"
)
[docs]
def disjoint_object_properties(self, op: OWLObjectPropertyExpression) -> Iterable[OWLObjectPropertyExpression]:
if isinstance(op, OWLObjectProperty):
query = (owl_prefix + rdf_prefix + "SELECT DISTINCT ?x \n"
+ "WHERE{ ?AllDisjointProperties owl:members/rdf:rest*/rdf:first ?x.\n"
+ "?AllDisjointProperties owl:members/rdf:rest*/rdf:first" + f"<{op.str}>" + ".\n"
+ "FILTER(?x != " + f"<{op.str}>" + ")}")
yield from send_http_request_to_ts_and_fetch_results(self.url, query, OWLObjectProperty)
elif isinstance(op, OWLObjectInverseOf):
query = (owl_prefix + " SELECT DISTINCT ?x "
+ "WHERE { ?inverseProperty owl:inverseOf " + f"<{op.get_inverse().str}> ."
+ " ?AllDisjointProperties owl:members/rdf:rest*/rdf:first ?x.\n"
+ " ?AllDisjointProperties owl:members/rdf:rest*/rdf:first ?inverseProperty.\n"
+ " FILTER(?x != ?inverseProperty)}")
yield from send_http_request_to_ts_and_fetch_results(self.url, query, OWLObjectProperty)
[docs]
def disjoint_data_properties(self, dp: OWLDataProperty) -> Iterable[OWLDataProperty]:
query = (owl_prefix + rdf_prefix + "SELECT DISTINCT ?x \n"
+ "WHERE{ ?AllDisjointProperties owl:members/rdf:rest*/rdf:first ?x.\n"
+ "?AllDisjointProperties owl:members/rdf:rest*/rdf:first" + f"<{dp.str}>" + ".\n"
+ "FILTER(?x != " + f"<{dp.str}>" + ")}")
yield from send_http_request_to_ts_and_fetch_results(self.url, query, OWLDataProperty)
[docs]
def all_data_property_values(self, pe: OWLDataProperty, direct: bool = True) -> Iterable[OWLLiteral]:
query = "SELECT DISTINCT ?x WHERE { ?y" + f"<{pe.str}>" + " ?x . }"
yield from send_http_request_to_ts_and_fetch_results(self.url, query, OWLLiteral)
if not direct:
for prop in self.sub_data_properties(pe):
yield from self.all_data_property_values(prop, True)
[docs]
def sub_data_properties(self, dp: OWLDataProperty, direct: bool = False) -> Iterable[OWLDataProperty]:
query = rdfs_prefix + "SELECT ?x WHERE { ?x rdfs:subPropertyOf" + suf(direct) + f"<{dp.str}>" + ". }"
yield from send_http_request_to_ts_and_fetch_results(self.url, query, OWLDataProperty)
[docs]
def super_data_properties(self, dp: OWLDataProperty, direct: bool = False) -> Iterable[OWLDataProperty]:
query = rdfs_prefix + "SELECT ?x WHERE {" + f"<{dp.str}>" + " rdfs:subPropertyOf" + suf(direct) + " ?x. }"
yield from send_http_request_to_ts_and_fetch_results(self.url, query, OWLDataProperty)
[docs]
def sub_object_properties(self, op: OWLObjectPropertyExpression, direct: bool = False) \
-> Iterable[OWLObjectPropertyExpression]:
if isinstance(op, OWLObjectProperty):
query = (rdfs_prefix + "SELECT ?x WHERE { ?x rdfs:subPropertyOf" + suf(direct) + f"<{op.str}> ." +
" FILTER(?x != " + f"<{op.str}>) }}")
yield from send_http_request_to_ts_and_fetch_results(self.url, query, OWLObjectProperty)
elif isinstance(op, OWLObjectInverseOf):
query = (rdfs_prefix + "SELECT ?x "
+ "WHERE { ?inverseProperty owl:inverseOf " + f"<{op.get_inverse().str}> ."
+ " ?x rdfs:subPropertyOf" + suf(direct) + " ?inverseProperty . }")
yield from send_http_request_to_ts_and_fetch_results(self.url, query, OWLObjectProperty)
[docs]
def super_object_properties(self, op: OWLObjectPropertyExpression, direct: bool = False) \
-> Iterable[OWLObjectPropertyExpression]:
if isinstance(op, OWLObjectProperty):
query = (rdfs_prefix + "SELECT ?x WHERE {" + f"<{op.str}>" + " rdfs:subPropertyOf" + suf(direct) + " ?x. "
+ "FILTER(?x != " + f"<{op.str}>) }}")
yield from send_http_request_to_ts_and_fetch_results(self.url, query, OWLObjectProperty)
elif isinstance(op, OWLObjectInverseOf):
query = (rdfs_prefix + "SELECT ?x "
+ "WHERE { ?inverseProperty owl:inverseOf " + f"<{op.get_inverse().str}> ."
+ " ?inverseProperty rdfs:subPropertyOf" + suf(direct) + "?x . }")
yield from send_http_request_to_ts_and_fetch_results(self.url, query, OWLObjectProperty)
[docs]
def types(self, ind: OWLNamedIndividual, direct: bool = False) -> Iterable[OWLClass]:
if direct:
query = "SELECT ?x WHERE {" + f"<{ind.str}> a" + " ?x. }"
else:
query = rdfs_prefix + "SELECT DISTINCT ?x WHERE {" + f"<{ind.str}> a ?cls. " " ?cls rdfs:subClassOf* ?x}"
yield from send_http_request_to_ts_and_fetch_results(self.url, query, OWLClass)
[docs]
def get_root_ontology(self) -> AbstractOWLOntology:
return self.ontology
[docs]
def is_isolated(self):
# not needed here
pass
[docs]
class TripleStore(AbstractKnowledgeBase):
def __init__(self, ontology=None, reasoner=None, url: str = None):
self.url = url
self.ontology = ontology
self.reasoner = reasoner
if url is None:
if ontology is not None:
self.url = ontology.url
else:
assert (reasoner is not None), "ontology or reasoner or url must be provided"
self.url = reasoner.url
if ontology is None:
if reasoner is not None:
self.ontology = reasoner.ontology
else:
self.ontology = TripleStoreOntology(url)
if reasoner is None:
self.reasoner = TripleStoreReasoner(self.ontology)
assert self.url == self.ontology.url == self.reasoner.url, "URLs do not match"
[docs]
def __str__(self):
return f"TripleStore:{self.ontology, self.reasoner, self.url}"
[docs]
def query(self, sparql_query: str):
return requests.Session().post(self.url, data={"query": sparql_query})
def _abox(self, str_iri: str) -> Generator[
Tuple[
Tuple[OWLNamedIndividual, OWLProperty, OWLClass],
Tuple[OWLObjectProperty, OWLObjectProperty, OWLNamedIndividual],
Tuple[OWLObjectProperty, OWLDataProperty, OWLLiteral],
],
None,
None,
]:
"""@TODO:"""
sparql_query = f"SELECT DISTINCT ?p ?o WHERE {{ <{str_iri}> ?p ?o }}"
subject_ = OWLNamedIndividual(str_iri)
for binding in self.query(sparql_query).json()["results"]["bindings"]:
p, o = binding["p"], binding["o"]
# ORDER MATTERS
if p["value"] == "http://www.w3.org/1999/02/22-rdf-syntax-ns#type":
yield subject_, OWLProperty(
"http://www.w3.org/1999/02/22-rdf-syntax-ns#type"
), OWLClass(o["value"])
elif o["type"] == "uri":
#################################################################
# IMPORTANT
# Can we assume that if o has URI and is not owl class, then o can be considered as an individual ?
# RE AB: No, it can be everything identified by an IRI, for example a property.
#################################################################
yield subject_, OWLObjectProperty(p["value"]), OWLNamedIndividual(
o["value"]
)
elif o["type"] == "literal":
if data_type := o.get("datatype", None):
if data_type == "http://www.w3.org/2001/XMLSchema#boolean":
yield subject_, OWLDataProperty(p["value"]), OWLLiteral(value=bool(o["value"]))
elif data_type == "http://www.w3.org/2001/XMLSchema#integer":
yield subject_, OWLDataProperty(p["value"]), OWLLiteral(value=int(o["value"]))
elif data_type == "http://www.w3.org/2001/XMLSchema#nonNegativeInteger":
# TODO AB: set type to NonNegativeInteger for OWLLiteral below
# after integrating the new owlapy release (> 1.3.3)
yield subject_, OWLDataProperty(p["value"]), OWLLiteral(value=int(o["value"]))
elif data_type == "http://www.w3.org/2001/XMLSchema#double":
yield subject_, OWLDataProperty(p["value"]), OWLLiteral(value=float(o["value"]))
else:
# TODO: Unclear for the time being.
# print(f"Currently this type of literal is not supported:{o} but can done easily let us know :)")
continue
"""
# TODO: Converting a SPARQL query becomes an issue with strings.
elif data_type == "http://www.w3.org/2001/XMLSchema#string":
yield subject_, OWLDataProperty(p["value"]), OWLLiteral(value=repr(o["value"]))
elif data_type == "http://www.w3.org/2001/XMLSchema#date":
yield subject_, OWLDataProperty(p["value"]), OWLLiteral(value=repr(o["value"]))
"""
else:
# print(f"Currently this type of literal is not supported:{o} but can done easily let us know :)")
continue
# yield subject_, OWLDataProperty(p["value"]), OWLLiteral(value=repr(o["value"]))
else:
raise RuntimeError(f"Unrecognized type {subject_} ({p}) ({o})")
def __abox_expression(self, individual: OWLNamedIndividual) -> Generator[
Union[
OWLClass,
OWLObjectSomeValuesFrom,
OWLObjectMinCardinality,
OWLDataSomeValuesFrom,
],
None,
None,
]:
"""
Return OWL Class Expressions obtained from all set of triples where an input OWLNamedIndividual is subject.
Retrieve all triples (i,p,o) where p \in Resources, and o \in [Resources, Literals] and return the followings
1- Owl Named Classes: C(i)=1.
2- ObjectSomeValuesFrom Nominals: \exists r. {a, b, ..., d}, e.g. (i r, a) exists.
3- OWLObjectSomeValuesFrom over named classes: \exists r. C s.t. x \in {a, b, ..., d} C(x)=1.
4- OWLObjectMinCardinality over named classes: ≥ c r. C
5- OWLDataSomeValuesFrom over literals: \exists r. {literal_a, ..., literal_b}
"""
object_property_to_individuals = dict()
data_property_to_individuals = dict()
# To no return duplicate objects.
quantifier_gate = set()
# (1) Iterate over triples where individual is in the subject position.
for s, p, o in self._abox(str_iri=individual.str):
if isinstance(p, OWLProperty) and isinstance(o, OWLClass):
##############################################################
# RETURN OWLClass
##############################################################
yield o
elif isinstance(p, OWLObjectProperty) and isinstance(o, OWLNamedIndividual):
##############################################################
# Store for \exist r. {i, ..., j} and OWLObjectMinCardinality over type counts
##############################################################
object_property_to_individuals.setdefault(p, []).append(o)
elif isinstance(p, OWLDataProperty) and isinstance(o, OWLLiteral):
##############################################################
# Store for \exist r. {literal, ..., another literal}
##############################################################
data_property_to_individuals.setdefault(p, []).append(o)
else:
raise RuntimeError(
f"Unrecognized triples to expression mappings {p}{o}"
)
# Iterating over the mappings of object properties to individuals.
for (
object_property,
list_owl_individuals,
) in object_property_to_individuals.items():
# RETURN: \exists r. {x1,x33, .., x8} => Existential restriction over nominals
yield OWLObjectSomeValuesFrom(
property=object_property, filler=OWLObjectOneOf(list_owl_individuals)
)
owl_class: OWLClass
count: int
for owl_class, count in Counter(
[
type_i
for i in list_owl_individuals
for type_i in self.get_types(ind=i, direct=True)
]
).items():
existential_quantifier = OWLObjectSomeValuesFrom(
property=object_property, filler=owl_class
)
if existential_quantifier in quantifier_gate:
"Do nothing"
else:
##############################################################
# RETURN: \exists r. C => Existential quantifiers over Named OWL Class
##############################################################
quantifier_gate.add(existential_quantifier)
yield existential_quantifier
object_min_cardinality = OWLObjectMinCardinality(
cardinality=count, property=object_property, filler=owl_class
)
if object_min_cardinality in quantifier_gate:
"Do nothing"
else:
##############################################################
# RETURN: ≥ c r. C => OWLObjectMinCardinality over Named OWL Class
##############################################################
quantifier_gate.add(object_min_cardinality)
yield object_min_cardinality
# Iterating over the mappings of data properties to individuals.
for data_property, list_owl_literal in data_property_to_individuals.items():
##############################################################
# RETURN: \exists r. {literal, ..., another literal} => Existential quantifiers over Named OWL Class
##############################################################
# if list_owl_literal is {True, False) doesn't really make sense OWLDataSomeValuesFrom
# Perhaps, if
yield OWLDataSomeValuesFrom(
property=data_property, filler=OWLDataOneOf(list_owl_literal)
)
[docs]
def abox(self, individual: OWLNamedIndividual, mode: str = "native"):
"""
Get all axioms of a given individual being a subject entity
Args:
individual (OWLNamedIndividual): An individual
mode (str): The return format.
1) 'native' -> returns triples as tuples of owlapy objects,
2) 'iri' -> returns triples as tuples of IRIs as string,
3) 'axiom' -> triples are represented by owlapy axioms.
4) 'expression' -> unique owl class expressions based on (1).
Returns: Iterable of tuples or owlapy axiom, depending on the mode.
"""
assert mode in [
"native",
"iri",
"axiom",
"expression",
], "Valid modes are: 'native', 'iri' or 'axiom', 'expression'"
# TODO: AB: We should probably remove "native" mode because it does not make sense since abox method is supposed
# to return abox axioms and axioms in owlapy are represented by an object of type "OWLAxiom", in
# other words we should keep only the "axiom" mode. The user can get the entities from the axiom
# object if he wants to do any other operations with them.
if mode == "native":
yield from self._abox(str_iri=individual.str)
elif mode == "expression":
yield from self.__abox_expression(individual)
elif mode == "axiom":
raise NotImplementedError("Axioms should be checked.")
[docs]
def tbox(self, entities: Union[Iterable[OWLClass], Iterable[OWLDataProperty], Iterable[OWLObjectProperty], OWLClass,
OWLDataProperty, OWLObjectProperty, None] = None, mode='native'):
raise NotImplementedError()
[docs]
def triples(self, mode=None):
raise NotImplementedError()
[docs]
def are_owl_concept_disjoint(self, c: OWLClass, cc: OWLClass) -> bool:
if cc in self.reasoner.disjoint_classes(c):
return True
return False
[docs]
def get_object_properties(self) -> Iterable[OWLObjectProperty]:
yield from self.ontology.object_properties_in_signature()
[docs]
def get_data_properties(self, ranges: Union[OWLDatatype, Iterable[OWLDatatype]] = None) \
-> Iterable[OWLDataProperty]:
if ranges is None:
yield from self.ontology.data_properties_in_signature()
else:
def get_properties_from_xsd_range(r: OWLDatatype):
query = (f"{rdf_prefix}\n{rdfs_prefix}\n{xsd_prefix}SELECT DISTINCT ?x " +
f"WHERE {{?x rdfs:range xsd:{r.iri.reminder}}}")
for binding in self.query(query).json()["results"]["bindings"]:
yield OWLDataProperty(binding["x"]["value"])
if isinstance(ranges, OWLDatatype):
yield from get_properties_from_xsd_range(ranges)
else:
for rng in ranges:
yield from get_properties_from_xsd_range(rng)
[docs]
def get_concepts(self) -> Iterable[OWLClass]:
yield from self.ontology.classes_in_signature()
[docs]
def get_boolean_data_properties(self) -> Iterable[OWLDataProperty]:
yield from self.get_data_properties(BooleanOWLDatatype)
[docs]
def get_double_data_properties(self):
yield from self.get_data_properties(DoubleOWLDatatype)
[docs]
def get_values_of_double_data_property(self, prop: OWLDataProperty):
query = f"{rdf_prefix}\n{rdfs_prefix}\n{xsd_prefix}SELECT DISTINCT ?x WHERE {{?z <{prop.str}> ?x}}"
for binding in self.query(query).json()["results"]["bindings"]:
yield OWLLiteral(value=float(binding["x"]["value"]))
[docs]
def individuals(self, concept: Optional[OWLClassExpression] = None, named_individuals: bool = False) \
-> Iterable[OWLNamedIndividual]:
"""Given an OWL class expression, retrieve all individuals belonging to it.
Args:
concept: Class expression of which to list individuals.
named_individuals: flag for returning only owl named individuals in the SPARQL mapping
Returns:
Generator of individuals belonging to the given class.
"""
if concept is None or concept.is_owl_thing():
yield from self.ontology.individuals_in_signature()
else:
# yield from self.reasoner.instances(concept, named_individuals=named_individuals)
yield from self.reasoner.instances(concept)
# def get_types(self, ind: OWLNamedIndividual, direct: True) -> Generator[OWLClass, None, None]:
# TODO AB: <<TO BE DECIDED>> this or the implementation down below
# TODO AB: Why the return type is Generator[OWLClass, None, None]? It does not adhere to get_types of KnowledgeBase
# if not direct:
# raise NotImplementedError("Inferring indirect types not available")
# query = f"""SELECT DISTINCT ?x WHERE {{ <{ind.str}> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> ?x }}"""
# for binding in self.query(query).json()["results"]["bindings"]:
# yield OWLClass(binding["x"]["value"])
[docs]
def get_types(self, ind: OWLNamedIndividual, direct: True) -> Iterable[OWLClass]:
return self.reasoner.types(ind, direct)
# def get_all_sub_concepts(self, concept: OWLClass, direct=True):
# TODO AB: <<TO BE DECIDED>> this or the implementation down below
# TODO AB: Why do we use 'rdf' and not 'rdfs' when direct=False?
# assert isinstance(concept, OWLClass)
# str_named_concept = f"<{concept.str}>"
# if direct:
# query = f"""{rdfs_prefix} SELECT ?x WHERE {{ ?x rdfs:subClassOf* {str_named_concept}. }} """
# else:
# query = f"""{rdf_prefix} SELECT ?x WHERE {{ ?x rdf:subClassOf {str_named_concept}. }} """
# for str_iri in self.query(query):
# yield OWLClass(str_iri)
[docs]
def get_all_sub_concepts(self, concept: OWLClassExpression, direct=False) -> Iterable[OWLClassExpression]:
assert isinstance(concept, OWLClass)
yield from self.reasoner.sub_classes(concept, direct)
[docs]
def classes_in_signature(self):
yield from self.ontology.classes_in_signature()
[docs]
def get_direct_parents(self, concept: OWLClassExpression) -> Iterable[OWLClass]:
assert isinstance(concept, OWLClass)
yield from self.reasoner.super_classes(concept, direct=True)
[docs]
def get_direct_sub_concepts(self, concept: OWLClass) -> Iterable[OWLClass]:
assert isinstance(concept, OWLClass)
yield from self.reasoner.sub_classes(concept, direct=True)
[docs]
def get_all_direct_sub_concepts(self, concept: OWLClassExpression) -> Iterable[OWLClassExpression]:
assert isinstance(concept, OWLClass)
yield from self.reasoner.sub_classes(concept, direct=True)
@property
def concepts(self) -> Iterable[OWLClass]:
yield from self.ontology.classes_in_signature()
[docs]
def contains_class(self, concept: OWLClassExpression) -> bool:
assert isinstance(concept, OWLClass)
return concept in self.ontology.classes_in_signature()
@property
def 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()
[docs]
def individuals_count(self, concept: Optional[OWLClassExpression] = None) -> int:
return len(set(self.individuals(concept)))
[docs]
def individuals_set(self,
arg: Union[Iterable[OWLNamedIndividual], OWLNamedIndividual, OWLClassExpression]) -> FrozenSet:
if isinstance(arg, OWLClassExpression):
return frozenset(self.individuals(arg))
elif isinstance(arg, OWLNamedIndividual):
return frozenset({arg})
else:
return frozenset(arg)
[docs]
def most_general_object_properties(
self, *, domain: OWLClassExpression, inverse: bool = False) -> Iterable[OWLObjectProperty]:
assert isinstance(domain, OWLClassExpression)
# TODO AB: Implementation copied from KnowledgeBase but is unclear what this method actually does.
func: Callable
func = (self.get_object_property_ranges if inverse else self.get_object_property_domains)
# TODO AB: <<REVIEW>> There is a contradiction in the implementation below because if domain is owl:thing then,
# the property is returned, meaning that the domain of the property is a subclass of the 'domain'
# argument. On the other side if set of individuals covered by the 'domain' argument is a subset
# of the set of individuals covered by the property's domain then the property is returned. That means
# that the 'domain' argument is a subclass of the property's domain, which contradict the first
# condition.
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
[docs]
def data_properties_for_domain(self, domain: OWLClassExpression, data_properties: Iterable[OWLDataProperty]) \
-> Iterable[OWLDataProperty]:
# TODO AB: <<REVIEW>> Its unclear what this method is supposed to do but by the name I can say that it is
# supposed to return the data properties from the given collection of data properties that have the
# specified 'domain'. However old implementation is commented below and is similar to the one in
# method 'most_general_object_properties' which is contradicting.
assert isinstance(domain, OWLClassExpression)
sub_domains = self.reasoner.sub_classes(domain)
for dp in data_properties:
dp_domains = self.get_data_property_domains(dp)
for d in dp_domains:
if d == domain or d in sub_domains:
yield dp
# inds_domain = self.individuals_set(domain)
# for prop in data_properties:
# if domain.is_owl_thing() or inds_domain <= self.individuals_set(next(self.get_data_property_domains(prop))):
# yield prop
[docs]
def most_general_classes(self) -> Iterable[OWLClass]:
"""At least it has single subclass and there is no superclass"""
query = f"""{rdf_prefix}{rdfs_prefix}{owl_prefix} SELECT ?x WHERE {{
?concept rdf:type owl:Class .
FILTER EXISTS {{ ?x rdfs:subClassOf ?z . }}
FILTER NOT EXISTS {{ ?y rdfs:subClassOf ?x . }}
}}
"""
for binding in self.query(query).json()["results"]["bindings"]:
yield OWLClass(binding["x"]["value"])
[docs]
def least_general_named_concepts(self) -> Generator[OWLClass, None, None]:
"""At least it has single superclass and there is no subclass"""
query = f"""{rdf_prefix}{rdfs_prefix}{owl_prefix} SELECT ?concept WHERE {{
?concept rdf:type owl:Class .
FILTER EXISTS {{ ?concept rdfs:subClassOf ?x . }}
FILTER NOT EXISTS {{ ?y rdfs:subClassOf ?concept . }}
}}"""
for binding in self.query(query).json()["results"]["bindings"]:
yield OWLClass(binding["concept"]["value"])
[docs]
def get_object_property_domains(self, prop: OWLObjectProperty, direct=True) -> Iterable[OWLClassExpression]:
yield from self.reasoner.object_property_domains(prop, direct)
[docs]
def get_object_property_ranges(self, prop: OWLObjectProperty, direct=True) -> Iterable[OWLClassExpression]:
yield from self.reasoner.object_property_ranges(prop, direct)
[docs]
def get_data_property_domains(self, prop: OWLDataProperty, direct=True) -> Iterable[OWLClassExpression]:
yield from self.reasoner.data_property_domains(prop, direct)
[docs]
def get_data_property_ranges(self, prop: OWLDataProperty, direct=True) -> Iterable[OWLClassExpression]:
yield from self.reasoner.data_property_ranges(prop, direct)
[docs]
def most_general_data_properties(self, *, domain: OWLClassExpression) -> Iterable[OWLDataProperty]:
yield from self.data_properties_for_domain(domain, self.get_data_properties())
[docs]
def most_general_boolean_data_properties(self, *, domain: OWLClassExpression) -> Iterable[OWLDataProperty]:
yield from self.data_properties_for_domain(domain, self.get_boolean_data_properties())
[docs]
def most_general_numeric_data_properties(self, *, domain: OWLClassExpression) -> Iterable[OWLDataProperty]:
yield from self.data_properties_for_domain(domain, self.get_numeric_data_properties())
[docs]
def most_general_time_data_properties(self, *, domain: OWLClassExpression) -> Iterable[OWLDataProperty]:
yield from self.data_properties_for_domain(domain, self.get_time_data_properties())
[docs]
def most_general_existential_restrictions(self, *,
domain: OWLClassExpression, filler: Optional[OWLClassExpression] = None) \
-> Iterable[OWLObjectSomeValuesFrom]:
if filler is None:
filler = OWLThing
assert isinstance(filler, OWLClassExpression)
for prop in self.most_general_object_properties(domain=domain):
yield OWLObjectSomeValuesFrom(property=prop, filler=filler)
[docs]
def most_general_universal_restrictions(self, *,
domain: OWLClassExpression, filler: Optional[OWLClassExpression] = None) \
-> Iterable[OWLObjectAllValuesFrom]:
if filler is None:
filler = OWLThing
assert isinstance(filler, OWLClassExpression)
for prop in self.most_general_object_properties(domain=domain):
yield OWLObjectAllValuesFrom(property=prop, filler=filler)
[docs]
def most_general_existential_restrictions_inverse(self, *,
domain: OWLClassExpression,
filler: Optional[OWLClassExpression] = None) \
-> Iterable[OWLObjectSomeValuesFrom]:
if filler is None:
filler = OWLThing
assert isinstance(filler, OWLClassExpression)
for prop in self.most_general_object_properties(domain=domain, inverse=True):
yield OWLObjectSomeValuesFrom(property=prop.get_inverse_property(), filler=filler)
[docs]
def most_general_universal_restrictions_inverse(self, *,
domain: OWLClassExpression,
filler: Optional[OWLClassExpression] = None) \
-> Iterable[OWLObjectAllValuesFrom]:
if filler is None:
filler = OWLThing
assert isinstance(filler, OWLClassExpression)
for prop in self.most_general_object_properties(domain=domain, inverse=True):
yield OWLObjectAllValuesFrom(property=prop.get_inverse_property(), filler=filler)
[docs]
def get_numeric_data_properties(self) -> Iterable[OWLDataProperty]:
yield from self.get_data_properties(NUMERIC_DATATYPES)
[docs]
def get_time_data_properties(self) -> Iterable[OWLDataProperty]:
"""Get all time data properties of this concept generator.
Returns:
Time data properties.
"""
yield from self.get_data_properties(TIME_DATATYPES)
[docs]
def get_object_properties_for_ind(self, ind: OWLNamedIndividual, direct: bool = True) \
-> Iterable[OWLObjectProperty]:
properties = set(self.get_object_properties())
yield from (pe for pe in self.reasoner.ind_object_properties(ind, direct) if pe in properties)
[docs]
def get_data_properties_for_ind(self, ind: OWLNamedIndividual, direct: bool = True) -> Iterable[OWLDataProperty]:
properties = set(self.get_data_properties())
yield from (pe for pe in self.reasoner.ind_data_properties(ind, direct) if pe in properties)
[docs]
def get_object_property_values(self, ind: OWLNamedIndividual,
property_: OWLObjectPropertyExpression,
direct: bool = True) -> Iterable[OWLNamedIndividual]:
yield from self.reasoner.object_property_values(ind, property_, direct)
[docs]
def get_data_property_values(self, ind: OWLNamedIndividual,
property_: OWLDataPropertyExpression,
direct: bool = True) -> Iterable[OWLLiteral]:
yield from self.reasoner.data_property_values(ind, property_, direct)