You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

300 lines
7.3 KiB

5 months ago
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.common 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):
# exp = -1, 0, 1 are already handled at this stage
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):
# super(ZeroMatrix, cls) instead of super(GenericZeroMatrix, cls)
# because ZeroMatrix.__new__ doesn't have the same signature
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")
# Avoid Matrix.__eq__ which might call .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):
# super(Identity, cls) instead of super(GenericIdentity, cls) because
# Identity.__new__ doesn't have the same signature
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
# Avoid Matrix.__eq__ which might call .shape
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):
# exp = -1, 0, 1 are already handled at this stage
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