import torch as th
import torch.nn as nn
[docs]
class ELModule(nn.Module):
"""Subclass of :class:`torch.nn.Module` for :math:`\mathcal{EL}` models.
This class provides \
an interface for loss functions of the 7 possible normal forms existing in the \
:math:`\mathcal{EL}` language. In case a negative version of one of the loss function exist, \
it must be placed inside the original loss function and be accesed through the ``neg`` \
parameter. More information of this can be found at :doc:`/embedding_el/index`
Subclasses should override :attr:`neg_capable_gcis` to declare which GCI loss functions
genuinely implement a different code path when ``neg=True``.
"""
#: Set of GCI names for which this module implements a true negative loss (i.e. the loss
#: function behaves differently when called with ``neg=True``). Subclasses must override
#: this to declare their capabilities.
neg_capable_gcis = frozenset()
def __init__(self):
super().__init__()
self.class_embed = None
self.rel_embed = None
self.ind_embed = None
self.gci_names = ["gci0", "gci1", "gci2", "gci3", "gci0_bot", "gci1_bot", "gci3_bot", "class_assertion", "object_property_assertion"]
[docs]
def gci0_loss(self, gci, neg=False):
"""Loss function for GCI0: :math:`C \sqsubseteq D`.
:param gci: Input tensor of shape \(\*,2\) where ``C`` classes will be at ``gci[:,0]`` \
and ``D`` classes will be at ``gci[:,1]``. It is recommended to use the \
:class:`ELDataset <mowl.datasets.el.ELDataset>`.
:type gci: :class:`torch.Tensor`
:param neg: Parameter indicating that the negative version of this loss function must be \
used. Defaults to ``False``.
:type neg: bool, optional.
"""
raise NotImplementedError()
[docs]
def gci1_loss(self, gci, neg=False):
"""Loss function for GCI1: :math:`C_1 \sqcap C_2 \sqsubseteq D`.
:param gci: Input tensor of shape \(\*,3\) where ``C1`` classes will be at \
``gci[:,0]``, ``C2`` classes will be at ``gci[:,1]`` and ``D`` classes will be at \
``gci[:,2]``. It is recommended to use the :class:`ELDataset <mowl.datasets.el.ELDataset>`.
:type gci: :class:`torch.Tensor`
:param neg: Parameter indicating that the negative version of this loss function must be \
used. Defaults to ``False``.
:type neg: bool, optional.
"""
raise NotImplementedError()
[docs]
def gci2_loss(self, gci, neg=False):
"""Loss function for GCI2: :math:`C \sqsubseteq \exists R.D`.
:math:`C \sqsubseteq \exists R. D`.
:param gci: Input tensor of shape \(\*,3\) where ``C`` classes will be at \
``gci[:,0]``, ``R`` object properties will be at ``gci[:,1]`` and ``D`` classes will be \
at ``gci[:,2]``. It is recommended to use the :class:`ELDataset <mowl.datasets.el.ELDataset>`.
:type gci: :class:`torch.Tensor`
:param neg: Parameter indicating that the negative version of this loss function must be \
used. Defaults to ``False``.
:type neg: bool, optional.
"""
raise NotImplementedError()
[docs]
def gci3_loss(self, gci, neg=False):
"""Loss function for GCI3: :math:`\exists R.C \sqsubseteq D`.
:param gci: Input tensor of shape \(\*,3\) where ``R`` object properties will be at \
gci[:,0], ``C`` classes will be at ``gci[:,1]`` and ``D`` classes will be at \
``gci[:,2]``. It is recommended to use the :class:`ELDataset <mowl.datasets.el.ELDataset>`.
:type gci: :class:`torch.Tensor`
:param neg: Parameter indicating that the negative version of this loss function must be \
used. Defaults to ``False``.
:type neg: bool, optional.
"""
raise NotImplementedError()
[docs]
def gci0_bot_loss(self, gci, neg=False):
"""Loss function for GCI0 with bottom concept: :math:`C \sqsubseteq \perp`.
:param gci: Input tensor of shape \(\*,2\) where ``C`` classes will be at ``gci[:,0]`` \
and ``bottom`` classes will be at ``gci[:,1]``. It is recommended to use the \
:class:`ELDataset <mowl.datasets.el.ELDataset>`.
:type gci: :class:`torch.Tensor`
:param neg: Parameter indicating that the negative version of this loss function must be \
used. Defaults to ``False``.
:type neg: bool, optional.
"""
raise NotImplementedError()
[docs]
def gci1_bot_loss(self, gci, neg=False):
"""Loss function for GCI1 with bottom concept: :math:`C_1 \sqcap C_2 \sqsubseteq \perp`.
:param gci: Input tensor of shape \(\*,3\) where ``C1`` classes will be at ``gci[:,0]``, \
``C2`` classes will be at ``gci[:,1] and`` ``bottom`` classes will be at ``gci[:,2]``. It is \
recommended to use the :class:`ELDataset <mowl.datasets.el.ELDataset>`.
:type gci: :class:`torch.Tensor`
:param neg: Parameter indicating that the negative version of this loss function must be \
used. Defaults to ``False``.
:type neg: bool, optional.
"""
raise NotImplementedError()
[docs]
def gci3_bot_loss(self, gci, neg=False):
"""Loss function for GCI3 with bottom concept: :math:`\exists R.C \sqsubseteq \perp`.
:param gci: Input tensor of shape \(\*,3\) where ``R`` object properties will be at \
gci[:,0], ``C`` classes will be at ``gci[:,1]`` and ``bottom`` classes will be at \
``gci[:,2]``. It is recommended to use the :class:`ELDataset <mowl.datasets.el.ELDataset>`.
:type gci: :class:`torch.Tensor`
:param neg: Parameter indicating that the negative version of this loss function must be \
used. Defaults to ``False``.
:type neg: bool, optional.
"""
raise NotImplementedError()
[docs]
def class_assertion_loss(self, axiom_data, neg=False):
"""Loss function for class assertion: :math:`C(a)`.
:param axiom_data: Input tensor of shape \(\*,2\) where ``C`` classes will be at \
``axiom_data[:,0]`` and ``a`` individuals will be at ``axiom_data[:,1]``. It is \
recommended to use the :class:`ELDataset <mowl.datasets.el.ELDataset>`.
:type axiom_data: :class:`torch.Tensor`
:param neg: Parameter indicating that the negative version of this loss function must be \
used. Defaults to ``False``.
:type neg: bool, optional.
"""
raise NotImplementedError()
[docs]
def object_property_assertion_loss(self, axiom_data, neg=False):
"""Loss function for role assertion: :math:`R(a,b)`.
:param axiom_data: Input tensor of shape \(\*,3\) where ``a`` object properties will be at \
``axiom_data[0], ``R`` object properties will be at ``axiom_data[:,1]`` and ``b`` individuals \
will be at ``axiom_data[:,2]``. It is recommended to use the :class:`ELDataset <mowl.datasets.el.ELDataset>`.
:type axiom_data: :class:`torch.Tensor`
:param neg: Parameter indicating that the negative version of this loss function must be \
used. Defaults to ``False``.
:type neg: bool, optional.
"""
return NotImplementedError()
[docs]
def get_loss_function(self, gci_name):
"""
This chooses the corresponding loss fuction given the name of the GCI.
:param gci_name: Name of the GCI. Choices are ``gci0``, ``gci1``, ``gci2``, ``gci3``, \
``gci0_bot``, ``gci1_bot`` and ``gci3_bot``.
:type gci_name: str
"""
if gci_name not in self.gci_names:
raise ValueError(
f"Parameter gci_name must be one of the following: {', '.join(self.gci_names)}.")
if gci_name == "gci2_bot":
raise ValueError("GCI2 does not allow bottom entity in the right side.")
return {
"gci0_bot": self.gci0_bot_loss,
"gci1_bot": self.gci1_bot_loss,
"gci3_bot": self.gci3_bot_loss,
"gci0": self.gci0_loss,
"gci1": self.gci1_loss,
"gci2": self.gci2_loss,
"gci3": self.gci3_loss,
"class_assertion": self.class_assertion_loss,
"object_property_assertion": self.object_property_assertion_loss
}[gci_name]
[docs]
def forward(self, gci, gci_name, neg=False):
loss_fn = self.get_loss_function(gci_name)
loss = loss_fn(gci, neg=neg)
return loss