Coverage for /usr/lib/python3/dist-packages/sympy/matrices/expressions/special.py: 47%

181 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-06-14 15:55 +0200

1from sympy.assumptions.ask import ask, Q 

2from sympy.core.relational import Eq 

3from sympy.core.singleton import S 

4from sympy.core.sympify import _sympify 

5from sympy.functions.special.tensor_functions import KroneckerDelta 

6from sympy.matrices.common import NonInvertibleMatrixError 

7from .matexpr import MatrixExpr 

8 

9 

10class ZeroMatrix(MatrixExpr): 

11 """The Matrix Zero 0 - additive identity 

12 

13 Examples 

14 ======== 

15 

16 >>> from sympy import MatrixSymbol, ZeroMatrix 

17 >>> A = MatrixSymbol('A', 3, 5) 

18 >>> Z = ZeroMatrix(3, 5) 

19 >>> A + Z 

20 A 

21 >>> Z*A.T 

22 0 

23 """ 

24 is_ZeroMatrix = True 

25 

26 def __new__(cls, m, n): 

27 m, n = _sympify(m), _sympify(n) 

28 cls._check_dim(m) 

29 cls._check_dim(n) 

30 

31 return super().__new__(cls, m, n) 

32 

33 @property 

34 def shape(self): 

35 return (self.args[0], self.args[1]) 

36 

37 def _eval_power(self, exp): 

38 # exp = -1, 0, 1 are already handled at this stage 

39 if (exp < 0) == True: 

40 raise NonInvertibleMatrixError("Matrix det == 0; not invertible") 

41 return self 

42 

43 def _eval_transpose(self): 

44 return ZeroMatrix(self.cols, self.rows) 

45 

46 def _eval_adjoint(self): 

47 return ZeroMatrix(self.cols, self.rows) 

48 

49 def _eval_trace(self): 

50 return S.Zero 

51 

52 def _eval_determinant(self): 

53 return S.Zero 

54 

55 def _eval_inverse(self): 

56 raise NonInvertibleMatrixError("Matrix det == 0; not invertible.") 

57 

58 def _eval_as_real_imag(self): 

59 return (self, self) 

60 

61 def _eval_conjugate(self): 

62 return self 

63 

64 def _entry(self, i, j, **kwargs): 

65 return S.Zero 

66 

67 

68class GenericZeroMatrix(ZeroMatrix): 

69 """ 

70 A zero matrix without a specified shape 

71 

72 This exists primarily so MatAdd() with no arguments can return something 

73 meaningful. 

74 """ 

75 def __new__(cls): 

76 # super(ZeroMatrix, cls) instead of super(GenericZeroMatrix, cls) 

77 # because ZeroMatrix.__new__ doesn't have the same signature 

78 return super(ZeroMatrix, cls).__new__(cls) 

79 

80 @property 

81 def rows(self): 

82 raise TypeError("GenericZeroMatrix does not have a specified shape") 

83 

84 @property 

85 def cols(self): 

86 raise TypeError("GenericZeroMatrix does not have a specified shape") 

87 

88 @property 

89 def shape(self): 

90 raise TypeError("GenericZeroMatrix does not have a specified shape") 

91 

92 # Avoid Matrix.__eq__ which might call .shape 

93 def __eq__(self, other): 

94 return isinstance(other, GenericZeroMatrix) 

95 

96 def __ne__(self, other): 

97 return not (self == other) 

98 

99 def __hash__(self): 

100 return super().__hash__() 

101 

102 

103 

104class Identity(MatrixExpr): 

105 """The Matrix Identity I - multiplicative identity 

106 

107 Examples 

108 ======== 

109 

110 >>> from sympy import Identity, MatrixSymbol 

111 >>> A = MatrixSymbol('A', 3, 5) 

112 >>> I = Identity(3) 

113 >>> I*A 

114 A 

115 """ 

116 

117 is_Identity = True 

118 

119 def __new__(cls, n): 

120 n = _sympify(n) 

121 cls._check_dim(n) 

122 

123 return super().__new__(cls, n) 

124 

125 @property 

126 def rows(self): 

127 return self.args[0] 

128 

129 @property 

130 def cols(self): 

131 return self.args[0] 

132 

133 @property 

134 def shape(self): 

135 return (self.args[0], self.args[0]) 

136 

137 @property 

138 def is_square(self): 

139 return True 

140 

141 def _eval_transpose(self): 

142 return self 

143 

144 def _eval_trace(self): 

145 return self.rows 

146 

147 def _eval_inverse(self): 

148 return self 

149 

150 def _eval_as_real_imag(self): 

151 return (self, ZeroMatrix(*self.shape)) 

