Lazylinop core module#
LazyLinOp: the core class for lazy linear operators.
Available operations
+ (addition), - (subtraction),
@ (matrix product), * (scalar multiplication),
** (matrix power for square operators),
indexing, slicing and others.
For a nicer introduction you might look at these tutorials.
Basic operators:
Construction:
eye(),anti_eye(),diag(),anti_diag(),ones(),zeros(),Agglomeration & Transformation:
add(),block_diag(),hstack(),pad(),kron(),vstack().
LazyLinOp class#
- class lazylinop.LazyLinOp(shape, matvec=None, matmat=None, rmatvec=None, rmatmat=None)#
The
LazyLinOpclass.The lazy principle
The evaluation of any defined operation on a
LazyLinOpis delayed until a multiplication by a matrix/vector or a call ofLazyLinOp.toarray()is made.Two ways to instantiate
Using
lazylinop.aslazylinop()orUsing this constructor (
lazylinop.LazyLinOp()) to definematmat,matvecfunctions.
Available operations
+(addition),-(subtraction),@(matrix product),*(scalar multiplication),**(matrix power for square operators), indexing, slicing and others. For a nicer introduction you might look at these tutorials.Recursion limit
Repeated “inplace” modifications of a
LazyLinOpthrough any operation like a concatenation (op = vstack((op, anything))) are subject to aRecursionErrorif the number of recursive calls exceedssys.getrecursionlimit(). You might change this limit if needed usingsys.setrecursionlimit().A
LazyLinOpinstance is defined by a shape and at least functionsmatvecormatmatandrmatvecorrmatmat.Parameters#
- shape: (
tuple[int, int]) Operator \(L\) dimensions \((M, N)\).
- matvec: (callable)
Returns \(y = L * v\) with \(v\) a vector of size \(N\). \(y\) size is \(M\) with the same number of dimension(s) as \(v\).
- rmatvec: (callable)
Returns \(y = L^H * v\) with \(v\) a vector of size \(M\). \(y\) size is \(N\) with the same number of dimension(s) as \(v\).
- matmat: (callable)
Returns \(L * V\). The output matrix shape is \((M, K)\).
- rmatmat: (
callable) Returns \(L^H * V\). The output matrix shape is \((N, K)\).
Auto-implemented operations
If only
matvecis defined and notmatmat, an automatic naivematmatwill be defined upon the givenmatvecbut note that it might be suboptimal (in which case amatmatis useful). The same applies forrmatvecandrmatmat.No need to provide the implementation of the multiplication by a
LazyLinOp, or a numpy array withndim > 2because both of them are auto-implemented. For the latter operation, it is computed as in numpy.__matmul__.
- Return:
LazyLinOp- Example:
>>> # In this example we create a LazyLinOp >>> # for the DFT using the fft from scipy >>> import numpy as np >>> from scipy.fft import fft, ifft >>> from lazylinop import LazyLinOp >>> fft_mm = lambda x: fft(x, norm='ortho') >>> fft_rmm = lambda x: ifft(x, norm='ortho') >>> n = 16 >>> F = LazyLinOp((n, n), matvec=fft_mm, rmatvec=fft_rmm) >>> x = np.random.rand(n) >>> y = F @ x >>> np.allclose(y, fft(x, norm='ortho')) True >>> np.allclose(x, F.H @ y) True
See also
Attributes#
Methods#
LazyLinOp main methods: LazyLinOp.toarray(),
LazyLinOp.conj(),
LazyLinOp.check(),
LazyLinOp.__pow__().
- lazylinop.LazyLinOp.toarray(self, dtype=None, array_namespace=None, device=None)#
Returns self as an array
Internally, it computes
self @ array_namespace.eye(self.shape[1], dtype=dtype), with array_namespace and dtype depending on arguments.with smallest possible
dtypeand returns self as a NumPy/CuPy array or torch tensor.dtypeof the outputydepends on theLazyLinOpinstanceself.- Args:
- dtype:
str, NumPy/CuPy or torch dtype, optional The
dtypeused eye() in eye()Default value is
Noneand will select the smallest possible dtype.Note that
dtypeof the returned array depends on theLazyLinOpinstanceself.- array_namespace:
namespace, optional The type of the return array, as an array API namespace (NumPy, torch, CuPy, …) Default is
None, which would use NumPy.- device:
str, optional The
devicewhere the returned array will be computedDefault value is
None, the array will reside on CPUNote:
devicehas no effect ifarray_namespaceis not equal to'torch'.
- dtype:
- Examples:
>>> import numpy as np >>> from lazylinop import aslazylinop >>> L = aslazylinop(np.eye(2, dtype='int')) >>> L.toarray(array_namespace=np, dtype='float') array([[1., 0.], [0., 1.]])
- lazylinop.LazyLinOp.conj(self)#
Returns the
LazyLinOpconjugate.
- lazylinop.LazyLinOp.check(self, array_namespace=None, dtype='float64', device=None, rtol=1e-05, atol=1e-08)#
Verifies validity assertions on any
LazyLinOp.Notations:
Let
opaLazyLinOp,u,vvectors such thatu.shape[0] == op.shape[1]
and
v.shape[0] == op.shape[0], -X,Y2d-arrays such thatX.shape[0] == op.shape[1]andY.shape[0] == op.shape[0].The function verifies:
Consistency of operator/adjoint product shape:
(op @ u).shape == (op.shape[0],),(op.H @ v).shape == (op.shape[1],),
(op @ X).shape == (op.shape[0], X.shape[1]),(op.H @ Y).shape == (op.shape[1], Y.shape[1]),
Consistency of operator & adjoint products:
(op @ u).conj().T @ v == u.conj().T @ op.H @ v
Consistency of operator-by-matrix & operator-by-vector products:
op @ Xis equal to the horizontal concatenation of allop @ X[:, j](\(0\le j < X.shape[1]\)).(it implies also that
(op @ X).shape[1] == X.shape[1], as previously verified in 2.a)
Consistency of adjoint-by-matrix & adjoint-by-vector products:
op.H @ Yis equal to the horizontal concatenation of allop.H @ Y[:, j](\(0\le j < Y.shape[1]\)).(it implies also that
(op.H @ Y).shape[1] == Y.shape[1], as previously verified in 2.b)
Linearity:
op @ (a1 * u1 + a2 * u2) == a1 * (op @ u1) + a2 * (op @ u2).
Device:
array_api_compat.device(x) == array_api_compat.device(op @ x)
Raises:
Exception("Operator shape[0] and operator-by-vector product shape must agree")(assertion 1.a)Exception("Operator shape[1] and adjoint-by-vector product shape must agree")(assertion 1.b)Exception("Operator-by-matrix product shape and operator/input-matrix shape must agree")(assertion 2.a)Exception("Operator-by-matrix & operator-by-vector products must agree")(assertion 2.b)Exception("Operator and adjoint products do not match")(assertion 3)Exception("Operator-by-matrix & operator-by-vector products must agree")(assertion 4)Exception("Adjoint-by-matrix product shape and adjoint/input-matrix shape must agree")(assertion 5)
Computational cost
This function has a computational cost of several matrix products. It shouldn’t be used into an efficient implementation but only to test a
LazyLinOpimplementation is valid.Necessary condition but not sufficient
This function is able to detect an inconsistent
LazyLinOpaccording to the assertions above but it cannot ensure a particular operator computes what someone is excepted this operator to compute. In other words, the operator can be consistent but not correct at the same time. Thus, this function is not enough by itself to write unit tests for an operator, complementary tests are necessary.- Args:
- self: (
LazyLinOp) Operator to test.
- array_namespace:
namespace, optional Namespace of the input to test
self(see Examples section).Nonewhich would use NumPy (defaut value).npNumPy namespaceimport numpy as np.cpCuPy namespaceimport cupy as cp.torchPyTorch namespaceimport torch.listor array namespaces.
- dtype:
str, NumPy/CuPy or torch dtype, optional dtype of the input that will be used to test
self.- device: optional
Use device
deviceto runself.check(...). Default value isNone.- rtol:
float, optional The relative tolerance parameter used by NumPy
allclosefunction to verify validity assertions. Default value is1e-5. See NumPy allclose for more details.- atol:
float, optional The absolute tolerance parameter used by NumPy
allclosefunction to verify validity assertions. Default value is1e-8. See NumPy allclose for more details.
- self: (
- Example:
>>> import numpy as np >>> from numpy.random import rand >>> from lazylinop import aslazylinop, LazyLinOp >>> M = rand(12, 14) >>> # numpy array M is OK as a LazyLinOp >>> aslazylinop(M).check(array_namespace=np) >>> # the next LazyLinOp is not >>> L2 = LazyLinOp((6, 7), matmat=lambda x: np.ones((6, 7)), rmatmat=lambda x: np.zeros((7,6))) >>> L2.check(array_namespace=np) Traceback (most recent call last): ... Exception: ...
See also
aslazylinop(),LazyLinOp.
- lazylinop.LazyLinOp.__pow__(self, n)#
Returns the
LazyLinOpfor the n-th power ofself.L**n == L @ L @ ... @ L(n-1 multiplications).
- Args:
n: a positive integer
- Raises:
The
LazyLinOpis not square.- Example:
>>> from lazylinop import aslazylinop >>> import numpy as np >>> M = np.random.rand(10, 10).astype('float32') >>> lM = aslazylinop(M) >>> lM <10x10 ArrayBasedLazyLinOp with dtype=float32> >>> np.allclose((lM**2).toarray(), M @ M) True
Module utility functions#
- lazylinop.islazylinop(op)#
- lazylinop.aslazylinop(op)#
Basic operators#
Construction#
Functions for creating a Lazylinop from scratch
(providing the related parameters).
Important
Note that lazylinop provides two closely named functions
padandpadder:padtakes a lazylinop as input and returns a padded lazylinop, and is part of a family of tools to “glue” various types of lazylinops together.padderbuilds a lazylinop that maps a (batch of) vector(s) to their (batch of) padded versions.
In general, function names such as
slicer,indexer,padder(see alsopadder2d) also refer to functions that return a LazyLinop which action is described by the verb appearing in their name.
- lazylinop.anti_diag(v, k=0, extract_meth='canonical_vectors', extract_batch=1)#
Returns a
LazyLinOpLthat extracts an antidiagonal or builds an antidiagonal .The shape of
Lis square and depends on the size ofv.- Args:
- v: (compatible linear operator, 1D
numpy.ndarray) - k:
int, optional The index of antidiagonal,
0(default) for the main antidiagonal (the one starting from the upper right corner),k > 0for upper antidiagonals,k < 0for lower antidiagonals below (seeanti_eye()).- extract_meth:
str, optional The method used to extract the antidiagonal vector. The interest to have several methods resides in their difference of memory and execution time costs but also on the operator capabilities (e.g. not all of them support a CSC matrix as multiplication operand).
'canonical_vectors': use canonical basis vectors \(e_i\) to extract each antidiagonal element of the operator. It takes an operator-vector multiplication to extract each antidiagonal element.'canonical_vectors_csc': The same as above but using scipy CSC matrices to encode the canonical vectors. The memory cost is even smaller than that of'canonical_vectors'. Howevervmust be compatible with CSC matrix-vector multiplication.'slicing': extract antidiagonal elements by slicing rows and columns by blocks of shape(extract_batch, extract_batch).'toarray': useLazyLinOp.toarray()to extract the antidiagonal after a conversion to a whole numpy array.
- extract_batch:
int, optional The size of the batch used for partial antidiagonal extraction in
'canonical_vectors','canonical_vectors_csc'and'slicing 'methods.This argument is ignored for
'toarray'method.
Antidiagonal extraction cost
Even though the
'toarray'method is generally faster if the operator is not extremely large it has an important memory cost (\(O(v.shape[0] \times v.shape[1])\)) . Hence the default method iscanonical_vectorsin order to avoid memory consumption. However note that this method allows to define a memory-time trade-off with theextract_batchargument. The larger is the batch, the faster should be the execution (provided enough memory is available).- v: (compatible linear operator, 1D
- Returns:
The extracted antidiagonal numpy vector or the constructed antidiagonal
LazyLinOp.- Example: (antidiagonal
LazyLinOp) >>> import lazylinop.basicops as lz >>> import numpy as np >>> v = np.arange(1, 6) >>> v array([1, 2, 3, 4, 5]) >>> ld1 = lz.anti_diag(v) >>> ld1 <5x5 LazyLinOp with unspecified dtype> >>> ld1.toarray('int') array([[0, 0, 0, 0, 1], [0, 0, 0, 2, 0], [0, 0, 3, 0, 0], [0, 4, 0, 0, 0], [5, 0, 0, 0, 0]]) >>> ld2 = lz.anti_diag(v, -2) >>> ld2 <7x7 LazyLinOp with unspecified dtype> >>> ld2.toarray('int') array([[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 1], [0, 0, 0, 0, 0, 2, 0], [0, 0, 0, 0, 3, 0, 0], [0, 0, 0, 4, 0, 0, 0], [0, 0, 5, 0, 0, 0, 0]]) >>> ld3 = lz.anti_diag(v, 2) >>> ld3 <7x7 LazyLinOp with unspecified dtype> >>> ld3.toarray('int') array([[0, 0, 0, 0, 1, 0, 0], [0, 0, 0, 2, 0, 0, 0], [0, 0, 3, 0, 0, 0, 0], [0, 4, 0, 0, 0, 0, 0], [5, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0]])
- Example: (antidiagonal extraction)
>>> import lazylinop.basicops as lz >>> import numpy as np >>> lD = aslazylinop(np.random.rand(10, 12)) >>> d = lz.anti_diag(lD, -2, extract_meth='toarray', extract_batch=3) >>> # verify d is really the antidiagonal of index -2 >>> d_ = np.diag(np.fliplr(lD.toarray()), -2) >>> np.allclose(d, d_) True
See also
- lazylinop.anti_eye(M, N=None, k=0)#
Constructs a
LazyLinOpwhose equivalent array is filled with ones on the k-th antidiagonal and zeros everywhere else.L = anti_eye(M, N, k)is such thatL.toarray() == numpy.flip(numpy.eye(M, N, k), axis=1).- Args:
- M:
int Number of rows.
- N:
int, optional Number of columns (default is
M).- k:
int, optional Anti-diagonal to place ones on.
zero for the main antidiagonal (default), the one starting from the upper right corner.
positive integer for an upper antidiagonal.
negative integer for a lower antidiagonal,
if
k >= Nork <= - Mthenanti_eye(M, N, k)iszeros((M, N))(k-th antidiagonal is out of operator shape).
- M:
- Returns:
The anti-eye
LazyLinOp.- Examples:
>>> import lazylinop as lz >>> import numpy as np >>> x = np.arange(3) >>> L = lz.basicops.anti_eye(3) >>> np.allclose(L @ x, np.flip(x)) True >>> # Check the main diagonal is the one starting >>> # from the upper right corner. >>> L = lz.basicops.anti_eye(3, 4, k=0) >>> L.toarray(dtype='float') array([[0., 0., 0., 1.], [0., 0., 1., 0.], [0., 1., 0., 0.]]) >>> L = lz.basicops.anti_eye(3, N=3, k=0) >>> L.toarray(dtype='int') array([[0, 0, 1], [0, 1, 0], [1, 0, 0]]) >>> L = lz.basicops.anti_eye(3, N=3, k=1) >>> L.toarray(dtype='int') array([[0, 1, 0], [1, 0, 0], [0, 0, 0]]) >>> L = lz.basicops.anti_eye(3, N=3, k=-1) >>> L.toarray(dtype='int') array([[0, 0, 0], [0, 0, 1], [0, 1, 0]]) >>> L = lz.basicops.anti_eye(3, N=4, k=0) >>> L.toarray(dtype='int') array([[0, 0, 0, 1], [0, 0, 1, 0], [0, 1, 0, 0]]) >>> L = lz.basicops.anti_eye(3, N=4, k=1) >>> L.toarray(dtype='int') array([[0, 0, 1, 0], [0, 1, 0, 0], [1, 0, 0, 0]]) >>> L = lz.basicops.anti_eye(3, N=4, k=-1) >>> L.toarray(dtype='int') array([[0, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]])
See also
- lazylinop.diag(v, k=0, extract_meth='canonical_vectors', extract_batch=1)#
Extracts a diagonal or constructs a diagonal
LazyLinOp.- Args:
- v: (compatible linear operator, 1D
numpy.ndarray) - k: (
int) The index of diagonal,
0for the main diagonal,k > 0for diagonals above,k < 0for diagonals below (seeeye()).- extract_meth: (
str) The method used to extract the diagonal vector. The interest to have several methods resides in their difference of memory and execution time costs but also on the operator capabilities (e.g. not all of them support a CSC matrix as multiplication operand).
'canonical_vectors': use canonical basis vectors \(e_i\) to extract each diagonal element of the operator. It takes an operator-vector multiplication to extract each diagonal element.'canonical_vectors_csc': The same as above but using scipy CSC matrices to encode the canonical vectors. The memory cost is even smaller than that of'canonical_vectors'. Howevervmust be compatible to CSC matrices multiplication.'slicing': extract diagonal elements by slicing rows and columns by blocks of shape(extract_batch, extract_batch).'toarray': useLazyLinOp.toarray()to extract the diagonal after a conversion to a whole numpy array.
- extract_batch: (
int) The size of the batch used for partial diagonal extraction in
'canonical_vectors','canonical_vectors_csc'and'slicing 'methods.This argument is ignored for
'toarray'method.
Diagonal extraction cost
Even though the
'toarray'method is generally faster if the operator is not extremely large it has an important memory cost (\(O(v.shape[0] \times v.shape[1])\)) . Hence the default method iscanonical_vectorsin order to avoid memory consumption. However note that this method allows to define a memory-time trade-off with theextract_batchargument. The larger is the batch, the faster should be the execution (provided enough memory is available).- v: (compatible linear operator, 1D
- Returns:
The extracted diagonal numpy vector or the constructed diagonal
LazyLinOp.- Example: (diagonal
LazyLinOp) >>> import lazylinop as lz >>> import numpy as np >>> v = np.arange(1, 6) >>> v array([1, 2, 3, 4, 5]) >>> ld1 = lz.diag(v) >>> ld1 <5x5 LazyLinOp with unspecified dtype> >>> ld1.toarray('int') array([[1, 0, 0, 0, 0], [0, 2, 0, 0, 0], [0, 0, 3, 0, 0], [0, 0, 0, 4, 0], [0, 0, 0, 0, 5]]) >>> ld2 = lz.diag(v, -2) >>> ld2.toarray('int') array([[0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0], [1, 0, 0, 0, 0, 0, 0], [0, 2, 0, 0, 0, 0, 0], [0, 0, 3, 0, 0, 0, 0], [0, 0, 0, 4, 0, 0, 0], [0, 0, 0, 0, 5, 0, 0]]) >>> ld3 = lz.diag(v, 2) >>> ld3.toarray('int') array([[0, 0, 1, 0, 0, 0, 0], [0, 0, 0, 2, 0, 0, 0], [0, 0, 0, 0, 3, 0, 0], [0, 0, 0, 0, 0, 4, 0], [0, 0, 0, 0, 0, 0, 5], [0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0]])
- Example: (diagonal extraction)
>>> import lazylinop as lz >>> from lazylinop import aslazylinop >>> import numpy as np >>> M = np.random.rand(10, 12) >>> lD = aslazylinop(M) >>> d = lz.diag(lD, -2) >>> # verify d is really the diagonal of index -2 >>> d_ = np.array([M[i, i-2] for i in range(abs(-2), lD.shape[0])]) >>> np.allclose(d, d_) True
See also
- lazylinop.diff(N, n=1, prepend=0, append=0, backend='numpy')#
Returns a
LazyLinOpLthat calculates the n-th discrete difference of an input vector.Shape of
Lis \((M,~N)\) where \(M = prepend + N + append - n\).- Args:
- N:
int Size of the input.
- n
int, optional The number of times values are differenced (default is 1). If zero, the input is returned as-is.
- prepend, append
int, optional Prepend or append input vector with a number of zeros equals to the argument, prior to performing the difference. By default it is equal to 0.
- N:
- Returns:
See also
- Examples:
>>> import numpy as np >>> import lazylinop as lz >>> x = np.array([1, 2, 2, 4, 2, 2, 1]) >>> D = diff(x.shape[0]) >>> y = D @ x >>> np.allclose(np.array([1, 0, 2, -2, 0, -1]), y) True
- lazylinop.eye(M, N=None, k=0)#
Returns the
LazyLinOpLfor eye (identity matrix and variants).- Args:
- M:
int Number of rows.
- N:
int, optional Number of columns. Default is
M.- k:
int, optional Diagonal to place ones on.
zero for the main diagonal (default),
positive integer for an upper diagonal,
negative integer for a lower diagonal.
- M:
- Example:
>>> import lazylinop as lz >>> le1 = lz.eye(5) >>> le1 <5x5 LazyLinOp with unspecified dtype> >>> le1.toarray(dtype='float') array([[1., 0., 0., 0., 0.], [0., 1., 0., 0., 0.], [0., 0., 1., 0., 0.], [0., 0., 0., 1., 0.], [0., 0., 0., 0., 1.]]) >>> le2 = lz.eye(5, 2) >>> le2 <5x2 LazyLinOp with unspecified dtype> >>> le2.toarray(dtype='int') array([[1, 0], [0, 1], [0, 0], [0, 0], [0, 0]]) >>> le3 = lz.eye(5, 3, 1) >>> le3 <5x3 LazyLinOp with unspecified dtype> >>> le3.toarray(dtype='int') array([[0, 1, 0], [0, 0, 1], [0, 0, 0], [0, 0, 0], [0, 0, 0]]) >>> le4 = lz.eye(5, 3, -1) >>> le4 <5x3 LazyLinOp with unspecified dtype> >>> le4.toarray(dtype='int') array([[0, 0, 0], [1, 0, 0], [0, 1, 0], [0, 0, 1], [0, 0, 0]])
See also
- lazylinop.ones(shape)#
Returns a
LazyLinOpones.Fixed memory cost
Whatever is the shape of the
ones, it has the same memory cost.Args:
- shape:
tuple[int, int] Operator shape, e.g.,
(2, 3).
- Returns:
LazyLinOpones.- Example:
>>> import numpy as np >>> import lazylinop as lz >>> L = lz.ones((6, 5)) >>> v = np.arange(5) >>> v array([0, 1, 2, 3, 4]) >>> L @ v array([10, 10, 10, 10, 10, 10]) >>> Oa = np.ones((6, 5)).astype('int') >>> Oa @ v array([10, 10, 10, 10, 10, 10]) >>> M = np.arange(5 * 4).reshape(5, 4) >>> L @ M array([[40, 45, 50, 55], [40, 45, 50, 55], [40, 45, 50, 55], [40, 45, 50, 55], [40, 45, 50, 55], [40, 45, 50, 55]]) >>> Oa @ M array([[40, 45, 50, 55], [40, 45, 50, 55], [40, 45, 50, 55], [40, 45, 50, 55], [40, 45, 50, 55], [40, 45, 50, 55]])
See also
- shape:
- lazylinop.tri(N, M=None, k=0)#
Returns a
LazyLinOpLassociated to a matrix such that its lower triangular part is filled with ones. Above given diagonalk, matrix is filled with zeros.Shape of
Lis \((N,~M)\) where \(M = N\) by default.LazyLinOpL = tri(N, k=0)corresponds to a lower triangular matrix of shape \((N,~N)\) and its transpositionL.Tcorresponds to an upper triangular matrix.- Args:
- N:
int Number of rows.
- M:
int, optional Number of columns. By default
Mis equal toN.- k:
int, optional The sub-diagonal at and below which the matrix is filled with ones (the rest being filled by zeroes).
\(k<0\) below the main diagonal.
\(k=0\) corresponds to the main diagonal (default). When called with \(k=0\) (default), the resulting operator
Lperforms a cumulative sum.\(k>0\) above the main diagonal.
- N:
- Returns:
- Examples:
>>> import numpy as np >>> import lazylinop as lz >>> N = 4 >>> x = np.arange(N) >>> x array([0, 1, 2, 3]) >>> L = lz.basicops.tri(N) >>> y = L @ x >>> y_ = np.cumsum(x) >>> np.allclose(y, y_) True
See also
- lazylinop.cumsum(N)#
Returns a
LazyLinOpLassociated to the cumulative sum of an input of sizeN.Shape of
Lis \((N,~N)\).- Args:
- N:
int Size of the input array.
- N:
- Returns:
- Examples:
>>> import numpy as np >>> import lazylinop as lz >>> N = 4 >>> x = np.arange(N) >>> x array([0, 1, 2, 3]) >>> L = lz.basicops.tri(N) >>> y = L @ x >>> y_ = np.cumsum(x) >>> np.allclose(y, y_) True
- lazylinop.zeros(shape)#
Returns a zero
LazyLinOp.Fixed memory cost
Whatever is the shape of the
zeros, it has the same memory cost.- Args:
- shape: (
tuple[int, int]) The operator shape.
- shape: (
- Example:
>>> import numpy as np >>> import lazylinop as lz >>> Lz = lz.zeros((10, 12)) >>> x = np.random.rand(12) >>> Lz @ x array([0., 0., 0., 0., 0., 0., 0., 0., 0., 0.])
See also
- lazylinop.padder(N, width=(0, 0), mode='zero')#
Returns a
LazyLinOpLthat extends a signal with either zero, periodic, symmetric, antisymmetric or reflect boundary conditions.Shape of
Lis \((M,~N)\). By default \(M = N\).According to
modeand value ofwidth,padder()will addwidth[0] // N“copies” of the signal pluswidth[0] % Nelements before andwidth[1] // N“copies” of the signal pluswidth[1] % Nelements after.For an input
xwith entries \(x_0, x_1, \ldots, x_{N-1}\), the outputy = L @ xdepends on themode.In the general case,
widthis a multiple of input lengthN:with a periodic boundary condition and
width = (N, N), the entries ofyare:\(x_0, x_1, ..., x_{N-1} | x_0, x_1, ..., x_{N-1} | x_0, x_1, ..., x_{N-1}\)
with a symmetric boundary condition and
width = (N, N),yhas entries:\(x_{N-1}, ..., x_1, x_0 | x_0, x_1, ..., x_{N-1} | x_{N-1}, ..., x_1, x_0\)
with an antisymmetric boundary condition and
width = (N, N),yhas entries:\(-x_{N-1}, ..., -x_1, -x_0 | x_0, x_1, ..., x_{N-1} | -x_{N-1}, ..., -x_1, -x_0\)
with a reflect boundary condition and
width = (N - 1, N - 1), the entries ofyare:\(x_{N-1}, ..., x_1 | x_0, x_1, ..., x_{N-1} | x_{N-2}, x_{N-3}, ..., x_0\)
In the case,
widthis lesser than the input lengthNwe have:with a periodic boundary condition and
width = (b, a),b < N - 1anda < N - 1, the entries ofyare:\(x_{N-1-b+1}, ..., x_{N-1} | x_0, x_1, ..., x_{N-1} | x_0, ..., x_a\)
with a symmetric boundary condition and
width = (b, a),b < N - 1anda < N - 1, the entries ofyare:\(x_b, ..., x_0 | x_0, x_1, ..., x_{N-1} | x_{N-1}, ..., x_{N-1-a+1}\)
with an antisymmetric boundary condition and
width = (b, a),b < N - 1anda < N - 1, the entries ofyare:\(-x_b, ..., -x_0 | x_0, x_1, ..., x_{N-1} | -x_{N-1}, ..., -x_{N-1-a+1}\)
with a reflect boundary condition and
width = (b, a),1 < b < N - 2and1 < a < N - 2, the entries ofyare:\(x_b, ..., x_1 | x_0, x_1, ..., x_{N-1} | x_{N-2}, ..., x_{N-1-a}\)
y = L @ XwithXa 2D array (corresponding to an image) will only extend each column of X, since this is the normal behaviour of aLazyLinOp.- Args:
- N:
int Size of the input.
- width:
tuple, optional Number of values padded on both side of the input
(before, after). By default it is equal to(0, 0). The size of the output iswidth[0] + N + width[1].width[0]andwidth[1]must be greater or equal to zero.- mode:
str, optional zero(default),'wrap'/'periodic','symm'/'symmetric','antisymmetric'or'reflect'boundary condition.
- N:
- Returns:
LazyLinOpof shape \((M,~N)\) where \(M = before + N + after\) (default is \((3N,~N)\)).- Examples:
>>> import lazylinop as lz >>> import numpy as np >>> N = 3 >>> x = np.arange(1, N + 1).astype(np.float64) >>> x array([1., 2., 3.]) >>> L = lz.basicops.padder(N, (N, N - 1)) >>> L @ x array([0., 0., 0., 1., 2., 3., 0., 0.]) >>> L = lz.basicops.padder(N, (N, N), mode='periodic') >>> L @ x array([1., 2., 3., 1., 2., 3., 1., 2., 3.]) >>> L = lz.basicops.padder(N, (N, N), mode='symmetric') >>> L @ x array([3., 2., 1., 1., 2., 3., 3., 2., 1.]) >>> L = lz.basicops.padder(N, (1, N), mode='periodic') >>> L @ x array([3., 1., 2., 3., 1., 2., 3.]) >>> L = lz.basicops.padder(N, (2, 1), mode='symmetric') >>> L @ x array([2., 1., 1., 2., 3., 3.]) >>> L = lz.basicops.padder(N, (N + 2, N + 1), mode='symmetric') >>> L @ x array([2., 3., 3., 2., 1., 1., 2., 3., 3., 2., 1., 1.]) >>> L = lz.basicops.padder(N, (N, N), mode='antisymmetric') >>> L @ x array([-3., -2., -1., 1., 2., 3., -3., -2., -1.]) >>> L = lz.basicops.padder(N, (N, N), mode='reflect') >>> L @ x array([2., 3., 2., 1., 2., 3., 2., 1., 2.]) >>> X = np.array([[0., 3.], [1., 4.], [2., 5.]]) >>> X array([[0., 3.], [1., 4.], [2., 5.]]) >>> L = lz.basicops.padder(N, (N + 2, N + 1), mode='symmetric') >>> L @ X array([[1., 4.], [2., 5.], [2., 5.], [1., 4.], [0., 3.], [0., 3.], [1., 4.], [2., 5.], [2., 5.], [1., 4.], [0., 3.], [0., 3.]])
See also
Agglomeration & Transformation#
Functions for creating a Lazylinop from preexisting
linear operators.
- lazylinop.add(*ops)#
Returns a
LazyLinOpLthat acts as a sum of given compatible linear operatorsops.- Args:
- ops:
Operators (
LazyLinOp-s or other compatible linear operators) to sum.
- Returns:
The
LazyLinOpfor the sum ofops.- Examples:
>>> import numpy as np >>> import lazylinop as lz >>> from lazylinop import aslazylinop >>> nt = 10 >>> d = 8 >>> v = np.random.rand(d) >>> terms = [np.ones((d, d)) for i in range(nt)] >>> # terms are all Fausts here >>> ls = lz.add(*terms) # ls is the LazyLinOp add of terms >>> np_sum = 0 >>> for i in range(nt): np_sum += terms[i] >>> np.allclose(ls @ v, nt * np.ones((d, d)) @ v) True
See also
- lazylinop.block_diag(*ops)#
Returns a
LazyLinOpLthat acts as the block-diagonal concatenation of compatible linear operatorsops.- Args:
- ops:
Operators (
LazyLinOp-s or other compatible linear operators) to concatenate block-diagonally.
- Returns:
The resulting block-diagonal
LazyLinOp.- Example:
>>> import numpy as np >>> import lazylinop as lz >>> from lazylinop import aslazylinop >>> import scipy >>> nt = 10 >>> d = 64 >>> v = np.random.rand(d) >>> terms = [np.random.rand(64, 64) for _ in range(10)] >>> ls = lz.block_diag(*terms) # ls is the block diagonal LazyLinOp >>> np.allclose(scipy.linalg.block_diag(*terms), ls.toarray()) True
See also
- lazylinop.hstack(ops)#
Concatenates linear operators horizontally.
- Args:
- ops: (
tupleof compatible linear operators) For any pair
i, j < len(ops),ops[i].shape[0] == ops[i].shape[0].
- ops: (
- Returns:
A concatenation
LazyLinOp.- Example:
>>> import numpy as np >>> import lazylinop as lz >>> from lazylinop import islazylinop >>> A = np.ones((10, 10)) >>> B = lz.ones((10, 2)) >>> lcat = lz.hstack((A, B)) >>> islazylinop(lcat) True >>> np.allclose(lcat.toarray(), np.hstack((A, B.toarray()))) True
See also
- lazylinop.kron(L1, L2)#
Returns the
LazyLinOpfor the Kronecker product \(L_1\otimes L_2\) using as a definition the mixed Kronecker matrix-vector property\[\begin{equation} (L_2^T\otimes L_1)x=\mathtt{vec}(L_1XL_2) \end{equation}\]where \(X\) is a matrix of appropriate dimension such that \(\mathtt{vec}(X)=x\). Importantly here \(\mathtt{vec}(X)\) stacks the columns of \(X\).
Note
This specialization is particularly optimized for multiplying the operator by a vector.
- Args:
- L1: (compatible linear operator)
scaling factor,
- L2: (compatible linear operator)
block factor.
- Returns:
The Kronecker product
LazyLinOp.- Example:
>>> import numpy as np >>> import lazylinop as lz >>> A = np.random.randn(100, 100) >>> B = np.random.randn(100, 100) >>> AxB = np.kron(A, B) >>> lAxB = lz.kron(A, B) >>> x = np.random.randn(AxB.shape[1], 1) >>> print(np.allclose(AxB@x, lAxB@x)) True >>> from timeit import timeit >>> timeit(lambda: AxB @ x, number=10) 0... >>> # example: 0.4692082800902426 >>> timeit(lambda: lAxB @ x, number=10) 0... >>> # example 0.03464869409799576
- lazylinop.pad(op, pad_width, mode='constant', constant_values=0)#
Returns a
LazyLinOpLthat acts as a padded version of a given compatible linear operatorop.- Args:
- op: (
scipy LinearOperator,LazyLinOperator,numpy array,torch.Tensor) The operator/array to pad.
- pad_width: (
tuple,list) Number of values padded to the edges of each axis.
((B0, A0), (B1, A1))(See Figure Padding format).(B, A)is equivalent to((B, A), (B, A)).((B0, ), (B1, ))is equivalent to((B0, B0), (B1, B1)).(B, )is equivalent to((B, B), (B, B)).Cis equivalent to((C, C), (C, C)).
- mode: (
str) 'constant':Pads with a constant value.
'symmetric':Pads with the reflection of the vector mirrored along the edge of the array.
'antisymmetric':Pads with the reflection of the vector mirrored and negated along the edge of the array.
'reflect':Pads with the reflection of the vector mirrored on the first and last values of the vector along each axis.
'mean':Pads with the mean value of all the vector along each axis.
'edge':Pads with the edge values of
LazyLinOp.
'wrap':Pads with the wrap of the vector along the axis. The first values are used to pad the end and the end values are used to pad the beginning.
- constant_values: (
tuple,list,scalar) The padded values for each axis (in
mode='constant').((VB0, VA0),(VB1, VA1)): padding values before (VBi) and values after (VAi) on each dimension. In Figure Padding format valueVBi(resp.VAi) goes where padding widthBi(resp.Ai) is.((VB0, VA0))is equivalent to((VB0, VA0), (VB0, VA0)).(V,)orVis equivalent to((V, V), (V, V)).((VB0,), (VB1,))is equivalent to((VB0, VB0), (VB1, VB1)).
- op: (
Padding format (for an operator
op)#- Example
mode='constant': >>> import lazylinop as lz >>> import numpy as np >>> A = np.arange(18 * 2).reshape((18, 2)) >>> A array([[ 0, 1], [ 2, 3], [ 4, 5], [ 6, 7], [ 8, 9], [10, 11], [12, 13], [14, 15], [16, 17], [18, 19], [20, 21], [22, 23], [24, 25], [26, 27], [28, 29], [30, 31], [32, 33], [34, 35]]) >>> lpA = lz.pad(A, (2, 3)) >>> lpA <23x7 LazyLinOp with unspecified dtype> >>> lpA.toarray().astype(int) array([[ 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 1, 0, 0, 0], [ 0, 0, 2, 3, 0, 0, 0], [ 0, 0, 4, 5, 0, 0, 0], [ 0, 0, 6, 7, 0, 0, 0], [ 0, 0, 8, 9, 0, 0, 0], [ 0, 0, 10, 11, 0, 0, 0], [ 0, 0, 12, 13, 0, 0, 0], [ 0, 0, 14, 15, 0, 0, 0], [ 0, 0, 16, 17, 0, 0, 0], [ 0, 0, 18, 19, 0, 0, 0], [ 0, 0, 20, 21, 0, 0, 0], [ 0, 0, 22, 23, 0, 0, 0], [ 0, 0, 24, 25, 0, 0, 0], [ 0, 0, 26, 27, 0, 0, 0], [ 0, 0, 28, 29, 0, 0, 0], [ 0, 0, 30, 31, 0, 0, 0], [ 0, 0, 32, 33, 0, 0, 0], [ 0, 0, 34, 35, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0]]) >>> lpA2 = lz.pad(A, ((2, 3), (4, 1))) >>> lpA2 <23x7 LazyLinOp with unspecified dtype> >>> lpA2.toarray().astype('int') array([[ 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 1, 0], [ 0, 0, 0, 0, 2, 3, 0], [ 0, 0, 0, 0, 4, 5, 0], [ 0, 0, 0, 0, 6, 7, 0], [ 0, 0, 0, 0, 8, 9, 0], [ 0, 0, 0, 0, 10, 11, 0], [ 0, 0, 0, 0, 12, 13, 0], [ 0, 0, 0, 0, 14, 15, 0], [ 0, 0, 0, 0, 16, 17, 0], [ 0, 0, 0, 0, 18, 19, 0], [ 0, 0, 0, 0, 20, 21, 0], [ 0, 0, 0, 0, 22, 23, 0], [ 0, 0, 0, 0, 24, 25, 0], [ 0, 0, 0, 0, 26, 27, 0], [ 0, 0, 0, 0, 28, 29, 0], [ 0, 0, 0, 0, 30, 31, 0], [ 0, 0, 0, 0, 32, 33, 0], [ 0, 0, 0, 0, 34, 35, 0], [ 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0], [ 0, 0, 0, 0, 0, 0, 0]]) >>> # the same with arbitrary values >>> pw = ((2, 3), (4, 1)) >>> cv = ((-1, -2), (-3, -4)) >>> lpA3 = lz.pad(A, pw, constant_values=cv) >>> lpA3 <23x7 LazyLinOp with unspecified dtype> >>> lpA3.toarray().astype('int') array([[-3, -3, -3, -3, -1, -1, -4], [-3, -3, -3, -3, -1, -1, -4], [-3, -3, -3, -3, 0, 1, -4], [-3, -3, -3, -3, 2, 3, -4], [-3, -3, -3, -3, 4, 5, -4], [-3, -3, -3, -3, 6, 7, -4], [-3, -3, -3, -3, 8, 9, -4], [-3, -3, -3, -3, 10, 11, -4], [-3, -3, -3, -3, 12, 13, -4], [-3, -3, -3, -3, 14, 15, -4], [-3, -3, -3, -3, 16, 17, -4], [-3, -3, -3, -3, 18, 19, -4], [-3, -3, -3, -3, 20, 21, -4], [-3, -3, -3, -3, 22, 23, -4], [-3, -3, -3, -3, 24, 25, -4], [-3, -3, -3, -3, 26, 27, -4], [-3, -3, -3, -3, 28, 29, -4], [-3, -3, -3, -3, 30, 31, -4], [-3, -3, -3, -3, 32, 33, -4], [-3, -3, -3, -3, 34, 35, -4], [-3, -3, -3, -3, -2, -2, -4], [-3, -3, -3, -3, -2, -2, -4], [-3, -3, -3, -3, -2, -2, -4]])
- zero-padded DFT example:
>>> import lazylinop as lz >>> from lazylinop.signal import fft >>> e = lz.eye(5) >>> pe = lz.pad(e, (0, 3)) >>> pfft = fft(8) @ pe
- Example
mode='symmetric',mode='reflect': >>> import lazylinop as lz >>> a = np.arange(25).reshape(5, 5) >>> sp_a = lz.pad(a, (2, 1), mode='symmetric') >>> print(sp_a) <8x8 LazyLinOp with unspecified dtype> >>> sp_a.toarray().astype('int') array([[ 6, 5, 5, 6, 7, 8, 9, 9], [ 1, 0, 0, 1, 2, 3, 4, 4], [ 1, 0, 0, 1, 2, 3, 4, 4], [ 6, 5, 5, 6, 7, 8, 9, 9], [11, 10, 10, 11, 12, 13, 14, 14], [16, 15, 15, 16, 17, 18, 19, 19], [21, 20, 20, 21, 22, 23, 24, 24], [21, 20, 20, 21, 22, 23, 24, 24]]) >>> sp_a2 = lz.pad(a, ((1, 1), (2, 1)), mode='symmetric') >>> print(sp_a2) <7x8 LazyLinOp with unspecified dtype> >>> sp_a2.toarray().astype('int') array([[ 1, 0, 0, 1, 2, 3, 4, 4], [ 1, 0, 0, 1, 2, 3, 4, 4], [ 6, 5, 5, 6, 7, 8, 9, 9], [11, 10, 10, 11, 12, 13, 14, 14], [16, 15, 15, 16, 17, 18, 19, 19], [21, 20, 20, 21, 22, 23, 24, 24], [21, 20, 20, 21, 22, 23, 24, 24]]) >>> rp_a = lz.pad(a, (2, 1), mode='reflect') >>> print(rp_a) <8x8 LazyLinOp with unspecified dtype> >>> rp_a.toarray().astype('int') array([[12, 11, 10, 11, 12, 13, 14, 13], [ 7, 6, 5, 6, 7, 8, 9, 8], [ 2, 1, 0, 1, 2, 3, 4, 3], [ 7, 6, 5, 6, 7, 8, 9, 8], [12, 11, 10, 11, 12, 13, 14, 13], [17, 16, 15, 16, 17, 18, 19, 18], [22, 21, 20, 21, 22, 23, 24, 23], [17, 16, 15, 16, 17, 18, 19, 18]]) >>> rp_a2 = lz.pad(a, ((1, 1), (2, 1)), mode='reflect') >>> print(rp_a2) <7x8 LazyLinOp with unspecified dtype> >>> rp_a2.toarray().astype('int') array([[ 7, 6, 5, 6, 7, 8, 9, 8], [ 2, 1, 0, 1, 2, 3, 4, 3], [ 7, 6, 5, 6, 7, 8, 9, 8], [12, 11, 10, 11, 12, 13, 14, 13], [17, 16, 15, 16, 17, 18, 19, 18], [22, 21, 20, 21, 22, 23, 24, 23], [17, 16, 15, 16, 17, 18, 19, 18]])
- Example
mode='mean': >>> import lazylinop as lz >>> a = np.arange(25).reshape(5, 5) >>> mp_a = lz.pad(a, (2, 1), mode='mean') >>> print(mp_a) <8x8 LazyLinOp with unspecified dtype> >>> mp_a.toarray() array([[12., 12., 10., 11., 12., 13., 14., 12.], [12., 12., 10., 11., 12., 13., 14., 12.], [ 2., 2., 0., 1., 2., 3., 4., 2.], [ 7., 7., 5., 6., 7., 8., 9., 7.], [12., 12., 10., 11., 12., 13., 14., 12.], [17., 17., 15., 16., 17., 18., 19., 17.], [22., 22., 20., 21., 22., 23., 24., 22.], [12., 12., 10., 11., 12., 13., 14., 12.]]) >>> mp_a2 = lz.pad(a, ((1, 1), (2, 1)), mode='mean') >>> print(mp_a2) <7x8 LazyLinOp with unspecified dtype> >>> mp_a2.toarray() array([[12., 12., 10., 11., 12., 13., 14., 12.], [ 2., 2., 0., 1., 2., 3., 4., 2.], [ 7., 7., 5., 6., 7., 8., 9., 7.], [12., 12., 10., 11., 12., 13., 14., 12.], [17., 17., 15., 16., 17., 18., 19., 17.], [22., 22., 20., 21., 22., 23., 24., 22.], [12., 12., 10., 11., 12., 13., 14., 12.]])
- Example
mode='edge': >>> import lazylinop as lz >>> a = np.arange(25).reshape(5, 5) >>> ep_a = lz.pad(a, (2, 1), mode='edge') >>> print(ep_a) <8x8 LazyLinOp with unspecified dtype> >>> y = ep_a.toarray().astype('int') >>> y array([[ 0, 0, 0, 1, 2, 3, 4, 4], [ 0, 0, 0, 1, 2, 3, 4, 4], [ 0, 0, 0, 1, 2, 3, 4, 4], [ 5, 5, 5, 6, 7, 8, 9, 9], [10, 10, 10, 11, 12, 13, 14, 14], [15, 15, 15, 16, 17, 18, 19, 19], [20, 20, 20, 21, 22, 23, 24, 24], [20, 20, 20, 21, 22, 23, 24, 24]]) >>> z = np.pad(a, (2, 1), mode='edge') >>> np.allclose(y, z) True >>> ep_a2 = lz.pad(a, ((1, 1), (2, 1)), mode='edge') >>> print(ep_a2) <7x8 LazyLinOp with unspecified dtype> >>> ep_a2.toarray().astype('int') array([[ 0, 0, 0, 1, 2, 3, 4, 4], [ 0, 0, 0, 1, 2, 3, 4, 4], [ 5, 5, 5, 6, 7, 8, 9, 9], [10, 10, 10, 11, 12, 13, 14, 14], [15, 15, 15, 16, 17, 18, 19, 19], [20, 20, 20, 21, 22, 23, 24, 24], [20, 20, 20, 21, 22, 23, 24, 24]])
- Example
mode='wrap': >>> import lazylinop as lz >>> a = np.arange(25).reshape(5, 5) >>> wp_a = lz.pad(a, (2, 1), mode='wrap') >>> print(wp_a) <8x8 LazyLinOp with unspecified dtype> >>> wp_a.toarray().astype('int') array([[18, 19, 15, 16, 17, 18, 19, 15], [23, 24, 20, 21, 22, 23, 24, 20], [ 3, 4, 0, 1, 2, 3, 4, 0], [ 8, 9, 5, 6, 7, 8, 9, 5], [13, 14, 10, 11, 12, 13, 14, 10], [18, 19, 15, 16, 17, 18, 19, 15], [23, 24, 20, 21, 22, 23, 24, 20], [ 3, 4, 0, 1, 2, 3, 4, 0]]) >>> wp_a2 = lz.pad(a, ((1, 1), (2, 1)), mode='wrap') >>> print(wp_a2) <7x8 LazyLinOp with unspecified dtype> >>> wp_a2.toarray().astype('int') array([[23, 24, 20, 21, 22, 23, 24, 20], [ 3, 4, 0, 1, 2, 3, 4, 0], [ 8, 9, 5, 6, 7, 8, 9, 5], [13, 14, 10, 11, 12, 13, 14, 10], [18, 19, 15, 16, 17, 18, 19, 15], [23, 24, 20, 21, 22, 23, 24, 20], [ 3, 4, 0, 1, 2, 3, 4, 0]])
See also
- lazylinop.vstack(ops)#
Concatenates linear operators horizontally.
- Args:
- ops: (
tupleof compatible linear operators) For any pair
i, j < len(ops),ops[i].shape[1] == ops[i].shape[1].
- ops: (
- Returns:
A concatenation
LazyLinOp.- Example:
>>> import numpy as np >>> import lazylinop as lz >>> from lazylinop import islazylinop >>> A = np.ones((10, 10)) >>> B = lz.ones((2, 10)) >>> lcat = lz.vstack((A, B)) >>> islazylinop(lcat) True >>> np.allclose(lcat.toarray(), np.vstack((A, B.toarray()))) True
See also
Slicing and indexing operators#
- lazylinop.basicops.slicer(N, start=None, stop=None, step=None)#
Returns a
LazyLinOp' ``L`that extracts slices from a vector of size N, such that with:y = slicer(N, start, stop, step) @ xy is the vector formed by concatenation of several x slices given by each element of start/stop/step parameters, i.e:
y = [ x[start[0]:stop[0]:step[0]] … x[start[i]:stop[i]:step[i]] … ]where
start[i], stop[i], step[i]are interpreted in the same way as in usual Pythonsliceparameters (i.e. they can be a positive or negative integer or None).If start, stop and step are integers (or None), a single slicing is performed, i.e:
slicer(N, 0, 10, 2) @ x == x[0:10:2]A mix of arrays and integers (or None) may be provided for start/stop/step parameters. In that case, each integer parameter is expanded into an array filled with its value of the same length as the arrays provided in other parameters. (Note: all provided arrays must have the same length)
- Args:
- N:
int Length of the input.
- start:
intornp.ndarray The slice’s first element or slices’ list of first elements. Each
startelement is interpreted in the same way as Pythonslice()’sstartparameter. Default is None.- stop:
intornp.ndarray The slice’s stop index or slices’ list of stop index. Each
stopelement is interpreted in the same way as Pythonslice()’sstopparameter. Default is None.- step:
intornp.ndarray The slice’s stride or slices’s list of strides to be used. Each
stopelement is interpreted in the same way as Pythonslice()’sstepparameter. Default is None.
- N:
- Returns:
The slices
LazyLinOp.- Examples:
>>> import numpy as np >>> import lazylinop as lz >>> N = 10 >>> x = np.arange(N) >>> x array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) >>> # use integers to extract a single slice >>> lz.basicops.slicer(N, 5, 8) @ x array([5, 6, 7]) >>> # use list to extract multiple slices >>> lz.basicops.slicer(N, [0, 5], [2, 8]) @ x array([0, 1, 5, 6, 7]) >>> # pick one every two elements >>> lz.basicops.slicer(N, step=2) @ x array([0, 2, 4, 6, 8]) >>> # same example using a batch, in reverse order >>> X = np.arange(3*N).reshape((N, 3)) >>> X array([[ 0, 1, 2], [ 3, 4, 5], [ 6, 7, 8], [ 9, 10, 11], [12, 13, 14], [15, 16, 17], [18, 19, 20], [21, 22, 23], [24, 25, 26], [27, 28, 29]]) >>> lz.basicops.slicer(N, step=-2) @ X array([[27, 28, 29], [21, 22, 23], [15, 16, 17], [ 9, 10, 11], [ 3, 4, 5]]) >>> # windows of 5 elements every 2 elements across the vector >>> lz.basicops.slicer(N, np.arange(0, 5, 2), np.arange(5, N, 2)) @ x array([0, 1, 2, 3, 4, 2, 3, 4, 5, 6, 4, 5, 6, 7, 8])
- lazylinop.basicops.indexer(N, index)#
Returns a
LazyLinOp' ``L`that extracts elements from a vector of size N, such that withIan array of integers or booleans:indexer(N, I) @ x == x[I]- Args:
- N:
int Length of the input.
- index: An
array-compatible of integers or booleans. If
indexis an array of integers, it reprensents the indices of the element of the input vector to select.If
indexin an array of booleans,indexer()will returns the input vector elements whose indices areTrueinindex. In that caseindexsize must be N or 1.
- N:
- Returns:
The indexer
LazyLinOp.- Examples:
>>> import numpy as np >>> import lazylinop as lz >>> N = 10 >>> x = np.arange(N) >>> x array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) >>> # use integers to extract elements >>> lz.indexer(N, [2, 1, 1]) @ x array([2, 1, 1]) >>> # use booleans to extract elements >>> lz.indexer(N, x > 6) @ x array([7, 8, 9])
Permutations operators#
The following functions return LazyLinOp’s that implement permutations. These operators are all of shape \(\left(N,~N\right)\) and orthonormal.
- lazylinop.basicops.bitrev(N)#
Returns a
LazyLinOpLfor the bit-reversal permutation. Size of the signalNmust be a power of two. Bit-reversal permutation maps each item of the sequence0toN - 1to the item whose bit representation has the same bits in reversed order.- Args:
- N:
int Size of the signal.
- N:
- Returns:
- Example:
>>> import numpy as np >>> from lazylinop.basicops import bitrev >>> x = np.arange(4) >>> L = bitrev(4) >>> L @ x array([0, 2, 1, 3])
- References:
- [1] Fast Bit-Reversal Algorithms, Anne Cathrine Elster.
IEEE International Conf. on Acoustics, Speech, and Signal Processing 1989 (ICASSP’89), Vol. 2, pp. 1099-1102, May 1989.
- lazylinop.basicops.flip(N, start=0, end=None)#
Returns a
LazyLinOpLthat flips an input array or a sub-interval of the array.For an input
xwith entries \(x_0, x_1, \ldots, x_{N - 1}\), the result ofy = L @ xis:with defaults parameters, the entries of
yare:\(x_{N - 1}, x_{N - 2}, \ldots, x_0\)
with
start = aandend = b(a < b), the entries ofyare:\(x_0, x_1, \ldots, x_{b - 1}, \ldots, x_a, x_b, \ldots, x_{N - 1}\)
Shape of
Lis \((N,~N)\).- Args:
- N:
int Size of the input.
- start:
int, optional Start to flip from this value (default is 0).
- end:
int, optional Stop to flip (not included, default is None).
- N:
- Returns:
- Examples:
>>> import numpy as np >>> from lazylinop.basicops import flip >>> N = 6 >>> x = np.arange(N) >>> x array([0, 1, 2, 3, 4, 5]) >>> y = flip(N, 0, 5) @ x >>> y array([4, 3, 2, 1, 0, 5]) >>> z = flip(N, 2, 4) @ x >>> z array([0, 1, 3, 2, 4, 5]) >>> X = np.eye(6, 5) >>> X array([[1., 0., 0., 0., 0.], [0., 1., 0., 0., 0.], [0., 0., 1., 0., 0.], [0., 0., 0., 1., 0.], [0., 0., 0., 0., 1.], [0., 0., 0., 0., 0.]]) >>> flip(N, 1, 4) @ X array([[1., 0., 0., 0., 0.], [0., 0., 0., 1., 0.], [0., 0., 1., 0., 0.], [0., 1., 0., 0., 0.], [0., 0., 0., 0., 1.], [0., 0., 0., 0., 0.]])
- lazylinop.basicops.roll(N, shift=0)#
Returns a
LazyLinOpfor rolling the elements of a vectorx.The elements that roll beyond the first position (resp. the last) re-enter at the last (resp. the first). Rolling of \(x=\left(x_0,x_1,\cdots,x_{N-1}\right)\) with
shift=sis: \(x_{s}=\left(x_s,\cdots,x_{N-1},x_0,\cdots,x_{s-1}\right)\).- Args:
- N:
int Size of the input.
- shift:
int, optional Shift the elements by this number (to the left or to the right).
If negative, shift to the left.
If positive, shift to the right.
If zero (default), do nothing.
- N:
- Returns:
See also
- Examples:
>>> import numpy as np >>> import lazylinop as lz >>> x = np.arange(4) >>> L = roll(4, 2) >>> y = L @ x >>> np.allclose(np.array([2, 3, 0, 1]), y) True >>> L = roll(4, -1) >>> y = L @ x >>> np.allclose(np.array([1, 2, 3, 0]), y) True
version 1.24.11 documentation