Source code for pylops.basicoperators.FunctionOperator

from numbers import Integral

from pylops import LinearOperator


[docs]class FunctionOperator(LinearOperator): r"""Function Operator. Simple wrapper to functions for forward `f` and adjoint `f_c` multiplication. Functions :math:`f` and :math:`f_c` are such that :math:`f:\mathbb{F}^m \to \mathbb{F}_c^n` and :math:`f_c:\mathbb{F}_c^n \to \mathbb{F}^m` where :math:`\mathbb{F}` and :math:`\mathbb{F}_c` are the underlying fields (e.g., :math:`\mathbb{R}` for real or :math:`\mathbb{C}` for complex) FunctionOperator can be called in the following ways: ``FunctionOperator(f, n)``, ``FunctionOperator(f, n, m)``, ``FunctionOperator(f, fc, n)``, and ``FunctionOperator(f, fc, n, m)``. The first two methods can only be used for forward modelling and will return ``NotImplementedError`` if the adjoint is called. The first and third method assume the matrix (or matrices) to be square. All methods can be called with the ``dtype`` keyword argument. Parameters ---------- f : :obj:`callable` Function for forward multiplication. fc : :obj:`callable`, optional Function for adjoint multiplication. n : :obj:`int`, optional Number of rows (length of data vector). m : :obj:`int`, optional Number of columns (length of model vector). dtype : :obj:`str`, optional Type of elements in input array. Attributes ---------- shape : :obj:`tuple` Operator shape :math:`[n \times m]` explicit : :obj:`bool` Operator contains a matrix that can be solved explicitly (``True``) or not (``False``) Examples -------- >>> from pylops.basicoperators import FunctionOperator >>> def forward(v): ... return np.array([2*v[0], 3*v[1]]) ... >>> A = FunctionOperator(forward, 2) >>> A <2x2 FunctionOperator with dtype=float64> >>> A.matvec(np.ones(2)) array([2., 3.]) >>> A @ np.ones(2) array([2., 3.]) """ def __init__(self, f, *args, **kwargs): try: self.dtype = kwargs["dtype"] except KeyError: self.dtype = "float64" self.explicit = False super().__init__() self.f = f # call is FunctionOperator(f, n) if len(args) == 1: self.shape = (args[0], args[0]) self.fc = None elif len(args) == 2: # call is FunctionOperator(f, n, m) if isinstance(args[0], Integral): self.shape = (args[0], args[1]) self.fc = None # call is FunctionOperator(f, fc, n) else: self.fc = args[0] self.shape = (args[1], args[1]) # call is FunctionOperator(f, fc, n, m) elif len(args) == 3: self.fc = args[0] self.shape = args[1:3] def _matvec(self, x): return self.f(x) def _rmatvec(self, x): if self.fc is None: raise NotImplementedError("Adjoint not implemented") else: return self.fc(x)