Source code for pina.domain.base_operation
"""Module for all set-based operations Base class."""
from copy import deepcopy
from abc import ABCMeta
from .operation_interface import OperationInterface
from .base_domain import BaseDomain
from ..utils import check_consistency
[docs]
class BaseOperation(OperationInterface, BaseDomain, metaclass=ABCMeta):
"""
Base class for all set operation defined on geometric domains, implementing
common functionality.
All specific operation types should inherit from this class and implement
the abstract methods defined in both the following interfaces:
:class:`~pina.domain.operation_interface.OperationInterface`, and
:class:`~pina.domain.domain_interface.DomainInterface`.
This class is not meant to be instantiated directly.
"""
def __init__(self, geometries):
"""
Initialization of the :class:`OperationInterface` class.
:param geometries: The list of domains on which to perform the set
operation.
:type geometries: list[BaseDomain] | tuple[BaseDomain]
:raises TypeError: If geometries is neither a list nor a tuple.
:raises ValueError: If geometries elements are not instances of
:class:`~pina.domain.base_domain.BaseDomain`.
:raises NotImplementedError: If the dimensions of the geometries are not
consistent.
"""
super().__init__()
self.geometries = geometries
[docs]
def update(self, domain):
"""
Update the domain resulting from the operation.
:param DomainInterface domain: The domain whose labels are to be merged
into the current one.
:raises NotImplementedError: If the geometries involved in the operation
are of different types.
:raises TypeError: If the passed domain is not of the same type of all
the geometries involved in the operation.
:return: A new domain instance with the merged labels.
:rtype: BaseOperation
"""
# Check all geometries are of the same type
domain_type = type(self.geometries[0])
if not all(isinstance(g, domain_type) for g in self.geometries):
raise NotImplementedError(
f"The {self.__class__.__name__} of geometries of different"
" types does not support the update operation. All geometries"
" must be of the same type."
)
# Check domain type consistency
if not isinstance(domain, domain_type):
raise TypeError(
f"Cannot update the {self.__class__.__name__} of domains of"
f" type {domain_type} with domain of type {type(domain)}."
)
# Update each geometry
updated = deepcopy(self)
updated.geometries = [geom.update(domain) for geom in self.geometries]
return updated
@property
def sample_modes(self):
"""
The list of available sampling modes.
:return: The list of available sampling modes.
:rtype: list[str]
"""
return list(
set.intersection(
*map(set, [g.sample_modes for g in self.geometries])
)
)
@property
def variables(self):
"""
The list of variables of the domain.
:return: The list of variables of the domain.
:rtype: list[str]
"""
return sorted({v for g in self.geometries for v in g.variables})
@property
def domain_dict(self):
"""
Returns a dictionary representation of the operation domain.
:return: The dictionary representation of the operation domain.
:rtype: dict
"""
return {
"type": self.__class__.__name__,
"geometries": [geom.domain_dict for geom in self.geometries],
}
@property
def geometries(self):
"""
The domains on which to perform the set operation.
:return: The domains on which to perform the set operation.
:rtype: list[BaseDomain]
"""
return self._geometries
@property
def range(self):
"""
The range variables of each geometry.
:return: The range variables of each geometry.
:rtype: dict
"""
return {f"geometry_{i}": g.range for i, g in enumerate(self.geometries)}
@property
def fixed(self):
"""
The fixed variables of each geometry.
:return: The fixed variables of each geometry.
:rtype: dict
"""
return {f"geometry_{i}": g.fixed for i, g in enumerate(self.geometries)}
@geometries.setter
def geometries(self, values):
"""
Setter for the ``geometries`` property.
:param values: The geometries to be set.
:type values: list[BaseDomain] | tuple[BaseDomain]
:raises TypeError: If values is neither a list nor a tuple.
:raises ValueError: If values elements are not instances of
:class:`~pina.domain.base_domain.BaseDomain`.
:raises NotImplementedError: If the dimensions of the geometries are not
consistent.
"""
# Check geometries are list or tuple
if not isinstance(values, (list, tuple)):
raise TypeError(
"geometries must be either a list or a tuple of BaseDomain."
)
# Check consistency
check_consistency(values, (BaseDomain, BaseOperation))
# Check geometries
for v in values:
if v.variables != values[0].variables:
raise NotImplementedError(
f"The {self.__class__.__name__} of geometries living in "
"different ambient spaces is not well-defined. "
"All geometries must share the same dimensions and labels."
)
self._geometries = values