from de.tudresden.inf.lat.jcel.ontology.normalization import OntologyNormalizer
from de.tudresden.inf.lat.jcel.ontology.axiom.extension import IntegerOntologyObjectFactoryImpl
from de.tudresden.inf.lat.jcel.owlapi.translator import ReverseAxiomTranslator
from de.tudresden.inf.lat.jcel.owlapi.translator import Translator
from org.semanticweb.owlapi.model.parameters import Imports
from uk.ac.manchester.cs.owl.owlapi import OWLClassImpl, OWLObjectSomeValuesFromImpl, \
OWLObjectIntersectionOfImpl
from org.semanticweb.owlapi.model import OWLAxiom, OWLOntology
from java.util import HashSet
import logging
logging.basicConfig(level=logging.INFO)
from mowl.owlapi import OWLAPIAdapter
[docs]class ELNormalizer():
"""This class wraps the normalization functionality found in the Java library :class:`Jcel`. \
The normalization process transforms an ontology into 7 normal forms in the description \
logic EL language.
"""
def __init__(self):
return
[docs] def normalize(self, ontology):
"""Performs the normalization.
:param ontology: Input ontology
:type ontology: :class:`org.semanticweb.owlapi.model.OWLOntology`
:rtype: Dictionary where the keys are labels for each normal form and the values are a \
list of axioms of each normal form.
"""
# Type check
if not isinstance(ontology, OWLOntology):
raise TypeError(f"Parameter 'ontology' must be of \
type org.semanticweb.owlapi.model.OWLOntology. Found: {type(ontology)}")
# jreasoner = JcelReasoner(ontology, False)
# root_ont = jreasoner.getRootOntology()
ontology = self.preprocess_ontology(ontology)
root_ont = ontology
translator = Translator(ontology.getOWLOntologyManager().getOWLDataFactory(),
IntegerOntologyObjectFactoryImpl())
# translator = jreasoner.getTranslator()
axioms = HashSet()
axioms.addAll(root_ont.getAxioms())
translator.getTranslationRepository().addAxiomEntities(root_ont)
for ont in root_ont.getImportsClosure():
axioms.addAll(ont.getAxioms())
translator.getTranslationRepository().addAxiomEntities(ont)
intAxioms = translator.translateSA(axioms)
normalizer = OntologyNormalizer()
factory = IntegerOntologyObjectFactoryImpl()
normalized_ontology = normalizer.normalize(intAxioms, factory)
self.rTranslator = ReverseAxiomTranslator(translator, ontology)
axioms_dict = self.__revert_translation(normalized_ontology)
return axioms_dict
def __revert_translation(self, normalized_ontology):
axioms_dict = {
"gci0": [], "gci1": [], "gci2": [], "gci3": [], "gci0_bot": [], "gci1_bot": [],
"gci3_bot": []}
for ax in normalized_ontology:
try:
axiom = self.rTranslator.visit(ax)
key, value = process_axiom(axiom)
axioms_dict[key].append(value)
except Exception as e:
logging.info("Reverse translation. Ignoring axiom: %s", ax)
logging.info(e)
return axioms_dict
# TODO: This method is missing unit tests
[docs] def preprocess_ontology(self, ontology):
"""Preprocesses the ontology to remove axioms that are not supported by the normalization \
process.
:param ontology: Input ontology
:type ontology: :class:`org.semanticweb.owlapi.model.OWLOntology`
:rtype: :class:`org.semanticweb.owlapi.model.OWLOntology`
"""
# Type check
if not isinstance(ontology, OWLOntology):
raise TypeError("Parameter 'ontology' must be of \
type org.semanticweb.owlapi.model.OWLOntology")
tbox_axioms = ontology.getTBoxAxioms(Imports.fromBoolean(True))
new_tbox_axioms = HashSet()
for axiom in tbox_axioms:
axiom_as_str = axiom.toString()
if "UnionOf" in axiom_as_str:
continue
elif "MinCardinality" in axiom_as_str:
continue
elif "ComplementOf" in axiom_as_str:
continue
elif "AllValuesFrom" in axiom_as_str:
continue
elif "MaxCardinality" in axiom_as_str:
continue
elif "ExactCardinality" in axiom_as_str:
continue
elif "Annotation" in axiom_as_str:
continue
elif "ObjectHasSelf" in axiom_as_str:
continue
elif "urn:swrl" in axiom_as_str:
continue
elif "EquivalentObjectProperties" in axiom_as_str:
continue
elif "SymmetricObjectProperty" in axiom_as_str:
continue
elif "AsymmetricObjectProperty" in axiom_as_str:
continue
elif "ObjectOneOf" in axiom_as_str:
continue
else:
new_tbox_axioms.add(axiom)
owl_manager = OWLAPIAdapter().owl_manager
new_ontology = owl_manager.createOntology(new_tbox_axioms)
return new_ontology
def process_axiom(axiom: OWLAxiom):
# Type check
if not isinstance(axiom, OWLAxiom):
raise TypeError("Parameter 'axiom' must be of type org.semanticweb.owlapi.model.OWLAxiom")
subclass = axiom.getSubClass()
superclass = axiom.getSuperClass()
if type(subclass) == OWLObjectIntersectionOfImpl:
superclass = superclass.toStringID()
if superclass.contains("owl#Nothing"):
return "gci1_bot", GCI1_BOT(axiom)
return "gci1", GCI1(axiom)
elif type(subclass) == OWLObjectSomeValuesFromImpl:
superclass = superclass.toStringID()
if superclass.contains("owl#Nothing"):
return "gci3_bot", GCI3_BOT(axiom)
return "gci3", GCI3(axiom)
elif type(subclass) == OWLClassImpl:
if type(superclass) == OWLClassImpl:
superclass = superclass.toStringID()
if superclass.contains("owl#Nothing"):
return "gci0_bot", GCI0_BOT(axiom)
return "gci0", GCI0(axiom)
elif type(superclass) == OWLObjectSomeValuesFromImpl:
return "gci2", GCI2(axiom)
else:
logging.info("Superclass type not recognized. Ignoring axiom: %s", axiom)
else:
logging.info("Subclass type not recognized. Ignoring axiom: %s", axiom)
[docs]class GCI():
"""Base class for all GCI types in the :math:`\\mathcal{EL}` language"""
def __init__(self, axiom):
self._axiom = axiom
return
def __eq__(self, other):
return self._axiom.equals(other._axiom)
@property
def owl_subclass(self):
"""Returns the subclass of the GCI
:rtype: :class:`org.semanticweb.owlapi.model.OWLClassExpression`
"""
return self._axiom.getSubClass()
@property
def owl_superclass(self):
"""Returns the superclass of the GCI
:rtype: :class:`org.semanticweb.owlapi.model.OWLClassExpression`
"""
return self._axiom.getSuperClass()
@property
def owl_axiom(self):
"""Returns the axiom of the GCI
:rtype: :class:`org.semanticweb.owlapi.model.OWLAxiom`
"""
return self._axiom
[docs] @staticmethod
def get_entities(gcis):
"""Returns all the classes and object properties that appear in the GCIs
:param gcis: List of GCIs
:type gcis: list
:rtype: tuple(set, set)
"""
classes = set()
object_properties = set()
for gci in gcis:
new_classes, new_obj_props = gci.get_entities()
classes |= new_classes
object_properties |= new_obj_props
return classes, object_properties
[docs]class GCI0(GCI):
"""
GCI of the form :math:`C \\sqsubseteq D`
:param axiom: Axiom of the form :math:`C \\sqsubseteq D`
:type axiom: :class:`org.semanticweb.owlapi.model.OWLAxiom`
"""
def __init__(self, axiom):
super().__init__(axiom)
self._subclass = None
self._superclass = None
@property
def subclass(self):
"""Returns the subclass of the GCI: :math:`C`
:rtype: :class:`org.semanticweb.owlapi.model.OWLClass`
"""
if not self._subclass:
self._subclass = str(self.owl_subclass.toStringID())
return self._subclass
@property
def superclass(self):
""""
Returns the superclass of the GCI: :math:`D` or :math:`\\bot`
:rtype: :class:`org.semanticweb.owlapi.model.OWLClass`
"""
if not self._superclass:
self._superclass = str(self.owl_superclass.toStringID())
return self._superclass
[docs] def get_entities(self):
return set([self.subclass, self.superclass]), set()
[docs]class GCI0_BOT(GCI0):
"""
GCI of the form :math:`C \\sqsubseteq \\bot`
:param axiom: Axiom of the form :math:`C \\sqsubseteq \\bot`
:type axiom: :class:`org.semanticweb.owlapi.model.OWLAxiom`
"""
def __init__(self, axiom):
super().__init__(axiom)
if "owl#Nothing" not in self.superclass:
raise ValueError("Superclass in GCI0_BOT must be the bottom concept.")
[docs]class GCI1(GCI):
"""
GCI of the form :math:`C \\sqcap D \\sqsubseteq E`
:param axiom: Axiom of the form :math:`C \\sqcap D \\sqsubseteq E`
:type axiom: :class:`org.semanticweb.owlapi.model.OWLAxiom`
"""
def __init__(self, axiom):
super().__init__(axiom)
self._left_subclass = None
self._right_subclass = None
self._superclass = None
def _process_left_side(self):
operands = self.owl_subclass.getOperandsAsList()
left_subclass = operands[0].toStringID()
right_subclass = operands[1].toStringID()
self._left_subclass = str(left_subclass)
self._right_subclass = str(right_subclass)
@property
def left_subclass(self):
""""
Returns the left operand of the subclass of the GCI: :math:`C`
:rtype: :class:`org.semanticweb.owlapi.model.OWLClass`
"""
if not self._left_subclass:
self._process_left_side()
return self._left_subclass
@property
def right_subclass(self):
"""
Returns the right operand of the subclass of the GCI: :math:`D`
:rtype: :class:`org.semanticweb.owlapi.model.OWLClass`
"""
if not self._right_subclass:
self._process_left_side()
return self._right_subclass
@property
def superclass(self):
"""
Returns the superclass of the GCI: :math:`E` or :math:`\\bot`
:rtype: :class:`org.semanticweb.owlapi.model.OWLClass`
"""
if not self._superclass:
self._superclass = str(self.owl_superclass.toStringID())
return self._superclass
[docs] def get_entities(self):
return set([self.left_subclass, self.right_subclass, self.superclass]), set()
[docs]class GCI1_BOT(GCI1):
"""
GCI of the form :math:`C \\sqcap D \\sqsubseteq \\bot`
:param axiom: Axiom of the form :math:`C \\sqcap D \\sqsubseteq \\bot`
:type axiom: :class:`org.semanticweb.owlapi.model.OWLAxiom`
"""
def __init__(self, axiom):
super().__init__(axiom)
if "owl#Nothing" not in self.superclass:
raise ValueError("Superclass in GCI1_BOT must be the bottom concept.")
[docs]class GCI2(GCI):
"""
GCI of the form :math:`C \\sqsubseteq \\exists R.D`
:param axiom: Axiom of the form :math:`C \\sqsubseteq \\exists R.D`
:type axiom: :class:`org.semanticweb.owlapi.model.OWLAxiom`
"""
def __init__(self, axiom):
super().__init__(axiom)
self._subclass = None
self._object_property = None
self._filler = None
def _process_right_side(self):
object_property = str(self.owl_superclass.getProperty().toString())
filler = str(self.owl_superclass.getFiller().toStringID())
self._object_property = object_property[1:-1] if object_property.startswith("<") \
else object_property
self._filler = filler
@property
def subclass(self):
"""
Returns the subclass of the GCI: :math:`C`
:rtype: :class:`org.semanticweb.owlapi.model.OWLClass`
"""
if not self._subclass:
self._subclass = str(self.owl_subclass.toStringID())
return self._subclass
@property
def object_property(self):
"""
Returns the object property of the GCI: :math:`R`
:rtype: :class:`org.semanticweb.owlapi.model.OWLObjectProperty`
"""
if not self._object_property:
self._process_right_side()
return self._object_property
@property
def filler(self):
"""
Returns the filler of the GCI: :math:`D`
:rtype: :class:`org.semanticweb.owlapi.model.OWLClass`
"""
if not self._filler:
self._process_right_side()
return self._filler
[docs] def get_entities(self):
return set([self.subclass, self.filler]), set([self.object_property])
[docs]class GCI3(GCI):
"""
GCI of the form :math:`\\exists R.C \\sqsubseteq D`
:param axiom: Axiom of the form :math:`\\exists R.C \\sqsubseteq D`
:type axiom: :class:`org.semanticweb.owlapi.model.OWLAxiom`
"""
def __init__(self, axiom):
super().__init__(axiom)
self._object_property = None
self._filler = None
self._superclass = None
def _process_left_side(self):
object_property = str(self.owl_subclass.getProperty().toString())
filler = str(self.owl_subclass.getFiller().toStringID())
self._object_property = object_property[1:-1] if object_property.startswith("<") \
else object_property
self._filler = filler
@property
def object_property(self):
"""
Returns the object property of the GCI: :math:`R`
:rtype: :class:`org.semanticweb.owlapi.model.OWLObjectProperty`
"""
if not self._object_property:
self._process_left_side()
return self._object_property
@property
def filler(self):
"""
Returns the filler of the GCI: :math:`C`
:rtype: :class:`org.semanticweb.owlapi.model.OWLClass`
"""
if not self._filler:
self._process_left_side()
return self._filler
@property
def superclass(self):
"""
Returns the superclass of the GCI: :math:`D` or :math:`\\bot`
:rtype: :class:`org.semanticweb.owlapi.model.OWLClass`
"""
if not self._superclass:
self._superclass = str(self.owl_superclass.toStringID())
return self._superclass
[docs] def get_entities(self):
return set([self.filler, self.superclass]), set([self.object_property])
[docs]class GCI3_BOT(GCI3):
"""
GCI of the form :math:`\\exists R.C \\sqsubseteq \\bot`
:param axiom: Axiom of the form :math:`\\exists R.C \\sqsubseteq \\bot`
:type axiom: :class:`org.semanticweb.owlapi.model.OWLAxiom`
"""
def __init__(self, axiom):
super().__init__(axiom)
if "owl#Nothing" not in self.superclass:
raise ValueError("Superclass in GCI3_BOT must be the bottom concept.")