Coverage for /usr/lib/python3/dist-packages/sympy/matrices/expressions/matpow.py: 26%
97 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1from .matexpr import MatrixExpr
2from .special import Identity
3from sympy.core import S
4from sympy.core.expr import ExprBuilder
5from sympy.core.cache import cacheit
6from sympy.core.power import Pow
7from sympy.core.sympify import _sympify
8from sympy.matrices import MatrixBase
9from sympy.matrices.common import NonSquareMatrixError
12class MatPow(MatrixExpr):
13 def __new__(cls, base, exp, evaluate=False, **options):
14 base = _sympify(base)
15 if not base.is_Matrix:
16 raise TypeError("MatPow base should be a matrix")
18 if base.is_square is False:
19 raise NonSquareMatrixError("Power of non-square matrix %s" % base)
21 exp = _sympify(exp)
22 obj = super().__new__(cls, base, exp)
24 if evaluate:
25 obj = obj.doit(deep=False)
27 return obj
29 @property
30 def base(self):
31 return self.args[0]
33 @property
34 def exp(self):
35 return self.args[1]
37 @property
38 def shape(self):
39 return self.base.shape
41 @cacheit
42 def _get_explicit_matrix(self):
43 return self.base.as_explicit()**self.exp
45 def _entry(self, i, j, **kwargs):
46 from sympy.matrices.expressions import MatMul
47 A = self.doit()
48 if isinstance(A, MatPow):
49 # We still have a MatPow, make an explicit MatMul out of it.
50 if A.exp.is_Integer and A.exp.is_positive:
51 A = MatMul(*[A.base for k in range(A.exp)])
52 elif not self._is_shape_symbolic():
53 return A._get_explicit_matrix()[i, j]
54 else:
55 # Leave the expression unevaluated:
56 from sympy.matrices.expressions.matexpr import MatrixElement
57 return MatrixElement(self, i, j)
58 return A[i, j]
60 def doit(self, **hints):
61 if hints.get('deep', True):
62 base, exp = (arg.doit(**hints) for arg in self.args)
63 else:
64 base, exp = self.args
66 # combine all powers, e.g. (A ** 2) ** 3 -> A ** 6
67 while isinstance(base, MatPow):
68 exp *= base.args[1]
69 base = base.args[0]
71 if isinstance(base, MatrixBase):
72 # Delegate
73 return base ** exp
75 # Handle simple cases so that _eval_power() in MatrixExpr sub-classes can ignore them
76 if exp == S.One:
77 return base
78 if exp == S.Zero:
79 return Identity(base.rows)
80 if exp == S.NegativeOne:
81 from sympy.matrices.expressions import Inverse
82 return Inverse(base).doit(**hints)
84 eval_power = getattr(base, '_eval_power', None)
85 if eval_power is not None:
86 return eval_power(exp)
88 return MatPow(base, exp)
90 def _eval_transpose(self):
91 base, exp = self.args
92 return MatPow(base.T, exp)
94 def _eval_derivative(self, x):
95 return Pow._eval_derivative(self, x)
97 def _eval_derivative_matrix_lines(self, x):
98 from sympy.tensor.array.expressions.array_expressions import ArrayContraction
99 from ...tensor.array.expressions.array_expressions import ArrayTensorProduct
100 from .matmul import MatMul
101 from .inverse import Inverse
102 exp = self.exp
103 if self.base.shape == (1, 1) and not exp.has(x):
104 lr = self.base._eval_derivative_matrix_lines(x)
105 for i in lr:
106 subexpr = ExprBuilder(
107 ArrayContraction,
108 [
109 ExprBuilder(
110 ArrayTensorProduct,
111 [
112 Identity(1),
113 i._lines[0],
114 exp*self.base**(exp-1),
115 i._lines[1],
116 Identity(1),
117 ]
118 ),
119 (0, 3, 4), (5, 7, 8)
120 ],
121 validator=ArrayContraction._validate
122 )
123 i._first_pointer_parent = subexpr.args[0].args
124 i._first_pointer_index = 0
125 i._second_pointer_parent = subexpr.args[0].args
126 i._second_pointer_index = 4
127 i._lines = [subexpr]
128 return lr
129 if (exp > 0) == True:
130 newexpr = MatMul.fromiter([self.base for i in range(exp)])
131 elif (exp == -1) == True:
132 return Inverse(self.base)._eval_derivative_matrix_lines(x)
133 elif (exp < 0) == True:
134 newexpr = MatMul.fromiter([Inverse(self.base) for i in range(-exp)])
135 elif (exp == 0) == True:
136 return self.doit()._eval_derivative_matrix_lines(x)
137 else:
138 raise NotImplementedError("cannot evaluate %s derived by %s" % (self, x))
139 return newexpr._eval_derivative_matrix_lines(x)
141 def _eval_inverse(self):
142 return MatPow(self.base, -self.exp)