Coverage for /home/martinb/.local/share/virtualenvs/camcops/lib/python3.6/site-packages/scipy/sparse/linalg/interface.py : 29%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""Abstract linear algebra library.
3This module defines a class hierarchy that implements a kind of "lazy"
4matrix representation, called the ``LinearOperator``. It can be used to do
5linear algebra with extremely large sparse or structured matrices, without
6representing those explicitly in memory. Such matrices can be added,
7multiplied, transposed, etc.
9As a motivating example, suppose you want have a matrix where almost all of
10the elements have the value one. The standard sparse matrix representation
11skips the storage of zeros, but not ones. By contrast, a LinearOperator is
12able to represent such matrices efficiently. First, we need a compact way to
13represent an all-ones matrix::
15 >>> import numpy as np
16 >>> class Ones(LinearOperator):
17 ... def __init__(self, shape):
18 ... super(Ones, self).__init__(dtype=None, shape=shape)
19 ... def _matvec(self, x):
20 ... return np.repeat(x.sum(), self.shape[0])
22Instances of this class emulate ``np.ones(shape)``, but using a constant
23amount of storage, independent of ``shape``. The ``_matvec`` method specifies
24how this linear operator multiplies with (operates on) a vector. We can now
25add this operator to a sparse matrix that stores only offsets from one::
27 >>> from scipy.sparse import csr_matrix
28 >>> offsets = csr_matrix([[1, 0, 2], [0, -1, 0], [0, 0, 3]])
29 >>> A = aslinearoperator(offsets) + Ones(offsets.shape)
30 >>> A.dot([1, 2, 3])
31 array([13, 4, 15])
33The result is the same as that given by its dense, explicitly-stored
34counterpart::
36 >>> (np.ones(A.shape, A.dtype) + offsets.toarray()).dot([1, 2, 3])
37 array([13, 4, 15])
39Several algorithms in the ``scipy.sparse`` library are able to operate on
40``LinearOperator`` instances.
41"""
43import warnings
45import numpy as np
47from scipy.sparse import isspmatrix
48from scipy.sparse.sputils import isshape, isintlike, asmatrix, is_pydata_spmatrix
50__all__ = ['LinearOperator', 'aslinearoperator']
53class LinearOperator(object):
54 """Common interface for performing matrix vector products
56 Many iterative methods (e.g. cg, gmres) do not need to know the
57 individual entries of a matrix to solve a linear system A*x=b.
58 Such solvers only require the computation of matrix vector
59 products, A*v where v is a dense vector. This class serves as
60 an abstract interface between iterative solvers and matrix-like
61 objects.
63 To construct a concrete LinearOperator, either pass appropriate
64 callables to the constructor of this class, or subclass it.
66 A subclass must implement either one of the methods ``_matvec``
67 and ``_matmat``, and the attributes/properties ``shape`` (pair of
68 integers) and ``dtype`` (may be None). It may call the ``__init__``
69 on this class to have these attributes validated. Implementing
70 ``_matvec`` automatically implements ``_matmat`` (using a naive
71 algorithm) and vice-versa.
73 Optionally, a subclass may implement ``_rmatvec`` or ``_adjoint``
74 to implement the Hermitian adjoint (conjugate transpose). As with
75 ``_matvec`` and ``_matmat``, implementing either ``_rmatvec`` or
76 ``_adjoint`` implements the other automatically. Implementing
77 ``_adjoint`` is preferable; ``_rmatvec`` is mostly there for
78 backwards compatibility.
80 Parameters
81 ----------
82 shape : tuple
83 Matrix dimensions (M, N).
84 matvec : callable f(v)
85 Returns returns A * v.
86 rmatvec : callable f(v)
87 Returns A^H * v, where A^H is the conjugate transpose of A.
88 matmat : callable f(V)
89 Returns A * V, where V is a dense matrix with dimensions (N, K).
90 dtype : dtype
91 Data type of the matrix.
92 rmatmat : callable f(V)
93 Returns A^H * V, where V is a dense matrix with dimensions (M, K).
95 Attributes
96 ----------
97 args : tuple
98 For linear operators describing products etc. of other linear
99 operators, the operands of the binary operation.
100 ndim : int
101 Number of dimensions (this is always 2)
103 See Also
104 --------
105 aslinearoperator : Construct LinearOperators
107 Notes
108 -----
109 The user-defined matvec() function must properly handle the case
110 where v has shape (N,) as well as the (N,1) case. The shape of
111 the return type is handled internally by LinearOperator.
113 LinearOperator instances can also be multiplied, added with each
114 other and exponentiated, all lazily: the result of these operations
115 is always a new, composite LinearOperator, that defers linear
116 operations to the original operators and combines the results.
118 More details regarding how to subclass a LinearOperator and several
119 examples of concrete LinearOperator instances can be found in the
120 external project `PyLops <https://pylops.readthedocs.io>`_.
123 Examples
124 --------
125 >>> import numpy as np
126 >>> from scipy.sparse.linalg import LinearOperator
127 >>> def mv(v):
128 ... return np.array([2*v[0], 3*v[1]])
129 ...
130 >>> A = LinearOperator((2,2), matvec=mv)
131 >>> A
132 <2x2 _CustomLinearOperator with dtype=float64>
133 >>> A.matvec(np.ones(2))
134 array([ 2., 3.])
135 >>> A * np.ones(2)
136 array([ 2., 3.])
138 """
140 ndim = 2
142 def __new__(cls, *args, **kwargs):
143 if cls is LinearOperator:
144 # Operate as _CustomLinearOperator factory.
145 return super(LinearOperator, cls).__new__(_CustomLinearOperator)
146 else:
147 obj = super(LinearOperator, cls).__new__(cls)
149 if (type(obj)._matvec == LinearOperator._matvec
150 and type(obj)._matmat == LinearOperator._matmat):
151 warnings.warn("LinearOperator subclass should implement"
152 " at least one of _matvec and _matmat.",
153 category=RuntimeWarning, stacklevel=2)
155 return obj
157 def __init__(self, dtype, shape):
158 """Initialize this LinearOperator.
160 To be called by subclasses. ``dtype`` may be None; ``shape`` should
161 be convertible to a length-2 tuple.
162 """
163 if dtype is not None:
164 dtype = np.dtype(dtype)
166 shape = tuple(shape)
167 if not isshape(shape):
168 raise ValueError("invalid shape %r (must be 2-d)" % (shape,))
170 self.dtype = dtype
171 self.shape = shape
173 def _init_dtype(self):
174 """Called from subclasses at the end of the __init__ routine.
175 """
176 if self.dtype is None:
177 v = np.zeros(self.shape[-1])
178 self.dtype = np.asarray(self.matvec(v)).dtype
180 def _matmat(self, X):
181 """Default matrix-matrix multiplication handler.
183 Falls back on the user-defined _matvec method, so defining that will
184 define matrix multiplication (though in a very suboptimal way).
185 """
187 return np.hstack([self.matvec(col.reshape(-1,1)) for col in X.T])
189 def _matvec(self, x):
190 """Default matrix-vector multiplication handler.
192 If self is a linear operator of shape (M, N), then this method will
193 be called on a shape (N,) or (N, 1) ndarray, and should return a
194 shape (M,) or (M, 1) ndarray.
196 This default implementation falls back on _matmat, so defining that
197 will define matrix-vector multiplication as well.
198 """
199 return self.matmat(x.reshape(-1, 1))
201 def matvec(self, x):
202 """Matrix-vector multiplication.
204 Performs the operation y=A*x where A is an MxN linear
205 operator and x is a column vector or 1-d array.
207 Parameters
208 ----------
209 x : {matrix, ndarray}
210 An array with shape (N,) or (N,1).
212 Returns
213 -------
214 y : {matrix, ndarray}
215 A matrix or ndarray with shape (M,) or (M,1) depending
216 on the type and shape of the x argument.
218 Notes
219 -----
220 This matvec wraps the user-specified matvec routine or overridden
221 _matvec method to ensure that y has the correct shape and type.
223 """
225 x = np.asanyarray(x)
227 M,N = self.shape
229 if x.shape != (N,) and x.shape != (N,1):
230 raise ValueError('dimension mismatch')
232 y = self._matvec(x)
234 if isinstance(x, np.matrix):
235 y = asmatrix(y)
236 else:
237 y = np.asarray(y)
239 if x.ndim == 1:
240 y = y.reshape(M)
241 elif x.ndim == 2:
242 y = y.reshape(M,1)
243 else:
244 raise ValueError('invalid shape returned by user-defined matvec()')
246 return y
248 def rmatvec(self, x):
249 """Adjoint matrix-vector multiplication.
251 Performs the operation y = A^H * x where A is an MxN linear
252 operator and x is a column vector or 1-d array.
254 Parameters
255 ----------
256 x : {matrix, ndarray}
257 An array with shape (M,) or (M,1).
259 Returns
260 -------
261 y : {matrix, ndarray}
262 A matrix or ndarray with shape (N,) or (N,1) depending
263 on the type and shape of the x argument.
265 Notes
266 -----
267 This rmatvec wraps the user-specified rmatvec routine or overridden
268 _rmatvec method to ensure that y has the correct shape and type.
270 """
272 x = np.asanyarray(x)
274 M,N = self.shape
276 if x.shape != (M,) and x.shape != (M,1):
277 raise ValueError('dimension mismatch')
279 y = self._rmatvec(x)
281 if isinstance(x, np.matrix):
282 y = asmatrix(y)
283 else:
284 y = np.asarray(y)
286 if x.ndim == 1:
287 y = y.reshape(N)
288 elif x.ndim == 2:
289 y = y.reshape(N,1)
290 else:
291 raise ValueError('invalid shape returned by user-defined rmatvec()')
293 return y
295 def _rmatvec(self, x):
296 """Default implementation of _rmatvec; defers to adjoint."""
297 if type(self)._adjoint == LinearOperator._adjoint:
298 # _adjoint not overridden, prevent infinite recursion
299 raise NotImplementedError
300 else:
301 return self.H.matvec(x)
303 def matmat(self, X):
304 """Matrix-matrix multiplication.
306 Performs the operation y=A*X where A is an MxN linear
307 operator and X dense N*K matrix or ndarray.
309 Parameters
310 ----------
311 X : {matrix, ndarray}
312 An array with shape (N,K).
314 Returns
315 -------
316 Y : {matrix, ndarray}
317 A matrix or ndarray with shape (M,K) depending on
318 the type of the X argument.
320 Notes
321 -----
322 This matmat wraps any user-specified matmat routine or overridden
323 _matmat method to ensure that y has the correct type.
325 """
327 X = np.asanyarray(X)
329 if X.ndim != 2:
330 raise ValueError('expected 2-d ndarray or matrix, not %d-d'
331 % X.ndim)
333 if X.shape[0] != self.shape[1]:
334 raise ValueError('dimension mismatch: %r, %r'
335 % (self.shape, X.shape))
337 Y = self._matmat(X)
339 if isinstance(Y, np.matrix):
340 Y = asmatrix(Y)
342 return Y
344 def rmatmat(self, X):
345 """Adjoint matrix-matrix multiplication.
347 Performs the operation y = A^H * x where A is an MxN linear
348 operator and x is a column vector or 1-d array, or 2-d array.
349 The default implementation defers to the adjoint.
351 Parameters
352 ----------
353 X : {matrix, ndarray}
354 A matrix or 2D array.
356 Returns
357 -------
358 Y : {matrix, ndarray}
359 A matrix or 2D array depending on the type of the input.
361 Notes
362 -----
363 This rmatmat wraps the user-specified rmatmat routine.
365 """
367 X = np.asanyarray(X)
369 if X.ndim != 2:
370 raise ValueError('expected 2-d ndarray or matrix, not %d-d'
371 % X.ndim)
373 if X.shape[0] != self.shape[0]:
374 raise ValueError('dimension mismatch: %r, %r'
375 % (self.shape, X.shape))
377 Y = self._rmatmat(X)
378 if isinstance(Y, np.matrix):
379 Y = asmatrix(Y)
380 return Y
382 def _rmatmat(self, X):
383 """Default implementation of _rmatmat defers to rmatvec or adjoint."""
384 if type(self)._adjoint == LinearOperator._adjoint:
385 return np.hstack([self.rmatvec(col.reshape(-1, 1)) for col in X.T])
386 else:
387 return self.H.matmat(X)
389 def __call__(self, x):
390 return self*x
392 def __mul__(self, x):
393 return self.dot(x)
395 def dot(self, x):
396 """Matrix-matrix or matrix-vector multiplication.
398 Parameters
399 ----------
400 x : array_like
401 1-d or 2-d array, representing a vector or matrix.
403 Returns
404 -------
405 Ax : array
406 1-d or 2-d array (depending on the shape of x) that represents
407 the result of applying this linear operator on x.
409 """
410 if isinstance(x, LinearOperator):
411 return _ProductLinearOperator(self, x)
412 elif np.isscalar(x):
413 return _ScaledLinearOperator(self, x)
414 else:
415 x = np.asarray(x)
417 if x.ndim == 1 or x.ndim == 2 and x.shape[1] == 1:
418 return self.matvec(x)
419 elif x.ndim == 2:
420 return self.matmat(x)
421 else:
422 raise ValueError('expected 1-d or 2-d array or matrix, got %r'
423 % x)
425 def __matmul__(self, other):
426 if np.isscalar(other):
427 raise ValueError("Scalar operands are not allowed, "
428 "use '*' instead")
429 return self.__mul__(other)
431 def __rmatmul__(self, other):
432 if np.isscalar(other):
433 raise ValueError("Scalar operands are not allowed, "
434 "use '*' instead")
435 return self.__rmul__(other)
437 def __rmul__(self, x):
438 if np.isscalar(x):
439 return _ScaledLinearOperator(self, x)
440 else:
441 return NotImplemented
443 def __pow__(self, p):
444 if np.isscalar(p):
445 return _PowerLinearOperator(self, p)
446 else:
447 return NotImplemented
449 def __add__(self, x):
450 if isinstance(x, LinearOperator):
451 return _SumLinearOperator(self, x)
452 else:
453 return NotImplemented
455 def __neg__(self):
456 return _ScaledLinearOperator(self, -1)
458 def __sub__(self, x):
459 return self.__add__(-x)
461 def __repr__(self):
462 M,N = self.shape
463 if self.dtype is None:
464 dt = 'unspecified dtype'
465 else:
466 dt = 'dtype=' + str(self.dtype)
468 return '<%dx%d %s with %s>' % (M, N, self.__class__.__name__, dt)
470 def adjoint(self):
471 """Hermitian adjoint.
473 Returns the Hermitian adjoint of self, aka the Hermitian
474 conjugate or Hermitian transpose. For a complex matrix, the
475 Hermitian adjoint is equal to the conjugate transpose.
477 Can be abbreviated self.H instead of self.adjoint().
479 Returns
480 -------
481 A_H : LinearOperator
482 Hermitian adjoint of self.
483 """
484 return self._adjoint()
486 H = property(adjoint)
488 def transpose(self):
489 """Transpose this linear operator.
491 Returns a LinearOperator that represents the transpose of this one.
492 Can be abbreviated self.T instead of self.transpose().
493 """
494 return self._transpose()
496 T = property(transpose)
498 def _adjoint(self):
499 """Default implementation of _adjoint; defers to rmatvec."""
500 return _AdjointLinearOperator(self)
502 def _transpose(self):
503 """ Default implementation of _transpose; defers to rmatvec + conj"""
504 return _TransposedLinearOperator(self)
507class _CustomLinearOperator(LinearOperator):
508 """Linear operator defined in terms of user-specified operations."""
510 def __init__(self, shape, matvec, rmatvec=None, matmat=None,
511 dtype=None, rmatmat=None):
512 super(_CustomLinearOperator, self).__init__(dtype, shape)
514 self.args = ()
516 self.__matvec_impl = matvec
517 self.__rmatvec_impl = rmatvec
518 self.__rmatmat_impl = rmatmat
519 self.__matmat_impl = matmat
521 self._init_dtype()
523 def _matmat(self, X):
524 if self.__matmat_impl is not None:
525 return self.__matmat_impl(X)
526 else:
527 return super(_CustomLinearOperator, self)._matmat(X)
529 def _matvec(self, x):
530 return self.__matvec_impl(x)
532 def _rmatvec(self, x):
533 func = self.__rmatvec_impl
534 if func is None:
535 raise NotImplementedError("rmatvec is not defined")
536 return self.__rmatvec_impl(x)
538 def _rmatmat(self, X):
539 if self.__rmatmat_impl is not None:
540 return self.__rmatmat_impl(X)
541 else:
542 return super(_CustomLinearOperator, self)._rmatmat(X)
544 def _adjoint(self):
545 return _CustomLinearOperator(shape=(self.shape[1], self.shape[0]),
546 matvec=self.__rmatvec_impl,
547 rmatvec=self.__matvec_impl,
548 matmat=self.__rmatmat_impl,
549 rmatmat=self.__matmat_impl,
550 dtype=self.dtype)
553class _AdjointLinearOperator(LinearOperator):
554 """Adjoint of arbitrary Linear Operator"""
555 def __init__(self, A):
556 shape = (A.shape[1], A.shape[0])
557 super(_AdjointLinearOperator, self).__init__(dtype=A.dtype, shape=shape)
558 self.A = A
559 self.args = (A,)
561 def _matvec(self, x):
562 return self.A._rmatvec(x)
564 def _rmatvec(self, x):
565 return self.A._matvec(x)
567 def _matmat(self, x):
568 return self.A._rmatmat(x)
570 def _rmatmat(self, x):
571 return self.A._matmat(x)
573class _TransposedLinearOperator(LinearOperator):
574 """Transposition of arbitrary Linear Operator"""
575 def __init__(self, A):
576 shape = (A.shape[1], A.shape[0])
577 super(_TransposedLinearOperator, self).__init__(dtype=A.dtype, shape=shape)
578 self.A = A
579 self.args = (A,)
581 def _matvec(self, x):
582 # NB. np.conj works also on sparse matrices
583 return np.conj(self.A._rmatvec(np.conj(x)))
585 def _rmatvec(self, x):
586 return np.conj(self.A._matvec(np.conj(x)))
588 def _matmat(self, x):
589 # NB. np.conj works also on sparse matrices
590 return np.conj(self.A._rmatmat(np.conj(x)))
592 def _rmatmat(self, x):
593 return np.conj(self.A._matmat(np.conj(x)))
595def _get_dtype(operators, dtypes=None):
596 if dtypes is None:
597 dtypes = []
598 for obj in operators:
599 if obj is not None and hasattr(obj, 'dtype'):
600 dtypes.append(obj.dtype)
601 return np.find_common_type(dtypes, [])
604class _SumLinearOperator(LinearOperator):
605 def __init__(self, A, B):
606 if not isinstance(A, LinearOperator) or \
607 not isinstance(B, LinearOperator):
608 raise ValueError('both operands have to be a LinearOperator')
609 if A.shape != B.shape:
610 raise ValueError('cannot add %r and %r: shape mismatch'
611 % (A, B))
612 self.args = (A, B)
613 super(_SumLinearOperator, self).__init__(_get_dtype([A, B]), A.shape)
615 def _matvec(self, x):
616 return self.args[0].matvec(x) + self.args[1].matvec(x)
618 def _rmatvec(self, x):
619 return self.args[0].rmatvec(x) + self.args[1].rmatvec(x)
621 def _rmatmat(self, x):
622 return self.args[0].rmatmat(x) + self.args[1].rmatmat(x)
624 def _matmat(self, x):
625 return self.args[0].matmat(x) + self.args[1].matmat(x)
627 def _adjoint(self):
628 A, B = self.args
629 return A.H + B.H
632class _ProductLinearOperator(LinearOperator):
633 def __init__(self, A, B):
634 if not isinstance(A, LinearOperator) or \
635 not isinstance(B, LinearOperator):
636 raise ValueError('both operands have to be a LinearOperator')
637 if A.shape[1] != B.shape[0]:
638 raise ValueError('cannot multiply %r and %r: shape mismatch'
639 % (A, B))
640 super(_ProductLinearOperator, self).__init__(_get_dtype([A, B]),
641 (A.shape[0], B.shape[1]))
642 self.args = (A, B)
644 def _matvec(self, x):
645 return self.args[0].matvec(self.args[1].matvec(x))
647 def _rmatvec(self, x):
648 return self.args[1].rmatvec(self.args[0].rmatvec(x))
650 def _rmatmat(self, x):
651 return self.args[1].rmatmat(self.args[0].rmatmat(x))
653 def _matmat(self, x):
654 return self.args[0].matmat(self.args[1].matmat(x))
656 def _adjoint(self):
657 A, B = self.args
658 return B.H * A.H
661class _ScaledLinearOperator(LinearOperator):
662 def __init__(self, A, alpha):
663 if not isinstance(A, LinearOperator):
664 raise ValueError('LinearOperator expected as A')
665 if not np.isscalar(alpha):
666 raise ValueError('scalar expected as alpha')
667 dtype = _get_dtype([A], [type(alpha)])
668 super(_ScaledLinearOperator, self).__init__(dtype, A.shape)
669 self.args = (A, alpha)
671 def _matvec(self, x):
672 return self.args[1] * self.args[0].matvec(x)
674 def _rmatvec(self, x):
675 return np.conj(self.args[1]) * self.args[0].rmatvec(x)
677 def _rmatmat(self, x):
678 return np.conj(self.args[1]) * self.args[0].rmatmat(x)
680 def _matmat(self, x):
681 return self.args[1] * self.args[0].matmat(x)
683 def _adjoint(self):
684 A, alpha = self.args
685 return A.H * np.conj(alpha)
688class _PowerLinearOperator(LinearOperator):
689 def __init__(self, A, p):
690 if not isinstance(A, LinearOperator):
691 raise ValueError('LinearOperator expected as A')
692 if A.shape[0] != A.shape[1]:
693 raise ValueError('square LinearOperator expected, got %r' % A)
694 if not isintlike(p) or p < 0:
695 raise ValueError('non-negative integer expected as p')
697 super(_PowerLinearOperator, self).__init__(_get_dtype([A]), A.shape)
698 self.args = (A, p)
700 def _power(self, fun, x):
701 res = np.array(x, copy=True)
702 for i in range(self.args[1]):
703 res = fun(res)
704 return res
706 def _matvec(self, x):
707 return self._power(self.args[0].matvec, x)
709 def _rmatvec(self, x):
710 return self._power(self.args[0].rmatvec, x)
712 def _rmatmat(self, x):
713 return self._power(self.args[0].rmatmat, x)
715 def _matmat(self, x):
716 return self._power(self.args[0].matmat, x)
718 def _adjoint(self):
719 A, p = self.args
720 return A.H ** p
723class MatrixLinearOperator(LinearOperator):
724 def __init__(self, A):
725 super(MatrixLinearOperator, self).__init__(A.dtype, A.shape)
726 self.A = A
727 self.__adj = None
728 self.args = (A,)
730 def _matmat(self, X):
731 return self.A.dot(X)
733 def _adjoint(self):
734 if self.__adj is None:
735 self.__adj = _AdjointMatrixOperator(self)
736 return self.__adj
738class _AdjointMatrixOperator(MatrixLinearOperator):
739 def __init__(self, adjoint):
740 self.A = adjoint.A.T.conj()
741 self.__adjoint = adjoint
742 self.args = (adjoint,)
743 self.shape = adjoint.shape[1], adjoint.shape[0]
745 @property
746 def dtype(self):
747 return self.__adjoint.dtype
749 def _adjoint(self):
750 return self.__adjoint
753class IdentityOperator(LinearOperator):
754 def __init__(self, shape, dtype=None):
755 super(IdentityOperator, self).__init__(dtype, shape)
757 def _matvec(self, x):
758 return x
760 def _rmatvec(self, x):
761 return x
763 def _rmatmat(self, x):
764 return x
766 def _matmat(self, x):
767 return x
769 def _adjoint(self):
770 return self
773def aslinearoperator(A):
774 """Return A as a LinearOperator.
776 'A' may be any of the following types:
777 - ndarray
778 - matrix
779 - sparse matrix (e.g. csr_matrix, lil_matrix, etc.)
780 - LinearOperator
781 - An object with .shape and .matvec attributes
783 See the LinearOperator documentation for additional information.
785 Notes
786 -----
787 If 'A' has no .dtype attribute, the data type is determined by calling
788 :func:`LinearOperator.matvec()` - set the .dtype attribute to prevent this
789 call upon the linear operator creation.
791 Examples
792 --------
793 >>> from scipy.sparse.linalg import aslinearoperator
794 >>> M = np.array([[1,2,3],[4,5,6]], dtype=np.int32)
795 >>> aslinearoperator(M)
796 <2x3 MatrixLinearOperator with dtype=int32>
797 """
798 if isinstance(A, LinearOperator):
799 return A
801 elif isinstance(A, np.ndarray) or isinstance(A, np.matrix):
802 if A.ndim > 2:
803 raise ValueError('array must have ndim <= 2')
804 A = np.atleast_2d(np.asarray(A))
805 return MatrixLinearOperator(A)
807 elif isspmatrix(A) or is_pydata_spmatrix(A):
808 return MatrixLinearOperator(A)
810 else:
811 if hasattr(A, 'shape') and hasattr(A, 'matvec'):
812 rmatvec = None
813 rmatmat = None
814 dtype = None
816 if hasattr(A, 'rmatvec'):
817 rmatvec = A.rmatvec
818 if hasattr(A, 'rmatmat'):
819 rmatmat = A.rmatmat
820 if hasattr(A, 'dtype'):
821 dtype = A.dtype
822 return LinearOperator(A.shape, A.matvec, rmatvec=rmatvec,
823 rmatmat=rmatmat, dtype=dtype)
825 else:
826 raise TypeError('type not understood')