"""Format converter."""
from collections import defaultdict
from contextlib import contextmanager
from functools import singledispatchmethod
from types import MappingProxyType
from typing import Set, List, Dict, Optional, Iterable
from rdflib.plugins.sparql.parser import parseQuery
from owlapy.class_expression import OWLObjectHasValue, OWLObjectOneOf, OWLDatatypeRestriction, OWLDataMinCardinality, \
OWLDataMaxCardinality, OWLDataExactCardinality, OWLClass, OWLClassExpression, OWLObjectIntersectionOf, \
OWLObjectUnionOf, OWLObjectComplementOf, OWLObjectSomeValuesFrom, OWLObjectAllValuesFrom, \
OWLObjectCardinalityRestriction, OWLObjectMinCardinality, OWLObjectMaxCardinality, OWLObjectExactCardinality, \
OWLDataCardinalityRestriction, OWLObjectHasSelf, OWLDataSomeValuesFrom, OWLDataAllValuesFrom, OWLDataHasValue, \
OWLDataOneOf
from owlapy.owl_individual import OWLNamedIndividual
from owlapy.owl_literal import OWLLiteral, TopOWLDatatype
from owlapy.owl_property import OWLObjectProperty, OWLDataProperty
from owlapy.owl_object import OWLEntity
from owlapy.owl_datatype import OWLDatatype
from owlapy.vocab import OWLFacet, OWLRDFVocabulary
_Variable_facet_comp = MappingProxyType({
OWLFacet.MIN_INCLUSIVE: ">=",
OWLFacet.MIN_EXCLUSIVE: ">",
OWLFacet.MAX_INCLUSIVE: "<=",
OWLFacet.MAX_EXCLUSIVE: "<"
})
[docs]
def peek(x):
"""Peek the last element of an array.
Returns:
The last element arr[-1].
"""
return x[-1]
[docs]
class VariablesMapping:
"""Helper class for owl-to-sparql conversion."""
__slots__ = 'class_cnt', 'prop_cnt', 'ind_cnt', 'dict'
def __init__(self):
self.class_cnt = 0
self.prop_cnt = 0
self.ind_cnt = 0
self.dict = dict()
[docs]
def get_variable(self, e: OWLEntity) -> str:
if e in self.dict:
return self.dict[e]
if isinstance(e, OWLClass):
self.class_cnt += 1
var = f"?cls_{self.class_cnt}"
elif isinstance(e, OWLObjectProperty) or isinstance(e, OWLDataProperty):
self.prop_cnt += 1
var = f"?p_{self.prop_cnt}"
elif isinstance(e, OWLNamedIndividual):
self.ind_cnt += 1
var = f"?ind_{self.ind_cnt}"
else:
raise ValueError(e)
self.dict[e] = var
return var
[docs]
def new_individual_variable(self) -> str:
self.ind_cnt += 1
return f"?s_{self.ind_cnt}"
[docs]
def new_property_variable(self) -> str:
self.prop_cnt += 1
return f"?p_{self.prop_cnt}"
[docs]
def __contains__(self, item: OWLEntity) -> bool:
return item in self.dict
[docs]
def __getitem__(self, item: OWLEntity) -> str:
return self.dict[item]
[docs]
class Owl2SparqlConverter:
"""Convert owl (owlapy model class expressions) to SPARQL."""
__slots__ = 'ce', 'sparql', 'variables', 'parent', 'parent_var', 'properties', 'variable_entities', 'cnt', \
'mapping', 'grouping_vars', 'having_conditions', 'for_all_de_morgan', 'named_individuals', '_intersection'
# @TODO:CD: We need to document this class. The computation behind the mapping is not clear.
ce: OWLClassExpression
sparql: List[str]
variables: List[str]
parent: List[OWLClassExpression]
parent_var: List[str]
variable_entities: Set[OWLEntity]
properties: Dict[int, List[OWLEntity]]
_intersection: Dict[int, bool]
mapping: VariablesMapping
grouping_vars: Dict[OWLClassExpression, Set[str]]
having_conditions: Dict[OWLClassExpression, Set[str]]
cnt: int
for_all_de_morgan: bool
named_individuals: bool
[docs]
def convert(self, root_variable: str,
ce: OWLClassExpression,
for_all_de_morgan: bool = True,
named_individuals: bool = False):
"""Used to convert owl class expression to SPARQL syntax.
Args:
root_variable (str): Root variable name that will be used in SPARQL query.
ce (OWLClassExpression): The owl class expression to convert.
named_individuals (bool): If 'True' return only entities that are instances of owl:NamedIndividual.
Returns:
list[str]: The SPARQL query.
"""
self.ce = ce
self.sparql = []
self.variables = []
self.parent = []
self.parent_var = []
self.properties = defaultdict(list)
self.variable_entities = set()
self._intersection = defaultdict(bool)
self.cnt = 0
self.mapping = VariablesMapping()
self.grouping_vars = defaultdict(set)
self.having_conditions = defaultdict(set)
self.for_all_de_morgan = for_all_de_morgan
self.named_individuals = named_individuals
# # if named_individuals is True, we return only entities that are instances of owl:NamedIndividual
# if named_individuals:
# self.append_triple(root_variable, 'a', f"<{OWLRDFVocabulary.OWL_NAMED_INDIVIDUAL.as_str()}>")
with self.stack_variable(root_variable):
with self.stack_parent(ce):
self.process(ce)
return self.sparql
@property
def modal_depth(self):
return len(self.variables)
[docs]
@singledispatchmethod
def render(self, e):
raise NotImplementedError(e)
@render.register
def _(self, lit: OWLLiteral):
return f'"{lit.get_literal()}"^^<{lit.get_datatype().to_string_id()}>'
@render.register
def _(self, e: OWLEntity):
if e in self.variable_entities:
s = self.mapping.get_variable(e)
else:
s = f"<{e.to_string_id()}>"
if isinstance(e, OWLObjectProperty):
self.properties[self.modal_depth].append(e)
return s
def _maybe_quote(self, e):
assert isinstance(e, str)
if e.startswith("?"):
return e
else:
return f"<{e}>"
def _maybe_quote_p(self, p):
if isinstance(p, str):
if p.startswith("?") or p == "a" or p.startswith("<"):
return p
else:
return f"<{p}>"
else:
return self.render(p)
def _maybe_render(self, o):
if isinstance(o, str):
return o
else:
return self.render(o)
[docs]
@contextmanager
def stack_variable(self, var):
self.variables.append(var)
try:
yield
finally:
self.variables.pop()
[docs]
@contextmanager
def stack_parent(self, parent: OWLClassExpression):
self.parent.append(parent)
self.parent_var.append(self.current_variable)
try:
yield
finally:
self.parent.pop()
self.parent_var.pop()
@property
def current_variable(self):
return peek(self.variables)
# this method is responsible for translating class expressions to SPARQL queries
# the decorator "@singledispatchmethod" denotes that the method is overload
# each overload of the method is responsible for processing a different type of class expressions (e.g., ⊔ or ⊓)
[docs]
@singledispatchmethod
def process(self, ce: OWLClassExpression):
raise NotImplementedError(f"We cannot create SPARQL query based on the following owl class {ce}")
# an overload of process function
# this overload is responsible for handling single concepts (e.g., Brother)
# general case: C
# this is the final step of the recursion
@process.register
def _(self, ce: OWLClass):
if self.ce == ce or not ce.is_owl_thing():
self.append_triple(self.current_variable, "a", self.render(ce))
# old_var = self.current_variable
# new_var = self.mapping.new_individual_variable()
# with self.stack_variable(new_var):
# self.append_triple(old_var, "a", new_var)
# self.append_triple(new_var, "<http://www.w3.org/2000/01/rdf-schema#subClassOf>*", self.render(ce))
elif ce.is_owl_thing():
self.append_triple(self.current_variable, "a", "<http://www.w3.org/2002/07/owl#Thing>")
# an overload of process function
# this overload is responsible for handling intersections of concepts (e.g., Brother ⊓ Father)
# general case: C1 ⊓ ... ⊓ Cn
@process.register
def _(self, ce: OWLObjectIntersectionOf):
# we iterate over the concepts that appear in the intersection
for op in ce.operands():
self.process(op)
# an overload of process function
# this overload is responsible for handling unions of concepts (e.g., Brother ⊔ Sister)
# general case: C1 ⊔ ... ⊔ Cn
@process.register
def _(self, ce: OWLObjectUnionOf):
first = True
# we iterate over the concepts that appear in the union
for op in ce.operands():
# SPARQL's UNION comes after the first concept
if first:
first = False
else:
self.append(" UNION ")
self.append("{ ")
with self.stack_parent(op):
self.process(op)
self.append(" }")
# an overload of process function
# this overload is responsible for handling complements of concepts (e.g., ¬Brother)
# general case: ¬C
@process.register
def _(self, ce: OWLObjectComplementOf):
subject = self.current_variable
# the conversion was trying here to optimize the query
# but the proposed optimization alters the semantics of some queries
# example: ( A ⊓ ( B ⊔ ( ¬C ) ) )
# with the proposed optimization, the group graph pattern for (¬C) will be { FILTER NOT EXISTS { ?x a C } }
# however, the expected pattern is { ?x ?p ?o . FILTER NOT EXISTS { ?x a C } }
# the exclusion of "?x ?p ?o" results in the group graph pattern to just return true or false (not bindings)
# as a result, we need to comment out the if-clause of the following line
# if not self.in_intersection and self.modal_depth == 1:
# if namedIndividual is set to True, do not use variables --> restrict the subject to instances of NamedIndividual
if self.named_individuals:
self.append_triple(subject, "a", f"<{OWLRDFVocabulary.OWL_NAMED_INDIVIDUAL.as_str()}>")
else:
self.append_triple(subject, self.mapping.new_individual_variable(), self.mapping.new_individual_variable())
self.append("FILTER NOT EXISTS { ")
# process the concept after the ¬
self.process(ce.get_operand())
self.append(" }")
# an overload of process function
# this overload is responsible for handling the exists operator (e.g., ∃hasChild.Male)
# general case: ∃r.C
@process.register
def _(self, ce: OWLObjectSomeValuesFrom):
object_variable = self.mapping.new_individual_variable()
# property expression holds the role of the class expression (hasChild in our example)
property_expression = ce.get_property()
if property_expression.is_anonymous():
# property expression is inverse of a property
self.append_triple(object_variable, property_expression.get_named_property(), self.current_variable)
else:
self.append_triple(self.current_variable, property_expression.get_named_property(), object_variable)
# filler holds the concept of the expression (Male in our example) and is processed recursively
filler = ce.get_filler()
with self.stack_variable(object_variable):
self.process(filler)
@process.register
def _(self, ce: OWLObjectAllValuesFrom):
if self.for_all_de_morgan is True:
self.forAllDeMorgan(ce)
else:
self.forAll(ce)
# an overload of process function
# this overload is responsible for handling the forAll operator (e.g., ∀hasChild.Male)
# general case: ∀r.C
[docs]
def forAll(self, ce: OWLObjectAllValuesFrom):
subject = self.current_variable
object_variable = self.mapping.new_individual_variable()
# property expression holds the role of the class expression (hasChild in our example)
property_expression = ce.get_property()
predicate = property_expression.get_named_property()
# filler holds the concept of the expression (Male in our example) and is processed recursively
filler = ce.get_filler()
self.append("{")
if property_expression.is_anonymous():
# property expression is inverse of a property
self.append_triple(object_variable, predicate, self.current_variable)
else:
self.append_triple(self.current_variable, predicate, object_variable)
# restrict filler
var = self.mapping.new_individual_variable()
cnt_var1 = self.new_count_var()
# the count needs to use distinct
self.append(f"{{ SELECT {subject} ( COUNT( DISTINCT {var} ) AS {cnt_var1} ) WHERE {{ ")
self.append_triple(subject, predicate, var)
# here, we recursively process the filler (Male in our example)
with self.stack_variable(var):
self.process(filler)
self.append(f" }} GROUP BY {subject} }}")
var = self.mapping.new_individual_variable()
cnt_var2 = self.new_count_var()
# the count needs to use distinct
self.append(f"{{ SELECT {subject} ( COUNT( DISTINCT {var} ) AS {cnt_var2} ) WHERE {{ ")
self.append_triple(subject, predicate, var)
self.append(f" }} GROUP BY {subject} }}")
self.append(f" FILTER( {cnt_var1} = {cnt_var2} )")
self.append("} UNION { ")
# here, the second group graph pattern starts
# the second group graph pattern returns all those entities that do not appear in a triple with the property
self.append_triple(subject, self.mapping.new_individual_variable(), self.mapping.new_individual_variable())
self.append("FILTER NOT EXISTS { ")
if property_expression.is_anonymous():
# property expression is inverse of a property
self.append_triple(self.mapping.new_individual_variable(), predicate, self.current_variable)
else:
self.append_triple(self.current_variable, predicate, self.mapping.new_individual_variable())
self.append(" } }")
# an overload of process function
# this overload is responsible for handling the forAll operator but as if the DeMorgan law was applied
# (e.g., ∀hasChild.Male == ¬(∃hasChild.¬Male))
# general case: ∀r.C == ¬(∃r.¬C)
[docs]
def forAllDeMorgan(self, ce: OWLObjectAllValuesFrom):
subject = self.current_variable
# here, we need to apply the complement rule twice
# the first filter not exists covers the outer ¬
self.append_triple(subject, self.mapping.new_individual_variable(), self.mapping.new_individual_variable())
self.append("FILTER NOT EXISTS { ")
object_variable = self.mapping.new_individual_variable()
# property expression holds the role of the class expression (hasChild in our example)
property_expression = ce.get_property()
if property_expression.is_anonymous():
# property expression is inverse of a property
self.append_triple(object_variable, property_expression.get_named_property(), self.current_variable)
else:
self.append_triple(self.current_variable, property_expression.get_named_property(), object_variable)
# the second filter not exists covers the inner ¬
# filler holds the concept of the expression (Male in our example) and is processed recursively
self.append("FILTER NOT EXISTS { ")
filler = ce.get_filler()
with self.stack_variable(object_variable):
self.process(filler)
self.append(" }")
self.append(" }")
# an overload of process function
# this overload is responsible for handling the exists operator combined with an individual (e.g., ∃hasChild.{john})
# general case: ∃r.{a}
@process.register
def _(self, ce: OWLObjectHasValue):
property_expression = ce.get_property()
value = ce.get_filler()
# we ensure that the value is an individual
assert isinstance(value, OWLNamedIndividual)
if property_expression.is_anonymous():
self.append_triple(value.to_string_id(), property_expression.get_named_property(), self.current_variable)
else:
self.append_triple(self.current_variable, property_expression.get_named_property(), value)
# an overload of process function
# this overload is responsible for handling the exists operator combined with an individual(e.g., >=3 hasChild.Male)
# general case: \theta n r.C
@process.register
def _(self, ce: OWLObjectCardinalityRestriction):
subject_variable = self.current_variable
object_variable = self.mapping.new_individual_variable()
property_expression = ce.get_property()
cardinality = ce.get_cardinality()
if isinstance(ce, OWLObjectMinCardinality):
comparator = ">="
elif isinstance(ce, OWLObjectMaxCardinality):
comparator = "<="
elif isinstance(ce, OWLObjectExactCardinality):
comparator = "="
else:
raise ValueError(ce)
# if the comparator is ≤ or the cardinality is 0, we need an additional group graph pattern
# the additional group graph pattern will take care the cases where an individual is not associated with the
# property expression
if comparator == "<=" or cardinality == 0:
self.append("{")
self.append(f"{{ SELECT {subject_variable} WHERE {{ ")
if property_expression.is_anonymous():
# property expression is inverse of a property
self.append_triple(object_variable, property_expression.get_named_property(), subject_variable)
else:
self.append_triple(subject_variable, property_expression.get_named_property(), object_variable)
filler = ce.get_filler()
with self.stack_variable(object_variable):
self.process(filler)
self.append(f" }} GROUP BY {subject_variable}"
f" HAVING ( COUNT ( {object_variable} ) {comparator} {cardinality} ) }}")
# here, the second group graph pattern starts
if comparator == "<=" or cardinality == 0:
self.append("} UNION {")
self.append_triple(subject_variable, self.mapping.new_individual_variable(),
self.mapping.new_individual_variable())
self.append("FILTER NOT EXISTS { ")
object_variable = self.mapping.new_individual_variable()
if property_expression.is_anonymous():
# property expression is inverse of a property
self.append_triple(object_variable, property_expression.get_named_property(), self.current_variable)
else:
self.append_triple(self.current_variable, property_expression.get_named_property(), object_variable)
with self.stack_variable(object_variable):
self.process(filler)
self.append(" } }")
@process.register
def _(self, ce: OWLDataCardinalityRestriction):
subject_variable = self.current_variable
object_variable = self.mapping.new_individual_variable()
property_expression = ce.get_property()
assert isinstance(property_expression, OWLDataProperty)
cardinality = ce.get_cardinality()
if isinstance(ce, OWLDataMinCardinality):
comparator = ">="
elif isinstance(ce, OWLDataMaxCardinality):
comparator = "<="
elif isinstance(ce, OWLDataExactCardinality):
comparator = "="
else:
raise ValueError(ce)
self.append(f"{{ SELECT {subject_variable} WHERE {{ ")
self.append_triple(subject_variable, property_expression, object_variable)
filler = ce.get_filler()
with self.stack_variable(object_variable):
self.process(filler)
self.append(f" }} GROUP BY {subject_variable}"
f" HAVING ( COUNT ( {object_variable} ) {comparator} {cardinality} ) }}")
# an overload of process function
# this overload is responsible for handling the exists operator combined with SELF
# general case: ∃r.SELF
@process.register
def _(self, ce: OWLObjectHasSelf):
subject = self.current_variable
property = ce.get_property()
self.append_triple(subject, property.get_named_property(), subject)
# an overload of process function
# this overload is responsible for handling the one of case (e.g., { john, jane }
# general case: { a1, ..., an }
@process.register
def _(self, ce: OWLObjectOneOf):
subject = self.current_variable
if self.modal_depth == 1:
self.append_triple(subject, "?p", "?o")
self.append(f" FILTER ( {subject} IN ( ")
first = True
for ind in ce.individuals():
if first:
first = False
else:
self.append(",")
assert isinstance(ind, OWLNamedIndividual)
self.append(f"<{ind.to_string_id()}>")
self.append(" ) )")
@process.register
def _(self, ce: OWLDataSomeValuesFrom):
object_variable = self.mapping.new_individual_variable()
property_expression = ce.get_property()
assert isinstance(property_expression, OWLDataProperty)
self.append_triple(self.current_variable, property_expression, object_variable)
filler = ce.get_filler()
with self.stack_variable(object_variable):
self.process(filler)
@process.register
def _(self, ce: OWLDataAllValuesFrom):
subject = self.current_variable
object_variable = self.mapping.new_individual_variable()
property_expression = ce.get_property()
assert isinstance(property_expression, OWLDataProperty)
predicate = property_expression.to_string_id()
filler = ce.get_filler()
self.append_triple(self.current_variable, predicate, object_variable)
var = self.mapping.new_individual_variable()
cnt_var1 = self.new_count_var()
self.append(f"{{ SELECT {subject} ( COUNT( {var} ) AS {cnt_var1} ) WHERE {{ ")
self.append_triple(subject, predicate, var)
with self.stack_variable(var):
self.process(filler)
self.append(f" }} GROUP BY {subject} }}")
var = self.mapping.new_individual_variable()
cnt_var2 = self.new_count_var()
self.append(f"{{ SELECT {subject} ( COUNT( {var} ) AS {cnt_var2} ) WHERE {{ ")
self.append_triple(subject, predicate, var)
self.append(f" }} GROUP BY {subject} }}")
self.append(f" FILTER( {cnt_var1} = {cnt_var2} )")
@process.register
def _(self, ce: OWLDataHasValue):
property_expression = ce.get_property()
value = ce.get_filler()
assert isinstance(value, OWLLiteral)
self.append_triple(self.current_variable, property_expression, value)
@process.register
def _(self, node: OWLDatatype):
if node != TopOWLDatatype:
self.append(f" FILTER ( DATATYPE ( {self.current_variable} = <{node.to_string_id()}> ) ) ")
@process.register
def _(self, node: OWLDataOneOf):
subject = self.current_variable
if self.modal_depth == 1:
self.append_triple(subject, "?p", "?o")
self.append(f" FILTER ( {subject} IN ( ")
first = True
for value in node.values():
if first:
first = False
else:
self.append(",")
if value:
self.append(self.render(value))
self.append(" ) ) ")
@process.register
def _(self, node: OWLDatatypeRestriction):
frs = node.get_facet_restrictions()
for fr in frs:
facet = fr.get_facet()
value = fr.get_facet_value()
if facet in _Variable_facet_comp:
self.append(f' FILTER ( {self.current_variable} {_Variable_facet_comp[facet]}'
f' "{value.get_literal()}"^^<{value.get_datatype().to_string_id()}> ) ')
[docs]
def new_count_var(self) -> str:
self.cnt += 1
return f"?cnt_{self.cnt}"
[docs]
def append_triple(self, subject, predicate, object_):
self.append(self.triple(subject, predicate, object_))
[docs]
def append(self, frag):
self.sparql.append(frag)
[docs]
def triple(self, subject, predicate, object_):
return f"{self._maybe_quote(subject)} {self._maybe_quote_p(predicate)} {self._maybe_render(object_)} . "
[docs]
def as_query(self,
root_variable: str,
ce: OWLClassExpression,
for_all_de_morgan: bool = True,
count: bool = False,
values: Optional[Iterable[OWLNamedIndividual]] = None,
named_individuals: bool = False) -> str:
assert isinstance(ce,OWLClassExpression), f"ce must be an instance of OWLClassExpression. Currently {type(ce)}"
# root variable: the variable that will be projected
# ce: the class expression to be transformed to a SPARQL query
# for_all_de_morgan: true -> ¬(∃r.¬C), false -> (∀r.C)
# count: True, counts the results ; False, projects the individuals
# values: positive or negative examples from a class expression problem
# named_individuals: if set to True, the generated SPARQL query will return only entities that are instances
# of owl:NamedIndividual
qs = ["SELECT"]
tp = self.convert(root_variable, ce, for_all_de_morgan=for_all_de_morgan, named_individuals=named_individuals)
if count:
qs.append(f" ( COUNT ( DISTINCT {root_variable} ) AS ?cnt ) WHERE {{ ")
else:
qs.append(f" DISTINCT {root_variable} WHERE {{ ")
if values is not None and root_variable.startswith("?"):
q = [f"VALUES {root_variable} {{ "]
for x in values:
q.append(f"<{x.to_string_id()}>")
q.append("} . ")
qs.extend(q)
qs.extend(tp)
qs.append(" }")
query = "\n".join(qs)
parseQuery(query)
return query
[docs]
def as_confusion_matrix_query(self,
root_variable: str,
ce: OWLClassExpression,
positive_examples: Iterable[OWLNamedIndividual],
negative_examples: Iterable[OWLNamedIndividual],
for_all_de_morgan: bool = True,
named_individuals: bool = False) -> str:
# get the graph pattern corresponding to the provided class expression (ce)
graph_pattern_str = "".join(self.convert(root_variable,
ce,
for_all_de_morgan=for_all_de_morgan,
named_individuals=named_individuals))
# preparation for the final query
# required to compute false negatives
number_of_positive_examples = 0
# required to compute true negatives
number_of_negative_examples = 0
# string representation of the positive examples (to be passed to the first VALUES clause)
positive_examples_as_str = ""
# iterate over the positive examples
for positive_example in positive_examples:
number_of_positive_examples += 1
positive_examples_as_str += f"<{positive_example.to_string_id()}> "
assert (len(positive_examples_as_str) > 0)
# string representation of the positive examples (to be passed to the first VALUES clause)
negative_examples_as_str = ""
# iterate over the negative examples
for negative_example in negative_examples:
number_of_negative_examples += 1
negative_examples_as_str += f"<{negative_example.to_string_id()}> "
assert(len(negative_examples_as_str) > 0)
# create the sparql query
sparql_str= f"""
PREFIX xsd: <http://www.w3.org/2001/XMLSchema#>
SELECT * WHERE {{
{{
SELECT (COUNT(DISTINCT {root_variable}) as ?tp) (({number_of_positive_examples} - COUNT(DISTINCT {root_variable})) as ?fn) WHERE {{
VALUES {root_variable} {{ {positive_examples_as_str} }}
{graph_pattern_str}
}}
}}
{{
SELECT (COUNT(DISTINCT {root_variable}) as ?fp) (({number_of_negative_examples} - COUNT(DISTINCT {root_variable})) as ?tn) WHERE {{
VALUES {root_variable} {{ {negative_examples_as_str} }}
{graph_pattern_str}
}}
}}
}}
"""
parseQuery(sparql_str)
return sparql_str
converter = Owl2SparqlConverter()
[docs]
def owl_expression_to_sparql(expression: OWLClassExpression = None,
root_variable: str = "?x",
values: Optional[Iterable[OWLNamedIndividual]] = None,
for_all_de_morgan: bool = True,
named_individuals: bool = False) -> str:
"""Convert an OWL Class Expression (https://www.w3.org/TR/owl2-syntax/#Class_Expressions) into a SPARQL query
root variable: the variable that will be projected
expression: the class expression to be transformed to a SPARQL query
values: positive or negative examples from a class expression problem. Unclear
for_all_de_morgan: if set to True, the SPARQL mapping will use the mapping containing the nested FILTER NOT EXISTS
patterns for the universal quantifier (¬(∃r.¬C)), instead of the counting query
named_individuals: if set to True, the generated SPARQL query will return only entities
that are instances of owl:NamedIndividual
"""
assert expression is not None, "expression cannot be None"
return converter.as_query(root_variable, expression, count=False, values=values,
named_individuals=named_individuals, for_all_de_morgan=for_all_de_morgan)
[docs]
def owl_expression_to_sparql_with_confusion_matrix(expression: OWLClassExpression,
positive_examples: Optional[Iterable[OWLNamedIndividual]],
negative_examples: Optional[Iterable[OWLNamedIndividual]],
root_variable: str = "?x",
for_all_de_morgan: bool = True,
named_individuals: bool = False) -> str:
"""Convert an OWL Class Expression (https://www.w3.org/TR/owl2-syntax/#Class_Expressions) into a SPARQL query
root variable: the variable that will be projected
expression: the class expression to be transformed to a SPARQL query
positive_examples: positive examples from a class expression problem
negative_examples: positive examples from a class expression problem
for_all_de_morgan: if set to True, the SPARQL mapping will use the mapping containing the nested FILTER NOT EXISTS
patterns for the universal quantifier (¬(∃r.¬C)), instead of the counting query
named_individuals: if set to True, the generated SPARQL query will return only entities
that are instances of owl:NamedIndividual
"""
assert expression is not None, "expression cannot be None"
assert positive_examples is not None, "positive examples cannot be None"
assert negative_examples is not None, "negative examples cannot be None"
return converter.as_confusion_matrix_query(root_variable,
expression,
positive_examples=positive_examples,
negative_examples=negative_examples,
named_individuals=named_individuals,
for_all_de_morgan=for_all_de_morgan)