"""Partition algorithm classes.
Partition algorithms are used during the decomposition stage of BDM
(see :doc:`theory` and :py:mod:`pybdm.bdm`), in which datasets are sliced
into blocks of appropriate sizes.
Decomposition can be done in multiple ways that handles boundaries differently.
This is why partition algorithms have to be properly configured,
so it is well-specified what approach exactly is to be used.
"""
# pylint: disable=unused-argument
from .utils import decompose_dataset, iter_part_shapes
class _Partition:
"""Partition algorithm base class.
Attributes
----------
shape : tuple
Blocks' shape.
"""
name = 'none'
def __init__(self, shape):
"""Initialization method."""
self.shape = shape
def __repr__(self):
cn = self.__class__.__name__
return "<{}({})>".format(cn, ", ".join(self.params))
@property
def params(self):
return [ "shape={}".format(self.shape) ]
def decompose(self, X):
"""Decompose a dataset into blocks.
Parameters
----------
x : array_like
Dataset of arbitrary dimensionality represented as a *Numpy* array.
Yields
------
array_like
Dataset blocks.
"""
cn = self.__class__.__name__
raise NotImplementedError("'{}' is not meant for a direct use".format(cn))
def _iter_shapes(self, X):
yield from iter_part_shapes(X, shape=self.shape, shift=0)
[docs]class PartitionIgnore(_Partition):
"""Partition with the 'ignore' boundary condition.
Attributes
----------
shape : tuple
Part shape.
Notes
-----
See :doc:`theory` for a detailed description.
"""
name = 'ignore'
[docs] def decompose(self, X):
"""Decompose with the 'ignore' boundary.
.. automethod:: _Partition.decompose
"""
for part in decompose_dataset(X, shape=self.shape, shift=0):
if part.shape == self.shape:
yield part
def _iter_shapes(self, X):
for shape in super()._iter_shapes(X):
if shape == self.shape:
yield shape
[docs]class PartitionRecursive(_Partition):
"""Partition with the 'recursive' boundary condition.
Attributes
----------
shape : tuple
Part shape.
min_length : int
Minimum parts' length. Non-negative.
In case of multidimensional objects it specifies minimum
length of any single dimension.
Notes
-----
See :doc:`theory` for a detailed description.
"""
name = 'recursive'
def __init__(self, shape, min_length=2):
"""Initialization method."""
super().__init__(shape=shape)
self.min_length = min_length
@property
def params(self):
return super().params + [ "min_length={}".format(self.min_length) ]
def _decompose(self, X, shape):
for part in decompose_dataset(X, shape=shape, shift=0):
if part.shape == shape:
yield part
else:
min_dim_length = min(part.shape)
if min_dim_length < self.min_length:
continue
shrinked_shape = tuple(min_dim_length for _ in range(len(shape)))
yield from self._decompose(part, shrinked_shape)
[docs] def decompose(self, X):
"""Decompose with the 'recursive' boundary.
.. automethod:: _Partition.decompose
"""
yield from self._decompose(X, shape=self.shape)