|
from sympy.assumptions.ask import ask, Q |
|
from sympy.core.relational import Eq |
|
from sympy.core.singleton import S |
|
from sympy.core.sympify import _sympify |
|
from sympy.functions.special.tensor_functions import KroneckerDelta |
|
from sympy.matrices.exceptions import NonInvertibleMatrixError |
|
from .matexpr import MatrixExpr |
|
|
|
|
|
class ZeroMatrix(MatrixExpr): |
|
"""The Matrix Zero 0 - additive identity |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy import MatrixSymbol, ZeroMatrix |
|
>>> A = MatrixSymbol('A', 3, 5) |
|
>>> Z = ZeroMatrix(3, 5) |
|
>>> A + Z |
|
A |
|
>>> Z*A.T |
|
0 |
|
""" |
|
is_ZeroMatrix = True |
|
|
|
def __new__(cls, m, n): |
|
m, n = _sympify(m), _sympify(n) |
|
cls._check_dim(m) |
|
cls._check_dim(n) |
|
|
|
return super().__new__(cls, m, n) |
|
|
|
@property |
|
def shape(self): |
|
return (self.args[0], self.args[1]) |
|
|
|
def _eval_power(self, exp): |
|
|
|
if (exp < 0) == True: |
|
raise NonInvertibleMatrixError("Matrix det == 0; not invertible") |
|
return self |
|
|
|
def _eval_transpose(self): |
|
return ZeroMatrix(self.cols, self.rows) |
|
|
|
def _eval_adjoint(self): |
|
return ZeroMatrix(self.cols, self.rows) |
|
|
|
def _eval_trace(self): |
|
return S.Zero |
|
|
|
def _eval_determinant(self): |
|
return S.Zero |
|
|
|
def _eval_inverse(self): |
|
raise NonInvertibleMatrixError("Matrix det == 0; not invertible.") |
|
|
|
def _eval_as_real_imag(self): |
|
return (self, self) |
|
|
|
def _eval_conjugate(self): |
|
return self |
|
|
|
def _entry(self, i, j, **kwargs): |
|
return S.Zero |
|
|
|
|
|
class GenericZeroMatrix(ZeroMatrix): |
|
""" |
|
A zero matrix without a specified shape |
|
|
|
This exists primarily so MatAdd() with no arguments can return something |
|
meaningful. |
|
""" |
|
def __new__(cls): |
|
|
|
|
|
return super(ZeroMatrix, cls).__new__(cls) |
|
|
|
@property |
|
def rows(self): |
|
raise TypeError("GenericZeroMatrix does not have a specified shape") |
|
|
|
@property |
|
def cols(self): |
|
raise TypeError("GenericZeroMatrix does not have a specified shape") |
|
|
|
@property |
|
def shape(self): |
|
raise TypeError("GenericZeroMatrix does not have a specified shape") |
|
|
|
|
|
def __eq__(self, other): |
|
return isinstance(other, GenericZeroMatrix) |
|
|
|
def __ne__(self, other): |
|
return not (self == other) |
|
|
|
def __hash__(self): |
|
return super().__hash__() |
|
|
|
|
|
|
|
class Identity(MatrixExpr): |
|
"""The Matrix Identity I - multiplicative identity |
|
|
|
Examples |
|
======== |
|
|
|
>>> from sympy import Identity, MatrixSymbol |
|
>>> A = MatrixSymbol('A', 3, 5) |
|
>>> I = Identity(3) |
|
>>> I*A |
|
A |
|
""" |
|
|
|
is_Identity = True |
|
|
|
def __new__(cls, n): |
|
n = _sympify(n) |
|
cls._check_dim(n) |
|
|
|
return super().__new__(cls, n) |
|
|
|
@property |
|
def rows(self): |
|
return self.args[0] |
|
|
|
@property |
|
def cols(self): |
|
return self.args[0] |
|
|
|
@property |
|
def shape(self): |
|
return (self.args[0], self.args[0]) |
|
|
|
@property |
|
def is_square(self): |
|
return True |
|
|
|
def _eval_transpose(self): |
|
return self |
|
|
|
def _eval_trace(self): |
|
return self.rows |
|
|
|
def _eval_inverse(self): |
|
return self |
|
|
|
def _eval_as_real_imag(self): |
|
return (self, ZeroMatrix(*self.shape)) |
|
|
|
def _eval_conjugate(self): |
|
return self |
|
|
|
def _eval_adjoint(self): |
|
return self |
|
|
|
def _entry(self, i, j, **kwargs): |
|
eq = Eq(i, j) |
|
if eq is S.true: |
|
return S.One |
|
elif eq is S.false: |
|
return S.Zero |
|
return KroneckerDelta(i, j, (0, self.cols-1)) |
|
|
|
def _eval_determinant(self): |
|
return S.One |
|
|
|
def _eval_power(self, exp): |
|
return self |
|
|
|
|
|
class GenericIdentity(Identity): |
|
""" |
|
An identity matrix without a specified shape |
|
|
|
This exists primarily so MatMul() with no arguments can return something |
|
meaningful. |
|
""" |
|
def __new__(cls): |
|
|
|
|
|
return super(Identity, cls).__new__(cls) |
|
|
|
@property |
|
def rows(self): |
|
raise TypeError("GenericIdentity does not have a specified shape") |
|
|
|
@property |
|
def cols(self): |
|
raise TypeError("GenericIdentity does not have a specified shape") |
|
|
|
@property |
|
def shape(self): |
|
raise TypeError("GenericIdentity does not have a specified shape") |
|
|
|
@property |
|
def is_square(self): |
|
return True |
|
|
|
|
|
def __eq__(self, other): |
|
return isinstance(other, GenericIdentity) |
|
|
|
def __ne__(self, other): |
|
return not (self == other) |
|
|
|
def __hash__(self): |
|
return super().__hash__() |
|
|
|
|
|
class OneMatrix(MatrixExpr): |
|
""" |
|
Matrix whose all entries are ones. |
|
""" |
|
def __new__(cls, m, n, evaluate=False): |
|
m, n = _sympify(m), _sympify(n) |
|
cls._check_dim(m) |
|
cls._check_dim(n) |
|
|
|
if evaluate: |
|
condition = Eq(m, 1) & Eq(n, 1) |
|
if condition == True: |
|
return Identity(1) |
|
|
|
obj = super().__new__(cls, m, n) |
|
return obj |
|
|
|
@property |
|
def shape(self): |
|
return self._args |
|
|
|
@property |
|
def is_Identity(self): |
|
return self._is_1x1() == True |
|
|
|
def as_explicit(self): |
|
from sympy.matrices.immutable import ImmutableDenseMatrix |
|
return ImmutableDenseMatrix.ones(*self.shape) |
|
|
|
def doit(self, **hints): |
|
args = self.args |
|
if hints.get('deep', True): |
|
args = [a.doit(**hints) for a in args] |
|
return self.func(*args, evaluate=True) |
|
|
|
def _eval_power(self, exp): |
|
|
|
if self._is_1x1() == True: |
|
return Identity(1) |
|
if (exp < 0) == True: |
|
raise NonInvertibleMatrixError("Matrix det == 0; not invertible") |
|
if ask(Q.integer(exp)): |
|
return self.shape[0] ** (exp - 1) * OneMatrix(*self.shape) |
|
return super()._eval_power(exp) |
|
|
|
def _eval_transpose(self): |
|
return OneMatrix(self.cols, self.rows) |
|
|
|
def _eval_adjoint(self): |
|
return OneMatrix(self.cols, self.rows) |
|
|
|
def _eval_trace(self): |
|
return S.One*self.rows |
|
|
|
def _is_1x1(self): |
|
"""Returns true if the matrix is known to be 1x1""" |
|
shape = self.shape |
|
return Eq(shape[0], 1) & Eq(shape[1], 1) |
|
|
|
def _eval_determinant(self): |
|
condition = self._is_1x1() |
|
if condition == True: |
|
return S.One |
|
elif condition == False: |
|
return S.Zero |
|
else: |
|
from sympy.matrices.expressions.determinant import Determinant |
|
return Determinant(self) |
|
|
|
def _eval_inverse(self): |
|
condition = self._is_1x1() |
|
if condition == True: |
|
return Identity(1) |
|
elif condition == False: |
|
raise NonInvertibleMatrixError("Matrix det == 0; not invertible.") |
|
else: |
|
from .inverse import Inverse |
|
return Inverse(self) |
|
|
|
def _eval_as_real_imag(self): |
|
return (self, ZeroMatrix(*self.shape)) |
|
|
|
def _eval_conjugate(self): |
|
return self |
|
|
|
def _entry(self, i, j, **kwargs): |
|
return S.One |
|
|