Spaces:
Sleeping
Sleeping
from collections import defaultdict | |
from collections.abc import Iterable | |
from inspect import isfunction | |
from functools import reduce | |
from sympy.assumptions.refine import refine | |
from sympy.core import SympifyError, Add | |
from sympy.core.basic import Atom, Basic | |
from sympy.core.kind import UndefinedKind | |
from sympy.core.numbers import Integer | |
from sympy.core.mod import Mod | |
from sympy.core.symbol import Symbol, Dummy | |
from sympy.core.sympify import sympify, _sympify | |
from sympy.core.function import diff | |
from sympy.polys import cancel | |
from sympy.functions.elementary.complexes import Abs, re, im | |
from sympy.printing import sstr | |
from sympy.functions.elementary.miscellaneous import Max, Min, sqrt | |
from sympy.functions.special.tensor_functions import KroneckerDelta, LeviCivita | |
from sympy.core.singleton import S | |
from sympy.printing.defaults import Printable | |
from sympy.printing.str import StrPrinter | |
from sympy.functions.elementary.exponential import exp, log | |
from sympy.functions.combinatorial.factorials import binomial, factorial | |
import mpmath as mp | |
from collections.abc import Callable | |
from sympy.utilities.iterables import reshape | |
from sympy.core.expr import Expr | |
from sympy.core.power import Pow | |
from sympy.core.symbol import uniquely_named_symbol | |
from .utilities import _dotprodsimp, _simplify as _utilities_simplify | |
from sympy.polys.polytools import Poly | |
from sympy.utilities.iterables import flatten, is_sequence | |
from sympy.utilities.misc import as_int, filldedent | |
from sympy.core.decorators import call_highest_priority | |
from sympy.core.logic import fuzzy_and, FuzzyBool | |
from sympy.tensor.array import NDimArray | |
from sympy.utilities.iterables import NotIterable | |
from .utilities import _get_intermediate_simp_bool | |
from .kind import MatrixKind | |
from .exceptions import ( | |
MatrixError, ShapeError, NonSquareMatrixError, NonInvertibleMatrixError, | |
) | |
from .utilities import _iszero, _is_zero_after_expand_mul | |
from .determinant import ( | |
_find_reasonable_pivot, _find_reasonable_pivot_naive, | |
_adjugate, _charpoly, _cofactor, _cofactor_matrix, _per, | |
_det, _det_bareiss, _det_berkowitz, _det_bird, _det_laplace, _det_LU, | |
_minor, _minor_submatrix) | |
from .reductions import _is_echelon, _echelon_form, _rank, _rref | |
from .solvers import ( | |
_diagonal_solve, _lower_triangular_solve, _upper_triangular_solve, | |
_cholesky_solve, _LDLsolve, _LUsolve, _QRsolve, _gauss_jordan_solve, | |
_pinv_solve, _cramer_solve, _solve, _solve_least_squares) | |
from .inverse import ( | |
_pinv, _inv_ADJ, _inv_GE, _inv_LU, _inv_CH, _inv_LDL, _inv_QR, | |
_inv, _inv_block) | |
from .subspaces import _columnspace, _nullspace, _rowspace, _orthogonalize | |
from .eigen import ( | |
_eigenvals, _eigenvects, | |
_bidiagonalize, _bidiagonal_decomposition, | |
_is_diagonalizable, _diagonalize, | |
_is_positive_definite, _is_positive_semidefinite, | |
_is_negative_definite, _is_negative_semidefinite, _is_indefinite, | |
_jordan_form, _left_eigenvects, _singular_values) | |
from .decompositions import ( | |
_rank_decomposition, _cholesky, _LDLdecomposition, | |
_LUdecomposition, _LUdecomposition_Simple, _LUdecompositionFF, | |
_singular_value_decomposition, _QRdecomposition, _upper_hessenberg_decomposition) | |
from .graph import ( | |
_connected_components, _connected_components_decomposition, | |
_strongly_connected_components, _strongly_connected_components_decomposition) | |
__doctest_requires__ = { | |
('MatrixBase.is_indefinite', | |
'MatrixBase.is_positive_definite', | |
'MatrixBase.is_positive_semidefinite', | |
'MatrixBase.is_negative_definite', | |
'MatrixBase.is_negative_semidefinite'): ['matplotlib'], | |
} | |
class MatrixBase(Printable): | |
"""All common matrix operations including basic arithmetic, shaping, | |
and special matrices like `zeros`, and `eye`.""" | |
_op_priority = 10.01 | |
# Added just for numpy compatibility | |
__array_priority__ = 11 | |
is_Matrix = True | |
_class_priority = 3 | |
_sympify = staticmethod(sympify) | |
zero = S.Zero | |
one = S.One | |
_diff_wrt = True # type: bool | |
rows = None # type: int | |
cols = None # type: int | |
_simplify = None | |
def _new(cls, *args, **kwargs): | |
"""`_new` must, at minimum, be callable as | |
`_new(rows, cols, mat) where mat is a flat list of the | |
elements of the matrix.""" | |
raise NotImplementedError("Subclasses must implement this.") | |
def __eq__(self, other): | |
raise NotImplementedError("Subclasses must implement this.") | |
def __getitem__(self, key): | |
"""Implementations of __getitem__ should accept ints, in which | |
case the matrix is indexed as a flat list, tuples (i,j) in which | |
case the (i,j) entry is returned, slices, or mixed tuples (a,b) | |
where a and b are any combination of slices and integers.""" | |
raise NotImplementedError("Subclasses must implement this.") | |
def shape(self): | |
"""The shape (dimensions) of the matrix as the 2-tuple (rows, cols). | |
Examples | |
======== | |
>>> from sympy import zeros | |
>>> M = zeros(2, 3) | |
>>> M.shape | |
(2, 3) | |
>>> M.rows | |
2 | |
>>> M.cols | |
3 | |
""" | |
return (self.rows, self.cols) | |
def _eval_col_del(self, col): | |
def entry(i, j): | |
return self[i, j] if j < col else self[i, j + 1] | |
return self._new(self.rows, self.cols - 1, entry) | |
def _eval_col_insert(self, pos, other): | |
def entry(i, j): | |
if j < pos: | |
return self[i, j] | |
elif pos <= j < pos + other.cols: | |
return other[i, j - pos] | |
return self[i, j - other.cols] | |
return self._new(self.rows, self.cols + other.cols, entry) | |
def _eval_col_join(self, other): | |
rows = self.rows | |
def entry(i, j): | |
if i < rows: | |
return self[i, j] | |
return other[i - rows, j] | |
return classof(self, other)._new(self.rows + other.rows, self.cols, | |
entry) | |
def _eval_extract(self, rowsList, colsList): | |
mat = list(self) | |
cols = self.cols | |
indices = (i * cols + j for i in rowsList for j in colsList) | |
return self._new(len(rowsList), len(colsList), | |
[mat[i] for i in indices]) | |
def _eval_get_diag_blocks(self): | |
sub_blocks = [] | |
def recurse_sub_blocks(M): | |
i = 1 | |
while i <= M.shape[0]: | |
if i == 1: | |
to_the_right = M[0, i:] | |
to_the_bottom = M[i:, 0] | |
else: | |
to_the_right = M[:i, i:] | |
to_the_bottom = M[i:, :i] | |
if any(to_the_right) or any(to_the_bottom): | |
i += 1 | |
continue | |
else: | |
sub_blocks.append(M[:i, :i]) | |
if M.shape == M[:i, :i].shape: | |
return | |
else: | |
recurse_sub_blocks(M[i:, i:]) | |
return | |
recurse_sub_blocks(self) | |
return sub_blocks | |
def _eval_row_del(self, row): | |
def entry(i, j): | |
return self[i, j] if i < row else self[i + 1, j] | |
return self._new(self.rows - 1, self.cols, entry) | |
def _eval_row_insert(self, pos, other): | |
entries = list(self) | |
insert_pos = pos * self.cols | |
entries[insert_pos:insert_pos] = list(other) | |
return self._new(self.rows + other.rows, self.cols, entries) | |
def _eval_row_join(self, other): | |
cols = self.cols | |
def entry(i, j): | |
if j < cols: | |
return self[i, j] | |
return other[i, j - cols] | |
return classof(self, other)._new(self.rows, self.cols + other.cols, | |
entry) | |
def _eval_tolist(self): | |
return [list(self[i,:]) for i in range(self.rows)] | |
def _eval_todok(self): | |
dok = {} | |
rows, cols = self.shape | |
for i in range(rows): | |
for j in range(cols): | |
val = self[i, j] | |
if val != self.zero: | |
dok[i, j] = val | |
return dok | |
def _eval_from_dok(cls, rows, cols, dok): | |
out_flat = [cls.zero] * (rows * cols) | |
for (i, j), val in dok.items(): | |
out_flat[i * cols + j] = val | |
return cls._new(rows, cols, out_flat) | |
def _eval_vec(self): | |
rows = self.rows | |
def entry(n, _): | |
# we want to read off the columns first | |
j = n // rows | |
i = n - j * rows | |
return self[i, j] | |
return self._new(len(self), 1, entry) | |
def _eval_vech(self, diagonal): | |
c = self.cols | |
v = [] | |
if diagonal: | |
for j in range(c): | |
for i in range(j, c): | |
v.append(self[i, j]) | |
else: | |
for j in range(c): | |
for i in range(j + 1, c): | |
v.append(self[i, j]) | |
return self._new(len(v), 1, v) | |
def col_del(self, col): | |
"""Delete the specified column.""" | |
if col < 0: | |
col += self.cols | |
if not 0 <= col < self.cols: | |
raise IndexError("Column {} is out of range.".format(col)) | |
return self._eval_col_del(col) | |
def col_insert(self, pos, other): | |
"""Insert one or more columns at the given column position. | |
Examples | |
======== | |
>>> from sympy import zeros, ones | |
>>> M = zeros(3) | |
>>> V = ones(3, 1) | |
>>> M.col_insert(1, V) | |
Matrix([ | |
[0, 1, 0, 0], | |
[0, 1, 0, 0], | |
[0, 1, 0, 0]]) | |
See Also | |
======== | |
col | |
row_insert | |
""" | |
# Allows you to build a matrix even if it is null matrix | |
if not self: | |
return type(self)(other) | |
pos = as_int(pos) | |
if pos < 0: | |
pos = self.cols + pos | |
if pos < 0: | |
pos = 0 | |
elif pos > self.cols: | |
pos = self.cols | |
if self.rows != other.rows: | |
raise ShapeError( | |
"The matrices have incompatible number of rows ({} and {})" | |
.format(self.rows, other.rows)) | |
return self._eval_col_insert(pos, other) | |
def col_join(self, other): | |
"""Concatenates two matrices along self's last and other's first row. | |
Examples | |
======== | |
>>> from sympy import zeros, ones | |
>>> M = zeros(3) | |
>>> V = ones(1, 3) | |
>>> M.col_join(V) | |
Matrix([ | |
[0, 0, 0], | |
[0, 0, 0], | |
[0, 0, 0], | |
[1, 1, 1]]) | |
See Also | |
======== | |
col | |
row_join | |
""" | |
# A null matrix can always be stacked (see #10770) | |
if self.rows == 0 and self.cols != other.cols: | |
return self._new(0, other.cols, []).col_join(other) | |
if self.cols != other.cols: | |
raise ShapeError( | |
"The matrices have incompatible number of columns ({} and {})" | |
.format(self.cols, other.cols)) | |
return self._eval_col_join(other) | |
def col(self, j): | |
"""Elementary column selector. | |
Examples | |
======== | |
>>> from sympy import eye | |
>>> eye(2).col(0) | |
Matrix([ | |
[1], | |
[0]]) | |
See Also | |
======== | |
row | |
col_del | |
col_join | |
col_insert | |
""" | |
return self[:, j] | |
def extract(self, rowsList, colsList): | |
r"""Return a submatrix by specifying a list of rows and columns. | |
Negative indices can be given. All indices must be in the range | |
$-n \le i < n$ where $n$ is the number of rows or columns. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> m = Matrix(4, 3, range(12)) | |
>>> m | |
Matrix([ | |
[0, 1, 2], | |
[3, 4, 5], | |
[6, 7, 8], | |
[9, 10, 11]]) | |
>>> m.extract([0, 1, 3], [0, 1]) | |
Matrix([ | |
[0, 1], | |
[3, 4], | |
[9, 10]]) | |
Rows or columns can be repeated: | |
>>> m.extract([0, 0, 1], [-1]) | |
Matrix([ | |
[2], | |
[2], | |
[5]]) | |
Every other row can be taken by using range to provide the indices: | |
>>> m.extract(range(0, m.rows, 2), [-1]) | |
Matrix([ | |
[2], | |
[8]]) | |
RowsList or colsList can also be a list of booleans, in which case | |
the rows or columns corresponding to the True values will be selected: | |
>>> m.extract([0, 1, 2, 3], [True, False, True]) | |
Matrix([ | |
[0, 2], | |
[3, 5], | |
[6, 8], | |
[9, 11]]) | |
""" | |
if not is_sequence(rowsList) or not is_sequence(colsList): | |
raise TypeError("rowsList and colsList must be iterable") | |
# ensure rowsList and colsList are lists of integers | |
if rowsList and all(isinstance(i, bool) for i in rowsList): | |
rowsList = [index for index, item in enumerate(rowsList) if item] | |
if colsList and all(isinstance(i, bool) for i in colsList): | |
colsList = [index for index, item in enumerate(colsList) if item] | |
# ensure everything is in range | |
rowsList = [a2idx(k, self.rows) for k in rowsList] | |
colsList = [a2idx(k, self.cols) for k in colsList] | |
return self._eval_extract(rowsList, colsList) | |
def get_diag_blocks(self): | |
"""Obtains the square sub-matrices on the main diagonal of a square matrix. | |
Useful for inverting symbolic matrices or solving systems of | |
linear equations which may be decoupled by having a block diagonal | |
structure. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> from sympy.abc import x, y, z | |
>>> A = Matrix([[1, 3, 0, 0], [y, z*z, 0, 0], [0, 0, x, 0], [0, 0, 0, 0]]) | |
>>> a1, a2, a3 = A.get_diag_blocks() | |
>>> a1 | |
Matrix([ | |
[1, 3], | |
[y, z**2]]) | |
>>> a2 | |
Matrix([[x]]) | |
>>> a3 | |
Matrix([[0]]) | |
""" | |
return self._eval_get_diag_blocks() | |
def hstack(cls, *args): | |
"""Return a matrix formed by joining args horizontally (i.e. | |
by repeated application of row_join). | |
Examples | |
======== | |
>>> from sympy import Matrix, eye | |
>>> Matrix.hstack(eye(2), 2*eye(2)) | |
Matrix([ | |
[1, 0, 2, 0], | |
[0, 1, 0, 2]]) | |
""" | |
if len(args) == 0: | |
return cls._new() | |
kls = type(args[0]) | |
return reduce(kls.row_join, args) | |
def reshape(self, rows, cols): | |
"""Reshape the matrix. Total number of elements must remain the same. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> m = Matrix(2, 3, lambda i, j: 1) | |
>>> m | |
Matrix([ | |
[1, 1, 1], | |
[1, 1, 1]]) | |
>>> m.reshape(1, 6) | |
Matrix([[1, 1, 1, 1, 1, 1]]) | |
>>> m.reshape(3, 2) | |
Matrix([ | |
[1, 1], | |
[1, 1], | |
[1, 1]]) | |
""" | |
if self.rows * self.cols != rows * cols: | |
raise ValueError("Invalid reshape parameters %d %d" % (rows, cols)) | |
return self._new(rows, cols, lambda i, j: self[i * cols + j]) | |
def row_del(self, row): | |
"""Delete the specified row.""" | |
if row < 0: | |
row += self.rows | |
if not 0 <= row < self.rows: | |
raise IndexError("Row {} is out of range.".format(row)) | |
return self._eval_row_del(row) | |
def row_insert(self, pos, other): | |
"""Insert one or more rows at the given row position. | |
Examples | |
======== | |
>>> from sympy import zeros, ones | |
>>> M = zeros(3) | |
>>> V = ones(1, 3) | |
>>> M.row_insert(1, V) | |
Matrix([ | |
[0, 0, 0], | |
[1, 1, 1], | |
[0, 0, 0], | |
[0, 0, 0]]) | |
See Also | |
======== | |
row | |
col_insert | |
""" | |
# Allows you to build a matrix even if it is null matrix | |
if not self: | |
return self._new(other) | |
pos = as_int(pos) | |
if pos < 0: | |
pos = self.rows + pos | |
if pos < 0: | |
pos = 0 | |
elif pos > self.rows: | |
pos = self.rows | |
if self.cols != other.cols: | |
raise ShapeError( | |
"The matrices have incompatible number of columns ({} and {})" | |
.format(self.cols, other.cols)) | |
return self._eval_row_insert(pos, other) | |
def row_join(self, other): | |
"""Concatenates two matrices along self's last and rhs's first column | |
Examples | |
======== | |
>>> from sympy import zeros, ones | |
>>> M = zeros(3) | |
>>> V = ones(3, 1) | |
>>> M.row_join(V) | |
Matrix([ | |
[0, 0, 0, 1], | |
[0, 0, 0, 1], | |
[0, 0, 0, 1]]) | |
See Also | |
======== | |
row | |
col_join | |
""" | |
# A null matrix can always be stacked (see #10770) | |
if self.cols == 0 and self.rows != other.rows: | |
return self._new(other.rows, 0, []).row_join(other) | |
if self.rows != other.rows: | |
raise ShapeError( | |
"The matrices have incompatible number of rows ({} and {})" | |
.format(self.rows, other.rows)) | |
return self._eval_row_join(other) | |
def diagonal(self, k=0): | |
"""Returns the kth diagonal of self. The main diagonal | |
corresponds to `k=0`; diagonals above and below correspond to | |
`k > 0` and `k < 0`, respectively. The values of `self[i, j]` | |
for which `j - i = k`, are returned in order of increasing | |
`i + j`, starting with `i + j = |k|`. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> m = Matrix(3, 3, lambda i, j: j - i); m | |
Matrix([ | |
[ 0, 1, 2], | |
[-1, 0, 1], | |
[-2, -1, 0]]) | |
>>> _.diagonal() | |
Matrix([[0, 0, 0]]) | |
>>> m.diagonal(1) | |
Matrix([[1, 1]]) | |
>>> m.diagonal(-2) | |
Matrix([[-2]]) | |
Even though the diagonal is returned as a Matrix, the element | |
retrieval can be done with a single index: | |
>>> Matrix.diag(1, 2, 3).diagonal()[1] # instead of [0, 1] | |
2 | |
See Also | |
======== | |
diag | |
""" | |
rv = [] | |
k = as_int(k) | |
r = 0 if k > 0 else -k | |
c = 0 if r else k | |
while True: | |
if r == self.rows or c == self.cols: | |
break | |
rv.append(self[r, c]) | |
r += 1 | |
c += 1 | |
if not rv: | |
raise ValueError(filldedent(''' | |
The %s diagonal is out of range [%s, %s]''' % ( | |
k, 1 - self.rows, self.cols - 1))) | |
return self._new(1, len(rv), rv) | |
def row(self, i): | |
"""Elementary row selector. | |
Examples | |
======== | |
>>> from sympy import eye | |
>>> eye(2).row(0) | |
Matrix([[1, 0]]) | |
See Also | |
======== | |
col | |
row_del | |
row_join | |
row_insert | |
""" | |
return self[i, :] | |
def todok(self): | |
"""Return the matrix as dictionary of keys. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> M = Matrix.eye(3) | |
>>> M.todok() | |
{(0, 0): 1, (1, 1): 1, (2, 2): 1} | |
""" | |
return self._eval_todok() | |
def from_dok(cls, rows, cols, dok): | |
"""Create a matrix from a dictionary of keys. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> d = {(0, 0): 1, (1, 2): 3, (2, 1): 4} | |
>>> Matrix.from_dok(3, 3, d) | |
Matrix([ | |
[1, 0, 0], | |
[0, 0, 3], | |
[0, 4, 0]]) | |
""" | |
dok = {ij: cls._sympify(val) for ij, val in dok.items()} | |
return cls._eval_from_dok(rows, cols, dok) | |
def tolist(self): | |
"""Return the Matrix as a nested Python list. | |
Examples | |
======== | |
>>> from sympy import Matrix, ones | |
>>> m = Matrix(3, 3, range(9)) | |
>>> m | |
Matrix([ | |
[0, 1, 2], | |
[3, 4, 5], | |
[6, 7, 8]]) | |
>>> m.tolist() | |
[[0, 1, 2], [3, 4, 5], [6, 7, 8]] | |
>>> ones(3, 0).tolist() | |
[[], [], []] | |
When there are no rows then it will not be possible to tell how | |
many columns were in the original matrix: | |
>>> ones(0, 3).tolist() | |
[] | |
""" | |
if not self.rows: | |
return [] | |
if not self.cols: | |
return [[] for i in range(self.rows)] | |
return self._eval_tolist() | |
def todod(M): | |
"""Returns matrix as dict of dicts containing non-zero elements of the Matrix | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> A = Matrix([[0, 1],[0, 3]]) | |
>>> A | |
Matrix([ | |
[0, 1], | |
[0, 3]]) | |
>>> A.todod() | |
{0: {1: 1}, 1: {1: 3}} | |
""" | |
rowsdict = {} | |
Mlol = M.tolist() | |
for i, Mi in enumerate(Mlol): | |
row = {j: Mij for j, Mij in enumerate(Mi) if Mij} | |
if row: | |
rowsdict[i] = row | |
return rowsdict | |
def vec(self): | |
"""Return the Matrix converted into a one column matrix by stacking columns | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> m=Matrix([[1, 3], [2, 4]]) | |
>>> m | |
Matrix([ | |
[1, 3], | |
[2, 4]]) | |
>>> m.vec() | |
Matrix([ | |
[1], | |
[2], | |
[3], | |
[4]]) | |
See Also | |
======== | |
vech | |
""" | |
return self._eval_vec() | |
def vech(self, diagonal=True, check_symmetry=True): | |
"""Reshapes the matrix into a column vector by stacking the | |
elements in the lower triangle. | |
Parameters | |
========== | |
diagonal : bool, optional | |
If ``True``, it includes the diagonal elements. | |
check_symmetry : bool, optional | |
If ``True``, it checks whether the matrix is symmetric. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> m=Matrix([[1, 2], [2, 3]]) | |
>>> m | |
Matrix([ | |
[1, 2], | |
[2, 3]]) | |
>>> m.vech() | |
Matrix([ | |
[1], | |
[2], | |
[3]]) | |
>>> m.vech(diagonal=False) | |
Matrix([[2]]) | |
Notes | |
===== | |
This should work for symmetric matrices and ``vech`` can | |
represent symmetric matrices in vector form with less size than | |
``vec``. | |
See Also | |
======== | |
vec | |
""" | |
if not self.is_square: | |
raise NonSquareMatrixError | |
if check_symmetry and not self.is_symmetric(): | |
raise ValueError("The matrix is not symmetric.") | |
return self._eval_vech(diagonal) | |
def vstack(cls, *args): | |
"""Return a matrix formed by joining args vertically (i.e. | |
by repeated application of col_join). | |
Examples | |
======== | |
>>> from sympy import Matrix, eye | |
>>> Matrix.vstack(eye(2), 2*eye(2)) | |
Matrix([ | |
[1, 0], | |
[0, 1], | |
[2, 0], | |
[0, 2]]) | |
""" | |
if len(args) == 0: | |
return cls._new() | |
kls = type(args[0]) | |
return reduce(kls.col_join, args) | |
def _eval_diag(cls, rows, cols, diag_dict): | |
"""diag_dict is a defaultdict containing | |
all the entries of the diagonal matrix.""" | |
def entry(i, j): | |
return diag_dict[(i, j)] | |
return cls._new(rows, cols, entry) | |
def _eval_eye(cls, rows, cols): | |
vals = [cls.zero]*(rows*cols) | |
vals[::cols+1] = [cls.one]*min(rows, cols) | |
return cls._new(rows, cols, vals, copy=False) | |
def _eval_jordan_block(cls, size: int, eigenvalue, band='upper'): | |
if band == 'lower': | |
def entry(i, j): | |
if i == j: | |
return eigenvalue | |
elif j + 1 == i: | |
return cls.one | |
return cls.zero | |
else: | |
def entry(i, j): | |
if i == j: | |
return eigenvalue | |
elif i + 1 == j: | |
return cls.one | |
return cls.zero | |
return cls._new(size, size, entry) | |
def _eval_ones(cls, rows, cols): | |
def entry(i, j): | |
return cls.one | |
return cls._new(rows, cols, entry) | |
def _eval_zeros(cls, rows, cols): | |
return cls._new(rows, cols, [cls.zero]*(rows*cols), copy=False) | |
def _eval_wilkinson(cls, n): | |
def entry(i, j): | |
return cls.one if i + 1 == j else cls.zero | |
D = cls._new(2*n + 1, 2*n + 1, entry) | |
wminus = cls.diag(list(range(-n, n + 1)), unpack=True) + D + D.T | |
wplus = abs(cls.diag(list(range(-n, n + 1)), unpack=True)) + D + D.T | |
return wminus, wplus | |
def diag(kls, *args, strict=False, unpack=True, rows=None, cols=None, **kwargs): | |
"""Returns a matrix with the specified diagonal. | |
If matrices are passed, a block-diagonal matrix | |
is created (i.e. the "direct sum" of the matrices). | |
kwargs | |
====== | |
rows : rows of the resulting matrix; computed if | |
not given. | |
cols : columns of the resulting matrix; computed if | |
not given. | |
cls : class for the resulting matrix | |
unpack : bool which, when True (default), unpacks a single | |
sequence rather than interpreting it as a Matrix. | |
strict : bool which, when False (default), allows Matrices to | |
have variable-length rows. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> Matrix.diag(1, 2, 3) | |
Matrix([ | |
[1, 0, 0], | |
[0, 2, 0], | |
[0, 0, 3]]) | |
The current default is to unpack a single sequence. If this is | |
not desired, set `unpack=False` and it will be interpreted as | |
a matrix. | |
>>> Matrix.diag([1, 2, 3]) == Matrix.diag(1, 2, 3) | |
True | |
When more than one element is passed, each is interpreted as | |
something to put on the diagonal. Lists are converted to | |
matrices. Filling of the diagonal always continues from | |
the bottom right hand corner of the previous item: this | |
will create a block-diagonal matrix whether the matrices | |
are square or not. | |
>>> col = [1, 2, 3] | |
>>> row = [[4, 5]] | |
>>> Matrix.diag(col, row) | |
Matrix([ | |
[1, 0, 0], | |
[2, 0, 0], | |
[3, 0, 0], | |
[0, 4, 5]]) | |
When `unpack` is False, elements within a list need not all be | |
of the same length. Setting `strict` to True would raise a | |
ValueError for the following: | |
>>> Matrix.diag([[1, 2, 3], [4, 5], [6]], unpack=False) | |
Matrix([ | |
[1, 2, 3], | |
[4, 5, 0], | |
[6, 0, 0]]) | |
The type of the returned matrix can be set with the ``cls`` | |
keyword. | |
>>> from sympy import ImmutableMatrix | |
>>> from sympy.utilities.misc import func_name | |
>>> func_name(Matrix.diag(1, cls=ImmutableMatrix)) | |
'ImmutableDenseMatrix' | |
A zero dimension matrix can be used to position the start of | |
the filling at the start of an arbitrary row or column: | |
>>> from sympy import ones | |
>>> r2 = ones(0, 2) | |
>>> Matrix.diag(r2, 1, 2) | |
Matrix([ | |
[0, 0, 1, 0], | |
[0, 0, 0, 2]]) | |
See Also | |
======== | |
eye | |
diagonal | |
.dense.diag | |
.expressions.blockmatrix.BlockMatrix | |
.sparsetools.banded | |
""" | |
from sympy.matrices.matrixbase import MatrixBase | |
from sympy.matrices.dense import Matrix | |
from sympy.matrices import SparseMatrix | |
klass = kwargs.get('cls', kls) | |
if unpack and len(args) == 1 and is_sequence(args[0]) and \ | |
not isinstance(args[0], MatrixBase): | |
args = args[0] | |
# fill a default dict with the diagonal entries | |
diag_entries = defaultdict(int) | |
rmax = cmax = 0 # keep track of the biggest index seen | |
for m in args: | |
if isinstance(m, list): | |
if strict: | |
# if malformed, Matrix will raise an error | |
_ = Matrix(m) | |
r, c = _.shape | |
m = _.tolist() | |
else: | |
r, c, smat = SparseMatrix._handle_creation_inputs(m) | |
for (i, j), _ in smat.items(): | |
diag_entries[(i + rmax, j + cmax)] = _ | |
m = [] # to skip process below | |
elif hasattr(m, 'shape'): # a Matrix | |
# convert to list of lists | |
r, c = m.shape | |
m = m.tolist() | |
else: # in this case, we're a single value | |
diag_entries[(rmax, cmax)] = m | |
rmax += 1 | |
cmax += 1 | |
continue | |
# process list of lists | |
for i, mi in enumerate(m): | |
for j, _ in enumerate(mi): | |
diag_entries[(i + rmax, j + cmax)] = _ | |
rmax += r | |
cmax += c | |
if rows is None: | |
rows, cols = cols, rows | |
if rows is None: | |
rows, cols = rmax, cmax | |
else: | |
cols = rows if cols is None else cols | |
if rows < rmax or cols < cmax: | |
raise ValueError(filldedent(''' | |
The constructed matrix is {} x {} but a size of {} x {} | |
was specified.'''.format(rmax, cmax, rows, cols))) | |
return klass._eval_diag(rows, cols, diag_entries) | |
def eye(kls, rows, cols=None, **kwargs): | |
"""Returns an identity matrix. | |
Parameters | |
========== | |
rows : rows of the matrix | |
cols : cols of the matrix (if None, cols=rows) | |
kwargs | |
====== | |
cls : class of the returned matrix | |
""" | |
if cols is None: | |
cols = rows | |
if rows < 0 or cols < 0: | |
raise ValueError("Cannot create a {} x {} matrix. " | |
"Both dimensions must be positive".format(rows, cols)) | |
klass = kwargs.get('cls', kls) | |
rows, cols = as_int(rows), as_int(cols) | |
return klass._eval_eye(rows, cols) | |
def jordan_block(kls, size=None, eigenvalue=None, *, band='upper', **kwargs): | |
"""Returns a Jordan block | |
Parameters | |
========== | |
size : Integer, optional | |
Specifies the shape of the Jordan block matrix. | |
eigenvalue : Number or Symbol | |
Specifies the value for the main diagonal of the matrix. | |
.. note:: | |
The keyword ``eigenval`` is also specified as an alias | |
of this keyword, but it is not recommended to use. | |
We may deprecate the alias in later release. | |
band : 'upper' or 'lower', optional | |
Specifies the position of the off-diagonal to put `1` s on. | |
cls : Matrix, optional | |
Specifies the matrix class of the output form. | |
If it is not specified, the class type where the method is | |
being executed on will be returned. | |
Returns | |
======= | |
Matrix | |
A Jordan block matrix. | |
Raises | |
====== | |
ValueError | |
If insufficient arguments are given for matrix size | |
specification, or no eigenvalue is given. | |
Examples | |
======== | |
Creating a default Jordan block: | |
>>> from sympy import Matrix | |
>>> from sympy.abc import x | |
>>> Matrix.jordan_block(4, x) | |
Matrix([ | |
[x, 1, 0, 0], | |
[0, x, 1, 0], | |
[0, 0, x, 1], | |
[0, 0, 0, x]]) | |
Creating an alternative Jordan block matrix where `1` is on | |
lower off-diagonal: | |
>>> Matrix.jordan_block(4, x, band='lower') | |
Matrix([ | |
[x, 0, 0, 0], | |
[1, x, 0, 0], | |
[0, 1, x, 0], | |
[0, 0, 1, x]]) | |
Creating a Jordan block with keyword arguments | |
>>> Matrix.jordan_block(size=4, eigenvalue=x) | |
Matrix([ | |
[x, 1, 0, 0], | |
[0, x, 1, 0], | |
[0, 0, x, 1], | |
[0, 0, 0, x]]) | |
References | |
========== | |
.. [1] https://en.wikipedia.org/wiki/Jordan_matrix | |
""" | |
klass = kwargs.pop('cls', kls) | |
eigenval = kwargs.get('eigenval', None) | |
if eigenvalue is None and eigenval is None: | |
raise ValueError("Must supply an eigenvalue") | |
elif eigenvalue != eigenval and None not in (eigenval, eigenvalue): | |
raise ValueError( | |
"Inconsistent values are given: 'eigenval'={}, " | |
"'eigenvalue'={}".format(eigenval, eigenvalue)) | |
else: | |
if eigenval is not None: | |
eigenvalue = eigenval | |
if size is None: | |
raise ValueError("Must supply a matrix size") | |
size = as_int(size) | |
return klass._eval_jordan_block(size, eigenvalue, band) | |
def ones(kls, rows, cols=None, **kwargs): | |
"""Returns a matrix of ones. | |
Parameters | |
========== | |
rows : rows of the matrix | |
cols : cols of the matrix (if None, cols=rows) | |
kwargs | |
====== | |
cls : class of the returned matrix | |
""" | |
if cols is None: | |
cols = rows | |
klass = kwargs.get('cls', kls) | |
rows, cols = as_int(rows), as_int(cols) | |
return klass._eval_ones(rows, cols) | |
def zeros(kls, rows, cols=None, **kwargs): | |
"""Returns a matrix of zeros. | |
Parameters | |
========== | |
rows : rows of the matrix | |
cols : cols of the matrix (if None, cols=rows) | |
kwargs | |
====== | |
cls : class of the returned matrix | |
""" | |
if cols is None: | |
cols = rows | |
if rows < 0 or cols < 0: | |
raise ValueError("Cannot create a {} x {} matrix. " | |
"Both dimensions must be positive".format(rows, cols)) | |
klass = kwargs.get('cls', kls) | |
rows, cols = as_int(rows), as_int(cols) | |
return klass._eval_zeros(rows, cols) | |
def companion(kls, poly): | |
"""Returns a companion matrix of a polynomial. | |
Examples | |
======== | |
>>> from sympy import Matrix, Poly, Symbol, symbols | |
>>> x = Symbol('x') | |
>>> c0, c1, c2, c3, c4 = symbols('c0:5') | |
>>> p = Poly(c0 + c1*x + c2*x**2 + c3*x**3 + c4*x**4 + x**5, x) | |
>>> Matrix.companion(p) | |
Matrix([ | |
[0, 0, 0, 0, -c0], | |
[1, 0, 0, 0, -c1], | |
[0, 1, 0, 0, -c2], | |
[0, 0, 1, 0, -c3], | |
[0, 0, 0, 1, -c4]]) | |
""" | |
poly = kls._sympify(poly) | |
if not isinstance(poly, Poly): | |
raise ValueError("{} must be a Poly instance.".format(poly)) | |
if not poly.is_monic: | |
raise ValueError("{} must be a monic polynomial.".format(poly)) | |
if not poly.is_univariate: | |
raise ValueError( | |
"{} must be a univariate polynomial.".format(poly)) | |
size = poly.degree() | |
if not size >= 1: | |
raise ValueError( | |
"{} must have degree not less than 1.".format(poly)) | |
coeffs = poly.all_coeffs() | |
def entry(i, j): | |
if j == size - 1: | |
return -coeffs[-1 - i] | |
elif i == j + 1: | |
return kls.one | |
return kls.zero | |
return kls._new(size, size, entry) | |
def wilkinson(kls, n, **kwargs): | |
"""Returns two square Wilkinson Matrix of size 2*n + 1 | |
$W_{2n + 1}^-, W_{2n + 1}^+ =$ Wilkinson(n) | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> wminus, wplus = Matrix.wilkinson(3) | |
>>> wminus | |
Matrix([ | |
[-3, 1, 0, 0, 0, 0, 0], | |
[ 1, -2, 1, 0, 0, 0, 0], | |
[ 0, 1, -1, 1, 0, 0, 0], | |
[ 0, 0, 1, 0, 1, 0, 0], | |
[ 0, 0, 0, 1, 1, 1, 0], | |
[ 0, 0, 0, 0, 1, 2, 1], | |
[ 0, 0, 0, 0, 0, 1, 3]]) | |
>>> wplus | |
Matrix([ | |
[3, 1, 0, 0, 0, 0, 0], | |
[1, 2, 1, 0, 0, 0, 0], | |
[0, 1, 1, 1, 0, 0, 0], | |
[0, 0, 1, 0, 1, 0, 0], | |
[0, 0, 0, 1, 1, 1, 0], | |
[0, 0, 0, 0, 1, 2, 1], | |
[0, 0, 0, 0, 0, 1, 3]]) | |
References | |
========== | |
.. [1] https://blogs.mathworks.com/cleve/2013/04/15/wilkinsons-matrices-2/ | |
.. [2] J. H. Wilkinson, The Algebraic Eigenvalue Problem, Claredon Press, Oxford, 1965, 662 pp. | |
""" | |
klass = kwargs.get('cls', kls) | |
n = as_int(n) | |
return klass._eval_wilkinson(n) | |
# The RepMatrix subclass uses more efficient sparse implementations of | |
# _eval_iter_values and other things. | |
def _eval_iter_values(self): | |
return (i for i in self if i is not S.Zero) | |
def _eval_values(self): | |
return list(self.iter_values()) | |
def _eval_iter_items(self): | |
for i in range(self.rows): | |
for j in range(self.cols): | |
if self[i, j]: | |
yield (i, j), self[i, j] | |
def _eval_atoms(self, *types): | |
values = self.values() | |
if len(values) < self.rows * self.cols and isinstance(S.Zero, types): | |
s = {S.Zero} | |
else: | |
s = set() | |
return s.union(*[v.atoms(*types) for v in values]) | |
def _eval_free_symbols(self): | |
return set().union(*(i.free_symbols for i in set(self.values()))) | |
def _eval_has(self, *patterns): | |
return any(a.has(*patterns) for a in self.iter_values()) | |
def _eval_is_symbolic(self): | |
return self.has(Symbol) | |
# _eval_is_hermitian is called by some general SymPy | |
# routines and has a different *args signature. Make | |
# sure the names don't clash by adding `_matrix_` in name. | |
def _eval_is_matrix_hermitian(self, simpfunc): | |
herm = lambda i, j: simpfunc(self[i, j] - self[j, i].conjugate()).is_zero | |
return fuzzy_and(herm(i, j) for (i, j), v in self.iter_items()) | |
def _eval_is_zero_matrix(self): | |
return fuzzy_and(v.is_zero for v in self.iter_values()) | |
def _eval_is_Identity(self) -> FuzzyBool: | |
one = self.one | |
zero = self.zero | |
ident = lambda i, j, v: v is one if i == j else v is zero | |
return all(ident(i, j, v) for (i, j), v in self.iter_items()) | |
def _eval_is_diagonal(self): | |
return fuzzy_and(v.is_zero for (i, j), v in self.iter_items() if i != j) | |
def _eval_is_lower(self): | |
return all(v.is_zero for (i, j), v in self.iter_items() if i < j) | |
def _eval_is_upper(self): | |
return all(v.is_zero for (i, j), v in self.iter_items() if i > j) | |
def _eval_is_lower_hessenberg(self): | |
return all(v.is_zero for (i, j), v in self.iter_items() if i + 1 < j) | |
def _eval_is_upper_hessenberg(self): | |
return all(v.is_zero for (i, j), v in self.iter_items() if i > j + 1) | |
def _eval_is_symmetric(self, simpfunc): | |
sym = lambda i, j: simpfunc(self[i, j] - self[j, i]).is_zero | |
return fuzzy_and(sym(i, j) for (i, j), v in self.iter_items()) | |
def _eval_is_anti_symmetric(self, simpfunc): | |
anti = lambda i, j: simpfunc(self[i, j] + self[j, i]).is_zero | |
return fuzzy_and(anti(i, j) for (i, j), v in self.iter_items()) | |
def _has_positive_diagonals(self): | |
diagonal_entries = (self[i, i] for i in range(self.rows)) | |
return fuzzy_and(x.is_positive for x in diagonal_entries) | |
def _has_nonnegative_diagonals(self): | |
diagonal_entries = (self[i, i] for i in range(self.rows)) | |
return fuzzy_and(x.is_nonnegative for x in diagonal_entries) | |
def atoms(self, *types): | |
"""Returns the atoms that form the current object. | |
Examples | |
======== | |
>>> from sympy.abc import x, y | |
>>> from sympy import Matrix | |
>>> Matrix([[x]]) | |
Matrix([[x]]) | |
>>> _.atoms() | |
{x} | |
>>> Matrix([[x, y], [y, x]]) | |
Matrix([ | |
[x, y], | |
[y, x]]) | |
>>> _.atoms() | |
{x, y} | |
""" | |
types = tuple(t if isinstance(t, type) else type(t) for t in types) | |
if not types: | |
types = (Atom,) | |
return self._eval_atoms(*types) | |
def free_symbols(self): | |
"""Returns the free symbols within the matrix. | |
Examples | |
======== | |
>>> from sympy.abc import x | |
>>> from sympy import Matrix | |
>>> Matrix([[x], [1]]).free_symbols | |
{x} | |
""" | |
return self._eval_free_symbols() | |
def has(self, *patterns): | |
"""Test whether any subexpression matches any of the patterns. | |
Examples | |
======== | |
>>> from sympy import Matrix, SparseMatrix, Float | |
>>> from sympy.abc import x, y | |
>>> A = Matrix(((1, x), (0.2, 3))) | |
>>> B = SparseMatrix(((1, x), (0.2, 3))) | |
>>> A.has(x) | |
True | |
>>> A.has(y) | |
False | |
>>> A.has(Float) | |
True | |
>>> B.has(x) | |
True | |
>>> B.has(y) | |
False | |
>>> B.has(Float) | |
True | |
""" | |
return self._eval_has(*patterns) | |
def is_anti_symmetric(self, simplify=True): | |
"""Check if matrix M is an antisymmetric matrix, | |
that is, M is a square matrix with all M[i, j] == -M[j, i]. | |
When ``simplify=True`` (default), the sum M[i, j] + M[j, i] is | |
simplified before testing to see if it is zero. By default, | |
the SymPy simplify function is used. To use a custom function | |
set simplify to a function that accepts a single argument which | |
returns a simplified expression. To skip simplification, set | |
simplify to False but note that although this will be faster, | |
it may induce false negatives. | |
Examples | |
======== | |
>>> from sympy import Matrix, symbols | |
>>> m = Matrix(2, 2, [0, 1, -1, 0]) | |
>>> m | |
Matrix([ | |
[ 0, 1], | |
[-1, 0]]) | |
>>> m.is_anti_symmetric() | |
True | |
>>> x, y = symbols('x y') | |
>>> m = Matrix(2, 3, [0, 0, x, -y, 0, 0]) | |
>>> m | |
Matrix([ | |
[ 0, 0, x], | |
[-y, 0, 0]]) | |
>>> m.is_anti_symmetric() | |
False | |
>>> from sympy.abc import x, y | |
>>> m = Matrix(3, 3, [0, x**2 + 2*x + 1, y, | |
... -(x + 1)**2, 0, x*y, | |
... -y, -x*y, 0]) | |
Simplification of matrix elements is done by default so even | |
though two elements which should be equal and opposite would not | |
pass an equality test, the matrix is still reported as | |
anti-symmetric: | |
>>> m[0, 1] == -m[1, 0] | |
False | |
>>> m.is_anti_symmetric() | |
True | |
If ``simplify=False`` is used for the case when a Matrix is already | |
simplified, this will speed things up. Here, we see that without | |
simplification the matrix does not appear anti-symmetric: | |
>>> print(m.is_anti_symmetric(simplify=False)) | |
None | |
But if the matrix were already expanded, then it would appear | |
anti-symmetric and simplification in the is_anti_symmetric routine | |
is not needed: | |
>>> m = m.expand() | |
>>> m.is_anti_symmetric(simplify=False) | |
True | |
""" | |
# accept custom simplification | |
simpfunc = simplify | |
if not isfunction(simplify): | |
simpfunc = _utilities_simplify if simplify else lambda x: x | |
if not self.is_square: | |
return False | |
return self._eval_is_anti_symmetric(simpfunc) | |
def is_diagonal(self): | |
"""Check if matrix is diagonal, | |
that is matrix in which the entries outside the main diagonal are all zero. | |
Examples | |
======== | |
>>> from sympy import Matrix, diag | |
>>> m = Matrix(2, 2, [1, 0, 0, 2]) | |
>>> m | |
Matrix([ | |
[1, 0], | |
[0, 2]]) | |
>>> m.is_diagonal() | |
True | |
>>> m = Matrix(2, 2, [1, 1, 0, 2]) | |
>>> m | |
Matrix([ | |
[1, 1], | |
[0, 2]]) | |
>>> m.is_diagonal() | |
False | |
>>> m = diag(1, 2, 3) | |
>>> m | |
Matrix([ | |
[1, 0, 0], | |
[0, 2, 0], | |
[0, 0, 3]]) | |
>>> m.is_diagonal() | |
True | |
See Also | |
======== | |
is_lower | |
is_upper | |
sympy.matrices.matrixbase.MatrixBase.is_diagonalizable | |
diagonalize | |
""" | |
return self._eval_is_diagonal() | |
def is_weakly_diagonally_dominant(self): | |
r"""Tests if the matrix is row weakly diagonally dominant. | |
Explanation | |
=========== | |
A $n, n$ matrix $A$ is row weakly diagonally dominant if | |
.. math:: | |
\left|A_{i, i}\right| \ge \sum_{j = 0, j \neq i}^{n-1} | |
\left|A_{i, j}\right| \quad {\text{for all }} | |
i \in \{ 0, ..., n-1 \} | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> A = Matrix([[3, -2, 1], [1, -3, 2], [-1, 2, 4]]) | |
>>> A.is_weakly_diagonally_dominant | |
True | |
>>> A = Matrix([[-2, 2, 1], [1, 3, 2], [1, -2, 0]]) | |
>>> A.is_weakly_diagonally_dominant | |
False | |
>>> A = Matrix([[-4, 2, 1], [1, 6, 2], [1, -2, 5]]) | |
>>> A.is_weakly_diagonally_dominant | |
True | |
Notes | |
===== | |
If you want to test whether a matrix is column diagonally | |
dominant, you can apply the test after transposing the matrix. | |
""" | |
if not self.is_square: | |
return False | |
rows, cols = self.shape | |
def test_row(i): | |
summation = self.zero | |
for j in range(cols): | |
if i != j: | |
summation += Abs(self[i, j]) | |
return (Abs(self[i, i]) - summation).is_nonnegative | |
return fuzzy_and(test_row(i) for i in range(rows)) | |
def is_strongly_diagonally_dominant(self): | |
r"""Tests if the matrix is row strongly diagonally dominant. | |
Explanation | |
=========== | |
A $n, n$ matrix $A$ is row strongly diagonally dominant if | |
.. math:: | |
\left|A_{i, i}\right| > \sum_{j = 0, j \neq i}^{n-1} | |
\left|A_{i, j}\right| \quad {\text{for all }} | |
i \in \{ 0, ..., n-1 \} | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> A = Matrix([[3, -2, 1], [1, -3, 2], [-1, 2, 4]]) | |
>>> A.is_strongly_diagonally_dominant | |
False | |
>>> A = Matrix([[-2, 2, 1], [1, 3, 2], [1, -2, 0]]) | |
>>> A.is_strongly_diagonally_dominant | |
False | |
>>> A = Matrix([[-4, 2, 1], [1, 6, 2], [1, -2, 5]]) | |
>>> A.is_strongly_diagonally_dominant | |
True | |
Notes | |
===== | |
If you want to test whether a matrix is column diagonally | |
dominant, you can apply the test after transposing the matrix. | |
""" | |
if not self.is_square: | |
return False | |
rows, cols = self.shape | |
def test_row(i): | |
summation = self.zero | |
for j in range(cols): | |
if i != j: | |
summation += Abs(self[i, j]) | |
return (Abs(self[i, i]) - summation).is_positive | |
return fuzzy_and(test_row(i) for i in range(rows)) | |
def is_hermitian(self): | |
"""Checks if the matrix is Hermitian. | |
In a Hermitian matrix element i,j is the complex conjugate of | |
element j,i. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> from sympy import I | |
>>> from sympy.abc import x | |
>>> a = Matrix([[1, I], [-I, 1]]) | |
>>> a | |
Matrix([ | |
[ 1, I], | |
[-I, 1]]) | |
>>> a.is_hermitian | |
True | |
>>> a[0, 0] = 2*I | |
>>> a.is_hermitian | |
False | |
>>> a[0, 0] = x | |
>>> a.is_hermitian | |
>>> a[0, 1] = a[1, 0]*I | |
>>> a.is_hermitian | |
False | |
""" | |
if not self.is_square: | |
return False | |
return self._eval_is_matrix_hermitian(_utilities_simplify) | |
def is_Identity(self) -> FuzzyBool: | |
if not self.is_square: | |
return False | |
return self._eval_is_Identity() | |
def is_lower_hessenberg(self): | |
r"""Checks if the matrix is in the lower-Hessenberg form. | |
The lower hessenberg matrix has zero entries | |
above the first superdiagonal. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> a = Matrix([[1, 2, 0, 0], [5, 2, 3, 0], [3, 4, 3, 7], [5, 6, 1, 1]]) | |
>>> a | |
Matrix([ | |
[1, 2, 0, 0], | |
[5, 2, 3, 0], | |
[3, 4, 3, 7], | |
[5, 6, 1, 1]]) | |
>>> a.is_lower_hessenberg | |
True | |
See Also | |
======== | |
is_upper_hessenberg | |
is_lower | |
""" | |
return self._eval_is_lower_hessenberg() | |
def is_lower(self): | |
"""Check if matrix is a lower triangular matrix. True can be returned | |
even if the matrix is not square. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> m = Matrix(2, 2, [1, 0, 0, 1]) | |
>>> m | |
Matrix([ | |
[1, 0], | |
[0, 1]]) | |
>>> m.is_lower | |
True | |
>>> m = Matrix(4, 3, [0, 0, 0, 2, 0, 0, 1, 4, 0, 6, 6, 5]) | |
>>> m | |
Matrix([ | |
[0, 0, 0], | |
[2, 0, 0], | |
[1, 4, 0], | |
[6, 6, 5]]) | |
>>> m.is_lower | |
True | |
>>> from sympy.abc import x, y | |
>>> m = Matrix(2, 2, [x**2 + y, y**2 + x, 0, x + y]) | |
>>> m | |
Matrix([ | |
[x**2 + y, x + y**2], | |
[ 0, x + y]]) | |
>>> m.is_lower | |
False | |
See Also | |
======== | |
is_upper | |
is_diagonal | |
is_lower_hessenberg | |
""" | |
return self._eval_is_lower() | |
def is_square(self): | |
"""Checks if a matrix is square. | |
A matrix is square if the number of rows equals the number of columns. | |
The empty matrix is square by definition, since the number of rows and | |
the number of columns are both zero. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> a = Matrix([[1, 2, 3], [4, 5, 6]]) | |
>>> b = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) | |
>>> c = Matrix([]) | |
>>> a.is_square | |
False | |
>>> b.is_square | |
True | |
>>> c.is_square | |
True | |
""" | |
return self.rows == self.cols | |
def is_symbolic(self): | |
"""Checks if any elements contain Symbols. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> from sympy.abc import x, y | |
>>> M = Matrix([[x, y], [1, 0]]) | |
>>> M.is_symbolic() | |
True | |
""" | |
return self._eval_is_symbolic() | |
def is_symmetric(self, simplify=True): | |
"""Check if matrix is symmetric matrix, | |
that is square matrix and is equal to its transpose. | |
By default, simplifications occur before testing symmetry. | |
They can be skipped using 'simplify=False'; while speeding things a bit, | |
this may however induce false negatives. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> m = Matrix(2, 2, [0, 1, 1, 2]) | |
>>> m | |
Matrix([ | |
[0, 1], | |
[1, 2]]) | |
>>> m.is_symmetric() | |
True | |
>>> m = Matrix(2, 2, [0, 1, 2, 0]) | |
>>> m | |
Matrix([ | |
[0, 1], | |
[2, 0]]) | |
>>> m.is_symmetric() | |
False | |
>>> m = Matrix(2, 3, [0, 0, 0, 0, 0, 0]) | |
>>> m | |
Matrix([ | |
[0, 0, 0], | |
[0, 0, 0]]) | |
>>> m.is_symmetric() | |
False | |
>>> from sympy.abc import x, y | |
>>> m = Matrix(3, 3, [1, x**2 + 2*x + 1, y, (x + 1)**2, 2, 0, y, 0, 3]) | |
>>> m | |
Matrix([ | |
[ 1, x**2 + 2*x + 1, y], | |
[(x + 1)**2, 2, 0], | |
[ y, 0, 3]]) | |
>>> m.is_symmetric() | |
True | |
If the matrix is already simplified, you may speed-up is_symmetric() | |
test by using 'simplify=False'. | |
>>> bool(m.is_symmetric(simplify=False)) | |
False | |
>>> m1 = m.expand() | |
>>> m1.is_symmetric(simplify=False) | |
True | |
""" | |
simpfunc = simplify | |
if not isfunction(simplify): | |
simpfunc = _utilities_simplify if simplify else lambda x: x | |
if not self.is_square: | |
return False | |
return self._eval_is_symmetric(simpfunc) | |
def is_upper_hessenberg(self): | |
"""Checks if the matrix is the upper-Hessenberg form. | |
The upper hessenberg matrix has zero entries | |
below the first subdiagonal. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> a = Matrix([[1, 4, 2, 3], [3, 4, 1, 7], [0, 2, 3, 4], [0, 0, 1, 3]]) | |
>>> a | |
Matrix([ | |
[1, 4, 2, 3], | |
[3, 4, 1, 7], | |
[0, 2, 3, 4], | |
[0, 0, 1, 3]]) | |
>>> a.is_upper_hessenberg | |
True | |
See Also | |
======== | |
is_lower_hessenberg | |
is_upper | |
""" | |
return self._eval_is_upper_hessenberg() | |
def is_upper(self): | |
"""Check if matrix is an upper triangular matrix. True can be returned | |
even if the matrix is not square. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> m = Matrix(2, 2, [1, 0, 0, 1]) | |
>>> m | |
Matrix([ | |
[1, 0], | |
[0, 1]]) | |
>>> m.is_upper | |
True | |
>>> m = Matrix(4, 3, [5, 1, 9, 0, 4, 6, 0, 0, 5, 0, 0, 0]) | |
>>> m | |
Matrix([ | |
[5, 1, 9], | |
[0, 4, 6], | |
[0, 0, 5], | |
[0, 0, 0]]) | |
>>> m.is_upper | |
True | |
>>> m = Matrix(2, 3, [4, 2, 5, 6, 1, 1]) | |
>>> m | |
Matrix([ | |
[4, 2, 5], | |
[6, 1, 1]]) | |
>>> m.is_upper | |
False | |
See Also | |
======== | |
is_lower | |
is_diagonal | |
is_upper_hessenberg | |
""" | |
return self._eval_is_upper() | |
def is_zero_matrix(self): | |
"""Checks if a matrix is a zero matrix. | |
A matrix is zero if every element is zero. A matrix need not be square | |
to be considered zero. The empty matrix is zero by the principle of | |
vacuous truth. For a matrix that may or may not be zero (e.g. | |
contains a symbol), this will be None | |
Examples | |
======== | |
>>> from sympy import Matrix, zeros | |
>>> from sympy.abc import x | |
>>> a = Matrix([[0, 0], [0, 0]]) | |
>>> b = zeros(3, 4) | |
>>> c = Matrix([[0, 1], [0, 0]]) | |
>>> d = Matrix([]) | |
>>> e = Matrix([[x, 0], [0, 0]]) | |
>>> a.is_zero_matrix | |
True | |
>>> b.is_zero_matrix | |
True | |
>>> c.is_zero_matrix | |
False | |
>>> d.is_zero_matrix | |
True | |
>>> e.is_zero_matrix | |
""" | |
return self._eval_is_zero_matrix() | |
def values(self): | |
"""Return non-zero values of self. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> m = Matrix([[0, 1], [2, 3]]) | |
>>> m.values() | |
[1, 2, 3] | |
See Also | |
======== | |
iter_values | |
tolist | |
flat | |
""" | |
return self._eval_values() | |
def iter_values(self): | |
""" | |
Iterate over non-zero values of self. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> m = Matrix([[0, 1], [2, 3]]) | |
>>> list(m.iter_values()) | |
[1, 2, 3] | |
See Also | |
======== | |
values | |
""" | |
return self._eval_iter_values() | |
def iter_items(self): | |
"""Iterate over indices and values of nonzero items. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> m = Matrix([[0, 1], [2, 3]]) | |
>>> list(m.iter_items()) | |
[((0, 1), 1), ((1, 0), 2), ((1, 1), 3)] | |
See Also | |
======== | |
iter_values | |
todok | |
""" | |
return self._eval_iter_items() | |
def _eval_adjoint(self): | |
return self.transpose().conjugate() | |
def _eval_applyfunc(self, f): | |
cols = self.cols | |
size = self.rows*self.cols | |
dok = self.todok() | |
valmap = {v: f(v) for v in dok.values()} | |
if len(dok) < size and ((fzero := f(S.Zero)) is not S.Zero): | |
out_flat = [fzero]*size | |
for (i, j), v in dok.items(): | |
out_flat[i*cols + j] = valmap[v] | |
out = self._new(self.rows, self.cols, out_flat) | |
else: | |
fdok = {ij: valmap[v] for ij, v in dok.items()} | |
out = self.from_dok(self.rows, self.cols, fdok) | |
return out | |
def _eval_as_real_imag(self): # type: ignore | |
return (self.applyfunc(re), self.applyfunc(im)) | |
def _eval_conjugate(self): | |
return self.applyfunc(lambda x: x.conjugate()) | |
def _eval_permute_cols(self, perm): | |
# apply the permutation to a list | |
mapping = list(perm) | |
def entry(i, j): | |
return self[i, mapping[j]] | |
return self._new(self.rows, self.cols, entry) | |
def _eval_permute_rows(self, perm): | |
# apply the permutation to a list | |
mapping = list(perm) | |
def entry(i, j): | |
return self[mapping[i], j] | |
return self._new(self.rows, self.cols, entry) | |
def _eval_trace(self): | |
return sum(self[i, i] for i in range(self.rows)) | |
def _eval_transpose(self): | |
return self._new(self.cols, self.rows, lambda i, j: self[j, i]) | |
def adjoint(self): | |
"""Conjugate transpose or Hermitian conjugation.""" | |
return self._eval_adjoint() | |
def applyfunc(self, f): | |
"""Apply a function to each element of the matrix. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> m = Matrix(2, 2, lambda i, j: i*2+j) | |
>>> m | |
Matrix([ | |
[0, 1], | |
[2, 3]]) | |
>>> m.applyfunc(lambda i: 2*i) | |
Matrix([ | |
[0, 2], | |
[4, 6]]) | |
""" | |
if not callable(f): | |
raise TypeError("`f` must be callable.") | |
return self._eval_applyfunc(f) | |
def as_real_imag(self, deep=True, **hints): | |
"""Returns a tuple containing the (real, imaginary) part of matrix.""" | |
# XXX: Ignoring deep and hints... | |
return self._eval_as_real_imag() | |
def conjugate(self): | |
"""Return the by-element conjugation. | |
Examples | |
======== | |
>>> from sympy import SparseMatrix, I | |
>>> a = SparseMatrix(((1, 2 + I), (3, 4), (I, -I))) | |
>>> a | |
Matrix([ | |
[1, 2 + I], | |
[3, 4], | |
[I, -I]]) | |
>>> a.C | |
Matrix([ | |
[ 1, 2 - I], | |
[ 3, 4], | |
[-I, I]]) | |
See Also | |
======== | |
transpose: Matrix transposition | |
H: Hermite conjugation | |
sympy.matrices.matrixbase.MatrixBase.D: Dirac conjugation | |
""" | |
return self._eval_conjugate() | |
def doit(self, **hints): | |
return self.applyfunc(lambda x: x.doit(**hints)) | |
def evalf(self, n=15, subs=None, maxn=100, chop=False, strict=False, quad=None, verbose=False): | |
"""Apply evalf() to each element of self.""" | |
options = {'subs':subs, 'maxn':maxn, 'chop':chop, 'strict':strict, | |
'quad':quad, 'verbose':verbose} | |
return self.applyfunc(lambda i: i.evalf(n, **options)) | |
def expand(self, deep=True, modulus=None, power_base=True, power_exp=True, | |
mul=True, log=True, multinomial=True, basic=True, **hints): | |
"""Apply core.function.expand to each entry of the matrix. | |
Examples | |
======== | |
>>> from sympy.abc import x | |
>>> from sympy import Matrix | |
>>> Matrix(1, 1, [x*(x+1)]) | |
Matrix([[x*(x + 1)]]) | |
>>> _.expand() | |
Matrix([[x**2 + x]]) | |
""" | |
return self.applyfunc(lambda x: x.expand( | |
deep, modulus, power_base, power_exp, mul, log, multinomial, basic, | |
**hints)) | |
def H(self): | |
"""Return Hermite conjugate. | |
Examples | |
======== | |
>>> from sympy import Matrix, I | |
>>> m = Matrix((0, 1 + I, 2, 3)) | |
>>> m | |
Matrix([ | |
[ 0], | |
[1 + I], | |
[ 2], | |
[ 3]]) | |
>>> m.H | |
Matrix([[0, 1 - I, 2, 3]]) | |
See Also | |
======== | |
conjugate: By-element conjugation | |
sympy.matrices.matrixbase.MatrixBase.D: Dirac conjugation | |
""" | |
return self.T.C | |
def permute(self, perm, orientation='rows', direction='forward'): | |
r"""Permute the rows or columns of a matrix by the given list of | |
swaps. | |
Parameters | |
========== | |
perm : Permutation, list, or list of lists | |
A representation for the permutation. | |
If it is ``Permutation``, it is used directly with some | |
resizing with respect to the matrix size. | |
If it is specified as list of lists, | |
(e.g., ``[[0, 1], [0, 2]]``), then the permutation is formed | |
from applying the product of cycles. The direction how the | |
cyclic product is applied is described in below. | |
If it is specified as a list, the list should represent | |
an array form of a permutation. (e.g., ``[1, 2, 0]``) which | |
would would form the swapping function | |
`0 \mapsto 1, 1 \mapsto 2, 2\mapsto 0`. | |
orientation : 'rows', 'cols' | |
A flag to control whether to permute the rows or the columns | |
direction : 'forward', 'backward' | |
A flag to control whether to apply the permutations from | |
the start of the list first, or from the back of the list | |
first. | |
For example, if the permutation specification is | |
``[[0, 1], [0, 2]]``, | |
If the flag is set to ``'forward'``, the cycle would be | |
formed as `0 \mapsto 2, 2 \mapsto 1, 1 \mapsto 0`. | |
If the flag is set to ``'backward'``, the cycle would be | |
formed as `0 \mapsto 1, 1 \mapsto 2, 2 \mapsto 0`. | |
If the argument ``perm`` is not in a form of list of lists, | |
this flag takes no effect. | |
Examples | |
======== | |
>>> from sympy import eye | |
>>> M = eye(3) | |
>>> M.permute([[0, 1], [0, 2]], orientation='rows', direction='forward') | |
Matrix([ | |
[0, 0, 1], | |
[1, 0, 0], | |
[0, 1, 0]]) | |
>>> from sympy import eye | |
>>> M = eye(3) | |
>>> M.permute([[0, 1], [0, 2]], orientation='rows', direction='backward') | |
Matrix([ | |
[0, 1, 0], | |
[0, 0, 1], | |
[1, 0, 0]]) | |
Notes | |
===== | |
If a bijective function | |
`\sigma : \mathbb{N}_0 \rightarrow \mathbb{N}_0` denotes the | |
permutation. | |
If the matrix `A` is the matrix to permute, represented as | |
a horizontal or a vertical stack of vectors: | |
.. math:: | |
A = | |
\begin{bmatrix} | |
a_0 \\ a_1 \\ \vdots \\ a_{n-1} | |
\end{bmatrix} = | |
\begin{bmatrix} | |
\alpha_0 & \alpha_1 & \cdots & \alpha_{n-1} | |
\end{bmatrix} | |
If the matrix `B` is the result, the permutation of matrix rows | |
is defined as: | |
.. math:: | |
B := \begin{bmatrix} | |
a_{\sigma(0)} \\ a_{\sigma(1)} \\ \vdots \\ a_{\sigma(n-1)} | |
\end{bmatrix} | |
And the permutation of matrix columns is defined as: | |
.. math:: | |
B := \begin{bmatrix} | |
\alpha_{\sigma(0)} & \alpha_{\sigma(1)} & | |
\cdots & \alpha_{\sigma(n-1)} | |
\end{bmatrix} | |
""" | |
from sympy.combinatorics import Permutation | |
# allow british variants and `columns` | |
if direction == 'forwards': | |
direction = 'forward' | |
if direction == 'backwards': | |
direction = 'backward' | |
if orientation == 'columns': | |
orientation = 'cols' | |
if direction not in ('forward', 'backward'): | |
raise TypeError("direction='{}' is an invalid kwarg. " | |
"Try 'forward' or 'backward'".format(direction)) | |
if orientation not in ('rows', 'cols'): | |
raise TypeError("orientation='{}' is an invalid kwarg. " | |
"Try 'rows' or 'cols'".format(orientation)) | |
if not isinstance(perm, (Permutation, Iterable)): | |
raise ValueError( | |
"{} must be a list, a list of lists, " | |
"or a SymPy permutation object.".format(perm)) | |
# ensure all swaps are in range | |
max_index = self.rows if orientation == 'rows' else self.cols | |
if not all(0 <= t <= max_index for t in flatten(list(perm))): | |
raise IndexError("`swap` indices out of range.") | |
if perm and not isinstance(perm, Permutation) and \ | |
isinstance(perm[0], Iterable): | |
if direction == 'forward': | |
perm = list(reversed(perm)) | |
perm = Permutation(perm, size=max_index+1) | |
else: | |
perm = Permutation(perm, size=max_index+1) | |
if orientation == 'rows': | |
return self._eval_permute_rows(perm) | |
if orientation == 'cols': | |
return self._eval_permute_cols(perm) | |
def permute_cols(self, swaps, direction='forward'): | |
"""Alias for | |
``self.permute(swaps, orientation='cols', direction=direction)`` | |
See Also | |
======== | |
permute | |
""" | |
return self.permute(swaps, orientation='cols', direction=direction) | |
def permute_rows(self, swaps, direction='forward'): | |
"""Alias for | |
``self.permute(swaps, orientation='rows', direction=direction)`` | |
See Also | |
======== | |
permute | |
""" | |
return self.permute(swaps, orientation='rows', direction=direction) | |
def refine(self, assumptions=True): | |
"""Apply refine to each element of the matrix. | |
Examples | |
======== | |
>>> from sympy import Symbol, Matrix, Abs, sqrt, Q | |
>>> x = Symbol('x') | |
>>> Matrix([[Abs(x)**2, sqrt(x**2)],[sqrt(x**2), Abs(x)**2]]) | |
Matrix([ | |
[ Abs(x)**2, sqrt(x**2)], | |
[sqrt(x**2), Abs(x)**2]]) | |
>>> _.refine(Q.real(x)) | |
Matrix([ | |
[ x**2, Abs(x)], | |
[Abs(x), x**2]]) | |
""" | |
return self.applyfunc(lambda x: refine(x, assumptions)) | |
def replace(self, F, G, map=False, simultaneous=True, exact=None): | |
"""Replaces Function F in Matrix entries with Function G. | |
Examples | |
======== | |
>>> from sympy import symbols, Function, Matrix | |
>>> F, G = symbols('F, G', cls=Function) | |
>>> M = Matrix(2, 2, lambda i, j: F(i+j)) ; M | |
Matrix([ | |
[F(0), F(1)], | |
[F(1), F(2)]]) | |
>>> N = M.replace(F,G) | |
>>> N | |
Matrix([ | |
[G(0), G(1)], | |
[G(1), G(2)]]) | |
""" | |
kwargs = {'map': map, 'simultaneous': simultaneous, 'exact': exact} | |
if map: | |
d = {} | |
def func(eij): | |
eij, dij = eij.replace(F, G, **kwargs) | |
d.update(dij) | |
return eij | |
M = self.applyfunc(func) | |
return M, d | |
else: | |
return self.applyfunc(lambda i: i.replace(F, G, **kwargs)) | |
def rot90(self, k=1): | |
"""Rotates Matrix by 90 degrees | |
Parameters | |
========== | |
k : int | |
Specifies how many times the matrix is rotated by 90 degrees | |
(clockwise when positive, counter-clockwise when negative). | |
Examples | |
======== | |
>>> from sympy import Matrix, symbols | |
>>> A = Matrix(2, 2, symbols('a:d')) | |
>>> A | |
Matrix([ | |
[a, b], | |
[c, d]]) | |
Rotating the matrix clockwise one time: | |
>>> A.rot90(1) | |
Matrix([ | |
[c, a], | |
[d, b]]) | |
Rotating the matrix anticlockwise two times: | |
>>> A.rot90(-2) | |
Matrix([ | |
[d, c], | |
[b, a]]) | |
""" | |
mod = k%4 | |
if mod == 0: | |
return self | |
if mod == 1: | |
return self[::-1, ::].T | |
if mod == 2: | |
return self[::-1, ::-1] | |
if mod == 3: | |
return self[::, ::-1].T | |
def simplify(self, **kwargs): | |
"""Apply simplify to each element of the matrix. | |
Examples | |
======== | |
>>> from sympy.abc import x, y | |
>>> from sympy import SparseMatrix, sin, cos | |
>>> SparseMatrix(1, 1, [x*sin(y)**2 + x*cos(y)**2]) | |
Matrix([[x*sin(y)**2 + x*cos(y)**2]]) | |
>>> _.simplify() | |
Matrix([[x]]) | |
""" | |
return self.applyfunc(lambda x: x.simplify(**kwargs)) | |
def subs(self, *args, **kwargs): # should mirror core.basic.subs | |
"""Return a new matrix with subs applied to each entry. | |
Examples | |
======== | |
>>> from sympy.abc import x, y | |
>>> from sympy import SparseMatrix, Matrix | |
>>> SparseMatrix(1, 1, [x]) | |
Matrix([[x]]) | |
>>> _.subs(x, y) | |
Matrix([[y]]) | |
>>> Matrix(_).subs(y, x) | |
Matrix([[x]]) | |
""" | |
if len(args) == 1 and not isinstance(args[0], (dict, set)) and iter(args[0]) and not is_sequence(args[0]): | |
args = (list(args[0]),) | |
return self.applyfunc(lambda x: x.subs(*args, **kwargs)) | |
def trace(self): | |
""" | |
Returns the trace of a square matrix i.e. the sum of the | |
diagonal elements. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> A = Matrix(2, 2, [1, 2, 3, 4]) | |
>>> A.trace() | |
5 | |
""" | |
if self.rows != self.cols: | |
raise NonSquareMatrixError() | |
return self._eval_trace() | |
def transpose(self): | |
""" | |
Returns the transpose of the matrix. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> A = Matrix(2, 2, [1, 2, 3, 4]) | |
>>> A.transpose() | |
Matrix([ | |
[1, 3], | |
[2, 4]]) | |
>>> from sympy import Matrix, I | |
>>> m=Matrix(((1, 2+I), (3, 4))) | |
>>> m | |
Matrix([ | |
[1, 2 + I], | |
[3, 4]]) | |
>>> m.transpose() | |
Matrix([ | |
[ 1, 3], | |
[2 + I, 4]]) | |
>>> m.T == m.transpose() | |
True | |
See Also | |
======== | |
conjugate: By-element conjugation | |
""" | |
return self._eval_transpose() | |
def T(self): | |
'''Matrix transposition''' | |
return self.transpose() | |
def C(self): | |
'''By-element conjugation''' | |
return self.conjugate() | |
def n(self, *args, **kwargs): | |
"""Apply evalf() to each element of self.""" | |
return self.evalf(*args, **kwargs) | |
def xreplace(self, rule): # should mirror core.basic.xreplace | |
"""Return a new matrix with xreplace applied to each entry. | |
Examples | |
======== | |
>>> from sympy.abc import x, y | |
>>> from sympy import SparseMatrix, Matrix | |
>>> SparseMatrix(1, 1, [x]) | |
Matrix([[x]]) | |
>>> _.xreplace({x: y}) | |
Matrix([[y]]) | |
>>> Matrix(_).xreplace({y: x}) | |
Matrix([[x]]) | |
""" | |
return self.applyfunc(lambda x: x.xreplace(rule)) | |
def _eval_simplify(self, **kwargs): | |
# XXX: We can't use self.simplify here as mutable subclasses will | |
# override simplify and have it return None | |
return self.applyfunc(lambda x: x.simplify(**kwargs)) | |
def _eval_trigsimp(self, **opts): | |
from sympy.simplify.trigsimp import trigsimp | |
return self.applyfunc(lambda x: trigsimp(x, **opts)) | |
def upper_triangular(self, k=0): | |
"""Return the elements on and above the kth diagonal of a matrix. | |
If k is not specified then simply returns upper-triangular portion | |
of a matrix | |
Examples | |
======== | |
>>> from sympy import ones | |
>>> A = ones(4) | |
>>> A.upper_triangular() | |
Matrix([ | |
[1, 1, 1, 1], | |
[0, 1, 1, 1], | |
[0, 0, 1, 1], | |
[0, 0, 0, 1]]) | |
>>> A.upper_triangular(2) | |
Matrix([ | |
[0, 0, 1, 1], | |
[0, 0, 0, 1], | |
[0, 0, 0, 0], | |
[0, 0, 0, 0]]) | |
>>> A.upper_triangular(-1) | |
Matrix([ | |
[1, 1, 1, 1], | |
[1, 1, 1, 1], | |
[0, 1, 1, 1], | |
[0, 0, 1, 1]]) | |
""" | |
def entry(i, j): | |
return self[i, j] if i + k <= j else self.zero | |
return self._new(self.rows, self.cols, entry) | |
def lower_triangular(self, k=0): | |
"""Return the elements on and below the kth diagonal of a matrix. | |
If k is not specified then simply returns lower-triangular portion | |
of a matrix | |
Examples | |
======== | |
>>> from sympy import ones | |
>>> A = ones(4) | |
>>> A.lower_triangular() | |
Matrix([ | |
[1, 0, 0, 0], | |
[1, 1, 0, 0], | |
[1, 1, 1, 0], | |
[1, 1, 1, 1]]) | |
>>> A.lower_triangular(-2) | |
Matrix([ | |
[0, 0, 0, 0], | |
[0, 0, 0, 0], | |
[1, 0, 0, 0], | |
[1, 1, 0, 0]]) | |
>>> A.lower_triangular(1) | |
Matrix([ | |
[1, 1, 0, 0], | |
[1, 1, 1, 0], | |
[1, 1, 1, 1], | |
[1, 1, 1, 1]]) | |
""" | |
def entry(i, j): | |
return self[i, j] if i + k >= j else self.zero | |
return self._new(self.rows, self.cols, entry) | |
def _eval_Abs(self): | |
return self._new(self.rows, self.cols, lambda i, j: Abs(self[i, j])) | |
def _eval_add(self, other): | |
return self._new(self.rows, self.cols, | |
lambda i, j: self[i, j] + other[i, j]) | |
def _eval_matrix_mul(self, other): | |
def entry(i, j): | |
vec = [self[i,k]*other[k,j] for k in range(self.cols)] | |
try: | |
return Add(*vec) | |
except (TypeError, SympifyError): | |
# Some matrices don't work with `sum` or `Add` | |
# They don't work with `sum` because `sum` tries to add `0` | |
# Fall back to a safe way to multiply if the `Add` fails. | |
return reduce(lambda a, b: a + b, vec) | |
return self._new(self.rows, other.cols, entry) | |
def _eval_matrix_mul_elementwise(self, other): | |
return self._new(self.rows, self.cols, lambda i, j: self[i,j]*other[i,j]) | |
def _eval_matrix_rmul(self, other): | |
def entry(i, j): | |
return sum(other[i,k]*self[k,j] for k in range(other.cols)) | |
return self._new(other.rows, self.cols, entry) | |
def _eval_pow_by_recursion(self, num): | |
if num == 1: | |
return self | |
if num % 2 == 1: | |
a, b = self, self._eval_pow_by_recursion(num - 1) | |
else: | |
a = b = self._eval_pow_by_recursion(num // 2) | |
return a.multiply(b) | |
def _eval_pow_by_cayley(self, exp): | |
from sympy.discrete.recurrences import linrec_coeffs | |
row = self.shape[0] | |
p = self.charpoly() | |
coeffs = (-p).all_coeffs()[1:] | |
coeffs = linrec_coeffs(coeffs, exp) | |
new_mat = self.eye(row) | |
ans = self.zeros(row) | |
for i in range(row): | |
ans += coeffs[i]*new_mat | |
new_mat *= self | |
return ans | |
def _eval_pow_by_recursion_dotprodsimp(self, num, prevsimp=None): | |
if prevsimp is None: | |
prevsimp = [True]*len(self) | |
if num == 1: | |
return self | |
if num % 2 == 1: | |
a, b = self, self._eval_pow_by_recursion_dotprodsimp(num - 1, | |
prevsimp=prevsimp) | |
else: | |
a = b = self._eval_pow_by_recursion_dotprodsimp(num // 2, | |
prevsimp=prevsimp) | |
m = a.multiply(b, dotprodsimp=False) | |
lenm = len(m) | |
elems = [None]*lenm | |
for i in range(lenm): | |
if prevsimp[i]: | |
elems[i], prevsimp[i] = _dotprodsimp(m[i], withsimp=True) | |
else: | |
elems[i] = m[i] | |
return m._new(m.rows, m.cols, elems) | |
def _eval_scalar_mul(self, other): | |
return self._new(self.rows, self.cols, lambda i, j: self[i,j]*other) | |
def _eval_scalar_rmul(self, other): | |
return self._new(self.rows, self.cols, lambda i, j: other*self[i,j]) | |
def _eval_Mod(self, other): | |
return self._new(self.rows, self.cols, lambda i, j: Mod(self[i, j], other)) | |
# Python arithmetic functions | |
def __abs__(self): | |
"""Returns a new matrix with entry-wise absolute values.""" | |
return self._eval_Abs() | |
def __add__(self, other): | |
"""Return self + other, raising ShapeError if shapes do not match.""" | |
other, T = _coerce_operand(self, other) | |
if T != "is_matrix": | |
return NotImplemented | |
if self.shape != other.shape: | |
raise ShapeError(f"Matrix size mismatch: {self.shape} + {other.shape}.") | |
# Unify matrix types | |
a, b = self, other | |
if a.__class__ != classof(a, b): | |
b, a = a, b | |
return a._eval_add(b) | |
def __truediv__(self, other): | |
return self * (self.one / other) | |
def __matmul__(self, other): | |
self, other, T = _unify_with_other(self, other) | |
if T != "is_matrix": | |
return NotImplemented | |
return self.__mul__(other) | |
def __mod__(self, other): | |
return self.applyfunc(lambda x: x % other) | |
def __mul__(self, other): | |
"""Return self*other where other is either a scalar or a matrix | |
of compatible dimensions. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> A = Matrix([[1, 2, 3], [4, 5, 6]]) | |
>>> 2*A == A*2 == Matrix([[2, 4, 6], [8, 10, 12]]) | |
True | |
>>> B = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) | |
>>> A*B | |
Matrix([ | |
[30, 36, 42], | |
[66, 81, 96]]) | |
>>> B*A | |
Traceback (most recent call last): | |
... | |
ShapeError: Matrices size mismatch. | |
>>> | |
See Also | |
======== | |
matrix_multiply_elementwise | |
""" | |
return self.multiply(other) | |
def multiply(self, other, dotprodsimp=None): | |
"""Same as __mul__() but with optional simplification. | |
Parameters | |
========== | |
dotprodsimp : bool, optional | |
Specifies whether intermediate term algebraic simplification is used | |
during matrix multiplications to control expression blowup and thus | |
speed up calculation. Default is off. | |
""" | |
isimpbool = _get_intermediate_simp_bool(False, dotprodsimp) | |
self, other, T = _unify_with_other(self, other) | |
if T == "possible_scalar": | |
try: | |
return self._eval_scalar_mul(other) | |
except TypeError: | |
return NotImplemented | |
elif T == "is_matrix": | |
if self.shape[1] != other.shape[0]: | |
raise ShapeError(f"Matrix size mismatch: {self.shape} * {other.shape}.") | |
m = self._eval_matrix_mul(other) | |
if isimpbool: | |
m = m._new(m.rows, m.cols, [_dotprodsimp(e) for e in m]) | |
return m | |
else: | |
return NotImplemented | |
def multiply_elementwise(self, other): | |
"""Return the Hadamard product (elementwise product) of A and B | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> A = Matrix([[0, 1, 2], [3, 4, 5]]) | |
>>> B = Matrix([[1, 10, 100], [100, 10, 1]]) | |
>>> A.multiply_elementwise(B) | |
Matrix([ | |
[ 0, 10, 200], | |
[300, 40, 5]]) | |
See Also | |
======== | |
sympy.matrices.matrixbase.MatrixBase.cross | |
sympy.matrices.matrixbase.MatrixBase.dot | |
multiply | |
""" | |
if self.shape != other.shape: | |
raise ShapeError("Matrix shapes must agree {} != {}".format(self.shape, other.shape)) | |
return self._eval_matrix_mul_elementwise(other) | |
def __neg__(self): | |
return self._eval_scalar_mul(-1) | |
def __pow__(self, exp): | |
"""Return self**exp a scalar or symbol.""" | |
return self.pow(exp) | |
def pow(self, exp, method=None): | |
r"""Return self**exp a scalar or symbol. | |
Parameters | |
========== | |
method : multiply, mulsimp, jordan, cayley | |
If multiply then it returns exponentiation using recursion. | |
If jordan then Jordan form exponentiation will be used. | |
If cayley then the exponentiation is done using Cayley-Hamilton | |
theorem. | |
If mulsimp then the exponentiation is done using recursion | |
with dotprodsimp. This specifies whether intermediate term | |
algebraic simplification is used during naive matrix power to | |
control expression blowup and thus speed up calculation. | |
If None, then it heuristically decides which method to use. | |
""" | |
if method is not None and method not in ['multiply', 'mulsimp', 'jordan', 'cayley']: | |
raise TypeError('No such method') | |
if self.rows != self.cols: | |
raise NonSquareMatrixError() | |
a = self | |
jordan_pow = getattr(a, '_matrix_pow_by_jordan_blocks', None) | |
exp = sympify(exp) | |
if exp.is_zero: | |
return a._new(a.rows, a.cols, lambda i, j: int(i == j)) | |
if exp == 1: | |
return a | |
diagonal = getattr(a, 'is_diagonal', None) | |
if diagonal is not None and diagonal(): | |
return a._new(a.rows, a.cols, lambda i, j: a[i,j]**exp if i == j else 0) | |
if exp.is_Number and exp % 1 == 0: | |
if a.rows == 1: | |
return a._new([[a[0]**exp]]) | |
if exp < 0: | |
exp = -exp | |
a = a.inv() | |
# When certain conditions are met, | |
# Jordan block algorithm is faster than | |
# computation by recursion. | |
if method == 'jordan': | |
try: | |
return jordan_pow(exp) | |
except MatrixError: | |
if method == 'jordan': | |
raise | |
elif method == 'cayley': | |
if not exp.is_Number or exp % 1 != 0: | |
raise ValueError("cayley method is only valid for integer powers") | |
return a._eval_pow_by_cayley(exp) | |
elif method == "mulsimp": | |
if not exp.is_Number or exp % 1 != 0: | |
raise ValueError("mulsimp method is only valid for integer powers") | |
return a._eval_pow_by_recursion_dotprodsimp(exp) | |
elif method == "multiply": | |
if not exp.is_Number or exp % 1 != 0: | |
raise ValueError("multiply method is only valid for integer powers") | |
return a._eval_pow_by_recursion(exp) | |
elif method is None and exp.is_Number and exp % 1 == 0: | |
if exp.is_Float: | |
exp = Integer(exp) | |
# Decide heuristically which method to apply | |
if a.rows == 2 and exp > 100000: | |
return jordan_pow(exp) | |
elif _get_intermediate_simp_bool(True, None): | |
return a._eval_pow_by_recursion_dotprodsimp(exp) | |
elif exp > 10000: | |
return a._eval_pow_by_cayley(exp) | |
else: | |
return a._eval_pow_by_recursion(exp) | |
if jordan_pow: | |
try: | |
return jordan_pow(exp) | |
except NonInvertibleMatrixError: | |
# Raised by jordan_pow on zero determinant matrix unless exp is | |
# definitely known to be a non-negative integer. | |
# Here we raise if n is definitely not a non-negative integer | |
# but otherwise we can leave this as an unevaluated MatPow. | |
if exp.is_integer is False or exp.is_nonnegative is False: | |
raise | |
from sympy.matrices.expressions import MatPow | |
return MatPow(a, exp) | |
def __radd__(self, other): | |
return self + other | |
def __rmatmul__(self, other): | |
self, other, T = _unify_with_other(self, other) | |
if T != "is_matrix": | |
return NotImplemented | |
return self.__rmul__(other) | |
def __rmul__(self, other): | |
return self.rmultiply(other) | |
def rmultiply(self, other, dotprodsimp=None): | |
"""Same as __rmul__() but with optional simplification. | |
Parameters | |
========== | |
dotprodsimp : bool, optional | |
Specifies whether intermediate term algebraic simplification is used | |
during matrix multiplications to control expression blowup and thus | |
speed up calculation. Default is off. | |
""" | |
isimpbool = _get_intermediate_simp_bool(False, dotprodsimp) | |
self, other, T = _unify_with_other(self, other) | |
if T == "possible_scalar": | |
try: | |
return self._eval_scalar_rmul(other) | |
except TypeError: | |
return NotImplemented | |
elif T == "is_matrix": | |
if self.shape[0] != other.shape[1]: | |
raise ShapeError("Matrix size mismatch.") | |
m = self._eval_matrix_rmul(other) | |
if isimpbool: | |
return m._new(m.rows, m.cols, [_dotprodsimp(e) for e in m]) | |
return m | |
else: | |
return NotImplemented | |
def __rsub__(self, a): | |
return (-self) + a | |
def __sub__(self, a): | |
return self + (-a) | |
def _eval_det_bareiss(self, iszerofunc=_is_zero_after_expand_mul): | |
return _det_bareiss(self, iszerofunc=iszerofunc) | |
def _eval_det_berkowitz(self): | |
return _det_berkowitz(self) | |
def _eval_det_lu(self, iszerofunc=_iszero, simpfunc=None): | |
return _det_LU(self, iszerofunc=iszerofunc, simpfunc=simpfunc) | |
def _eval_det_bird(self): | |
return _det_bird(self) | |
def _eval_det_laplace(self): | |
return _det_laplace(self) | |
def _eval_determinant(self): # for expressions.determinant.Determinant | |
return _det(self) | |
def adjugate(self, method="berkowitz"): | |
return _adjugate(self, method=method) | |
def charpoly(self, x='lambda', simplify=_utilities_simplify): | |
return _charpoly(self, x=x, simplify=simplify) | |
def cofactor(self, i, j, method="berkowitz"): | |
return _cofactor(self, i, j, method=method) | |
def cofactor_matrix(self, method="berkowitz"): | |
return _cofactor_matrix(self, method=method) | |
def det(self, method="bareiss", iszerofunc=None): | |
return _det(self, method=method, iszerofunc=iszerofunc) | |
def per(self): | |
return _per(self) | |
def minor(self, i, j, method="berkowitz"): | |
return _minor(self, i, j, method=method) | |
def minor_submatrix(self, i, j): | |
return _minor_submatrix(self, i, j) | |
_find_reasonable_pivot.__doc__ = _find_reasonable_pivot.__doc__ | |
_find_reasonable_pivot_naive.__doc__ = _find_reasonable_pivot_naive.__doc__ | |
_eval_det_bareiss.__doc__ = _det_bareiss.__doc__ | |
_eval_det_berkowitz.__doc__ = _det_berkowitz.__doc__ | |
_eval_det_bird.__doc__ = _det_bird.__doc__ | |
_eval_det_laplace.__doc__ = _det_laplace.__doc__ | |
_eval_det_lu.__doc__ = _det_LU.__doc__ | |
_eval_determinant.__doc__ = _det.__doc__ | |
adjugate.__doc__ = _adjugate.__doc__ | |
charpoly.__doc__ = _charpoly.__doc__ | |
cofactor.__doc__ = _cofactor.__doc__ | |
cofactor_matrix.__doc__ = _cofactor_matrix.__doc__ | |
det.__doc__ = _det.__doc__ | |
per.__doc__ = _per.__doc__ | |
minor.__doc__ = _minor.__doc__ | |
minor_submatrix.__doc__ = _minor_submatrix.__doc__ | |
def echelon_form(self, iszerofunc=_iszero, simplify=False, with_pivots=False): | |
return _echelon_form(self, iszerofunc=iszerofunc, simplify=simplify, | |
with_pivots=with_pivots) | |
def is_echelon(self): | |
return _is_echelon(self) | |
def rank(self, iszerofunc=_iszero, simplify=False): | |
return _rank(self, iszerofunc=iszerofunc, simplify=simplify) | |
def rref_rhs(self, rhs): | |
"""Return reduced row-echelon form of matrix, matrix showing | |
rhs after reduction steps. ``rhs`` must have the same number | |
of rows as ``self``. | |
Examples | |
======== | |
>>> from sympy import Matrix, symbols | |
>>> r1, r2 = symbols('r1 r2') | |
>>> Matrix([[1, 1], [2, 1]]).rref_rhs(Matrix([r1, r2])) | |
(Matrix([ | |
[1, 0], | |
[0, 1]]), Matrix([ | |
[ -r1 + r2], | |
[2*r1 - r2]])) | |
""" | |
r, _ = _rref(self.hstack(self, self.eye(self.rows), rhs)) | |
return r[:, :self.cols], r[:, -rhs.cols:] | |
def rref(self, iszerofunc=_iszero, simplify=False, pivots=True, | |
normalize_last=True): | |
return _rref(self, iszerofunc=iszerofunc, simplify=simplify, | |
pivots=pivots, normalize_last=normalize_last) | |
echelon_form.__doc__ = _echelon_form.__doc__ | |
is_echelon.__doc__ = _is_echelon.__doc__ | |
rank.__doc__ = _rank.__doc__ | |
rref.__doc__ = _rref.__doc__ | |
def _normalize_op_args(self, op, col, k, col1, col2, error_str="col"): | |
"""Validate the arguments for a row/column operation. ``error_str`` | |
can be one of "row" or "col" depending on the arguments being parsed.""" | |
if op not in ["n->kn", "n<->m", "n->n+km"]: | |
raise ValueError("Unknown {} operation '{}'. Valid col operations " | |
"are 'n->kn', 'n<->m', 'n->n+km'".format(error_str, op)) | |
# define self_col according to error_str | |
self_cols = self.cols if error_str == 'col' else self.rows | |
# normalize and validate the arguments | |
if op == "n->kn": | |
col = col if col is not None else col1 | |
if col is None or k is None: | |
raise ValueError("For a {0} operation 'n->kn' you must provide the " | |
"kwargs `{0}` and `k`".format(error_str)) | |
if not 0 <= col < self_cols: | |
raise ValueError("This matrix does not have a {} '{}'".format(error_str, col)) | |
elif op == "n<->m": | |
# we need two cols to swap. It does not matter | |
# how they were specified, so gather them together and | |
# remove `None` | |
cols = {col, k, col1, col2}.difference([None]) | |
if len(cols) > 2: | |
# maybe the user left `k` by mistake? | |
cols = {col, col1, col2}.difference([None]) | |
if len(cols) != 2: | |
raise ValueError("For a {0} operation 'n<->m' you must provide the " | |
"kwargs `{0}1` and `{0}2`".format(error_str)) | |
col1, col2 = cols | |
if not 0 <= col1 < self_cols: | |
raise ValueError("This matrix does not have a {} '{}'".format(error_str, col1)) | |
if not 0 <= col2 < self_cols: | |
raise ValueError("This matrix does not have a {} '{}'".format(error_str, col2)) | |
elif op == "n->n+km": | |
col = col1 if col is None else col | |
col2 = col1 if col2 is None else col2 | |
if col is None or col2 is None or k is None: | |
raise ValueError("For a {0} operation 'n->n+km' you must provide the " | |
"kwargs `{0}`, `k`, and `{0}2`".format(error_str)) | |
if col == col2: | |
raise ValueError("For a {0} operation 'n->n+km' `{0}` and `{0}2` must " | |
"be different.".format(error_str)) | |
if not 0 <= col < self_cols: | |
raise ValueError("This matrix does not have a {} '{}'".format(error_str, col)) | |
if not 0 <= col2 < self_cols: | |
raise ValueError("This matrix does not have a {} '{}'".format(error_str, col2)) | |
else: | |
raise ValueError('invalid operation %s' % repr(op)) | |
return op, col, k, col1, col2 | |
def _eval_col_op_multiply_col_by_const(self, col, k): | |
def entry(i, j): | |
if j == col: | |
return k * self[i, j] | |
return self[i, j] | |
return self._new(self.rows, self.cols, entry) | |
def _eval_col_op_swap(self, col1, col2): | |
def entry(i, j): | |
if j == col1: | |
return self[i, col2] | |
elif j == col2: | |
return self[i, col1] | |
return self[i, j] | |
return self._new(self.rows, self.cols, entry) | |
def _eval_col_op_add_multiple_to_other_col(self, col, k, col2): | |
def entry(i, j): | |
if j == col: | |
return self[i, j] + k * self[i, col2] | |
return self[i, j] | |
return self._new(self.rows, self.cols, entry) | |
def _eval_row_op_swap(self, row1, row2): | |
def entry(i, j): | |
if i == row1: | |
return self[row2, j] | |
elif i == row2: | |
return self[row1, j] | |
return self[i, j] | |
return self._new(self.rows, self.cols, entry) | |
def _eval_row_op_multiply_row_by_const(self, row, k): | |
def entry(i, j): | |
if i == row: | |
return k * self[i, j] | |
return self[i, j] | |
return self._new(self.rows, self.cols, entry) | |
def _eval_row_op_add_multiple_to_other_row(self, row, k, row2): | |
def entry(i, j): | |
if i == row: | |
return self[i, j] + k * self[row2, j] | |
return self[i, j] | |
return self._new(self.rows, self.cols, entry) | |
def elementary_col_op(self, op="n->kn", col=None, k=None, col1=None, col2=None): | |
"""Performs the elementary column operation `op`. | |
`op` may be one of | |
* ``"n->kn"`` (column n goes to k*n) | |
* ``"n<->m"`` (swap column n and column m) | |
* ``"n->n+km"`` (column n goes to column n + k*column m) | |
Parameters | |
========== | |
op : string; the elementary row operation | |
col : the column to apply the column operation | |
k : the multiple to apply in the column operation | |
col1 : one column of a column swap | |
col2 : second column of a column swap or column "m" in the column operation | |
"n->n+km" | |
""" | |
op, col, k, col1, col2 = self._normalize_op_args(op, col, k, col1, col2, "col") | |
# now that we've validated, we're all good to dispatch | |
if op == "n->kn": | |
return self._eval_col_op_multiply_col_by_const(col, k) | |
if op == "n<->m": | |
return self._eval_col_op_swap(col1, col2) | |
if op == "n->n+km": | |
return self._eval_col_op_add_multiple_to_other_col(col, k, col2) | |
def elementary_row_op(self, op="n->kn", row=None, k=None, row1=None, row2=None): | |
"""Performs the elementary row operation `op`. | |
`op` may be one of | |
* ``"n->kn"`` (row n goes to k*n) | |
* ``"n<->m"`` (swap row n and row m) | |
* ``"n->n+km"`` (row n goes to row n + k*row m) | |
Parameters | |
========== | |
op : string; the elementary row operation | |
row : the row to apply the row operation | |
k : the multiple to apply in the row operation | |
row1 : one row of a row swap | |
row2 : second row of a row swap or row "m" in the row operation | |
"n->n+km" | |
""" | |
op, row, k, row1, row2 = self._normalize_op_args(op, row, k, row1, row2, "row") | |
# now that we've validated, we're all good to dispatch | |
if op == "n->kn": | |
return self._eval_row_op_multiply_row_by_const(row, k) | |
if op == "n<->m": | |
return self._eval_row_op_swap(row1, row2) | |
if op == "n->n+km": | |
return self._eval_row_op_add_multiple_to_other_row(row, k, row2) | |
def columnspace(self, simplify=False): | |
return _columnspace(self, simplify=simplify) | |
def nullspace(self, simplify=False, iszerofunc=_iszero): | |
return _nullspace(self, simplify=simplify, iszerofunc=iszerofunc) | |
def rowspace(self, simplify=False): | |
return _rowspace(self, simplify=simplify) | |
# This is a classmethod but is converted to such later in order to allow | |
# assignment of __doc__ since that does not work for already wrapped | |
# classmethods in Python 3.6. | |
def orthogonalize(cls, *vecs, **kwargs): | |
return _orthogonalize(cls, *vecs, **kwargs) | |
columnspace.__doc__ = _columnspace.__doc__ | |
nullspace.__doc__ = _nullspace.__doc__ | |
rowspace.__doc__ = _rowspace.__doc__ | |
orthogonalize.__doc__ = _orthogonalize.__doc__ | |
orthogonalize = classmethod(orthogonalize) # type:ignore | |
def eigenvals(self, error_when_incomplete=True, **flags): | |
return _eigenvals(self, error_when_incomplete=error_when_incomplete, **flags) | |
def eigenvects(self, error_when_incomplete=True, iszerofunc=_iszero, **flags): | |
return _eigenvects(self, error_when_incomplete=error_when_incomplete, | |
iszerofunc=iszerofunc, **flags) | |
def is_diagonalizable(self, reals_only=False, **kwargs): | |
return _is_diagonalizable(self, reals_only=reals_only, **kwargs) | |
def diagonalize(self, reals_only=False, sort=False, normalize=False): | |
return _diagonalize(self, reals_only=reals_only, sort=sort, | |
normalize=normalize) | |
def bidiagonalize(self, upper=True): | |
return _bidiagonalize(self, upper=upper) | |
def bidiagonal_decomposition(self, upper=True): | |
return _bidiagonal_decomposition(self, upper=upper) | |
def is_positive_definite(self): | |
return _is_positive_definite(self) | |
def is_positive_semidefinite(self): | |
return _is_positive_semidefinite(self) | |
def is_negative_definite(self): | |
return _is_negative_definite(self) | |
def is_negative_semidefinite(self): | |
return _is_negative_semidefinite(self) | |
def is_indefinite(self): | |
return _is_indefinite(self) | |
def jordan_form(self, calc_transform=True, **kwargs): | |
return _jordan_form(self, calc_transform=calc_transform, **kwargs) | |
def left_eigenvects(self, **flags): | |
return _left_eigenvects(self, **flags) | |
def singular_values(self): | |
return _singular_values(self) | |
eigenvals.__doc__ = _eigenvals.__doc__ | |
eigenvects.__doc__ = _eigenvects.__doc__ | |
is_diagonalizable.__doc__ = _is_diagonalizable.__doc__ | |
diagonalize.__doc__ = _diagonalize.__doc__ | |
is_positive_definite.__doc__ = _is_positive_definite.__doc__ | |
is_positive_semidefinite.__doc__ = _is_positive_semidefinite.__doc__ | |
is_negative_definite.__doc__ = _is_negative_definite.__doc__ | |
is_negative_semidefinite.__doc__ = _is_negative_semidefinite.__doc__ | |
is_indefinite.__doc__ = _is_indefinite.__doc__ | |
jordan_form.__doc__ = _jordan_form.__doc__ | |
left_eigenvects.__doc__ = _left_eigenvects.__doc__ | |
singular_values.__doc__ = _singular_values.__doc__ | |
bidiagonalize.__doc__ = _bidiagonalize.__doc__ | |
bidiagonal_decomposition.__doc__ = _bidiagonal_decomposition.__doc__ | |
def diff(self, *args, evaluate=True, **kwargs): | |
"""Calculate the derivative of each element in the matrix. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> from sympy.abc import x, y | |
>>> M = Matrix([[x, y], [1, 0]]) | |
>>> M.diff(x) | |
Matrix([ | |
[1, 0], | |
[0, 0]]) | |
See Also | |
======== | |
integrate | |
limit | |
""" | |
# XXX this should be handled here rather than in Derivative | |
from sympy.tensor.array.array_derivatives import ArrayDerivative | |
deriv = ArrayDerivative(self, *args, evaluate=evaluate) | |
# XXX This can rather changed to always return immutable matrix | |
if not isinstance(self, Basic) and evaluate: | |
return deriv.as_mutable() | |
return deriv | |
def _eval_derivative(self, arg): | |
return self.applyfunc(lambda x: x.diff(arg)) | |
def integrate(self, *args, **kwargs): | |
"""Integrate each element of the matrix. ``args`` will | |
be passed to the ``integrate`` function. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> from sympy.abc import x, y | |
>>> M = Matrix([[x, y], [1, 0]]) | |
>>> M.integrate((x, )) | |
Matrix([ | |
[x**2/2, x*y], | |
[ x, 0]]) | |
>>> M.integrate((x, 0, 2)) | |
Matrix([ | |
[2, 2*y], | |
[2, 0]]) | |
See Also | |
======== | |
limit | |
diff | |
""" | |
return self.applyfunc(lambda x: x.integrate(*args, **kwargs)) | |
def jacobian(self, X): | |
"""Calculates the Jacobian matrix (derivative of a vector-valued function). | |
Parameters | |
========== | |
``self`` : vector of expressions representing functions f_i(x_1, ..., x_n). | |
X : set of x_i's in order, it can be a list or a Matrix | |
Both ``self`` and X can be a row or a column matrix in any order | |
(i.e., jacobian() should always work). | |
Examples | |
======== | |
>>> from sympy import sin, cos, Matrix | |
>>> from sympy.abc import rho, phi | |
>>> X = Matrix([rho*cos(phi), rho*sin(phi), rho**2]) | |
>>> Y = Matrix([rho, phi]) | |
>>> X.jacobian(Y) | |
Matrix([ | |
[cos(phi), -rho*sin(phi)], | |
[sin(phi), rho*cos(phi)], | |
[ 2*rho, 0]]) | |
>>> X = Matrix([rho*cos(phi), rho*sin(phi)]) | |
>>> X.jacobian(Y) | |
Matrix([ | |
[cos(phi), -rho*sin(phi)], | |
[sin(phi), rho*cos(phi)]]) | |
See Also | |
======== | |
hessian | |
wronskian | |
""" | |
from sympy.matrices.matrixbase import MatrixBase | |
if not isinstance(X, MatrixBase): | |
X = self._new(X) | |
# Both X and ``self`` can be a row or a column matrix, so we need to make | |
# sure all valid combinations work, but everything else fails: | |
if self.shape[0] == 1: | |
m = self.shape[1] | |
elif self.shape[1] == 1: | |
m = self.shape[0] | |
else: | |
raise TypeError("``self`` must be a row or a column matrix") | |
if X.shape[0] == 1: | |
n = X.shape[1] | |
elif X.shape[1] == 1: | |
n = X.shape[0] | |
else: | |
raise TypeError("X must be a row or a column matrix") | |
# m is the number of functions and n is the number of variables | |
# computing the Jacobian is now easy: | |
return self._new(m, n, lambda j, i: self[j].diff(X[i])) | |
def limit(self, *args): | |
"""Calculate the limit of each element in the matrix. | |
``args`` will be passed to the ``limit`` function. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> from sympy.abc import x, y | |
>>> M = Matrix([[x, y], [1, 0]]) | |
>>> M.limit(x, 2) | |
Matrix([ | |
[2, y], | |
[1, 0]]) | |
See Also | |
======== | |
integrate | |
diff | |
""" | |
return self.applyfunc(lambda x: x.limit(*args)) | |
def berkowitz_charpoly(self, x=Dummy('lambda'), simplify=_utilities_simplify): | |
return self.charpoly(x=x) | |
def berkowitz_det(self): | |
"""Computes determinant using Berkowitz method. | |
See Also | |
======== | |
det | |
""" | |
return self.det(method='berkowitz') | |
def berkowitz_eigenvals(self, **flags): | |
"""Computes eigenvalues of a Matrix using Berkowitz method.""" | |
return self.eigenvals(**flags) | |
def berkowitz_minors(self): | |
"""Computes principal minors using Berkowitz method.""" | |
sign, minors = self.one, [] | |
for poly in self.berkowitz(): | |
minors.append(sign * poly[-1]) | |
sign = -sign | |
return tuple(minors) | |
def berkowitz(self): | |
from sympy.matrices import zeros | |
berk = ((1,),) | |
if not self: | |
return berk | |
if not self.is_square: | |
raise NonSquareMatrixError() | |
A, N = self, self.rows | |
transforms = [0] * (N - 1) | |
for n in range(N, 1, -1): | |
T, k = zeros(n + 1, n), n - 1 | |
R, C = -A[k, :k], A[:k, k] | |
A, a = A[:k, :k], -A[k, k] | |
items = [C] | |
for i in range(0, n - 2): | |
items.append(A * items[i]) | |
for i, B in enumerate(items): | |
items[i] = (R * B)[0, 0] | |
items = [self.one, a] + items | |
for i in range(n): | |
T[i:, i] = items[:n - i + 1] | |
transforms[k - 1] = T | |
polys = [self._new([self.one, -A[0, 0]])] | |
for i, T in enumerate(transforms): | |
polys.append(T * polys[i]) | |
return berk + tuple(map(tuple, polys)) | |
def cofactorMatrix(self, method="berkowitz"): | |
return self.cofactor_matrix(method=method) | |
def det_bareis(self): | |
return _det_bareiss(self) | |
def det_LU_decomposition(self): | |
"""Compute matrix determinant using LU decomposition. | |
Note that this method fails if the LU decomposition itself | |
fails. In particular, if the matrix has no inverse this method | |
will fail. | |
TODO: Implement algorithm for sparse matrices (SFF), | |
http://www.eecis.udel.edu/~saunders/papers/sffge/it5.ps. | |
See Also | |
======== | |
det | |
berkowitz_det | |
""" | |
return self.det(method='lu') | |
def jordan_cell(self, eigenval, n): | |
return self.jordan_block(size=n, eigenvalue=eigenval) | |
def jordan_cells(self, calc_transformation=True): | |
P, J = self.jordan_form() | |
return P, J.get_diag_blocks() | |
def minorEntry(self, i, j, method="berkowitz"): | |
return self.minor(i, j, method=method) | |
def minorMatrix(self, i, j): | |
return self.minor_submatrix(i, j) | |
def permuteBkwd(self, perm): | |
"""Permute the rows of the matrix with the given permutation in reverse.""" | |
return self.permute_rows(perm, direction='backward') | |
def permuteFwd(self, perm): | |
"""Permute the rows of the matrix with the given permutation.""" | |
return self.permute_rows(perm, direction='forward') | |
def kind(self) -> MatrixKind: | |
elem_kinds = {e.kind for e in self.flat()} | |
if len(elem_kinds) == 1: | |
elemkind, = elem_kinds | |
else: | |
elemkind = UndefinedKind | |
return MatrixKind(elemkind) | |
def flat(self): | |
""" | |
Returns a flat list of all elements in the matrix. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> m = Matrix([[0, 2], [3, 4]]) | |
>>> m.flat() | |
[0, 2, 3, 4] | |
See Also | |
======== | |
tolist | |
values | |
""" | |
return [self[i, j] for i in range(self.rows) for j in range(self.cols)] | |
def __array__(self, dtype=object, copy=None): | |
if copy is not None and not copy: | |
raise TypeError("Cannot implement copy=False when converting Matrix to ndarray") | |
from .dense import matrix2numpy | |
return matrix2numpy(self, dtype=dtype) | |
def __len__(self): | |
"""Return the number of elements of ``self``. | |
Implemented mainly so bool(Matrix()) == False. | |
""" | |
return self.rows * self.cols | |
def _matrix_pow_by_jordan_blocks(self, num): | |
from sympy.matrices import diag, MutableMatrix | |
def jordan_cell_power(jc, n): | |
N = jc.shape[0] | |
l = jc[0,0] | |
if l.is_zero: | |
if N == 1 and n.is_nonnegative: | |
jc[0,0] = l**n | |
elif not (n.is_integer and n.is_nonnegative): | |
raise NonInvertibleMatrixError("Non-invertible matrix can only be raised to a nonnegative integer") | |
else: | |
for i in range(N): | |
jc[0,i] = KroneckerDelta(i, n) | |
else: | |
for i in range(N): | |
bn = binomial(n, i) | |
if isinstance(bn, binomial): | |
bn = bn._eval_expand_func() | |
jc[0,i] = l**(n-i)*bn | |
for i in range(N): | |
for j in range(1, N-i): | |
jc[j,i+j] = jc [j-1,i+j-1] | |
P, J = self.jordan_form() | |
jordan_cells = J.get_diag_blocks() | |
# Make sure jordan_cells matrices are mutable: | |
jordan_cells = [MutableMatrix(j) for j in jordan_cells] | |
for j in jordan_cells: | |
jordan_cell_power(j, num) | |
return self._new(P.multiply(diag(*jordan_cells)) | |
.multiply(P.inv())) | |
def __str__(self): | |
if S.Zero in self.shape: | |
return 'Matrix(%s, %s, [])' % (self.rows, self.cols) | |
return "Matrix(%s)" % str(self.tolist()) | |
def _format_str(self, printer=None): | |
if not printer: | |
printer = StrPrinter() | |
# Handle zero dimensions: | |
if S.Zero in self.shape: | |
return 'Matrix(%s, %s, [])' % (self.rows, self.cols) | |
if self.rows == 1: | |
return "Matrix([%s])" % self.table(printer, rowsep=',\n') | |
return "Matrix([\n%s])" % self.table(printer, rowsep=',\n') | |
def irregular(cls, ntop, *matrices, **kwargs): | |
"""Return a matrix filled by the given matrices which | |
are listed in order of appearance from left to right, top to | |
bottom as they first appear in the matrix. They must fill the | |
matrix completely. | |
Examples | |
======== | |
>>> from sympy import ones, Matrix | |
>>> Matrix.irregular(3, ones(2,1), ones(3,3)*2, ones(2,2)*3, | |
... ones(1,1)*4, ones(2,2)*5, ones(1,2)*6, ones(1,2)*7) | |
Matrix([ | |
[1, 2, 2, 2, 3, 3], | |
[1, 2, 2, 2, 3, 3], | |
[4, 2, 2, 2, 5, 5], | |
[6, 6, 7, 7, 5, 5]]) | |
""" | |
ntop = as_int(ntop) | |
# make sure we are working with explicit matrices | |
b = [i.as_explicit() if hasattr(i, 'as_explicit') else i | |
for i in matrices] | |
q = list(range(len(b))) | |
dat = [i.rows for i in b] | |
active = [q.pop(0) for _ in range(ntop)] | |
cols = sum(b[i].cols for i in active) | |
rows = [] | |
while any(dat): | |
r = [] | |
for a, j in enumerate(active): | |
r.extend(b[j][-dat[j], :]) | |
dat[j] -= 1 | |
if dat[j] == 0 and q: | |
active[a] = q.pop(0) | |
if len(r) != cols: | |
raise ValueError(filldedent(''' | |
Matrices provided do not appear to fill | |
the space completely.''')) | |
rows.append(r) | |
return cls._new(rows) | |
def _handle_ndarray(cls, arg): | |
# NumPy array or matrix or some other object that implements | |
# __array__. So let's first use this method to get a | |
# numpy.array() and then make a Python list out of it. | |
arr = arg.__array__() | |
if len(arr.shape) == 2: | |
rows, cols = arr.shape[0], arr.shape[1] | |
flat_list = [cls._sympify(i) for i in arr.ravel()] | |
return rows, cols, flat_list | |
elif len(arr.shape) == 1: | |
flat_list = [cls._sympify(i) for i in arr] | |
return arr.shape[0], 1, flat_list | |
else: | |
raise NotImplementedError( | |
"SymPy supports just 1D and 2D matrices") | |
def _handle_creation_inputs(cls, *args, **kwargs): | |
"""Return the number of rows, cols and flat matrix elements. | |
Examples | |
======== | |
>>> from sympy import Matrix, I | |
Matrix can be constructed as follows: | |
* from a nested list of iterables | |
>>> Matrix( ((1, 2+I), (3, 4)) ) | |
Matrix([ | |
[1, 2 + I], | |
[3, 4]]) | |
* from un-nested iterable (interpreted as a column) | |
>>> Matrix( [1, 2] ) | |
Matrix([ | |
[1], | |
[2]]) | |
* from un-nested iterable with dimensions | |
>>> Matrix(1, 2, [1, 2] ) | |
Matrix([[1, 2]]) | |
* from no arguments (a 0 x 0 matrix) | |
>>> Matrix() | |
Matrix(0, 0, []) | |
* from a rule | |
>>> Matrix(2, 2, lambda i, j: i/(j + 1) ) | |
Matrix([ | |
[0, 0], | |
[1, 1/2]]) | |
See Also | |
======== | |
irregular - filling a matrix with irregular blocks | |
""" | |
from sympy.matrices import SparseMatrix | |
from sympy.matrices.expressions.matexpr import MatrixSymbol | |
from sympy.matrices.expressions.blockmatrix import BlockMatrix | |
flat_list = None | |
if len(args) == 1: | |
# Matrix(SparseMatrix(...)) | |
if isinstance(args[0], SparseMatrix): | |
return args[0].rows, args[0].cols, flatten(args[0].tolist()) | |
# Matrix(Matrix(...)) | |
elif isinstance(args[0], MatrixBase): | |
return args[0].rows, args[0].cols, args[0].flat() | |
# Matrix(MatrixSymbol('X', 2, 2)) | |
elif isinstance(args[0], Basic) and args[0].is_Matrix: | |
return args[0].rows, args[0].cols, args[0].as_explicit().flat() | |
elif isinstance(args[0], mp.matrix): | |
M = args[0] | |
flat_list = [cls._sympify(x) for x in M] | |
return M.rows, M.cols, flat_list | |
# Matrix(numpy.ones((2, 2))) | |
elif hasattr(args[0], "__array__"): | |
return cls._handle_ndarray(args[0]) | |
# Matrix([1, 2, 3]) or Matrix([[1, 2], [3, 4]]) | |
elif is_sequence(args[0]) \ | |
and not isinstance(args[0], DeferredVector): | |
dat = list(args[0]) | |
ismat = lambda i: isinstance(i, MatrixBase) and ( | |
evaluate or isinstance(i, (BlockMatrix, MatrixSymbol))) | |
raw = lambda i: is_sequence(i) and not ismat(i) | |
evaluate = kwargs.get('evaluate', True) | |
if evaluate: | |
def make_explicit(x): | |
"""make Block and Symbol explicit""" | |
if isinstance(x, BlockMatrix): | |
return x.as_explicit() | |
elif isinstance(x, MatrixSymbol) and all(_.is_Integer for _ in x.shape): | |
return x.as_explicit() | |
else: | |
return x | |
def make_explicit_row(row): | |
# Could be list or could be list of lists | |
if isinstance(row, (list, tuple)): | |
return [make_explicit(x) for x in row] | |
else: | |
return make_explicit(row) | |
if isinstance(dat, (list, tuple)): | |
dat = [make_explicit_row(row) for row in dat] | |
if dat in ([], [[]]): | |
rows = cols = 0 | |
flat_list = [] | |
elif not any(raw(i) or ismat(i) for i in dat): | |
# a column as a list of values | |
flat_list = [cls._sympify(i) for i in dat] | |
rows = len(flat_list) | |
cols = 1 if rows else 0 | |
elif evaluate and all(ismat(i) for i in dat): | |
# a column as a list of matrices | |
ncol = {i.cols for i in dat if any(i.shape)} | |
if ncol: | |
if len(ncol) != 1: | |
raise ValueError('mismatched dimensions') | |
flat_list = [_ for i in dat for r in i.tolist() for _ in r] | |
cols = ncol.pop() | |
rows = len(flat_list)//cols | |
else: | |
rows = cols = 0 | |
flat_list = [] | |
elif evaluate and any(ismat(i) for i in dat): | |
ncol = set() | |
flat_list = [] | |
for i in dat: | |
if ismat(i): | |
flat_list.extend( | |
[k for j in i.tolist() for k in j]) | |
if any(i.shape): | |
ncol.add(i.cols) | |
elif raw(i): | |
if i: | |
ncol.add(len(i)) | |
flat_list.extend([cls._sympify(ij) for ij in i]) | |
else: | |
ncol.add(1) | |
flat_list.append(i) | |
if len(ncol) > 1: | |
raise ValueError('mismatched dimensions') | |
cols = ncol.pop() | |
rows = len(flat_list)//cols | |
else: | |
# list of lists; each sublist is a logical row | |
# which might consist of many rows if the values in | |
# the row are matrices | |
flat_list = [] | |
ncol = set() | |
rows = cols = 0 | |
for row in dat: | |
if not is_sequence(row) and \ | |
not getattr(row, 'is_Matrix', False): | |
raise ValueError('expecting list of lists') | |
if hasattr(row, '__array__'): | |
if 0 in row.shape: | |
continue | |
if evaluate and all(ismat(i) for i in row): | |
r, c, flatT = cls._handle_creation_inputs( | |
[i.T for i in row]) | |
T = reshape(flatT, [c]) | |
flat = \ | |
[T[i][j] for j in range(c) for i in range(r)] | |
r, c = c, r | |
else: | |
r = 1 | |
if getattr(row, 'is_Matrix', False): | |
c = 1 | |
flat = [row] | |
else: | |
c = len(row) | |
flat = [cls._sympify(i) for i in row] | |
ncol.add(c) | |
if len(ncol) > 1: | |
raise ValueError('mismatched dimensions') | |
flat_list.extend(flat) | |
rows += r | |
cols = ncol.pop() if ncol else 0 | |
elif len(args) == 3: | |
rows = as_int(args[0]) | |
cols = as_int(args[1]) | |
if rows < 0 or cols < 0: | |
raise ValueError("Cannot create a {} x {} matrix. " | |
"Both dimensions must be positive".format(rows, cols)) | |
# Matrix(2, 2, lambda i, j: i+j) | |
if len(args) == 3 and isinstance(args[2], Callable): | |
op = args[2] | |
flat_list = [] | |
for i in range(rows): | |
flat_list.extend( | |
[cls._sympify(op(cls._sympify(i), cls._sympify(j))) | |
for j in range(cols)]) | |
# Matrix(2, 2, [1, 2, 3, 4]) | |
elif len(args) == 3 and is_sequence(args[2]): | |
flat_list = args[2] | |
if len(flat_list) != rows * cols: | |
raise ValueError( | |
'List length should be equal to rows*columns') | |
flat_list = [cls._sympify(i) for i in flat_list] | |
# Matrix() | |
elif len(args) == 0: | |
# Empty Matrix | |
rows = cols = 0 | |
flat_list = [] | |
if flat_list is None: | |
raise TypeError(filldedent(''' | |
Data type not understood; expecting list of lists | |
or lists of values.''')) | |
return rows, cols, flat_list | |
def _setitem(self, key, value): | |
"""Helper to set value at location given by key. | |
Examples | |
======== | |
>>> from sympy import Matrix, I, zeros, ones | |
>>> m = Matrix(((1, 2+I), (3, 4))) | |
>>> m | |
Matrix([ | |
[1, 2 + I], | |
[3, 4]]) | |
>>> m[1, 0] = 9 | |
>>> m | |
Matrix([ | |
[1, 2 + I], | |
[9, 4]]) | |
>>> m[1, 0] = [[0, 1]] | |
To replace row r you assign to position r*m where m | |
is the number of columns: | |
>>> M = zeros(4) | |
>>> m = M.cols | |
>>> M[3*m] = ones(1, m)*2; M | |
Matrix([ | |
[0, 0, 0, 0], | |
[0, 0, 0, 0], | |
[0, 0, 0, 0], | |
[2, 2, 2, 2]]) | |
And to replace column c you can assign to position c: | |
>>> M[2] = ones(m, 1)*4; M | |
Matrix([ | |
[0, 0, 4, 0], | |
[0, 0, 4, 0], | |
[0, 0, 4, 0], | |
[2, 2, 4, 2]]) | |
""" | |
from .dense import Matrix | |
is_slice = isinstance(key, slice) | |
i, j = key = self.key2ij(key) | |
is_mat = isinstance(value, MatrixBase) | |
if isinstance(i, slice) or isinstance(j, slice): | |
if is_mat: | |
self.copyin_matrix(key, value) | |
return | |
if not isinstance(value, Expr) and is_sequence(value): | |
self.copyin_list(key, value) | |
return | |
raise ValueError('unexpected value: %s' % value) | |
else: | |
if (not is_mat and | |
not isinstance(value, Basic) and is_sequence(value)): | |
value = Matrix(value) | |
is_mat = True | |
if is_mat: | |
if is_slice: | |
key = (slice(*divmod(i, self.cols)), | |
slice(*divmod(j, self.cols))) | |
else: | |
key = (slice(i, i + value.rows), | |
slice(j, j + value.cols)) | |
self.copyin_matrix(key, value) | |
else: | |
return i, j, self._sympify(value) | |
return | |
def add(self, b): | |
"""Return self + b.""" | |
return self + b | |
def condition_number(self): | |
"""Returns the condition number of a matrix. | |
This is the maximum singular value divided by the minimum singular value | |
Examples | |
======== | |
>>> from sympy import Matrix, S | |
>>> A = Matrix([[1, 0, 0], [0, 10, 0], [0, 0, S.One/10]]) | |
>>> A.condition_number() | |
100 | |
See Also | |
======== | |
singular_values | |
""" | |
if not self: | |
return self.zero | |
singularvalues = self.singular_values() | |
return Max(*singularvalues) / Min(*singularvalues) | |
def copy(self): | |
""" | |
Returns the copy of a matrix. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> A = Matrix(2, 2, [1, 2, 3, 4]) | |
>>> A.copy() | |
Matrix([ | |
[1, 2], | |
[3, 4]]) | |
""" | |
return self._new(self.rows, self.cols, self.flat()) | |
def cross(self, b): | |
r""" | |
Return the cross product of ``self`` and ``b`` relaxing the condition | |
of compatible dimensions: if each has 3 elements, a matrix of the | |
same type and shape as ``self`` will be returned. If ``b`` has the same | |
shape as ``self`` then common identities for the cross product (like | |
`a \times b = - b \times a`) will hold. | |
Parameters | |
========== | |
b : 3x1 or 1x3 Matrix | |
See Also | |
======== | |
dot | |
hat | |
vee | |
multiply | |
multiply_elementwise | |
""" | |
from sympy.matrices.expressions.matexpr import MatrixExpr | |
if not isinstance(b, (MatrixBase, MatrixExpr)): | |
raise TypeError( | |
"{} must be a Matrix, not {}.".format(b, type(b))) | |
if not (self.rows * self.cols == b.rows * b.cols == 3): | |
raise ShapeError("Dimensions incorrect for cross product: %s x %s" % | |
((self.rows, self.cols), (b.rows, b.cols))) | |
else: | |
return self._new(self.rows, self.cols, ( | |
(self[1] * b[2] - self[2] * b[1]), | |
(self[2] * b[0] - self[0] * b[2]), | |
(self[0] * b[1] - self[1] * b[0]))) | |
def hat(self): | |
r""" | |
Return the skew-symmetric matrix representing the cross product, | |
so that ``self.hat() * b`` is equivalent to ``self.cross(b)``. | |
Examples | |
======== | |
Calling ``hat`` creates a skew-symmetric 3x3 Matrix from a 3x1 Matrix: | |
>>> from sympy import Matrix | |
>>> a = Matrix([1, 2, 3]) | |
>>> a.hat() | |
Matrix([ | |
[ 0, -3, 2], | |
[ 3, 0, -1], | |
[-2, 1, 0]]) | |
Multiplying it with another 3x1 Matrix calculates the cross product: | |
>>> b = Matrix([3, 2, 1]) | |
>>> a.hat() * b | |
Matrix([ | |
[-4], | |
[ 8], | |
[-4]]) | |
Which is equivalent to calling the ``cross`` method: | |
>>> a.cross(b) | |
Matrix([ | |
[-4], | |
[ 8], | |
[-4]]) | |
See Also | |
======== | |
dot | |
cross | |
vee | |
multiply | |
multiply_elementwise | |
""" | |
if self.shape != (3, 1): | |
raise ShapeError("Dimensions incorrect, expected (3, 1), got " + | |
str(self.shape)) | |
else: | |
x, y, z = self | |
return self._new(3, 3, ( | |
0, -z, y, | |
z, 0, -x, | |
-y, x, 0)) | |
def vee(self): | |
r""" | |
Return a 3x1 vector from a skew-symmetric matrix representing the cross product, | |
so that ``self * b`` is equivalent to ``self.vee().cross(b)``. | |
Examples | |
======== | |
Calling ``vee`` creates a vector from a skew-symmetric Matrix: | |
>>> from sympy import Matrix | |
>>> A = Matrix([[0, -3, 2], [3, 0, -1], [-2, 1, 0]]) | |
>>> a = A.vee() | |
>>> a | |
Matrix([ | |
[1], | |
[2], | |
[3]]) | |
Calculating the matrix product of the original matrix with a vector | |
is equivalent to a cross product: | |
>>> b = Matrix([3, 2, 1]) | |
>>> A * b | |
Matrix([ | |
[-4], | |
[ 8], | |
[-4]]) | |
>>> a.cross(b) | |
Matrix([ | |
[-4], | |
[ 8], | |
[-4]]) | |
``vee`` can also be used to retrieve angular velocity expressions. | |
Defining a rotation matrix: | |
>>> from sympy import rot_ccw_axis3, trigsimp | |
>>> from sympy.physics.mechanics import dynamicsymbols | |
>>> theta = dynamicsymbols('theta') | |
>>> R = rot_ccw_axis3(theta) | |
>>> R | |
Matrix([ | |
[cos(theta(t)), -sin(theta(t)), 0], | |
[sin(theta(t)), cos(theta(t)), 0], | |
[ 0, 0, 1]]) | |
We can retrive the angular velocity: | |
>>> Omega = R.T * R.diff() | |
>>> Omega = trigsimp(Omega) | |
>>> Omega.vee() | |
Matrix([ | |
[ 0], | |
[ 0], | |
[Derivative(theta(t), t)]]) | |
See Also | |
======== | |
dot | |
cross | |
hat | |
multiply | |
multiply_elementwise | |
""" | |
if self.shape != (3, 3): | |
raise ShapeError("Dimensions incorrect, expected (3, 3), got " + | |
str(self.shape)) | |
elif not self.is_anti_symmetric(): | |
raise ValueError("Matrix is not skew-symmetric") | |
else: | |
return self._new(3, 1, ( | |
self[2, 1], | |
self[0, 2], | |
self[1, 0])) | |
def D(self): | |
"""Return Dirac conjugate (if ``self.rows == 4``). | |
Examples | |
======== | |
>>> from sympy import Matrix, I, eye | |
>>> m = Matrix((0, 1 + I, 2, 3)) | |
>>> m.D | |
Matrix([[0, 1 - I, -2, -3]]) | |
>>> m = (eye(4) + I*eye(4)) | |
>>> m[0, 3] = 2 | |
>>> m.D | |
Matrix([ | |
[1 - I, 0, 0, 0], | |
[ 0, 1 - I, 0, 0], | |
[ 0, 0, -1 + I, 0], | |
[ 2, 0, 0, -1 + I]]) | |
If the matrix does not have 4 rows an AttributeError will be raised | |
because this property is only defined for matrices with 4 rows. | |
>>> Matrix(eye(2)).D | |
Traceback (most recent call last): | |
... | |
AttributeError: Matrix has no attribute D. | |
See Also | |
======== | |
sympy.matrices.matrixbase.MatrixBase.conjugate: By-element conjugation | |
sympy.matrices.matrixbase.MatrixBase.H: Hermite conjugation | |
""" | |
from sympy.physics.matrices import mgamma | |
if self.rows != 4: | |
# In Python 3.2, properties can only return an AttributeError | |
# so we can't raise a ShapeError -- see commit which added the | |
# first line of this inline comment. Also, there is no need | |
# for a message since MatrixBase will raise the AttributeError | |
raise AttributeError | |
return self.H * mgamma(0) | |
def dot(self, b, hermitian=None, conjugate_convention=None): | |
"""Return the dot or inner product of two vectors of equal length. | |
Here ``self`` must be a ``Matrix`` of size 1 x n or n x 1, and ``b`` | |
must be either a matrix of size 1 x n, n x 1, or a list/tuple of length n. | |
A scalar is returned. | |
By default, ``dot`` does not conjugate ``self`` or ``b``, even if there are | |
complex entries. Set ``hermitian=True`` (and optionally a ``conjugate_convention``) | |
to compute the hermitian inner product. | |
Possible kwargs are ``hermitian`` and ``conjugate_convention``. | |
If ``conjugate_convention`` is ``"left"``, ``"math"`` or ``"maths"``, | |
the conjugate of the first vector (``self``) is used. If ``"right"`` | |
or ``"physics"`` is specified, the conjugate of the second vector ``b`` is used. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> M = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) | |
>>> v = Matrix([1, 1, 1]) | |
>>> M.row(0).dot(v) | |
6 | |
>>> M.col(0).dot(v) | |
12 | |
>>> v = [3, 2, 1] | |
>>> M.row(0).dot(v) | |
10 | |
>>> from sympy import I | |
>>> q = Matrix([1*I, 1*I, 1*I]) | |
>>> q.dot(q, hermitian=False) | |
-3 | |
>>> q.dot(q, hermitian=True) | |
3 | |
>>> q1 = Matrix([1, 1, 1*I]) | |
>>> q.dot(q1, hermitian=True, conjugate_convention="maths") | |
1 - 2*I | |
>>> q.dot(q1, hermitian=True, conjugate_convention="physics") | |
1 + 2*I | |
See Also | |
======== | |
cross | |
multiply | |
multiply_elementwise | |
""" | |
from .dense import Matrix | |
if not isinstance(b, MatrixBase): | |
if is_sequence(b): | |
if len(b) != self.cols and len(b) != self.rows: | |
raise ShapeError( | |
"Dimensions incorrect for dot product: %s, %s" % ( | |
self.shape, len(b))) | |
return self.dot(Matrix(b)) | |
else: | |
raise TypeError( | |
"`b` must be an ordered iterable or Matrix, not %s." % | |
type(b)) | |
if (1 not in self.shape) or (1 not in b.shape): | |
raise ShapeError | |
if len(self) != len(b): | |
raise ShapeError( | |
"Dimensions incorrect for dot product: %s, %s" % (self.shape, b.shape)) | |
mat = self | |
n = len(mat) | |
if mat.shape != (1, n): | |
mat = mat.reshape(1, n) | |
if b.shape != (n, 1): | |
b = b.reshape(n, 1) | |
# Now ``mat`` is a row vector and ``b`` is a column vector. | |
# If it so happens that only conjugate_convention is passed | |
# then automatically set hermitian to True. If only hermitian | |
# is true but no conjugate_convention is not passed then | |
# automatically set it to ``"maths"`` | |
if conjugate_convention is not None and hermitian is None: | |
hermitian = True | |
if hermitian and conjugate_convention is None: | |
conjugate_convention = "maths" | |
if hermitian == True: | |
if conjugate_convention in ("maths", "left", "math"): | |
mat = mat.conjugate() | |
elif conjugate_convention in ("physics", "right"): | |
b = b.conjugate() | |
else: | |
raise ValueError("Unknown conjugate_convention was entered." | |
" conjugate_convention must be one of the" | |
" following: math, maths, left, physics or right.") | |
return (mat * b)[0] | |
def dual(self): | |
"""Returns the dual of a matrix. | |
A dual of a matrix is: | |
``(1/2)*levicivita(i, j, k, l)*M(k, l)`` summed over indices `k` and `l` | |
Since the levicivita method is anti_symmetric for any pairwise | |
exchange of indices, the dual of a symmetric matrix is the zero | |
matrix. Strictly speaking the dual defined here assumes that the | |
'matrix' `M` is a contravariant anti_symmetric second rank tensor, | |
so that the dual is a covariant second rank tensor. | |
""" | |
from sympy.matrices import zeros | |
M, n = self[:, :], self.rows | |
work = zeros(n) | |
if self.is_symmetric(): | |
return work | |
for i in range(1, n): | |
for j in range(1, n): | |
acum = 0 | |
for k in range(1, n): | |
acum += LeviCivita(i, j, 0, k) * M[0, k] | |
work[i, j] = acum | |
work[j, i] = -acum | |
for l in range(1, n): | |
acum = 0 | |
for a in range(1, n): | |
for b in range(1, n): | |
acum += LeviCivita(0, l, a, b) * M[a, b] | |
acum /= 2 | |
work[0, l] = -acum | |
work[l, 0] = acum | |
return work | |
def _eval_matrix_exp_jblock(self): | |
"""A helper function to compute an exponential of a Jordan block | |
matrix | |
Examples | |
======== | |
>>> from sympy import Symbol, Matrix | |
>>> l = Symbol('lamda') | |
A trivial example of 1*1 Jordan block: | |
>>> m = Matrix.jordan_block(1, l) | |
>>> m._eval_matrix_exp_jblock() | |
Matrix([[exp(lamda)]]) | |
An example of 3*3 Jordan block: | |
>>> m = Matrix.jordan_block(3, l) | |
>>> m._eval_matrix_exp_jblock() | |
Matrix([ | |
[exp(lamda), exp(lamda), exp(lamda)/2], | |
[ 0, exp(lamda), exp(lamda)], | |
[ 0, 0, exp(lamda)]]) | |
References | |
========== | |
.. [1] https://en.wikipedia.org/wiki/Matrix_function#Jordan_decomposition | |
""" | |
size = self.rows | |
l = self[0, 0] | |
exp_l = exp(l) | |
bands = {i: exp_l / factorial(i) for i in range(size)} | |
from .sparsetools import banded | |
return self.__class__(banded(size, bands)) | |
def analytic_func(self, f, x): | |
""" | |
Computes f(A) where A is a Square Matrix | |
and f is an analytic function. | |
Examples | |
======== | |
>>> from sympy import Symbol, Matrix, S, log | |
>>> x = Symbol('x') | |
>>> m = Matrix([[S(5)/4, S(3)/4], [S(3)/4, S(5)/4]]) | |
>>> f = log(x) | |
>>> m.analytic_func(f, x) | |
Matrix([ | |
[ 0, log(2)], | |
[log(2), 0]]) | |
Parameters | |
========== | |
f : Expr | |
Analytic Function | |
x : Symbol | |
parameter of f | |
""" | |
f, x = _sympify(f), _sympify(x) | |
if not self.is_square: | |
raise NonSquareMatrixError | |
if not x.is_symbol: | |
raise ValueError("{} must be a symbol.".format(x)) | |
if x not in f.free_symbols: | |
raise ValueError( | |
"{} must be a parameter of {}.".format(x, f)) | |
if x in self.free_symbols: | |
raise ValueError( | |
"{} must not be a parameter of {}.".format(x, self)) | |
eigen = self.eigenvals() | |
max_mul = max(eigen.values()) | |
derivative = {} | |
dd = f | |
for i in range(max_mul - 1): | |
dd = diff(dd, x) | |
derivative[i + 1] = dd | |
n = self.shape[0] | |
r = self.zeros(n) | |
f_val = self.zeros(n, 1) | |
row = 0 | |
for i in eigen: | |
mul = eigen[i] | |
f_val[row] = f.subs(x, i) | |
if f_val[row].is_number and not f_val[row].is_complex: | |
raise ValueError( | |
"Cannot evaluate the function because the " | |
"function {} is not analytic at the given " | |
"eigenvalue {}".format(f, f_val[row])) | |
val = 1 | |
for a in range(n): | |
r[row, a] = val | |
val *= i | |
if mul > 1: | |
coe = [1 for ii in range(n)] | |
deri = 1 | |
while mul > 1: | |
row = row + 1 | |
mul -= 1 | |
d_i = derivative[deri].subs(x, i) | |
if d_i.is_number and not d_i.is_complex: | |
raise ValueError( | |
"Cannot evaluate the function because the " | |
"derivative {} is not analytic at the given " | |
"eigenvalue {}".format(derivative[deri], d_i)) | |
f_val[row] = d_i | |
for a in range(n): | |
if a - deri + 1 <= 0: | |
r[row, a] = 0 | |
coe[a] = 0 | |
continue | |
coe[a] = coe[a]*(a - deri + 1) | |
r[row, a] = coe[a]*pow(i, a - deri) | |
deri += 1 | |
row += 1 | |
c = r.solve(f_val) | |
ans = self.zeros(n) | |
pre = self.eye(n) | |
for i in range(n): | |
ans = ans + c[i]*pre | |
pre *= self | |
return ans | |
def exp(self): | |
"""Return the exponential of a square matrix. | |
Examples | |
======== | |
>>> from sympy import Symbol, Matrix | |
>>> t = Symbol('t') | |
>>> m = Matrix([[0, 1], [-1, 0]]) * t | |
>>> m.exp() | |
Matrix([ | |
[ exp(I*t)/2 + exp(-I*t)/2, -I*exp(I*t)/2 + I*exp(-I*t)/2], | |
[I*exp(I*t)/2 - I*exp(-I*t)/2, exp(I*t)/2 + exp(-I*t)/2]]) | |
""" | |
if not self.is_square: | |
raise NonSquareMatrixError( | |
"Exponentiation is valid only for square matrices") | |
try: | |
P, J = self.jordan_form() | |
cells = J.get_diag_blocks() | |
except MatrixError: | |
raise NotImplementedError( | |
"Exponentiation is implemented only for matrices for which the Jordan normal form can be computed") | |
blocks = [cell._eval_matrix_exp_jblock() for cell in cells] | |
from sympy.matrices import diag | |
eJ = diag(*blocks) | |
# n = self.rows | |
ret = P.multiply(eJ, dotprodsimp=None).multiply(P.inv(), dotprodsimp=None) | |
if all(value.is_real for value in self.values()): | |
return type(self)(re(ret)) | |
else: | |
return type(self)(ret) | |
def _eval_matrix_log_jblock(self): | |
"""Helper function to compute logarithm of a jordan block. | |
Examples | |
======== | |
>>> from sympy import Symbol, Matrix | |
>>> l = Symbol('lamda') | |
A trivial example of 1*1 Jordan block: | |
>>> m = Matrix.jordan_block(1, l) | |
>>> m._eval_matrix_log_jblock() | |
Matrix([[log(lamda)]]) | |
An example of 3*3 Jordan block: | |
>>> m = Matrix.jordan_block(3, l) | |
>>> m._eval_matrix_log_jblock() | |
Matrix([ | |
[log(lamda), 1/lamda, -1/(2*lamda**2)], | |
[ 0, log(lamda), 1/lamda], | |
[ 0, 0, log(lamda)]]) | |
""" | |
size = self.rows | |
l = self[0, 0] | |
if l.is_zero: | |
raise MatrixError( | |
'Could not take logarithm or reciprocal for the given ' | |
'eigenvalue {}'.format(l)) | |
bands = {0: log(l)} | |
for i in range(1, size): | |
bands[i] = -((-l) ** -i) / i | |
from .sparsetools import banded | |
return self.__class__(banded(size, bands)) | |
def log(self, simplify=cancel): | |
"""Return the logarithm of a square matrix. | |
Parameters | |
========== | |
simplify : function, bool | |
The function to simplify the result with. | |
Default is ``cancel``, which is effective to reduce the | |
expression growing for taking reciprocals and inverses for | |
symbolic matrices. | |
Examples | |
======== | |
>>> from sympy import S, Matrix | |
Examples for positive-definite matrices: | |
>>> m = Matrix([[1, 1], [0, 1]]) | |
>>> m.log() | |
Matrix([ | |
[0, 1], | |
[0, 0]]) | |
>>> m = Matrix([[S(5)/4, S(3)/4], [S(3)/4, S(5)/4]]) | |
>>> m.log() | |
Matrix([ | |
[ 0, log(2)], | |
[log(2), 0]]) | |
Examples for non positive-definite matrices: | |
>>> m = Matrix([[S(3)/4, S(5)/4], [S(5)/4, S(3)/4]]) | |
>>> m.log() | |
Matrix([ | |
[ I*pi/2, log(2) - I*pi/2], | |
[log(2) - I*pi/2, I*pi/2]]) | |
>>> m = Matrix( | |
... [[0, 0, 0, 1], | |
... [0, 0, 1, 0], | |
... [0, 1, 0, 0], | |
... [1, 0, 0, 0]]) | |
>>> m.log() | |
Matrix([ | |
[ I*pi/2, 0, 0, -I*pi/2], | |
[ 0, I*pi/2, -I*pi/2, 0], | |
[ 0, -I*pi/2, I*pi/2, 0], | |
[-I*pi/2, 0, 0, I*pi/2]]) | |
""" | |
if not self.is_square: | |
raise NonSquareMatrixError( | |
"Logarithm is valid only for square matrices") | |
try: | |
if simplify: | |
P, J = simplify(self).jordan_form() | |
else: | |
P, J = self.jordan_form() | |
cells = J.get_diag_blocks() | |
except MatrixError: | |
raise NotImplementedError( | |
"Logarithm is implemented only for matrices for which " | |
"the Jordan normal form can be computed") | |
blocks = [ | |
cell._eval_matrix_log_jblock() | |
for cell in cells] | |
from sympy.matrices import diag | |
eJ = diag(*blocks) | |
if simplify: | |
ret = simplify(P * eJ * simplify(P.inv())) | |
ret = self.__class__(ret) | |
else: | |
ret = P * eJ * P.inv() | |
return ret | |
def is_nilpotent(self): | |
"""Checks if a matrix is nilpotent. | |
A matrix B is nilpotent if for some integer k, B**k is | |
a zero matrix. | |
Examples | |
======== | |
>>> from sympy import Matrix | |
>>> a = Matrix([[0, 0, 0], [1, 0, 0], [1, 1, 0]]) | |
>>> a.is_nilpotent() | |
True | |
>>> a = Matrix([[1, 0, 1], [1, 0, 0], [1, 1, 0]]) | |
>>> a.is_nilpotent() | |
False | |
""" | |
if not self: | |
return True | |
if not self.is_square: | |
raise NonSquareMatrixError( | |
"Nilpotency is valid only for square matrices") | |
x = uniquely_named_symbol('x', self, modify=lambda s: '_' + s) | |
p = self.charpoly(x) | |
if p.args[0] == x ** self.rows: | |
return True | |
return False | |
def key2bounds(self, keys): | |
"""Converts a key with potentially mixed types of keys (integer and slice) | |
into a tuple of ranges and raises an error if any index is out of ``self``'s | |
range. | |
See Also | |
======== | |
key2ij | |
""" | |
islice, jslice = [isinstance(k, slice) for k in keys] | |
if islice: | |
if not self.rows: | |
rlo = rhi = 0 | |
else: | |
rlo, rhi = keys[0].indices(self.rows)[:2] | |
else: | |
rlo = a2idx(keys[0], self.rows) | |
rhi = rlo + 1 | |
if jslice: | |
if not self.cols: | |
clo = chi = 0 | |
else: | |
clo, chi = keys[1].indices(self.cols)[:2] | |
else: | |
clo = a2idx(keys[1], self.cols) | |
chi = clo + 1 | |
return rlo, rhi, clo, chi | |
def key2ij(self, key): | |
"""Converts key into canonical form, converting integers or indexable | |
items into valid integers for ``self``'s range or returning slices | |
unchanged. | |
See Also | |
======== | |
key2bounds | |
""" | |
if is_sequence(key): | |
if not len(key) == 2: | |
raise TypeError('key must be a sequence of length 2') | |
return [a2idx(i, n) if not isinstance(i, slice) else i | |
for i, n in zip(key, self.shape)] | |
elif isinstance(key, slice): | |
return key.indices(len(self))[:2] | |
else: | |
return divmod(a2idx(key, len(self)), self.cols) | |
def normalized(self, iszerofunc=_iszero): | |
"""Return the normalized version of ``self``. | |
Parameters | |
========== | |
iszerofunc : Function, optional | |
A function to determine whether ``self`` is a zero vector. | |
The default ``_iszero`` tests to see if each element is | |
exactly zero. | |
Returns | |
======= | |
Matrix | |
Normalized vector form of ``self``. | |
It has the same length as a unit vector. However, a zero vector | |
will be returned for a vector with norm 0. | |
Raises | |
====== | |
ShapeError | |
If the matrix is not in a vector form. | |
See Also | |
======== | |
norm | |
""" | |
if self.rows != 1 and self.cols != 1: | |
raise ShapeError("A Matrix must be a vector to normalize.") | |
norm = self.norm() | |
if iszerofunc(norm): | |
out = self.zeros(self.rows, self.cols) | |
else: | |
out = self.applyfunc(lambda i: i / norm) | |
return out | |
def norm(self, ord=None): | |
"""Return the Norm of a Matrix or Vector. | |
In the simplest case this is the geometric size of the vector | |
Other norms can be specified by the ord parameter | |
===== ============================ ========================== | |
ord norm for matrices norm for vectors | |
===== ============================ ========================== | |
None Frobenius norm 2-norm | |
'fro' Frobenius norm - does not exist | |
inf maximum row sum max(abs(x)) | |
-inf -- min(abs(x)) | |
1 maximum column sum as below | |
-1 -- as below | |
2 2-norm (largest sing. value) as below | |
-2 smallest singular value as below | |
other - does not exist sum(abs(x)**ord)**(1./ord) | |
===== ============================ ========================== | |
Examples | |
======== | |
>>> from sympy import Matrix, Symbol, trigsimp, cos, sin, oo | |
>>> x = Symbol('x', real=True) | |
>>> v = Matrix([cos(x), sin(x)]) | |
>>> trigsimp( v.norm() ) | |
1 | |
>>> v.norm(10) | |
(sin(x)**10 + cos(x)**10)**(1/10) | |
>>> A = Matrix([[1, 1], [1, 1]]) | |
>>> A.norm(1) # maximum sum of absolute values of A is 2 | |
2 | |
>>> A.norm(2) # Spectral norm (max of |Ax|/|x| under 2-vector-norm) | |
2 | |
>>> A.norm(-2) # Inverse spectral norm (smallest singular value) | |
0 | |
>>> A.norm() # Frobenius Norm | |
2 | |
>>> A.norm(oo) # Infinity Norm | |
2 | |
>>> Matrix([1, -2]).norm(oo) | |
2 | |
>>> Matrix([-1, 2]).norm(-oo) | |
1 | |
See Also | |
======== | |
normalized | |
""" | |
# Row or Column Vector Norms | |
vals = list(self.values()) or [0] | |
if S.One in self.shape: | |
if ord in (2, None): # Common case sqrt(<x, x>) | |
return sqrt(Add(*(abs(i) ** 2 for i in vals))) | |
elif ord == 1: # sum(abs(x)) | |
return Add(*(abs(i) for i in vals)) | |
elif ord is S.Infinity: # max(abs(x)) | |
return Max(*[abs(i) for i in vals]) | |
elif ord is S.NegativeInfinity: # min(abs(x)) | |
return Min(*[abs(i) for i in vals]) | |
# Otherwise generalize the 2-norm, Sum(x_i**ord)**(1/ord) | |
# Note that while useful this is not mathematically a norm | |
try: | |
return Pow(Add(*(abs(i) ** ord for i in vals)), S.One / ord) | |
except (NotImplementedError, TypeError): | |
raise ValueError("Expected order to be Number, Symbol, oo") | |
# Matrix Norms | |
else: | |
if ord == 1: # Maximum column sum | |
m = self.applyfunc(abs) | |
return Max(*[sum(m.col(i)) for i in range(m.cols)]) | |
elif ord == 2: # Spectral Norm | |
# Maximum singular value | |
return Max(*self.singular_values()) | |
elif ord == -2: | |
# Minimum singular value | |
return Min(*self.singular_values()) | |
elif ord is S.Infinity: # Infinity Norm - Maximum row sum | |
m = self.applyfunc(abs) | |
return Max(*[sum(m.row(i)) for i in range(m.rows)]) | |
elif (ord is None or isinstance(ord, | |
str) and ord.lower() in | |
['f', 'fro', 'frobenius', 'vector']): | |
# Reshape as vector and send back to norm function | |
return self.vec().norm(ord=2) | |
else: | |
raise NotImplementedError("Matrix Norms under development") | |
def print_nonzero(self, symb="X"): | |
"""Shows location of non-zero entries for fast shape lookup. | |
Examples | |
======== | |
>>> from sympy import Matrix, eye | |
>>> m = Matrix(2, 3, lambda i, j: i*3+j) | |
>>> m | |
Matrix([ | |
[0, 1, 2], | |
[3, 4, 5]]) | |
>>> m.print_nonzero() | |
[ XX] | |
[XXX] | |
>>> m = eye(4) | |
>>> m.print_nonzero("x") | |
[x ] | |
[ x ] | |
[ x ] | |
[ x] | |
""" | |
s = [] | |
for i in range(self.rows): | |
line = [] | |
for j in range(self.cols): | |
if self[i, j] == 0: | |
line.append(" ") | |
else: | |
line.append(str(symb)) | |
s.append("[%s]" % ''.join(line)) | |
print('\n'.join(s)) | |
def project(self, v): | |
"""Return the projection of ``self`` onto the line containing ``v``. | |
Examples | |
======== | |
>>> from sympy import Matrix, S, sqrt | |
>>> V = Matrix([sqrt(3)/2, S.Half]) | |
>>> x = Matrix([[1, 0]]) | |
>>> V.project(x) | |
Matrix([[sqrt(3)/2, 0]]) | |
>>> V.project(-x) | |
Matrix([[sqrt(3)/2, 0]]) | |
""" | |
return v * (self.dot(v) / v.dot(v)) | |
def table(self, printer, rowstart='[', rowend=']', rowsep='\n', | |
colsep=', ', align='right'): | |
r""" | |
String form of Matrix as a table. | |
``printer`` is the printer to use for on the elements (generally | |
something like StrPrinter()) | |
``rowstart`` is the string used to start each row (by default '['). | |
``rowend`` is the string used to end each row (by default ']'). | |
``rowsep`` is the string used to separate rows (by default a newline). | |
``colsep`` is the string used to separate columns (by default ', '). | |
``align`` defines how the elements are aligned. Must be one of 'left', | |
'right', or 'center'. You can also use '<', '>', and '^' to mean the | |
same thing, respectively. | |
This is used by the string printer for Matrix. | |
Examples | |
======== | |
>>> from sympy import Matrix, StrPrinter | |
>>> M = Matrix([[1, 2], [-33, 4]]) | |
>>> printer = StrPrinter() | |
>>> M.table(printer) | |
'[ 1, 2]\n[-33, 4]' | |
>>> print(M.table(printer)) | |
[ 1, 2] | |
[-33, 4] | |
>>> print(M.table(printer, rowsep=',\n')) | |
[ 1, 2], | |
[-33, 4] | |
>>> print('[%s]' % M.table(printer, rowsep=',\n')) | |
[[ 1, 2], | |
[-33, 4]] | |
>>> print(M.table(printer, colsep=' ')) | |
[ 1 2] | |
[-33 4] | |
>>> print(M.table(printer, align='center')) | |
[ 1 , 2] | |
[-33, 4] | |
>>> print(M.table(printer, rowstart='{', rowend='}')) | |
{ 1, 2} | |
{-33, 4} | |
""" | |
# Handle zero dimensions: | |
if S.Zero in self.shape: | |
return '[]' | |
# Build table of string representations of the elements | |
res = [] | |
# Track per-column max lengths for pretty alignment | |
maxlen = [0] * self.cols | |
for i in range(self.rows): | |
res.append([]) | |
for j in range(self.cols): | |
s = printer._print(self[i, j]) | |
res[-1].append(s) | |
maxlen[j] = max(len(s), maxlen[j]) | |
# Patch strings together | |
align = { | |
'left': 'ljust', | |
'right': 'rjust', | |
'center': 'center', | |
'<': 'ljust', | |
'>': 'rjust', | |
'^': 'center', | |
}[align] | |
for i, row in enumerate(res): | |
for j, elem in enumerate(row): | |
row[j] = getattr(elem, align)(maxlen[j]) | |
res[i] = rowstart + colsep.join(row) + rowend | |
return rowsep.join(res) | |
def rank_decomposition(self, iszerofunc=_iszero, simplify=False): | |
return _rank_decomposition(self, iszerofunc=iszerofunc, | |
simplify=simplify) | |
def cholesky(self, hermitian=True): | |
raise NotImplementedError('This function is implemented in DenseMatrix or SparseMatrix') | |
def LDLdecomposition(self, hermitian=True): | |
raise NotImplementedError('This function is implemented in DenseMatrix or SparseMatrix') | |
def LUdecomposition(self, iszerofunc=_iszero, simpfunc=None, | |
rankcheck=False): | |
return _LUdecomposition(self, iszerofunc=iszerofunc, simpfunc=simpfunc, | |
rankcheck=rankcheck) | |
def LUdecomposition_Simple(self, iszerofunc=_iszero, simpfunc=None, | |
rankcheck=False): | |
return _LUdecomposition_Simple(self, iszerofunc=iszerofunc, | |
simpfunc=simpfunc, rankcheck=rankcheck) | |
def LUdecompositionFF(self): | |
return _LUdecompositionFF(self) | |
def singular_value_decomposition(self): | |
return _singular_value_decomposition(self) | |
def QRdecomposition(self): | |
return _QRdecomposition(self) | |
def upper_hessenberg_decomposition(self): | |
return _upper_hessenberg_decomposition(self) | |
def diagonal_solve(self, rhs): | |
return _diagonal_solve(self, rhs) | |
def lower_triangular_solve(self, rhs): | |
raise NotImplementedError('This function is implemented in DenseMatrix or SparseMatrix') | |
def upper_triangular_solve(self, rhs): | |
raise NotImplementedError('This function is implemented in DenseMatrix or SparseMatrix') | |
def cholesky_solve(self, rhs): | |
return _cholesky_solve(self, rhs) | |
def LDLsolve(self, rhs): | |
return _LDLsolve(self, rhs) | |
def LUsolve(self, rhs, iszerofunc=_iszero): | |
return _LUsolve(self, rhs, iszerofunc=iszerofunc) | |
def QRsolve(self, b): | |
return _QRsolve(self, b) | |
def gauss_jordan_solve(self, B, freevar=False): | |
return _gauss_jordan_solve(self, B, freevar=freevar) | |
def pinv_solve(self, B, arbitrary_matrix=None): | |
return _pinv_solve(self, B, arbitrary_matrix=arbitrary_matrix) | |
def cramer_solve(self, rhs, det_method="laplace"): | |
return _cramer_solve(self, rhs, det_method=det_method) | |
def solve(self, rhs, method='GJ'): | |
return _solve(self, rhs, method=method) | |
def solve_least_squares(self, rhs, method='CH'): | |
return _solve_least_squares(self, rhs, method=method) | |
def pinv(self, method='RD'): | |
return _pinv(self, method=method) | |
def inverse_ADJ(self, iszerofunc=_iszero): | |
return _inv_ADJ(self, iszerofunc=iszerofunc) | |
def inverse_BLOCK(self, iszerofunc=_iszero): | |
return _inv_block(self, iszerofunc=iszerofunc) | |
def inverse_GE(self, iszerofunc=_iszero): | |
return _inv_GE(self, iszerofunc=iszerofunc) | |
def inverse_LU(self, iszerofunc=_iszero): | |
return _inv_LU(self, iszerofunc=iszerofunc) | |
def inverse_CH(self, iszerofunc=_iszero): | |
return _inv_CH(self, iszerofunc=iszerofunc) | |
def inverse_LDL(self, iszerofunc=_iszero): | |
return _inv_LDL(self, iszerofunc=iszerofunc) | |
def inverse_QR(self, iszerofunc=_iszero): | |
return _inv_QR(self, iszerofunc=iszerofunc) | |
def inv(self, method=None, iszerofunc=_iszero, try_block_diag=False): | |
return _inv(self, method=method, iszerofunc=iszerofunc, | |
try_block_diag=try_block_diag) | |
def connected_components(self): | |
return _connected_components(self) | |
def connected_components_decomposition(self): | |
return _connected_components_decomposition(self) | |
def strongly_connected_components(self): | |
return _strongly_connected_components(self) | |
def strongly_connected_components_decomposition(self, lower=True): | |
return _strongly_connected_components_decomposition(self, lower=lower) | |
_sage_ = Basic._sage_ | |
rank_decomposition.__doc__ = _rank_decomposition.__doc__ | |
cholesky.__doc__ = _cholesky.__doc__ | |
LDLdecomposition.__doc__ = _LDLdecomposition.__doc__ | |
LUdecomposition.__doc__ = _LUdecomposition.__doc__ | |
LUdecomposition_Simple.__doc__ = _LUdecomposition_Simple.__doc__ | |
LUdecompositionFF.__doc__ = _LUdecompositionFF.__doc__ | |
singular_value_decomposition.__doc__ = _singular_value_decomposition.__doc__ | |
QRdecomposition.__doc__ = _QRdecomposition.__doc__ | |
upper_hessenberg_decomposition.__doc__ = _upper_hessenberg_decomposition.__doc__ | |
diagonal_solve.__doc__ = _diagonal_solve.__doc__ | |
lower_triangular_solve.__doc__ = _lower_triangular_solve.__doc__ | |
upper_triangular_solve.__doc__ = _upper_triangular_solve.__doc__ | |
cholesky_solve.__doc__ = _cholesky_solve.__doc__ | |
LDLsolve.__doc__ = _LDLsolve.__doc__ | |
LUsolve.__doc__ = _LUsolve.__doc__ | |
QRsolve.__doc__ = _QRsolve.__doc__ | |
gauss_jordan_solve.__doc__ = _gauss_jordan_solve.__doc__ | |
pinv_solve.__doc__ = _pinv_solve.__doc__ | |
cramer_solve.__doc__ = _cramer_solve.__doc__ | |
solve.__doc__ = _solve.__doc__ | |
solve_least_squares.__doc__ = _solve_least_squares.__doc__ | |
pinv.__doc__ = _pinv.__doc__ | |
inverse_ADJ.__doc__ = _inv_ADJ.__doc__ | |
inverse_GE.__doc__ = _inv_GE.__doc__ | |
inverse_LU.__doc__ = _inv_LU.__doc__ | |
inverse_CH.__doc__ = _inv_CH.__doc__ | |
inverse_LDL.__doc__ = _inv_LDL.__doc__ | |
inverse_QR.__doc__ = _inv_QR.__doc__ | |
inverse_BLOCK.__doc__ = _inv_block.__doc__ | |
inv.__doc__ = _inv.__doc__ | |
connected_components.__doc__ = _connected_components.__doc__ | |
connected_components_decomposition.__doc__ = \ | |
_connected_components_decomposition.__doc__ | |
strongly_connected_components.__doc__ = \ | |
_strongly_connected_components.__doc__ | |
strongly_connected_components_decomposition.__doc__ = \ | |
_strongly_connected_components_decomposition.__doc__ | |
def _convert_matrix(typ, mat): | |
"""Convert mat to a Matrix of type typ.""" | |
from sympy.matrices.matrixbase import MatrixBase | |
if getattr(mat, "is_Matrix", False) and not isinstance(mat, MatrixBase): | |
# This is needed for interop between Matrix and the redundant matrix | |
# mixin types like _MinimalMatrix etc. If anyone should happen to be | |
# using those then this keeps them working. Really _MinimalMatrix etc | |
# should be deprecated and removed though. | |
return typ(*mat.shape, list(mat)) | |
else: | |
return typ(mat) | |
def _has_matrix_shape(other): | |
shape = getattr(other, 'shape', None) | |
if shape is None: | |
return False | |
return isinstance(shape, tuple) and len(shape) == 2 | |
def _has_rows_cols(other): | |
return hasattr(other, 'rows') and hasattr(other, 'cols') | |
def _coerce_operand(self, other): | |
"""Convert other to a Matrix, or check for possible scalar.""" | |
INVALID = None, 'invalid_type' | |
# Disallow mixing Matrix and Array | |
if isinstance(other, NDimArray): | |
return INVALID | |
is_Matrix = getattr(other, 'is_Matrix', None) | |
# Return a Matrix as-is | |
if is_Matrix: | |
return other, 'is_matrix' | |
# Try to convert numpy array, mpmath matrix etc. | |
if is_Matrix is None: | |
if _has_matrix_shape(other) or _has_rows_cols(other): | |
return _convert_matrix(type(self), other), 'is_matrix' | |
# Could be a scalar but only if not iterable... | |
if not isinstance(other, Iterable): | |
return other, 'possible_scalar' | |
return INVALID | |
def classof(A, B): | |
""" | |
Get the type of the result when combining matrices of different types. | |
Currently the strategy is that immutability is contagious. | |
Examples | |
======== | |
>>> from sympy import Matrix, ImmutableMatrix | |
>>> from sympy.matrices.matrixbase import classof | |
>>> M = Matrix([[1, 2], [3, 4]]) # a Mutable Matrix | |
>>> IM = ImmutableMatrix([[1, 2], [3, 4]]) | |
>>> classof(M, IM) | |
<class 'sympy.matrices.immutable.ImmutableDenseMatrix'> | |
""" | |
priority_A = getattr(A, '_class_priority', None) | |
priority_B = getattr(B, '_class_priority', None) | |
if None not in (priority_A, priority_B): | |
if A._class_priority > B._class_priority: | |
return A.__class__ | |
else: | |
return B.__class__ | |
try: | |
import numpy | |
except ImportError: | |
pass | |
else: | |
if isinstance(A, numpy.ndarray): | |
return B.__class__ | |
if isinstance(B, numpy.ndarray): | |
return A.__class__ | |
raise TypeError("Incompatible classes %s, %s" % (A.__class__, B.__class__)) | |
def _unify_with_other(self, other): | |
"""Unify self and other into a single matrix type, or check for scalar.""" | |
other, T = _coerce_operand(self, other) | |
if T == "is_matrix": | |
typ = classof(self, other) | |
if typ != self.__class__: | |
self = _convert_matrix(typ, self) | |
if typ != other.__class__: | |
other = _convert_matrix(typ, other) | |
return self, other, T | |
def a2idx(j, n=None): | |
"""Return integer after making positive and validating against n.""" | |
if not isinstance(j, int): | |
jindex = getattr(j, '__index__', None) | |
if jindex is not None: | |
j = jindex() | |
else: | |
raise IndexError("Invalid index a[%r]" % (j,)) | |
if n is not None: | |
if j < 0: | |
j += n | |
if not (j >= 0 and j < n): | |
raise IndexError("Index out of range: a[%s]" % (j,)) | |
return int(j) | |
class DeferredVector(Symbol, NotIterable): | |
"""A vector whose components are deferred (e.g. for use with lambdify). | |
Examples | |
======== | |
>>> from sympy import DeferredVector, lambdify | |
>>> X = DeferredVector( 'X' ) | |
>>> X | |
X | |
>>> expr = (X[0] + 2, X[2] + 3) | |
>>> func = lambdify( X, expr) | |
>>> func( [1, 2, 3] ) | |
(3, 6) | |
""" | |
def __getitem__(self, i): | |
if i == -0: | |
i = 0 | |
if i < 0: | |
raise IndexError('DeferredVector index out of range') | |
component_name = '%s[%d]' % (self.name, i) | |
return Symbol(component_name) | |
def __str__(self): | |
return sstr(self) | |
def __repr__(self): | |
return "DeferredVector('%s')" % self.name | |