Source code for pylops.pytensoroperator
import pylops
from pylops.utils import deps
pytensor_message = deps.pytensor_import("the pytensor module")
if pytensor_message is not None:
class PyTensorOperator:
"""PyTensor Op which applies a PyLops Linear Operator, including gradient support.
This class "converts" a PyLops `LinearOperator` class into a PyTensor `Op`.
This applies the `LinearOperator` in "forward-mode" in `self.perform`, and applies
its adjoint when computing the vector-Jacobian product (`self.grad`), as that is
the analytically correct gradient for linear operators. This class should pass
`pytensor.gradient.verify_grad`.
Parameters
----------
LOp : pylops.LinearOperator
"""
def __init__(self, LOp: pylops.LinearOperator) -> None:
if not deps.pytensor_enabled:
raise NotImplementedError(pytensor_message)
else:
import pytensor.tensor as pt
from pytensor.graph.basic import Apply
from pytensor.graph.op import Op
class _PyTensorOperatorNoGrad(Op):
"""PyTensor Op which applies a PyLops Linear Operator, excluding gradient support.
This class "converts" a PyLops `LinearOperator` class into a PyTensor `Op`.
This applies the `LinearOperator` in "forward-mode" in `self.perform`.
Parameters
----------
LOp : pylops.LinearOperator
"""
__props__ = ("dims", "dimsd", "shape")
def __init__(self, LOp: pylops.LinearOperator) -> None:
self._LOp = LOp
self.dims = self._LOp.dims
self.dimsd = self._LOp.dimsd
self.shape = self._LOp.shape
super().__init__()
def make_node(self, x) -> Apply:
x = pt.as_tensor_variable(x)
inputs = [x]
outputs = [pt.tensor(dtype=x.type.dtype, shape=self._LOp.dimsd)]
return Apply(self, inputs, outputs)
def perform(
self, node: Apply, inputs: list, output_storage: list[list[None]]
) -> None:
(x,) = inputs
(yt,) = output_storage
yt[0] = self._LOp @ x
[docs] class PyTensorOperator(_PyTensorOperatorNoGrad):
"""PyTensor Op which applies a PyLops Linear Operator, including gradient support.
This class "converts" a PyLops `LinearOperator` class into a PyTensor `Op`.
This applies the `LinearOperator` in "forward-mode" in `self.perform`, and applies
its adjoint when computing the vector-Jacobian product (`self.grad`), as that is
the analytically correct gradient for linear operators. This class should pass
`pytensor.gradient.verify_grad`.
Parameters
----------
LOp : pylops.LinearOperator
"""
def __init__(self, LOp: pylops.LinearOperator) -> None:
super().__init__(LOp)
self._gradient_op = _PyTensorOperatorNoGrad(self._LOp.H)
def grad(
self, inputs: list[pt.TensorVariable], output_grads: list[pt.TensorVariable]
):
return [self._gradient_op(output_grads[0])]