152 

153 def _eval_conjugate(self): 

154 return self 

155 

156 def _eval_adjoint(self): 

157 return self 

158 

159 def _entry(self, i, j, **kwargs): 

160 eq = Eq(i, j) 

161 if eq is S.true: 

162 return S.One 

163 elif eq is S.false: 

164 return S.Zero 

165 return KroneckerDelta(i, j, (0, self.cols-1)) 

166 

167 def _eval_determinant(self): 

168 return S.One 

169 

170 def _eval_power(self, exp): 

171 return self 

172 

173 

174class GenericIdentity(Identity): 

175 """ 

176 An identity matrix without a specified shape 

177 

178 This exists primarily so MatMul() with no arguments can return something 

179 meaningful. 

180 """ 

181 def __new__(cls): 

182 # super(Identity, cls) instead of super(GenericIdentity, cls) because 

183 # Identity.__new__ doesn't have the same signature 

184 return super(Identity, cls).__new__(cls) 

185 

186 @property 

187 def rows(self): 

188 raise TypeError("GenericIdentity does not have a specified shape") 

189 

190 @property 

191 def cols(self): 

192 raise TypeError("GenericIdentity does not have a specified shape") 

193 

194 @property 

195 def shape(self): 

196 raise TypeError("GenericIdentity does not have a specified shape") 

197 

198 @property 

199 def is_square(self): 

200 return True 

201 

202 # Avoid Matrix.__eq__ which might call .shape 

203 def __eq__(self, other): 

204 return isinstance(other, GenericIdentity) 

205 

206 def __ne__(self, other): 

207 return not (self == other) 

208 

209 def __hash__(self): 

210 return super().__hash__() 

211 

212 

213class OneMatrix(MatrixExpr): 

214 """ 

215 Matrix whose all entries are ones. 

216 """ 

217 def __new__(cls, m, n, evaluate=False): 

218 m, n = _sympify(m), _sympify(n) 

219 cls._check_dim(m) 

220 cls._check_dim(n) 

221 

222 if evaluate: 

223 condition = Eq(m, 1) & Eq(n, 1) 

224 if condition == True: 

225 return Identity(1) 

226 

227 obj = super().__new__(cls, m, n) 

228 return obj 

229 

230 @property 

231 def shape(self): 

232 return self._args 

233 

234 @property 

235 def is_Identity(self): 

236 return self._is_1x1() == True 

237 

238 def as_explicit(self): 

239 from sympy.matrices.immutable import ImmutableDenseMatrix 

240 return ImmutableDenseMatrix.ones(*self.shape) 

241 

242 def doit(self, **hints): 

243 args = self.args 

244 if hints.get('deep', True): 

245 args = [a.doit(**hints) for a in args] 

246 return self.func(*args, evaluate=True) 

247 

248 def _eval_power(self, exp): 

249 # exp = -1, 0, 1 are already handled at this stage 

250 if self._is_1x1() == True: 

251 return Identity(1) 

252 if (exp < 0) == True: 

253 raise NonInvertibleMatrixError("Matrix det == 0; not invertible") 

254 if ask(Q.integer(exp)): 

255 return self.shape[0] ** (exp - 1) * OneMatrix(*self.shape) 

256 return super()._eval_power(exp) 

257 

258 def _eval_transpose(self): 

259 return OneMatrix(self.cols, self.rows) 

260 

261 def _eval_adjoint(self): 

262 return OneMatrix(self.cols, self.rows) 

263 

264 def _eval_trace(self): 

265 return S.One*self.rows 

266 

267 def _is_1x1(self): 

268 """Returns true if the matrix is known to be 1x1""" 

269 shape = self.shape 

270 return Eq(shape[0], 1) & Eq(shape[1], 1) 

271 

272 def _eval_determinant(self): 

273 condition = self._is_1x1() 

274 if condition == True: 

275 return S.One 

276 elif condition == False: 

277 return S.Zero 

278 else: 

279 from sympy.matrices.expressions.determinant import Determinant 

280 return Determinant(self) 

281 

282 def _eval_inverse(self): 

283 condition = self._is_1x1() 

284 if condition == True: 

285 return Identity(1) 

286 elif condition == False: 

287 raise NonInvertibleMatrixError("Matrix det == 0; not invertible.") 

288 else: 

289 from .inverse import Inverse 

290 return Inverse(self) 

291 

292 def _eval_as_real_imag(self): 

293 return (self, ZeroMatrix(*self.shape)) 

294 

295 def _eval_conjugate(self): 

296 return self 

297 

298 def _entry(self, i, j, **kwargs): 

299 return S.One