Source code for sympy.matrices.expressions.matpow

from __future__ import print_function, division

from .matexpr import MatrixExpr, ShapeError, Identity, ZeroMatrix
from sympy.core import S
from sympy.core.compatibility import range
from sympy.core.sympify import _sympify
from sympy.matrices import MatrixBase


[docs]class MatPow(MatrixExpr): def __new__(cls, base, exp): base = _sympify(base) if not base.is_Matrix: raise TypeError("Function parameter should be a matrix") exp = _sympify(exp) return super(MatPow, cls).__new__(cls, base, exp) @property def base(self): return self.args[0] @property def exp(self): return self.args[1] @property def shape(self): return self.base.shape def _entry(self, i, j, **kwargs): from sympy.matrices.expressions import MatMul A = self.doit() if isinstance(A, MatPow): # We still have a MatPow, make an explicit MatMul out of it. if not A.base.is_square: raise ShapeError("Power of non-square matrix %s" % A.base) elif A.exp.is_Integer and A.exp.is_positive: A = MatMul(*[A.base for k in range(A.exp)]) #elif A.exp.is_Integer and self.exp.is_negative: # Note: possible future improvement: in principle we can take # positive powers of the inverse, but carefully avoid recursion, # perhaps by adding `_entry` to Inverse (as it is our subclass). # T = A.base.as_explicit().inverse() # A = MatMul(*[T for k in range(-A.exp)]) else: # Leave the expression unevaluated: from sympy.matrices.expressions.matexpr import MatrixElement return MatrixElement(self, i, j) return A._entry(i, j) def doit(self, **kwargs): from sympy.matrices.expressions import Inverse deep = kwargs.get('deep', True) if deep: args = [arg.doit(**kwargs) for arg in self.args] else: args = self.args base, exp = args # combine all powers, e.g. (A**2)**3 = A**6 while isinstance(base, MatPow): exp = exp*base.args[1] base = base.args[0] if exp.is_zero and base.is_square: if isinstance(base, MatrixBase): return base.func(Identity(base.shape[0])) return Identity(base.shape[0]) elif isinstance(base, ZeroMatrix) and exp.is_negative: raise ValueError("Matrix determinant is 0, not invertible.") elif isinstance(base, (Identity, ZeroMatrix)): return base elif isinstance(base, MatrixBase) and exp.is_number: if exp is S.One: return base return base**exp # Note: just evaluate cases we know, return unevaluated on others. # E.g., MatrixSymbol('x', n, m) to power 0 is not an error. elif exp is S(-1) and base.is_square: return Inverse(base).doit(**kwargs) elif exp is S.One: return base return MatPow(base, exp) def _eval_transpose(self): base, exp = self.args return MatPow(base.T, exp) def _eval_derivative_matrix_lines(self, x): from .matmul import MatMul from .inverse import Inverse exp = self.exp if (exp > 0) == True: newexpr = MatMul.fromiter([self.base for i in range(exp)]) elif (exp == -1) == True: return Inverse(self.base)._eval_derivative_matrix_lines(x) elif (exp < 0) == True: newexpr = MatMul.fromiter([Inverse(self.base) for i in range(-exp)]) elif (exp == 0) == True: return self.doit()._eval_derivative_matrix_lines(x) else: raise NotImplementedError("cannot evaluate %s derived by %s" % (self, x)) return newexpr._eval_derivative_matrix_lines(x)