123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220 |
- from sympy.core.sympify import _sympify
- from sympy.matrices.expressions import MatrixExpr
- from sympy.core import S, Eq, Ge
- from sympy.core.mul import Mul
- from sympy.functions.special.tensor_functions import KroneckerDelta
- class DiagonalMatrix(MatrixExpr):
- """DiagonalMatrix(M) will create a matrix expression that
- behaves as though all off-diagonal elements,
- `M[i, j]` where `i != j`, are zero.
- Examples
- ========
- >>> from sympy import MatrixSymbol, DiagonalMatrix, Symbol
- >>> n = Symbol('n', integer=True)
- >>> m = Symbol('m', integer=True)
- >>> D = DiagonalMatrix(MatrixSymbol('x', 2, 3))
- >>> D[1, 2]
- 0
- >>> D[1, 1]
- x[1, 1]
- The length of the diagonal -- the lesser of the two dimensions of `M` --
- is accessed through the `diagonal_length` property:
- >>> D.diagonal_length
- 2
- >>> DiagonalMatrix(MatrixSymbol('x', n + 1, n)).diagonal_length
- n
- When one of the dimensions is symbolic the other will be treated as
- though it is smaller:
- >>> tall = DiagonalMatrix(MatrixSymbol('x', n, 3))
- >>> tall.diagonal_length
- 3
- >>> tall[10, 1]
- 0
- When the size of the diagonal is not known, a value of None will
- be returned:
- >>> DiagonalMatrix(MatrixSymbol('x', n, m)).diagonal_length is None
- True
- """
- arg = property(lambda self: self.args[0])
- shape = property(lambda self: self.arg.shape) # type:ignore
- @property
- def diagonal_length(self):
- r, c = self.shape
- if r.is_Integer and c.is_Integer:
- m = min(r, c)
- elif r.is_Integer and not c.is_Integer:
- m = r
- elif c.is_Integer and not r.is_Integer:
- m = c
- elif r == c:
- m = r
- else:
- try:
- m = min(r, c)
- except TypeError:
- m = None
- return m
- def _entry(self, i, j, **kwargs):
- if self.diagonal_length is not None:
- if Ge(i, self.diagonal_length) is S.true:
- return S.Zero
- elif Ge(j, self.diagonal_length) is S.true:
- return S.Zero
- eq = Eq(i, j)
- if eq is S.true:
- return self.arg[i, i]
- elif eq is S.false:
- return S.Zero
- return self.arg[i, j]*KroneckerDelta(i, j)
- class DiagonalOf(MatrixExpr):
- """DiagonalOf(M) will create a matrix expression that
- is equivalent to the diagonal of `M`, represented as
- a single column matrix.
- Examples
- ========
- >>> from sympy import MatrixSymbol, DiagonalOf, Symbol
- >>> n = Symbol('n', integer=True)
- >>> m = Symbol('m', integer=True)
- >>> x = MatrixSymbol('x', 2, 3)
- >>> diag = DiagonalOf(x)
- >>> diag.shape
- (2, 1)
- The diagonal can be addressed like a matrix or vector and will
- return the corresponding element of the original matrix:
- >>> diag[1, 0] == diag[1] == x[1, 1]
- True
- The length of the diagonal -- the lesser of the two dimensions of `M` --
- is accessed through the `diagonal_length` property:
- >>> diag.diagonal_length
- 2
- >>> DiagonalOf(MatrixSymbol('x', n + 1, n)).diagonal_length
- n
- When only one of the dimensions is symbolic the other will be
- treated as though it is smaller:
- >>> dtall = DiagonalOf(MatrixSymbol('x', n, 3))
- >>> dtall.diagonal_length
- 3
- When the size of the diagonal is not known, a value of None will
- be returned:
- >>> DiagonalOf(MatrixSymbol('x', n, m)).diagonal_length is None
- True
- """
- arg = property(lambda self: self.args[0])
- @property
- def shape(self):
- r, c = self.arg.shape
- if r.is_Integer and c.is_Integer:
- m = min(r, c)
- elif r.is_Integer and not c.is_Integer:
- m = r
- elif c.is_Integer and not r.is_Integer:
- m = c
- elif r == c:
- m = r
- else:
- try:
- m = min(r, c)
- except TypeError:
- m = None
- return m, S.One
- @property
- def diagonal_length(self):
- return self.shape[0]
- def _entry(self, i, j, **kwargs):
- return self.arg._entry(i, i, **kwargs)
- class DiagMatrix(MatrixExpr):
- """
- Turn a vector into a diagonal matrix.
- """
- def __new__(cls, vector):
- vector = _sympify(vector)
- obj = MatrixExpr.__new__(cls, vector)
- shape = vector.shape
- dim = shape[1] if shape[0] == 1 else shape[0]
- if vector.shape[0] != 1:
- obj._iscolumn = True
- else:
- obj._iscolumn = False
- obj._shape = (dim, dim)
- obj._vector = vector
- return obj
- @property
- def shape(self):
- return self._shape
- def _entry(self, i, j, **kwargs):
- if self._iscolumn:
- result = self._vector._entry(i, 0, **kwargs)
- else:
- result = self._vector._entry(0, j, **kwargs)
- if i != j:
- result *= KroneckerDelta(i, j)
- return result
- def _eval_transpose(self):
- return self
- def as_explicit(self):
- from sympy.matrices.dense import diag
- return diag(*list(self._vector.as_explicit()))
- def doit(self, **hints):
- from sympy.assumptions import ask, Q
- from sympy.matrices.expressions.matmul import MatMul
- from sympy.matrices.expressions.transpose import Transpose
- from sympy.matrices.dense import eye
- from sympy.matrices.matrices import MatrixBase
- vector = self._vector
- # This accounts for shape (1, 1) and identity matrices, among others:
- if ask(Q.diagonal(vector)):
- return vector
- if isinstance(vector, MatrixBase):
- ret = eye(max(vector.shape))
- for i in range(ret.shape[0]):
- ret[i, i] = vector[i]
- return type(vector)(ret)
- if vector.is_MatMul:
- matrices = [arg for arg in vector.args if arg.is_Matrix]
- scalars = [arg for arg in vector.args if arg not in matrices]
- if scalars:
- return Mul.fromiter(scalars)*DiagMatrix(MatMul.fromiter(matrices).doit()).doit()
- if isinstance(vector, Transpose):
- vector = vector.arg
- return DiagMatrix(vector)
- def diagonalize_vector(vector):
- return DiagMatrix(vector).doit()
|