# -----------------------------------------------------------------------------
# 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.
# -----------------------------------------------------------------------------
"""A module to generate concepts."""
from typing import Iterable, List, Generator
from owlapy.class_expression import OWLObjectMaxCardinality, OWLObjectMinCardinality, OWLObjectSomeValuesFrom, \
OWLObjectAllValuesFrom, OWLObjectIntersectionOf, OWLObjectUnionOf, OWLThing, OWLNothing, OWLClass, \
OWLClassExpression, OWLObjectComplementOf, OWLObjectExactCardinality, OWLDataAllValuesFrom, OWLDataSomeValuesFrom, \
OWLDataHasValue, OWLObjectHasValue
from owlapy.owl_data_ranges import OWLDataRange
from owlapy.owl_individual import OWLIndividual
from owlapy.owl_literal import OWLLiteral
from owlapy.owl_property import OWLObjectPropertyExpression, OWLDataPropertyExpression
from ontolearn.utils import parametrized_performance_debugger
[docs]
class ConceptGenerator:
"""A class that can generate some sorts of OWL Class Expressions."""
[docs]
@parametrized_performance_debugger()
def negation_from_iterables(self, class_expressions: Iterable[OWLClassExpression]):
"""Negate a sequence of Class Expressions.
Args:
class_expressions: Iterable of class expressions to negate.
Returns:
Negated form of input.
{ x \\| ( x \\equv not s} """
for item in class_expressions:
assert isinstance(item, OWLClassExpression)
yield self.negation(item)
[docs]
@staticmethod
def intersect_from_iterables(a_operands: Iterable[OWLClassExpression], b_operands: Iterable[OWLClassExpression]) \
-> Iterable[OWLObjectIntersectionOf]:
""" Create an intersection of each class expression in a_operands with each class expression in b_operands."""
assert isinstance(a_operands, Generator) is False and isinstance(b_operands, Generator) is False
seen = set()
for i in a_operands:
for j in b_operands:
if (i, j) in seen:
continue
i_and_j = OWLObjectIntersectionOf((i, j))
seen.add((i, j))
seen.add((j, i))
yield i_and_j
[docs]
@staticmethod
def union_from_iterables(a_operands: Iterable[OWLClassExpression],
b_operands: Iterable[OWLClassExpression]) -> Iterable[OWLObjectUnionOf]:
""" Create an union of each class expression in a_operands with each class expression in b_operands."""
assert (isinstance(a_operands, Generator) is False) and (isinstance(b_operands, Generator) is False)
# TODO: if input sizes say 10^4, we can employ multiprocessing
seen = set()
for i in a_operands:
for j in b_operands:
if (i, j) in seen:
continue
i_and_j = OWLObjectUnionOf((i, j))
seen.add((i, j))
seen.add((j, i))
yield i_and_j
# noinspection PyMethodMayBeStatic
[docs]
def intersection(self, ops: Iterable[OWLClassExpression]) -> OWLObjectIntersectionOf:
"""Create intersection of class expression.
Args:
ops: Operands of the intersection.
Returns:
Intersection with all operands (intersections are merged).
"""
# TODO CD: I would rather prefer def intersection(self, a: OWLClassExpression, b: OWLClassExpression). This is
# TODO CD: more advantages as one does not need to create a tuple of a list before intersection two expressions.
operands: List[OWLClassExpression] = []
for c in ops:
if isinstance(c, OWLObjectIntersectionOf):
operands.extend(c.operands())
else:
assert isinstance(c, OWLClassExpression)
operands.append(c)
# operands = _avoid_overly_redundant_operands(operands)
return OWLObjectIntersectionOf(operands)
# noinspection PyMethodMayBeStatic
[docs]
def union(self, ops: Iterable[OWLClassExpression]) -> OWLObjectUnionOf:
"""Create union of class expressions.
Args:
ops: Operands of the union
Returns:
Union with all operands (unions are merged).
"""
operands: List[OWLClassExpression] = []
for c in ops:
if isinstance(c, OWLObjectUnionOf):
operands.extend(c.operands())
else:
assert isinstance(c, OWLClassExpression)
operands.append(c)
# operands = _avoid_overly_redundand_operands(operands)
return OWLObjectUnionOf(operands)
# noinspection PyMethodMayBeStatic
[docs]
def existential_restriction(self, filler: OWLClassExpression, property: OWLObjectPropertyExpression) \
-> OWLObjectSomeValuesFrom:
"""Create existential restriction.
Args:
property: Property.
filler: Filler of the restriction.
Returns:
Existential restriction.
"""
assert isinstance(property, OWLObjectPropertyExpression)
return OWLObjectSomeValuesFrom(property=property, filler=filler)
# noinspection PyMethodMayBeStatic
[docs]
def universal_restriction(self, filler: OWLClassExpression, property: OWLObjectPropertyExpression) \
-> OWLObjectAllValuesFrom:
"""Create universal restriction.
Args:
property: Property.
filler: Filler of the restriction.
Returns:
universal restriction
"""
assert isinstance(property, OWLObjectPropertyExpression)
return OWLObjectAllValuesFrom(property=property, filler=filler)
[docs]
def has_value_restriction(self, individual: OWLIndividual, property: OWLObjectPropertyExpression) \
-> OWLObjectHasValue:
"""Create object has value restriction.
Args:
property: Property.
individual: Individual of the restriction.
Returns:
Object has value restriction.
"""
assert isinstance(property, OWLObjectPropertyExpression)
return OWLObjectHasValue(property=property, individual=individual)
[docs]
def min_cardinality_restriction(self, filler: OWLClassExpression,
property: OWLObjectPropertyExpression, card: int) \
-> OWLObjectMinCardinality:
"""Create min cardinality restriction.
Args:
filler: Filler of the restriction.
property: Property.
card: Cardinality of the restriction.
Returns:
Min cardinality restriction.
"""
assert isinstance(property, OWLObjectPropertyExpression)
return OWLObjectMinCardinality(cardinality=card, property=property, filler=filler)
[docs]
def max_cardinality_restriction(self, filler: OWLClassExpression,
property: OWLObjectPropertyExpression, card: int) \
-> OWLObjectMaxCardinality:
"""Create max cardinality restriction.
Args:
filler: Filler of the restriction.
property: Property.
card: Cardinality of the restriction.
Returns:
Max cardinality restriction.
"""
assert isinstance(property, OWLObjectPropertyExpression)
return OWLObjectMaxCardinality(cardinality=card, property=property, filler=filler)
[docs]
def exact_cardinality_restriction(self, filler: OWLClassExpression,
property: OWLObjectPropertyExpression, card: int) \
-> OWLObjectExactCardinality:
"""Create exact cardinality restriction.
Args:
filler: Filler of the restriction.
property: Property.
card: Cardinality of the restriction.
Returns:
Exact cardinality restriction.
"""
assert isinstance(property, OWLObjectPropertyExpression)
return OWLObjectExactCardinality(cardinality=card, property=property, filler=filler)
[docs]
@staticmethod
def data_existential_restriction(filler: OWLDataRange, property: OWLDataPropertyExpression) \
-> OWLDataSomeValuesFrom:
"""Create data existential restriction.
Args:
filler: Filler of the restriction.
property: Property.
Returns:
Data existential restriction.
"""
assert isinstance(property, OWLDataPropertyExpression)
return OWLDataSomeValuesFrom(property=property, filler=filler)
[docs]
@staticmethod
def data_universal_restriction(filler: OWLDataRange, property: OWLDataPropertyExpression) \
-> OWLDataAllValuesFrom:
"""Create data universal restriction.
Args:
filler: Filler of the restriction.
property: Property.
Returns:
Data universal restriction.
"""
assert isinstance(property, OWLDataPropertyExpression)
return OWLDataAllValuesFrom(property=property, filler=filler)
[docs]
@staticmethod
def data_has_value_restriction(value: OWLLiteral, property: OWLDataPropertyExpression) \
-> OWLDataHasValue:
"""Create data has value restriction.
Args:
value: Value of the restriction.
property: Property.
Returns:
Data has value restriction.
"""
assert isinstance(property, OWLDataPropertyExpression)
return OWLDataHasValue(property=property, value=value)
[docs]
def negation(self, concept: OWLClassExpression) -> OWLClassExpression:
"""Create negation of a concept.
Args:
concept: Class expression.
Returns:
Negation of concept.
"""
if concept.is_owl_thing():
return self.nothing
elif isinstance(concept, OWLObjectComplementOf):
return concept.get_operand()
else:
return concept.get_object_complement_of()
@property
def thing(self) -> OWLClass:
"""OWL Thing."""
return OWLThing
@property
def nothing(self) -> OWLClass:
"""OWL Nothing."""
return OWLNothing