Source code for pylops.basicoperators.block

__all__ = ["Block"]

from collections.abc import Iterable

from pylops import LinearOperator
from pylops.basicoperators import HStack, VStack
from pylops.utils.typing import DTypeLike, NDArray, Tparallel_kind


class _Block(LinearOperator):
    """Block operator.

    Used to be able to provide operators from different libraries to
    Block.
    """

    def __init__(
        self,
        ops: Iterable[Iterable[LinearOperator]],
        forceflat: bool | None = None,
        dtype: DTypeLike | None = None,
        _HStack=HStack,
        _VStack=VStack,
        _args_HStack: dict | None = None,
        _args_VStack: dict | None = None,
        name: str = "B",
    ):
        if _args_HStack is None:
            self._args_HStack = {}
        else:
            self._args_HStack = _args_HStack
        if _args_VStack is None:
            self._args_VStack = {}
        else:
            self._args_VStack = _args_VStack
        hblocks = [_HStack(hblock, dtype=dtype, **self._args_HStack) for hblock in ops]
        super().__init__(
            Op=_VStack(
                ops=hblocks, forceflat=forceflat, dtype=dtype, **self._args_VStack
            ),
            name=name,
        )

    def _matvec(self, x: NDArray) -> NDArray:
        return super()._matvec(x)

    def _rmatvec(self, x: NDArray) -> NDArray:
        return super()._rmatvec(x)


[docs] class Block(_Block): r"""Block operator. Create a block operator from N lists of M linear operators each. Note that in case one or more operators are filled with zeros, it is recommended to use the :py:class:`pylops.Zero` operator instead of e.g., :py:class:`pylops.MatrixMult` with a matrix of zeros, as the former will be simply by-passed both in the forward and adjoint steps. Parameters ---------- ops : :obj:`list` List of lists of operators to be combined in block fashion. Alternatively, :obj:`numpy.ndarray` or :obj:`scipy.sparse` matrices can be passed in place of one or more operators. nproc : :obj:`int`, optional Number of processes/threads used to evaluate the N operators in parallel using ``multiprocessing``/``concurrent.futures``. If ``nproc=1``, work in serial mode. forceflat : :obj:`bool`, optional .. versionadded:: 2.2.0 Force an array to be flattened after rmatvec. parallel_kind : :obj:`str`, optional .. versionadded:: 2.6.0 Parallelism kind when ``nproc>1``. Can be ``multiproc`` (using :mod:`multiprocessing`) or ``multithread`` (using :class:`concurrent.futures.ThreadPoolExecutor`). Defaults to ``multiproc``. dtype : :obj:`str`, optional Type of elements in input array. Attributes ---------- pool : :obj:`multiprocessing.Pool` or :obj:`concurrent.futures.ThreadPoolExecutor` or :obj:`None` Pool of workers used to evaluate the N operators in parallel. When ``nproc=1``, no pool is created (i.e., ``pool=None``). shape : :obj:`tuple` Operator shape. Notes ----- In mathematics, a block or a partitioned matrix is a matrix that is interpreted as being broken into sections called blocks or submatrices. Similarly a block operator is composed of N sets of M linear operators each such that its application in forward mode leads to .. math:: \begin{bmatrix} \mathbf{L}_{1,1} & \mathbf{L}_{1,2} & \ldots & \mathbf{L}_{1,M} \\ \mathbf{L}_{2,1} & \mathbf{L}_{2,2} & \ldots & \mathbf{L}_{2,M} \\ \vdots & \vdots & \ddots & \vdots \\ \mathbf{L}_{N,1} & \mathbf{L}_{N,2} & \ldots & \mathbf{L}_{N,M} \end{bmatrix} \begin{bmatrix} \mathbf{x}_{1} \\ \mathbf{x}_{2} \\ \vdots \\ \mathbf{x}_{M} \end{bmatrix} = \begin{bmatrix} \mathbf{L}_{1,1} \mathbf{x}_{1} + \mathbf{L}_{1,2} \mathbf{x}_{2} + \mathbf{L}_{1,M} \mathbf{x}_{M} \\ \mathbf{L}_{2,1} \mathbf{x}_{1} + \mathbf{L}_{2,2} \mathbf{x}_{2} + \mathbf{L}_{2,M} \mathbf{x}_{M} \\ \vdots \\ \mathbf{L}_{N,1} \mathbf{x}_{1} + \mathbf{L}_{N,2} \mathbf{x}_{2} + \mathbf{L}_{N,M} \mathbf{x}_{M} \end{bmatrix} while its application in adjoint mode leads to .. math:: \begin{bmatrix} \mathbf{L}_{1,1}^H & \mathbf{L}_{2,1}^H & \ldots & \mathbf{L}_{N,1}^H \\ \mathbf{L}_{1,2}^H & \mathbf{L}_{2,2}^H & \ldots & \mathbf{L}_{N,2}^H \\ \vdots & \vdots & \ddots & \vdots \\ \mathbf{L}_{1,M}^H & \mathbf{L}_{2,M}^H & \ldots & \mathbf{L}_{N,M}^H \end{bmatrix} \begin{bmatrix} \mathbf{y}_{1} \\ \mathbf{y}_{2} \\ \vdots \\ \mathbf{y}_{N} \end{bmatrix} = \begin{bmatrix} \mathbf{L}_{1,1}^H \mathbf{y}_{1} + \mathbf{L}_{2,1}^H \mathbf{y}_{2} + \mathbf{L}_{N,1}^H \mathbf{y}_{N} \\ \mathbf{L}_{1,2}^H \mathbf{y}_{1} + \mathbf{L}_{2,2}^H \mathbf{y}_{2} + \mathbf{L}_{N,2}^H \mathbf{y}_{N} \\ \vdots \\ \mathbf{L}_{1,M}^H \mathbf{y}_{1} + \mathbf{L}_{2,M}^H \mathbf{y}_{2} + \mathbf{L}_{N,M}^H \mathbf{y}_{N} \end{bmatrix} """ def __init__( self, ops: Iterable[Iterable[LinearOperator]], nproc: int = 1, forceflat: bool | None = None, parallel_kind: Tparallel_kind = "multiproc", dtype: DTypeLike | None = None, ): super().__init__( ops=ops, forceflat=forceflat, dtype=dtype, _args_VStack={"nproc": nproc, "parallel_kind": parallel_kind}, )