Coverage for /usr/lib/python3/dist-packages/sympy/matrices/expressions/permutation.py: 17%
144 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 sympy.core import S
2from sympy.core.sympify import _sympify
3from sympy.functions import KroneckerDelta
5from .matexpr import MatrixExpr
6from .special import ZeroMatrix, Identity, OneMatrix
9class PermutationMatrix(MatrixExpr):
10 """A Permutation Matrix
12 Parameters
13 ==========
15 perm : Permutation
16 The permutation the matrix uses.
18 The size of the permutation determines the matrix size.
20 See the documentation of
21 :class:`sympy.combinatorics.permutations.Permutation` for
22 the further information of how to create a permutation object.
24 Examples
25 ========
27 >>> from sympy import Matrix, PermutationMatrix
28 >>> from sympy.combinatorics import Permutation
30 Creating a permutation matrix:
32 >>> p = Permutation(1, 2, 0)
33 >>> P = PermutationMatrix(p)
34 >>> P = P.as_explicit()
35 >>> P
36 Matrix([
37 [0, 1, 0],
38 [0, 0, 1],
39 [1, 0, 0]])
41 Permuting a matrix row and column:
43 >>> M = Matrix([0, 1, 2])
44 >>> Matrix(P*M)
45 Matrix([
46 [1],
47 [2],
48 [0]])
50 >>> Matrix(M.T*P)
51 Matrix([[2, 0, 1]])
53 See Also
54 ========
56 sympy.combinatorics.permutations.Permutation
57 """
59 def __new__(cls, perm):
60 from sympy.combinatorics.permutations import Permutation
62 perm = _sympify(perm)
63 if not isinstance(perm, Permutation):
64 raise ValueError(
65 "{} must be a SymPy Permutation instance.".format(perm))
67 return super().__new__(cls, perm)
69 @property
70 def shape(self):
71 size = self.args[0].size
72 return (size, size)
74 @property
75 def is_Identity(self):
76 return self.args[0].is_Identity
78 def doit(self, **hints):
79 if self.is_Identity:
80 return Identity(self.rows)
81 return self
83 def _entry(self, i, j, **kwargs):
84 perm = self.args[0]
85 return KroneckerDelta(perm.apply(i), j)
87 def _eval_power(self, exp):
88 return PermutationMatrix(self.args[0] ** exp).doit()
90 def _eval_inverse(self):
91 return PermutationMatrix(self.args[0] ** -1)
93 _eval_transpose = _eval_adjoint = _eval_inverse
95 def _eval_determinant(self):
96 sign = self.args[0].signature()
97 if sign == 1:
98 return S.One
99 elif sign == -1:
100 return S.NegativeOne
101 raise NotImplementedError
103 def _eval_rewrite_as_BlockDiagMatrix(self, *args, **kwargs):
104 from sympy.combinatorics.permutations import Permutation
105 from .blockmatrix import BlockDiagMatrix
107 perm = self.args[0]
108 full_cyclic_form = perm.full_cyclic_form
110 cycles_picks = []
112 # Stage 1. Decompose the cycles into the blockable form.
113 a, b, c = 0, 0, 0
114 flag = False
115 for cycle in full_cyclic_form:
116 l = len(cycle)
117 m = max(cycle)
119 if not flag:
120 if m + 1 > a + l:
121 flag = True
122 temp = [cycle]
123 b = m
124 c = l
125 else:
126 cycles_picks.append([cycle])
127 a += l
129 else:
130 if m > b:
131 if m + 1 == a + c + l:
132 temp.append(cycle)
133 cycles_picks.append(temp)
134 flag = False
135 a = m+1
136 else:
137 b = m
138 temp.append(cycle)
139 c += l
140 else:
141 if b + 1 == a + c + l:
142 temp.append(cycle)
143 cycles_picks.append(temp)
144 flag = False
145 a = b+1
146 else:
147 temp.append(cycle)
148 c += l
150 # Stage 2. Normalize each decomposed cycles and build matrix.
151 p = 0
152 args = []
153 for pick in cycles_picks:
154 new_cycles = []
155 l = 0
156 for cycle in pick:
157 new_cycle = [i - p for i in cycle]
158 new_cycles.append(new_cycle)
159 l += len(cycle)
160 p += l
161 perm = Permutation(new_cycles)
162 mat = PermutationMatrix(perm)
163 args.append(mat)
165 return BlockDiagMatrix(*args)
168class MatrixPermute(MatrixExpr):
169 r"""Symbolic representation for permuting matrix rows or columns.
171 Parameters
172 ==========
174 perm : Permutation, PermutationMatrix
175 The permutation to use for permuting the matrix.
176 The permutation can be resized to the suitable one,
178 axis : 0 or 1
179 The axis to permute alongside.
180 If `0`, it will permute the matrix rows.
181 If `1`, it will permute the matrix columns.
183 Notes
184 =====
186 This follows the same notation used in
187 :meth:`sympy.matrices.common.MatrixCommon.permute`.
189 Examples
190 ========
192 >>> from sympy import Matrix, MatrixPermute
193 >>> from sympy.combinatorics import Permutation
195 Permuting the matrix rows:
197 >>> p = Permutation(1, 2, 0)
198 >>> A = Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
199 >>> B = MatrixPermute(A, p, axis=0)
200 >>> B.as_explicit()
201 Matrix([
202 [4, 5, 6],
203 [7, 8, 9],
204 [1, 2, 3]])
206 Permuting the matrix columns:
208 >>> B = MatrixPermute(A, p, axis=1)
209 >>> B.as_explicit()
210 Matrix([
211 [2, 3, 1],
212 [5, 6, 4],
213 [8, 9, 7]])
215 See Also
216 ========
218 sympy.matrices.common.MatrixCommon.permute
219 """
220 def __new__(cls, mat, perm, axis=S.Zero):
221 from sympy.combinatorics.permutations import Permutation
223 mat = _sympify(mat)
224 if not mat.is_Matrix:
225 raise ValueError(
226 "{} must be a SymPy matrix instance.".format(perm))
228 perm = _sympify(perm)
229 if isinstance(perm, PermutationMatrix):
230 perm = perm.args[0]
232 if not isinstance(perm, Permutation):
233 raise ValueError(
234 "{} must be a SymPy Permutation or a PermutationMatrix " \
235 "instance".format(perm))
237 axis = _sympify(axis)
238 if axis not in (0, 1):
239 raise ValueError("The axis must be 0 or 1.")
241 mat_size = mat.shape[axis]
242 if mat_size != perm.size:
243 try:
244 perm = perm.resize(mat_size)
245 except ValueError:
246 raise ValueError(
247 "Size does not match between the permutation {} "
248 "and the matrix {} threaded over the axis {} "
249 "and cannot be converted."
250 .format(perm, mat, axis))
252 return super().__new__(cls, mat, perm, axis)
254 def doit(self, deep=True, **hints):
255 mat, perm, axis = self.args
257 if deep:
258 mat = mat.doit(deep=deep, **hints)
259 perm = perm.doit(deep=deep, **hints)
261 if perm.is_Identity:
262 return mat
264 if mat.is_Identity:
265 if axis is S.Zero:
266 return PermutationMatrix(perm)
267 elif axis is S.One:
268 return PermutationMatrix(perm**-1)
270 if isinstance(mat, (ZeroMatrix, OneMatrix)):
271 return mat
273 if isinstance(mat, MatrixPermute) and mat.args[2] == axis:
274 return MatrixPermute(mat.args[0], perm * mat.args[1], axis)
276 return self
278 @property
279 def shape(self):
280 return self.args[0].shape
282 def _entry(self, i, j, **kwargs):
283 mat, perm, axis = self.args
285 if axis == 0:
286 return mat[perm.apply(i), j]
287 elif axis == 1:
288 return mat[i, perm.apply(j)]
290 def _eval_rewrite_as_MatMul(self, *args, **kwargs):
291 from .matmul import MatMul
293 mat, perm, axis = self.args
295 deep = kwargs.get("deep", True)
297 if deep:
298 mat = mat.rewrite(MatMul)
300 if axis == 0:
301 return MatMul(PermutationMatrix(perm), mat)
302 elif axis == 1:
303 return MatMul(mat, PermutationMatrix(perm**-1))