123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272 |
- """Utilities to deal with sympy.Matrix, numpy and scipy.sparse."""
- from sympy.core.expr import Expr
- from sympy.core.numbers import I
- from sympy.core.singleton import S
- from sympy.matrices.matrices import MatrixBase
- from sympy.matrices import eye, zeros
- from sympy.external import import_module
- __all__ = [
- 'numpy_ndarray',
- 'scipy_sparse_matrix',
- 'sympy_to_numpy',
- 'sympy_to_scipy_sparse',
- 'numpy_to_sympy',
- 'scipy_sparse_to_sympy',
- 'flatten_scalar',
- 'matrix_dagger',
- 'to_sympy',
- 'to_numpy',
- 'to_scipy_sparse',
- 'matrix_tensor_product',
- 'matrix_zeros'
- ]
- # Conditionally define the base classes for numpy and scipy.sparse arrays
- # for use in isinstance tests.
- np = import_module('numpy')
- if not np:
- class numpy_ndarray:
- pass
- else:
- numpy_ndarray = np.ndarray # type: ignore
- scipy = import_module('scipy', import_kwargs={'fromlist': ['sparse']})
- if not scipy:
- class scipy_sparse_matrix:
- pass
- sparse = None
- else:
- sparse = scipy.sparse
- scipy_sparse_matrix = sparse.spmatrix # type: ignore
- def sympy_to_numpy(m, **options):
- """Convert a SymPy Matrix/complex number to a numpy matrix or scalar."""
- if not np:
- raise ImportError
- dtype = options.get('dtype', 'complex')
- if isinstance(m, MatrixBase):
- return np.matrix(m.tolist(), dtype=dtype)
- elif isinstance(m, Expr):
- if m.is_Number or m.is_NumberSymbol or m == I:
- return complex(m)
- raise TypeError('Expected MatrixBase or complex scalar, got: %r' % m)
- def sympy_to_scipy_sparse(m, **options):
- """Convert a SymPy Matrix/complex number to a numpy matrix or scalar."""
- if not np or not sparse:
- raise ImportError
- dtype = options.get('dtype', 'complex')
- if isinstance(m, MatrixBase):
- return sparse.csr_matrix(np.matrix(m.tolist(), dtype=dtype))
- elif isinstance(m, Expr):
- if m.is_Number or m.is_NumberSymbol or m == I:
- return complex(m)
- raise TypeError('Expected MatrixBase or complex scalar, got: %r' % m)
- def scipy_sparse_to_sympy(m, **options):
- """Convert a scipy.sparse matrix to a SymPy matrix."""
- return MatrixBase(m.todense())
- def numpy_to_sympy(m, **options):
- """Convert a numpy matrix to a SymPy matrix."""
- return MatrixBase(m)
- def to_sympy(m, **options):
- """Convert a numpy/scipy.sparse matrix to a SymPy matrix."""
- if isinstance(m, MatrixBase):
- return m
- elif isinstance(m, numpy_ndarray):
- return numpy_to_sympy(m)
- elif isinstance(m, scipy_sparse_matrix):
- return scipy_sparse_to_sympy(m)
- elif isinstance(m, Expr):
- return m
- raise TypeError('Expected sympy/numpy/scipy.sparse matrix, got: %r' % m)
- def to_numpy(m, **options):
- """Convert a sympy/scipy.sparse matrix to a numpy matrix."""
- dtype = options.get('dtype', 'complex')
- if isinstance(m, (MatrixBase, Expr)):
- return sympy_to_numpy(m, dtype=dtype)
- elif isinstance(m, numpy_ndarray):
- return m
- elif isinstance(m, scipy_sparse_matrix):
- return m.todense()
- raise TypeError('Expected sympy/numpy/scipy.sparse matrix, got: %r' % m)
- def to_scipy_sparse(m, **options):
- """Convert a sympy/numpy matrix to a scipy.sparse matrix."""
- dtype = options.get('dtype', 'complex')
- if isinstance(m, (MatrixBase, Expr)):
- return sympy_to_scipy_sparse(m, dtype=dtype)
- elif isinstance(m, numpy_ndarray):
- if not sparse:
- raise ImportError
- return sparse.csr_matrix(m)
- elif isinstance(m, scipy_sparse_matrix):
- return m
- raise TypeError('Expected sympy/numpy/scipy.sparse matrix, got: %r' % m)
- def flatten_scalar(e):
- """Flatten a 1x1 matrix to a scalar, return larger matrices unchanged."""
- if isinstance(e, MatrixBase):
- if e.shape == (1, 1):
- e = e[0]
- if isinstance(e, (numpy_ndarray, scipy_sparse_matrix)):
- if e.shape == (1, 1):
- e = complex(e[0, 0])
- return e
- def matrix_dagger(e):
- """Return the dagger of a sympy/numpy/scipy.sparse matrix."""
- if isinstance(e, MatrixBase):
- return e.H
- elif isinstance(e, (numpy_ndarray, scipy_sparse_matrix)):
- return e.conjugate().transpose()
- raise TypeError('Expected sympy/numpy/scipy.sparse matrix, got: %r' % e)
- # TODO: Move this into sympy.matricies.
- def _sympy_tensor_product(*matrices):
- """Compute the kronecker product of a sequence of SymPy Matrices.
- """
- from sympy.matrices.expressions.kronecker import matrix_kronecker_product
- return matrix_kronecker_product(*matrices)
- def _numpy_tensor_product(*product):
- """numpy version of tensor product of multiple arguments."""
- if not np:
- raise ImportError
- answer = product[0]
- for item in product[1:]:
- answer = np.kron(answer, item)
- return answer
- def _scipy_sparse_tensor_product(*product):
- """scipy.sparse version of tensor product of multiple arguments."""
- if not sparse:
- raise ImportError
- answer = product[0]
- for item in product[1:]:
- answer = sparse.kron(answer, item)
- # The final matrices will just be multiplied, so csr is a good final
- # sparse format.
- return sparse.csr_matrix(answer)
- def matrix_tensor_product(*product):
- """Compute the matrix tensor product of sympy/numpy/scipy.sparse matrices."""
- if isinstance(product[0], MatrixBase):
- return _sympy_tensor_product(*product)
- elif isinstance(product[0], numpy_ndarray):
- return _numpy_tensor_product(*product)
- elif isinstance(product[0], scipy_sparse_matrix):
- return _scipy_sparse_tensor_product(*product)
- def _numpy_eye(n):
- """numpy version of complex eye."""
- if not np:
- raise ImportError
- return np.matrix(np.eye(n, dtype='complex'))
- def _scipy_sparse_eye(n):
- """scipy.sparse version of complex eye."""
- if not sparse:
- raise ImportError
- return sparse.eye(n, n, dtype='complex')
- def matrix_eye(n, **options):
- """Get the version of eye and tensor_product for a given format."""
- format = options.get('format', 'sympy')
- if format == 'sympy':
- return eye(n)
- elif format == 'numpy':
- return _numpy_eye(n)
- elif format == 'scipy.sparse':
- return _scipy_sparse_eye(n)
- raise NotImplementedError('Invalid format: %r' % format)
- def _numpy_zeros(m, n, **options):
- """numpy version of zeros."""
- dtype = options.get('dtype', 'float64')
- if not np:
- raise ImportError
- return np.zeros((m, n), dtype=dtype)
- def _scipy_sparse_zeros(m, n, **options):
- """scipy.sparse version of zeros."""
- spmatrix = options.get('spmatrix', 'csr')
- dtype = options.get('dtype', 'float64')
- if not sparse:
- raise ImportError
- if spmatrix == 'lil':
- return sparse.lil_matrix((m, n), dtype=dtype)
- elif spmatrix == 'csr':
- return sparse.csr_matrix((m, n), dtype=dtype)
- def matrix_zeros(m, n, **options):
- """"Get a zeros matrix for a given format."""
- format = options.get('format', 'sympy')
- if format == 'sympy':
- return zeros(m, n)
- elif format == 'numpy':
- return _numpy_zeros(m, n, **options)
- elif format == 'scipy.sparse':
- return _scipy_sparse_zeros(m, n, **options)
- raise NotImplementedError('Invaild format: %r' % format)
- def _numpy_matrix_to_zero(e):
- """Convert a numpy zero matrix to the zero scalar."""
- if not np:
- raise ImportError
- test = np.zeros_like(e)
- if np.allclose(e, test):
- return 0.0
- else:
- return e
- def _scipy_sparse_matrix_to_zero(e):
- """Convert a scipy.sparse zero matrix to the zero scalar."""
- if not np:
- raise ImportError
- edense = e.todense()
- test = np.zeros_like(edense)
- if np.allclose(edense, test):
- return 0.0
- else:
- return e
- def matrix_to_zero(e):
- """Convert a zero matrix to the scalar zero."""
- if isinstance(e, MatrixBase):
- if zeros(*e.shape) == e:
- e = S.Zero
- elif isinstance(e, numpy_ndarray):
- e = _numpy_matrix_to_zero(e)
- elif isinstance(e, scipy_sparse_matrix):
- e = _scipy_sparse_matrix_to_zero(e)
- return e
|