Source code for fastr.core.dimension

# Copyright 2011-2014 Biomedical Imaging Group Rotterdam, Departments of
# Medical Informatics and Radiology, Erasmus MC, Rotterdam, The Netherlands
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.


from abc import ABCMeta, abstractmethod
from typing import Tuple, TypeVar

from sympy.core.symbol import Symbol

from .. import exceptions

SizeType = TypeVar('SizeType', int, Symbol)


[docs]class Dimension(object): """ A class representing a dimension. It contains the name and size of the dimension. """ __slots__ = ('_name', '_size')
[docs] def __init__(self, name: str, size: SizeType): """ The constructor for the dimension. :param str name: Name of the dimension :param size: Size fo the dimension :type size: int or Symbol """ if not isinstance(name, str): raise exceptions.FastrTypeError("Dimension.name should be a str, " "found [{}] {}".format(type(name).__name__, size)) self._name = name self._size = None self.size = size
[docs] def __repr__(self) -> str: """ String representation of a Dimension """ return "<Dimension {d.name} ({d.size})>".format(d=self)
[docs] def __eq__(self, other: 'Dimension') -> bool: """ Dimension is the same if the name and size are the same """ return self.name == other.name and self.size == other.size
[docs] def __ne__(self, other: 'Dimension') -> bool: """ The not equal test is simply the inverse of the equal test """ return not self == other
@property def name(self) -> str: return self._name @property def size(self) -> SizeType: return self._size @size.setter def size(self, value: SizeType): if not isinstance(value, (int, Symbol)): raise exceptions.FastrTypeError("Dimension.size should be an int or" " sympy.Symbol, found [{}] {}".format(type(value).__name__, value)) if isinstance(value, int) and value < 0: raise exceptions.FastrValueError("Dimension.size should be positive") self._size = value
[docs] def update_size(self, value: SizeType): if self._size == value: # No effect return if not isinstance(value, int): raise exceptions.FastrTypeError("Dimension.size can only be updated by an int " " found [{}] {}".format(type(value).__name__, value)) if value < 0: raise exceptions.FastrValueError("Dimension.size should be positive") if isinstance(self._size, Symbol): self._size = value else: self._size = max(self._size, value)
[docs] def copy(self) -> 'Dimension': """ Get a copy object of a Dimension """ return Dimension(self.name, self.size)
[docs]class HasDimensions(object, metaclass=ABCMeta): """ A Mixin class for any object that has a notion of dimensions and size. It uses the dimension property to expose the dimension name and size. """ @property @abstractmethod def dimensions(self) -> Tuple['Dimension', ...]: """ The dimensions has to be implemented by any subclass. It has to provide a tuple of Dimensions. :return: dimensions :rtype: tuple """ @property def dimnames(self) -> Tuple[str]: """ A tuple containing the dimension names of this object. All items of the tuple are of type str. """ return tuple(x.name for x in self.dimensions) @property def size(self) -> Tuple[SizeType]: """ A tuple containing the size of this object. All items of the tuple are of type int or Symbol. """ return tuple(x.size for x in self.dimensions) @property def ndims(self) -> int: """ The number of dimensions in this object """ return len(self.dimensions)
[docs]class ForwardsDimensions(HasDimensions): """ Class of objects that have dimensions not because they contain data with dimensions but forward them (optionally with changes via combine_dimensions) """ @property @abstractmethod def source(self) -> HasDimensions: """ The source object from which the dimensions are forwarded :return: the object from which the dimensions are forwarded :rtype: HasDimensions """
[docs] @abstractmethod def combine_dimensions(self, dimensions) -> Tuple[Dimension, ...]: """ Method to combine/manipulate the dimensions :param dimensions: the input dimensions from the source :return: dimensions manipulated for this object :rtype: tuple of dimensions """
@property def dimensions(self) -> Tuple[Dimension, ...]: """ The dimensions of the object based on the forwarding """ return self.combine_dimensions(self.source.dimensions)