MTerryJack commited on
Commit
0135cc9
·
verified ·
1 Parent(s): 0c8bf8b

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .venv/lib/python3.13/site-packages/sympy/assumptions/predicates/matrices.py +511 -0
  2. .venv/lib/python3.13/site-packages/sympy/assumptions/predicates/order.py +390 -0
  3. .venv/lib/python3.13/site-packages/sympy/codegen/tests/__init__.py +0 -0
  4. .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_abstract_nodes.py +14 -0
  5. .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_algorithms.py +180 -0
  6. .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_applications.py +58 -0
  7. .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_approximations.py +53 -0
  8. .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_ast.py +661 -0
  9. .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_cfunctions.py +186 -0
  10. .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_cnodes.py +112 -0
  11. .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_cxxnodes.py +14 -0
  12. .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_fnodes.py +213 -0
  13. .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_matrix_nodes.py +50 -0
  14. .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_numpy_nodes.py +69 -0
  15. .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_pynodes.py +13 -0
  16. .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_pyutils.py +7 -0
  17. .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_rewriting.py +479 -0
  18. .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_scipy_nodes.py +44 -0
  19. .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/__init__.py +0 -0
  20. .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_coset_table.py +825 -0
  21. .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_fp_groups.py +257 -0
  22. .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_free_groups.py +226 -0
  23. .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_galois.py +82 -0
  24. .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_generators.py +105 -0
  25. .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_graycode.py +72 -0
  26. .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_group_constructs.py +15 -0
  27. .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_group_numbers.py +110 -0
  28. .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_homomorphisms.py +114 -0
  29. .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_named_groups.py +70 -0
  30. .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_partitions.py +118 -0
  31. .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_pc_groups.py +87 -0
  32. .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_perm_groups.py +1243 -0
  33. .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_permutations.py +564 -0
  34. .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_polyhedron.py +105 -0
  35. .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_prufer.py +74 -0
  36. .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_rewriting.py +49 -0
  37. .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_schur_number.py +55 -0
  38. .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_subsets.py +63 -0
  39. .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_tensor_can.py +560 -0
  40. .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_testutil.py +55 -0
  41. .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_util.py +120 -0
  42. .venv/lib/python3.13/site-packages/sympy/functions/combinatorial/__init__.py +1 -0
  43. .venv/lib/python3.13/site-packages/sympy/functions/combinatorial/factorials.py +1133 -0
  44. .venv/lib/python3.13/site-packages/sympy/functions/combinatorial/numbers.py +0 -0
  45. .venv/lib/python3.13/site-packages/sympy/functions/combinatorial/tests/__init__.py +0 -0
  46. .venv/lib/python3.13/site-packages/sympy/functions/combinatorial/tests/test_comb_factorials.py +653 -0
  47. .venv/lib/python3.13/site-packages/sympy/functions/combinatorial/tests/test_comb_numbers.py +1250 -0
  48. .venv/lib/python3.13/site-packages/sympy/functions/elementary/__init__.py +1 -0
  49. .venv/lib/python3.13/site-packages/sympy/functions/elementary/_trigonometric_special.py +261 -0
  50. .venv/lib/python3.13/site-packages/sympy/functions/elementary/benchmarks/__init__.py +0 -0
.venv/lib/python3.13/site-packages/sympy/assumptions/predicates/matrices.py ADDED
@@ -0,0 +1,511 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.assumptions import Predicate
2
+ from sympy.multipledispatch import Dispatcher
3
+
4
+ class SquarePredicate(Predicate):
5
+ """
6
+ Square matrix predicate.
7
+
8
+ Explanation
9
+ ===========
10
+
11
+ ``Q.square(x)`` is true iff ``x`` is a square matrix. A square matrix
12
+ is a matrix with the same number of rows and columns.
13
+
14
+ Examples
15
+ ========
16
+
17
+ >>> from sympy import Q, ask, MatrixSymbol, ZeroMatrix, Identity
18
+ >>> X = MatrixSymbol('X', 2, 2)
19
+ >>> Y = MatrixSymbol('X', 2, 3)
20
+ >>> ask(Q.square(X))
21
+ True
22
+ >>> ask(Q.square(Y))
23
+ False
24
+ >>> ask(Q.square(ZeroMatrix(3, 3)))
25
+ True
26
+ >>> ask(Q.square(Identity(3)))
27
+ True
28
+
29
+ References
30
+ ==========
31
+
32
+ .. [1] https://en.wikipedia.org/wiki/Square_matrix
33
+
34
+ """
35
+ name = 'square'
36
+ handler = Dispatcher("SquareHandler", doc="Handler for Q.square.")
37
+
38
+
39
+ class SymmetricPredicate(Predicate):
40
+ """
41
+ Symmetric matrix predicate.
42
+
43
+ Explanation
44
+ ===========
45
+
46
+ ``Q.symmetric(x)`` is true iff ``x`` is a square matrix and is equal to
47
+ its transpose. Every square diagonal matrix is a symmetric matrix.
48
+
49
+ Examples
50
+ ========
51
+
52
+ >>> from sympy import Q, ask, MatrixSymbol
53
+ >>> X = MatrixSymbol('X', 2, 2)
54
+ >>> Y = MatrixSymbol('Y', 2, 3)
55
+ >>> Z = MatrixSymbol('Z', 2, 2)
56
+ >>> ask(Q.symmetric(X*Z), Q.symmetric(X) & Q.symmetric(Z))
57
+ True
58
+ >>> ask(Q.symmetric(X + Z), Q.symmetric(X) & Q.symmetric(Z))
59
+ True
60
+ >>> ask(Q.symmetric(Y))
61
+ False
62
+
63
+
64
+ References
65
+ ==========
66
+
67
+ .. [1] https://en.wikipedia.org/wiki/Symmetric_matrix
68
+
69
+ """
70
+ # TODO: Add handlers to make these keys work with
71
+ # actual matrices and add more examples in the docstring.
72
+ name = 'symmetric'
73
+ handler = Dispatcher("SymmetricHandler", doc="Handler for Q.symmetric.")
74
+
75
+
76
+ class InvertiblePredicate(Predicate):
77
+ """
78
+ Invertible matrix predicate.
79
+
80
+ Explanation
81
+ ===========
82
+
83
+ ``Q.invertible(x)`` is true iff ``x`` is an invertible matrix.
84
+ A square matrix is called invertible only if its determinant is 0.
85
+
86
+ Examples
87
+ ========
88
+
89
+ >>> from sympy import Q, ask, MatrixSymbol
90
+ >>> X = MatrixSymbol('X', 2, 2)
91
+ >>> Y = MatrixSymbol('Y', 2, 3)
92
+ >>> Z = MatrixSymbol('Z', 2, 2)
93
+ >>> ask(Q.invertible(X*Y), Q.invertible(X))
94
+ False
95
+ >>> ask(Q.invertible(X*Z), Q.invertible(X) & Q.invertible(Z))
96
+ True
97
+ >>> ask(Q.invertible(X), Q.fullrank(X) & Q.square(X))
98
+ True
99
+
100
+ References
101
+ ==========
102
+
103
+ .. [1] https://en.wikipedia.org/wiki/Invertible_matrix
104
+
105
+ """
106
+ name = 'invertible'
107
+ handler = Dispatcher("InvertibleHandler", doc="Handler for Q.invertible.")
108
+
109
+
110
+ class OrthogonalPredicate(Predicate):
111
+ """
112
+ Orthogonal matrix predicate.
113
+
114
+ Explanation
115
+ ===========
116
+
117
+ ``Q.orthogonal(x)`` is true iff ``x`` is an orthogonal matrix.
118
+ A square matrix ``M`` is an orthogonal matrix if it satisfies
119
+ ``M^TM = MM^T = I`` where ``M^T`` is the transpose matrix of
120
+ ``M`` and ``I`` is an identity matrix. Note that an orthogonal
121
+ matrix is necessarily invertible.
122
+
123
+ Examples
124
+ ========
125
+
126
+ >>> from sympy import Q, ask, MatrixSymbol, Identity
127
+ >>> X = MatrixSymbol('X', 2, 2)
128
+ >>> Y = MatrixSymbol('Y', 2, 3)
129
+ >>> Z = MatrixSymbol('Z', 2, 2)
130
+ >>> ask(Q.orthogonal(Y))
131
+ False
132
+ >>> ask(Q.orthogonal(X*Z*X), Q.orthogonal(X) & Q.orthogonal(Z))
133
+ True
134
+ >>> ask(Q.orthogonal(Identity(3)))
135
+ True
136
+ >>> ask(Q.invertible(X), Q.orthogonal(X))
137
+ True
138
+
139
+ References
140
+ ==========
141
+
142
+ .. [1] https://en.wikipedia.org/wiki/Orthogonal_matrix
143
+
144
+ """
145
+ name = 'orthogonal'
146
+ handler = Dispatcher("OrthogonalHandler", doc="Handler for key 'orthogonal'.")
147
+
148
+
149
+ class UnitaryPredicate(Predicate):
150
+ """
151
+ Unitary matrix predicate.
152
+
153
+ Explanation
154
+ ===========
155
+
156
+ ``Q.unitary(x)`` is true iff ``x`` is a unitary matrix.
157
+ Unitary matrix is an analogue to orthogonal matrix. A square
158
+ matrix ``M`` with complex elements is unitary if :math:``M^TM = MM^T= I``
159
+ where :math:``M^T`` is the conjugate transpose matrix of ``M``.
160
+
161
+ Examples
162
+ ========
163
+
164
+ >>> from sympy import Q, ask, MatrixSymbol, Identity
165
+ >>> X = MatrixSymbol('X', 2, 2)
166
+ >>> Y = MatrixSymbol('Y', 2, 3)
167
+ >>> Z = MatrixSymbol('Z', 2, 2)
168
+ >>> ask(Q.unitary(Y))
169
+ False
170
+ >>> ask(Q.unitary(X*Z*X), Q.unitary(X) & Q.unitary(Z))
171
+ True
172
+ >>> ask(Q.unitary(Identity(3)))
173
+ True
174
+
175
+ References
176
+ ==========
177
+
178
+ .. [1] https://en.wikipedia.org/wiki/Unitary_matrix
179
+
180
+ """
181
+ name = 'unitary'
182
+ handler = Dispatcher("UnitaryHandler", doc="Handler for key 'unitary'.")
183
+
184
+
185
+ class FullRankPredicate(Predicate):
186
+ """
187
+ Fullrank matrix predicate.
188
+
189
+ Explanation
190
+ ===========
191
+
192
+ ``Q.fullrank(x)`` is true iff ``x`` is a full rank matrix.
193
+ A matrix is full rank if all rows and columns of the matrix
194
+ are linearly independent. A square matrix is full rank iff
195
+ its determinant is nonzero.
196
+
197
+ Examples
198
+ ========
199
+
200
+ >>> from sympy import Q, ask, MatrixSymbol, ZeroMatrix, Identity
201
+ >>> X = MatrixSymbol('X', 2, 2)
202
+ >>> ask(Q.fullrank(X.T), Q.fullrank(X))
203
+ True
204
+ >>> ask(Q.fullrank(ZeroMatrix(3, 3)))
205
+ False
206
+ >>> ask(Q.fullrank(Identity(3)))
207
+ True
208
+
209
+ """
210
+ name = 'fullrank'
211
+ handler = Dispatcher("FullRankHandler", doc="Handler for key 'fullrank'.")
212
+
213
+
214
+ class PositiveDefinitePredicate(Predicate):
215
+ r"""
216
+ Positive definite matrix predicate.
217
+
218
+ Explanation
219
+ ===========
220
+
221
+ If $M$ is a :math:`n \times n` symmetric real matrix, it is said
222
+ to be positive definite if :math:`Z^TMZ` is positive for
223
+ every non-zero column vector $Z$ of $n$ real numbers.
224
+
225
+ Examples
226
+ ========
227
+
228
+ >>> from sympy import Q, ask, MatrixSymbol, Identity
229
+ >>> X = MatrixSymbol('X', 2, 2)
230
+ >>> Y = MatrixSymbol('Y', 2, 3)
231
+ >>> Z = MatrixSymbol('Z', 2, 2)
232
+ >>> ask(Q.positive_definite(Y))
233
+ False
234
+ >>> ask(Q.positive_definite(Identity(3)))
235
+ True
236
+ >>> ask(Q.positive_definite(X + Z), Q.positive_definite(X) &
237
+ ... Q.positive_definite(Z))
238
+ True
239
+
240
+ References
241
+ ==========
242
+
243
+ .. [1] https://en.wikipedia.org/wiki/Positive-definite_matrix
244
+
245
+ """
246
+ name = "positive_definite"
247
+ handler = Dispatcher("PositiveDefiniteHandler", doc="Handler for key 'positive_definite'.")
248
+
249
+
250
+ class UpperTriangularPredicate(Predicate):
251
+ """
252
+ Upper triangular matrix predicate.
253
+
254
+ Explanation
255
+ ===========
256
+
257
+ A matrix $M$ is called upper triangular matrix if :math:`M_{ij}=0`
258
+ for :math:`i<j`.
259
+
260
+ Examples
261
+ ========
262
+
263
+ >>> from sympy import Q, ask, ZeroMatrix, Identity
264
+ >>> ask(Q.upper_triangular(Identity(3)))
265
+ True
266
+ >>> ask(Q.upper_triangular(ZeroMatrix(3, 3)))
267
+ True
268
+
269
+ References
270
+ ==========
271
+
272
+ .. [1] https://mathworld.wolfram.com/UpperTriangularMatrix.html
273
+
274
+ """
275
+ name = "upper_triangular"
276
+ handler = Dispatcher("UpperTriangularHandler", doc="Handler for key 'upper_triangular'.")
277
+
278
+
279
+ class LowerTriangularPredicate(Predicate):
280
+ """
281
+ Lower triangular matrix predicate.
282
+
283
+ Explanation
284
+ ===========
285
+
286
+ A matrix $M$ is called lower triangular matrix if :math:`M_{ij}=0`
287
+ for :math:`i>j`.
288
+
289
+ Examples
290
+ ========
291
+
292
+ >>> from sympy import Q, ask, ZeroMatrix, Identity
293
+ >>> ask(Q.lower_triangular(Identity(3)))
294
+ True
295
+ >>> ask(Q.lower_triangular(ZeroMatrix(3, 3)))
296
+ True
297
+
298
+ References
299
+ ==========
300
+
301
+ .. [1] https://mathworld.wolfram.com/LowerTriangularMatrix.html
302
+
303
+ """
304
+ name = "lower_triangular"
305
+ handler = Dispatcher("LowerTriangularHandler", doc="Handler for key 'lower_triangular'.")
306
+
307
+
308
+ class DiagonalPredicate(Predicate):
309
+ """
310
+ Diagonal matrix predicate.
311
+
312
+ Explanation
313
+ ===========
314
+
315
+ ``Q.diagonal(x)`` is true iff ``x`` is a diagonal matrix. A diagonal
316
+ matrix is a matrix in which the entries outside the main diagonal
317
+ are all zero.
318
+
319
+ Examples
320
+ ========
321
+
322
+ >>> from sympy import Q, ask, MatrixSymbol, ZeroMatrix
323
+ >>> X = MatrixSymbol('X', 2, 2)
324
+ >>> ask(Q.diagonal(ZeroMatrix(3, 3)))
325
+ True
326
+ >>> ask(Q.diagonal(X), Q.lower_triangular(X) &
327
+ ... Q.upper_triangular(X))
328
+ True
329
+
330
+ References
331
+ ==========
332
+
333
+ .. [1] https://en.wikipedia.org/wiki/Diagonal_matrix
334
+
335
+ """
336
+ name = "diagonal"
337
+ handler = Dispatcher("DiagonalHandler", doc="Handler for key 'diagonal'.")
338
+
339
+
340
+ class IntegerElementsPredicate(Predicate):
341
+ """
342
+ Integer elements matrix predicate.
343
+
344
+ Explanation
345
+ ===========
346
+
347
+ ``Q.integer_elements(x)`` is true iff all the elements of ``x``
348
+ are integers.
349
+
350
+ Examples
351
+ ========
352
+
353
+ >>> from sympy import Q, ask, MatrixSymbol
354
+ >>> X = MatrixSymbol('X', 4, 4)
355
+ >>> ask(Q.integer(X[1, 2]), Q.integer_elements(X))
356
+ True
357
+
358
+ """
359
+ name = "integer_elements"
360
+ handler = Dispatcher("IntegerElementsHandler", doc="Handler for key 'integer_elements'.")
361
+
362
+
363
+ class RealElementsPredicate(Predicate):
364
+ """
365
+ Real elements matrix predicate.
366
+
367
+ Explanation
368
+ ===========
369
+
370
+ ``Q.real_elements(x)`` is true iff all the elements of ``x``
371
+ are real numbers.
372
+
373
+ Examples
374
+ ========
375
+
376
+ >>> from sympy import Q, ask, MatrixSymbol
377
+ >>> X = MatrixSymbol('X', 4, 4)
378
+ >>> ask(Q.real(X[1, 2]), Q.real_elements(X))
379
+ True
380
+
381
+ """
382
+ name = "real_elements"
383
+ handler = Dispatcher("RealElementsHandler", doc="Handler for key 'real_elements'.")
384
+
385
+
386
+ class ComplexElementsPredicate(Predicate):
387
+ """
388
+ Complex elements matrix predicate.
389
+
390
+ Explanation
391
+ ===========
392
+
393
+ ``Q.complex_elements(x)`` is true iff all the elements of ``x``
394
+ are complex numbers.
395
+
396
+ Examples
397
+ ========
398
+
399
+ >>> from sympy import Q, ask, MatrixSymbol
400
+ >>> X = MatrixSymbol('X', 4, 4)
401
+ >>> ask(Q.complex(X[1, 2]), Q.complex_elements(X))
402
+ True
403
+ >>> ask(Q.complex_elements(X), Q.integer_elements(X))
404
+ True
405
+
406
+ """
407
+ name = "complex_elements"
408
+ handler = Dispatcher("ComplexElementsHandler", doc="Handler for key 'complex_elements'.")
409
+
410
+
411
+ class SingularPredicate(Predicate):
412
+ """
413
+ Singular matrix predicate.
414
+
415
+ A matrix is singular iff the value of its determinant is 0.
416
+
417
+ Examples
418
+ ========
419
+
420
+ >>> from sympy import Q, ask, MatrixSymbol
421
+ >>> X = MatrixSymbol('X', 4, 4)
422
+ >>> ask(Q.singular(X), Q.invertible(X))
423
+ False
424
+ >>> ask(Q.singular(X), ~Q.invertible(X))
425
+ True
426
+
427
+ References
428
+ ==========
429
+
430
+ .. [1] https://mathworld.wolfram.com/SingularMatrix.html
431
+
432
+ """
433
+ name = "singular"
434
+ handler = Dispatcher("SingularHandler", doc="Predicate fore key 'singular'.")
435
+
436
+
437
+ class NormalPredicate(Predicate):
438
+ """
439
+ Normal matrix predicate.
440
+
441
+ A matrix is normal if it commutes with its conjugate transpose.
442
+
443
+ Examples
444
+ ========
445
+
446
+ >>> from sympy import Q, ask, MatrixSymbol
447
+ >>> X = MatrixSymbol('X', 4, 4)
448
+ >>> ask(Q.normal(X), Q.unitary(X))
449
+ True
450
+
451
+ References
452
+ ==========
453
+
454
+ .. [1] https://en.wikipedia.org/wiki/Normal_matrix
455
+
456
+ """
457
+ name = "normal"
458
+ handler = Dispatcher("NormalHandler", doc="Predicate fore key 'normal'.")
459
+
460
+
461
+ class TriangularPredicate(Predicate):
462
+ """
463
+ Triangular matrix predicate.
464
+
465
+ Explanation
466
+ ===========
467
+
468
+ ``Q.triangular(X)`` is true if ``X`` is one that is either lower
469
+ triangular or upper triangular.
470
+
471
+ Examples
472
+ ========
473
+
474
+ >>> from sympy import Q, ask, MatrixSymbol
475
+ >>> X = MatrixSymbol('X', 4, 4)
476
+ >>> ask(Q.triangular(X), Q.upper_triangular(X))
477
+ True
478
+ >>> ask(Q.triangular(X), Q.lower_triangular(X))
479
+ True
480
+
481
+ References
482
+ ==========
483
+
484
+ .. [1] https://en.wikipedia.org/wiki/Triangular_matrix
485
+
486
+ """
487
+ name = "triangular"
488
+ handler = Dispatcher("TriangularHandler", doc="Predicate fore key 'triangular'.")
489
+
490
+
491
+ class UnitTriangularPredicate(Predicate):
492
+ """
493
+ Unit triangular matrix predicate.
494
+
495
+ Explanation
496
+ ===========
497
+
498
+ A unit triangular matrix is a triangular matrix with 1s
499
+ on the diagonal.
500
+
501
+ Examples
502
+ ========
503
+
504
+ >>> from sympy import Q, ask, MatrixSymbol
505
+ >>> X = MatrixSymbol('X', 4, 4)
506
+ >>> ask(Q.triangular(X), Q.unit_triangular(X))
507
+ True
508
+
509
+ """
510
+ name = "unit_triangular"
511
+ handler = Dispatcher("UnitTriangularHandler", doc="Predicate fore key 'unit_triangular'.")
.venv/lib/python3.13/site-packages/sympy/assumptions/predicates/order.py ADDED
@@ -0,0 +1,390 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.assumptions import Predicate
2
+ from sympy.multipledispatch import Dispatcher
3
+
4
+
5
+ class NegativePredicate(Predicate):
6
+ r"""
7
+ Negative number predicate.
8
+
9
+ Explanation
10
+ ===========
11
+
12
+ ``Q.negative(x)`` is true iff ``x`` is a real number and :math:`x < 0`, that is,
13
+ it is in the interval :math:`(-\infty, 0)`. Note in particular that negative
14
+ infinity is not negative.
15
+
16
+ A few important facts about negative numbers:
17
+
18
+ - Note that ``Q.nonnegative`` and ``~Q.negative`` are *not* the same
19
+ thing. ``~Q.negative(x)`` simply means that ``x`` is not negative,
20
+ whereas ``Q.nonnegative(x)`` means that ``x`` is real and not
21
+ negative, i.e., ``Q.nonnegative(x)`` is logically equivalent to
22
+ ``Q.zero(x) | Q.positive(x)``. So for example, ``~Q.negative(I)`` is
23
+ true, whereas ``Q.nonnegative(I)`` is false.
24
+
25
+ - See the documentation of ``Q.real`` for more information about
26
+ related facts.
27
+
28
+ Examples
29
+ ========
30
+
31
+ >>> from sympy import Q, ask, symbols, I
32
+ >>> x = symbols('x')
33
+ >>> ask(Q.negative(x), Q.real(x) & ~Q.positive(x) & ~Q.zero(x))
34
+ True
35
+ >>> ask(Q.negative(-1))
36
+ True
37
+ >>> ask(Q.nonnegative(I))
38
+ False
39
+ >>> ask(~Q.negative(I))
40
+ True
41
+
42
+ """
43
+ name = 'negative'
44
+ handler = Dispatcher(
45
+ "NegativeHandler",
46
+ doc=("Handler for Q.negative. Test that an expression is strictly less"
47
+ " than zero.")
48
+ )
49
+
50
+
51
+ class NonNegativePredicate(Predicate):
52
+ """
53
+ Nonnegative real number predicate.
54
+
55
+ Explanation
56
+ ===========
57
+
58
+ ``ask(Q.nonnegative(x))`` is true iff ``x`` belongs to the set of
59
+ positive numbers including zero.
60
+
61
+ - Note that ``Q.nonnegative`` and ``~Q.negative`` are *not* the same
62
+ thing. ``~Q.negative(x)`` simply means that ``x`` is not negative,
63
+ whereas ``Q.nonnegative(x)`` means that ``x`` is real and not
64
+ negative, i.e., ``Q.nonnegative(x)`` is logically equivalent to
65
+ ``Q.zero(x) | Q.positive(x)``. So for example, ``~Q.negative(I)`` is
66
+ true, whereas ``Q.nonnegative(I)`` is false.
67
+
68
+ Examples
69
+ ========
70
+
71
+ >>> from sympy import Q, ask, I
72
+ >>> ask(Q.nonnegative(1))
73
+ True
74
+ >>> ask(Q.nonnegative(0))
75
+ True
76
+ >>> ask(Q.nonnegative(-1))
77
+ False
78
+ >>> ask(Q.nonnegative(I))
79
+ False
80
+ >>> ask(Q.nonnegative(-I))
81
+ False
82
+
83
+ """
84
+ name = 'nonnegative'
85
+ handler = Dispatcher(
86
+ "NonNegativeHandler",
87
+ doc=("Handler for Q.nonnegative.")
88
+ )
89
+
90
+
91
+ class NonZeroPredicate(Predicate):
92
+ """
93
+ Nonzero real number predicate.
94
+
95
+ Explanation
96
+ ===========
97
+
98
+ ``ask(Q.nonzero(x))`` is true iff ``x`` is real and ``x`` is not zero. Note in
99
+ particular that ``Q.nonzero(x)`` is false if ``x`` is not real. Use
100
+ ``~Q.zero(x)`` if you want the negation of being zero without any real
101
+ assumptions.
102
+
103
+ A few important facts about nonzero numbers:
104
+
105
+ - ``Q.nonzero`` is logically equivalent to ``Q.positive | Q.negative``.
106
+
107
+ - See the documentation of ``Q.real`` for more information about
108
+ related facts.
109
+
110
+ Examples
111
+ ========
112
+
113
+ >>> from sympy import Q, ask, symbols, I, oo
114
+ >>> x = symbols('x')
115
+ >>> print(ask(Q.nonzero(x), ~Q.zero(x)))
116
+ None
117
+ >>> ask(Q.nonzero(x), Q.positive(x))
118
+ True
119
+ >>> ask(Q.nonzero(x), Q.zero(x))
120
+ False
121
+ >>> ask(Q.nonzero(0))
122
+ False
123
+ >>> ask(Q.nonzero(I))
124
+ False
125
+ >>> ask(~Q.zero(I))
126
+ True
127
+ >>> ask(Q.nonzero(oo))
128
+ False
129
+
130
+ """
131
+ name = 'nonzero'
132
+ handler = Dispatcher(
133
+ "NonZeroHandler",
134
+ doc=("Handler for key 'nonzero'. Test that an expression is not identically"
135
+ " zero.")
136
+ )
137
+
138
+
139
+ class ZeroPredicate(Predicate):
140
+ """
141
+ Zero number predicate.
142
+
143
+ Explanation
144
+ ===========
145
+
146
+ ``ask(Q.zero(x))`` is true iff the value of ``x`` is zero.
147
+
148
+ Examples
149
+ ========
150
+
151
+ >>> from sympy import ask, Q, oo, symbols
152
+ >>> x, y = symbols('x, y')
153
+ >>> ask(Q.zero(0))
154
+ True
155
+ >>> ask(Q.zero(1/oo))
156
+ True
157
+ >>> print(ask(Q.zero(0*oo)))
158
+ None
159
+ >>> ask(Q.zero(1))
160
+ False
161
+ >>> ask(Q.zero(x*y), Q.zero(x) | Q.zero(y))
162
+ True
163
+
164
+ """
165
+ name = 'zero'
166
+ handler = Dispatcher(
167
+ "ZeroHandler",
168
+ doc="Handler for key 'zero'."
169
+ )
170
+
171
+
172
+ class NonPositivePredicate(Predicate):
173
+ """
174
+ Nonpositive real number predicate.
175
+
176
+ Explanation
177
+ ===========
178
+
179
+ ``ask(Q.nonpositive(x))`` is true iff ``x`` belongs to the set of
180
+ negative numbers including zero.
181
+
182
+ - Note that ``Q.nonpositive`` and ``~Q.positive`` are *not* the same
183
+ thing. ``~Q.positive(x)`` simply means that ``x`` is not positive,
184
+ whereas ``Q.nonpositive(x)`` means that ``x`` is real and not
185
+ positive, i.e., ``Q.nonpositive(x)`` is logically equivalent to
186
+ `Q.negative(x) | Q.zero(x)``. So for example, ``~Q.positive(I)`` is
187
+ true, whereas ``Q.nonpositive(I)`` is false.
188
+
189
+ Examples
190
+ ========
191
+
192
+ >>> from sympy import Q, ask, I
193
+
194
+ >>> ask(Q.nonpositive(-1))
195
+ True
196
+ >>> ask(Q.nonpositive(0))
197
+ True
198
+ >>> ask(Q.nonpositive(1))
199
+ False
200
+ >>> ask(Q.nonpositive(I))
201
+ False
202
+ >>> ask(Q.nonpositive(-I))
203
+ False
204
+
205
+ """
206
+ name = 'nonpositive'
207
+ handler = Dispatcher(
208
+ "NonPositiveHandler",
209
+ doc="Handler for key 'nonpositive'."
210
+ )
211
+
212
+
213
+ class PositivePredicate(Predicate):
214
+ r"""
215
+ Positive real number predicate.
216
+
217
+ Explanation
218
+ ===========
219
+
220
+ ``Q.positive(x)`` is true iff ``x`` is real and `x > 0`, that is if ``x``
221
+ is in the interval `(0, \infty)`. In particular, infinity is not
222
+ positive.
223
+
224
+ A few important facts about positive numbers:
225
+
226
+ - Note that ``Q.nonpositive`` and ``~Q.positive`` are *not* the same
227
+ thing. ``~Q.positive(x)`` simply means that ``x`` is not positive,
228
+ whereas ``Q.nonpositive(x)`` means that ``x`` is real and not
229
+ positive, i.e., ``Q.nonpositive(x)`` is logically equivalent to
230
+ `Q.negative(x) | Q.zero(x)``. So for example, ``~Q.positive(I)`` is
231
+ true, whereas ``Q.nonpositive(I)`` is false.
232
+
233
+ - See the documentation of ``Q.real`` for more information about
234
+ related facts.
235
+
236
+ Examples
237
+ ========
238
+
239
+ >>> from sympy import Q, ask, symbols, I
240
+ >>> x = symbols('x')
241
+ >>> ask(Q.positive(x), Q.real(x) & ~Q.negative(x) & ~Q.zero(x))
242
+ True
243
+ >>> ask(Q.positive(1))
244
+ True
245
+ >>> ask(Q.nonpositive(I))
246
+ False
247
+ >>> ask(~Q.positive(I))
248
+ True
249
+
250
+ """
251
+ name = 'positive'
252
+ handler = Dispatcher(
253
+ "PositiveHandler",
254
+ doc=("Handler for key 'positive'. Test that an expression is strictly"
255
+ " greater than zero.")
256
+ )
257
+
258
+
259
+ class ExtendedPositivePredicate(Predicate):
260
+ r"""
261
+ Positive extended real number predicate.
262
+
263
+ Explanation
264
+ ===========
265
+
266
+ ``Q.extended_positive(x)`` is true iff ``x`` is extended real and
267
+ `x > 0`, that is if ``x`` is in the interval `(0, \infty]`.
268
+
269
+ Examples
270
+ ========
271
+
272
+ >>> from sympy import ask, I, oo, Q
273
+ >>> ask(Q.extended_positive(1))
274
+ True
275
+ >>> ask(Q.extended_positive(oo))
276
+ True
277
+ >>> ask(Q.extended_positive(I))
278
+ False
279
+
280
+ """
281
+ name = 'extended_positive'
282
+ handler = Dispatcher("ExtendedPositiveHandler")
283
+
284
+
285
+ class ExtendedNegativePredicate(Predicate):
286
+ r"""
287
+ Negative extended real number predicate.
288
+
289
+ Explanation
290
+ ===========
291
+
292
+ ``Q.extended_negative(x)`` is true iff ``x`` is extended real and
293
+ `x < 0`, that is if ``x`` is in the interval `[-\infty, 0)`.
294
+
295
+ Examples
296
+ ========
297
+
298
+ >>> from sympy import ask, I, oo, Q
299
+ >>> ask(Q.extended_negative(-1))
300
+ True
301
+ >>> ask(Q.extended_negative(-oo))
302
+ True
303
+ >>> ask(Q.extended_negative(-I))
304
+ False
305
+
306
+ """
307
+ name = 'extended_negative'
308
+ handler = Dispatcher("ExtendedNegativeHandler")
309
+
310
+
311
+ class ExtendedNonZeroPredicate(Predicate):
312
+ """
313
+ Nonzero extended real number predicate.
314
+
315
+ Explanation
316
+ ===========
317
+
318
+ ``ask(Q.extended_nonzero(x))`` is true iff ``x`` is extended real and
319
+ ``x`` is not zero.
320
+
321
+ Examples
322
+ ========
323
+
324
+ >>> from sympy import ask, I, oo, Q
325
+ >>> ask(Q.extended_nonzero(-1))
326
+ True
327
+ >>> ask(Q.extended_nonzero(oo))
328
+ True
329
+ >>> ask(Q.extended_nonzero(I))
330
+ False
331
+
332
+ """
333
+ name = 'extended_nonzero'
334
+ handler = Dispatcher("ExtendedNonZeroHandler")
335
+
336
+
337
+ class ExtendedNonPositivePredicate(Predicate):
338
+ """
339
+ Nonpositive extended real number predicate.
340
+
341
+ Explanation
342
+ ===========
343
+
344
+ ``ask(Q.extended_nonpositive(x))`` is true iff ``x`` is extended real and
345
+ ``x`` is not positive.
346
+
347
+ Examples
348
+ ========
349
+
350
+ >>> from sympy import ask, I, oo, Q
351
+ >>> ask(Q.extended_nonpositive(-1))
352
+ True
353
+ >>> ask(Q.extended_nonpositive(oo))
354
+ False
355
+ >>> ask(Q.extended_nonpositive(0))
356
+ True
357
+ >>> ask(Q.extended_nonpositive(I))
358
+ False
359
+
360
+ """
361
+ name = 'extended_nonpositive'
362
+ handler = Dispatcher("ExtendedNonPositiveHandler")
363
+
364
+
365
+ class ExtendedNonNegativePredicate(Predicate):
366
+ """
367
+ Nonnegative extended real number predicate.
368
+
369
+ Explanation
370
+ ===========
371
+
372
+ ``ask(Q.extended_nonnegative(x))`` is true iff ``x`` is extended real and
373
+ ``x`` is not negative.
374
+
375
+ Examples
376
+ ========
377
+
378
+ >>> from sympy import ask, I, oo, Q
379
+ >>> ask(Q.extended_nonnegative(-1))
380
+ False
381
+ >>> ask(Q.extended_nonnegative(oo))
382
+ True
383
+ >>> ask(Q.extended_nonnegative(0))
384
+ True
385
+ >>> ask(Q.extended_nonnegative(I))
386
+ False
387
+
388
+ """
389
+ name = 'extended_nonnegative'
390
+ handler = Dispatcher("ExtendedNonNegativeHandler")
.venv/lib/python3.13/site-packages/sympy/codegen/tests/__init__.py ADDED
File without changes
.venv/lib/python3.13/site-packages/sympy/codegen/tests/test_abstract_nodes.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core.symbol import symbols
2
+ from sympy.codegen.abstract_nodes import List
3
+
4
+
5
+ def test_List():
6
+ l = List(2, 3, 4)
7
+ assert l == List(2, 3, 4)
8
+ assert str(l) == "[2, 3, 4]"
9
+ x, y, z = symbols('x y z')
10
+ l = List(x**2,y**3,z**4)
11
+ # contrary to python's built-in list, we can call e.g. "replace" on List.
12
+ m = l.replace(lambda arg: arg.is_Pow and arg.exp>2, lambda p: p.base-p.exp)
13
+ assert m == [x**2, y-3, z-4]
14
+ hash(m)
.venv/lib/python3.13/site-packages/sympy/codegen/tests/test_algorithms.py ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import tempfile
2
+ from sympy import log, Min, Max, sqrt
3
+ from sympy.core.numbers import Float
4
+ from sympy.core.symbol import Symbol, symbols
5
+ from sympy.functions.elementary.trigonometric import cos
6
+ from sympy.codegen.ast import Assignment, Raise, RuntimeError_, QuotedString
7
+ from sympy.codegen.algorithms import newtons_method, newtons_method_function
8
+ from sympy.codegen.cfunctions import expm1
9
+ from sympy.codegen.fnodes import bind_C
10
+ from sympy.codegen.futils import render_as_module as f_module
11
+ from sympy.codegen.pyutils import render_as_module as py_module
12
+ from sympy.external import import_module
13
+ from sympy.printing.codeprinter import ccode
14
+ from sympy.utilities._compilation import compile_link_import_strings, has_c, has_fortran
15
+ from sympy.utilities._compilation.util import may_xfail
16
+ from sympy.testing.pytest import skip, raises, skip_under_pyodide
17
+
18
+ cython = import_module('cython')
19
+ wurlitzer = import_module('wurlitzer')
20
+
21
+ def test_newtons_method():
22
+ x, dx, atol = symbols('x dx atol')
23
+ expr = cos(x) - x**3
24
+ algo = newtons_method(expr, x, atol, dx)
25
+ assert algo.has(Assignment(dx, -expr/expr.diff(x)))
26
+
27
+
28
+ @may_xfail
29
+ def test_newtons_method_function__ccode():
30
+ x = Symbol('x', real=True)
31
+ expr = cos(x) - x**3
32
+ func = newtons_method_function(expr, x)
33
+
34
+ if not cython:
35
+ skip("cython not installed.")
36
+ if not has_c():
37
+ skip("No C compiler found.")
38
+
39
+ compile_kw = {"std": 'c99'}
40
+ with tempfile.TemporaryDirectory() as folder:
41
+ mod, info = compile_link_import_strings([
42
+ ('newton.c', ('#include <math.h>\n'
43
+ '#include <stdio.h>\n') + ccode(func)),
44
+ ('_newton.pyx', ("#cython: language_level={}\n".format("3") +
45
+ "cdef extern double newton(double)\n"
46
+ "def py_newton(x):\n"
47
+ " return newton(x)\n"))
48
+ ], build_dir=folder, compile_kwargs=compile_kw)
49
+ assert abs(mod.py_newton(0.5) - 0.865474033102) < 1e-12
50
+
51
+
52
+ @may_xfail
53
+ def test_newtons_method_function__fcode():
54
+ x = Symbol('x', real=True)
55
+ expr = cos(x) - x**3
56
+ func = newtons_method_function(expr, x, attrs=[bind_C(name='newton')])
57
+
58
+ if not cython:
59
+ skip("cython not installed.")
60
+ if not has_fortran():
61
+ skip("No Fortran compiler found.")
62
+
63
+ f_mod = f_module([func], 'mod_newton')
64
+ with tempfile.TemporaryDirectory() as folder:
65
+ mod, info = compile_link_import_strings([
66
+ ('newton.f90', f_mod),
67
+ ('_newton.pyx', ("#cython: language_level={}\n".format("3") +
68
+ "cdef extern double newton(double*)\n"
69
+ "def py_newton(double x):\n"
70
+ " return newton(&x)\n"))
71
+ ], build_dir=folder)
72
+ assert abs(mod.py_newton(0.5) - 0.865474033102) < 1e-12
73
+
74
+
75
+ def test_newtons_method_function__pycode():
76
+ x = Symbol('x', real=True)
77
+ expr = cos(x) - x**3
78
+ func = newtons_method_function(expr, x)
79
+ py_mod = py_module(func)
80
+ namespace = {}
81
+ exec(py_mod, namespace, namespace)
82
+ res = eval('newton(0.5)', namespace)
83
+ assert abs(res - 0.865474033102) < 1e-12
84
+
85
+
86
+ @may_xfail
87
+ @skip_under_pyodide("Emscripten does not support process spawning")
88
+ def test_newtons_method_function__ccode_parameters():
89
+ args = x, A, k, p = symbols('x A k p')
90
+ expr = A*cos(k*x) - p*x**3
91
+ raises(ValueError, lambda: newtons_method_function(expr, x))
92
+ use_wurlitzer = wurlitzer
93
+
94
+ func = newtons_method_function(expr, x, args, debug=use_wurlitzer)
95
+
96
+ if not has_c():
97
+ skip("No C compiler found.")
98
+ if not cython:
99
+ skip("cython not installed.")
100
+
101
+ compile_kw = {"std": 'c99'}
102
+ with tempfile.TemporaryDirectory() as folder:
103
+ mod, info = compile_link_import_strings([
104
+ ('newton_par.c', ('#include <math.h>\n'
105
+ '#include <stdio.h>\n') + ccode(func)),
106
+ ('_newton_par.pyx', ("#cython: language_level={}\n".format("3") +
107
+ "cdef extern double newton(double, double, double, double)\n"
108
+ "def py_newton(x, A=1, k=1, p=1):\n"
109
+ " return newton(x, A, k, p)\n"))
110
+ ], compile_kwargs=compile_kw, build_dir=folder)
111
+
112
+ if use_wurlitzer:
113
+ with wurlitzer.pipes() as (out, err):
114
+ result = mod.py_newton(0.5)
115
+ else:
116
+ result = mod.py_newton(0.5)
117
+
118
+ assert abs(result - 0.865474033102) < 1e-12
119
+
120
+ if not use_wurlitzer:
121
+ skip("C-level output only tested when package 'wurlitzer' is available.")
122
+
123
+ out, err = out.read(), err.read()
124
+ assert err == ''
125
+ assert out == """\
126
+ x= 0.5
127
+ x= 1.1121 d_x= 0.61214
128
+ x= 0.90967 d_x= -0.20247
129
+ x= 0.86726 d_x= -0.042409
130
+ x= 0.86548 d_x= -0.0017867
131
+ x= 0.86547 d_x= -3.1022e-06
132
+ x= 0.86547 d_x= -9.3421e-12
133
+ x= 0.86547 d_x= 3.6902e-17
134
+ """ # try to run tests with LC_ALL=C if this assertion fails
135
+
136
+
137
+ def test_newtons_method_function__rtol_cse_nan():
138
+ a, b, c, N_geo, N_tot = symbols('a b c N_geo N_tot', real=True, nonnegative=True)
139
+ i = Symbol('i', integer=True, nonnegative=True)
140
+ N_ari = N_tot - N_geo - 1
141
+ delta_ari = (c-b)/N_ari
142
+ ln_delta_geo = log(b) + log(-expm1((log(a)-log(b))/N_geo))
143
+ eqb_log = ln_delta_geo - log(delta_ari)
144
+
145
+ def _clamp(low, expr, high):
146
+ return Min(Max(low, expr), high)
147
+
148
+ meth_kw = {
149
+ 'clamped_newton': {'delta_fn': lambda e, x: _clamp(
150
+ (sqrt(a*x)-x)*0.99,
151
+ -e/e.diff(x),
152
+ (sqrt(c*x)-x)*0.99
153
+ )},
154
+ 'halley': {'delta_fn': lambda e, x: (-2*(e*e.diff(x))/(2*e.diff(x)**2 - e*e.diff(x, 2)))},
155
+ 'halley_alt': {'delta_fn': lambda e, x: (-e/e.diff(x)/(1-e/e.diff(x)*e.diff(x,2)/2/e.diff(x)))},
156
+ }
157
+ args = eqb_log, b
158
+ for use_cse in [False, True]:
159
+ kwargs = {
160
+ 'params': (b, a, c, N_geo, N_tot), 'itermax': 60, 'debug': True, 'cse': use_cse,
161
+ 'counter': i, 'atol': 1e-100, 'rtol': 2e-16, 'bounds': (a,c),
162
+ 'handle_nan': Raise(RuntimeError_(QuotedString("encountered NaN.")))
163
+ }
164
+ func = {k: newtons_method_function(*args, func_name=f"{k}_b", **dict(kwargs, **kw)) for k, kw in meth_kw.items()}
165
+ py_mod = {k: py_module(v) for k, v in func.items()}
166
+ namespace = {}
167
+ root_find_b = {}
168
+ for k, v in py_mod.items():
169
+ ns = namespace[k] = {}
170
+ exec(v, ns, ns)
171
+ root_find_b[k] = ns[f'{k}_b']
172
+ ref = Float('13.2261515064168768938151923226496')
173
+ reftol = {'clamped_newton': 2e-16, 'halley': 2e-16, 'halley_alt': 3e-16}
174
+ guess = 4.0
175
+ for meth, func in root_find_b.items():
176
+ result = func(guess, 1e-2, 1e2, 50, 100)
177
+ req = ref*reftol[meth]
178
+ if use_cse:
179
+ req *= 2
180
+ assert abs(result - ref) < req
.venv/lib/python3.13/site-packages/sympy/codegen/tests/test_applications.py ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # This file contains tests that exercise multiple AST nodes
2
+
3
+ import tempfile
4
+
5
+ from sympy.external import import_module
6
+ from sympy.printing.codeprinter import ccode
7
+ from sympy.utilities._compilation import compile_link_import_strings, has_c
8
+ from sympy.utilities._compilation.util import may_xfail
9
+ from sympy.testing.pytest import skip, skip_under_pyodide
10
+ from sympy.codegen.ast import (
11
+ FunctionDefinition, FunctionPrototype, Variable, Pointer, real, Assignment,
12
+ integer, CodeBlock, While
13
+ )
14
+ from sympy.codegen.cnodes import void, PreIncrement
15
+ from sympy.codegen.cutils import render_as_source_file
16
+
17
+ cython = import_module('cython')
18
+ np = import_module('numpy')
19
+
20
+ def _mk_func1():
21
+ declars = n, inp, out = Variable('n', integer), Pointer('inp', real), Pointer('out', real)
22
+ i = Variable('i', integer)
23
+ whl = While(i<n, [Assignment(out[i], inp[i]), PreIncrement(i)])
24
+ body = CodeBlock(i.as_Declaration(value=0), whl)
25
+ return FunctionDefinition(void, 'our_test_function', declars, body)
26
+
27
+
28
+ def _render_compile_import(funcdef, build_dir):
29
+ code_str = render_as_source_file(funcdef, settings={"contract": False})
30
+ declar = ccode(FunctionPrototype.from_FunctionDefinition(funcdef))
31
+ return compile_link_import_strings([
32
+ ('our_test_func.c', code_str),
33
+ ('_our_test_func.pyx', ("#cython: language_level={}\n".format("3") +
34
+ "cdef extern {declar}\n"
35
+ "def _{fname}({typ}[:] inp, {typ}[:] out):\n"
36
+ " {fname}(inp.size, &inp[0], &out[0])").format(
37
+ declar=declar, fname=funcdef.name, typ='double'
38
+ ))
39
+ ], build_dir=build_dir)
40
+
41
+
42
+ @may_xfail
43
+ @skip_under_pyodide("Emscripten does not support process spawning")
44
+ def test_copying_function():
45
+ if not np:
46
+ skip("numpy not installed.")
47
+ if not has_c():
48
+ skip("No C compiler found.")
49
+ if not cython:
50
+ skip("Cython not found.")
51
+
52
+ info = None
53
+ with tempfile.TemporaryDirectory() as folder:
54
+ mod, info = _render_compile_import(_mk_func1(), build_dir=folder)
55
+ inp = np.arange(10.0)
56
+ out = np.empty_like(inp)
57
+ mod._our_test_function(inp, out)
58
+ assert np.allclose(inp, out)
.venv/lib/python3.13/site-packages/sympy/codegen/tests/test_approximations.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ from sympy.core.symbol import symbols
3
+ from sympy.functions.elementary.exponential import exp
4
+ from sympy.codegen.rewriting import optimize
5
+ from sympy.codegen.approximations import SumApprox, SeriesApprox
6
+
7
+
8
+ def test_SumApprox_trivial():
9
+ x = symbols('x')
10
+ expr1 = 1 + x
11
+ sum_approx = SumApprox(bounds={x: (-1e-20, 1e-20)}, reltol=1e-16)
12
+ apx1 = optimize(expr1, [sum_approx])
13
+ assert apx1 - 1 == 0
14
+
15
+
16
+ def test_SumApprox_monotone_terms():
17
+ x, y, z = symbols('x y z')
18
+ expr1 = exp(z)*(x**2 + y**2 + 1)
19
+ bnds1 = {x: (0, 1e-3), y: (100, 1000)}
20
+ sum_approx_m2 = SumApprox(bounds=bnds1, reltol=1e-2)
21
+ sum_approx_m5 = SumApprox(bounds=bnds1, reltol=1e-5)
22
+ sum_approx_m11 = SumApprox(bounds=bnds1, reltol=1e-11)
23
+ assert (optimize(expr1, [sum_approx_m2])/exp(z) - (y**2)).simplify() == 0
24
+ assert (optimize(expr1, [sum_approx_m5])/exp(z) - (y**2 + 1)).simplify() == 0
25
+ assert (optimize(expr1, [sum_approx_m11])/exp(z) - (y**2 + 1 + x**2)).simplify() == 0
26
+
27
+
28
+ def test_SeriesApprox_trivial():
29
+ x, z = symbols('x z')
30
+ for factor in [1, exp(z)]:
31
+ x = symbols('x')
32
+ expr1 = exp(x)*factor
33
+ bnds1 = {x: (-1, 1)}
34
+ series_approx_50 = SeriesApprox(bounds=bnds1, reltol=0.50)
35
+ series_approx_10 = SeriesApprox(bounds=bnds1, reltol=0.10)
36
+ series_approx_05 = SeriesApprox(bounds=bnds1, reltol=0.05)
37
+ c = (bnds1[x][1] + bnds1[x][0])/2 # 0.0
38
+ f0 = math.exp(c) # 1.0
39
+
40
+ ref_50 = f0 + x + x**2/2
41
+ ref_10 = f0 + x + x**2/2 + x**3/6
42
+ ref_05 = f0 + x + x**2/2 + x**3/6 + x**4/24
43
+
44
+ res_50 = optimize(expr1, [series_approx_50])
45
+ res_10 = optimize(expr1, [series_approx_10])
46
+ res_05 = optimize(expr1, [series_approx_05])
47
+
48
+ assert (res_50/factor - ref_50).simplify() == 0
49
+ assert (res_10/factor - ref_10).simplify() == 0
50
+ assert (res_05/factor - ref_05).simplify() == 0
51
+
52
+ max_ord3 = SeriesApprox(bounds=bnds1, reltol=0.05, max_order=3)
53
+ assert optimize(expr1, [max_ord3]) == expr1
.venv/lib/python3.13/site-packages/sympy/codegen/tests/test_ast.py ADDED
@@ -0,0 +1,661 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import math
2
+ from sympy.core.containers import Tuple
3
+ from sympy.core.numbers import nan, oo, Float, Integer
4
+ from sympy.core.relational import Lt
5
+ from sympy.core.symbol import symbols, Symbol
6
+ from sympy.functions.elementary.trigonometric import sin
7
+ from sympy.matrices.dense import Matrix
8
+ from sympy.matrices.expressions.matexpr import MatrixSymbol
9
+ from sympy.sets.fancysets import Range
10
+ from sympy.tensor.indexed import Idx, IndexedBase
11
+ from sympy.testing.pytest import raises
12
+
13
+
14
+ from sympy.codegen.ast import (
15
+ Assignment, Attribute, aug_assign, CodeBlock, For, Type, Variable, Pointer, Declaration,
16
+ AddAugmentedAssignment, SubAugmentedAssignment, MulAugmentedAssignment,
17
+ DivAugmentedAssignment, ModAugmentedAssignment, value_const, pointer_const,
18
+ integer, real, complex_, int8, uint8, float16 as f16, float32 as f32,
19
+ float64 as f64, float80 as f80, float128 as f128, complex64 as c64, complex128 as c128,
20
+ While, Scope, String, Print, QuotedString, FunctionPrototype, FunctionDefinition, Return,
21
+ FunctionCall, untyped, IntBaseType, intc, Node, none, NoneToken, Token, Comment
22
+ )
23
+
24
+ x, y, z, t, x0, x1, x2, a, b = symbols("x, y, z, t, x0, x1, x2, a, b")
25
+ n = symbols("n", integer=True)
26
+ A = MatrixSymbol('A', 3, 1)
27
+ mat = Matrix([1, 2, 3])
28
+ B = IndexedBase('B')
29
+ i = Idx("i", n)
30
+ A22 = MatrixSymbol('A22',2,2)
31
+ B22 = MatrixSymbol('B22',2,2)
32
+
33
+
34
+ def test_Assignment():
35
+ # Here we just do things to show they don't error
36
+ Assignment(x, y)
37
+ Assignment(x, 0)
38
+ Assignment(A, mat)
39
+ Assignment(A[1,0], 0)
40
+ Assignment(A[1,0], x)
41
+ Assignment(B[i], x)
42
+ Assignment(B[i], 0)
43
+ a = Assignment(x, y)
44
+ assert a.func(*a.args) == a
45
+ assert a.op == ':='
46
+ # Here we test things to show that they error
47
+ # Matrix to scalar
48
+ raises(ValueError, lambda: Assignment(B[i], A))
49
+ raises(ValueError, lambda: Assignment(B[i], mat))
50
+ raises(ValueError, lambda: Assignment(x, mat))
51
+ raises(ValueError, lambda: Assignment(x, A))
52
+ raises(ValueError, lambda: Assignment(A[1,0], mat))
53
+ # Scalar to matrix
54
+ raises(ValueError, lambda: Assignment(A, x))
55
+ raises(ValueError, lambda: Assignment(A, 0))
56
+ # Non-atomic lhs
57
+ raises(TypeError, lambda: Assignment(mat, A))
58
+ raises(TypeError, lambda: Assignment(0, x))
59
+ raises(TypeError, lambda: Assignment(x*x, 1))
60
+ raises(TypeError, lambda: Assignment(A + A, mat))
61
+ raises(TypeError, lambda: Assignment(B, 0))
62
+
63
+
64
+ def test_AugAssign():
65
+ # Here we just do things to show they don't error
66
+ aug_assign(x, '+', y)
67
+ aug_assign(x, '+', 0)
68
+ aug_assign(A, '+', mat)
69
+ aug_assign(A[1, 0], '+', 0)
70
+ aug_assign(A[1, 0], '+', x)
71
+ aug_assign(B[i], '+', x)
72
+ aug_assign(B[i], '+', 0)
73
+
74
+ # Check creation via aug_assign vs constructor
75
+ for binop, cls in [
76
+ ('+', AddAugmentedAssignment),
77
+ ('-', SubAugmentedAssignment),
78
+ ('*', MulAugmentedAssignment),
79
+ ('/', DivAugmentedAssignment),
80
+ ('%', ModAugmentedAssignment),
81
+ ]:
82
+ a = aug_assign(x, binop, y)
83
+ b = cls(x, y)
84
+ assert a.func(*a.args) == a == b
85
+ assert a.binop == binop
86
+ assert a.op == binop + '='
87
+
88
+ # Here we test things to show that they error
89
+ # Matrix to scalar
90
+ raises(ValueError, lambda: aug_assign(B[i], '+', A))
91
+ raises(ValueError, lambda: aug_assign(B[i], '+', mat))
92
+ raises(ValueError, lambda: aug_assign(x, '+', mat))
93
+ raises(ValueError, lambda: aug_assign(x, '+', A))
94
+ raises(ValueError, lambda: aug_assign(A[1, 0], '+', mat))
95
+ # Scalar to matrix
96
+ raises(ValueError, lambda: aug_assign(A, '+', x))
97
+ raises(ValueError, lambda: aug_assign(A, '+', 0))
98
+ # Non-atomic lhs
99
+ raises(TypeError, lambda: aug_assign(mat, '+', A))
100
+ raises(TypeError, lambda: aug_assign(0, '+', x))
101
+ raises(TypeError, lambda: aug_assign(x * x, '+', 1))
102
+ raises(TypeError, lambda: aug_assign(A + A, '+', mat))
103
+ raises(TypeError, lambda: aug_assign(B, '+', 0))
104
+
105
+
106
+ def test_Assignment_printing():
107
+ assignment_classes = [
108
+ Assignment,
109
+ AddAugmentedAssignment,
110
+ SubAugmentedAssignment,
111
+ MulAugmentedAssignment,
112
+ DivAugmentedAssignment,
113
+ ModAugmentedAssignment,
114
+ ]
115
+ pairs = [
116
+ (x, 2 * y + 2),
117
+ (B[i], x),
118
+ (A22, B22),
119
+ (A[0, 0], x),
120
+ ]
121
+
122
+ for cls in assignment_classes:
123
+ for lhs, rhs in pairs:
124
+ a = cls(lhs, rhs)
125
+ assert repr(a) == '%s(%s, %s)' % (cls.__name__, repr(lhs), repr(rhs))
126
+
127
+
128
+ def test_CodeBlock():
129
+ c = CodeBlock(Assignment(x, 1), Assignment(y, x + 1))
130
+ assert c.func(*c.args) == c
131
+
132
+ assert c.left_hand_sides == Tuple(x, y)
133
+ assert c.right_hand_sides == Tuple(1, x + 1)
134
+
135
+ def test_CodeBlock_topological_sort():
136
+ assignments = [
137
+ Assignment(x, y + z),
138
+ Assignment(z, 1),
139
+ Assignment(t, x),
140
+ Assignment(y, 2),
141
+ ]
142
+
143
+ ordered_assignments = [
144
+ # Note that the unrelated z=1 and y=2 are kept in that order
145
+ Assignment(z, 1),
146
+ Assignment(y, 2),
147
+ Assignment(x, y + z),
148
+ Assignment(t, x),
149
+ ]
150
+ c1 = CodeBlock.topological_sort(assignments)
151
+ assert c1 == CodeBlock(*ordered_assignments)
152
+
153
+ # Cycle
154
+ invalid_assignments = [
155
+ Assignment(x, y + z),
156
+ Assignment(z, 1),
157
+ Assignment(y, x),
158
+ Assignment(y, 2),
159
+ ]
160
+
161
+ raises(ValueError, lambda: CodeBlock.topological_sort(invalid_assignments))
162
+
163
+ # Free symbols
164
+ free_assignments = [
165
+ Assignment(x, y + z),
166
+ Assignment(z, a * b),
167
+ Assignment(t, x),
168
+ Assignment(y, b + 3),
169
+ ]
170
+
171
+ free_assignments_ordered = [
172
+ Assignment(z, a * b),
173
+ Assignment(y, b + 3),
174
+ Assignment(x, y + z),
175
+ Assignment(t, x),
176
+ ]
177
+
178
+ c2 = CodeBlock.topological_sort(free_assignments)
179
+ assert c2 == CodeBlock(*free_assignments_ordered)
180
+
181
+ def test_CodeBlock_free_symbols():
182
+ c1 = CodeBlock(
183
+ Assignment(x, y + z),
184
+ Assignment(z, 1),
185
+ Assignment(t, x),
186
+ Assignment(y, 2),
187
+ )
188
+ assert c1.free_symbols == set()
189
+
190
+ c2 = CodeBlock(
191
+ Assignment(x, y + z),
192
+ Assignment(z, a * b),
193
+ Assignment(t, x),
194
+ Assignment(y, b + 3),
195
+ )
196
+ assert c2.free_symbols == {a, b}
197
+
198
+ def test_CodeBlock_cse():
199
+ c1 = CodeBlock(
200
+ Assignment(y, 1),
201
+ Assignment(x, sin(y)),
202
+ Assignment(z, sin(y)),
203
+ Assignment(t, x*z),
204
+ )
205
+ assert c1.cse() == CodeBlock(
206
+ Assignment(y, 1),
207
+ Assignment(x0, sin(y)),
208
+ Assignment(x, x0),
209
+ Assignment(z, x0),
210
+ Assignment(t, x*z),
211
+ )
212
+
213
+ # Multiple assignments to same symbol not supported
214
+ raises(NotImplementedError, lambda: CodeBlock(
215
+ Assignment(x, 1),
216
+ Assignment(y, 1), Assignment(y, 2)
217
+ ).cse())
218
+
219
+ # Check auto-generated symbols do not collide with existing ones
220
+ c2 = CodeBlock(
221
+ Assignment(x0, sin(y) + 1),
222
+ Assignment(x1, 2 * sin(y)),
223
+ Assignment(z, x * y),
224
+ )
225
+ assert c2.cse() == CodeBlock(
226
+ Assignment(x2, sin(y)),
227
+ Assignment(x0, x2 + 1),
228
+ Assignment(x1, 2 * x2),
229
+ Assignment(z, x * y),
230
+ )
231
+
232
+
233
+ def test_CodeBlock_cse__issue_14118():
234
+ # see https://github.com/sympy/sympy/issues/14118
235
+ c = CodeBlock(
236
+ Assignment(A22, Matrix([[x, sin(y)],[3, 4]])),
237
+ Assignment(B22, Matrix([[sin(y), 2*sin(y)], [sin(y)**2, 7]]))
238
+ )
239
+ assert c.cse() == CodeBlock(
240
+ Assignment(x0, sin(y)),
241
+ Assignment(A22, Matrix([[x, x0],[3, 4]])),
242
+ Assignment(B22, Matrix([[x0, 2*x0], [x0**2, 7]]))
243
+ )
244
+
245
+ def test_For():
246
+ f = For(n, Range(0, 3), (Assignment(A[n, 0], x + n), aug_assign(x, '+', y)))
247
+ f = For(n, (1, 2, 3, 4, 5), (Assignment(A[n, 0], x + n),))
248
+ assert f.func(*f.args) == f
249
+ raises(TypeError, lambda: For(n, x, (x + y,)))
250
+
251
+
252
+ def test_none():
253
+ assert none.is_Atom
254
+ assert none == none
255
+ class Foo(Token):
256
+ pass
257
+ foo = Foo()
258
+ assert foo != none
259
+ assert none == None
260
+ assert none == NoneToken()
261
+ assert none.func(*none.args) == none
262
+
263
+
264
+ def test_String():
265
+ st = String('foobar')
266
+ assert st.is_Atom
267
+ assert st == String('foobar')
268
+ assert st.text == 'foobar'
269
+ assert st.func(**st.kwargs()) == st
270
+ assert st.func(*st.args) == st
271
+
272
+
273
+ class Signifier(String):
274
+ pass
275
+
276
+ si = Signifier('foobar')
277
+ assert si != st
278
+ assert si.text == st.text
279
+ s = String('foo')
280
+ assert str(s) == 'foo'
281
+ assert repr(s) == "String('foo')"
282
+
283
+ def test_Comment():
284
+ c = Comment('foobar')
285
+ assert c.text == 'foobar'
286
+ assert str(c) == 'foobar'
287
+
288
+ def test_Node():
289
+ n = Node()
290
+ assert n == Node()
291
+ assert n.func(*n.args) == n
292
+
293
+
294
+ def test_Type():
295
+ t = Type('MyType')
296
+ assert len(t.args) == 1
297
+ assert t.name == String('MyType')
298
+ assert str(t) == 'MyType'
299
+ assert repr(t) == "Type(String('MyType'))"
300
+ assert Type(t) == t
301
+ assert t.func(*t.args) == t
302
+ t1 = Type('t1')
303
+ t2 = Type('t2')
304
+ assert t1 != t2
305
+ assert t1 == t1 and t2 == t2
306
+ t1b = Type('t1')
307
+ assert t1 == t1b
308
+ assert t2 != t1b
309
+
310
+
311
+ def test_Type__from_expr():
312
+ assert Type.from_expr(i) == integer
313
+ u = symbols('u', real=True)
314
+ assert Type.from_expr(u) == real
315
+ assert Type.from_expr(n) == integer
316
+ assert Type.from_expr(3) == integer
317
+ assert Type.from_expr(3.0) == real
318
+ assert Type.from_expr(3+1j) == complex_
319
+ raises(ValueError, lambda: Type.from_expr(sum))
320
+
321
+
322
+ def test_Type__cast_check__integers():
323
+ # Rounding
324
+ raises(ValueError, lambda: integer.cast_check(3.5))
325
+ assert integer.cast_check('3') == 3
326
+ assert integer.cast_check(Float('3.0000000000000000000')) == 3
327
+ assert integer.cast_check(Float('3.0000000000000000001')) == 3 # unintuitive maybe?
328
+
329
+ # Range
330
+ assert int8.cast_check(127.0) == 127
331
+ raises(ValueError, lambda: int8.cast_check(128))
332
+ assert int8.cast_check(-128) == -128
333
+ raises(ValueError, lambda: int8.cast_check(-129))
334
+
335
+ assert uint8.cast_check(0) == 0
336
+ assert uint8.cast_check(128) == 128
337
+ raises(ValueError, lambda: uint8.cast_check(256.0))
338
+ raises(ValueError, lambda: uint8.cast_check(-1))
339
+
340
+ def test_Attribute():
341
+ noexcept = Attribute('noexcept')
342
+ assert noexcept == Attribute('noexcept')
343
+ alignas16 = Attribute('alignas', [16])
344
+ alignas32 = Attribute('alignas', [32])
345
+ assert alignas16 != alignas32
346
+ assert alignas16.func(*alignas16.args) == alignas16
347
+
348
+
349
+ def test_Variable():
350
+ v = Variable(x, type=real)
351
+ assert v == Variable(v)
352
+ assert v == Variable('x', type=real)
353
+ assert v.symbol == x
354
+ assert v.type == real
355
+ assert value_const not in v.attrs
356
+ assert v.func(*v.args) == v
357
+ assert str(v) == 'Variable(x, type=real)'
358
+
359
+ w = Variable(y, f32, attrs={value_const})
360
+ assert w.symbol == y
361
+ assert w.type == f32
362
+ assert value_const in w.attrs
363
+ assert w.func(*w.args) == w
364
+
365
+ v_n = Variable(n, type=Type.from_expr(n))
366
+ assert v_n.type == integer
367
+ assert v_n.func(*v_n.args) == v_n
368
+ v_i = Variable(i, type=Type.from_expr(n))
369
+ assert v_i.type == integer
370
+ assert v_i != v_n
371
+
372
+ a_i = Variable.deduced(i)
373
+ assert a_i.type == integer
374
+ assert Variable.deduced(Symbol('x', real=True)).type == real
375
+ assert a_i.func(*a_i.args) == a_i
376
+
377
+ v_n2 = Variable.deduced(n, value=3.5, cast_check=False)
378
+ assert v_n2.func(*v_n2.args) == v_n2
379
+ assert abs(v_n2.value - 3.5) < 1e-15
380
+ raises(ValueError, lambda: Variable.deduced(n, value=3.5, cast_check=True))
381
+
382
+ v_n3 = Variable.deduced(n)
383
+ assert v_n3.type == integer
384
+ assert str(v_n3) == 'Variable(n, type=integer)'
385
+ assert Variable.deduced(z, value=3).type == integer
386
+ assert Variable.deduced(z, value=3.0).type == real
387
+ assert Variable.deduced(z, value=3.0+1j).type == complex_
388
+
389
+
390
+ def test_Pointer():
391
+ p = Pointer(x)
392
+ assert p.symbol == x
393
+ assert p.type == untyped
394
+ assert value_const not in p.attrs
395
+ assert pointer_const not in p.attrs
396
+ assert p.func(*p.args) == p
397
+
398
+ u = symbols('u', real=True)
399
+ pu = Pointer(u, type=Type.from_expr(u), attrs={value_const, pointer_const})
400
+ assert pu.symbol is u
401
+ assert pu.type == real
402
+ assert value_const in pu.attrs
403
+ assert pointer_const in pu.attrs
404
+ assert pu.func(*pu.args) == pu
405
+
406
+ i = symbols('i', integer=True)
407
+ deref = pu[i]
408
+ assert deref.indices == (i,)
409
+
410
+
411
+ def test_Declaration():
412
+ u = symbols('u', real=True)
413
+ vu = Variable(u, type=Type.from_expr(u))
414
+ assert Declaration(vu).variable.type == real
415
+ vn = Variable(n, type=Type.from_expr(n))
416
+ assert Declaration(vn).variable.type == integer
417
+
418
+ # PR 19107, does not allow comparison between expressions and Basic
419
+ # lt = StrictLessThan(vu, vn)
420
+ # assert isinstance(lt, StrictLessThan)
421
+
422
+ vuc = Variable(u, Type.from_expr(u), value=3.0, attrs={value_const})
423
+ assert value_const in vuc.attrs
424
+ assert pointer_const not in vuc.attrs
425
+ decl = Declaration(vuc)
426
+ assert decl.variable == vuc
427
+ assert isinstance(decl.variable.value, Float)
428
+ assert decl.variable.value == 3.0
429
+ assert decl.func(*decl.args) == decl
430
+ assert vuc.as_Declaration() == decl
431
+ assert vuc.as_Declaration(value=None, attrs=None) == Declaration(vu)
432
+
433
+ vy = Variable(y, type=integer, value=3)
434
+ decl2 = Declaration(vy)
435
+ assert decl2.variable == vy
436
+ assert decl2.variable.value == Integer(3)
437
+
438
+ vi = Variable(i, type=Type.from_expr(i), value=3.0)
439
+ decl3 = Declaration(vi)
440
+ assert decl3.variable.type == integer
441
+ assert decl3.variable.value == 3.0
442
+
443
+ raises(ValueError, lambda: Declaration(vi, 42))
444
+
445
+
446
+ def test_IntBaseType():
447
+ assert intc.name == String('intc')
448
+ assert intc.args == (intc.name,)
449
+ assert str(IntBaseType('a').name) == 'a'
450
+
451
+
452
+ def test_FloatType():
453
+ assert f16.dig == 3
454
+ assert f32.dig == 6
455
+ assert f64.dig == 15
456
+ assert f80.dig == 18
457
+ assert f128.dig == 33
458
+
459
+ assert f16.decimal_dig == 5
460
+ assert f32.decimal_dig == 9
461
+ assert f64.decimal_dig == 17
462
+ assert f80.decimal_dig == 21
463
+ assert f128.decimal_dig == 36
464
+
465
+ assert f16.max_exponent == 16
466
+ assert f32.max_exponent == 128
467
+ assert f64.max_exponent == 1024
468
+ assert f80.max_exponent == 16384
469
+ assert f128.max_exponent == 16384
470
+
471
+ assert f16.min_exponent == -13
472
+ assert f32.min_exponent == -125
473
+ assert f64.min_exponent == -1021
474
+ assert f80.min_exponent == -16381
475
+ assert f128.min_exponent == -16381
476
+
477
+ assert abs(f16.eps / Float('0.00097656', precision=16) - 1) < 0.1*10**-f16.dig
478
+ assert abs(f32.eps / Float('1.1920929e-07', precision=32) - 1) < 0.1*10**-f32.dig
479
+ assert abs(f64.eps / Float('2.2204460492503131e-16', precision=64) - 1) < 0.1*10**-f64.dig
480
+ assert abs(f80.eps / Float('1.08420217248550443401e-19', precision=80) - 1) < 0.1*10**-f80.dig
481
+ assert abs(f128.eps / Float(' 1.92592994438723585305597794258492732e-34', precision=128) - 1) < 0.1*10**-f128.dig
482
+
483
+ assert abs(f16.max / Float('65504', precision=16) - 1) < .1*10**-f16.dig
484
+ assert abs(f32.max / Float('3.40282347e+38', precision=32) - 1) < 0.1*10**-f32.dig
485
+ assert abs(f64.max / Float('1.79769313486231571e+308', precision=64) - 1) < 0.1*10**-f64.dig # cf. np.finfo(np.float64).max
486
+ assert abs(f80.max / Float('1.18973149535723176502e+4932', precision=80) - 1) < 0.1*10**-f80.dig
487
+ assert abs(f128.max / Float('1.18973149535723176508575932662800702e+4932', precision=128) - 1) < 0.1*10**-f128.dig
488
+
489
+ # cf. np.finfo(np.float32).tiny
490
+ assert abs(f16.tiny / Float('6.1035e-05', precision=16) - 1) < 0.1*10**-f16.dig
491
+ assert abs(f32.tiny / Float('1.17549435e-38', precision=32) - 1) < 0.1*10**-f32.dig
492
+ assert abs(f64.tiny / Float('2.22507385850720138e-308', precision=64) - 1) < 0.1*10**-f64.dig
493
+ assert abs(f80.tiny / Float('3.36210314311209350626e-4932', precision=80) - 1) < 0.1*10**-f80.dig
494
+ assert abs(f128.tiny / Float('3.3621031431120935062626778173217526e-4932', precision=128) - 1) < 0.1*10**-f128.dig
495
+
496
+ assert f64.cast_check(0.5) == Float(0.5, 17)
497
+ assert abs(f64.cast_check(3.7) - 3.7) < 3e-17
498
+ assert isinstance(f64.cast_check(3), (Float, float))
499
+
500
+ assert f64.cast_nocheck(oo) == float('inf')
501
+ assert f64.cast_nocheck(-oo) == float('-inf')
502
+ assert f64.cast_nocheck(float(oo)) == float('inf')
503
+ assert f64.cast_nocheck(float(-oo)) == float('-inf')
504
+ assert math.isnan(f64.cast_nocheck(nan))
505
+
506
+ assert f32 != f64
507
+ assert f64 == f64.func(*f64.args)
508
+
509
+
510
+ def test_Type__cast_check__floating_point():
511
+ raises(ValueError, lambda: f32.cast_check(123.45678949))
512
+ raises(ValueError, lambda: f32.cast_check(12.345678949))
513
+ raises(ValueError, lambda: f32.cast_check(1.2345678949))
514
+ raises(ValueError, lambda: f32.cast_check(.12345678949))
515
+ assert abs(123.456789049 - f32.cast_check(123.456789049) - 4.9e-8) < 1e-8
516
+ assert abs(0.12345678904 - f32.cast_check(0.12345678904) - 4e-11) < 1e-11
517
+
518
+ dcm21 = Float('0.123456789012345670499') # 21 decimals
519
+ assert abs(dcm21 - f64.cast_check(dcm21) - 4.99e-19) < 1e-19
520
+
521
+ f80.cast_check(Float('0.12345678901234567890103', precision=88))
522
+ raises(ValueError, lambda: f80.cast_check(Float('0.12345678901234567890149', precision=88)))
523
+
524
+ v10 = 12345.67894
525
+ raises(ValueError, lambda: f32.cast_check(v10))
526
+ assert abs(Float(str(v10), precision=64+8) - f64.cast_check(v10)) < v10*1e-16
527
+
528
+ assert abs(f32.cast_check(2147483647) - 2147483650) < 1
529
+
530
+
531
+ def test_Type__cast_check__complex_floating_point():
532
+ val9_11 = 123.456789049 + 0.123456789049j
533
+ raises(ValueError, lambda: c64.cast_check(.12345678949 + .12345678949j))
534
+ assert abs(val9_11 - c64.cast_check(val9_11) - 4.9e-8) < 1e-8
535
+
536
+ dcm21 = Float('0.123456789012345670499') + 1e-20j # 21 decimals
537
+ assert abs(dcm21 - c128.cast_check(dcm21) - 4.99e-19) < 1e-19
538
+ v19 = Float('0.1234567890123456749') + 1j*Float('0.1234567890123456749')
539
+ raises(ValueError, lambda: c128.cast_check(v19))
540
+
541
+
542
+ def test_While():
543
+ xpp = AddAugmentedAssignment(x, 1)
544
+ whl1 = While(x < 2, [xpp])
545
+ assert whl1.condition.args[0] == x
546
+ assert whl1.condition.args[1] == 2
547
+ assert whl1.condition == Lt(x, 2, evaluate=False)
548
+ assert whl1.body.args == (xpp,)
549
+ assert whl1.func(*whl1.args) == whl1
550
+
551
+ cblk = CodeBlock(AddAugmentedAssignment(x, 1))
552
+ whl2 = While(x < 2, cblk)
553
+ assert whl1 == whl2
554
+ assert whl1 != While(x < 3, [xpp])
555
+
556
+
557
+ def test_Scope():
558
+ assign = Assignment(x, y)
559
+ incr = AddAugmentedAssignment(x, 1)
560
+ scp = Scope([assign, incr])
561
+ cblk = CodeBlock(assign, incr)
562
+ assert scp.body == cblk
563
+ assert scp == Scope(cblk)
564
+ assert scp != Scope([incr, assign])
565
+ assert scp.func(*scp.args) == scp
566
+
567
+
568
+ def test_Print():
569
+ fmt = "%d %.3f"
570
+ ps = Print([n, x], fmt)
571
+ assert str(ps.format_string) == fmt
572
+ assert ps.print_args == Tuple(n, x)
573
+ assert ps.args == (Tuple(n, x), QuotedString(fmt), none)
574
+ assert ps == Print((n, x), fmt)
575
+ assert ps != Print([x, n], fmt)
576
+ assert ps.func(*ps.args) == ps
577
+
578
+ ps2 = Print([n, x])
579
+ assert ps2 == Print([n, x])
580
+ assert ps2 != ps
581
+ assert ps2.format_string == None
582
+
583
+
584
+ def test_FunctionPrototype_and_FunctionDefinition():
585
+ vx = Variable(x, type=real)
586
+ vn = Variable(n, type=integer)
587
+ fp1 = FunctionPrototype(real, 'power', [vx, vn])
588
+ assert fp1.return_type == real
589
+ assert fp1.name == String('power')
590
+ assert fp1.parameters == Tuple(vx, vn)
591
+ assert fp1 == FunctionPrototype(real, 'power', [vx, vn])
592
+ assert fp1 != FunctionPrototype(real, 'power', [vn, vx])
593
+ assert fp1.func(*fp1.args) == fp1
594
+
595
+
596
+ body = [Assignment(x, x**n), Return(x)]
597
+ fd1 = FunctionDefinition(real, 'power', [vx, vn], body)
598
+ assert fd1.return_type == real
599
+ assert str(fd1.name) == 'power'
600
+ assert fd1.parameters == Tuple(vx, vn)
601
+ assert fd1.body == CodeBlock(*body)
602
+ assert fd1 == FunctionDefinition(real, 'power', [vx, vn], body)
603
+ assert fd1 != FunctionDefinition(real, 'power', [vx, vn], body[::-1])
604
+ assert fd1.func(*fd1.args) == fd1
605
+
606
+ fp2 = FunctionPrototype.from_FunctionDefinition(fd1)
607
+ assert fp2 == fp1
608
+
609
+ fd2 = FunctionDefinition.from_FunctionPrototype(fp1, body)
610
+ assert fd2 == fd1
611
+
612
+
613
+ def test_Return():
614
+ rs = Return(x)
615
+ assert rs.args == (x,)
616
+ assert rs == Return(x)
617
+ assert rs != Return(y)
618
+ assert rs.func(*rs.args) == rs
619
+
620
+
621
+ def test_FunctionCall():
622
+ fc = FunctionCall('power', (x, 3))
623
+ assert fc.function_args[0] == x
624
+ assert fc.function_args[1] == 3
625
+ assert len(fc.function_args) == 2
626
+ assert isinstance(fc.function_args[1], Integer)
627
+ assert fc == FunctionCall('power', (x, 3))
628
+ assert fc != FunctionCall('power', (3, x))
629
+ assert fc != FunctionCall('Power', (x, 3))
630
+ assert fc.func(*fc.args) == fc
631
+
632
+ fc2 = FunctionCall('fma', [2, 3, 4])
633
+ assert len(fc2.function_args) == 3
634
+ assert fc2.function_args[0] == 2
635
+ assert fc2.function_args[1] == 3
636
+ assert fc2.function_args[2] == 4
637
+ assert str(fc2) in ( # not sure if QuotedString is a better default...
638
+ 'FunctionCall(fma, function_args=(2, 3, 4))',
639
+ 'FunctionCall("fma", function_args=(2, 3, 4))',
640
+ )
641
+
642
+ def test_ast_replace():
643
+ x = Variable('x', real)
644
+ y = Variable('y', real)
645
+ n = Variable('n', integer)
646
+
647
+ pwer = FunctionDefinition(real, 'pwer', [x, n], [pow(x.symbol, n.symbol)])
648
+ pname = pwer.name
649
+ pcall = FunctionCall('pwer', [y, 3])
650
+
651
+ tree1 = CodeBlock(pwer, pcall)
652
+ assert str(tree1.args[0].name) == 'pwer'
653
+ assert str(tree1.args[1].name) == 'pwer'
654
+ for a, b in zip(tree1, [pwer, pcall]):
655
+ assert a == b
656
+
657
+ tree2 = tree1.replace(pname, String('power'))
658
+ assert str(tree1.args[0].name) == 'pwer'
659
+ assert str(tree1.args[1].name) == 'pwer'
660
+ assert str(tree2.args[0].name) == 'power'
661
+ assert str(tree2.args[1].name) == 'power'
.venv/lib/python3.13/site-packages/sympy/codegen/tests/test_cfunctions.py ADDED
@@ -0,0 +1,186 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core.numbers import (Rational, pi)
2
+ from sympy.core.singleton import S
3
+ from sympy.core.symbol import (Symbol, symbols)
4
+ from sympy.functions.elementary.exponential import (exp, log)
5
+ from sympy.codegen.cfunctions import (
6
+ expm1, log1p, exp2, log2, fma, log10, Sqrt, Cbrt, hypot, isnan, isinf
7
+ )
8
+ from sympy.core.function import expand_log
9
+
10
+
11
+ def test_expm1():
12
+ # Eval
13
+ assert expm1(0) == 0
14
+
15
+ x = Symbol('x', real=True)
16
+
17
+ # Expand and rewrite
18
+ assert expm1(x).expand(func=True) - exp(x) == -1
19
+ assert expm1(x).rewrite('tractable') - exp(x) == -1
20
+ assert expm1(x).rewrite('exp') - exp(x) == -1
21
+
22
+ # Precision
23
+ assert not ((exp(1e-10).evalf() - 1) - 1e-10 - 5e-21) < 1e-22 # for comparison
24
+ assert abs(expm1(1e-10).evalf() - 1e-10 - 5e-21) < 1e-22
25
+
26
+ # Properties
27
+ assert expm1(x).is_real
28
+ assert expm1(x).is_finite
29
+
30
+ # Diff
31
+ assert expm1(42*x).diff(x) - 42*exp(42*x) == 0
32
+ assert expm1(42*x).diff(x) - expm1(42*x).expand(func=True).diff(x) == 0
33
+
34
+
35
+ def test_log1p():
36
+ # Eval
37
+ assert log1p(0) == 0
38
+ d = S(10)
39
+ assert expand_log(log1p(d**-1000) - log(d**1000 + 1) + log(d**1000)) == 0
40
+
41
+ x = Symbol('x', real=True)
42
+
43
+ # Expand and rewrite
44
+ assert log1p(x).expand(func=True) - log(x + 1) == 0
45
+ assert log1p(x).rewrite('tractable') - log(x + 1) == 0
46
+ assert log1p(x).rewrite('log') - log(x + 1) == 0
47
+
48
+ # Precision
49
+ assert not abs(log(1e-99 + 1).evalf() - 1e-99) < 1e-100 # for comparison
50
+ assert abs(expand_log(log1p(1e-99)).evalf() - 1e-99) < 1e-100
51
+
52
+ # Properties
53
+ assert log1p(-2**Rational(-1, 2)).is_real
54
+
55
+ assert not log1p(-1).is_finite
56
+ assert log1p(pi).is_finite
57
+
58
+ assert not log1p(x).is_positive
59
+ assert log1p(Symbol('y', positive=True)).is_positive
60
+
61
+ assert not log1p(x).is_zero
62
+ assert log1p(Symbol('z', zero=True)).is_zero
63
+
64
+ assert not log1p(x).is_nonnegative
65
+ assert log1p(Symbol('o', nonnegative=True)).is_nonnegative
66
+
67
+ # Diff
68
+ assert log1p(42*x).diff(x) - 42/(42*x + 1) == 0
69
+ assert log1p(42*x).diff(x) - log1p(42*x).expand(func=True).diff(x) == 0
70
+
71
+
72
+ def test_exp2():
73
+ # Eval
74
+ assert exp2(2) == 4
75
+
76
+ x = Symbol('x', real=True)
77
+
78
+ # Expand
79
+ assert exp2(x).expand(func=True) - 2**x == 0
80
+
81
+ # Diff
82
+ assert exp2(42*x).diff(x) - 42*exp2(42*x)*log(2) == 0
83
+ assert exp2(42*x).diff(x) - exp2(42*x).diff(x) == 0
84
+
85
+
86
+ def test_log2():
87
+ # Eval
88
+ assert log2(8) == 3
89
+ assert log2(pi) != log(pi)/log(2) # log2 should *save* (CPU) instructions
90
+
91
+ x = Symbol('x', real=True)
92
+ assert log2(x) != log(x)/log(2)
93
+ assert log2(2**x) == x
94
+
95
+ # Expand
96
+ assert log2(x).expand(func=True) - log(x)/log(2) == 0
97
+
98
+ # Diff
99
+ assert log2(42*x).diff() - 1/(log(2)*x) == 0
100
+ assert log2(42*x).diff() - log2(42*x).expand(func=True).diff(x) == 0
101
+
102
+
103
+ def test_fma():
104
+ x, y, z = symbols('x y z')
105
+
106
+ # Expand
107
+ assert fma(x, y, z).expand(func=True) - x*y - z == 0
108
+
109
+ expr = fma(17*x, 42*y, 101*z)
110
+
111
+ # Diff
112
+ assert expr.diff(x) - expr.expand(func=True).diff(x) == 0
113
+ assert expr.diff(y) - expr.expand(func=True).diff(y) == 0
114
+ assert expr.diff(z) - expr.expand(func=True).diff(z) == 0
115
+
116
+ assert expr.diff(x) - 17*42*y == 0
117
+ assert expr.diff(y) - 17*42*x == 0
118
+ assert expr.diff(z) - 101 == 0
119
+
120
+
121
+ def test_log10():
122
+ x = Symbol('x')
123
+
124
+ # Expand
125
+ assert log10(x).expand(func=True) - log(x)/log(10) == 0
126
+
127
+ # Diff
128
+ assert log10(42*x).diff(x) - 1/(log(10)*x) == 0
129
+ assert log10(42*x).diff(x) - log10(42*x).expand(func=True).diff(x) == 0
130
+
131
+
132
+ def test_Cbrt():
133
+ x = Symbol('x')
134
+
135
+ # Expand
136
+ assert Cbrt(x).expand(func=True) - x**Rational(1, 3) == 0
137
+
138
+ # Diff
139
+ assert Cbrt(42*x).diff(x) - 42*(42*x)**(Rational(1, 3) - 1)/3 == 0
140
+ assert Cbrt(42*x).diff(x) - Cbrt(42*x).expand(func=True).diff(x) == 0
141
+
142
+
143
+ def test_Sqrt():
144
+ x = Symbol('x')
145
+
146
+ # Expand
147
+ assert Sqrt(x).expand(func=True) - x**S.Half == 0
148
+
149
+ # Diff
150
+ assert Sqrt(42*x).diff(x) - 42*(42*x)**(S.Half - 1)/2 == 0
151
+ assert Sqrt(42*x).diff(x) - Sqrt(42*x).expand(func=True).diff(x) == 0
152
+
153
+
154
+ def test_hypot():
155
+ x, y = symbols('x y')
156
+
157
+ # Expand
158
+ assert hypot(x, y).expand(func=True) - (x**2 + y**2)**S.Half == 0
159
+
160
+ # Diff
161
+ assert hypot(17*x, 42*y).diff(x).expand(func=True) - hypot(17*x, 42*y).expand(func=True).diff(x) == 0
162
+ assert hypot(17*x, 42*y).diff(y).expand(func=True) - hypot(17*x, 42*y).expand(func=True).diff(y) == 0
163
+
164
+ assert hypot(17*x, 42*y).diff(x).expand(func=True) - 2*17*17*x*((17*x)**2 + (42*y)**2)**Rational(-1, 2)/2 == 0
165
+ assert hypot(17*x, 42*y).diff(y).expand(func=True) - 2*42*42*y*((17*x)**2 + (42*y)**2)**Rational(-1, 2)/2 == 0
166
+
167
+
168
+ def test_isnan_isinf():
169
+ x = Symbol('x')
170
+
171
+ # isinf
172
+ assert isinf(+S.Infinity) == True
173
+ assert isinf(-S.Infinity) == True
174
+ assert isinf(S.Pi) == False
175
+ isinfx = isinf(x)
176
+ assert isinfx not in (False, True)
177
+ assert isinfx.func is isinf
178
+ assert isinfx.args == (x,)
179
+
180
+ # isnan
181
+ assert isnan(S.NaN) == True
182
+ assert isnan(S.Pi) == False
183
+ isnanx = isnan(x)
184
+ assert isnanx not in (False, True)
185
+ assert isnanx.func is isnan
186
+ assert isnanx.args == (x,)
.venv/lib/python3.13/site-packages/sympy/codegen/tests/test_cnodes.py ADDED
@@ -0,0 +1,112 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core.symbol import symbols
2
+ from sympy.printing.codeprinter import ccode
3
+ from sympy.codegen.ast import Declaration, Variable, float64, int64, String, CodeBlock
4
+ from sympy.codegen.cnodes import (
5
+ alignof, CommaOperator, goto, Label, PreDecrement, PostDecrement, PreIncrement, PostIncrement,
6
+ sizeof, union, struct
7
+ )
8
+
9
+ x, y = symbols('x y')
10
+
11
+
12
+ def test_alignof():
13
+ ax = alignof(x)
14
+ assert ccode(ax) == 'alignof(x)'
15
+ assert ax.func(*ax.args) == ax
16
+
17
+
18
+ def test_CommaOperator():
19
+ expr = CommaOperator(PreIncrement(x), 2*x)
20
+ assert ccode(expr) == '(++(x), 2*x)'
21
+ assert expr.func(*expr.args) == expr
22
+
23
+
24
+ def test_goto_Label():
25
+ s = 'early_exit'
26
+ g = goto(s)
27
+ assert g.func(*g.args) == g
28
+ assert g != goto('foobar')
29
+ assert ccode(g) == 'goto early_exit'
30
+
31
+ l1 = Label(s)
32
+ assert ccode(l1) == 'early_exit:'
33
+ assert l1 == Label('early_exit')
34
+ assert l1 != Label('foobar')
35
+
36
+ body = [PreIncrement(x)]
37
+ l2 = Label(s, body)
38
+ assert l2.name == String("early_exit")
39
+ assert l2.body == CodeBlock(PreIncrement(x))
40
+ assert ccode(l2) == ("early_exit:\n"
41
+ "++(x);")
42
+
43
+ body = [PreIncrement(x), PreDecrement(y)]
44
+ l2 = Label(s, body)
45
+ assert l2.name == String("early_exit")
46
+ assert l2.body == CodeBlock(PreIncrement(x), PreDecrement(y))
47
+ assert ccode(l2) == ("early_exit:\n"
48
+ "{\n ++(x);\n --(y);\n}")
49
+
50
+
51
+ def test_PreDecrement():
52
+ p = PreDecrement(x)
53
+ assert p.func(*p.args) == p
54
+ assert ccode(p) == '--(x)'
55
+
56
+
57
+ def test_PostDecrement():
58
+ p = PostDecrement(x)
59
+ assert p.func(*p.args) == p
60
+ assert ccode(p) == '(x)--'
61
+
62
+
63
+ def test_PreIncrement():
64
+ p = PreIncrement(x)
65
+ assert p.func(*p.args) == p
66
+ assert ccode(p) == '++(x)'
67
+
68
+
69
+ def test_PostIncrement():
70
+ p = PostIncrement(x)
71
+ assert p.func(*p.args) == p
72
+ assert ccode(p) == '(x)++'
73
+
74
+
75
+ def test_sizeof():
76
+ typename = 'unsigned int'
77
+ sz = sizeof(typename)
78
+ assert ccode(sz) == 'sizeof(%s)' % typename
79
+ assert sz.func(*sz.args) == sz
80
+ assert not sz.is_Atom
81
+ assert sz.atoms() == {String('unsigned int'), String('sizeof')}
82
+
83
+
84
+ def test_struct():
85
+ vx, vy = Variable(x, type=float64), Variable(y, type=float64)
86
+ s = struct('vec2', [vx, vy])
87
+ assert s.func(*s.args) == s
88
+ assert s == struct('vec2', (vx, vy))
89
+ assert s != struct('vec2', (vy, vx))
90
+ assert str(s.name) == 'vec2'
91
+ assert len(s.declarations) == 2
92
+ assert all(isinstance(arg, Declaration) for arg in s.declarations)
93
+ assert ccode(s) == (
94
+ "struct vec2 {\n"
95
+ " double x;\n"
96
+ " double y;\n"
97
+ "}")
98
+
99
+
100
+ def test_union():
101
+ vx, vy = Variable(x, type=float64), Variable(y, type=int64)
102
+ u = union('dualuse', [vx, vy])
103
+ assert u.func(*u.args) == u
104
+ assert u == union('dualuse', (vx, vy))
105
+ assert str(u.name) == 'dualuse'
106
+ assert len(u.declarations) == 2
107
+ assert all(isinstance(arg, Declaration) for arg in u.declarations)
108
+ assert ccode(u) == (
109
+ "union dualuse {\n"
110
+ " double x;\n"
111
+ " int64_t y;\n"
112
+ "}")
.venv/lib/python3.13/site-packages/sympy/codegen/tests/test_cxxnodes.py ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core.symbol import Symbol
2
+ from sympy.codegen.ast import Type
3
+ from sympy.codegen.cxxnodes import using
4
+ from sympy.printing.codeprinter import cxxcode
5
+
6
+ x = Symbol('x')
7
+
8
+ def test_using():
9
+ v = Type('std::vector')
10
+ u1 = using(v)
11
+ assert cxxcode(u1) == 'using std::vector'
12
+
13
+ u2 = using(v, 'vec')
14
+ assert cxxcode(u2) == 'using vec = std::vector'
.venv/lib/python3.13/site-packages/sympy/codegen/tests/test_fnodes.py ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import tempfile
3
+ from sympy.core.symbol import (Symbol, symbols)
4
+ from sympy.codegen.ast import (
5
+ Assignment, Print, Declaration, FunctionDefinition, Return, real,
6
+ FunctionCall, Variable, Element, integer
7
+ )
8
+ from sympy.codegen.fnodes import (
9
+ allocatable, ArrayConstructor, isign, dsign, cmplx, kind, literal_dp,
10
+ Program, Module, use, Subroutine, dimension, assumed_extent, ImpliedDoLoop,
11
+ intent_out, size, Do, SubroutineCall, sum_, array, bind_C
12
+ )
13
+ from sympy.codegen.futils import render_as_module
14
+ from sympy.core.expr import unchanged
15
+ from sympy.external import import_module
16
+ from sympy.printing.codeprinter import fcode
17
+ from sympy.utilities._compilation import has_fortran, compile_run_strings, compile_link_import_strings
18
+ from sympy.utilities._compilation.util import may_xfail
19
+ from sympy.testing.pytest import skip, XFAIL
20
+
21
+ cython = import_module('cython')
22
+ np = import_module('numpy')
23
+
24
+
25
+ def test_size():
26
+ x = Symbol('x', real=True)
27
+ sx = size(x)
28
+ assert fcode(sx, source_format='free') == 'size(x)'
29
+
30
+
31
+ @may_xfail
32
+ def test_size_assumed_shape():
33
+ if not has_fortran():
34
+ skip("No fortran compiler found.")
35
+ a = Symbol('a', real=True)
36
+ body = [Return((sum_(a**2)/size(a))**.5)]
37
+ arr = array(a, dim=[':'], intent='in')
38
+ fd = FunctionDefinition(real, 'rms', [arr], body)
39
+ render_as_module([fd], 'mod_rms')
40
+
41
+ (stdout, stderr), info = compile_run_strings([
42
+ ('rms.f90', render_as_module([fd], 'mod_rms')),
43
+ ('main.f90', (
44
+ 'program myprog\n'
45
+ 'use mod_rms, only: rms\n'
46
+ 'real*8, dimension(4), parameter :: x = [4, 2, 2, 2]\n'
47
+ 'print "(f7.5)", dsqrt(7d0) - rms(x)\n'
48
+ 'end program\n'
49
+ ))
50
+ ], clean=True)
51
+ assert '0.00000' in stdout
52
+ assert stderr == ''
53
+ assert info['exit_status'] == os.EX_OK
54
+
55
+
56
+ @XFAIL # https://github.com/sympy/sympy/issues/20265
57
+ @may_xfail
58
+ def test_ImpliedDoLoop():
59
+ if not has_fortran():
60
+ skip("No fortran compiler found.")
61
+
62
+ a, i = symbols('a i', integer=True)
63
+ idl = ImpliedDoLoop(i**3, i, -3, 3, 2)
64
+ ac = ArrayConstructor([-28, idl, 28])
65
+ a = array(a, dim=[':'], attrs=[allocatable])
66
+ prog = Program('idlprog', [
67
+ a.as_Declaration(),
68
+ Assignment(a, ac),
69
+ Print([a])
70
+ ])
71
+ fsrc = fcode(prog, standard=2003, source_format='free')
72
+ (stdout, stderr), info = compile_run_strings([('main.f90', fsrc)], clean=True)
73
+ for numstr in '-28 -27 -1 1 27 28'.split():
74
+ assert numstr in stdout
75
+ assert stderr == ''
76
+ assert info['exit_status'] == os.EX_OK
77
+
78
+
79
+ @may_xfail
80
+ def test_Program():
81
+ x = Symbol('x', real=True)
82
+ vx = Variable.deduced(x, 42)
83
+ decl = Declaration(vx)
84
+ prnt = Print([x, x+1])
85
+ prog = Program('foo', [decl, prnt])
86
+ if not has_fortran():
87
+ skip("No fortran compiler found.")
88
+
89
+ (stdout, stderr), info = compile_run_strings([('main.f90', fcode(prog, standard=90))], clean=True)
90
+ assert '42' in stdout
91
+ assert '43' in stdout
92
+ assert stderr == ''
93
+ assert info['exit_status'] == os.EX_OK
94
+
95
+
96
+ @may_xfail
97
+ def test_Module():
98
+ x = Symbol('x', real=True)
99
+ v_x = Variable.deduced(x)
100
+ sq = FunctionDefinition(real, 'sqr', [v_x], [Return(x**2)])
101
+ mod_sq = Module('mod_sq', [], [sq])
102
+ sq_call = FunctionCall('sqr', [42.])
103
+ prg_sq = Program('foobar', [
104
+ use('mod_sq', only=['sqr']),
105
+ Print(['"Square of 42 = "', sq_call])
106
+ ])
107
+ if not has_fortran():
108
+ skip("No fortran compiler found.")
109
+ (stdout, stderr), info = compile_run_strings([
110
+ ('mod_sq.f90', fcode(mod_sq, standard=90)),
111
+ ('main.f90', fcode(prg_sq, standard=90))
112
+ ], clean=True)
113
+ assert '42' in stdout
114
+ assert str(42**2) in stdout
115
+ assert stderr == ''
116
+
117
+
118
+ @XFAIL # https://github.com/sympy/sympy/issues/20265
119
+ @may_xfail
120
+ def test_Subroutine():
121
+ # Code to generate the subroutine in the example from
122
+ # http://www.fortran90.org/src/best-practices.html#arrays
123
+ r = Symbol('r', real=True)
124
+ i = Symbol('i', integer=True)
125
+ v_r = Variable.deduced(r, attrs=(dimension(assumed_extent), intent_out))
126
+ v_i = Variable.deduced(i)
127
+ v_n = Variable('n', integer)
128
+ do_loop = Do([
129
+ Assignment(Element(r, [i]), literal_dp(1)/i**2)
130
+ ], i, 1, v_n)
131
+ sub = Subroutine("f", [v_r], [
132
+ Declaration(v_n),
133
+ Declaration(v_i),
134
+ Assignment(v_n, size(r)),
135
+ do_loop
136
+ ])
137
+ x = Symbol('x', real=True)
138
+ v_x3 = Variable.deduced(x, attrs=[dimension(3)])
139
+ mod = Module('mymod', definitions=[sub])
140
+ prog = Program('foo', [
141
+ use(mod, only=[sub]),
142
+ Declaration(v_x3),
143
+ SubroutineCall(sub, [v_x3]),
144
+ Print([sum_(v_x3), v_x3])
145
+ ])
146
+
147
+ if not has_fortran():
148
+ skip("No fortran compiler found.")
149
+
150
+ (stdout, stderr), info = compile_run_strings([
151
+ ('a.f90', fcode(mod, standard=90)),
152
+ ('b.f90', fcode(prog, standard=90))
153
+ ], clean=True)
154
+ ref = [1.0/i**2 for i in range(1, 4)]
155
+ assert str(sum(ref))[:-3] in stdout
156
+ for _ in ref:
157
+ assert str(_)[:-3] in stdout
158
+ assert stderr == ''
159
+
160
+
161
+ def test_isign():
162
+ x = Symbol('x', integer=True)
163
+ assert unchanged(isign, 1, x)
164
+ assert fcode(isign(1, x), standard=95, source_format='free') == 'isign(1, x)'
165
+
166
+
167
+ def test_dsign():
168
+ x = Symbol('x')
169
+ assert unchanged(dsign, 1, x)
170
+ assert fcode(dsign(literal_dp(1), x), standard=95, source_format='free') == 'dsign(1d0, x)'
171
+
172
+
173
+ def test_cmplx():
174
+ x = Symbol('x')
175
+ assert unchanged(cmplx, 1, x)
176
+
177
+
178
+ def test_kind():
179
+ x = Symbol('x')
180
+ assert unchanged(kind, x)
181
+
182
+
183
+ def test_literal_dp():
184
+ assert fcode(literal_dp(0), source_format='free') == '0d0'
185
+
186
+
187
+ @may_xfail
188
+ def test_bind_C():
189
+ if not has_fortran():
190
+ skip("No fortran compiler found.")
191
+ if not cython:
192
+ skip("Cython not found.")
193
+ if not np:
194
+ skip("NumPy not found.")
195
+
196
+ a = Symbol('a', real=True)
197
+ s = Symbol('s', integer=True)
198
+ body = [Return((sum_(a**2)/s)**.5)]
199
+ arr = array(a, dim=[s], intent='in')
200
+ fd = FunctionDefinition(real, 'rms', [arr, s], body, attrs=[bind_C('rms')])
201
+ f_mod = render_as_module([fd], 'mod_rms')
202
+
203
+ with tempfile.TemporaryDirectory() as folder:
204
+ mod, info = compile_link_import_strings([
205
+ ('rms.f90', f_mod),
206
+ ('_rms.pyx', (
207
+ "#cython: language_level={}\n".format("3") +
208
+ "cdef extern double rms(double*, int*)\n"
209
+ "def py_rms(double[::1] x):\n"
210
+ " cdef int s = x.size\n"
211
+ " return rms(&x[0], &s)\n"))
212
+ ], build_dir=folder)
213
+ assert abs(mod.py_rms(np.array([2., 4., 2., 2.])) - 7**0.5) < 1e-14
.venv/lib/python3.13/site-packages/sympy/codegen/tests/test_matrix_nodes.py ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core.symbol import symbols
2
+ from sympy.core.function import Function
3
+ from sympy.matrices.dense import Matrix
4
+ from sympy.matrices.dense import zeros
5
+ from sympy.simplify.simplify import simplify
6
+ from sympy.codegen.matrix_nodes import MatrixSolve
7
+ from sympy.utilities.lambdify import lambdify
8
+ from sympy.printing.numpy import NumPyPrinter
9
+ from sympy.testing.pytest import skip
10
+ from sympy.external import import_module
11
+
12
+
13
+ def test_matrix_solve_issue_24862():
14
+ A = Matrix(3, 3, symbols('a:9'))
15
+ b = Matrix(3, 1, symbols('b:3'))
16
+ hash(MatrixSolve(A, b))
17
+
18
+
19
+ def test_matrix_solve_derivative_exact():
20
+ q = symbols('q')
21
+ a11, a12, a21, a22, b1, b2 = (
22
+ f(q) for f in symbols('a11 a12 a21 a22 b1 b2', cls=Function))
23
+ A = Matrix([[a11, a12], [a21, a22]])
24
+ b = Matrix([b1, b2])
25
+ x_lu = A.LUsolve(b)
26
+ dxdq_lu = A.LUsolve(b.diff(q) - A.diff(q) * A.LUsolve(b))
27
+ assert simplify(x_lu.diff(q) - dxdq_lu) == zeros(2, 1)
28
+ # dxdq_ms is the MatrixSolve equivalent of dxdq_lu
29
+ dxdq_ms = MatrixSolve(A, b.diff(q) - A.diff(q) * MatrixSolve(A, b))
30
+ assert MatrixSolve(A, b).diff(q) == dxdq_ms
31
+
32
+
33
+ def test_matrix_solve_derivative_numpy():
34
+ np = import_module('numpy')
35
+ if not np:
36
+ skip("numpy not installed.")
37
+ q = symbols('q')
38
+ a11, a12, a21, a22, b1, b2 = (
39
+ f(q) for f in symbols('a11 a12 a21 a22 b1 b2', cls=Function))
40
+ A = Matrix([[a11, a12], [a21, a22]])
41
+ b = Matrix([b1, b2])
42
+ dx_lu = A.LUsolve(b).diff(q)
43
+ subs = {a11.diff(q): 0.2, a12.diff(q): 0.3, a21.diff(q): 0.1,
44
+ a22.diff(q): 0.5, b1.diff(q): 0.4, b2.diff(q): 0.9,
45
+ a11: 1.3, a12: 0.5, a21: 1.2, a22: 4, b1: 6.2, b2: 3.5}
46
+ p, p_vals = zip(*subs.items())
47
+ dx_sm = MatrixSolve(A, b).diff(q)
48
+ np.testing.assert_allclose(
49
+ lambdify(p, dx_sm, printer=NumPyPrinter)(*p_vals),
50
+ lambdify(p, dx_lu, printer=NumPyPrinter)(*p_vals))
.venv/lib/python3.13/site-packages/sympy/codegen/tests/test_numpy_nodes.py ADDED
@@ -0,0 +1,69 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from itertools import product
2
+ from sympy.core.singleton import S
3
+ from sympy.core.symbol import symbols
4
+ from sympy.functions.elementary.exponential import (exp, log)
5
+ from sympy.functions.elementary.miscellaneous import Max, Min
6
+ from sympy.printing.repr import srepr
7
+ from sympy.codegen.numpy_nodes import logaddexp, logaddexp2, minimum, maximum, amax, amin
8
+ from sympy.testing.pytest import raises
9
+
10
+ x, y, z = symbols('x y z')
11
+
12
+ def test_logaddexp():
13
+ lae_xy = logaddexp(x, y)
14
+ ref_xy = log(exp(x) + exp(y))
15
+ for wrt, deriv_order in product([x, y, z], range(3)):
16
+ assert (
17
+ lae_xy.diff(wrt, deriv_order) -
18
+ ref_xy.diff(wrt, deriv_order)
19
+ ).rewrite(log).simplify() == 0
20
+
21
+ one_third_e = 1*exp(1)/3
22
+ two_thirds_e = 2*exp(1)/3
23
+ logThirdE = log(one_third_e)
24
+ logTwoThirdsE = log(two_thirds_e)
25
+ lae_sum_to_e = logaddexp(logThirdE, logTwoThirdsE)
26
+ assert lae_sum_to_e.rewrite(log) == 1
27
+ assert lae_sum_to_e.simplify() == 1
28
+ was = logaddexp(2, 3)
29
+ assert srepr(was) == srepr(was.simplify()) # cannot simplify with 2, 3
30
+
31
+
32
+ def test_logaddexp2():
33
+ lae2_xy = logaddexp2(x, y)
34
+ ref2_xy = log(2**x + 2**y)/log(2)
35
+ for wrt, deriv_order in product([x, y, z], range(3)):
36
+ assert (
37
+ lae2_xy.diff(wrt, deriv_order) -
38
+ ref2_xy.diff(wrt, deriv_order)
39
+ ).rewrite(log).cancel() == 0
40
+
41
+ def lb(x):
42
+ return log(x)/log(2)
43
+
44
+ two_thirds = S.One*2/3
45
+ four_thirds = 2*two_thirds
46
+ lbTwoThirds = lb(two_thirds)
47
+ lbFourThirds = lb(four_thirds)
48
+ lae2_sum_to_2 = logaddexp2(lbTwoThirds, lbFourThirds)
49
+ assert lae2_sum_to_2.rewrite(log) == 1
50
+ assert lae2_sum_to_2.simplify() == 1
51
+ was = logaddexp2(x, y)
52
+ assert srepr(was) == srepr(was.simplify()) # cannot simplify with x, y
53
+
54
+
55
+ def test_minimum_maximum():
56
+ for MM, mm in zip([Min, Max], [minimum, maximum]):
57
+ ref = MM(x, y, z)
58
+ m = mm(x, y, z)
59
+ assert m != ref
60
+ assert m.rewrite(MM) == ref
61
+
62
+
63
+ def test_amin_amax():
64
+ for am in [amin, amax]:
65
+ assert am(x).array == x
66
+ assert am(x).axis == None
67
+ assert am(x, axis=3).axis == 3
68
+ with raises(ValueError):
69
+ am(x, y, z)
.venv/lib/python3.13/site-packages/sympy/codegen/tests/test_pynodes.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core.symbol import symbols
2
+ from sympy.codegen.pynodes import List
3
+
4
+
5
+ def test_List():
6
+ l = List(2, 3, 4)
7
+ assert l == List(2, 3, 4)
8
+ assert str(l) == "[2, 3, 4]"
9
+ x, y, z = symbols('x y z')
10
+ l = List(x**2,y**3,z**4)
11
+ # contrary to python's built-in list, we can call e.g. "replace" on List.
12
+ m = l.replace(lambda arg: arg.is_Pow and arg.exp>2, lambda p: p.base-p.exp)
13
+ assert m == [x**2, y-3, z-4]
.venv/lib/python3.13/site-packages/sympy/codegen/tests/test_pyutils.py ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ from sympy.codegen.ast import Print
2
+ from sympy.codegen.pyutils import render_as_module
3
+
4
+ def test_standard():
5
+ ast = Print('x y'.split(), r"coordinate: %12.5g %12.5g\n")
6
+ assert render_as_module(ast, standard='python3') == \
7
+ '\n\nprint("coordinate: %12.5g %12.5g\\n" % (x, y), end="")'
.venv/lib/python3.13/site-packages/sympy/codegen/tests/test_rewriting.py ADDED
@@ -0,0 +1,479 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import tempfile
2
+ from sympy.core.numbers import pi, Rational
3
+ from sympy.core.power import Pow
4
+ from sympy.core.singleton import S
5
+ from sympy.core.symbol import Symbol
6
+ from sympy.functions.elementary.complexes import Abs
7
+ from sympy.functions.elementary.exponential import (exp, log)
8
+ from sympy.functions.elementary.trigonometric import (cos, sin, sinc)
9
+ from sympy.matrices.expressions.matexpr import MatrixSymbol
10
+ from sympy.assumptions import assuming, Q
11
+ from sympy.external import import_module
12
+ from sympy.printing.codeprinter import ccode
13
+ from sympy.codegen.matrix_nodes import MatrixSolve
14
+ from sympy.codegen.cfunctions import log2, exp2, expm1, log1p
15
+ from sympy.codegen.numpy_nodes import logaddexp, logaddexp2
16
+ from sympy.codegen.scipy_nodes import cosm1, powm1
17
+ from sympy.codegen.rewriting import (
18
+ optimize, cosm1_opt, log2_opt, exp2_opt, expm1_opt, log1p_opt, powm1_opt, optims_c99,
19
+ create_expand_pow_optimization, matinv_opt, logaddexp_opt, logaddexp2_opt,
20
+ optims_numpy, optims_scipy, sinc_opts, FuncMinusOneOptim
21
+ )
22
+ from sympy.testing.pytest import XFAIL, skip
23
+ from sympy.utilities import lambdify
24
+ from sympy.utilities._compilation import compile_link_import_strings, has_c
25
+ from sympy.utilities._compilation.util import may_xfail
26
+
27
+ cython = import_module('cython')
28
+ numpy = import_module('numpy')
29
+ scipy = import_module('scipy')
30
+
31
+
32
+ def test_log2_opt():
33
+ x = Symbol('x')
34
+ expr1 = 7*log(3*x + 5)/(log(2))
35
+ opt1 = optimize(expr1, [log2_opt])
36
+ assert opt1 == 7*log2(3*x + 5)
37
+ assert opt1.rewrite(log) == expr1
38
+
39
+ expr2 = 3*log(5*x + 7)/(13*log(2))
40
+ opt2 = optimize(expr2, [log2_opt])
41
+ assert opt2 == 3*log2(5*x + 7)/13
42
+ assert opt2.rewrite(log) == expr2
43
+
44
+ expr3 = log(x)/log(2)
45
+ opt3 = optimize(expr3, [log2_opt])
46
+ assert opt3 == log2(x)
47
+ assert opt3.rewrite(log) == expr3
48
+
49
+ expr4 = log(x)/log(2) + log(x+1)
50
+ opt4 = optimize(expr4, [log2_opt])
51
+ assert opt4 == log2(x) + log(2)*log2(x+1)
52
+ assert opt4.rewrite(log) == expr4
53
+
54
+ expr5 = log(17)
55
+ opt5 = optimize(expr5, [log2_opt])
56
+ assert opt5 == expr5
57
+
58
+ expr6 = log(x + 3)/log(2)
59
+ opt6 = optimize(expr6, [log2_opt])
60
+ assert str(opt6) == 'log2(x + 3)'
61
+ assert opt6.rewrite(log) == expr6
62
+
63
+
64
+ def test_exp2_opt():
65
+ x = Symbol('x')
66
+ expr1 = 1 + 2**x
67
+ opt1 = optimize(expr1, [exp2_opt])
68
+ assert opt1 == 1 + exp2(x)
69
+ assert opt1.rewrite(Pow) == expr1
70
+
71
+ expr2 = 1 + 3**x
72
+ assert expr2 == optimize(expr2, [exp2_opt])
73
+
74
+
75
+ def test_expm1_opt():
76
+ x = Symbol('x')
77
+
78
+ expr1 = exp(x) - 1
79
+ opt1 = optimize(expr1, [expm1_opt])
80
+ assert expm1(x) - opt1 == 0
81
+ assert opt1.rewrite(exp) == expr1
82
+
83
+ expr2 = 3*exp(x) - 3
84
+ opt2 = optimize(expr2, [expm1_opt])
85
+ assert 3*expm1(x) == opt2
86
+ assert opt2.rewrite(exp) == expr2
87
+
88
+ expr3 = 3*exp(x) - 5
89
+ opt3 = optimize(expr3, [expm1_opt])
90
+ assert 3*expm1(x) - 2 == opt3
91
+ assert opt3.rewrite(exp) == expr3
92
+ expm1_opt_non_opportunistic = FuncMinusOneOptim(exp, expm1, opportunistic=False)
93
+ assert expr3 == optimize(expr3, [expm1_opt_non_opportunistic])
94
+ assert opt1 == optimize(expr1, [expm1_opt_non_opportunistic])
95
+ assert opt2 == optimize(expr2, [expm1_opt_non_opportunistic])
96
+
97
+ expr4 = 3*exp(x) + log(x) - 3
98
+ opt4 = optimize(expr4, [expm1_opt])
99
+ assert 3*expm1(x) + log(x) == opt4
100
+ assert opt4.rewrite(exp) == expr4
101
+
102
+ expr5 = 3*exp(2*x) - 3
103
+ opt5 = optimize(expr5, [expm1_opt])
104
+ assert 3*expm1(2*x) == opt5
105
+ assert opt5.rewrite(exp) == expr5
106
+
107
+ expr6 = (2*exp(x) + 1)/(exp(x) + 1) + 1
108
+ opt6 = optimize(expr6, [expm1_opt])
109
+ assert opt6.count_ops() <= expr6.count_ops()
110
+
111
+ def ev(e):
112
+ return e.subs(x, 3).evalf()
113
+ assert abs(ev(expr6) - ev(opt6)) < 1e-15
114
+
115
+ y = Symbol('y')
116
+ expr7 = (2*exp(x) - 1)/(1 - exp(y)) - 1/(1-exp(y))
117
+ opt7 = optimize(expr7, [expm1_opt])
118
+ assert -2*expm1(x)/expm1(y) == opt7
119
+ assert (opt7.rewrite(exp) - expr7).factor() == 0
120
+
121
+ expr8 = (1+exp(x))**2 - 4
122
+ opt8 = optimize(expr8, [expm1_opt])
123
+ tgt8a = (exp(x) + 3)*expm1(x)
124
+ tgt8b = 2*expm1(x) + expm1(2*x)
125
+ # Both tgt8a & tgt8b seem to give full precision (~16 digits for double)
126
+ # for x=1e-7 (compare with expr8 which only achieves ~8 significant digits).
127
+ # If we can show that either tgt8a or tgt8b is preferable, we can
128
+ # change this test to ensure the preferable version is returned.
129
+ assert (tgt8a - tgt8b).rewrite(exp).factor() == 0
130
+ assert opt8 in (tgt8a, tgt8b)
131
+ assert (opt8.rewrite(exp) - expr8).factor() == 0
132
+
133
+ expr9 = sin(expr8)
134
+ opt9 = optimize(expr9, [expm1_opt])
135
+ tgt9a = sin(tgt8a)
136
+ tgt9b = sin(tgt8b)
137
+ assert opt9 in (tgt9a, tgt9b)
138
+ assert (opt9.rewrite(exp) - expr9.rewrite(exp)).factor().is_zero
139
+
140
+
141
+ def test_expm1_two_exp_terms():
142
+ x, y = map(Symbol, 'x y'.split())
143
+ expr1 = exp(x) + exp(y) - 2
144
+ opt1 = optimize(expr1, [expm1_opt])
145
+ assert opt1 == expm1(x) + expm1(y)
146
+
147
+
148
+ def test_cosm1_opt():
149
+ x = Symbol('x')
150
+
151
+ expr1 = cos(x) - 1
152
+ opt1 = optimize(expr1, [cosm1_opt])
153
+ assert cosm1(x) - opt1 == 0
154
+ assert opt1.rewrite(cos) == expr1
155
+
156
+ expr2 = 3*cos(x) - 3
157
+ opt2 = optimize(expr2, [cosm1_opt])
158
+ assert 3*cosm1(x) == opt2
159
+ assert opt2.rewrite(cos) == expr2
160
+
161
+ expr3 = 3*cos(x) - 5
162
+ opt3 = optimize(expr3, [cosm1_opt])
163
+ assert 3*cosm1(x) - 2 == opt3
164
+ assert opt3.rewrite(cos) == expr3
165
+ cosm1_opt_non_opportunistic = FuncMinusOneOptim(cos, cosm1, opportunistic=False)
166
+ assert expr3 == optimize(expr3, [cosm1_opt_non_opportunistic])
167
+ assert opt1 == optimize(expr1, [cosm1_opt_non_opportunistic])
168
+ assert opt2 == optimize(expr2, [cosm1_opt_non_opportunistic])
169
+
170
+ expr4 = 3*cos(x) + log(x) - 3
171
+ opt4 = optimize(expr4, [cosm1_opt])
172
+ assert 3*cosm1(x) + log(x) == opt4
173
+ assert opt4.rewrite(cos) == expr4
174
+
175
+ expr5 = 3*cos(2*x) - 3
176
+ opt5 = optimize(expr5, [cosm1_opt])
177
+ assert 3*cosm1(2*x) == opt5
178
+ assert opt5.rewrite(cos) == expr5
179
+
180
+ expr6 = 2 - 2*cos(x)
181
+ opt6 = optimize(expr6, [cosm1_opt])
182
+ assert -2*cosm1(x) == opt6
183
+ assert opt6.rewrite(cos) == expr6
184
+
185
+
186
+ def test_cosm1_two_cos_terms():
187
+ x, y = map(Symbol, 'x y'.split())
188
+ expr1 = cos(x) + cos(y) - 2
189
+ opt1 = optimize(expr1, [cosm1_opt])
190
+ assert opt1 == cosm1(x) + cosm1(y)
191
+
192
+
193
+ def test_expm1_cosm1_mixed():
194
+ x = Symbol('x')
195
+ expr1 = exp(x) + cos(x) - 2
196
+ opt1 = optimize(expr1, [expm1_opt, cosm1_opt])
197
+ assert opt1 == cosm1(x) + expm1(x)
198
+
199
+
200
+ def _check_num_lambdify(expr, opt, val_subs, approx_ref, lambdify_kw=None, poorness=1e10):
201
+ """ poorness=1e10 signifies that `expr` loses precision of at least ten decimal digits. """
202
+ num_ref = expr.subs(val_subs).evalf()
203
+ eps = numpy.finfo(numpy.float64).eps
204
+ assert abs(num_ref - approx_ref) < approx_ref*eps
205
+ f1 = lambdify(list(val_subs.keys()), opt, **(lambdify_kw or {}))
206
+ args_float = tuple(map(float, val_subs.values()))
207
+ num_err1 = abs(f1(*args_float) - approx_ref)
208
+ assert num_err1 < abs(num_ref*eps)
209
+ f2 = lambdify(list(val_subs.keys()), expr, **(lambdify_kw or {}))
210
+ num_err2 = abs(f2(*args_float) - approx_ref)
211
+ assert num_err2 > abs(num_ref*eps*poorness) # this only ensures that the *test* works as intended
212
+
213
+
214
+ def test_cosm1_apart():
215
+ x = Symbol('x')
216
+
217
+ expr1 = 1/cos(x) - 1
218
+ opt1 = optimize(expr1, [cosm1_opt])
219
+ assert opt1 == -cosm1(x)/cos(x)
220
+ if scipy:
221
+ _check_num_lambdify(expr1, opt1, {x: S(10)**-30}, 5e-61, lambdify_kw={"modules": 'scipy'})
222
+
223
+ expr2 = 2/cos(x) - 2
224
+ opt2 = optimize(expr2, optims_scipy)
225
+ assert opt2 == -2*cosm1(x)/cos(x)
226
+ if scipy:
227
+ _check_num_lambdify(expr2, opt2, {x: S(10)**-30}, 1e-60, lambdify_kw={"modules": 'scipy'})
228
+
229
+ expr3 = pi/cos(3*x) - pi
230
+ opt3 = optimize(expr3, [cosm1_opt])
231
+ assert opt3 == -pi*cosm1(3*x)/cos(3*x)
232
+ if scipy:
233
+ _check_num_lambdify(expr3, opt3, {x: S(10)**-30/3}, float(5e-61*pi), lambdify_kw={"modules": 'scipy'})
234
+
235
+
236
+ def test_powm1():
237
+ args = x, y = map(Symbol, "xy")
238
+
239
+ expr1 = x**y - 1
240
+ opt1 = optimize(expr1, [powm1_opt])
241
+ assert opt1 == powm1(x, y)
242
+ for arg in args:
243
+ assert expr1.diff(arg) == opt1.diff(arg)
244
+ if scipy and tuple(map(int, scipy.version.version.split('.')[:3])) >= (1, 10, 0):
245
+ subs1_a = {x: Rational(*(1.0+1e-13).as_integer_ratio()), y: pi}
246
+ ref1_f64_a = 3.139081648208105e-13
247
+ _check_num_lambdify(expr1, opt1, subs1_a, ref1_f64_a, lambdify_kw={"modules": 'scipy'}, poorness=10**11)
248
+
249
+ subs1_b = {x: pi, y: Rational(*(1e-10).as_integer_ratio())}
250
+ ref1_f64_b = 1.1447298859149205e-10
251
+ _check_num_lambdify(expr1, opt1, subs1_b, ref1_f64_b, lambdify_kw={"modules": 'scipy'}, poorness=10**9)
252
+
253
+
254
+ def test_log1p_opt():
255
+ x = Symbol('x')
256
+ expr1 = log(x + 1)
257
+ opt1 = optimize(expr1, [log1p_opt])
258
+ assert log1p(x) - opt1 == 0
259
+ assert opt1.rewrite(log) == expr1
260
+
261
+ expr2 = log(3*x + 3)
262
+ opt2 = optimize(expr2, [log1p_opt])
263
+ assert log1p(x) + log(3) == opt2
264
+ assert (opt2.rewrite(log) - expr2).simplify() == 0
265
+
266
+ expr3 = log(2*x + 1)
267
+ opt3 = optimize(expr3, [log1p_opt])
268
+ assert log1p(2*x) - opt3 == 0
269
+ assert opt3.rewrite(log) == expr3
270
+
271
+ expr4 = log(x+3)
272
+ opt4 = optimize(expr4, [log1p_opt])
273
+ assert str(opt4) == 'log(x + 3)'
274
+
275
+
276
+ def test_optims_c99():
277
+ x = Symbol('x')
278
+
279
+ expr1 = 2**x + log(x)/log(2) + log(x + 1) + exp(x) - 1
280
+ opt1 = optimize(expr1, optims_c99).simplify()
281
+ assert opt1 == exp2(x) + log2(x) + log1p(x) + expm1(x)
282
+ assert opt1.rewrite(exp).rewrite(log).rewrite(Pow) == expr1
283
+
284
+ expr2 = log(x)/log(2) + log(x + 1)
285
+ opt2 = optimize(expr2, optims_c99)
286
+ assert opt2 == log2(x) + log1p(x)
287
+ assert opt2.rewrite(log) == expr2
288
+
289
+ expr3 = log(x)/log(2) + log(17*x + 17)
290
+ opt3 = optimize(expr3, optims_c99)
291
+ delta3 = opt3 - (log2(x) + log(17) + log1p(x))
292
+ assert delta3 == 0
293
+ assert (opt3.rewrite(log) - expr3).simplify() == 0
294
+
295
+ expr4 = 2**x + 3*log(5*x + 7)/(13*log(2)) + 11*exp(x) - 11 + log(17*x + 17)
296
+ opt4 = optimize(expr4, optims_c99).simplify()
297
+ delta4 = opt4 - (exp2(x) + 3*log2(5*x + 7)/13 + 11*expm1(x) + log(17) + log1p(x))
298
+ assert delta4 == 0
299
+ assert (opt4.rewrite(exp).rewrite(log).rewrite(Pow) - expr4).simplify() == 0
300
+
301
+ expr5 = 3*exp(2*x) - 3
302
+ opt5 = optimize(expr5, optims_c99)
303
+ delta5 = opt5 - 3*expm1(2*x)
304
+ assert delta5 == 0
305
+ assert opt5.rewrite(exp) == expr5
306
+
307
+ expr6 = exp(2*x) - 3
308
+ opt6 = optimize(expr6, optims_c99)
309
+ assert opt6 in (expm1(2*x) - 2, expr6) # expm1(2*x) - 2 is not better or worse
310
+
311
+ expr7 = log(3*x + 3)
312
+ opt7 = optimize(expr7, optims_c99)
313
+ delta7 = opt7 - (log(3) + log1p(x))
314
+ assert delta7 == 0
315
+ assert (opt7.rewrite(log) - expr7).simplify() == 0
316
+
317
+ expr8 = log(2*x + 3)
318
+ opt8 = optimize(expr8, optims_c99)
319
+ assert opt8 == expr8
320
+
321
+
322
+ def test_create_expand_pow_optimization():
323
+ cc = lambda x: ccode(
324
+ optimize(x, [create_expand_pow_optimization(4)]))
325
+ x = Symbol('x')
326
+ assert cc(x**4) == 'x*x*x*x'
327
+ assert cc(x**4 + x**2) == 'x*x + x*x*x*x'
328
+ assert cc(x**5 + x**4) == 'pow(x, 5) + x*x*x*x'
329
+ assert cc(sin(x)**4) == 'pow(sin(x), 4)'
330
+ # gh issue 15335
331
+ assert cc(x**(-4)) == '1.0/(x*x*x*x)'
332
+ assert cc(x**(-5)) == 'pow(x, -5)'
333
+ assert cc(-x**4) == '-(x*x*x*x)'
334
+ assert cc(x**4 - x**2) == '-(x*x) + x*x*x*x'
335
+ i = Symbol('i', integer=True)
336
+ assert cc(x**i - x**2) == 'pow(x, i) - (x*x)'
337
+ y = Symbol('y', real=True)
338
+ assert cc(Abs(exp(y**4))) == "exp(y*y*y*y)"
339
+
340
+ # gh issue 20753
341
+ cc2 = lambda x: ccode(optimize(x, [create_expand_pow_optimization(
342
+ 4, base_req=lambda b: b.is_Function)]))
343
+ assert cc2(x**3 + sin(x)**3) == "pow(x, 3) + sin(x)*sin(x)*sin(x)"
344
+
345
+
346
+ def test_matsolve():
347
+ n = Symbol('n', integer=True)
348
+ A = MatrixSymbol('A', n, n)
349
+ x = MatrixSymbol('x', n, 1)
350
+
351
+ with assuming(Q.fullrank(A)):
352
+ assert optimize(A**(-1) * x, [matinv_opt]) == MatrixSolve(A, x)
353
+ assert optimize(A**(-1) * x + x, [matinv_opt]) == MatrixSolve(A, x) + x
354
+
355
+
356
+ def test_logaddexp_opt():
357
+ x, y = map(Symbol, 'x y'.split())
358
+ expr1 = log(exp(x) + exp(y))
359
+ opt1 = optimize(expr1, [logaddexp_opt])
360
+ assert logaddexp(x, y) - opt1 == 0
361
+ assert logaddexp(y, x) - opt1 == 0
362
+ assert opt1.rewrite(log) == expr1
363
+
364
+
365
+ def test_logaddexp2_opt():
366
+ x, y = map(Symbol, 'x y'.split())
367
+ expr1 = log(2**x + 2**y)/log(2)
368
+ opt1 = optimize(expr1, [logaddexp2_opt])
369
+ assert logaddexp2(x, y) - opt1 == 0
370
+ assert logaddexp2(y, x) - opt1 == 0
371
+ assert opt1.rewrite(log) == expr1
372
+
373
+
374
+ def test_sinc_opts():
375
+ def check(d):
376
+ for k, v in d.items():
377
+ assert optimize(k, sinc_opts) == v
378
+
379
+ x = Symbol('x')
380
+ check({
381
+ sin(x)/x : sinc(x),
382
+ sin(2*x)/(2*x) : sinc(2*x),
383
+ sin(3*x)/x : 3*sinc(3*x),
384
+ x*sin(x) : x*sin(x)
385
+ })
386
+
387
+ y = Symbol('y')
388
+ check({
389
+ sin(x*y)/(x*y) : sinc(x*y),
390
+ y*sin(x/y)/x : sinc(x/y),
391
+ sin(sin(x))/sin(x) : sinc(sin(x)),
392
+ sin(3*sin(x))/sin(x) : 3*sinc(3*sin(x)),
393
+ sin(x)/y : sin(x)/y
394
+ })
395
+
396
+
397
+ def test_optims_numpy():
398
+ def check(d):
399
+ for k, v in d.items():
400
+ assert optimize(k, optims_numpy) == v
401
+
402
+ x = Symbol('x')
403
+ check({
404
+ sin(2*x)/(2*x) + exp(2*x) - 1: sinc(2*x) + expm1(2*x),
405
+ log(x+3)/log(2) + log(x**2 + 1): log1p(x**2) + log2(x+3)
406
+ })
407
+
408
+
409
+ @XFAIL # room for improvement, ideally this test case should pass.
410
+ def test_optims_numpy_TODO():
411
+ def check(d):
412
+ for k, v in d.items():
413
+ assert optimize(k, optims_numpy) == v
414
+
415
+ x, y = map(Symbol, 'x y'.split())
416
+ check({
417
+ log(x*y)*sin(x*y)*log(x*y+1)/(log(2)*x*y): log2(x*y)*sinc(x*y)*log1p(x*y),
418
+ exp(x*sin(y)/y) - 1: expm1(x*sinc(y))
419
+ })
420
+
421
+
422
+ @may_xfail
423
+ def test_compiled_ccode_with_rewriting():
424
+ if not cython:
425
+ skip("cython not installed.")
426
+ if not has_c():
427
+ skip("No C compiler found.")
428
+
429
+ x = Symbol('x')
430
+ about_two = 2**(58/S(117))*3**(97/S(117))*5**(4/S(39))*7**(92/S(117))/S(30)*pi
431
+ # about_two: 1.999999999999581826
432
+ unchanged = 2*exp(x) - about_two
433
+ xval = S(10)**-11
434
+ ref = unchanged.subs(x, xval).n(19) # 2.0418173913673213e-11
435
+
436
+ rewritten = optimize(2*exp(x) - about_two, [expm1_opt])
437
+
438
+ # Unfortunately, we need to call ``.n()`` on our expressions before we hand them
439
+ # to ``ccode``, and we need to request a large number of significant digits.
440
+ # In this test, results converged for double precision when the following number
441
+ # of significant digits were chosen:
442
+ NUMBER_OF_DIGITS = 25 # TODO: this should ideally be automatically handled.
443
+
444
+ func_c = '''
445
+ #include <math.h>
446
+
447
+ double func_unchanged(double x) {
448
+ return %(unchanged)s;
449
+ }
450
+ double func_rewritten(double x) {
451
+ return %(rewritten)s;
452
+ }
453
+ ''' % {"unchanged": ccode(unchanged.n(NUMBER_OF_DIGITS)),
454
+ "rewritten": ccode(rewritten.n(NUMBER_OF_DIGITS))}
455
+
456
+ func_pyx = '''
457
+ #cython: language_level=3
458
+ cdef extern double func_unchanged(double)
459
+ cdef extern double func_rewritten(double)
460
+ def py_unchanged(x):
461
+ return func_unchanged(x)
462
+ def py_rewritten(x):
463
+ return func_rewritten(x)
464
+ '''
465
+ with tempfile.TemporaryDirectory() as folder:
466
+ mod, info = compile_link_import_strings(
467
+ [('func.c', func_c), ('_func.pyx', func_pyx)],
468
+ build_dir=folder, compile_kwargs={"std": 'c99'}
469
+ )
470
+ err_rewritten = abs(mod.py_rewritten(1e-11) - ref)
471
+ err_unchanged = abs(mod.py_unchanged(1e-11) - ref)
472
+ assert 1e-27 < err_rewritten < 1e-25 # highly accurate.
473
+ assert 1e-19 < err_unchanged < 1e-16 # quite poor.
474
+
475
+ # Tolerances used above were determined as follows:
476
+ # >>> no_opt = unchanged.subs(x, xval.evalf()).evalf()
477
+ # >>> with_opt = rewritten.n(25).subs(x, 1e-11).evalf()
478
+ # >>> with_opt - ref, no_opt - ref
479
+ # (1.1536301877952077e-26, 1.6547074214222335e-18)
.venv/lib/python3.13/site-packages/sympy/codegen/tests/test_scipy_nodes.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from itertools import product
2
+ from sympy.core.power import Pow
3
+ from sympy.core.symbol import symbols
4
+ from sympy.functions.elementary.exponential import exp, log
5
+ from sympy.functions.elementary.trigonometric import cos
6
+ from sympy.core.numbers import pi
7
+ from sympy.codegen.scipy_nodes import cosm1, powm1
8
+
9
+ x, y, z = symbols('x y z')
10
+
11
+
12
+ def test_cosm1():
13
+ cm1_xy = cosm1(x*y)
14
+ ref_xy = cos(x*y) - 1
15
+ for wrt, deriv_order in product([x, y, z], range(3)):
16
+ assert (
17
+ cm1_xy.diff(wrt, deriv_order) -
18
+ ref_xy.diff(wrt, deriv_order)
19
+ ).rewrite(cos).simplify() == 0
20
+
21
+ expr_minus2 = cosm1(pi)
22
+ assert expr_minus2.rewrite(cos) == -2
23
+ assert cosm1(3.14).simplify() == cosm1(3.14) # cannot simplify with 3.14
24
+ assert cosm1(pi/2).simplify() == -1
25
+ assert (1/cos(x) - 1 + cosm1(x)/cos(x)).simplify() == 0
26
+
27
+
28
+ def test_powm1():
29
+ cases = {
30
+ powm1(x, y): x**y - 1,
31
+ powm1(x*y, z): (x*y)**z - 1,
32
+ powm1(x, y*z): x**(y*z)-1,
33
+ powm1(x*y*z, x*y*z): (x*y*z)**(x*y*z)-1
34
+ }
35
+ for pm1_e, ref_e in cases.items():
36
+ for wrt, deriv_order in product([x, y, z], range(3)):
37
+ der = pm1_e.diff(wrt, deriv_order)
38
+ ref = ref_e.diff(wrt, deriv_order)
39
+ delta = (der - ref).rewrite(Pow)
40
+ assert delta.simplify() == 0
41
+
42
+ eulers_constant_m1 = powm1(x, 1/log(x))
43
+ assert eulers_constant_m1.rewrite(Pow) == exp(1) - 1
44
+ assert eulers_constant_m1.simplify() == exp(1) - 1
.venv/lib/python3.13/site-packages/sympy/combinatorics/tests/__init__.py ADDED
File without changes
.venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_coset_table.py ADDED
@@ -0,0 +1,825 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.combinatorics.fp_groups import FpGroup
2
+ from sympy.combinatorics.coset_table import (CosetTable,
3
+ coset_enumeration_r, coset_enumeration_c)
4
+ from sympy.combinatorics.coset_table import modified_coset_enumeration_r
5
+ from sympy.combinatorics.free_groups import free_group
6
+
7
+ from sympy.testing.pytest import slow
8
+
9
+ """
10
+ References
11
+ ==========
12
+
13
+ [1] Holt, D., Eick, B., O'Brien, E.
14
+ "Handbook of Computational Group Theory"
15
+
16
+ [2] John J. Cannon; Lucien A. Dimino; George Havas; Jane M. Watson
17
+ Mathematics of Computation, Vol. 27, No. 123. (Jul., 1973), pp. 463-490.
18
+ "Implementation and Analysis of the Todd-Coxeter Algorithm"
19
+
20
+ """
21
+
22
+ def test_scan_1():
23
+ # Example 5.1 from [1]
24
+ F, x, y = free_group("x, y")
25
+ f = FpGroup(F, [x**3, y**3, x**-1*y**-1*x*y])
26
+ c = CosetTable(f, [x])
27
+
28
+ c.scan_and_fill(0, x)
29
+ assert c.table == [[0, 0, None, None]]
30
+ assert c.p == [0]
31
+ assert c.n == 1
32
+ assert c.omega == [0]
33
+
34
+ c.scan_and_fill(0, x**3)
35
+ assert c.table == [[0, 0, None, None]]
36
+ assert c.p == [0]
37
+ assert c.n == 1
38
+ assert c.omega == [0]
39
+
40
+ c.scan_and_fill(0, y**3)
41
+ assert c.table == [[0, 0, 1, 2], [None, None, 2, 0], [None, None, 0, 1]]
42
+ assert c.p == [0, 1, 2]
43
+ assert c.n == 3
44
+ assert c.omega == [0, 1, 2]
45
+
46
+ c.scan_and_fill(0, x**-1*y**-1*x*y)
47
+ assert c.table == [[0, 0, 1, 2], [None, None, 2, 0], [2, 2, 0, 1]]
48
+ assert c.p == [0, 1, 2]
49
+ assert c.n == 3
50
+ assert c.omega == [0, 1, 2]
51
+
52
+ c.scan_and_fill(1, x**3)
53
+ assert c.table == [[0, 0, 1, 2], [3, 4, 2, 0], [2, 2, 0, 1], \
54
+ [4, 1, None, None], [1, 3, None, None]]
55
+ assert c.p == [0, 1, 2, 3, 4]
56
+ assert c.n == 5
57
+ assert c.omega == [0, 1, 2, 3, 4]
58
+
59
+ c.scan_and_fill(1, y**3)
60
+ assert c.table == [[0, 0, 1, 2], [3, 4, 2, 0], [2, 2, 0, 1], \
61
+ [4, 1, None, None], [1, 3, None, None]]
62
+ assert c.p == [0, 1, 2, 3, 4]
63
+ assert c.n == 5
64
+ assert c.omega == [0, 1, 2, 3, 4]
65
+
66
+ c.scan_and_fill(1, x**-1*y**-1*x*y)
67
+ assert c.table == [[0, 0, 1, 2], [1, 1, 2, 0], [2, 2, 0, 1], \
68
+ [None, 1, None, None], [1, 3, None, None]]
69
+ assert c.p == [0, 1, 2, 1, 1]
70
+ assert c.n == 3
71
+ assert c.omega == [0, 1, 2]
72
+
73
+ # Example 5.2 from [1]
74
+ f = FpGroup(F, [x**2, y**3, (x*y)**3])
75
+ c = CosetTable(f, [x*y])
76
+
77
+ c.scan_and_fill(0, x*y)
78
+ assert c.table == [[1, None, None, 1], [None, 0, 0, None]]
79
+ assert c.p == [0, 1]
80
+ assert c.n == 2
81
+ assert c.omega == [0, 1]
82
+
83
+ c.scan_and_fill(0, x**2)
84
+ assert c.table == [[1, 1, None, 1], [0, 0, 0, None]]
85
+ assert c.p == [0, 1]
86
+ assert c.n == 2
87
+ assert c.omega == [0, 1]
88
+
89
+ c.scan_and_fill(0, y**3)
90
+ assert c.table == [[1, 1, 2, 1], [0, 0, 0, 2], [None, None, 1, 0]]
91
+ assert c.p == [0, 1, 2]
92
+ assert c.n == 3
93
+ assert c.omega == [0, 1, 2]
94
+
95
+ c.scan_and_fill(0, (x*y)**3)
96
+ assert c.table == [[1, 1, 2, 1], [0, 0, 0, 2], [None, None, 1, 0]]
97
+ assert c.p == [0, 1, 2]
98
+ assert c.n == 3
99
+ assert c.omega == [0, 1, 2]
100
+
101
+ c.scan_and_fill(1, x**2)
102
+ assert c.table == [[1, 1, 2, 1], [0, 0, 0, 2], [None, None, 1, 0]]
103
+ assert c.p == [0, 1, 2]
104
+ assert c.n == 3
105
+ assert c.omega == [0, 1, 2]
106
+
107
+ c.scan_and_fill(1, y**3)
108
+ assert c.table == [[1, 1, 2, 1], [0, 0, 0, 2], [None, None, 1, 0]]
109
+ assert c.p == [0, 1, 2]
110
+ assert c.n == 3
111
+ assert c.omega == [0, 1, 2]
112
+
113
+ c.scan_and_fill(1, (x*y)**3)
114
+ assert c.table == [[1, 1, 2, 1], [0, 0, 0, 2], [3, 4, 1, 0], [None, 2, 4, None], [2, None, None, 3]]
115
+ assert c.p == [0, 1, 2, 3, 4]
116
+ assert c.n == 5
117
+ assert c.omega == [0, 1, 2, 3, 4]
118
+
119
+ c.scan_and_fill(2, x**2)
120
+ assert c.table == [[1, 1, 2, 1], [0, 0, 0, 2], [3, 3, 1, 0], [2, 2, 3, 3], [2, None, None, 3]]
121
+ assert c.p == [0, 1, 2, 3, 3]
122
+ assert c.n == 4
123
+ assert c.omega == [0, 1, 2, 3]
124
+
125
+
126
+ @slow
127
+ def test_coset_enumeration():
128
+ # this test function contains the combined tests for the two strategies
129
+ # i.e. HLT and Felsch strategies.
130
+
131
+ # Example 5.1 from [1]
132
+ F, x, y = free_group("x, y")
133
+ f = FpGroup(F, [x**3, y**3, x**-1*y**-1*x*y])
134
+ C_r = coset_enumeration_r(f, [x])
135
+ C_r.compress(); C_r.standardize()
136
+ C_c = coset_enumeration_c(f, [x])
137
+ C_c.compress(); C_c.standardize()
138
+ table1 = [[0, 0, 1, 2], [1, 1, 2, 0], [2, 2, 0, 1]]
139
+ assert C_r.table == table1
140
+ assert C_c.table == table1
141
+
142
+ # E1 from [2] Pg. 474
143
+ F, r, s, t = free_group("r, s, t")
144
+ E1 = FpGroup(F, [t**-1*r*t*r**-2, r**-1*s*r*s**-2, s**-1*t*s*t**-2])
145
+ C_r = coset_enumeration_r(E1, [])
146
+ C_r.compress()
147
+ C_c = coset_enumeration_c(E1, [])
148
+ C_c.compress()
149
+ table2 = [[0, 0, 0, 0, 0, 0]]
150
+ assert C_r.table == table2
151
+ # test for issue #11449
152
+ assert C_c.table == table2
153
+
154
+ # Cox group from [2] Pg. 474
155
+ F, a, b = free_group("a, b")
156
+ Cox = FpGroup(F, [a**6, b**6, (a*b)**2, (a**2*b**2)**2, (a**3*b**3)**5])
157
+ C_r = coset_enumeration_r(Cox, [a])
158
+ C_r.compress(); C_r.standardize()
159
+ C_c = coset_enumeration_c(Cox, [a])
160
+ C_c.compress(); C_c.standardize()
161
+ table3 = [[0, 0, 1, 2],
162
+ [2, 3, 4, 0],
163
+ [5, 1, 0, 6],
164
+ [1, 7, 8, 9],
165
+ [9, 10, 11, 1],
166
+ [12, 2, 9, 13],
167
+ [14, 9, 2, 11],
168
+ [3, 12, 15, 16],
169
+ [16, 17, 18, 3],
170
+ [6, 4, 3, 5],
171
+ [4, 19, 20, 21],
172
+ [21, 22, 6, 4],
173
+ [7, 5, 23, 24],
174
+ [25, 23, 5, 18],
175
+ [19, 6, 22, 26],
176
+ [24, 27, 28, 7],
177
+ [29, 8, 7, 30],
178
+ [8, 31, 32, 33],
179
+ [33, 34, 13, 8],
180
+ [10, 14, 35, 35],
181
+ [35, 36, 37, 10],
182
+ [30, 11, 10, 29],
183
+ [11, 38, 39, 14],
184
+ [13, 39, 38, 12],
185
+ [40, 15, 12, 41],
186
+ [42, 13, 34, 43],
187
+ [44, 35, 14, 45],
188
+ [15, 46, 47, 34],
189
+ [34, 48, 49, 15],
190
+ [50, 16, 21, 51],
191
+ [52, 21, 16, 49],
192
+ [17, 50, 53, 54],
193
+ [54, 55, 56, 17],
194
+ [41, 18, 17, 40],
195
+ [18, 28, 27, 25],
196
+ [26, 20, 19, 19],
197
+ [20, 57, 58, 59],
198
+ [59, 60, 51, 20],
199
+ [22, 52, 61, 23],
200
+ [23, 62, 63, 22],
201
+ [64, 24, 33, 65],
202
+ [48, 33, 24, 61],
203
+ [62, 25, 54, 66],
204
+ [67, 54, 25, 68],
205
+ [57, 26, 59, 69],
206
+ [70, 59, 26, 63],
207
+ [27, 64, 71, 72],
208
+ [72, 73, 68, 27],
209
+ [28, 41, 74, 75],
210
+ [75, 76, 30, 28],
211
+ [31, 29, 77, 78],
212
+ [79, 77, 29, 37],
213
+ [38, 30, 76, 80],
214
+ [78, 81, 82, 31],
215
+ [43, 32, 31, 42],
216
+ [32, 83, 84, 85],
217
+ [85, 86, 65, 32],
218
+ [36, 44, 87, 88],
219
+ [88, 89, 90, 36],
220
+ [45, 37, 36, 44],
221
+ [37, 82, 81, 79],
222
+ [80, 74, 41, 38],
223
+ [39, 42, 91, 92],
224
+ [92, 93, 45, 39],
225
+ [46, 40, 94, 95],
226
+ [96, 94, 40, 56],
227
+ [97, 91, 42, 82],
228
+ [83, 43, 98, 99],
229
+ [100, 98, 43, 47],
230
+ [101, 87, 44, 90],
231
+ [82, 45, 93, 97],
232
+ [95, 102, 103, 46],
233
+ [104, 47, 46, 105],
234
+ [47, 106, 107, 100],
235
+ [61, 108, 109, 48],
236
+ [105, 49, 48, 104],
237
+ [49, 110, 111, 52],
238
+ [51, 111, 110, 50],
239
+ [112, 53, 50, 113],
240
+ [114, 51, 60, 115],
241
+ [116, 61, 52, 117],
242
+ [53, 118, 119, 60],
243
+ [60, 70, 66, 53],
244
+ [55, 67, 120, 121],
245
+ [121, 122, 123, 55],
246
+ [113, 56, 55, 112],
247
+ [56, 103, 102, 96],
248
+ [69, 124, 125, 57],
249
+ [115, 58, 57, 114],
250
+ [58, 126, 127, 128],
251
+ [128, 128, 69, 58],
252
+ [66, 129, 130, 62],
253
+ [117, 63, 62, 116],
254
+ [63, 125, 124, 70],
255
+ [65, 109, 108, 64],
256
+ [131, 71, 64, 132],
257
+ [133, 65, 86, 134],
258
+ [135, 66, 70, 136],
259
+ [68, 130, 129, 67],
260
+ [137, 120, 67, 138],
261
+ [132, 68, 73, 131],
262
+ [139, 69, 128, 140],
263
+ [71, 141, 142, 86],
264
+ [86, 143, 144, 71],
265
+ [145, 72, 75, 146],
266
+ [147, 75, 72, 144],
267
+ [73, 145, 148, 120],
268
+ [120, 149, 150, 73],
269
+ [74, 151, 152, 94],
270
+ [94, 153, 146, 74],
271
+ [76, 147, 154, 77],
272
+ [77, 155, 156, 76],
273
+ [157, 78, 85, 158],
274
+ [143, 85, 78, 154],
275
+ [155, 79, 88, 159],
276
+ [160, 88, 79, 161],
277
+ [151, 80, 92, 162],
278
+ [163, 92, 80, 156],
279
+ [81, 157, 164, 165],
280
+ [165, 166, 161, 81],
281
+ [99, 107, 106, 83],
282
+ [134, 84, 83, 133],
283
+ [84, 167, 168, 169],
284
+ [169, 170, 158, 84],
285
+ [87, 171, 172, 93],
286
+ [93, 163, 159, 87],
287
+ [89, 160, 173, 174],
288
+ [174, 175, 176, 89],
289
+ [90, 90, 89, 101],
290
+ [91, 177, 178, 98],
291
+ [98, 179, 162, 91],
292
+ [180, 95, 100, 181],
293
+ [179, 100, 95, 152],
294
+ [153, 96, 121, 148],
295
+ [182, 121, 96, 183],
296
+ [177, 97, 165, 184],
297
+ [185, 165, 97, 172],
298
+ [186, 99, 169, 187],
299
+ [188, 169, 99, 178],
300
+ [171, 101, 174, 189],
301
+ [190, 174, 101, 176],
302
+ [102, 180, 191, 192],
303
+ [192, 193, 183, 102],
304
+ [103, 113, 194, 195],
305
+ [195, 196, 105, 103],
306
+ [106, 104, 197, 198],
307
+ [199, 197, 104, 109],
308
+ [110, 105, 196, 200],
309
+ [198, 201, 133, 106],
310
+ [107, 186, 202, 203],
311
+ [203, 204, 181, 107],
312
+ [108, 116, 205, 206],
313
+ [206, 207, 132, 108],
314
+ [109, 133, 201, 199],
315
+ [200, 194, 113, 110],
316
+ [111, 114, 208, 209],
317
+ [209, 210, 117, 111],
318
+ [118, 112, 211, 212],
319
+ [213, 211, 112, 123],
320
+ [214, 208, 114, 125],
321
+ [126, 115, 215, 216],
322
+ [217, 215, 115, 119],
323
+ [218, 205, 116, 130],
324
+ [125, 117, 210, 214],
325
+ [212, 219, 220, 118],
326
+ [136, 119, 118, 135],
327
+ [119, 221, 222, 217],
328
+ [122, 182, 223, 224],
329
+ [224, 225, 226, 122],
330
+ [138, 123, 122, 137],
331
+ [123, 220, 219, 213],
332
+ [124, 139, 227, 228],
333
+ [228, 229, 136, 124],
334
+ [216, 222, 221, 126],
335
+ [140, 127, 126, 139],
336
+ [127, 230, 231, 232],
337
+ [232, 233, 140, 127],
338
+ [129, 135, 234, 235],
339
+ [235, 236, 138, 129],
340
+ [130, 132, 207, 218],
341
+ [141, 131, 237, 238],
342
+ [239, 237, 131, 150],
343
+ [167, 134, 240, 241],
344
+ [242, 240, 134, 142],
345
+ [243, 234, 135, 220],
346
+ [221, 136, 229, 244],
347
+ [149, 137, 245, 246],
348
+ [247, 245, 137, 226],
349
+ [220, 138, 236, 243],
350
+ [244, 227, 139, 221],
351
+ [230, 140, 233, 248],
352
+ [238, 249, 250, 141],
353
+ [251, 142, 141, 252],
354
+ [142, 253, 254, 242],
355
+ [154, 255, 256, 143],
356
+ [252, 144, 143, 251],
357
+ [144, 257, 258, 147],
358
+ [146, 258, 257, 145],
359
+ [259, 148, 145, 260],
360
+ [261, 146, 153, 262],
361
+ [263, 154, 147, 264],
362
+ [148, 265, 266, 153],
363
+ [246, 267, 268, 149],
364
+ [260, 150, 149, 259],
365
+ [150, 250, 249, 239],
366
+ [162, 269, 270, 151],
367
+ [262, 152, 151, 261],
368
+ [152, 271, 272, 179],
369
+ [159, 273, 274, 155],
370
+ [264, 156, 155, 263],
371
+ [156, 270, 269, 163],
372
+ [158, 256, 255, 157],
373
+ [275, 164, 157, 276],
374
+ [277, 158, 170, 278],
375
+ [279, 159, 163, 280],
376
+ [161, 274, 273, 160],
377
+ [281, 173, 160, 282],
378
+ [276, 161, 166, 275],
379
+ [283, 162, 179, 284],
380
+ [164, 285, 286, 170],
381
+ [170, 188, 184, 164],
382
+ [166, 185, 189, 173],
383
+ [173, 287, 288, 166],
384
+ [241, 254, 253, 167],
385
+ [278, 168, 167, 277],
386
+ [168, 289, 290, 291],
387
+ [291, 292, 187, 168],
388
+ [189, 293, 294, 171],
389
+ [280, 172, 171, 279],
390
+ [172, 295, 296, 185],
391
+ [175, 190, 297, 297],
392
+ [297, 298, 299, 175],
393
+ [282, 176, 175, 281],
394
+ [176, 294, 293, 190],
395
+ [184, 296, 295, 177],
396
+ [284, 178, 177, 283],
397
+ [178, 300, 301, 188],
398
+ [181, 272, 271, 180],
399
+ [302, 191, 180, 303],
400
+ [304, 181, 204, 305],
401
+ [183, 266, 265, 182],
402
+ [306, 223, 182, 307],
403
+ [303, 183, 193, 302],
404
+ [308, 184, 188, 309],
405
+ [310, 189, 185, 311],
406
+ [187, 301, 300, 186],
407
+ [305, 202, 186, 304],
408
+ [312, 187, 292, 313],
409
+ [314, 297, 190, 315],
410
+ [191, 316, 317, 204],
411
+ [204, 318, 319, 191],
412
+ [320, 192, 195, 321],
413
+ [322, 195, 192, 319],
414
+ [193, 320, 323, 223],
415
+ [223, 324, 325, 193],
416
+ [194, 326, 327, 211],
417
+ [211, 328, 321, 194],
418
+ [196, 322, 329, 197],
419
+ [197, 330, 331, 196],
420
+ [332, 198, 203, 333],
421
+ [318, 203, 198, 329],
422
+ [330, 199, 206, 334],
423
+ [335, 206, 199, 336],
424
+ [326, 200, 209, 337],
425
+ [338, 209, 200, 331],
426
+ [201, 332, 339, 240],
427
+ [240, 340, 336, 201],
428
+ [202, 341, 342, 292],
429
+ [292, 343, 333, 202],
430
+ [205, 344, 345, 210],
431
+ [210, 338, 334, 205],
432
+ [207, 335, 346, 237],
433
+ [237, 347, 348, 207],
434
+ [208, 349, 350, 215],
435
+ [215, 351, 337, 208],
436
+ [352, 212, 217, 353],
437
+ [351, 217, 212, 327],
438
+ [328, 213, 224, 323],
439
+ [354, 224, 213, 355],
440
+ [349, 214, 228, 356],
441
+ [357, 228, 214, 345],
442
+ [358, 216, 232, 359],
443
+ [360, 232, 216, 350],
444
+ [344, 218, 235, 361],
445
+ [362, 235, 218, 348],
446
+ [219, 352, 363, 364],
447
+ [364, 365, 355, 219],
448
+ [222, 358, 366, 367],
449
+ [367, 368, 353, 222],
450
+ [225, 354, 369, 370],
451
+ [370, 371, 372, 225],
452
+ [307, 226, 225, 306],
453
+ [226, 268, 267, 247],
454
+ [227, 373, 374, 233],
455
+ [233, 360, 356, 227],
456
+ [229, 357, 361, 234],
457
+ [234, 375, 376, 229],
458
+ [248, 231, 230, 230],
459
+ [231, 377, 378, 379],
460
+ [379, 380, 359, 231],
461
+ [236, 362, 381, 245],
462
+ [245, 382, 383, 236],
463
+ [384, 238, 242, 385],
464
+ [340, 242, 238, 346],
465
+ [347, 239, 246, 381],
466
+ [386, 246, 239, 387],
467
+ [388, 241, 291, 389],
468
+ [343, 291, 241, 339],
469
+ [375, 243, 364, 390],
470
+ [391, 364, 243, 383],
471
+ [373, 244, 367, 392],
472
+ [393, 367, 244, 376],
473
+ [382, 247, 370, 394],
474
+ [395, 370, 247, 396],
475
+ [377, 248, 379, 397],
476
+ [398, 379, 248, 374],
477
+ [249, 384, 399, 400],
478
+ [400, 401, 387, 249],
479
+ [250, 260, 402, 403],
480
+ [403, 404, 252, 250],
481
+ [253, 251, 405, 406],
482
+ [407, 405, 251, 256],
483
+ [257, 252, 404, 408],
484
+ [406, 409, 277, 253],
485
+ [254, 388, 410, 411],
486
+ [411, 412, 385, 254],
487
+ [255, 263, 413, 414],
488
+ [414, 415, 276, 255],
489
+ [256, 277, 409, 407],
490
+ [408, 402, 260, 257],
491
+ [258, 261, 416, 417],
492
+ [417, 418, 264, 258],
493
+ [265, 259, 419, 420],
494
+ [421, 419, 259, 268],
495
+ [422, 416, 261, 270],
496
+ [271, 262, 423, 424],
497
+ [425, 423, 262, 266],
498
+ [426, 413, 263, 274],
499
+ [270, 264, 418, 422],
500
+ [420, 427, 307, 265],
501
+ [266, 303, 428, 425],
502
+ [267, 386, 429, 430],
503
+ [430, 431, 396, 267],
504
+ [268, 307, 427, 421],
505
+ [269, 283, 432, 433],
506
+ [433, 434, 280, 269],
507
+ [424, 428, 303, 271],
508
+ [272, 304, 435, 436],
509
+ [436, 437, 284, 272],
510
+ [273, 279, 438, 439],
511
+ [439, 440, 282, 273],
512
+ [274, 276, 415, 426],
513
+ [285, 275, 441, 442],
514
+ [443, 441, 275, 288],
515
+ [289, 278, 444, 445],
516
+ [446, 444, 278, 286],
517
+ [447, 438, 279, 294],
518
+ [295, 280, 434, 448],
519
+ [287, 281, 449, 450],
520
+ [451, 449, 281, 299],
521
+ [294, 282, 440, 447],
522
+ [448, 432, 283, 295],
523
+ [300, 284, 437, 452],
524
+ [442, 453, 454, 285],
525
+ [309, 286, 285, 308],
526
+ [286, 455, 456, 446],
527
+ [450, 457, 458, 287],
528
+ [311, 288, 287, 310],
529
+ [288, 454, 453, 443],
530
+ [445, 456, 455, 289],
531
+ [313, 290, 289, 312],
532
+ [290, 459, 460, 461],
533
+ [461, 462, 389, 290],
534
+ [293, 310, 463, 464],
535
+ [464, 465, 315, 293],
536
+ [296, 308, 466, 467],
537
+ [467, 468, 311, 296],
538
+ [298, 314, 469, 470],
539
+ [470, 471, 472, 298],
540
+ [315, 299, 298, 314],
541
+ [299, 458, 457, 451],
542
+ [452, 435, 304, 300],
543
+ [301, 312, 473, 474],
544
+ [474, 475, 309, 301],
545
+ [316, 302, 476, 477],
546
+ [478, 476, 302, 325],
547
+ [341, 305, 479, 480],
548
+ [481, 479, 305, 317],
549
+ [324, 306, 482, 483],
550
+ [484, 482, 306, 372],
551
+ [485, 466, 308, 454],
552
+ [455, 309, 475, 486],
553
+ [487, 463, 310, 458],
554
+ [454, 311, 468, 485],
555
+ [486, 473, 312, 455],
556
+ [459, 313, 488, 489],
557
+ [490, 488, 313, 342],
558
+ [491, 469, 314, 472],
559
+ [458, 315, 465, 487],
560
+ [477, 492, 485, 316],
561
+ [463, 317, 316, 468],
562
+ [317, 487, 493, 481],
563
+ [329, 447, 464, 318],
564
+ [468, 319, 318, 463],
565
+ [319, 467, 448, 322],
566
+ [321, 448, 467, 320],
567
+ [475, 323, 320, 466],
568
+ [432, 321, 328, 437],
569
+ [438, 329, 322, 434],
570
+ [323, 474, 452, 328],
571
+ [483, 494, 486, 324],
572
+ [466, 325, 324, 475],
573
+ [325, 485, 492, 478],
574
+ [337, 422, 433, 326],
575
+ [437, 327, 326, 432],
576
+ [327, 436, 424, 351],
577
+ [334, 426, 439, 330],
578
+ [434, 331, 330, 438],
579
+ [331, 433, 422, 338],
580
+ [333, 464, 447, 332],
581
+ [449, 339, 332, 440],
582
+ [465, 333, 343, 469],
583
+ [413, 334, 338, 418],
584
+ [336, 439, 426, 335],
585
+ [441, 346, 335, 415],
586
+ [440, 336, 340, 449],
587
+ [416, 337, 351, 423],
588
+ [339, 451, 470, 343],
589
+ [346, 443, 450, 340],
590
+ [480, 493, 487, 341],
591
+ [469, 342, 341, 465],
592
+ [342, 491, 495, 490],
593
+ [361, 407, 414, 344],
594
+ [418, 345, 344, 413],
595
+ [345, 417, 408, 357],
596
+ [381, 446, 442, 347],
597
+ [415, 348, 347, 441],
598
+ [348, 414, 407, 362],
599
+ [356, 408, 417, 349],
600
+ [423, 350, 349, 416],
601
+ [350, 425, 420, 360],
602
+ [353, 424, 436, 352],
603
+ [479, 363, 352, 435],
604
+ [428, 353, 368, 476],
605
+ [355, 452, 474, 354],
606
+ [488, 369, 354, 473],
607
+ [435, 355, 365, 479],
608
+ [402, 356, 360, 419],
609
+ [405, 361, 357, 404],
610
+ [359, 420, 425, 358],
611
+ [476, 366, 358, 428],
612
+ [427, 359, 380, 482],
613
+ [444, 381, 362, 409],
614
+ [363, 481, 477, 368],
615
+ [368, 393, 390, 363],
616
+ [365, 391, 394, 369],
617
+ [369, 490, 480, 365],
618
+ [366, 478, 483, 380],
619
+ [380, 398, 392, 366],
620
+ [371, 395, 496, 497],
621
+ [497, 498, 489, 371],
622
+ [473, 372, 371, 488],
623
+ [372, 486, 494, 484],
624
+ [392, 400, 403, 373],
625
+ [419, 374, 373, 402],
626
+ [374, 421, 430, 398],
627
+ [390, 411, 406, 375],
628
+ [404, 376, 375, 405],
629
+ [376, 403, 400, 393],
630
+ [397, 430, 421, 377],
631
+ [482, 378, 377, 427],
632
+ [378, 484, 497, 499],
633
+ [499, 499, 397, 378],
634
+ [394, 461, 445, 382],
635
+ [409, 383, 382, 444],
636
+ [383, 406, 411, 391],
637
+ [385, 450, 443, 384],
638
+ [492, 399, 384, 453],
639
+ [457, 385, 412, 493],
640
+ [387, 442, 446, 386],
641
+ [494, 429, 386, 456],
642
+ [453, 387, 401, 492],
643
+ [389, 470, 451, 388],
644
+ [493, 410, 388, 457],
645
+ [471, 389, 462, 495],
646
+ [412, 390, 393, 399],
647
+ [462, 394, 391, 410],
648
+ [401, 392, 398, 429],
649
+ [396, 445, 461, 395],
650
+ [498, 496, 395, 460],
651
+ [456, 396, 431, 494],
652
+ [431, 397, 499, 496],
653
+ [399, 477, 481, 412],
654
+ [429, 483, 478, 401],
655
+ [410, 480, 490, 462],
656
+ [496, 497, 484, 431],
657
+ [489, 495, 491, 459],
658
+ [495, 460, 459, 471],
659
+ [460, 489, 498, 498],
660
+ [472, 472, 471, 491]]
661
+
662
+ assert C_r.table == table3
663
+ assert C_c.table == table3
664
+
665
+ # Group denoted by B2,4 from [2] Pg. 474
666
+ F, a, b = free_group("a, b")
667
+ B_2_4 = FpGroup(F, [a**4, b**4, (a*b)**4, (a**-1*b)**4, (a**2*b)**4, \
668
+ (a*b**2)**4, (a**2*b**2)**4, (a**-1*b*a*b)**4, (a*b**-1*a*b)**4])
669
+ C_r = coset_enumeration_r(B_2_4, [a])
670
+ C_c = coset_enumeration_c(B_2_4, [a])
671
+ index_r = 0
672
+ for i in range(len(C_r.p)):
673
+ if C_r.p[i] == i:
674
+ index_r += 1
675
+ assert index_r == 1024
676
+
677
+ index_c = 0
678
+ for i in range(len(C_c.p)):
679
+ if C_c.p[i] == i:
680
+ index_c += 1
681
+ assert index_c == 1024
682
+
683
+ # trivial Macdonald group G(2,2) from [2] Pg. 480
684
+ M = FpGroup(F, [b**-1*a**-1*b*a*b**-1*a*b*a**-2, a**-1*b**-1*a*b*a**-1*b*a*b**-2])
685
+ C_r = coset_enumeration_r(M, [a])
686
+ C_r.compress(); C_r.standardize()
687
+ C_c = coset_enumeration_c(M, [a])
688
+ C_c.compress(); C_c.standardize()
689
+ table4 = [[0, 0, 0, 0]]
690
+ assert C_r.table == table4
691
+ assert C_c.table == table4
692
+
693
+
694
+ def test_look_ahead():
695
+ # Section 3.2 [Test Example] Example (d) from [2]
696
+ F, a, b, c = free_group("a, b, c")
697
+ f = FpGroup(F, [a**11, b**5, c**4, (a*c)**3, b**2*c**-1*b**-1*c, a**4*b**-1*a**-1*b])
698
+ H = [c, b, c**2]
699
+ table0 = [[1, 2, 0, 0, 0, 0],
700
+ [3, 0, 4, 5, 6, 7],
701
+ [0, 8, 9, 10, 11, 12],
702
+ [5, 1, 10, 13, 14, 15],
703
+ [16, 5, 16, 1, 17, 18],
704
+ [4, 3, 1, 8, 19, 20],
705
+ [12, 21, 22, 23, 24, 1],
706
+ [25, 26, 27, 28, 1, 24],
707
+ [2, 10, 5, 16, 22, 28],
708
+ [10, 13, 13, 2, 29, 30]]
709
+ CosetTable.max_stack_size = 10
710
+ C_c = coset_enumeration_c(f, H)
711
+ C_c.compress(); C_c.standardize()
712
+ assert C_c.table[: 10] == table0
713
+
714
+ def test_modified_methods():
715
+ '''
716
+ Tests for modified coset table methods.
717
+ Example 5.7 from [1] Holt, D., Eick, B., O'Brien
718
+ "Handbook of Computational Group Theory".
719
+
720
+ '''
721
+ F, x, y = free_group("x, y")
722
+ f = FpGroup(F, [x**3, y**5, (x*y)**2])
723
+ H = [x*y, x**-1*y**-1*x*y*x]
724
+ C = CosetTable(f, H)
725
+ C.modified_define(0, x)
726
+ identity = C._grp.identity
727
+ a_0 = C._grp.generators[0]
728
+ a_1 = C._grp.generators[1]
729
+
730
+ assert C.P == [[identity, None, None, None],
731
+ [None, identity, None, None]]
732
+ assert C.table == [[1, None, None, None],
733
+ [None, 0, None, None]]
734
+
735
+ C.modified_define(1, x)
736
+ assert C.table == [[1, None, None, None],
737
+ [2, 0, None, None],
738
+ [None, 1, None, None]]
739
+ assert C.P == [[identity, None, None, None],
740
+ [identity, identity, None, None],
741
+ [None, identity, None, None]]
742
+
743
+ C.modified_scan(0, x**3, C._grp.identity, fill=False)
744
+ assert C.P == [[identity, identity, None, None],
745
+ [identity, identity, None, None],
746
+ [identity, identity, None, None]]
747
+ assert C.table == [[1, 2, None, None],
748
+ [2, 0, None, None],
749
+ [0, 1, None, None]]
750
+
751
+ C.modified_scan(0, x*y, C._grp.generators[0], fill=False)
752
+ assert C.P == [[identity, identity, None, a_0**-1],
753
+ [identity, identity, a_0, None],
754
+ [identity, identity, None, None]]
755
+ assert C.table == [[1, 2, None, 1],
756
+ [2, 0, 0, None],
757
+ [0, 1, None, None]]
758
+
759
+ C.modified_define(2, y**-1)
760
+ assert C.table == [[1, 2, None, 1],
761
+ [2, 0, 0, None],
762
+ [0, 1, None, 3],
763
+ [None, None, 2, None]]
764
+ assert C.P == [[identity, identity, None, a_0**-1],
765
+ [identity, identity, a_0, None],
766
+ [identity, identity, None, identity],
767
+ [None, None, identity, None]]
768
+
769
+ C.modified_scan(0, x**-1*y**-1*x*y*x, C._grp.generators[1])
770
+ assert C.table == [[1, 2, None, 1],
771
+ [2, 0, 0, None],
772
+ [0, 1, None, 3],
773
+ [3, 3, 2, None]]
774
+ assert C.P == [[identity, identity, None, a_0**-1],
775
+ [identity, identity, a_0, None],
776
+ [identity, identity, None, identity],
777
+ [a_1, a_1**-1, identity, None]]
778
+
779
+ C.modified_scan(2, (x*y)**2, C._grp.identity)
780
+ assert C.table == [[1, 2, 3, 1],
781
+ [2, 0, 0, None],
782
+ [0, 1, None, 3],
783
+ [3, 3, 2, 0]]
784
+ assert C.P == [[identity, identity, a_1**-1, a_0**-1],
785
+ [identity, identity, a_0, None],
786
+ [identity, identity, None, identity],
787
+ [a_1, a_1**-1, identity, a_1]]
788
+
789
+ C.modified_define(2, y)
790
+ assert C.table == [[1, 2, 3, 1],
791
+ [2, 0, 0, None],
792
+ [0, 1, 4, 3],
793
+ [3, 3, 2, 0],
794
+ [None, None, None, 2]]
795
+ assert C.P == [[identity, identity, a_1**-1, a_0**-1],
796
+ [identity, identity, a_0, None],
797
+ [identity, identity, identity, identity],
798
+ [a_1, a_1**-1, identity, a_1],
799
+ [None, None, None, identity]]
800
+
801
+ C.modified_scan(0, y**5, C._grp.identity)
802
+ assert C.table == [[1, 2, 3, 1], [2, 0, 0, 4], [0, 1, 4, 3], [3, 3, 2, 0], [None, None, 1, 2]]
803
+ assert C.P == [[identity, identity, a_1**-1, a_0**-1],
804
+ [identity, identity, a_0, a_0*a_1**-1],
805
+ [identity, identity, identity, identity],
806
+ [a_1, a_1**-1, identity, a_1],
807
+ [None, None, a_1*a_0**-1, identity]]
808
+
809
+ C.modified_scan(1, (x*y)**2, C._grp.identity)
810
+ assert C.table == [[1, 2, 3, 1],
811
+ [2, 0, 0, 4],
812
+ [0, 1, 4, 3],
813
+ [3, 3, 2, 0],
814
+ [4, 4, 1, 2]]
815
+ assert C.P == [[identity, identity, a_1**-1, a_0**-1],
816
+ [identity, identity, a_0, a_0*a_1**-1],
817
+ [identity, identity, identity, identity],
818
+ [a_1, a_1**-1, identity, a_1],
819
+ [a_0*a_1**-1, a_1*a_0**-1, a_1*a_0**-1, identity]]
820
+
821
+ # Modified coset enumeration test
822
+ f = FpGroup(F, [x**3, y**3, x**-1*y**-1*x*y])
823
+ C = coset_enumeration_r(f, [x])
824
+ C_m = modified_coset_enumeration_r(f, [x])
825
+ assert C_m.table == C.table
.venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_fp_groups.py ADDED
@@ -0,0 +1,257 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core.singleton import S
2
+ from sympy.combinatorics.fp_groups import (FpGroup, low_index_subgroups,
3
+ reidemeister_presentation, FpSubgroup,
4
+ simplify_presentation)
5
+ from sympy.combinatorics.free_groups import (free_group, FreeGroup)
6
+
7
+ from sympy.testing.pytest import slow
8
+
9
+ """
10
+ References
11
+ ==========
12
+
13
+ [1] Holt, D., Eick, B., O'Brien, E.
14
+ "Handbook of Computational Group Theory"
15
+
16
+ [2] John J. Cannon; Lucien A. Dimino; George Havas; Jane M. Watson
17
+ Mathematics of Computation, Vol. 27, No. 123. (Jul., 1973), pp. 463-490.
18
+ "Implementation and Analysis of the Todd-Coxeter Algorithm"
19
+
20
+ [3] PROC. SECOND INTERNAT. CONF. THEORY OF GROUPS, CANBERRA 1973,
21
+ pp. 347-356. "A Reidemeister-Schreier program" by George Havas.
22
+ http://staff.itee.uq.edu.au/havas/1973cdhw.pdf
23
+
24
+ """
25
+
26
+ def test_low_index_subgroups():
27
+ F, x, y = free_group("x, y")
28
+
29
+ # Example 5.10 from [1] Pg. 194
30
+ f = FpGroup(F, [x**2, y**3, (x*y)**4])
31
+ L = low_index_subgroups(f, 4)
32
+ t1 = [[[0, 0, 0, 0]],
33
+ [[0, 0, 1, 2], [1, 1, 2, 0], [3, 3, 0, 1], [2, 2, 3, 3]],
34
+ [[0, 0, 1, 2], [2, 2, 2, 0], [1, 1, 0, 1]],
35
+ [[1, 1, 0, 0], [0, 0, 1, 1]]]
36
+ for i in range(len(t1)):
37
+ assert L[i].table == t1[i]
38
+
39
+ f = FpGroup(F, [x**2, y**3, (x*y)**7])
40
+ L = low_index_subgroups(f, 15)
41
+ t2 = [[[0, 0, 0, 0]],
42
+ [[0, 0, 1, 2], [1, 1, 2, 0], [3, 3, 0, 1], [2, 2, 4, 5],
43
+ [4, 4, 5, 3], [6, 6, 3, 4], [5, 5, 6, 6]],
44
+ [[0, 0, 1, 2], [1, 1, 2, 0], [3, 3, 0, 1], [2, 2, 4, 5],
45
+ [6, 6, 5, 3], [5, 5, 3, 4], [4, 4, 6, 6]],
46
+ [[0, 0, 1, 2], [1, 1, 2, 0], [3, 3, 0, 1], [2, 2, 4, 5],
47
+ [6, 6, 5, 3], [7, 7, 3, 4], [4, 4, 8, 9], [5, 5, 10, 11],
48
+ [11, 11, 9, 6], [9, 9, 6, 8], [12, 12, 11, 7], [8, 8, 7, 10],
49
+ [10, 10, 13, 14], [14, 14, 14, 12], [13, 13, 12, 13]],
50
+ [[0, 0, 1, 2], [1, 1, 2, 0], [3, 3, 0, 1], [2, 2, 4, 5],
51
+ [6, 6, 5, 3], [7, 7, 3, 4], [4, 4, 8, 9], [5, 5, 10, 11],
52
+ [11, 11, 9, 6], [12, 12, 6, 8], [10, 10, 11, 7], [8, 8, 7, 10],
53
+ [9, 9, 13, 14], [14, 14, 14, 12], [13, 13, 12, 13]],
54
+ [[0, 0, 1, 2], [1, 1, 2, 0], [3, 3, 0, 1], [2, 2, 4, 5],
55
+ [6, 6, 5, 3], [7, 7, 3, 4], [4, 4, 8, 9], [5, 5, 10, 11],
56
+ [11, 11, 9, 6], [12, 12, 6, 8], [13, 13, 11, 7], [8, 8, 7, 10],
57
+ [9, 9, 12, 12], [10, 10, 13, 13]],
58
+ [[0, 0, 1, 2], [3, 3, 2, 0], [4, 4, 0, 1], [1, 1, 3, 3], [2, 2, 5, 6]
59
+ , [7, 7, 6, 4], [8, 8, 4, 5], [5, 5, 8, 9], [6, 6, 9, 7],
60
+ [10, 10, 7, 8], [9, 9, 11, 12], [11, 11, 12, 10], [13, 13, 10, 11],
61
+ [12, 12, 13, 13]],
62
+ [[0, 0, 1, 2], [3, 3, 2, 0], [4, 4, 0, 1], [1, 1, 3, 3], [2, 2, 5, 6]
63
+ , [7, 7, 6, 4], [8, 8, 4, 5], [5, 5, 8, 9], [6, 6, 9, 7],
64
+ [10, 10, 7, 8], [9, 9, 11, 12], [13, 13, 12, 10], [12, 12, 10, 11],
65
+ [11, 11, 13, 13]],
66
+ [[0, 0, 1, 2], [3, 3, 2, 0], [4, 4, 0, 1], [1, 1, 5, 6], [2, 2, 4, 4]
67
+ , [7, 7, 6, 3], [8, 8, 3, 5], [5, 5, 8, 9], [6, 6, 9, 7],
68
+ [10, 10, 7, 8], [9, 9, 11, 12], [13, 13, 12, 10], [12, 12, 10, 11],
69
+ [11, 11, 13, 13]],
70
+ [[0, 0, 1, 2], [3, 3, 2, 0], [4, 4, 0, 1], [1, 1, 5, 6], [2, 2, 7, 8]
71
+ , [5, 5, 6, 3], [9, 9, 3, 5], [10, 10, 8, 4], [8, 8, 4, 7],
72
+ [6, 6, 10, 11], [7, 7, 11, 9], [12, 12, 9, 10], [11, 11, 13, 14],
73
+ [14, 14, 14, 12], [13, 13, 12, 13]],
74
+ [[0, 0, 1, 2], [3, 3, 2, 0], [4, 4, 0, 1], [1, 1, 5, 6], [2, 2, 7, 8]
75
+ , [6, 6, 6, 3], [5, 5, 3, 5], [8, 8, 8, 4], [7, 7, 4, 7]],
76
+ [[0, 0, 1, 2], [3, 3, 2, 0], [4, 4, 0, 1], [1, 1, 5, 6], [2, 2, 7, 8]
77
+ , [9, 9, 6, 3], [6, 6, 3, 5], [10, 10, 8, 4], [11, 11, 4, 7],
78
+ [5, 5, 10, 12], [7, 7, 12, 9], [8, 8, 11, 11], [13, 13, 9, 10],
79
+ [12, 12, 13, 13]],
80
+ [[0, 0, 1, 2], [3, 3, 2, 0], [4, 4, 0, 1], [1, 1, 5, 6], [2, 2, 7, 8]
81
+ , [9, 9, 6, 3], [6, 6, 3, 5], [10, 10, 8, 4], [11, 11, 4, 7],
82
+ [5, 5, 12, 11], [7, 7, 10, 10], [8, 8, 9, 12], [13, 13, 11, 9],
83
+ [12, 12, 13, 13]],
84
+ [[0, 0, 1, 2], [3, 3, 2, 0], [4, 4, 0, 1], [1, 1, 5, 6], [2, 2, 7, 8]
85
+ , [9, 9, 6, 3], [10, 10, 3, 5], [7, 7, 8, 4], [11, 11, 4, 7],
86
+ [5, 5, 9, 9], [6, 6, 11, 12], [8, 8, 12, 10], [13, 13, 10, 11],
87
+ [12, 12, 13, 13]],
88
+ [[0, 0, 1, 2], [3, 3, 2, 0], [4, 4, 0, 1], [1, 1, 5, 6], [2, 2, 7, 8]
89
+ , [9, 9, 6, 3], [10, 10, 3, 5], [7, 7, 8, 4], [11, 11, 4, 7],
90
+ [5, 5, 12, 11], [6, 6, 10, 10], [8, 8, 9, 12], [13, 13, 11, 9],
91
+ [12, 12, 13, 13]],
92
+ [[0, 0, 1, 2], [3, 3, 2, 0], [4, 4, 0, 1], [1, 1, 5, 6], [2, 2, 7, 8]
93
+ , [9, 9, 6, 3], [10, 10, 3, 5], [11, 11, 8, 4], [12, 12, 4, 7],
94
+ [5, 5, 9, 9], [6, 6, 12, 13], [7, 7, 11, 11], [8, 8, 13, 10],
95
+ [13, 13, 10, 12]],
96
+ [[1, 1, 0, 0], [0, 0, 2, 3], [4, 4, 3, 1], [5, 5, 1, 2], [2, 2, 4, 4]
97
+ , [3, 3, 6, 7], [7, 7, 7, 5], [6, 6, 5, 6]]]
98
+ for i in range(len(t2)):
99
+ assert L[i].table == t2[i]
100
+
101
+ f = FpGroup(F, [x**2, y**3, (x*y)**7])
102
+ L = low_index_subgroups(f, 10, [x])
103
+ t3 = [[[0, 0, 0, 0]],
104
+ [[0, 0, 1, 2], [1, 1, 2, 0], [3, 3, 0, 1], [2, 2, 4, 5], [4, 4, 5, 3],
105
+ [6, 6, 3, 4], [5, 5, 6, 6]],
106
+ [[0, 0, 1, 2], [1, 1, 2, 0], [3, 3, 0, 1], [2, 2, 4, 5], [6, 6, 5, 3],
107
+ [5, 5, 3, 4], [4, 4, 6, 6]],
108
+ [[0, 0, 1, 2], [3, 3, 2, 0], [4, 4, 0, 1], [1, 1, 5, 6], [2, 2, 7, 8],
109
+ [6, 6, 6, 3], [5, 5, 3, 5], [8, 8, 8, 4], [7, 7, 4, 7]]]
110
+ for i in range(len(t3)):
111
+ assert L[i].table == t3[i]
112
+
113
+
114
+ def test_subgroup_presentations():
115
+ F, x, y = free_group("x, y")
116
+ f = FpGroup(F, [x**3, y**5, (x*y)**2])
117
+ H = [x*y, x**-1*y**-1*x*y*x]
118
+ p1 = reidemeister_presentation(f, H)
119
+ assert str(p1) == "((y_1, y_2), (y_1**2, y_2**3, y_2*y_1*y_2*y_1*y_2*y_1))"
120
+
121
+ H = f.subgroup(H)
122
+ assert (H.generators, H.relators) == p1
123
+
124
+ f = FpGroup(F, [x**3, y**3, (x*y)**3])
125
+ H = [x*y, x*y**-1]
126
+ p2 = reidemeister_presentation(f, H)
127
+ assert str(p2) == "((x_0, y_0), (x_0**3, y_0**3, x_0*y_0*x_0*y_0*x_0*y_0))"
128
+
129
+ f = FpGroup(F, [x**2*y**2, y**-1*x*y*x**-3])
130
+ H = [x]
131
+ p3 = reidemeister_presentation(f, H)
132
+ assert str(p3) == "((x_0,), (x_0**4,))"
133
+
134
+ f = FpGroup(F, [x**3*y**-3, (x*y)**3, (x*y**-1)**2])
135
+ H = [x]
136
+ p4 = reidemeister_presentation(f, H)
137
+ assert str(p4) == "((x_0,), (x_0**6,))"
138
+
139
+ # this presentation can be improved, the most simplified form
140
+ # of presentation is <a, b | a^11, b^2, (a*b)^3, (a^4*b*a^-5*b)^2>
141
+ # See [2] Pg 474 group PSL_2(11)
142
+ # This is the group PSL_2(11)
143
+ F, a, b, c = free_group("a, b, c")
144
+ f = FpGroup(F, [a**11, b**5, c**4, (b*c**2)**2, (a*b*c)**3, (a**4*c**2)**3, b**2*c**-1*b**-1*c, a**4*b**-1*a**-1*b])
145
+ H = [a, b, c**2]
146
+ gens, rels = reidemeister_presentation(f, H)
147
+ assert str(gens) == "(b_1, c_3)"
148
+ assert len(rels) == 18
149
+
150
+
151
+ @slow
152
+ def test_order():
153
+ F, x, y = free_group("x, y")
154
+ f = FpGroup(F, [x**4, y**2, x*y*x**-1*y])
155
+ assert f.order() == 8
156
+
157
+ f = FpGroup(F, [x*y*x**-1*y**-1, y**2])
158
+ assert f.order() is S.Infinity
159
+
160
+ F, a, b, c = free_group("a, b, c")
161
+ f = FpGroup(F, [a**250, b**2, c*b*c**-1*b, c**4, c**-1*a**-1*c*a, a**-1*b**-1*a*b])
162
+ assert f.order() == 2000
163
+
164
+ F, x = free_group("x")
165
+ f = FpGroup(F, [])
166
+ assert f.order() is S.Infinity
167
+
168
+ f = FpGroup(free_group('')[0], [])
169
+ assert f.order() == 1
170
+
171
+ def test_fp_subgroup():
172
+ def _test_subgroup(K, T, S):
173
+ _gens = T(K.generators)
174
+ assert all(elem in S for elem in _gens)
175
+ assert T.is_injective()
176
+ assert T.image().order() == S.order()
177
+ F, x, y = free_group("x, y")
178
+ f = FpGroup(F, [x**4, y**2, x*y*x**-1*y])
179
+ S = FpSubgroup(f, [x*y])
180
+ assert (x*y)**-3 in S
181
+ K, T = f.subgroup([x*y], homomorphism=True)
182
+ assert T(K.generators) == [y*x**-1]
183
+ _test_subgroup(K, T, S)
184
+
185
+ S = FpSubgroup(f, [x**-1*y*x])
186
+ assert x**-1*y**4*x in S
187
+ assert x**-1*y**4*x**2 not in S
188
+ K, T = f.subgroup([x**-1*y*x], homomorphism=True)
189
+ assert T(K.generators[0]**3) == y**3
190
+ _test_subgroup(K, T, S)
191
+
192
+ f = FpGroup(F, [x**3, y**5, (x*y)**2])
193
+ H = [x*y, x**-1*y**-1*x*y*x]
194
+ K, T = f.subgroup(H, homomorphism=True)
195
+ S = FpSubgroup(f, H)
196
+ _test_subgroup(K, T, S)
197
+
198
+ def test_permutation_methods():
199
+ F, x, y = free_group("x, y")
200
+ # DihedralGroup(8)
201
+ G = FpGroup(F, [x**2, y**8, x*y*x**-1*y])
202
+ T = G._to_perm_group()[1]
203
+ assert T.is_isomorphism()
204
+ assert G.center() == [y**4]
205
+
206
+ # DiheadralGroup(4)
207
+ G = FpGroup(F, [x**2, y**4, x*y*x**-1*y])
208
+ S = FpSubgroup(G, G.normal_closure([x]))
209
+ assert x in S
210
+ assert y**-1*x*y in S
211
+
212
+ # Z_5xZ_4
213
+ G = FpGroup(F, [x*y*x**-1*y**-1, y**5, x**4])
214
+ assert G.is_abelian
215
+ assert G.is_solvable
216
+
217
+ # AlternatingGroup(5)
218
+ G = FpGroup(F, [x**3, y**2, (x*y)**5])
219
+ assert not G.is_solvable
220
+
221
+ # AlternatingGroup(4)
222
+ G = FpGroup(F, [x**3, y**2, (x*y)**3])
223
+ assert len(G.derived_series()) == 3
224
+ S = FpSubgroup(G, G.derived_subgroup())
225
+ assert S.order() == 4
226
+
227
+
228
+ def test_simplify_presentation():
229
+ # ref #16083
230
+ G = simplify_presentation(FpGroup(FreeGroup([]), []))
231
+ assert not G.generators
232
+ assert not G.relators
233
+
234
+ # CyclicGroup(3)
235
+ # The second generator in <x, y | x^2, x^5, y^3> is trivial due to relators {x^2, x^5}
236
+ F, x, y = free_group("x, y")
237
+ G = simplify_presentation(FpGroup(F, [x**2, x**5, y**3]))
238
+ assert x in G.relators
239
+
240
+ def test_cyclic():
241
+ F, x, y = free_group("x, y")
242
+ f = FpGroup(F, [x*y, x**-1*y**-1*x*y*x])
243
+ assert f.is_cyclic
244
+ f = FpGroup(F, [x*y, x*y**-1])
245
+ assert f.is_cyclic
246
+ f = FpGroup(F, [x**4, y**2, x*y*x**-1*y])
247
+ assert not f.is_cyclic
248
+
249
+
250
+ def test_abelian_invariants():
251
+ F, x, y = free_group("x, y")
252
+ f = FpGroup(F, [x*y, x**-1*y**-1*x*y*x])
253
+ assert f.abelian_invariants() == []
254
+ f = FpGroup(F, [x*y, x*y**-1])
255
+ assert f.abelian_invariants() == [2]
256
+ f = FpGroup(F, [x**4, y**2, x*y*x**-1*y])
257
+ assert f.abelian_invariants() == [2, 4]
.venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_free_groups.py ADDED
@@ -0,0 +1,226 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.combinatorics.free_groups import free_group, FreeGroup
2
+ from sympy.core import Symbol
3
+ from sympy.testing.pytest import raises
4
+ from sympy.core.numbers import oo
5
+
6
+ F, x, y, z = free_group("x, y, z")
7
+
8
+
9
+ def test_FreeGroup__init__():
10
+ x, y, z = map(Symbol, "xyz")
11
+
12
+ assert len(FreeGroup("x, y, z").generators) == 3
13
+ assert len(FreeGroup(x).generators) == 1
14
+ assert len(FreeGroup(("x", "y", "z"))) == 3
15
+ assert len(FreeGroup((x, y, z)).generators) == 3
16
+
17
+
18
+ def test_FreeGroup__getnewargs__():
19
+ x, y, z = map(Symbol, "xyz")
20
+ assert FreeGroup("x, y, z").__getnewargs__() == ((x, y, z),)
21
+
22
+
23
+ def test_free_group():
24
+ G, a, b, c = free_group("a, b, c")
25
+ assert F.generators == (x, y, z)
26
+ assert x*z**2 in F
27
+ assert x in F
28
+ assert y*z**-1 in F
29
+ assert (y*z)**0 in F
30
+ assert a not in F
31
+ assert a**0 not in F
32
+ assert len(F) == 3
33
+ assert str(F) == '<free group on the generators (x, y, z)>'
34
+ assert not F == G
35
+ assert F.order() is oo
36
+ assert F.is_abelian == False
37
+ assert F.center() == {F.identity}
38
+
39
+ (e,) = free_group("")
40
+ assert e.order() == 1
41
+ assert e.generators == ()
42
+ assert e.elements == {e.identity}
43
+ assert e.is_abelian == True
44
+
45
+
46
+ def test_FreeGroup__hash__():
47
+ assert hash(F)
48
+
49
+
50
+ def test_FreeGroup__eq__():
51
+ assert free_group("x, y, z")[0] == free_group("x, y, z")[0]
52
+ assert free_group("x, y, z")[0] is free_group("x, y, z")[0]
53
+
54
+ assert free_group("x, y, z")[0] != free_group("a, x, y")[0]
55
+ assert free_group("x, y, z")[0] is not free_group("a, x, y")[0]
56
+
57
+ assert free_group("x, y")[0] != free_group("x, y, z")[0]
58
+ assert free_group("x, y")[0] is not free_group("x, y, z")[0]
59
+
60
+ assert free_group("x, y, z")[0] != free_group("x, y")[0]
61
+ assert free_group("x, y, z")[0] is not free_group("x, y")[0]
62
+
63
+
64
+ def test_FreeGroup__getitem__():
65
+ assert F[0:] == FreeGroup("x, y, z")
66
+ assert F[1:] == FreeGroup("y, z")
67
+ assert F[2:] == FreeGroup("z")
68
+
69
+
70
+ def test_FreeGroupElm__hash__():
71
+ assert hash(x*y*z)
72
+
73
+
74
+ def test_FreeGroupElm_copy():
75
+ f = x*y*z**3
76
+ g = f.copy()
77
+ h = x*y*z**7
78
+
79
+ assert f == g
80
+ assert f != h
81
+
82
+
83
+ def test_FreeGroupElm_inverse():
84
+ assert x.inverse() == x**-1
85
+ assert (x*y).inverse() == y**-1*x**-1
86
+ assert (y*x*y**-1).inverse() == y*x**-1*y**-1
87
+ assert (y**2*x**-1).inverse() == x*y**-2
88
+
89
+
90
+ def test_FreeGroupElm_type_error():
91
+ raises(TypeError, lambda: 2/x)
92
+ raises(TypeError, lambda: x**2 + y**2)
93
+ raises(TypeError, lambda: x/2)
94
+
95
+
96
+ def test_FreeGroupElm_methods():
97
+ assert (x**0).order() == 1
98
+ assert (y**2).order() is oo
99
+ assert (x**-1*y).commutator(x) == y**-1*x**-1*y*x
100
+ assert len(x**2*y**-1) == 3
101
+ assert len(x**-1*y**3*z) == 5
102
+
103
+
104
+ def test_FreeGroupElm_eliminate_word():
105
+ w = x**5*y*x**2*y**-4*x
106
+ assert w.eliminate_word( x, x**2 ) == x**10*y*x**4*y**-4*x**2
107
+ w3 = x**2*y**3*x**-1*y
108
+ assert w3.eliminate_word(x, x**2) == x**4*y**3*x**-2*y
109
+ assert w3.eliminate_word(x, y) == y**5
110
+ assert w3.eliminate_word(x, y**4) == y**8
111
+ assert w3.eliminate_word(y, x**-1) == x**-3
112
+ assert w3.eliminate_word(x, y*z) == y*z*y*z*y**3*z**-1
113
+ assert (y**-3).eliminate_word(y, x**-1*z**-1) == z*x*z*x*z*x
114
+ #assert w3.eliminate_word(x, y*x) == y*x*y*x**2*y*x*y*x*y*x*z**3
115
+ #assert w3.eliminate_word(x, x*y) == x*y*x**2*y*x*y*x*y*x*y*z**3
116
+
117
+
118
+ def test_FreeGroupElm_array_form():
119
+ assert (x*z).array_form == ((Symbol('x'), 1), (Symbol('z'), 1))
120
+ assert (x**2*z*y*x**-2).array_form == \
121
+ ((Symbol('x'), 2), (Symbol('z'), 1), (Symbol('y'), 1), (Symbol('x'), -2))
122
+ assert (x**-2*y**-1).array_form == ((Symbol('x'), -2), (Symbol('y'), -1))
123
+
124
+
125
+ def test_FreeGroupElm_letter_form():
126
+ assert (x**3).letter_form == (Symbol('x'), Symbol('x'), Symbol('x'))
127
+ assert (x**2*z**-2*x).letter_form == \
128
+ (Symbol('x'), Symbol('x'), -Symbol('z'), -Symbol('z'), Symbol('x'))
129
+
130
+
131
+ def test_FreeGroupElm_ext_rep():
132
+ assert (x**2*z**-2*x).ext_rep == \
133
+ (Symbol('x'), 2, Symbol('z'), -2, Symbol('x'), 1)
134
+ assert (x**-2*y**-1).ext_rep == (Symbol('x'), -2, Symbol('y'), -1)
135
+ assert (x*z).ext_rep == (Symbol('x'), 1, Symbol('z'), 1)
136
+
137
+
138
+ def test_FreeGroupElm__mul__pow__():
139
+ x1 = x.group.dtype(((Symbol('x'), 1),))
140
+ assert x**2 == x1*x
141
+
142
+ assert (x**2*y*x**-2)**4 == x**2*y**4*x**-2
143
+ assert (x**2)**2 == x**4
144
+ assert (x**-1)**-1 == x
145
+ assert (x**-1)**0 == F.identity
146
+ assert (y**2)**-2 == y**-4
147
+
148
+ assert x**2*x**-1 == x
149
+ assert x**2*y**2*y**-1 == x**2*y
150
+ assert x*x**-1 == F.identity
151
+
152
+ assert x/x == F.identity
153
+ assert x/x**2 == x**-1
154
+ assert (x**2*y)/(x**2*y**-1) == x**2*y**2*x**-2
155
+ assert (x**2*y)/(y**-1*x**2) == x**2*y*x**-2*y
156
+
157
+ assert x*(x**-1*y*z*y**-1) == y*z*y**-1
158
+ assert x**2*(x**-2*y**-1*z**2*y) == y**-1*z**2*y
159
+
160
+ a = F.identity
161
+ for n in range(10):
162
+ assert a == x**n
163
+ assert a**-1 == x**-n
164
+ a *= x
165
+
166
+
167
+ def test_FreeGroupElm__len__():
168
+ assert len(x**5*y*x**2*y**-4*x) == 13
169
+ assert len(x**17) == 17
170
+ assert len(y**0) == 0
171
+
172
+
173
+ def test_FreeGroupElm_comparison():
174
+ assert not (x*y == y*x)
175
+ assert x**0 == y**0
176
+
177
+ assert x**2 < y**3
178
+ assert not x**3 < y**2
179
+ assert x*y < x**2*y
180
+ assert x**2*y**2 < y**4
181
+ assert not y**4 < y**-4
182
+ assert not y**4 < x**-4
183
+ assert y**-2 < y**2
184
+
185
+ assert x**2 <= y**2
186
+ assert x**2 <= x**2
187
+
188
+ assert not y*z > z*y
189
+ assert x > x**-1
190
+
191
+ assert not x**2 >= y**2
192
+
193
+
194
+ def test_FreeGroupElm_syllables():
195
+ w = x**5*y*x**2*y**-4*x
196
+ assert w.number_syllables() == 5
197
+ assert w.exponent_syllable(2) == 2
198
+ assert w.generator_syllable(3) == Symbol('y')
199
+ assert w.sub_syllables(1, 2) == y
200
+ assert w.sub_syllables(3, 3) == F.identity
201
+
202
+
203
+ def test_FreeGroup_exponents():
204
+ w1 = x**2*y**3
205
+ assert w1.exponent_sum(x) == 2
206
+ assert w1.exponent_sum(x**-1) == -2
207
+ assert w1.generator_count(x) == 2
208
+
209
+ w2 = x**2*y**4*x**-3
210
+ assert w2.exponent_sum(x) == -1
211
+ assert w2.generator_count(x) == 5
212
+
213
+
214
+ def test_FreeGroup_generators():
215
+ assert (x**2*y**4*z**-1).contains_generators() == {x, y, z}
216
+ assert (x**-1*y**3).contains_generators() == {x, y}
217
+
218
+
219
+ def test_FreeGroupElm_words():
220
+ w = x**5*y*x**2*y**-4*x
221
+ assert w.subword(2, 6) == x**3*y
222
+ assert w.subword(3, 2) == F.identity
223
+ assert w.subword(6, 10) == x**2*y**-2
224
+
225
+ assert w.substituted_word(0, 7, y**-1) == y**-1*x*y**-4*x
226
+ assert w.substituted_word(0, 7, y**2*x) == y**2*x**2*y**-4*x
.venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_galois.py ADDED
@@ -0,0 +1,82 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Test groups defined by the galois module. """
2
+
3
+ from sympy.combinatorics.galois import (
4
+ S4TransitiveSubgroups, S5TransitiveSubgroups, S6TransitiveSubgroups,
5
+ find_transitive_subgroups_of_S6,
6
+ )
7
+ from sympy.combinatorics.homomorphisms import is_isomorphic
8
+ from sympy.combinatorics.named_groups import (
9
+ SymmetricGroup, AlternatingGroup, CyclicGroup,
10
+ )
11
+
12
+
13
+ def test_four_group():
14
+ G = S4TransitiveSubgroups.V.get_perm_group()
15
+ A4 = AlternatingGroup(4)
16
+ assert G.is_subgroup(A4)
17
+ assert G.degree == 4
18
+ assert G.is_transitive()
19
+ assert G.order() == 4
20
+ assert not G.is_cyclic
21
+
22
+
23
+ def test_M20():
24
+ G = S5TransitiveSubgroups.M20.get_perm_group()
25
+ S5 = SymmetricGroup(5)
26
+ A5 = AlternatingGroup(5)
27
+ assert G.is_subgroup(S5)
28
+ assert not G.is_subgroup(A5)
29
+ assert G.degree == 5
30
+ assert G.is_transitive()
31
+ assert G.order() == 20
32
+
33
+
34
+ # Setting this True means that for each of the transitive subgroups of S6,
35
+ # we run a test not only on the fixed representation, but also on one freshly
36
+ # generated by the search procedure.
37
+ INCLUDE_SEARCH_REPS = False
38
+ S6_randomized = {}
39
+ if INCLUDE_SEARCH_REPS:
40
+ S6_randomized = find_transitive_subgroups_of_S6(*list(S6TransitiveSubgroups))
41
+
42
+
43
+ def get_versions_of_S6_subgroup(name):
44
+ vers = [name.get_perm_group()]
45
+ if INCLUDE_SEARCH_REPS:
46
+ vers.append(S6_randomized[name])
47
+ return vers
48
+
49
+
50
+ def test_S6_transitive_subgroups():
51
+ """
52
+ Test enough characteristics to distinguish all 16 transitive subgroups.
53
+ """
54
+ ts = S6TransitiveSubgroups
55
+ A6 = AlternatingGroup(6)
56
+ for name, alt, order, is_isom, not_isom in [
57
+ (ts.C6, False, 6, CyclicGroup(6), None),
58
+ (ts.S3, False, 6, SymmetricGroup(3), None),
59
+ (ts.D6, False, 12, None, None),
60
+ (ts.A4, True, 12, None, None),
61
+ (ts.G18, False, 18, None, None),
62
+ (ts.A4xC2, False, 24, None, SymmetricGroup(4)),
63
+ (ts.S4m, False, 24, SymmetricGroup(4), None),
64
+ (ts.S4p, True, 24, None, None),
65
+ (ts.G36m, False, 36, None, None),
66
+ (ts.G36p, True, 36, None, None),
67
+ (ts.S4xC2, False, 48, None, None),
68
+ (ts.PSL2F5, True, 60, None, None),
69
+ (ts.G72, False, 72, None, None),
70
+ (ts.PGL2F5, False, 120, None, None),
71
+ (ts.A6, True, 360, None, None),
72
+ (ts.S6, False, 720, None, None),
73
+ ]:
74
+ for G in get_versions_of_S6_subgroup(name):
75
+ assert G.is_transitive()
76
+ assert G.degree == 6
77
+ assert G.is_subgroup(A6) is alt
78
+ assert G.order() == order
79
+ if is_isom:
80
+ assert is_isomorphic(G, is_isom)
81
+ if not_isom:
82
+ assert not is_isomorphic(G, not_isom)
.venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_generators.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.combinatorics.generators import symmetric, cyclic, alternating, \
2
+ dihedral, rubik
3
+ from sympy.combinatorics.permutations import Permutation
4
+ from sympy.testing.pytest import raises
5
+
6
+ def test_generators():
7
+
8
+ assert list(cyclic(6)) == [
9
+ Permutation([0, 1, 2, 3, 4, 5]),
10
+ Permutation([1, 2, 3, 4, 5, 0]),
11
+ Permutation([2, 3, 4, 5, 0, 1]),
12
+ Permutation([3, 4, 5, 0, 1, 2]),
13
+ Permutation([4, 5, 0, 1, 2, 3]),
14
+ Permutation([5, 0, 1, 2, 3, 4])]
15
+
16
+ assert list(cyclic(10)) == [
17
+ Permutation([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]),
18
+ Permutation([1, 2, 3, 4, 5, 6, 7, 8, 9, 0]),
19
+ Permutation([2, 3, 4, 5, 6, 7, 8, 9, 0, 1]),
20
+ Permutation([3, 4, 5, 6, 7, 8, 9, 0, 1, 2]),
21
+ Permutation([4, 5, 6, 7, 8, 9, 0, 1, 2, 3]),
22
+ Permutation([5, 6, 7, 8, 9, 0, 1, 2, 3, 4]),
23
+ Permutation([6, 7, 8, 9, 0, 1, 2, 3, 4, 5]),
24
+ Permutation([7, 8, 9, 0, 1, 2, 3, 4, 5, 6]),
25
+ Permutation([8, 9, 0, 1, 2, 3, 4, 5, 6, 7]),
26
+ Permutation([9, 0, 1, 2, 3, 4, 5, 6, 7, 8])]
27
+
28
+ assert list(alternating(4)) == [
29
+ Permutation([0, 1, 2, 3]),
30
+ Permutation([0, 2, 3, 1]),
31
+ Permutation([0, 3, 1, 2]),
32
+ Permutation([1, 0, 3, 2]),
33
+ Permutation([1, 2, 0, 3]),
34
+ Permutation([1, 3, 2, 0]),
35
+ Permutation([2, 0, 1, 3]),
36
+ Permutation([2, 1, 3, 0]),
37
+ Permutation([2, 3, 0, 1]),
38
+ Permutation([3, 0, 2, 1]),
39
+ Permutation([3, 1, 0, 2]),
40
+ Permutation([3, 2, 1, 0])]
41
+
42
+ assert list(symmetric(3)) == [
43
+ Permutation([0, 1, 2]),
44
+ Permutation([0, 2, 1]),
45
+ Permutation([1, 0, 2]),
46
+ Permutation([1, 2, 0]),
47
+ Permutation([2, 0, 1]),
48
+ Permutation([2, 1, 0])]
49
+
50
+ assert list(symmetric(4)) == [
51
+ Permutation([0, 1, 2, 3]),
52
+ Permutation([0, 1, 3, 2]),
53
+ Permutation([0, 2, 1, 3]),
54
+ Permutation([0, 2, 3, 1]),
55
+ Permutation([0, 3, 1, 2]),
56
+ Permutation([0, 3, 2, 1]),
57
+ Permutation([1, 0, 2, 3]),
58
+ Permutation([1, 0, 3, 2]),
59
+ Permutation([1, 2, 0, 3]),
60
+ Permutation([1, 2, 3, 0]),
61
+ Permutation([1, 3, 0, 2]),
62
+ Permutation([1, 3, 2, 0]),
63
+ Permutation([2, 0, 1, 3]),
64
+ Permutation([2, 0, 3, 1]),
65
+ Permutation([2, 1, 0, 3]),
66
+ Permutation([2, 1, 3, 0]),
67
+ Permutation([2, 3, 0, 1]),
68
+ Permutation([2, 3, 1, 0]),
69
+ Permutation([3, 0, 1, 2]),
70
+ Permutation([3, 0, 2, 1]),
71
+ Permutation([3, 1, 0, 2]),
72
+ Permutation([3, 1, 2, 0]),
73
+ Permutation([3, 2, 0, 1]),
74
+ Permutation([3, 2, 1, 0])]
75
+
76
+ assert list(dihedral(1)) == [
77
+ Permutation([0, 1]), Permutation([1, 0])]
78
+
79
+ assert list(dihedral(2)) == [
80
+ Permutation([0, 1, 2, 3]),
81
+ Permutation([1, 0, 3, 2]),
82
+ Permutation([2, 3, 0, 1]),
83
+ Permutation([3, 2, 1, 0])]
84
+
85
+ assert list(dihedral(3)) == [
86
+ Permutation([0, 1, 2]),
87
+ Permutation([2, 1, 0]),
88
+ Permutation([1, 2, 0]),
89
+ Permutation([0, 2, 1]),
90
+ Permutation([2, 0, 1]),
91
+ Permutation([1, 0, 2])]
92
+
93
+ assert list(dihedral(5)) == [
94
+ Permutation([0, 1, 2, 3, 4]),
95
+ Permutation([4, 3, 2, 1, 0]),
96
+ Permutation([1, 2, 3, 4, 0]),
97
+ Permutation([0, 4, 3, 2, 1]),
98
+ Permutation([2, 3, 4, 0, 1]),
99
+ Permutation([1, 0, 4, 3, 2]),
100
+ Permutation([3, 4, 0, 1, 2]),
101
+ Permutation([2, 1, 0, 4, 3]),
102
+ Permutation([4, 0, 1, 2, 3]),
103
+ Permutation([3, 2, 1, 0, 4])]
104
+
105
+ raises(ValueError, lambda: rubik(1))
.venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_graycode.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.combinatorics.graycode import (GrayCode, bin_to_gray,
2
+ random_bitstring, get_subset_from_bitstring, graycode_subsets,
3
+ gray_to_bin)
4
+ from sympy.testing.pytest import raises
5
+
6
+ def test_graycode():
7
+ g = GrayCode(2)
8
+ got = []
9
+ for i in g.generate_gray():
10
+ if i.startswith('0'):
11
+ g.skip()
12
+ got.append(i)
13
+ assert got == '00 11 10'.split()
14
+ a = GrayCode(6)
15
+ assert a.current == '0'*6
16
+ assert a.rank == 0
17
+ assert len(list(a.generate_gray())) == 64
18
+ codes = ['011001', '011011', '011010',
19
+ '011110', '011111', '011101', '011100', '010100', '010101', '010111',
20
+ '010110', '010010', '010011', '010001', '010000', '110000', '110001',
21
+ '110011', '110010', '110110', '110111', '110101', '110100', '111100',
22
+ '111101', '111111', '111110', '111010', '111011', '111001', '111000',
23
+ '101000', '101001', '101011', '101010', '101110', '101111', '101101',
24
+ '101100', '100100', '100101', '100111', '100110', '100010', '100011',
25
+ '100001', '100000']
26
+ assert list(a.generate_gray(start='011001')) == codes
27
+ assert list(
28
+ a.generate_gray(rank=GrayCode(6, start='011001').rank)) == codes
29
+ assert a.next().current == '000001'
30
+ assert a.next(2).current == '000011'
31
+ assert a.next(-1).current == '100000'
32
+
33
+ a = GrayCode(5, start='10010')
34
+ assert a.rank == 28
35
+ a = GrayCode(6, start='101000')
36
+ assert a.rank == 48
37
+
38
+ assert GrayCode(6, rank=4).current == '000110'
39
+ assert GrayCode(6, rank=4).rank == 4
40
+ assert [GrayCode(4, start=s).rank for s in
41
+ GrayCode(4).generate_gray()] == [0, 1, 2, 3, 4, 5, 6, 7, 8,
42
+ 9, 10, 11, 12, 13, 14, 15]
43
+ a = GrayCode(15, rank=15)
44
+ assert a.current == '000000000001000'
45
+
46
+ assert bin_to_gray('111') == '100'
47
+
48
+ a = random_bitstring(5)
49
+ assert type(a) is str
50
+ assert len(a) == 5
51
+ assert all(i in ['0', '1'] for i in a)
52
+
53
+ assert get_subset_from_bitstring(
54
+ ['a', 'b', 'c', 'd'], '0011') == ['c', 'd']
55
+ assert get_subset_from_bitstring('abcd', '1001') == ['a', 'd']
56
+ assert list(graycode_subsets(['a', 'b', 'c'])) == \
57
+ [[], ['c'], ['b', 'c'], ['b'], ['a', 'b'], ['a', 'b', 'c'],
58
+ ['a', 'c'], ['a']]
59
+
60
+ raises(ValueError, lambda: GrayCode(0))
61
+ raises(ValueError, lambda: GrayCode(2.2))
62
+ raises(ValueError, lambda: GrayCode(2, start=[1, 1, 0]))
63
+ raises(ValueError, lambda: GrayCode(2, rank=2.5))
64
+ raises(ValueError, lambda: get_subset_from_bitstring(['c', 'a', 'c'], '1100'))
65
+ raises(ValueError, lambda: list(GrayCode(3).generate_gray(start="1111")))
66
+
67
+
68
+ def test_live_issue_117():
69
+ assert bin_to_gray('0100') == '0110'
70
+ assert bin_to_gray('0101') == '0111'
71
+ for bits in ('0100', '0101'):
72
+ assert gray_to_bin(bin_to_gray(bits)) == bits
.venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_group_constructs.py ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.combinatorics.group_constructs import DirectProduct
2
+ from sympy.combinatorics.named_groups import CyclicGroup, DihedralGroup
3
+
4
+
5
+ def test_direct_product_n():
6
+ C = CyclicGroup(4)
7
+ D = DihedralGroup(4)
8
+ G = DirectProduct(C, C, C)
9
+ assert G.order() == 64
10
+ assert G.degree == 12
11
+ assert len(G.orbits()) == 3
12
+ assert G.is_abelian is True
13
+ H = DirectProduct(D, C)
14
+ assert H.order() == 32
15
+ assert H.is_abelian is False
.venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_group_numbers.py ADDED
@@ -0,0 +1,110 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.combinatorics.group_numbers import (is_nilpotent_number,
2
+ is_abelian_number, is_cyclic_number, _holder_formula, groups_count)
3
+ from sympy.ntheory.factor_ import factorint
4
+ from sympy.ntheory.generate import prime
5
+ from sympy.testing.pytest import raises
6
+ from sympy import randprime
7
+
8
+
9
+ def test_is_nilpotent_number():
10
+ assert is_nilpotent_number(21) == False
11
+ assert is_nilpotent_number(randprime(1, 30)**12) == True
12
+ raises(ValueError, lambda: is_nilpotent_number(-5))
13
+
14
+ A056867 = [1, 2, 3, 4, 5, 7, 8, 9, 11, 13, 15, 16, 17, 19,
15
+ 23, 25, 27, 29, 31, 32, 33, 35, 37, 41, 43, 45,
16
+ 47, 49, 51, 53, 59, 61, 64, 65, 67, 69, 71, 73,
17
+ 77, 79, 81, 83, 85, 87, 89, 91, 95, 97, 99]
18
+ for n in range(1, 100):
19
+ assert is_nilpotent_number(n) == (n in A056867)
20
+
21
+
22
+ def test_is_abelian_number():
23
+ assert is_abelian_number(4) == True
24
+ assert is_abelian_number(randprime(1, 2000)**2) == True
25
+ assert is_abelian_number(randprime(1000, 100000)) == True
26
+ assert is_abelian_number(60) == False
27
+ assert is_abelian_number(24) == False
28
+ raises(ValueError, lambda: is_abelian_number(-5))
29
+
30
+ A051532 = [1, 2, 3, 4, 5, 7, 9, 11, 13, 15, 17, 19, 23, 25,
31
+ 29, 31, 33, 35, 37, 41, 43, 45, 47, 49, 51, 53,
32
+ 59, 61, 65, 67, 69, 71, 73, 77, 79, 83, 85, 87,
33
+ 89, 91, 95, 97, 99]
34
+ for n in range(1, 100):
35
+ assert is_abelian_number(n) == (n in A051532)
36
+
37
+
38
+ A003277 = [1, 2, 3, 5, 7, 11, 13, 15, 17, 19, 23, 29,
39
+ 31, 33, 35, 37, 41, 43, 47, 51, 53, 59, 61,
40
+ 65, 67, 69, 71, 73, 77, 79, 83, 85, 87, 89,
41
+ 91, 95, 97]
42
+
43
+
44
+ def test_is_cyclic_number():
45
+ assert is_cyclic_number(15) == True
46
+ assert is_cyclic_number(randprime(1, 2000)**2) == False
47
+ assert is_cyclic_number(randprime(1000, 100000)) == True
48
+ assert is_cyclic_number(4) == False
49
+ raises(ValueError, lambda: is_cyclic_number(-5))
50
+
51
+ for n in range(1, 100):
52
+ assert is_cyclic_number(n) == (n in A003277)
53
+
54
+
55
+ def test_holder_formula():
56
+ # semiprime
57
+ assert _holder_formula({3, 5}) == 1
58
+ assert _holder_formula({5, 11}) == 2
59
+ # n in A003277 is always 1
60
+ for n in A003277:
61
+ assert _holder_formula(set(factorint(n).keys())) == 1
62
+ # otherwise
63
+ assert _holder_formula({2, 3, 5, 7}) == 12
64
+
65
+
66
+ def test_groups_count():
67
+ A000001 = [0, 1, 1, 1, 2, 1, 2, 1, 5, 2, 2, 1, 5, 1,
68
+ 2, 1, 14, 1, 5, 1, 5, 2, 2, 1, 15, 2, 2,
69
+ 5, 4, 1, 4, 1, 51, 1, 2, 1, 14, 1, 2, 2,
70
+ 14, 1, 6, 1, 4, 2, 2, 1, 52, 2, 5, 1, 5,
71
+ 1, 15, 2, 13, 2, 2, 1, 13, 1, 2, 4, 267,
72
+ 1, 4, 1, 5, 1, 4, 1, 50, 1, 2, 3, 4, 1,
73
+ 6, 1, 52, 15, 2, 1, 15, 1, 2, 1, 12, 1,
74
+ 10, 1, 4, 2]
75
+ for n in range(1, len(A000001)):
76
+ try:
77
+ assert groups_count(n) == A000001[n]
78
+ except ValueError:
79
+ pass
80
+
81
+ A000679 = [1, 1, 2, 5, 14, 51, 267, 2328, 56092, 10494213, 49487367289]
82
+ for e in range(1, len(A000679)):
83
+ assert groups_count(2**e) == A000679[e]
84
+
85
+ A090091 = [1, 1, 2, 5, 15, 67, 504, 9310, 1396077, 5937876645]
86
+ for e in range(1, len(A090091)):
87
+ assert groups_count(3**e) == A090091[e]
88
+
89
+ A090130 = [1, 1, 2, 5, 15, 77, 684, 34297]
90
+ for e in range(1, len(A090130)):
91
+ assert groups_count(5**e) == A090130[e]
92
+
93
+ A090140 = [1, 1, 2, 5, 15, 83, 860, 113147]
94
+ for e in range(1, len(A090140)):
95
+ assert groups_count(7**e) == A090140[e]
96
+
97
+ A232105 = [51, 67, 77, 83, 87, 97, 101, 107, 111, 125, 131,
98
+ 145, 149, 155, 159, 173, 183, 193, 203, 207, 217]
99
+ for i in range(len(A232105)):
100
+ assert groups_count(prime(i+1)**5) == A232105[i]
101
+
102
+ A232106 = [267, 504, 684, 860, 1192, 1476, 1944, 2264, 2876,
103
+ 4068, 4540, 6012, 7064, 7664, 8852, 10908, 13136]
104
+ for i in range(len(A232106)):
105
+ assert groups_count(prime(i+1)**6) == A232106[i]
106
+
107
+ A232107 = [2328, 9310, 34297, 113147, 750735, 1600573,
108
+ 5546909, 9380741, 23316851, 71271069, 98488755]
109
+ for i in range(len(A232107)):
110
+ assert groups_count(prime(i+1)**7) == A232107[i]
.venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_homomorphisms.py ADDED
@@ -0,0 +1,114 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.combinatorics import Permutation
2
+ from sympy.combinatorics.perm_groups import PermutationGroup
3
+ from sympy.combinatorics.homomorphisms import homomorphism, group_isomorphism, is_isomorphic
4
+ from sympy.combinatorics.free_groups import free_group
5
+ from sympy.combinatorics.fp_groups import FpGroup
6
+ from sympy.combinatorics.named_groups import AlternatingGroup, DihedralGroup, CyclicGroup
7
+ from sympy.testing.pytest import raises
8
+
9
+ def test_homomorphism():
10
+ # FpGroup -> PermutationGroup
11
+ F, a, b = free_group("a, b")
12
+ G = FpGroup(F, [a**3, b**3, (a*b)**2])
13
+
14
+ c = Permutation(3)(0, 1, 2)
15
+ d = Permutation(3)(1, 2, 3)
16
+ A = AlternatingGroup(4)
17
+ T = homomorphism(G, A, [a, b], [c, d])
18
+ assert T(a*b**2*a**-1) == c*d**2*c**-1
19
+ assert T.is_isomorphism()
20
+ assert T(T.invert(Permutation(3)(0, 2, 3))) == Permutation(3)(0, 2, 3)
21
+
22
+ T = homomorphism(G, AlternatingGroup(4), G.generators)
23
+ assert T.is_trivial()
24
+ assert T.kernel().order() == G.order()
25
+
26
+ E, e = free_group("e")
27
+ G = FpGroup(E, [e**8])
28
+ P = PermutationGroup([Permutation(0, 1, 2, 3), Permutation(0, 2)])
29
+ T = homomorphism(G, P, [e], [Permutation(0, 1, 2, 3)])
30
+ assert T.image().order() == 4
31
+ assert T(T.invert(Permutation(0, 2)(1, 3))) == Permutation(0, 2)(1, 3)
32
+
33
+ T = homomorphism(E, AlternatingGroup(4), E.generators, [c])
34
+ assert T.invert(c**2) == e**-1 #order(c) == 3 so c**2 == c**-1
35
+
36
+ # FreeGroup -> FreeGroup
37
+ T = homomorphism(F, E, [a], [e])
38
+ assert T(a**-2*b**4*a**2).is_identity
39
+
40
+ # FreeGroup -> FpGroup
41
+ G = FpGroup(F, [a*b*a**-1*b**-1])
42
+ T = homomorphism(F, G, F.generators, G.generators)
43
+ assert T.invert(a**-1*b**-1*a**2) == a*b**-1
44
+
45
+ # PermutationGroup -> PermutationGroup
46
+ D = DihedralGroup(8)
47
+ p = Permutation(0, 1, 2, 3, 4, 5, 6, 7)
48
+ P = PermutationGroup(p)
49
+ T = homomorphism(P, D, [p], [p])
50
+ assert T.is_injective()
51
+ assert not T.is_isomorphism()
52
+ assert T.invert(p**3) == p**3
53
+
54
+ T2 = homomorphism(F, P, [F.generators[0]], P.generators)
55
+ T = T.compose(T2)
56
+ assert T.domain == F
57
+ assert T.codomain == D
58
+ assert T(a*b) == p
59
+
60
+ D3 = DihedralGroup(3)
61
+ T = homomorphism(D3, D3, D3.generators, D3.generators)
62
+ assert T.is_isomorphism()
63
+
64
+
65
+ def test_isomorphisms():
66
+
67
+ F, a, b = free_group("a, b")
68
+ E, c, d = free_group("c, d")
69
+ # Infinite groups with differently ordered relators.
70
+ G = FpGroup(F, [a**2, b**3])
71
+ H = FpGroup(F, [b**3, a**2])
72
+ assert is_isomorphic(G, H)
73
+
74
+ # Trivial Case
75
+ # FpGroup -> FpGroup
76
+ H = FpGroup(F, [a**3, b**3, (a*b)**2])
77
+ F, c, d = free_group("c, d")
78
+ G = FpGroup(F, [c**3, d**3, (c*d)**2])
79
+ check, T = group_isomorphism(G, H)
80
+ assert check
81
+ assert T(c**3*d**2) == a**3*b**2
82
+
83
+ # FpGroup -> PermutationGroup
84
+ # FpGroup is converted to the equivalent isomorphic group.
85
+ F, a, b = free_group("a, b")
86
+ G = FpGroup(F, [a**3, b**3, (a*b)**2])
87
+ H = AlternatingGroup(4)
88
+ check, T = group_isomorphism(G, H)
89
+ assert check
90
+ assert T(b*a*b**-1*a**-1*b**-1) == Permutation(0, 2, 3)
91
+ assert T(b*a*b*a**-1*b**-1) == Permutation(0, 3, 2)
92
+
93
+ # PermutationGroup -> PermutationGroup
94
+ D = DihedralGroup(8)
95
+ p = Permutation(0, 1, 2, 3, 4, 5, 6, 7)
96
+ P = PermutationGroup(p)
97
+ assert not is_isomorphic(D, P)
98
+
99
+ A = CyclicGroup(5)
100
+ B = CyclicGroup(7)
101
+ assert not is_isomorphic(A, B)
102
+
103
+ # Two groups of the same prime order are isomorphic to each other.
104
+ G = FpGroup(F, [a, b**5])
105
+ H = CyclicGroup(5)
106
+ assert G.order() == H.order()
107
+ assert is_isomorphic(G, H)
108
+
109
+
110
+ def test_check_homomorphism():
111
+ a = Permutation(1,2,3,4)
112
+ b = Permutation(1,3)
113
+ G = PermutationGroup([a, b])
114
+ raises(ValueError, lambda: homomorphism(G, G, [a], [a]))
.venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_named_groups.py ADDED
@@ -0,0 +1,70 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.combinatorics.named_groups import (SymmetricGroup, CyclicGroup,
2
+ DihedralGroup, AlternatingGroup,
3
+ AbelianGroup, RubikGroup)
4
+ from sympy.testing.pytest import raises
5
+
6
+
7
+ def test_SymmetricGroup():
8
+ G = SymmetricGroup(5)
9
+ elements = list(G.generate())
10
+ assert (G.generators[0]).size == 5
11
+ assert len(elements) == 120
12
+ assert G.is_solvable is False
13
+ assert G.is_abelian is False
14
+ assert G.is_nilpotent is False
15
+ assert G.is_transitive() is True
16
+ H = SymmetricGroup(1)
17
+ assert H.order() == 1
18
+ L = SymmetricGroup(2)
19
+ assert L.order() == 2
20
+
21
+
22
+ def test_CyclicGroup():
23
+ G = CyclicGroup(10)
24
+ elements = list(G.generate())
25
+ assert len(elements) == 10
26
+ assert (G.derived_subgroup()).order() == 1
27
+ assert G.is_abelian is True
28
+ assert G.is_solvable is True
29
+ assert G.is_nilpotent is True
30
+ H = CyclicGroup(1)
31
+ assert H.order() == 1
32
+ L = CyclicGroup(2)
33
+ assert L.order() == 2
34
+
35
+
36
+ def test_DihedralGroup():
37
+ G = DihedralGroup(6)
38
+ elements = list(G.generate())
39
+ assert len(elements) == 12
40
+ assert G.is_transitive() is True
41
+ assert G.is_abelian is False
42
+ assert G.is_solvable is True
43
+ assert G.is_nilpotent is False
44
+ H = DihedralGroup(1)
45
+ assert H.order() == 2
46
+ L = DihedralGroup(2)
47
+ assert L.order() == 4
48
+ assert L.is_abelian is True
49
+ assert L.is_nilpotent is True
50
+
51
+
52
+ def test_AlternatingGroup():
53
+ G = AlternatingGroup(5)
54
+ elements = list(G.generate())
55
+ assert len(elements) == 60
56
+ assert [perm.is_even for perm in elements] == [True]*60
57
+ H = AlternatingGroup(1)
58
+ assert H.order() == 1
59
+ L = AlternatingGroup(2)
60
+ assert L.order() == 1
61
+
62
+
63
+ def test_AbelianGroup():
64
+ A = AbelianGroup(3, 3, 3)
65
+ assert A.order() == 27
66
+ assert A.is_abelian is True
67
+
68
+
69
+ def test_RubikGroup():
70
+ raises(ValueError, lambda: RubikGroup(1))
.venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_partitions.py ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core.sorting import ordered, default_sort_key
2
+ from sympy.combinatorics.partitions import (Partition, IntegerPartition,
3
+ RGS_enum, RGS_unrank, RGS_rank,
4
+ random_integer_partition)
5
+ from sympy.testing.pytest import raises
6
+ from sympy.utilities.iterables import partitions
7
+ from sympy.sets.sets import Set, FiniteSet
8
+
9
+
10
+ def test_partition_constructor():
11
+ raises(ValueError, lambda: Partition([1, 1, 2]))
12
+ raises(ValueError, lambda: Partition([1, 2, 3], [2, 3, 4]))
13
+ raises(ValueError, lambda: Partition(1, 2, 3))
14
+ raises(ValueError, lambda: Partition(*list(range(3))))
15
+
16
+ assert Partition([1, 2, 3], [4, 5]) == Partition([4, 5], [1, 2, 3])
17
+ assert Partition({1, 2, 3}, {4, 5}) == Partition([1, 2, 3], [4, 5])
18
+
19
+ a = FiniteSet(1, 2, 3)
20
+ b = FiniteSet(4, 5)
21
+ assert Partition(a, b) == Partition([1, 2, 3], [4, 5])
22
+ assert Partition({a, b}) == Partition(FiniteSet(a, b))
23
+ assert Partition({a, b}) != Partition(a, b)
24
+
25
+ def test_partition():
26
+ from sympy.abc import x
27
+
28
+ a = Partition([1, 2, 3], [4])
29
+ b = Partition([1, 2], [3, 4])
30
+ c = Partition([x])
31
+ l = [a, b, c]
32
+ l.sort(key=default_sort_key)
33
+ assert l == [c, a, b]
34
+ l.sort(key=lambda w: default_sort_key(w, order='rev-lex'))
35
+ assert l == [c, a, b]
36
+
37
+ assert (a == b) is False
38
+ assert a <= b
39
+ assert (a > b) is False
40
+ assert a != b
41
+ assert a < b
42
+
43
+ assert (a + 2).partition == [[1, 2], [3, 4]]
44
+ assert (b - 1).partition == [[1, 2, 4], [3]]
45
+
46
+ assert (a - 1).partition == [[1, 2, 3, 4]]
47
+ assert (a + 1).partition == [[1, 2, 4], [3]]
48
+ assert (b + 1).partition == [[1, 2], [3], [4]]
49
+
50
+ assert a.rank == 1
51
+ assert b.rank == 3
52
+
53
+ assert a.RGS == (0, 0, 0, 1)
54
+ assert b.RGS == (0, 0, 1, 1)
55
+
56
+
57
+ def test_integer_partition():
58
+ # no zeros in partition
59
+ raises(ValueError, lambda: IntegerPartition(list(range(3))))
60
+ # check fails since 1 + 2 != 100
61
+ raises(ValueError, lambda: IntegerPartition(100, list(range(1, 3))))
62
+ a = IntegerPartition(8, [1, 3, 4])
63
+ b = a.next_lex()
64
+ c = IntegerPartition([1, 3, 4])
65
+ d = IntegerPartition(8, {1: 3, 3: 1, 2: 1})
66
+ assert a == c
67
+ assert a.integer == d.integer
68
+ assert a.conjugate == [3, 2, 2, 1]
69
+ assert (a == b) is False
70
+ assert a <= b
71
+ assert (a > b) is False
72
+ assert a != b
73
+
74
+ for i in range(1, 11):
75
+ next = set()
76
+ prev = set()
77
+ a = IntegerPartition([i])
78
+ ans = {IntegerPartition(p) for p in partitions(i)}
79
+ n = len(ans)
80
+ for j in range(n):
81
+ next.add(a)
82
+ a = a.next_lex()
83
+ IntegerPartition(i, a.partition) # check it by giving i
84
+ for j in range(n):
85
+ prev.add(a)
86
+ a = a.prev_lex()
87
+ IntegerPartition(i, a.partition) # check it by giving i
88
+ assert next == ans
89
+ assert prev == ans
90
+
91
+ assert IntegerPartition([1, 2, 3]).as_ferrers() == '###\n##\n#'
92
+ assert IntegerPartition([1, 1, 3]).as_ferrers('o') == 'ooo\no\no'
93
+ assert str(IntegerPartition([1, 1, 3])) == '[3, 1, 1]'
94
+ assert IntegerPartition([1, 1, 3]).partition == [3, 1, 1]
95
+
96
+ raises(ValueError, lambda: random_integer_partition(-1))
97
+ assert random_integer_partition(1) == [1]
98
+ assert random_integer_partition(10, seed=[1, 3, 2, 1, 5, 1]
99
+ ) == [5, 2, 1, 1, 1]
100
+
101
+
102
+ def test_rgs():
103
+ raises(ValueError, lambda: RGS_unrank(-1, 3))
104
+ raises(ValueError, lambda: RGS_unrank(3, 0))
105
+ raises(ValueError, lambda: RGS_unrank(10, 1))
106
+
107
+ raises(ValueError, lambda: Partition.from_rgs(list(range(3)), list(range(2))))
108
+ raises(ValueError, lambda: Partition.from_rgs(list(range(1, 3)), list(range(2))))
109
+ assert RGS_enum(-1) == 0
110
+ assert RGS_enum(1) == 1
111
+ assert RGS_unrank(7, 5) == [0, 0, 1, 0, 2]
112
+ assert RGS_unrank(23, 14) == [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 2]
113
+ assert RGS_rank(RGS_unrank(40, 100)) == 40
114
+
115
+ def test_ordered_partition_9608():
116
+ a = Partition([1, 2, 3], [4])
117
+ b = Partition([1, 2], [3, 4])
118
+ assert list(ordered([a,b], Set._infimum_key))
.venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_pc_groups.py ADDED
@@ -0,0 +1,87 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.combinatorics.permutations import Permutation
2
+ from sympy.combinatorics.named_groups import SymmetricGroup, AlternatingGroup, DihedralGroup
3
+ from sympy.matrices import Matrix
4
+
5
+ def test_pc_presentation():
6
+ Groups = [SymmetricGroup(3), SymmetricGroup(4), SymmetricGroup(9).sylow_subgroup(3),
7
+ SymmetricGroup(9).sylow_subgroup(2), SymmetricGroup(8).sylow_subgroup(2), DihedralGroup(10)]
8
+
9
+ S = SymmetricGroup(125).sylow_subgroup(5)
10
+ G = S.derived_series()[2]
11
+ Groups.append(G)
12
+
13
+ G = SymmetricGroup(25).sylow_subgroup(5)
14
+ Groups.append(G)
15
+
16
+ S = SymmetricGroup(11**2).sylow_subgroup(11)
17
+ G = S.derived_series()[2]
18
+ Groups.append(G)
19
+
20
+ for G in Groups:
21
+ PcGroup = G.polycyclic_group()
22
+ collector = PcGroup.collector
23
+ pc_presentation = collector.pc_presentation
24
+
25
+ pcgs = PcGroup.pcgs
26
+ free_group = collector.free_group
27
+ free_to_perm = {}
28
+ for s, g in zip(free_group.symbols, pcgs):
29
+ free_to_perm[s] = g
30
+
31
+ for k, v in pc_presentation.items():
32
+ k_array = k.array_form
33
+ if v != ():
34
+ v_array = v.array_form
35
+
36
+ lhs = Permutation()
37
+ for gen in k_array:
38
+ s = gen[0]
39
+ e = gen[1]
40
+ lhs = lhs*free_to_perm[s]**e
41
+
42
+ if v == ():
43
+ assert lhs.is_identity
44
+ continue
45
+
46
+ rhs = Permutation()
47
+ for gen in v_array:
48
+ s = gen[0]
49
+ e = gen[1]
50
+ rhs = rhs*free_to_perm[s]**e
51
+
52
+ assert lhs == rhs
53
+
54
+
55
+ def test_exponent_vector():
56
+
57
+ Groups = [SymmetricGroup(3), SymmetricGroup(4), SymmetricGroup(9).sylow_subgroup(3),
58
+ SymmetricGroup(9).sylow_subgroup(2), SymmetricGroup(8).sylow_subgroup(2)]
59
+
60
+ for G in Groups:
61
+ PcGroup = G.polycyclic_group()
62
+ collector = PcGroup.collector
63
+
64
+ pcgs = PcGroup.pcgs
65
+ # free_group = collector.free_group
66
+
67
+ for gen in G.generators:
68
+ exp = collector.exponent_vector(gen)
69
+ g = Permutation()
70
+ for i in range(len(exp)):
71
+ g = g*pcgs[i]**exp[i] if exp[i] else g
72
+ assert g == gen
73
+
74
+
75
+ def test_induced_pcgs():
76
+ G = [SymmetricGroup(9).sylow_subgroup(3), SymmetricGroup(20).sylow_subgroup(2), AlternatingGroup(4),
77
+ DihedralGroup(4), DihedralGroup(10), DihedralGroup(9), SymmetricGroup(3), SymmetricGroup(4)]
78
+
79
+ for g in G:
80
+ PcGroup = g.polycyclic_group()
81
+ collector = PcGroup.collector
82
+ gens = list(g.generators)
83
+ ipcgs = collector.induced_pcgs(gens)
84
+ m = []
85
+ for i in ipcgs:
86
+ m.append(collector.exponent_vector(i))
87
+ assert Matrix(m).is_upper
.venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_perm_groups.py ADDED
@@ -0,0 +1,1243 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core.containers import Tuple
2
+ from sympy.combinatorics.generators import rubik_cube_generators
3
+ from sympy.combinatorics.homomorphisms import is_isomorphic
4
+ from sympy.combinatorics.named_groups import SymmetricGroup, CyclicGroup,\
5
+ DihedralGroup, AlternatingGroup, AbelianGroup, RubikGroup
6
+ from sympy.combinatorics.perm_groups import (PermutationGroup,
7
+ _orbit_transversal, Coset, SymmetricPermutationGroup)
8
+ from sympy.combinatorics.permutations import Permutation
9
+ from sympy.combinatorics.polyhedron import tetrahedron as Tetra, cube
10
+ from sympy.combinatorics.testutil import _verify_bsgs, _verify_centralizer,\
11
+ _verify_normal_closure
12
+ from sympy.testing.pytest import skip, XFAIL, slow
13
+
14
+ rmul = Permutation.rmul
15
+
16
+
17
+ def test_has():
18
+ a = Permutation([1, 0])
19
+ G = PermutationGroup([a])
20
+ assert G.is_abelian
21
+ a = Permutation([2, 0, 1])
22
+ b = Permutation([2, 1, 0])
23
+ G = PermutationGroup([a, b])
24
+ assert not G.is_abelian
25
+
26
+ G = PermutationGroup([a])
27
+ assert G.has(a)
28
+ assert not G.has(b)
29
+
30
+ a = Permutation([2, 0, 1, 3, 4, 5])
31
+ b = Permutation([0, 2, 1, 3, 4])
32
+ assert PermutationGroup(a, b).degree == \
33
+ PermutationGroup(a, b).degree == 6
34
+
35
+ g = PermutationGroup(Permutation(0, 2, 1))
36
+ assert Tuple(1, g).has(g)
37
+
38
+
39
+ def test_generate():
40
+ a = Permutation([1, 0])
41
+ g = list(PermutationGroup([a]).generate())
42
+ assert g == [Permutation([0, 1]), Permutation([1, 0])]
43
+ assert len(list(PermutationGroup(Permutation((0, 1))).generate())) == 1
44
+ g = PermutationGroup([a]).generate(method='dimino')
45
+ assert list(g) == [Permutation([0, 1]), Permutation([1, 0])]
46
+ a = Permutation([2, 0, 1])
47
+ b = Permutation([2, 1, 0])
48
+ G = PermutationGroup([a, b])
49
+ g = G.generate()
50
+ v1 = [p.array_form for p in list(g)]
51
+ v1.sort()
52
+ assert v1 == [[0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], [2, 0,
53
+ 1], [2, 1, 0]]
54
+ v2 = list(G.generate(method='dimino', af=True))
55
+ assert v1 == sorted(v2)
56
+ a = Permutation([2, 0, 1, 3, 4, 5])
57
+ b = Permutation([2, 1, 3, 4, 5, 0])
58
+ g = PermutationGroup([a, b]).generate(af=True)
59
+ assert len(list(g)) == 360
60
+
61
+
62
+ def test_order():
63
+ a = Permutation([2, 0, 1, 3, 4, 5, 6, 7, 8, 9])
64
+ b = Permutation([2, 1, 3, 4, 5, 6, 7, 8, 9, 0])
65
+ g = PermutationGroup([a, b])
66
+ assert g.order() == 1814400
67
+ assert PermutationGroup().order() == 1
68
+
69
+
70
+ def test_equality():
71
+ p_1 = Permutation(0, 1, 3)
72
+ p_2 = Permutation(0, 2, 3)
73
+ p_3 = Permutation(0, 1, 2)
74
+ p_4 = Permutation(0, 1, 3)
75
+ g_1 = PermutationGroup(p_1, p_2)
76
+ g_2 = PermutationGroup(p_3, p_4)
77
+ g_3 = PermutationGroup(p_2, p_1)
78
+ g_4 = PermutationGroup(p_1, p_2)
79
+
80
+ assert g_1 != g_2
81
+ assert g_1.generators != g_2.generators
82
+ assert g_1.equals(g_2)
83
+ assert g_1 != g_3
84
+ assert g_1.equals(g_3)
85
+ assert g_1 == g_4
86
+
87
+
88
+ def test_stabilizer():
89
+ S = SymmetricGroup(2)
90
+ H = S.stabilizer(0)
91
+ assert H.generators == [Permutation(1)]
92
+ a = Permutation([2, 0, 1, 3, 4, 5])
93
+ b = Permutation([2, 1, 3, 4, 5, 0])
94
+ G = PermutationGroup([a, b])
95
+ G0 = G.stabilizer(0)
96
+ assert G0.order() == 60
97
+
98
+ gens_cube = [[1, 3, 5, 7, 0, 2, 4, 6], [1, 3, 0, 2, 5, 7, 4, 6]]
99
+ gens = [Permutation(p) for p in gens_cube]
100
+ G = PermutationGroup(gens)
101
+ G2 = G.stabilizer(2)
102
+ assert G2.order() == 6
103
+ G2_1 = G2.stabilizer(1)
104
+ v = list(G2_1.generate(af=True))
105
+ assert v == [[0, 1, 2, 3, 4, 5, 6, 7], [3, 1, 2, 0, 7, 5, 6, 4]]
106
+
107
+ gens = (
108
+ (1, 2, 0, 4, 5, 3, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19),
109
+ (0, 1, 2, 3, 4, 5, 19, 6, 8, 9, 10, 11, 12, 13, 14,
110
+ 15, 16, 7, 17, 18),
111
+ (0, 1, 2, 3, 4, 5, 6, 7, 9, 18, 16, 11, 12, 13, 14, 15, 8, 17, 10, 19))
112
+ gens = [Permutation(p) for p in gens]
113
+ G = PermutationGroup(gens)
114
+ G2 = G.stabilizer(2)
115
+ assert G2.order() == 181440
116
+ S = SymmetricGroup(3)
117
+ assert [G.order() for G in S.basic_stabilizers] == [6, 2]
118
+
119
+
120
+ def test_center():
121
+ # the center of the dihedral group D_n is of order 2 for even n
122
+ for i in (4, 6, 10):
123
+ D = DihedralGroup(i)
124
+ assert (D.center()).order() == 2
125
+ # the center of the dihedral group D_n is of order 1 for odd n>2
126
+ for i in (3, 5, 7):
127
+ D = DihedralGroup(i)
128
+ assert (D.center()).order() == 1
129
+ # the center of an abelian group is the group itself
130
+ for i in (2, 3, 5):
131
+ for j in (1, 5, 7):
132
+ for k in (1, 1, 11):
133
+ G = AbelianGroup(i, j, k)
134
+ assert G.center().is_subgroup(G)
135
+ # the center of a nonabelian simple group is trivial
136
+ for i in(1, 5, 9):
137
+ A = AlternatingGroup(i)
138
+ assert (A.center()).order() == 1
139
+ # brute-force verifications
140
+ D = DihedralGroup(5)
141
+ A = AlternatingGroup(3)
142
+ C = CyclicGroup(4)
143
+ G.is_subgroup(D*A*C)
144
+ assert _verify_centralizer(G, G)
145
+
146
+
147
+ def test_centralizer():
148
+ # the centralizer of the trivial group is the entire group
149
+ S = SymmetricGroup(2)
150
+ assert S.centralizer(Permutation(list(range(2)))).is_subgroup(S)
151
+ A = AlternatingGroup(5)
152
+ assert A.centralizer(Permutation(list(range(5)))).is_subgroup(A)
153
+ # a centralizer in the trivial group is the trivial group itself
154
+ triv = PermutationGroup([Permutation([0, 1, 2, 3])])
155
+ D = DihedralGroup(4)
156
+ assert triv.centralizer(D).is_subgroup(triv)
157
+ # brute-force verifications for centralizers of groups
158
+ for i in (4, 5, 6):
159
+ S = SymmetricGroup(i)
160
+ A = AlternatingGroup(i)
161
+ C = CyclicGroup(i)
162
+ D = DihedralGroup(i)
163
+ for gp in (S, A, C, D):
164
+ for gp2 in (S, A, C, D):
165
+ if not gp2.is_subgroup(gp):
166
+ assert _verify_centralizer(gp, gp2)
167
+ # verify the centralizer for all elements of several groups
168
+ S = SymmetricGroup(5)
169
+ elements = list(S.generate_dimino())
170
+ for element in elements:
171
+ assert _verify_centralizer(S, element)
172
+ A = AlternatingGroup(5)
173
+ elements = list(A.generate_dimino())
174
+ for element in elements:
175
+ assert _verify_centralizer(A, element)
176
+ D = DihedralGroup(7)
177
+ elements = list(D.generate_dimino())
178
+ for element in elements:
179
+ assert _verify_centralizer(D, element)
180
+ # verify centralizers of small groups within small groups
181
+ small = []
182
+ for i in (1, 2, 3):
183
+ small.append(SymmetricGroup(i))
184
+ small.append(AlternatingGroup(i))
185
+ small.append(DihedralGroup(i))
186
+ small.append(CyclicGroup(i))
187
+ for gp in small:
188
+ for gp2 in small:
189
+ if gp.degree == gp2.degree:
190
+ assert _verify_centralizer(gp, gp2)
191
+
192
+
193
+ def test_coset_rank():
194
+ gens_cube = [[1, 3, 5, 7, 0, 2, 4, 6], [1, 3, 0, 2, 5, 7, 4, 6]]
195
+ gens = [Permutation(p) for p in gens_cube]
196
+ G = PermutationGroup(gens)
197
+ i = 0
198
+ for h in G.generate(af=True):
199
+ rk = G.coset_rank(h)
200
+ assert rk == i
201
+ h1 = G.coset_unrank(rk, af=True)
202
+ assert h == h1
203
+ i += 1
204
+ assert G.coset_unrank(48) is None
205
+ assert G.coset_unrank(G.coset_rank(gens[0])) == gens[0]
206
+
207
+
208
+ def test_coset_factor():
209
+ a = Permutation([0, 2, 1])
210
+ G = PermutationGroup([a])
211
+ c = Permutation([2, 1, 0])
212
+ assert not G.coset_factor(c)
213
+ assert G.coset_rank(c) is None
214
+
215
+ a = Permutation([2, 0, 1, 3, 4, 5])
216
+ b = Permutation([2, 1, 3, 4, 5, 0])
217
+ g = PermutationGroup([a, b])
218
+ assert g.order() == 360
219
+ d = Permutation([1, 0, 2, 3, 4, 5])
220
+ assert not g.coset_factor(d.array_form)
221
+ assert not g.contains(d)
222
+ assert Permutation(2) in G
223
+ c = Permutation([1, 0, 2, 3, 5, 4])
224
+ v = g.coset_factor(c, True)
225
+ tr = g.basic_transversals
226
+ p = Permutation.rmul(*[tr[i][v[i]] for i in range(len(g.base))])
227
+ assert p == c
228
+ v = g.coset_factor(c)
229
+ p = Permutation.rmul(*v)
230
+ assert p == c
231
+ assert g.contains(c)
232
+ G = PermutationGroup([Permutation([2, 1, 0])])
233
+ p = Permutation([1, 0, 2])
234
+ assert G.coset_factor(p) == []
235
+
236
+
237
+ def test_orbits():
238
+ a = Permutation([2, 0, 1])
239
+ b = Permutation([2, 1, 0])
240
+ g = PermutationGroup([a, b])
241
+ assert g.orbit(0) == {0, 1, 2}
242
+ assert g.orbits() == [{0, 1, 2}]
243
+ assert g.is_transitive() and g.is_transitive(strict=False)
244
+ assert g.orbit_transversal(0) == \
245
+ [Permutation(
246
+ [0, 1, 2]), Permutation([2, 0, 1]), Permutation([1, 2, 0])]
247
+ assert g.orbit_transversal(0, True) == \
248
+ [(0, Permutation([0, 1, 2])), (2, Permutation([2, 0, 1])),
249
+ (1, Permutation([1, 2, 0]))]
250
+
251
+ G = DihedralGroup(6)
252
+ transversal, slps = _orbit_transversal(G.degree, G.generators, 0, True, slp=True)
253
+ for i, t in transversal:
254
+ slp = slps[i]
255
+ w = G.identity
256
+ for s in slp:
257
+ w = G.generators[s]*w
258
+ assert w == t
259
+
260
+ a = Permutation(list(range(1, 100)) + [0])
261
+ G = PermutationGroup([a])
262
+ assert [min(o) for o in G.orbits()] == [0]
263
+ G = PermutationGroup(rubik_cube_generators())
264
+ assert [min(o) for o in G.orbits()] == [0, 1]
265
+ assert not G.is_transitive() and not G.is_transitive(strict=False)
266
+ G = PermutationGroup([Permutation(0, 1, 3), Permutation(3)(0, 1)])
267
+ assert not G.is_transitive() and G.is_transitive(strict=False)
268
+ assert PermutationGroup(
269
+ Permutation(3)).is_transitive(strict=False) is False
270
+
271
+
272
+ def test_is_normal():
273
+ gens_s5 = [Permutation(p) for p in [[1, 2, 3, 4, 0], [2, 1, 4, 0, 3]]]
274
+ G1 = PermutationGroup(gens_s5)
275
+ assert G1.order() == 120
276
+ gens_a5 = [Permutation(p) for p in [[1, 0, 3, 2, 4], [2, 1, 4, 3, 0]]]
277
+ G2 = PermutationGroup(gens_a5)
278
+ assert G2.order() == 60
279
+ assert G2.is_normal(G1)
280
+ gens3 = [Permutation(p) for p in [[2, 1, 3, 0, 4], [1, 2, 0, 3, 4]]]
281
+ G3 = PermutationGroup(gens3)
282
+ assert not G3.is_normal(G1)
283
+ assert G3.order() == 12
284
+ G4 = G1.normal_closure(G3.generators)
285
+ assert G4.order() == 60
286
+ gens5 = [Permutation(p) for p in [[1, 2, 3, 0, 4], [1, 2, 0, 3, 4]]]
287
+ G5 = PermutationGroup(gens5)
288
+ assert G5.order() == 24
289
+ G6 = G1.normal_closure(G5.generators)
290
+ assert G6.order() == 120
291
+ assert G1.is_subgroup(G6)
292
+ assert not G1.is_subgroup(G4)
293
+ assert G2.is_subgroup(G4)
294
+ I5 = PermutationGroup(Permutation(4))
295
+ assert I5.is_normal(G5)
296
+ assert I5.is_normal(G6, strict=False)
297
+ p1 = Permutation([1, 0, 2, 3, 4])
298
+ p2 = Permutation([0, 1, 2, 4, 3])
299
+ p3 = Permutation([3, 4, 2, 1, 0])
300
+ id_ = Permutation([0, 1, 2, 3, 4])
301
+ H = PermutationGroup([p1, p3])
302
+ H_n1 = PermutationGroup([p1, p2])
303
+ H_n2_1 = PermutationGroup(p1)
304
+ H_n2_2 = PermutationGroup(p2)
305
+ H_id = PermutationGroup(id_)
306
+ assert H_n1.is_normal(H)
307
+ assert H_n2_1.is_normal(H_n1)
308
+ assert H_n2_2.is_normal(H_n1)
309
+ assert H_id.is_normal(H_n2_1)
310
+ assert H_id.is_normal(H_n1)
311
+ assert H_id.is_normal(H)
312
+ assert not H_n2_1.is_normal(H)
313
+ assert not H_n2_2.is_normal(H)
314
+
315
+
316
+ def test_eq():
317
+ a = [[1, 2, 0, 3, 4, 5], [1, 0, 2, 3, 4, 5], [2, 1, 0, 3, 4, 5], [
318
+ 1, 2, 0, 3, 4, 5]]
319
+ a = [Permutation(p) for p in a + [[1, 2, 3, 4, 5, 0]]]
320
+ g = Permutation([1, 2, 3, 4, 5, 0])
321
+ G1, G2, G3 = [PermutationGroup(x) for x in [a[:2], a[2:4], [g, g**2]]]
322
+ assert G1.order() == G2.order() == G3.order() == 6
323
+ assert G1.is_subgroup(G2)
324
+ assert not G1.is_subgroup(G3)
325
+ G4 = PermutationGroup([Permutation([0, 1])])
326
+ assert not G1.is_subgroup(G4)
327
+ assert G4.is_subgroup(G1, 0)
328
+ assert PermutationGroup(g, g).is_subgroup(PermutationGroup(g))
329
+ assert SymmetricGroup(3).is_subgroup(SymmetricGroup(4), 0)
330
+ assert SymmetricGroup(3).is_subgroup(SymmetricGroup(3)*CyclicGroup(5), 0)
331
+ assert not CyclicGroup(5).is_subgroup(SymmetricGroup(3)*CyclicGroup(5), 0)
332
+ assert CyclicGroup(3).is_subgroup(SymmetricGroup(3)*CyclicGroup(5), 0)
333
+
334
+
335
+ def test_derived_subgroup():
336
+ a = Permutation([1, 0, 2, 4, 3])
337
+ b = Permutation([0, 1, 3, 2, 4])
338
+ G = PermutationGroup([a, b])
339
+ C = G.derived_subgroup()
340
+ assert C.order() == 3
341
+ assert C.is_normal(G)
342
+ assert C.is_subgroup(G, 0)
343
+ assert not G.is_subgroup(C, 0)
344
+ gens_cube = [[1, 3, 5, 7, 0, 2, 4, 6], [1, 3, 0, 2, 5, 7, 4, 6]]
345
+ gens = [Permutation(p) for p in gens_cube]
346
+ G = PermutationGroup(gens)
347
+ C = G.derived_subgroup()
348
+ assert C.order() == 12
349
+
350
+
351
+ def test_is_solvable():
352
+ a = Permutation([1, 2, 0])
353
+ b = Permutation([1, 0, 2])
354
+ G = PermutationGroup([a, b])
355
+ assert G.is_solvable
356
+ G = PermutationGroup([a])
357
+ assert G.is_solvable
358
+ a = Permutation([1, 2, 3, 4, 0])
359
+ b = Permutation([1, 0, 2, 3, 4])
360
+ G = PermutationGroup([a, b])
361
+ assert not G.is_solvable
362
+ P = SymmetricGroup(10)
363
+ S = P.sylow_subgroup(3)
364
+ assert S.is_solvable
365
+
366
+ def test_rubik1():
367
+ gens = rubik_cube_generators()
368
+ gens1 = [gens[-1]] + [p**2 for p in gens[1:]]
369
+ G1 = PermutationGroup(gens1)
370
+ assert G1.order() == 19508428800
371
+ gens2 = [p**2 for p in gens]
372
+ G2 = PermutationGroup(gens2)
373
+ assert G2.order() == 663552
374
+ assert G2.is_subgroup(G1, 0)
375
+ C1 = G1.derived_subgroup()
376
+ assert C1.order() == 4877107200
377
+ assert C1.is_subgroup(G1, 0)
378
+ assert not G2.is_subgroup(C1, 0)
379
+
380
+ G = RubikGroup(2)
381
+ assert G.order() == 3674160
382
+
383
+
384
+ @XFAIL
385
+ def test_rubik():
386
+ skip('takes too much time')
387
+ G = PermutationGroup(rubik_cube_generators())
388
+ assert G.order() == 43252003274489856000
389
+ G1 = PermutationGroup(G[:3])
390
+ assert G1.order() == 170659735142400
391
+ assert not G1.is_normal(G)
392
+ G2 = G.normal_closure(G1.generators)
393
+ assert G2.is_subgroup(G)
394
+
395
+
396
+ def test_direct_product():
397
+ C = CyclicGroup(4)
398
+ D = DihedralGroup(4)
399
+ G = C*C*C
400
+ assert G.order() == 64
401
+ assert G.degree == 12
402
+ assert len(G.orbits()) == 3
403
+ assert G.is_abelian is True
404
+ H = D*C
405
+ assert H.order() == 32
406
+ assert H.is_abelian is False
407
+
408
+
409
+ def test_orbit_rep():
410
+ G = DihedralGroup(6)
411
+ assert G.orbit_rep(1, 3) in [Permutation([2, 3, 4, 5, 0, 1]),
412
+ Permutation([4, 3, 2, 1, 0, 5])]
413
+ H = CyclicGroup(4)*G
414
+ assert H.orbit_rep(1, 5) is False
415
+
416
+
417
+ def test_schreier_vector():
418
+ G = CyclicGroup(50)
419
+ v = [0]*50
420
+ v[23] = -1
421
+ assert G.schreier_vector(23) == v
422
+ H = DihedralGroup(8)
423
+ assert H.schreier_vector(2) == [0, 1, -1, 0, 0, 1, 0, 0]
424
+ L = SymmetricGroup(4)
425
+ assert L.schreier_vector(1) == [1, -1, 0, 0]
426
+
427
+
428
+ def test_random_pr():
429
+ D = DihedralGroup(6)
430
+ r = 11
431
+ n = 3
432
+ _random_prec_n = {}
433
+ _random_prec_n[0] = {'s': 7, 't': 3, 'x': 2, 'e': -1}
434
+ _random_prec_n[1] = {'s': 5, 't': 5, 'x': 1, 'e': -1}
435
+ _random_prec_n[2] = {'s': 3, 't': 4, 'x': 2, 'e': 1}
436
+ D._random_pr_init(r, n, _random_prec_n=_random_prec_n)
437
+ assert D._random_gens[11] == [0, 1, 2, 3, 4, 5]
438
+ _random_prec = {'s': 2, 't': 9, 'x': 1, 'e': -1}
439
+ assert D.random_pr(_random_prec=_random_prec) == \
440
+ Permutation([0, 5, 4, 3, 2, 1])
441
+
442
+
443
+ def test_is_alt_sym():
444
+ G = DihedralGroup(10)
445
+ assert G.is_alt_sym() is False
446
+ assert G._eval_is_alt_sym_naive() is False
447
+ assert G._eval_is_alt_sym_naive(only_alt=True) is False
448
+ assert G._eval_is_alt_sym_naive(only_sym=True) is False
449
+
450
+ S = SymmetricGroup(10)
451
+ assert S._eval_is_alt_sym_naive() is True
452
+ assert S._eval_is_alt_sym_naive(only_alt=True) is False
453
+ assert S._eval_is_alt_sym_naive(only_sym=True) is True
454
+
455
+ N_eps = 10
456
+ _random_prec = {'N_eps': N_eps,
457
+ 0: Permutation([[2], [1, 4], [0, 6, 7, 8, 9, 3, 5]]),
458
+ 1: Permutation([[1, 8, 7, 6, 3, 5, 2, 9], [0, 4]]),
459
+ 2: Permutation([[5, 8], [4, 7], [0, 1, 2, 3, 6, 9]]),
460
+ 3: Permutation([[3], [0, 8, 2, 7, 4, 1, 6, 9, 5]]),
461
+ 4: Permutation([[8], [4, 7, 9], [3, 6], [0, 5, 1, 2]]),
462
+ 5: Permutation([[6], [0, 2, 4, 5, 1, 8, 3, 9, 7]]),
463
+ 6: Permutation([[6, 9, 8], [4, 5], [1, 3, 7], [0, 2]]),
464
+ 7: Permutation([[4], [0, 2, 9, 1, 3, 8, 6, 5, 7]]),
465
+ 8: Permutation([[1, 5, 6, 3], [0, 2, 7, 8, 4, 9]]),
466
+ 9: Permutation([[8], [6, 7], [2, 3, 4, 5], [0, 1, 9]])}
467
+ assert S.is_alt_sym(_random_prec=_random_prec) is True
468
+
469
+ A = AlternatingGroup(10)
470
+ assert A._eval_is_alt_sym_naive() is True
471
+ assert A._eval_is_alt_sym_naive(only_alt=True) is True
472
+ assert A._eval_is_alt_sym_naive(only_sym=True) is False
473
+
474
+ _random_prec = {'N_eps': N_eps,
475
+ 0: Permutation([[1, 6, 4, 2, 7, 8, 5, 9, 3], [0]]),
476
+ 1: Permutation([[1], [0, 5, 8, 4, 9, 2, 3, 6, 7]]),
477
+ 2: Permutation([[1, 9, 8, 3, 2, 5], [0, 6, 7, 4]]),
478
+ 3: Permutation([[6, 8, 9], [4, 5], [1, 3, 7, 2], [0]]),
479
+ 4: Permutation([[8], [5], [4], [2, 6, 9, 3], [1], [0, 7]]),
480
+ 5: Permutation([[3, 6], [0, 8, 1, 7, 5, 9, 4, 2]]),
481
+ 6: Permutation([[5], [2, 9], [1, 8, 3], [0, 4, 7, 6]]),
482
+ 7: Permutation([[1, 8, 4, 7, 2, 3], [0, 6, 9, 5]]),
483
+ 8: Permutation([[5, 8, 7], [3], [1, 4, 2, 6], [0, 9]]),
484
+ 9: Permutation([[4, 9, 6], [3, 8], [1, 2], [0, 5, 7]])}
485
+ assert A.is_alt_sym(_random_prec=_random_prec) is False
486
+
487
+ G = PermutationGroup(
488
+ Permutation(1, 3, size=8)(0, 2, 4, 6),
489
+ Permutation(5, 7, size=8)(0, 2, 4, 6))
490
+ assert G.is_alt_sym() is False
491
+
492
+ # Tests for monte-carlo c_n parameter setting, and which guarantees
493
+ # to give False.
494
+ G = DihedralGroup(10)
495
+ assert G._eval_is_alt_sym_monte_carlo() is False
496
+ G = DihedralGroup(20)
497
+ assert G._eval_is_alt_sym_monte_carlo() is False
498
+
499
+ # A dry-running test to check if it looks up for the updated cache.
500
+ G = DihedralGroup(6)
501
+ G.is_alt_sym()
502
+ assert G.is_alt_sym() is False
503
+
504
+
505
+ def test_minimal_block():
506
+ D = DihedralGroup(6)
507
+ block_system = D.minimal_block([0, 3])
508
+ for i in range(3):
509
+ assert block_system[i] == block_system[i + 3]
510
+ S = SymmetricGroup(6)
511
+ assert S.minimal_block([0, 1]) == [0, 0, 0, 0, 0, 0]
512
+
513
+ assert Tetra.pgroup.minimal_block([0, 1]) == [0, 0, 0, 0]
514
+
515
+ P1 = PermutationGroup(Permutation(1, 5)(2, 4), Permutation(0, 1, 2, 3, 4, 5))
516
+ P2 = PermutationGroup(Permutation(0, 1, 2, 3, 4, 5), Permutation(1, 5)(2, 4))
517
+ assert P1.minimal_block([0, 2]) == [0, 1, 0, 1, 0, 1]
518
+ assert P2.minimal_block([0, 2]) == [0, 1, 0, 1, 0, 1]
519
+
520
+
521
+ def test_minimal_blocks():
522
+ P = PermutationGroup(Permutation(1, 5)(2, 4), Permutation(0, 1, 2, 3, 4, 5))
523
+ assert P.minimal_blocks() == [[0, 1, 0, 1, 0, 1], [0, 1, 2, 0, 1, 2]]
524
+
525
+ P = SymmetricGroup(5)
526
+ assert P.minimal_blocks() == [[0]*5]
527
+
528
+ P = PermutationGroup(Permutation(0, 3))
529
+ assert P.minimal_blocks() is False
530
+
531
+
532
+ def test_max_div():
533
+ S = SymmetricGroup(10)
534
+ assert S.max_div == 5
535
+
536
+
537
+ def test_is_primitive():
538
+ S = SymmetricGroup(5)
539
+ assert S.is_primitive() is True
540
+ C = CyclicGroup(7)
541
+ assert C.is_primitive() is True
542
+
543
+ a = Permutation(0, 1, 2, size=6)
544
+ b = Permutation(3, 4, 5, size=6)
545
+ G = PermutationGroup(a, b)
546
+ assert G.is_primitive() is False
547
+
548
+
549
+ def test_random_stab():
550
+ S = SymmetricGroup(5)
551
+ _random_el = Permutation([1, 3, 2, 0, 4])
552
+ _random_prec = {'rand': _random_el}
553
+ g = S.random_stab(2, _random_prec=_random_prec)
554
+ assert g == Permutation([1, 3, 2, 0, 4])
555
+ h = S.random_stab(1)
556
+ assert h(1) == 1
557
+
558
+
559
+ def test_transitivity_degree():
560
+ perm = Permutation([1, 2, 0])
561
+ C = PermutationGroup([perm])
562
+ assert C.transitivity_degree == 1
563
+ gen1 = Permutation([1, 2, 0, 3, 4])
564
+ gen2 = Permutation([1, 2, 3, 4, 0])
565
+ # alternating group of degree 5
566
+ Alt = PermutationGroup([gen1, gen2])
567
+ assert Alt.transitivity_degree == 3
568
+
569
+
570
+ def test_schreier_sims_random():
571
+ assert sorted(Tetra.pgroup.base) == [0, 1]
572
+
573
+ S = SymmetricGroup(3)
574
+ base = [0, 1]
575
+ strong_gens = [Permutation([1, 2, 0]), Permutation([1, 0, 2]),
576
+ Permutation([0, 2, 1])]
577
+ assert S.schreier_sims_random(base, strong_gens, 5) == (base, strong_gens)
578
+ D = DihedralGroup(3)
579
+ _random_prec = {'g': [Permutation([2, 0, 1]), Permutation([1, 2, 0]),
580
+ Permutation([1, 0, 2])]}
581
+ base = [0, 1]
582
+ strong_gens = [Permutation([1, 2, 0]), Permutation([2, 1, 0]),
583
+ Permutation([0, 2, 1])]
584
+ assert D.schreier_sims_random([], D.generators, 2,
585
+ _random_prec=_random_prec) == (base, strong_gens)
586
+
587
+
588
+ def test_baseswap():
589
+ S = SymmetricGroup(4)
590
+ S.schreier_sims()
591
+ base = S.base
592
+ strong_gens = S.strong_gens
593
+ assert base == [0, 1, 2]
594
+ deterministic = S.baseswap(base, strong_gens, 1, randomized=False)
595
+ randomized = S.baseswap(base, strong_gens, 1)
596
+ assert deterministic[0] == [0, 2, 1]
597
+ assert _verify_bsgs(S, deterministic[0], deterministic[1]) is True
598
+ assert randomized[0] == [0, 2, 1]
599
+ assert _verify_bsgs(S, randomized[0], randomized[1]) is True
600
+
601
+
602
+ def test_schreier_sims_incremental():
603
+ identity = Permutation([0, 1, 2, 3, 4])
604
+ TrivialGroup = PermutationGroup([identity])
605
+ base, strong_gens = TrivialGroup.schreier_sims_incremental(base=[0, 1, 2])
606
+ assert _verify_bsgs(TrivialGroup, base, strong_gens) is True
607
+ S = SymmetricGroup(5)
608
+ base, strong_gens = S.schreier_sims_incremental(base=[0, 1, 2])
609
+ assert _verify_bsgs(S, base, strong_gens) is True
610
+ D = DihedralGroup(2)
611
+ base, strong_gens = D.schreier_sims_incremental(base=[1])
612
+ assert _verify_bsgs(D, base, strong_gens) is True
613
+ A = AlternatingGroup(7)
614
+ gens = A.generators[:]
615
+ gen0 = gens[0]
616
+ gen1 = gens[1]
617
+ gen1 = rmul(gen1, ~gen0)
618
+ gen0 = rmul(gen0, gen1)
619
+ gen1 = rmul(gen0, gen1)
620
+ base, strong_gens = A.schreier_sims_incremental(base=[0, 1], gens=gens)
621
+ assert _verify_bsgs(A, base, strong_gens) is True
622
+ C = CyclicGroup(11)
623
+ gen = C.generators[0]
624
+ base, strong_gens = C.schreier_sims_incremental(gens=[gen**3])
625
+ assert _verify_bsgs(C, base, strong_gens) is True
626
+
627
+
628
+ def _subgroup_search(i, j, k):
629
+ prop_true = lambda x: True
630
+ prop_fix_points = lambda x: [x(point) for point in points] == points
631
+ prop_comm_g = lambda x: rmul(x, g) == rmul(g, x)
632
+ prop_even = lambda x: x.is_even
633
+ for i in range(i, j, k):
634
+ S = SymmetricGroup(i)
635
+ A = AlternatingGroup(i)
636
+ C = CyclicGroup(i)
637
+ Sym = S.subgroup_search(prop_true)
638
+ assert Sym.is_subgroup(S)
639
+ Alt = S.subgroup_search(prop_even)
640
+ assert Alt.is_subgroup(A)
641
+ Sym = S.subgroup_search(prop_true, init_subgroup=C)
642
+ assert Sym.is_subgroup(S)
643
+ points = [7]
644
+ assert S.stabilizer(7).is_subgroup(S.subgroup_search(prop_fix_points))
645
+ points = [3, 4]
646
+ assert S.stabilizer(3).stabilizer(4).is_subgroup(
647
+ S.subgroup_search(prop_fix_points))
648
+ points = [3, 5]
649
+ fix35 = A.subgroup_search(prop_fix_points)
650
+ points = [5]
651
+ fix5 = A.subgroup_search(prop_fix_points)
652
+ assert A.subgroup_search(prop_fix_points, init_subgroup=fix35
653
+ ).is_subgroup(fix5)
654
+ base, strong_gens = A.schreier_sims_incremental()
655
+ g = A.generators[0]
656
+ comm_g = \
657
+ A.subgroup_search(prop_comm_g, base=base, strong_gens=strong_gens)
658
+ assert _verify_bsgs(comm_g, base, comm_g.generators) is True
659
+ assert [prop_comm_g(gen) is True for gen in comm_g.generators]
660
+
661
+
662
+ def test_subgroup_search():
663
+ _subgroup_search(10, 15, 2)
664
+
665
+
666
+ @XFAIL
667
+ def test_subgroup_search2():
668
+ skip('takes too much time')
669
+ _subgroup_search(16, 17, 1)
670
+
671
+
672
+ def test_normal_closure():
673
+ # the normal closure of the trivial group is trivial
674
+ S = SymmetricGroup(3)
675
+ identity = Permutation([0, 1, 2])
676
+ closure = S.normal_closure(identity)
677
+ assert closure.is_trivial
678
+ # the normal closure of the entire group is the entire group
679
+ A = AlternatingGroup(4)
680
+ assert A.normal_closure(A).is_subgroup(A)
681
+ # brute-force verifications for subgroups
682
+ for i in (3, 4, 5):
683
+ S = SymmetricGroup(i)
684
+ A = AlternatingGroup(i)
685
+ D = DihedralGroup(i)
686
+ C = CyclicGroup(i)
687
+ for gp in (A, D, C):
688
+ assert _verify_normal_closure(S, gp)
689
+ # brute-force verifications for all elements of a group
690
+ S = SymmetricGroup(5)
691
+ elements = list(S.generate_dimino())
692
+ for element in elements:
693
+ assert _verify_normal_closure(S, element)
694
+ # small groups
695
+ small = []
696
+ for i in (1, 2, 3):
697
+ small.append(SymmetricGroup(i))
698
+ small.append(AlternatingGroup(i))
699
+ small.append(DihedralGroup(i))
700
+ small.append(CyclicGroup(i))
701
+ for gp in small:
702
+ for gp2 in small:
703
+ if gp2.is_subgroup(gp, 0) and gp2.degree == gp.degree:
704
+ assert _verify_normal_closure(gp, gp2)
705
+
706
+
707
+ def test_derived_series():
708
+ # the derived series of the trivial group consists only of the trivial group
709
+ triv = PermutationGroup([Permutation([0, 1, 2])])
710
+ assert triv.derived_series()[0].is_subgroup(triv)
711
+ # the derived series for a simple group consists only of the group itself
712
+ for i in (5, 6, 7):
713
+ A = AlternatingGroup(i)
714
+ assert A.derived_series()[0].is_subgroup(A)
715
+ # the derived series for S_4 is S_4 > A_4 > K_4 > triv
716
+ S = SymmetricGroup(4)
717
+ series = S.derived_series()
718
+ assert series[1].is_subgroup(AlternatingGroup(4))
719
+ assert series[2].is_subgroup(DihedralGroup(2))
720
+ assert series[3].is_trivial
721
+
722
+
723
+ def test_lower_central_series():
724
+ # the lower central series of the trivial group consists of the trivial
725
+ # group
726
+ triv = PermutationGroup([Permutation([0, 1, 2])])
727
+ assert triv.lower_central_series()[0].is_subgroup(triv)
728
+ # the lower central series of a simple group consists of the group itself
729
+ for i in (5, 6, 7):
730
+ A = AlternatingGroup(i)
731
+ assert A.lower_central_series()[0].is_subgroup(A)
732
+ # GAP-verified example
733
+ S = SymmetricGroup(6)
734
+ series = S.lower_central_series()
735
+ assert len(series) == 2
736
+ assert series[1].is_subgroup(AlternatingGroup(6))
737
+
738
+
739
+ def test_commutator():
740
+ # the commutator of the trivial group and the trivial group is trivial
741
+ S = SymmetricGroup(3)
742
+ triv = PermutationGroup([Permutation([0, 1, 2])])
743
+ assert S.commutator(triv, triv).is_subgroup(triv)
744
+ # the commutator of the trivial group and any other group is again trivial
745
+ A = AlternatingGroup(3)
746
+ assert S.commutator(triv, A).is_subgroup(triv)
747
+ # the commutator is commutative
748
+ for i in (3, 4, 5):
749
+ S = SymmetricGroup(i)
750
+ A = AlternatingGroup(i)
751
+ D = DihedralGroup(i)
752
+ assert S.commutator(A, D).is_subgroup(S.commutator(D, A))
753
+ # the commutator of an abelian group is trivial
754
+ S = SymmetricGroup(7)
755
+ A1 = AbelianGroup(2, 5)
756
+ A2 = AbelianGroup(3, 4)
757
+ triv = PermutationGroup([Permutation([0, 1, 2, 3, 4, 5, 6])])
758
+ assert S.commutator(A1, A1).is_subgroup(triv)
759
+ assert S.commutator(A2, A2).is_subgroup(triv)
760
+ # examples calculated by hand
761
+ S = SymmetricGroup(3)
762
+ A = AlternatingGroup(3)
763
+ assert S.commutator(A, S).is_subgroup(A)
764
+
765
+
766
+ def test_is_nilpotent():
767
+ # every abelian group is nilpotent
768
+ for i in (1, 2, 3):
769
+ C = CyclicGroup(i)
770
+ Ab = AbelianGroup(i, i + 2)
771
+ assert C.is_nilpotent
772
+ assert Ab.is_nilpotent
773
+ Ab = AbelianGroup(5, 7, 10)
774
+ assert Ab.is_nilpotent
775
+ # A_5 is not solvable and thus not nilpotent
776
+ assert AlternatingGroup(5).is_nilpotent is False
777
+
778
+
779
+ def test_is_trivial():
780
+ for i in range(5):
781
+ triv = PermutationGroup([Permutation(list(range(i)))])
782
+ assert triv.is_trivial
783
+
784
+
785
+ def test_pointwise_stabilizer():
786
+ S = SymmetricGroup(2)
787
+ stab = S.pointwise_stabilizer([0])
788
+ assert stab.generators == [Permutation(1)]
789
+ S = SymmetricGroup(5)
790
+ points = []
791
+ stab = S
792
+ for point in (2, 0, 3, 4, 1):
793
+ stab = stab.stabilizer(point)
794
+ points.append(point)
795
+ assert S.pointwise_stabilizer(points).is_subgroup(stab)
796
+
797
+
798
+ def test_make_perm():
799
+ assert cube.pgroup.make_perm(5, seed=list(range(5))) == \
800
+ Permutation([4, 7, 6, 5, 0, 3, 2, 1])
801
+ assert cube.pgroup.make_perm(7, seed=list(range(7))) == \
802
+ Permutation([6, 7, 3, 2, 5, 4, 0, 1])
803
+
804
+
805
+ def test_elements():
806
+ from sympy.sets.sets import FiniteSet
807
+
808
+ p = Permutation(2, 3)
809
+ assert set(PermutationGroup(p).elements) == {Permutation(3), Permutation(2, 3)}
810
+ assert FiniteSet(*PermutationGroup(p).elements) \
811
+ == FiniteSet(Permutation(2, 3), Permutation(3))
812
+
813
+
814
+ def test_is_group():
815
+ assert PermutationGroup(Permutation(1,2), Permutation(2,4)).is_group is True
816
+ assert SymmetricGroup(4).is_group is True
817
+
818
+
819
+ def test_PermutationGroup():
820
+ assert PermutationGroup() == PermutationGroup(Permutation())
821
+ assert (PermutationGroup() == 0) is False
822
+
823
+
824
+ def test_coset_transvesal():
825
+ G = AlternatingGroup(5)
826
+ H = PermutationGroup(Permutation(0,1,2),Permutation(1,2)(3,4))
827
+ assert G.coset_transversal(H) == \
828
+ [Permutation(4), Permutation(2, 3, 4), Permutation(2, 4, 3),
829
+ Permutation(1, 2, 4), Permutation(4)(1, 2, 3), Permutation(1, 3)(2, 4),
830
+ Permutation(0, 1, 2, 3, 4), Permutation(0, 1, 2, 4, 3),
831
+ Permutation(0, 1, 3, 2, 4), Permutation(0, 2, 4, 1, 3)]
832
+
833
+
834
+ def test_coset_table():
835
+ G = PermutationGroup(Permutation(0,1,2,3), Permutation(0,1,2),
836
+ Permutation(0,4,2,7), Permutation(5,6), Permutation(0,7))
837
+ H = PermutationGroup(Permutation(0,1,2,3), Permutation(0,7))
838
+ assert G.coset_table(H) == \
839
+ [[0, 0, 0, 0, 1, 2, 3, 3, 0, 0], [4, 5, 2, 5, 6, 0, 7, 7, 1, 1],
840
+ [5, 4, 5, 1, 0, 6, 8, 8, 6, 6], [3, 3, 3, 3, 7, 8, 0, 0, 3, 3],
841
+ [2, 1, 4, 4, 4, 4, 9, 9, 4, 4], [1, 2, 1, 2, 5, 5, 10, 10, 5, 5],
842
+ [6, 6, 6, 6, 2, 1, 11, 11, 2, 2], [9, 10, 8, 10, 11, 3, 1, 1, 7, 7],
843
+ [10, 9, 10, 7, 3, 11, 2, 2, 11, 11], [8, 7, 9, 9, 9, 9, 4, 4, 9, 9],
844
+ [7, 8, 7, 8, 10, 10, 5, 5, 10, 10], [11, 11, 11, 11, 8, 7, 6, 6, 8, 8]]
845
+
846
+
847
+ def test_subgroup():
848
+ G = PermutationGroup(Permutation(0,1,2), Permutation(0,2,3))
849
+ H = G.subgroup([Permutation(0,1,3)])
850
+ assert H.is_subgroup(G)
851
+
852
+
853
+ def test_generator_product():
854
+ G = SymmetricGroup(5)
855
+ p = Permutation(0, 2, 3)(1, 4)
856
+ gens = G.generator_product(p)
857
+ assert all(g in G.strong_gens for g in gens)
858
+ w = G.identity
859
+ for g in gens:
860
+ w = g*w
861
+ assert w == p
862
+
863
+
864
+ def test_sylow_subgroup():
865
+ P = PermutationGroup(Permutation(1, 5)(2, 4), Permutation(0, 1, 2, 3, 4, 5))
866
+ S = P.sylow_subgroup(2)
867
+ assert S.order() == 4
868
+
869
+ P = DihedralGroup(12)
870
+ S = P.sylow_subgroup(3)
871
+ assert S.order() == 3
872
+
873
+ P = PermutationGroup(
874
+ Permutation(1, 5)(2, 4), Permutation(0, 1, 2, 3, 4, 5), Permutation(0, 2))
875
+ S = P.sylow_subgroup(3)
876
+ assert S.order() == 9
877
+ S = P.sylow_subgroup(2)
878
+ assert S.order() == 8
879
+
880
+ P = SymmetricGroup(10)
881
+ S = P.sylow_subgroup(2)
882
+ assert S.order() == 256
883
+ S = P.sylow_subgroup(3)
884
+ assert S.order() == 81
885
+ S = P.sylow_subgroup(5)
886
+ assert S.order() == 25
887
+
888
+ # the length of the lower central series
889
+ # of a p-Sylow subgroup of Sym(n) grows with
890
+ # the highest exponent exp of p such
891
+ # that n >= p**exp
892
+ exp = 1
893
+ length = 0
894
+ for i in range(2, 9):
895
+ P = SymmetricGroup(i)
896
+ S = P.sylow_subgroup(2)
897
+ ls = S.lower_central_series()
898
+ if i // 2**exp > 0:
899
+ # length increases with exponent
900
+ assert len(ls) > length
901
+ length = len(ls)
902
+ exp += 1
903
+ else:
904
+ assert len(ls) == length
905
+
906
+ G = SymmetricGroup(100)
907
+ S = G.sylow_subgroup(3)
908
+ assert G.order() % S.order() == 0
909
+ assert G.order()/S.order() % 3 > 0
910
+
911
+ G = AlternatingGroup(100)
912
+ S = G.sylow_subgroup(2)
913
+ assert G.order() % S.order() == 0
914
+ assert G.order()/S.order() % 2 > 0
915
+
916
+ G = DihedralGroup(18)
917
+ S = G.sylow_subgroup(p=2)
918
+ assert S.order() == 4
919
+
920
+ G = DihedralGroup(50)
921
+ S = G.sylow_subgroup(p=2)
922
+ assert S.order() == 4
923
+
924
+
925
+ @slow
926
+ def test_presentation():
927
+ def _test(P):
928
+ G = P.presentation()
929
+ return G.order() == P.order()
930
+
931
+ def _strong_test(P):
932
+ G = P.strong_presentation()
933
+ chk = len(G.generators) == len(P.strong_gens)
934
+ return chk and G.order() == P.order()
935
+
936
+ P = PermutationGroup(Permutation(0,1,5,2)(3,7,4,6), Permutation(0,3,5,4)(1,6,2,7))
937
+ assert _test(P)
938
+
939
+ P = AlternatingGroup(5)
940
+ assert _test(P)
941
+
942
+ P = SymmetricGroup(5)
943
+ assert _test(P)
944
+
945
+ P = PermutationGroup(
946
+ [Permutation(0,3,1,2), Permutation(3)(0,1), Permutation(0,1)(2,3)])
947
+ assert _strong_test(P)
948
+
949
+ P = DihedralGroup(6)
950
+ assert _strong_test(P)
951
+
952
+ a = Permutation(0,1)(2,3)
953
+ b = Permutation(0,2)(3,1)
954
+ c = Permutation(4,5)
955
+ P = PermutationGroup(c, a, b)
956
+ assert _strong_test(P)
957
+
958
+
959
+ def test_polycyclic():
960
+ a = Permutation([0, 1, 2])
961
+ b = Permutation([2, 1, 0])
962
+ G = PermutationGroup([a, b])
963
+ assert G.is_polycyclic is True
964
+
965
+ a = Permutation([1, 2, 3, 4, 0])
966
+ b = Permutation([1, 0, 2, 3, 4])
967
+ G = PermutationGroup([a, b])
968
+ assert G.is_polycyclic is False
969
+
970
+
971
+ def test_elementary():
972
+ a = Permutation([1, 5, 2, 0, 3, 6, 4])
973
+ G = PermutationGroup([a])
974
+ assert G.is_elementary(7) is False
975
+
976
+ a = Permutation(0, 1)(2, 3)
977
+ b = Permutation(0, 2)(3, 1)
978
+ G = PermutationGroup([a, b])
979
+ assert G.is_elementary(2) is True
980
+ c = Permutation(4, 5, 6)
981
+ G = PermutationGroup([a, b, c])
982
+ assert G.is_elementary(2) is False
983
+
984
+ G = SymmetricGroup(4).sylow_subgroup(2)
985
+ assert G.is_elementary(2) is False
986
+ H = AlternatingGroup(4).sylow_subgroup(2)
987
+ assert H.is_elementary(2) is True
988
+
989
+
990
+ def test_perfect():
991
+ G = AlternatingGroup(3)
992
+ assert G.is_perfect is False
993
+ G = AlternatingGroup(5)
994
+ assert G.is_perfect is True
995
+
996
+
997
+ def test_index():
998
+ G = PermutationGroup(Permutation(0,1,2), Permutation(0,2,3))
999
+ H = G.subgroup([Permutation(0,1,3)])
1000
+ assert G.index(H) == 4
1001
+
1002
+
1003
+ def test_cyclic():
1004
+ G = SymmetricGroup(2)
1005
+ assert G.is_cyclic
1006
+ G = AbelianGroup(3, 7)
1007
+ assert G.is_cyclic
1008
+ G = AbelianGroup(7, 7)
1009
+ assert not G.is_cyclic
1010
+ G = AlternatingGroup(3)
1011
+ assert G.is_cyclic
1012
+ G = AlternatingGroup(4)
1013
+ assert not G.is_cyclic
1014
+
1015
+ # Order less than 6
1016
+ G = PermutationGroup(Permutation(0, 1, 2), Permutation(0, 2, 1))
1017
+ assert G.is_cyclic
1018
+ G = PermutationGroup(
1019
+ Permutation(0, 1, 2, 3),
1020
+ Permutation(0, 2)(1, 3)
1021
+ )
1022
+ assert G.is_cyclic
1023
+ G = PermutationGroup(
1024
+ Permutation(3),
1025
+ Permutation(0, 1)(2, 3),
1026
+ Permutation(0, 2)(1, 3),
1027
+ Permutation(0, 3)(1, 2)
1028
+ )
1029
+ assert G.is_cyclic is False
1030
+
1031
+ # Order 15
1032
+ G = PermutationGroup(
1033
+ Permutation(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14),
1034
+ Permutation(0, 2, 4, 6, 8, 10, 12, 14, 1, 3, 5, 7, 9, 11, 13)
1035
+ )
1036
+ assert G.is_cyclic
1037
+
1038
+ # Distinct prime orders
1039
+ assert PermutationGroup._distinct_primes_lemma([3, 5]) is True
1040
+ assert PermutationGroup._distinct_primes_lemma([5, 7]) is True
1041
+ assert PermutationGroup._distinct_primes_lemma([2, 3]) is None
1042
+ assert PermutationGroup._distinct_primes_lemma([3, 5, 7]) is None
1043
+ assert PermutationGroup._distinct_primes_lemma([5, 7, 13]) is True
1044
+
1045
+ G = PermutationGroup(
1046
+ Permutation(0, 1, 2, 3),
1047
+ Permutation(0, 2)(1, 3))
1048
+ assert G.is_cyclic
1049
+ assert G._is_abelian
1050
+
1051
+ # Non-abelian and therefore not cyclic
1052
+ G = PermutationGroup(*SymmetricGroup(3).generators)
1053
+ assert G.is_cyclic is False
1054
+
1055
+ # Abelian and cyclic
1056
+ G = PermutationGroup(
1057
+ Permutation(0, 1, 2, 3),
1058
+ Permutation(4, 5, 6)
1059
+ )
1060
+ assert G.is_cyclic
1061
+
1062
+ # Abelian but not cyclic
1063
+ G = PermutationGroup(
1064
+ Permutation(0, 1),
1065
+ Permutation(2, 3),
1066
+ Permutation(4, 5, 6)
1067
+ )
1068
+ assert G.is_cyclic is False
1069
+
1070
+
1071
+ def test_dihedral():
1072
+ G = SymmetricGroup(2)
1073
+ assert G.is_dihedral
1074
+ G = SymmetricGroup(3)
1075
+ assert G.is_dihedral
1076
+
1077
+ G = AbelianGroup(2, 2)
1078
+ assert G.is_dihedral
1079
+ G = CyclicGroup(4)
1080
+ assert not G.is_dihedral
1081
+
1082
+ G = AbelianGroup(3, 5)
1083
+ assert not G.is_dihedral
1084
+ G = AbelianGroup(2)
1085
+ assert G.is_dihedral
1086
+ G = AbelianGroup(6)
1087
+ assert not G.is_dihedral
1088
+
1089
+ # D6, generated by two adjacent flips
1090
+ G = PermutationGroup(
1091
+ Permutation(1, 5)(2, 4),
1092
+ Permutation(0, 1)(3, 4)(2, 5))
1093
+ assert G.is_dihedral
1094
+
1095
+ # D7, generated by a flip and a rotation
1096
+ G = PermutationGroup(
1097
+ Permutation(1, 6)(2, 5)(3, 4),
1098
+ Permutation(0, 1, 2, 3, 4, 5, 6))
1099
+ assert G.is_dihedral
1100
+
1101
+ # S4, presented by three generators, fails due to having exactly 9
1102
+ # elements of order 2:
1103
+ G = PermutationGroup(
1104
+ Permutation(0, 1), Permutation(0, 2),
1105
+ Permutation(0, 3))
1106
+ assert not G.is_dihedral
1107
+
1108
+ # D7, given by three generators
1109
+ G = PermutationGroup(
1110
+ Permutation(1, 6)(2, 5)(3, 4),
1111
+ Permutation(2, 0)(3, 6)(4, 5),
1112
+ Permutation(0, 1, 2, 3, 4, 5, 6))
1113
+ assert G.is_dihedral
1114
+
1115
+
1116
+ def test_abelian_invariants():
1117
+ G = AbelianGroup(2, 3, 4)
1118
+ assert G.abelian_invariants() == [2, 3, 4]
1119
+ G=PermutationGroup([Permutation(1, 2, 3, 4), Permutation(1, 2), Permutation(5, 6)])
1120
+ assert G.abelian_invariants() == [2, 2]
1121
+ G = AlternatingGroup(7)
1122
+ assert G.abelian_invariants() == []
1123
+ G = AlternatingGroup(4)
1124
+ assert G.abelian_invariants() == [3]
1125
+ G = DihedralGroup(4)
1126
+ assert G.abelian_invariants() == [2, 2]
1127
+
1128
+ G = PermutationGroup([Permutation(1, 2, 3, 4, 5, 6, 7)])
1129
+ assert G.abelian_invariants() == [7]
1130
+ G = DihedralGroup(12)
1131
+ S = G.sylow_subgroup(3)
1132
+ assert S.abelian_invariants() == [3]
1133
+ G = PermutationGroup(Permutation(0, 1, 2), Permutation(0, 2, 3))
1134
+ assert G.abelian_invariants() == [3]
1135
+ G = PermutationGroup([Permutation(0, 1), Permutation(0, 2, 4, 6)(1, 3, 5, 7)])
1136
+ assert G.abelian_invariants() == [2, 4]
1137
+ G = SymmetricGroup(30)
1138
+ S = G.sylow_subgroup(2)
1139
+ assert S.abelian_invariants() == [2, 2, 2, 2, 2, 2, 2, 2, 2, 2]
1140
+ S = G.sylow_subgroup(3)
1141
+ assert S.abelian_invariants() == [3, 3, 3, 3]
1142
+ S = G.sylow_subgroup(5)
1143
+ assert S.abelian_invariants() == [5, 5, 5]
1144
+
1145
+
1146
+ def test_composition_series():
1147
+ a = Permutation(1, 2, 3)
1148
+ b = Permutation(1, 2)
1149
+ G = PermutationGroup([a, b])
1150
+ comp_series = G.composition_series()
1151
+ assert comp_series == G.derived_series()
1152
+ # The first group in the composition series is always the group itself and
1153
+ # the last group in the series is the trivial group.
1154
+ S = SymmetricGroup(4)
1155
+ assert S.composition_series()[0] == S
1156
+ assert len(S.composition_series()) == 5
1157
+ A = AlternatingGroup(4)
1158
+ assert A.composition_series()[0] == A
1159
+ assert len(A.composition_series()) == 4
1160
+
1161
+ # the composition series for C_8 is C_8 > C_4 > C_2 > triv
1162
+ G = CyclicGroup(8)
1163
+ series = G.composition_series()
1164
+ assert is_isomorphic(series[1], CyclicGroup(4))
1165
+ assert is_isomorphic(series[2], CyclicGroup(2))
1166
+ assert series[3].is_trivial
1167
+
1168
+
1169
+ def test_is_symmetric():
1170
+ a = Permutation(0, 1, 2)
1171
+ b = Permutation(0, 1, size=3)
1172
+ assert PermutationGroup(a, b).is_symmetric is True
1173
+
1174
+ a = Permutation(0, 2, 1)
1175
+ b = Permutation(1, 2, size=3)
1176
+ assert PermutationGroup(a, b).is_symmetric is True
1177
+
1178
+ a = Permutation(0, 1, 2, 3)
1179
+ b = Permutation(0, 3)(1, 2)
1180
+ assert PermutationGroup(a, b).is_symmetric is False
1181
+
1182
+ def test_conjugacy_class():
1183
+ S = SymmetricGroup(4)
1184
+ x = Permutation(1, 2, 3)
1185
+ C = {Permutation(0, 1, 2, size = 4), Permutation(0, 1, 3),
1186
+ Permutation(0, 2, 1, size = 4), Permutation(0, 2, 3),
1187
+ Permutation(0, 3, 1), Permutation(0, 3, 2),
1188
+ Permutation(1, 2, 3), Permutation(1, 3, 2)}
1189
+ assert S.conjugacy_class(x) == C
1190
+
1191
+ def test_conjugacy_classes():
1192
+ S = SymmetricGroup(3)
1193
+ expected = [{Permutation(size = 3)},
1194
+ {Permutation(0, 1, size = 3), Permutation(0, 2), Permutation(1, 2)},
1195
+ {Permutation(0, 1, 2), Permutation(0, 2, 1)}]
1196
+ computed = S.conjugacy_classes()
1197
+
1198
+ assert len(expected) == len(computed)
1199
+ assert all(e in computed for e in expected)
1200
+
1201
+ def test_coset_class():
1202
+ a = Permutation(1, 2)
1203
+ b = Permutation(0, 1)
1204
+ G = PermutationGroup([a, b])
1205
+ #Creating right coset
1206
+ rht_coset = G*a
1207
+ #Checking whether it is left coset or right coset
1208
+ assert rht_coset.is_right_coset
1209
+ assert not rht_coset.is_left_coset
1210
+ #Creating list representation of coset
1211
+ list_repr = rht_coset.as_list()
1212
+ expected = [Permutation(0, 2), Permutation(0, 2, 1), Permutation(1, 2),
1213
+ Permutation(2), Permutation(2)(0, 1), Permutation(0, 1, 2)]
1214
+ for ele in list_repr:
1215
+ assert ele in expected
1216
+ #Creating left coset
1217
+ left_coset = a*G
1218
+ #Checking whether it is left coset or right coset
1219
+ assert not left_coset.is_right_coset
1220
+ assert left_coset.is_left_coset
1221
+ #Creating list representation of Coset
1222
+ list_repr = left_coset.as_list()
1223
+ expected = [Permutation(2)(0, 1), Permutation(0, 1, 2), Permutation(1, 2),
1224
+ Permutation(2), Permutation(0, 2), Permutation(0, 2, 1)]
1225
+ for ele in list_repr:
1226
+ assert ele in expected
1227
+
1228
+ G = PermutationGroup(Permutation(1, 2, 3, 4), Permutation(2, 3, 4))
1229
+ H = PermutationGroup(Permutation(1, 2, 3, 4))
1230
+ g = Permutation(1, 3)(2, 4)
1231
+ rht_coset = Coset(g, H, G, dir='+')
1232
+ assert rht_coset.is_right_coset
1233
+ list_repr = rht_coset.as_list()
1234
+ expected = [Permutation(1, 2, 3, 4), Permutation(4), Permutation(1, 3)(2, 4),
1235
+ Permutation(1, 4, 3, 2)]
1236
+ for ele in list_repr:
1237
+ assert ele in expected
1238
+
1239
+ def test_symmetricpermutationgroup():
1240
+ a = SymmetricPermutationGroup(5)
1241
+ assert a.degree == 5
1242
+ assert a.order() == 120
1243
+ assert a.identity() == Permutation(4)
.venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_permutations.py ADDED
@@ -0,0 +1,564 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from itertools import permutations
2
+ from copy import copy
3
+
4
+ from sympy.core.expr import unchanged
5
+ from sympy.core.numbers import Integer
6
+ from sympy.core.relational import Eq
7
+ from sympy.core.symbol import Symbol
8
+ from sympy.core.singleton import S
9
+ from sympy.combinatorics.permutations import \
10
+ Permutation, _af_parity, _af_rmul, _af_rmuln, AppliedPermutation, Cycle
11
+ from sympy.printing import sstr, srepr, pretty, latex
12
+ from sympy.testing.pytest import raises, warns_deprecated_sympy
13
+
14
+
15
+ rmul = Permutation.rmul
16
+ a = Symbol('a', integer=True)
17
+
18
+
19
+ def test_Permutation():
20
+ # don't auto fill 0
21
+ raises(ValueError, lambda: Permutation([1]))
22
+ p = Permutation([0, 1, 2, 3])
23
+ # call as bijective
24
+ assert [p(i) for i in range(p.size)] == list(p)
25
+ # call as operator
26
+ assert p(list(range(p.size))) == list(p)
27
+ # call as function
28
+ assert list(p(1, 2)) == [0, 2, 1, 3]
29
+ raises(TypeError, lambda: p(-1))
30
+ raises(TypeError, lambda: p(5))
31
+ # conversion to list
32
+ assert list(p) == list(range(4))
33
+ assert p.copy() == p
34
+ assert copy(p) == p
35
+ assert Permutation(size=4) == Permutation(3)
36
+ assert Permutation(Permutation(3), size=5) == Permutation(4)
37
+ # cycle form with size
38
+ assert Permutation([[1, 2]], size=4) == Permutation([[1, 2], [0], [3]])
39
+ # random generation
40
+ assert Permutation.random(2) in (Permutation([1, 0]), Permutation([0, 1]))
41
+
42
+ p = Permutation([2, 5, 1, 6, 3, 0, 4])
43
+ q = Permutation([[1], [0, 3, 5, 6, 2, 4]])
44
+ assert len({p, p}) == 1
45
+ r = Permutation([1, 3, 2, 0, 4, 6, 5])
46
+ ans = Permutation(_af_rmuln(*[w.array_form for w in (p, q, r)])).array_form
47
+ assert rmul(p, q, r).array_form == ans
48
+ # make sure no other permutation of p, q, r could have given
49
+ # that answer
50
+ for a, b, c in permutations((p, q, r)):
51
+ if (a, b, c) == (p, q, r):
52
+ continue
53
+ assert rmul(a, b, c).array_form != ans
54
+
55
+ assert p.support() == list(range(7))
56
+ assert q.support() == [0, 2, 3, 4, 5, 6]
57
+ assert Permutation(p.cyclic_form).array_form == p.array_form
58
+ assert p.cardinality == 5040
59
+ assert q.cardinality == 5040
60
+ assert q.cycles == 2
61
+ assert rmul(q, p) == Permutation([4, 6, 1, 2, 5, 3, 0])
62
+ assert rmul(p, q) == Permutation([6, 5, 3, 0, 2, 4, 1])
63
+ assert _af_rmul(p.array_form, q.array_form) == \
64
+ [6, 5, 3, 0, 2, 4, 1]
65
+
66
+ assert rmul(Permutation([[1, 2, 3], [0, 4]]),
67
+ Permutation([[1, 2, 4], [0], [3]])).cyclic_form == \
68
+ [[0, 4, 2], [1, 3]]
69
+ assert q.array_form == [3, 1, 4, 5, 0, 6, 2]
70
+ assert q.cyclic_form == [[0, 3, 5, 6, 2, 4]]
71
+ assert q.full_cyclic_form == [[0, 3, 5, 6, 2, 4], [1]]
72
+ assert p.cyclic_form == [[0, 2, 1, 5], [3, 6, 4]]
73
+ t = p.transpositions()
74
+ assert t == [(0, 5), (0, 1), (0, 2), (3, 4), (3, 6)]
75
+ assert Permutation.rmul(*[Permutation(Cycle(*ti)) for ti in (t)])
76
+ assert Permutation([1, 0]).transpositions() == [(0, 1)]
77
+
78
+ assert p**13 == p
79
+ assert q**0 == Permutation(list(range(q.size)))
80
+ assert q**-2 == ~q**2
81
+ assert q**2 == Permutation([5, 1, 0, 6, 3, 2, 4])
82
+ assert q**3 == q**2*q
83
+ assert q**4 == q**2*q**2
84
+
85
+ a = Permutation(1, 3)
86
+ b = Permutation(2, 0, 3)
87
+ I = Permutation(3)
88
+ assert ~a == a**-1
89
+ assert a*~a == I
90
+ assert a*b**-1 == a*~b
91
+
92
+ ans = Permutation(0, 5, 3, 1, 6)(2, 4)
93
+ assert (p + q.rank()).rank() == ans.rank()
94
+ assert (p + q.rank())._rank == ans.rank()
95
+ assert (q + p.rank()).rank() == ans.rank()
96
+ raises(TypeError, lambda: p + Permutation(list(range(10))))
97
+
98
+ assert (p - q.rank()).rank() == Permutation(0, 6, 3, 1, 2, 5, 4).rank()
99
+ assert p.rank() - q.rank() < 0 # for coverage: make sure mod is used
100
+ assert (q - p.rank()).rank() == Permutation(1, 4, 6, 2)(3, 5).rank()
101
+
102
+ assert p*q == Permutation(_af_rmuln(*[list(w) for w in (q, p)]))
103
+ assert p*Permutation([]) == p
104
+ assert Permutation([])*p == p
105
+ assert p*Permutation([[0, 1]]) == Permutation([2, 5, 0, 6, 3, 1, 4])
106
+ assert Permutation([[0, 1]])*p == Permutation([5, 2, 1, 6, 3, 0, 4])
107
+
108
+ pq = p ^ q
109
+ assert pq == Permutation([5, 6, 0, 4, 1, 2, 3])
110
+ assert pq == rmul(q, p, ~q)
111
+ qp = q ^ p
112
+ assert qp == Permutation([4, 3, 6, 2, 1, 5, 0])
113
+ assert qp == rmul(p, q, ~p)
114
+ raises(ValueError, lambda: p ^ Permutation([]))
115
+
116
+ assert p.commutator(q) == Permutation(0, 1, 3, 4, 6, 5, 2)
117
+ assert q.commutator(p) == Permutation(0, 2, 5, 6, 4, 3, 1)
118
+ assert p.commutator(q) == ~q.commutator(p)
119
+ raises(ValueError, lambda: p.commutator(Permutation([])))
120
+
121
+ assert len(p.atoms()) == 7
122
+ assert q.atoms() == {0, 1, 2, 3, 4, 5, 6}
123
+
124
+ assert p.inversion_vector() == [2, 4, 1, 3, 1, 0]
125
+ assert q.inversion_vector() == [3, 1, 2, 2, 0, 1]
126
+
127
+ assert Permutation.from_inversion_vector(p.inversion_vector()) == p
128
+ assert Permutation.from_inversion_vector(q.inversion_vector()).array_form\
129
+ == q.array_form
130
+ raises(ValueError, lambda: Permutation.from_inversion_vector([0, 2]))
131
+ assert Permutation(list(range(500, -1, -1))).inversions() == 125250
132
+
133
+ s = Permutation([0, 4, 1, 3, 2])
134
+ assert s.parity() == 0
135
+ _ = s.cyclic_form # needed to create a value for _cyclic_form
136
+ assert len(s._cyclic_form) != s.size and s.parity() == 0
137
+ assert not s.is_odd
138
+ assert s.is_even
139
+ assert Permutation([0, 1, 4, 3, 2]).parity() == 1
140
+ assert _af_parity([0, 4, 1, 3, 2]) == 0
141
+ assert _af_parity([0, 1, 4, 3, 2]) == 1
142
+
143
+ s = Permutation([0])
144
+
145
+ assert s.is_Singleton
146
+ assert Permutation([]).is_Empty
147
+
148
+ r = Permutation([3, 2, 1, 0])
149
+ assert (r**2).is_Identity
150
+
151
+ assert rmul(~p, p).is_Identity
152
+ assert (~p)**13 == Permutation([5, 2, 0, 4, 6, 1, 3])
153
+ assert p.max() == 6
154
+ assert p.min() == 0
155
+
156
+ q = Permutation([[6], [5], [0, 1, 2, 3, 4]])
157
+
158
+ assert q.max() == 4
159
+ assert q.min() == 0
160
+
161
+ p = Permutation([1, 5, 2, 0, 3, 6, 4])
162
+ q = Permutation([[1, 2, 3, 5, 6], [0, 4]])
163
+
164
+ assert p.ascents() == [0, 3, 4]
165
+ assert q.ascents() == [1, 2, 4]
166
+ assert r.ascents() == []
167
+
168
+ assert p.descents() == [1, 2, 5]
169
+ assert q.descents() == [0, 3, 5]
170
+ assert Permutation(r.descents()).is_Identity
171
+
172
+ assert p.inversions() == 7
173
+ # test the merge-sort with a longer permutation
174
+ big = list(p) + list(range(p.max() + 1, p.max() + 130))
175
+ assert Permutation(big).inversions() == 7
176
+ assert p.signature() == -1
177
+ assert q.inversions() == 11
178
+ assert q.signature() == -1
179
+ assert rmul(p, ~p).inversions() == 0
180
+ assert rmul(p, ~p).signature() == 1
181
+
182
+ assert p.order() == 6
183
+ assert q.order() == 10
184
+ assert (p**(p.order())).is_Identity
185
+
186
+ assert p.length() == 6
187
+ assert q.length() == 7
188
+ assert r.length() == 4
189
+
190
+ assert p.runs() == [[1, 5], [2], [0, 3, 6], [4]]
191
+ assert q.runs() == [[4], [2, 3, 5], [0, 6], [1]]
192
+ assert r.runs() == [[3], [2], [1], [0]]
193
+
194
+ assert p.index() == 8
195
+ assert q.index() == 8
196
+ assert r.index() == 3
197
+
198
+ assert p.get_precedence_distance(q) == q.get_precedence_distance(p)
199
+ assert p.get_adjacency_distance(q) == p.get_adjacency_distance(q)
200
+ assert p.get_positional_distance(q) == p.get_positional_distance(q)
201
+ p = Permutation([0, 1, 2, 3])
202
+ q = Permutation([3, 2, 1, 0])
203
+ assert p.get_precedence_distance(q) == 6
204
+ assert p.get_adjacency_distance(q) == 3
205
+ assert p.get_positional_distance(q) == 8
206
+ p = Permutation([0, 3, 1, 2, 4])
207
+ q = Permutation.josephus(4, 5, 2)
208
+ assert p.get_adjacency_distance(q) == 3
209
+ raises(ValueError, lambda: p.get_adjacency_distance(Permutation([])))
210
+ raises(ValueError, lambda: p.get_positional_distance(Permutation([])))
211
+ raises(ValueError, lambda: p.get_precedence_distance(Permutation([])))
212
+
213
+ a = [Permutation.unrank_nonlex(4, i) for i in range(5)]
214
+ iden = Permutation([0, 1, 2, 3])
215
+ for i in range(5):
216
+ for j in range(i + 1, 5):
217
+ assert a[i].commutes_with(a[j]) == \
218
+ (rmul(a[i], a[j]) == rmul(a[j], a[i]))
219
+ if a[i].commutes_with(a[j]):
220
+ assert a[i].commutator(a[j]) == iden
221
+ assert a[j].commutator(a[i]) == iden
222
+
223
+ a = Permutation(3)
224
+ b = Permutation(0, 6, 3)(1, 2)
225
+ assert a.cycle_structure == {1: 4}
226
+ assert b.cycle_structure == {2: 1, 3: 1, 1: 2}
227
+ # issue 11130
228
+ raises(ValueError, lambda: Permutation(3, size=3))
229
+ raises(ValueError, lambda: Permutation([1, 2, 0, 3], size=3))
230
+
231
+
232
+ def test_Permutation_subclassing():
233
+ # Subclass that adds permutation application on iterables
234
+ class CustomPermutation(Permutation):
235
+ def __call__(self, *i):
236
+ try:
237
+ return super().__call__(*i)
238
+ except TypeError:
239
+ pass
240
+
241
+ try:
242
+ perm_obj = i[0]
243
+ return [self._array_form[j] for j in perm_obj]
244
+ except TypeError:
245
+ raise TypeError('unrecognized argument')
246
+
247
+ def __eq__(self, other):
248
+ if isinstance(other, Permutation):
249
+ return self._hashable_content() == other._hashable_content()
250
+ else:
251
+ return super().__eq__(other)
252
+
253
+ def __hash__(self):
254
+ return super().__hash__()
255
+
256
+ p = CustomPermutation([1, 2, 3, 0])
257
+ q = Permutation([1, 2, 3, 0])
258
+
259
+ assert p == q
260
+ raises(TypeError, lambda: q([1, 2]))
261
+ assert [2, 3] == p([1, 2])
262
+
263
+ assert type(p * q) == CustomPermutation
264
+ assert type(q * p) == Permutation # True because q.__mul__(p) is called!
265
+
266
+ # Run all tests for the Permutation class also on the subclass
267
+ def wrapped_test_Permutation():
268
+ # Monkeypatch the class definition in the globals
269
+ globals()['__Perm'] = globals()['Permutation']
270
+ globals()['Permutation'] = CustomPermutation
271
+ test_Permutation()
272
+ globals()['Permutation'] = globals()['__Perm'] # Restore
273
+ del globals()['__Perm']
274
+
275
+ wrapped_test_Permutation()
276
+
277
+
278
+ def test_josephus():
279
+ assert Permutation.josephus(4, 6, 1) == Permutation([3, 1, 0, 2, 5, 4])
280
+ assert Permutation.josephus(1, 5, 1).is_Identity
281
+
282
+
283
+ def test_ranking():
284
+ assert Permutation.unrank_lex(5, 10).rank() == 10
285
+ p = Permutation.unrank_lex(15, 225)
286
+ assert p.rank() == 225
287
+ p1 = p.next_lex()
288
+ assert p1.rank() == 226
289
+ assert Permutation.unrank_lex(15, 225).rank() == 225
290
+ assert Permutation.unrank_lex(10, 0).is_Identity
291
+ p = Permutation.unrank_lex(4, 23)
292
+ assert p.rank() == 23
293
+ assert p.array_form == [3, 2, 1, 0]
294
+ assert p.next_lex() is None
295
+
296
+ p = Permutation([1, 5, 2, 0, 3, 6, 4])
297
+ q = Permutation([[1, 2, 3, 5, 6], [0, 4]])
298
+ a = [Permutation.unrank_trotterjohnson(4, i).array_form for i in range(5)]
299
+ assert a == [[0, 1, 2, 3], [0, 1, 3, 2], [0, 3, 1, 2], [3, 0, 1,
300
+ 2], [3, 0, 2, 1] ]
301
+ assert [Permutation(pa).rank_trotterjohnson() for pa in a] == list(range(5))
302
+ assert Permutation([0, 1, 2, 3]).next_trotterjohnson() == \
303
+ Permutation([0, 1, 3, 2])
304
+
305
+ assert q.rank_trotterjohnson() == 2283
306
+ assert p.rank_trotterjohnson() == 3389
307
+ assert Permutation([1, 0]).rank_trotterjohnson() == 1
308
+ a = Permutation(list(range(3)))
309
+ b = a
310
+ l = []
311
+ tj = []
312
+ for i in range(6):
313
+ l.append(a)
314
+ tj.append(b)
315
+ a = a.next_lex()
316
+ b = b.next_trotterjohnson()
317
+ assert a == b is None
318
+ assert {tuple(a) for a in l} == {tuple(a) for a in tj}
319
+
320
+ p = Permutation([2, 5, 1, 6, 3, 0, 4])
321
+ q = Permutation([[6], [5], [0, 1, 2, 3, 4]])
322
+ assert p.rank() == 1964
323
+ assert q.rank() == 870
324
+ assert Permutation([]).rank_nonlex() == 0
325
+ prank = p.rank_nonlex()
326
+ assert prank == 1600
327
+ assert Permutation.unrank_nonlex(7, 1600) == p
328
+ qrank = q.rank_nonlex()
329
+ assert qrank == 41
330
+ assert Permutation.unrank_nonlex(7, 41) == Permutation(q.array_form)
331
+
332
+ a = [Permutation.unrank_nonlex(4, i).array_form for i in range(24)]
333
+ assert a == [
334
+ [1, 2, 3, 0], [3, 2, 0, 1], [1, 3, 0, 2], [1, 2, 0, 3], [2, 3, 1, 0],
335
+ [2, 0, 3, 1], [3, 0, 1, 2], [2, 0, 1, 3], [1, 3, 2, 0], [3, 0, 2, 1],
336
+ [1, 0, 3, 2], [1, 0, 2, 3], [2, 1, 3, 0], [2, 3, 0, 1], [3, 1, 0, 2],
337
+ [2, 1, 0, 3], [3, 2, 1, 0], [0, 2, 3, 1], [0, 3, 1, 2], [0, 2, 1, 3],
338
+ [3, 1, 2, 0], [0, 3, 2, 1], [0, 1, 3, 2], [0, 1, 2, 3]]
339
+
340
+ N = 10
341
+ p1 = Permutation(a[0])
342
+ for i in range(1, N+1):
343
+ p1 = p1*Permutation(a[i])
344
+ p2 = Permutation.rmul_with_af(*[Permutation(h) for h in a[N::-1]])
345
+ assert p1 == p2
346
+
347
+ ok = []
348
+ p = Permutation([1, 0])
349
+ for i in range(3):
350
+ ok.append(p.array_form)
351
+ p = p.next_nonlex()
352
+ if p is None:
353
+ ok.append(None)
354
+ break
355
+ assert ok == [[1, 0], [0, 1], None]
356
+ assert Permutation([3, 2, 0, 1]).next_nonlex() == Permutation([1, 3, 0, 2])
357
+ assert [Permutation(pa).rank_nonlex() for pa in a] == list(range(24))
358
+
359
+
360
+ def test_mul():
361
+ a, b = [0, 2, 1, 3], [0, 1, 3, 2]
362
+ assert _af_rmul(a, b) == [0, 2, 3, 1]
363
+ assert _af_rmuln(a, b, list(range(4))) == [0, 2, 3, 1]
364
+ assert rmul(Permutation(a), Permutation(b)).array_form == [0, 2, 3, 1]
365
+
366
+ a = Permutation([0, 2, 1, 3])
367
+ b = (0, 1, 3, 2)
368
+ c = (3, 1, 2, 0)
369
+ assert Permutation.rmul(a, b, c) == Permutation([1, 2, 3, 0])
370
+ assert Permutation.rmul(a, c) == Permutation([3, 2, 1, 0])
371
+ raises(TypeError, lambda: Permutation.rmul(b, c))
372
+
373
+ n = 6
374
+ m = 8
375
+ a = [Permutation.unrank_nonlex(n, i).array_form for i in range(m)]
376
+ h = list(range(n))
377
+ for i in range(m):
378
+ h = _af_rmul(h, a[i])
379
+ h2 = _af_rmuln(*a[:i + 1])
380
+ assert h == h2
381
+
382
+
383
+ def test_args():
384
+ p = Permutation([(0, 3, 1, 2), (4, 5)])
385
+ assert p._cyclic_form is None
386
+ assert Permutation(p) == p
387
+ assert p.cyclic_form == [[0, 3, 1, 2], [4, 5]]
388
+ assert p._array_form == [3, 2, 0, 1, 5, 4]
389
+ p = Permutation((0, 3, 1, 2))
390
+ assert p._cyclic_form is None
391
+ assert p._array_form == [0, 3, 1, 2]
392
+ assert Permutation([0]) == Permutation((0, ))
393
+ assert Permutation([[0], [1]]) == Permutation(((0, ), (1, ))) == \
394
+ Permutation(((0, ), [1]))
395
+ assert Permutation([[1, 2]]) == Permutation([0, 2, 1])
396
+ assert Permutation([[1], [4, 2]]) == Permutation([0, 1, 4, 3, 2])
397
+ assert Permutation([[1], [4, 2]], size=1) == Permutation([0, 1, 4, 3, 2])
398
+ assert Permutation(
399
+ [[1], [4, 2]], size=6) == Permutation([0, 1, 4, 3, 2, 5])
400
+ assert Permutation([[0, 1], [0, 2]]) == Permutation(0, 1, 2)
401
+ assert Permutation([], size=3) == Permutation([0, 1, 2])
402
+ assert Permutation(3).list(5) == [0, 1, 2, 3, 4]
403
+ assert Permutation(3).list(-1) == []
404
+ assert Permutation(5)(1, 2).list(-1) == [0, 2, 1]
405
+ assert Permutation(5)(1, 2).list() == [0, 2, 1, 3, 4, 5]
406
+ raises(ValueError, lambda: Permutation([1, 2], [0]))
407
+ # enclosing brackets needed
408
+ raises(ValueError, lambda: Permutation([[1, 2], 0]))
409
+ # enclosing brackets needed on 0
410
+ raises(ValueError, lambda: Permutation([1, 1, 0]))
411
+ raises(ValueError, lambda: Permutation([4, 5], size=10)) # where are 0-3?
412
+ # but this is ok because cycles imply that only those listed moved
413
+ assert Permutation(4, 5) == Permutation([0, 1, 2, 3, 5, 4])
414
+
415
+
416
+ def test_Cycle():
417
+ assert str(Cycle()) == '()'
418
+ assert Cycle(Cycle(1,2)) == Cycle(1, 2)
419
+ assert Cycle(1,2).copy() == Cycle(1,2)
420
+ assert list(Cycle(1, 3, 2)) == [0, 3, 1, 2]
421
+ assert Cycle(1, 2)(2, 3) == Cycle(1, 3, 2)
422
+ assert Cycle(1, 2)(2, 3)(4, 5) == Cycle(1, 3, 2)(4, 5)
423
+ assert Permutation(Cycle(1, 2)(2, 1, 0, 3)).cyclic_form, Cycle(0, 2, 1)
424
+ raises(ValueError, lambda: Cycle().list())
425
+ assert Cycle(1, 2).list() == [0, 2, 1]
426
+ assert Cycle(1, 2).list(4) == [0, 2, 1, 3]
427
+ assert Cycle(3).list(2) == [0, 1]
428
+ assert Cycle(3).list(6) == [0, 1, 2, 3, 4, 5]
429
+ assert Permutation(Cycle(1, 2), size=4) == \
430
+ Permutation([0, 2, 1, 3])
431
+ assert str(Cycle(1, 2)(4, 5)) == '(1 2)(4 5)'
432
+ assert str(Cycle(1, 2)) == '(1 2)'
433
+ assert Cycle(Permutation(list(range(3)))) == Cycle()
434
+ assert Cycle(1, 2).list() == [0, 2, 1]
435
+ assert Cycle(1, 2).list(4) == [0, 2, 1, 3]
436
+ assert Cycle().size == 0
437
+ raises(ValueError, lambda: Cycle((1, 2)))
438
+ raises(ValueError, lambda: Cycle(1, 2, 1))
439
+ raises(TypeError, lambda: Cycle(1, 2)*{})
440
+ raises(ValueError, lambda: Cycle(4)[a])
441
+ raises(ValueError, lambda: Cycle(2, -4, 3))
442
+
443
+ # check round-trip
444
+ p = Permutation([[1, 2], [4, 3]], size=5)
445
+ assert Permutation(Cycle(p)) == p
446
+
447
+
448
+ def test_from_sequence():
449
+ assert Permutation.from_sequence('SymPy') == Permutation(4)(0, 1, 3)
450
+ assert Permutation.from_sequence('SymPy', key=lambda x: x.lower()) == \
451
+ Permutation(4)(0, 2)(1, 3)
452
+
453
+
454
+ def test_resize():
455
+ p = Permutation(0, 1, 2)
456
+ assert p.resize(5) == Permutation(0, 1, 2, size=5)
457
+ assert p.resize(4) == Permutation(0, 1, 2, size=4)
458
+ assert p.resize(3) == p
459
+ raises(ValueError, lambda: p.resize(2))
460
+
461
+ p = Permutation(0, 1, 2)(3, 4)(5, 6)
462
+ assert p.resize(3) == Permutation(0, 1, 2)
463
+ raises(ValueError, lambda: p.resize(4))
464
+
465
+
466
+ def test_printing_cyclic():
467
+ p1 = Permutation([0, 2, 1])
468
+ assert repr(p1) == 'Permutation(1, 2)'
469
+ assert str(p1) == '(1 2)'
470
+ p2 = Permutation()
471
+ assert repr(p2) == 'Permutation()'
472
+ assert str(p2) == '()'
473
+ p3 = Permutation([1, 2, 0, 3])
474
+ assert repr(p3) == 'Permutation(3)(0, 1, 2)'
475
+
476
+
477
+ def test_printing_non_cyclic():
478
+ p1 = Permutation([0, 1, 2, 3, 4, 5])
479
+ assert srepr(p1, perm_cyclic=False) == 'Permutation([], size=6)'
480
+ assert sstr(p1, perm_cyclic=False) == 'Permutation([], size=6)'
481
+ p2 = Permutation([0, 1, 2])
482
+ assert srepr(p2, perm_cyclic=False) == 'Permutation([0, 1, 2])'
483
+ assert sstr(p2, perm_cyclic=False) == 'Permutation([0, 1, 2])'
484
+
485
+ p3 = Permutation([0, 2, 1])
486
+ assert srepr(p3, perm_cyclic=False) == 'Permutation([0, 2, 1])'
487
+ assert sstr(p3, perm_cyclic=False) == 'Permutation([0, 2, 1])'
488
+ p4 = Permutation([0, 1, 3, 2, 4, 5, 6, 7])
489
+ assert srepr(p4, perm_cyclic=False) == 'Permutation([0, 1, 3, 2], size=8)'
490
+
491
+
492
+ def test_deprecated_print_cyclic():
493
+ p = Permutation(0, 1, 2)
494
+ try:
495
+ Permutation.print_cyclic = True
496
+ with warns_deprecated_sympy():
497
+ assert sstr(p) == '(0 1 2)'
498
+ with warns_deprecated_sympy():
499
+ assert srepr(p) == 'Permutation(0, 1, 2)'
500
+ with warns_deprecated_sympy():
501
+ assert pretty(p) == '(0 1 2)'
502
+ with warns_deprecated_sympy():
503
+ assert latex(p) == r'\left( 0\; 1\; 2\right)'
504
+
505
+ Permutation.print_cyclic = False
506
+ with warns_deprecated_sympy():
507
+ assert sstr(p) == 'Permutation([1, 2, 0])'
508
+ with warns_deprecated_sympy():
509
+ assert srepr(p) == 'Permutation([1, 2, 0])'
510
+ with warns_deprecated_sympy():
511
+ assert pretty(p, use_unicode=False) == '/0 1 2\\\n\\1 2 0/'
512
+ with warns_deprecated_sympy():
513
+ assert latex(p) == \
514
+ r'\begin{pmatrix} 0 & 1 & 2 \\ 1 & 2 & 0 \end{pmatrix}'
515
+ finally:
516
+ Permutation.print_cyclic = None
517
+
518
+
519
+ def test_permutation_equality():
520
+ a = Permutation(0, 1, 2)
521
+ b = Permutation(0, 1, 2)
522
+ assert Eq(a, b) is S.true
523
+ c = Permutation(0, 2, 1)
524
+ assert Eq(a, c) is S.false
525
+
526
+ d = Permutation(0, 1, 2, size=4)
527
+ assert unchanged(Eq, a, d)
528
+ e = Permutation(0, 2, 1, size=4)
529
+ assert unchanged(Eq, a, e)
530
+
531
+ i = Permutation()
532
+ assert unchanged(Eq, i, 0)
533
+ assert unchanged(Eq, 0, i)
534
+
535
+
536
+ def test_issue_17661():
537
+ c1 = Cycle(1,2)
538
+ c2 = Cycle(1,2)
539
+ assert c1 == c2
540
+ assert repr(c1) == 'Cycle(1, 2)'
541
+ assert c1 == c2
542
+
543
+
544
+ def test_permutation_apply():
545
+ x = Symbol('x')
546
+ p = Permutation(0, 1, 2)
547
+ assert p.apply(0) == 1
548
+ assert isinstance(p.apply(0), Integer)
549
+ assert p.apply(x) == AppliedPermutation(p, x)
550
+ assert AppliedPermutation(p, x).subs(x, 0) == 1
551
+
552
+ x = Symbol('x', integer=False)
553
+ raises(NotImplementedError, lambda: p.apply(x))
554
+ x = Symbol('x', negative=True)
555
+ raises(NotImplementedError, lambda: p.apply(x))
556
+
557
+
558
+ def test_AppliedPermutation():
559
+ x = Symbol('x')
560
+ p = Permutation(0, 1, 2)
561
+ raises(ValueError, lambda: AppliedPermutation((0, 1, 2), x))
562
+ assert AppliedPermutation(p, 1, evaluate=True) == 2
563
+ assert AppliedPermutation(p, 1, evaluate=False).__class__ == \
564
+ AppliedPermutation
.venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_polyhedron.py ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core.symbol import symbols
2
+ from sympy.sets.sets import FiniteSet
3
+ from sympy.combinatorics.polyhedron import (Polyhedron,
4
+ tetrahedron, cube as square, octahedron, dodecahedron, icosahedron,
5
+ cube_faces)
6
+ from sympy.combinatorics.permutations import Permutation
7
+ from sympy.combinatorics.perm_groups import PermutationGroup
8
+ from sympy.testing.pytest import raises
9
+
10
+ rmul = Permutation.rmul
11
+
12
+
13
+ def test_polyhedron():
14
+ raises(ValueError, lambda: Polyhedron(list('ab'),
15
+ pgroup=[Permutation([0])]))
16
+ pgroup = [Permutation([[0, 7, 2, 5], [6, 1, 4, 3]]),
17
+ Permutation([[0, 7, 1, 6], [5, 2, 4, 3]]),
18
+ Permutation([[3, 6, 0, 5], [4, 1, 7, 2]]),
19
+ Permutation([[7, 4, 5], [1, 3, 0], [2], [6]]),
20
+ Permutation([[1, 3, 2], [7, 6, 5], [4], [0]]),
21
+ Permutation([[4, 7, 6], [2, 0, 3], [1], [5]]),
22
+ Permutation([[1, 2, 0], [4, 5, 6], [3], [7]]),
23
+ Permutation([[4, 2], [0, 6], [3, 7], [1, 5]]),
24
+ Permutation([[3, 5], [7, 1], [2, 6], [0, 4]]),
25
+ Permutation([[2, 5], [1, 6], [0, 4], [3, 7]]),
26
+ Permutation([[4, 3], [7, 0], [5, 1], [6, 2]]),
27
+ Permutation([[4, 1], [0, 5], [6, 2], [7, 3]]),
28
+ Permutation([[7, 2], [3, 6], [0, 4], [1, 5]]),
29
+ Permutation([0, 1, 2, 3, 4, 5, 6, 7])]
30
+ corners = tuple(symbols('A:H'))
31
+ faces = cube_faces
32
+ cube = Polyhedron(corners, faces, pgroup)
33
+
34
+ assert cube.edges == FiniteSet(*(
35
+ (0, 1), (6, 7), (1, 2), (5, 6), (0, 3), (2, 3),
36
+ (4, 7), (4, 5), (3, 7), (1, 5), (0, 4), (2, 6)))
37
+
38
+ for i in range(3): # add 180 degree face rotations
39
+ cube.rotate(cube.pgroup[i]**2)
40
+
41
+ assert cube.corners == corners
42
+
43
+ for i in range(3, 7): # add 240 degree axial corner rotations
44
+ cube.rotate(cube.pgroup[i]**2)
45
+
46
+ assert cube.corners == corners
47
+ cube.rotate(1)
48
+ raises(ValueError, lambda: cube.rotate(Permutation([0, 1])))
49
+ assert cube.corners != corners
50
+ assert cube.array_form == [7, 6, 4, 5, 3, 2, 0, 1]
51
+ assert cube.cyclic_form == [[0, 7, 1, 6], [2, 4, 3, 5]]
52
+ cube.reset()
53
+ assert cube.corners == corners
54
+
55
+ def check(h, size, rpt, target):
56
+
57
+ assert len(h.faces) + len(h.vertices) - len(h.edges) == 2
58
+ assert h.size == size
59
+
60
+ got = set()
61
+ for p in h.pgroup:
62
+ # make sure it restores original
63
+ P = h.copy()
64
+ hit = P.corners
65
+ for i in range(rpt):
66
+ P.rotate(p)
67
+ if P.corners == hit:
68
+ break
69
+ else:
70
+ print('error in permutation', p.array_form)
71
+ for i in range(rpt):
72
+ P.rotate(p)
73
+ got.add(tuple(P.corners))
74
+ c = P.corners
75
+ f = [[c[i] for i in f] for f in P.faces]
76
+ assert h.faces == Polyhedron(c, f).faces
77
+ assert len(got) == target
78
+ assert PermutationGroup([Permutation(g) for g in got]).is_group
79
+
80
+ for h, size, rpt, target in zip(
81
+ (tetrahedron, square, octahedron, dodecahedron, icosahedron),
82
+ (4, 8, 6, 20, 12),
83
+ (3, 4, 4, 5, 5),
84
+ (12, 24, 24, 60, 60)):
85
+ check(h, size, rpt, target)
86
+
87
+
88
+ def test_pgroups():
89
+ from sympy.combinatorics.polyhedron import (cube, tetrahedron_faces,
90
+ octahedron_faces, dodecahedron_faces, icosahedron_faces)
91
+ from sympy.combinatorics.polyhedron import _pgroup_calcs
92
+ (tetrahedron2, cube2, octahedron2, dodecahedron2, icosahedron2,
93
+ tetrahedron_faces2, cube_faces2, octahedron_faces2,
94
+ dodecahedron_faces2, icosahedron_faces2) = _pgroup_calcs()
95
+
96
+ assert tetrahedron == tetrahedron2
97
+ assert cube == cube2
98
+ assert octahedron == octahedron2
99
+ assert dodecahedron == dodecahedron2
100
+ assert icosahedron == icosahedron2
101
+ assert sorted(map(sorted, tetrahedron_faces)) == sorted(map(sorted, tetrahedron_faces2))
102
+ assert sorted(cube_faces) == sorted(cube_faces2)
103
+ assert sorted(octahedron_faces) == sorted(octahedron_faces2)
104
+ assert sorted(dodecahedron_faces) == sorted(dodecahedron_faces2)
105
+ assert sorted(icosahedron_faces) == sorted(icosahedron_faces2)
.venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_prufer.py ADDED
@@ -0,0 +1,74 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.combinatorics.prufer import Prufer
2
+ from sympy.testing.pytest import raises
3
+
4
+
5
+ def test_prufer():
6
+ # number of nodes is optional
7
+ assert Prufer([[0, 1], [0, 2], [0, 3], [0, 4]], 5).nodes == 5
8
+ assert Prufer([[0, 1], [0, 2], [0, 3], [0, 4]]).nodes == 5
9
+
10
+ a = Prufer([[0, 1], [0, 2], [0, 3], [0, 4]])
11
+ assert a.rank == 0
12
+ assert a.nodes == 5
13
+ assert a.prufer_repr == [0, 0, 0]
14
+
15
+ a = Prufer([[2, 4], [1, 4], [1, 3], [0, 5], [0, 4]])
16
+ assert a.rank == 924
17
+ assert a.nodes == 6
18
+ assert a.tree_repr == [[2, 4], [1, 4], [1, 3], [0, 5], [0, 4]]
19
+ assert a.prufer_repr == [4, 1, 4, 0]
20
+
21
+ assert Prufer.edges([0, 1, 2, 3], [1, 4, 5], [1, 4, 6]) == \
22
+ ([[0, 1], [1, 2], [1, 4], [2, 3], [4, 5], [4, 6]], 7)
23
+ assert Prufer([0]*4).size == Prufer([6]*4).size == 1296
24
+
25
+ # accept iterables but convert to list of lists
26
+ tree = [(0, 1), (1, 5), (0, 3), (0, 2), (2, 6), (4, 7), (2, 4)]
27
+ tree_lists = [list(t) for t in tree]
28
+ assert Prufer(tree).tree_repr == tree_lists
29
+ assert sorted(Prufer(set(tree)).tree_repr) == sorted(tree_lists)
30
+
31
+ raises(ValueError, lambda: Prufer([[1, 2], [3, 4]])) # 0 is missing
32
+ raises(ValueError, lambda: Prufer([[2, 3], [3, 4]])) # 0, 1 are missing
33
+ assert Prufer(*Prufer.edges([1, 2], [3, 4])).prufer_repr == [1, 3]
34
+ raises(ValueError, lambda: Prufer.edges(
35
+ [1, 3], [3, 4])) # a broken tree but edges doesn't care
36
+ raises(ValueError, lambda: Prufer.edges([1, 2], [5, 6]))
37
+ raises(ValueError, lambda: Prufer([[]]))
38
+
39
+ a = Prufer([[0, 1], [0, 2], [0, 3]])
40
+ b = a.next()
41
+ assert b.tree_repr == [[0, 2], [0, 1], [1, 3]]
42
+ assert b.rank == 1
43
+
44
+
45
+ def test_round_trip():
46
+ def doit(t, b):
47
+ e, n = Prufer.edges(*t)
48
+ t = Prufer(e, n)
49
+ a = sorted(t.tree_repr)
50
+ b = [i - 1 for i in b]
51
+ assert t.prufer_repr == b
52
+ assert sorted(Prufer(b).tree_repr) == a
53
+ assert Prufer.unrank(t.rank, n).prufer_repr == b
54
+
55
+ doit([[1, 2]], [])
56
+ doit([[2, 1, 3]], [1])
57
+ doit([[1, 3, 2]], [3])
58
+ doit([[1, 2, 3]], [2])
59
+ doit([[2, 1, 4], [1, 3]], [1, 1])
60
+ doit([[3, 2, 1, 4]], [2, 1])
61
+ doit([[3, 2, 1], [2, 4]], [2, 2])
62
+ doit([[1, 3, 2, 4]], [3, 2])
63
+ doit([[1, 4, 2, 3]], [4, 2])
64
+ doit([[3, 1, 4, 2]], [4, 1])
65
+ doit([[4, 2, 1, 3]], [1, 2])
66
+ doit([[1, 2, 4, 3]], [2, 4])
67
+ doit([[1, 3, 4, 2]], [3, 4])
68
+ doit([[2, 4, 1], [4, 3]], [4, 4])
69
+ doit([[1, 2, 3, 4]], [2, 3])
70
+ doit([[2, 3, 1], [3, 4]], [3, 3])
71
+ doit([[1, 4, 3, 2]], [4, 3])
72
+ doit([[2, 1, 4, 3]], [1, 4])
73
+ doit([[2, 1, 3, 4]], [1, 3])
74
+ doit([[6, 2, 1, 4], [1, 3, 5, 8], [3, 7]], [1, 2, 1, 3, 3, 5])
.venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_rewriting.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.combinatorics.fp_groups import FpGroup
2
+ from sympy.combinatorics.free_groups import free_group
3
+ from sympy.testing.pytest import raises
4
+
5
+
6
+ def test_rewriting():
7
+ F, a, b = free_group("a, b")
8
+ G = FpGroup(F, [a*b*a**-1*b**-1])
9
+ a, b = G.generators
10
+ R = G._rewriting_system
11
+ assert R.is_confluent
12
+
13
+ assert G.reduce(b**-1*a) == a*b**-1
14
+ assert G.reduce(b**3*a**4*b**-2*a) == a**5*b
15
+ assert G.equals(b**2*a**-1*b, b**4*a**-1*b**-1)
16
+
17
+ assert R.reduce_using_automaton(b*a*a**2*b**-1) == a**3
18
+ assert R.reduce_using_automaton(b**3*a**4*b**-2*a) == a**5*b
19
+ assert R.reduce_using_automaton(b**-1*a) == a*b**-1
20
+
21
+ G = FpGroup(F, [a**3, b**3, (a*b)**2])
22
+ R = G._rewriting_system
23
+ R.make_confluent()
24
+ # R._is_confluent should be set to True after
25
+ # a successful run of make_confluent
26
+ assert R.is_confluent
27
+ # but also the system should actually be confluent
28
+ assert R._check_confluence()
29
+ assert G.reduce(b*a**-1*b**-1*a**3*b**4*a**-1*b**-15) == a**-1*b**-1
30
+ # check for automaton reduction
31
+ assert R.reduce_using_automaton(b*a**-1*b**-1*a**3*b**4*a**-1*b**-15) == a**-1*b**-1
32
+
33
+ G = FpGroup(F, [a**2, b**3, (a*b)**4])
34
+ R = G._rewriting_system
35
+ assert G.reduce(a**2*b**-2*a**2*b) == b**-1
36
+ assert R.reduce_using_automaton(a**2*b**-2*a**2*b) == b**-1
37
+ assert G.reduce(a**3*b**-2*a**2*b) == a**-1*b**-1
38
+ assert R.reduce_using_automaton(a**3*b**-2*a**2*b) == a**-1*b**-1
39
+ # Check after adding a rule
40
+ R.add_rule(a**2, b)
41
+ assert R.reduce_using_automaton(a**2*b**-2*a**2*b) == b**-1
42
+ assert R.reduce_using_automaton(a**4*b**-2*a**2*b**3) == b
43
+
44
+ R.set_max(15)
45
+ raises(RuntimeError, lambda: R.add_rule(a**-3, b))
46
+ R.set_max(20)
47
+ R.add_rule(a**-3, b)
48
+
49
+ assert R.add_rule(a, a) == set()
.venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_schur_number.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.core import S, Rational
2
+ from sympy.combinatorics.schur_number import schur_partition, SchurNumber
3
+ from sympy.core.random import _randint
4
+ from sympy.testing.pytest import raises
5
+ from sympy.core.symbol import symbols
6
+
7
+
8
+ def _sum_free_test(subset):
9
+ """
10
+ Checks if subset is sum-free(There are no x,y,z in the subset such that
11
+ x + y = z)
12
+ """
13
+ for i in subset:
14
+ for j in subset:
15
+ assert (i + j in subset) is False
16
+
17
+
18
+ def test_schur_partition():
19
+ raises(ValueError, lambda: schur_partition(S.Infinity))
20
+ raises(ValueError, lambda: schur_partition(-1))
21
+ raises(ValueError, lambda: schur_partition(0))
22
+ assert schur_partition(2) == [[1, 2]]
23
+
24
+ random_number_generator = _randint(1000)
25
+ for _ in range(5):
26
+ n = random_number_generator(1, 1000)
27
+ result = schur_partition(n)
28
+ t = 0
29
+ numbers = []
30
+ for item in result:
31
+ _sum_free_test(item)
32
+ """
33
+ Checks if the occurrence of all numbers is exactly one
34
+ """
35
+ t += len(item)
36
+ for l in item:
37
+ assert (l in numbers) is False
38
+ numbers.append(l)
39
+ assert n == t
40
+
41
+ x = symbols("x")
42
+ raises(ValueError, lambda: schur_partition(x))
43
+
44
+ def test_schur_number():
45
+ first_known_schur_numbers = {1: 1, 2: 4, 3: 13, 4: 44, 5: 160}
46
+ for k in first_known_schur_numbers:
47
+ assert SchurNumber(k) == first_known_schur_numbers[k]
48
+
49
+ assert SchurNumber(S.Infinity) == S.Infinity
50
+ assert SchurNumber(0) == 0
51
+ raises(ValueError, lambda: SchurNumber(0.5))
52
+
53
+ n = symbols("n")
54
+ assert SchurNumber(n).lower_bound() == 3**n/2 - Rational(1, 2)
55
+ assert SchurNumber(8).lower_bound() == 5039
.venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_subsets.py ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.combinatorics.subsets import Subset, ksubsets
2
+ from sympy.testing.pytest import raises
3
+
4
+
5
+ def test_subset():
6
+ a = Subset(['c', 'd'], ['a', 'b', 'c', 'd'])
7
+ assert a.next_binary() == Subset(['b'], ['a', 'b', 'c', 'd'])
8
+ assert a.prev_binary() == Subset(['c'], ['a', 'b', 'c', 'd'])
9
+ assert a.next_lexicographic() == Subset(['d'], ['a', 'b', 'c', 'd'])
10
+ assert a.prev_lexicographic() == Subset(['c'], ['a', 'b', 'c', 'd'])
11
+ assert a.next_gray() == Subset(['c'], ['a', 'b', 'c', 'd'])
12
+ assert a.prev_gray() == Subset(['d'], ['a', 'b', 'c', 'd'])
13
+ assert a.rank_binary == 3
14
+ assert a.rank_lexicographic == 14
15
+ assert a.rank_gray == 2
16
+ assert a.cardinality == 16
17
+ assert a.size == 2
18
+ assert Subset.bitlist_from_subset(a, ['a', 'b', 'c', 'd']) == '0011'
19
+
20
+ a = Subset([2, 5, 7], [1, 2, 3, 4, 5, 6, 7])
21
+ assert a.next_binary() == Subset([2, 5, 6], [1, 2, 3, 4, 5, 6, 7])
22
+ assert a.prev_binary() == Subset([2, 5], [1, 2, 3, 4, 5, 6, 7])
23
+ assert a.next_lexicographic() == Subset([2, 6], [1, 2, 3, 4, 5, 6, 7])
24
+ assert a.prev_lexicographic() == Subset([2, 5, 6, 7], [1, 2, 3, 4, 5, 6, 7])
25
+ assert a.next_gray() == Subset([2, 5, 6, 7], [1, 2, 3, 4, 5, 6, 7])
26
+ assert a.prev_gray() == Subset([2, 5], [1, 2, 3, 4, 5, 6, 7])
27
+ assert a.rank_binary == 37
28
+ assert a.rank_lexicographic == 93
29
+ assert a.rank_gray == 57
30
+ assert a.cardinality == 128
31
+
32
+ superset = ['a', 'b', 'c', 'd']
33
+ assert Subset.unrank_binary(4, superset).rank_binary == 4
34
+ assert Subset.unrank_gray(10, superset).rank_gray == 10
35
+
36
+ superset = [1, 2, 3, 4, 5, 6, 7, 8, 9]
37
+ assert Subset.unrank_binary(33, superset).rank_binary == 33
38
+ assert Subset.unrank_gray(25, superset).rank_gray == 25
39
+
40
+ a = Subset([], ['a', 'b', 'c', 'd'])
41
+ i = 1
42
+ while a.subset != Subset(['d'], ['a', 'b', 'c', 'd']).subset:
43
+ a = a.next_lexicographic()
44
+ i = i + 1
45
+ assert i == 16
46
+
47
+ i = 1
48
+ while a.subset != Subset([], ['a', 'b', 'c', 'd']).subset:
49
+ a = a.prev_lexicographic()
50
+ i = i + 1
51
+ assert i == 16
52
+
53
+ raises(ValueError, lambda: Subset(['a', 'b'], ['a']))
54
+ raises(ValueError, lambda: Subset(['a'], ['b', 'c']))
55
+ raises(ValueError, lambda: Subset.subset_from_bitlist(['a', 'b'], '010'))
56
+
57
+ assert Subset(['a'], ['a', 'b']) != Subset(['b'], ['a', 'b'])
58
+ assert Subset(['a'], ['a', 'b']) != Subset(['a'], ['a', 'c'])
59
+
60
+ def test_ksubsets():
61
+ assert list(ksubsets([1, 2, 3], 2)) == [(1, 2), (1, 3), (2, 3)]
62
+ assert list(ksubsets([1, 2, 3, 4, 5], 2)) == [(1, 2), (1, 3), (1, 4),
63
+ (1, 5), (2, 3), (2, 4), (2, 5), (3, 4), (3, 5), (4, 5)]
.venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_tensor_can.py ADDED
@@ -0,0 +1,560 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.combinatorics.permutations import Permutation, Perm
2
+ from sympy.combinatorics.tensor_can import (perm_af_direct_product, dummy_sgs,
3
+ riemann_bsgs, get_symmetric_group_sgs, canonicalize, bsgs_direct_product)
4
+ from sympy.combinatorics.testutil import canonicalize_naive, graph_certificate
5
+ from sympy.testing.pytest import skip, XFAIL
6
+
7
+ def test_perm_af_direct_product():
8
+ gens1 = [[1,0,2,3], [0,1,3,2]]
9
+ gens2 = [[1,0]]
10
+ assert perm_af_direct_product(gens1, gens2, 0) == [[1, 0, 2, 3, 4, 5], [0, 1, 3, 2, 4, 5], [0, 1, 2, 3, 5, 4]]
11
+ gens1 = [[1,0,2,3,5,4], [0,1,3,2,4,5]]
12
+ gens2 = [[1,0,2,3]]
13
+ assert [[1, 0, 2, 3, 4, 5, 7, 6], [0, 1, 3, 2, 4, 5, 6, 7], [0, 1, 2, 3, 5, 4, 6, 7]]
14
+
15
+ def test_dummy_sgs():
16
+ a = dummy_sgs([1,2], 0, 4)
17
+ assert a == [[0,2,1,3,4,5]]
18
+ a = dummy_sgs([2,3,4,5], 0, 8)
19
+ assert a == [x._array_form for x in [Perm(9)(2,3), Perm(9)(4,5),
20
+ Perm(9)(2,4)(3,5)]]
21
+
22
+ a = dummy_sgs([2,3,4,5], 1, 8)
23
+ assert a == [x._array_form for x in [Perm(2,3)(8,9), Perm(4,5)(8,9),
24
+ Perm(9)(2,4)(3,5)]]
25
+
26
+ def test_get_symmetric_group_sgs():
27
+ assert get_symmetric_group_sgs(2) == ([0], [Permutation(3)(0,1)])
28
+ assert get_symmetric_group_sgs(2, 1) == ([0], [Permutation(0,1)(2,3)])
29
+ assert get_symmetric_group_sgs(3) == ([0,1], [Permutation(4)(0,1), Permutation(4)(1,2)])
30
+ assert get_symmetric_group_sgs(3, 1) == ([0,1], [Permutation(0,1)(3,4), Permutation(1,2)(3,4)])
31
+ assert get_symmetric_group_sgs(4) == ([0,1,2], [Permutation(5)(0,1), Permutation(5)(1,2), Permutation(5)(2,3)])
32
+ assert get_symmetric_group_sgs(4, 1) == ([0,1,2], [Permutation(0,1)(4,5), Permutation(1,2)(4,5), Permutation(2,3)(4,5)])
33
+
34
+
35
+ def test_canonicalize_no_slot_sym():
36
+ # cases in which there is no slot symmetry after fixing the
37
+ # free indices; here and in the following if the symmetry of the
38
+ # metric is not specified, it is assumed to be symmetric.
39
+ # If it is not specified, tensors are commuting.
40
+
41
+ # A_d0 * B^d0; g = [1,0, 2,3]; T_c = A^d0*B_d0; can = [0,1,2,3]
42
+ base1, gens1 = get_symmetric_group_sgs(1)
43
+ dummies = [0, 1]
44
+ g = Permutation([1,0,2,3])
45
+ can = canonicalize(g, dummies, 0, (base1,gens1,1,0), (base1,gens1,1,0))
46
+ assert can == [0,1,2,3]
47
+ # equivalently
48
+ can = canonicalize(g, dummies, 0, (base1, gens1, 2, None))
49
+ assert can == [0,1,2,3]
50
+
51
+ # with antisymmetric metric; T_c = -A^d0*B_d0; can = [0,1,3,2]
52
+ can = canonicalize(g, dummies, 1, (base1,gens1,1,0), (base1,gens1,1,0))
53
+ assert can == [0,1,3,2]
54
+
55
+ # A^a * B^b; ord = [a,b]; g = [0,1,2,3]; can = g
56
+ g = Permutation([0,1,2,3])
57
+ dummies = []
58
+ t0 = t1 = (base1, gens1, 1, 0)
59
+ can = canonicalize(g, dummies, 0, t0, t1)
60
+ assert can == [0,1,2,3]
61
+ # B^b * A^a
62
+ g = Permutation([1,0,2,3])
63
+ can = canonicalize(g, dummies, 0, t0, t1)
64
+ assert can == [1,0,2,3]
65
+
66
+ # A symmetric
67
+ # A^{b}_{d0}*A^{d0, a} order a,b,d0,-d0; T_c = A^{a d0}*A{b}_{d0}
68
+ # g = [1,3,2,0,4,5]; can = [0,2,1,3,4,5]
69
+ base2, gens2 = get_symmetric_group_sgs(2)
70
+ dummies = [2,3]
71
+ g = Permutation([1,3,2,0,4,5])
72
+ can = canonicalize(g, dummies, 0, (base2, gens2, 2, 0))
73
+ assert can == [0, 2, 1, 3, 4, 5]
74
+ # with antisymmetric metric
75
+ can = canonicalize(g, dummies, 1, (base2, gens2, 2, 0))
76
+ assert can == [0, 2, 1, 3, 4, 5]
77
+ # A^{a}_{d0}*A^{d0, b}
78
+ g = Permutation([0,3,2,1,4,5])
79
+ can = canonicalize(g, dummies, 1, (base2, gens2, 2, 0))
80
+ assert can == [0, 2, 1, 3, 5, 4]
81
+
82
+ # A, B symmetric
83
+ # A^b_d0*B^{d0,a}; g=[1,3,2,0,4,5]
84
+ # T_c = A^{b,d0}*B_{a,d0}; can = [1,2,0,3,4,5]
85
+ dummies = [2,3]
86
+ g = Permutation([1,3,2,0,4,5])
87
+ can = canonicalize(g, dummies, 0, (base2,gens2,1,0), (base2,gens2,1,0))
88
+ assert can == [1,2,0,3,4,5]
89
+ # same with antisymmetric metric
90
+ can = canonicalize(g, dummies, 1, (base2,gens2,1,0), (base2,gens2,1,0))
91
+ assert can == [1,2,0,3,5,4]
92
+
93
+ # A^{d1}_{d0}*B^d0*C_d1 ord=[d0,-d0,d1,-d1]; g = [2,1,0,3,4,5]
94
+ # T_c = A^{d0 d1}*B_d0*C_d1; can = [0,2,1,3,4,5]
95
+ base1, gens1 = get_symmetric_group_sgs(1)
96
+ base2, gens2 = get_symmetric_group_sgs(2)
97
+ g = Permutation([2,1,0,3,4,5])
98
+ dummies = [0,1,2,3]
99
+ t0 = (base2, gens2, 1, 0)
100
+ t1 = t2 = (base1, gens1, 1, 0)
101
+ can = canonicalize(g, dummies, 0, t0, t1, t2)
102
+ assert can == [0, 2, 1, 3, 4, 5]
103
+
104
+ # A without symmetry
105
+ # A^{d1}_{d0}*B^d0*C_d1 ord=[d0,-d0,d1,-d1]; g = [2,1,0,3,4,5]
106
+ # T_c = A^{d0 d1}*B_d1*C_d0; can = [0,2,3,1,4,5]
107
+ g = Permutation([2,1,0,3,4,5])
108
+ dummies = [0,1,2,3]
109
+ t0 = ([], [Permutation(list(range(4)))], 1, 0)
110
+ can = canonicalize(g, dummies, 0, t0, t1, t2)
111
+ assert can == [0,2,3,1,4,5]
112
+ # A, B without symmetry
113
+ # A^{d1}_{d0}*B_{d1}^{d0}; g = [2,1,3,0,4,5]
114
+ # T_c = A^{d0 d1}*B_{d0 d1}; can = [0,2,1,3,4,5]
115
+ t0 = t1 = ([], [Permutation(list(range(4)))], 1, 0)
116
+ dummies = [0,1,2,3]
117
+ g = Permutation([2,1,3,0,4,5])
118
+ can = canonicalize(g, dummies, 0, t0, t1)
119
+ assert can == [0, 2, 1, 3, 4, 5]
120
+ # A_{d0}^{d1}*B_{d1}^{d0}; g = [1,2,3,0,4,5]
121
+ # T_c = A^{d0 d1}*B_{d1 d0}; can = [0,2,3,1,4,5]
122
+ g = Permutation([1,2,3,0,4,5])
123
+ can = canonicalize(g, dummies, 0, t0, t1)
124
+ assert can == [0,2,3,1,4,5]
125
+
126
+ # A, B, C without symmetry
127
+ # A^{d1 d0}*B_{a d0}*C_{d1 b} ord=[a,b,d0,-d0,d1,-d1]
128
+ # g=[4,2,0,3,5,1,6,7]
129
+ # T_c=A^{d0 d1}*B_{a d1}*C_{d0 b}; can = [2,4,0,5,3,1,6,7]
130
+ t0 = t1 = t2 = ([], [Permutation(list(range(4)))], 1, 0)
131
+ dummies = [2,3,4,5]
132
+ g = Permutation([4,2,0,3,5,1,6,7])
133
+ can = canonicalize(g, dummies, 0, t0, t1, t2)
134
+ assert can == [2,4,0,5,3,1,6,7]
135
+
136
+ # A symmetric, B and C without symmetry
137
+ # A^{d1 d0}*B_{a d0}*C_{d1 b} ord=[a,b,d0,-d0,d1,-d1]
138
+ # g=[4,2,0,3,5,1,6,7]
139
+ # T_c = A^{d0 d1}*B_{a d0}*C_{d1 b}; can = [2,4,0,3,5,1,6,7]
140
+ t0 = (base2,gens2,1,0)
141
+ t1 = t2 = ([], [Permutation(list(range(4)))], 1, 0)
142
+ dummies = [2,3,4,5]
143
+ g = Permutation([4,2,0,3,5,1,6,7])
144
+ can = canonicalize(g, dummies, 0, t0, t1, t2)
145
+ assert can == [2,4,0,3,5,1,6,7]
146
+
147
+ # A and C symmetric, B without symmetry
148
+ # A^{d1 d0}*B_{a d0}*C_{d1 b} ord=[a,b,d0,-d0,d1,-d1]
149
+ # g=[4,2,0,3,5,1,6,7]
150
+ # T_c = A^{d0 d1}*B_{a d0}*C_{b d1}; can = [2,4,0,3,1,5,6,7]
151
+ t0 = t2 = (base2,gens2,1,0)
152
+ t1 = ([], [Permutation(list(range(4)))], 1, 0)
153
+ dummies = [2,3,4,5]
154
+ g = Permutation([4,2,0,3,5,1,6,7])
155
+ can = canonicalize(g, dummies, 0, t0, t1, t2)
156
+ assert can == [2,4,0,3,1,5,6,7]
157
+
158
+ # A symmetric, B without symmetry, C antisymmetric
159
+ # A^{d1 d0}*B_{a d0}*C_{d1 b} ord=[a,b,d0,-d0,d1,-d1]
160
+ # g=[4,2,0,3,5,1,6,7]
161
+ # T_c = -A^{d0 d1}*B_{a d0}*C_{b d1}; can = [2,4,0,3,1,5,7,6]
162
+ t0 = (base2,gens2, 1, 0)
163
+ t1 = ([], [Permutation(list(range(4)))], 1, 0)
164
+ base2a, gens2a = get_symmetric_group_sgs(2, 1)
165
+ t2 = (base2a, gens2a, 1, 0)
166
+ dummies = [2,3,4,5]
167
+ g = Permutation([4,2,0,3,5,1,6,7])
168
+ can = canonicalize(g, dummies, 0, t0, t1, t2)
169
+ assert can == [2,4,0,3,1,5,7,6]
170
+
171
+
172
+ def test_canonicalize_no_dummies():
173
+ base1, gens1 = get_symmetric_group_sgs(1)
174
+ base2, gens2 = get_symmetric_group_sgs(2)
175
+ base2a, gens2a = get_symmetric_group_sgs(2, 1)
176
+
177
+ # A commuting
178
+ # A^c A^b A^a; ord = [a,b,c]; g = [2,1,0,3,4]
179
+ # T_c = A^a A^b A^c; can = list(range(5))
180
+ g = Permutation([2,1,0,3,4])
181
+ can = canonicalize(g, [], 0, (base1, gens1, 3, 0))
182
+ assert can == list(range(5))
183
+
184
+ # A anticommuting
185
+ # A^c A^b A^a; ord = [a,b,c]; g = [2,1,0,3,4]
186
+ # T_c = -A^a A^b A^c; can = [0,1,2,4,3]
187
+ g = Permutation([2,1,0,3,4])
188
+ can = canonicalize(g, [], 0, (base1, gens1, 3, 1))
189
+ assert can == [0,1,2,4,3]
190
+
191
+ # A commuting and symmetric
192
+ # A^{b,d}*A^{c,a}; ord = [a,b,c,d]; g = [1,3,2,0,4,5]
193
+ # T_c = A^{a c}*A^{b d}; can = [0,2,1,3,4,5]
194
+ g = Permutation([1,3,2,0,4,5])
195
+ can = canonicalize(g, [], 0, (base2, gens2, 2, 0))
196
+ assert can == [0,2,1,3,4,5]
197
+
198
+ # A anticommuting and symmetric
199
+ # A^{b,d}*A^{c,a}; ord = [a,b,c,d]; g = [1,3,2,0,4,5]
200
+ # T_c = -A^{a c}*A^{b d}; can = [0,2,1,3,5,4]
201
+ g = Permutation([1,3,2,0,4,5])
202
+ can = canonicalize(g, [], 0, (base2, gens2, 2, 1))
203
+ assert can == [0,2,1,3,5,4]
204
+ # A^{c,a}*A^{b,d} ; g = [2,0,1,3,4,5]
205
+ # T_c = A^{a c}*A^{b d}; can = [0,2,1,3,4,5]
206
+ g = Permutation([2,0,1,3,4,5])
207
+ can = canonicalize(g, [], 0, (base2, gens2, 2, 1))
208
+ assert can == [0,2,1,3,4,5]
209
+
210
+ def test_no_metric_symmetry():
211
+ # no metric symmetry
212
+ # A^d1_d0 * A^d0_d1; ord = [d0,-d0,d1,-d1]; g= [2,1,0,3,4,5]
213
+ # T_c = A^d0_d1 * A^d1_d0; can = [0,3,2,1,4,5]
214
+ g = Permutation([2,1,0,3,4,5])
215
+ can = canonicalize(g, list(range(4)), None, [[], [Permutation(list(range(4)))], 2, 0])
216
+ assert can == [0,3,2,1,4,5]
217
+
218
+ # A^d1_d2 * A^d0_d3 * A^d2_d1 * A^d3_d0
219
+ # ord = [d0,-d0,d1,-d1,d2,-d2,d3,-d3]
220
+ # 0 1 2 3 4 5 6 7
221
+ # g = [2,5,0,7,4,3,6,1,8,9]
222
+ # T_c = A^d0_d1 * A^d1_d0 * A^d2_d3 * A^d3_d2
223
+ # can = [0,3,2,1,4,7,6,5,8,9]
224
+ g = Permutation([2,5,0,7,4,3,6,1,8,9])
225
+ #can = canonicalize(g, list(range(8)), 0, [[], [list(range(4))], 4, 0])
226
+ #assert can == [0, 2, 3, 1, 4, 6, 7, 5, 8, 9]
227
+ can = canonicalize(g, list(range(8)), None, [[], [Permutation(list(range(4)))], 4, 0])
228
+ assert can == [0, 3, 2, 1, 4, 7, 6, 5, 8, 9]
229
+
230
+ # A^d0_d2 * A^d1_d3 * A^d3_d0 * A^d2_d1
231
+ # g = [0,5,2,7,6,1,4,3,8,9]
232
+ # T_c = A^d0_d1 * A^d1_d2 * A^d2_d3 * A^d3_d0
233
+ # can = [0,3,2,5,4,7,6,1,8,9]
234
+ g = Permutation([0,5,2,7,6,1,4,3,8,9])
235
+ can = canonicalize(g, list(range(8)), None, [[], [Permutation(list(range(4)))], 4, 0])
236
+ assert can == [0,3,2,5,4,7,6,1,8,9]
237
+
238
+ g = Permutation([12,7,10,3,14,13,4,11,6,1,2,9,0,15,8,5,16,17])
239
+ can = canonicalize(g, list(range(16)), None, [[], [Permutation(list(range(4)))], 8, 0])
240
+ assert can == [0,3,2,5,4,7,6,1,8,11,10,13,12,15,14,9,16,17]
241
+
242
+ def test_canonical_free():
243
+ # t = A^{d0 a1}*A_d0^a0
244
+ # ord = [a0,a1,d0,-d0]; g = [2,1,3,0,4,5]; dummies = [[2,3]]
245
+ # t_c = A_d0^a0*A^{d0 a1}
246
+ # can = [3,0, 2,1, 4,5]
247
+ g = Permutation([2,1,3,0,4,5])
248
+ dummies = [[2,3]]
249
+ can = canonicalize(g, dummies, [None], ([], [Permutation(3)], 2, 0))
250
+ assert can == [3,0, 2,1, 4,5]
251
+
252
+ def test_canonicalize1():
253
+ base1, gens1 = get_symmetric_group_sgs(1)
254
+ base1a, gens1a = get_symmetric_group_sgs(1, 1)
255
+ base2, gens2 = get_symmetric_group_sgs(2)
256
+ base3, gens3 = get_symmetric_group_sgs(3)
257
+ base2a, gens2a = get_symmetric_group_sgs(2, 1)
258
+ base3a, gens3a = get_symmetric_group_sgs(3, 1)
259
+
260
+ # A_d0*A^d0; ord = [d0,-d0]; g = [1,0,2,3]
261
+ # T_c = A^d0*A_d0; can = [0,1,2,3]
262
+ g = Permutation([1,0,2,3])
263
+ can = canonicalize(g, [0, 1], 0, (base1, gens1, 2, 0))
264
+ assert can == list(range(4))
265
+
266
+ # A commuting
267
+ # A_d0*A_d1*A_d2*A^d2*A^d1*A^d0; ord=[d0,-d0,d1,-d1,d2,-d2]
268
+ # g = [1,3,5,4,2,0,6,7]
269
+ # T_c = A^d0*A_d0*A^d1*A_d1*A^d2*A_d2; can = list(range(8))
270
+ g = Permutation([1,3,5,4,2,0,6,7])
271
+ can = canonicalize(g, list(range(6)), 0, (base1, gens1, 6, 0))
272
+ assert can == list(range(8))
273
+
274
+ # A anticommuting
275
+ # A_d0*A_d1*A_d2*A^d2*A^d1*A^d0; ord=[d0,-d0,d1,-d1,d2,-d2]
276
+ # g = [1,3,5,4,2,0,6,7]
277
+ # T_c 0; can = 0
278
+ g = Permutation([1,3,5,4,2,0,6,7])
279
+ can = canonicalize(g, list(range(6)), 0, (base1, gens1, 6, 1))
280
+ assert can == 0
281
+ can1 = canonicalize_naive(g, list(range(6)), 0, (base1, gens1, 6, 1))
282
+ assert can1 == 0
283
+
284
+ # A commuting symmetric
285
+ # A^{d0 b}*A^a_d1*A^d1_d0; ord=[a,b,d0,-d0,d1,-d1]
286
+ # g = [2,1,0,5,4,3,6,7]
287
+ # T_c = A^{a d0}*A^{b d1}*A_{d0 d1}; can = [0,2,1,4,3,5,6,7]
288
+ g = Permutation([2,1,0,5,4,3,6,7])
289
+ can = canonicalize(g, list(range(2,6)), 0, (base2, gens2, 3, 0))
290
+ assert can == [0,2,1,4,3,5,6,7]
291
+
292
+ # A, B commuting symmetric
293
+ # A^{d0 b}*A^d1_d0*B^a_d1; ord=[a,b,d0,-d0,d1,-d1]
294
+ # g = [2,1,4,3,0,5,6,7]
295
+ # T_c = A^{b d0}*A_d0^d1*B^a_d1; can = [1,2,3,4,0,5,6,7]
296
+ g = Permutation([2,1,4,3,0,5,6,7])
297
+ can = canonicalize(g, list(range(2,6)), 0, (base2,gens2,2,0), (base2,gens2,1,0))
298
+ assert can == [1,2,3,4,0,5,6,7]
299
+
300
+ # A commuting symmetric
301
+ # A^{d1 d0 b}*A^{a}_{d1 d0}; ord=[a,b, d0,-d0,d1,-d1]
302
+ # g = [4,2,1,0,5,3,6,7]
303
+ # T_c = A^{a d0 d1}*A^{b}_{d0 d1}; can = [0,2,4,1,3,5,6,7]
304
+ g = Permutation([4,2,1,0,5,3,6,7])
305
+ can = canonicalize(g, list(range(2,6)), 0, (base3, gens3, 2, 0))
306
+ assert can == [0,2,4,1,3,5,6,7]
307
+
308
+
309
+ # A^{d3 d0 d2}*A^a0_{d1 d2}*A^d1_d3^a1*A^{a2 a3}_d0
310
+ # ord = [a0,a1,a2,a3,d0,-d0,d1,-d1,d2,-d2,d3,-d3]
311
+ # 0 1 2 3 4 5 6 7 8 9 10 11
312
+ # g = [10,4,8, 0,7,9, 6,11,1, 2,3,5, 12,13]
313
+ # T_c = A^{a0 d0 d1}*A^a1_d0^d2*A^{a2 a3 d3}*A_{d1 d2 d3}
314
+ # can = [0,4,6, 1,5,8, 2,3,10, 7,9,11, 12,13]
315
+ g = Permutation([10,4,8, 0,7,9, 6,11,1, 2,3,5, 12,13])
316
+ can = canonicalize(g, list(range(4,12)), 0, (base3, gens3, 4, 0))
317
+ assert can == [0,4,6, 1,5,8, 2,3,10, 7,9,11, 12,13]
318
+
319
+ # A commuting symmetric, B antisymmetric
320
+ # A^{d0 d1 d2} * A_{d2 d3 d1} * B_d0^d3
321
+ # ord = [d0,-d0,d1,-d1,d2,-d2,d3,-d3]
322
+ # g = [0,2,4,5,7,3,1,6,8,9]
323
+ # in this esxample and in the next three,
324
+ # renaming dummy indices and using symmetry of A,
325
+ # T = A^{d0 d1 d2} * A_{d0 d1 d3} * B_d2^d3
326
+ # can = 0
327
+ g = Permutation([0,2,4,5,7,3,1,6,8,9])
328
+ can = canonicalize(g, list(range(8)), 0, (base3, gens3,2,0), (base2a,gens2a,1,0))
329
+ assert can == 0
330
+ # A anticommuting symmetric, B anticommuting
331
+ # A^{d0 d1 d2} * A_{d2 d3 d1} * B_d0^d3
332
+ # T_c = A^{d0 d1 d2} * A_{d0 d1}^d3 * B_{d2 d3}
333
+ # can = [0,2,4, 1,3,6, 5,7, 8,9]
334
+ can = canonicalize(g, list(range(8)), 0, (base3, gens3,2,1), (base2a,gens2a,1,0))
335
+ assert can == [0,2,4, 1,3,6, 5,7, 8,9]
336
+ # A anticommuting symmetric, B antisymmetric commuting, antisymmetric metric
337
+ # A^{d0 d1 d2} * A_{d2 d3 d1} * B_d0^d3
338
+ # T_c = -A^{d0 d1 d2} * A_{d0 d1}^d3 * B_{d2 d3}
339
+ # can = [0,2,4, 1,3,6, 5,7, 9,8]
340
+ can = canonicalize(g, list(range(8)), 1, (base3, gens3,2,1), (base2a,gens2a,1,0))
341
+ assert can == [0,2,4, 1,3,6, 5,7, 9,8]
342
+
343
+ # A anticommuting symmetric, B anticommuting anticommuting,
344
+ # no metric symmetry
345
+ # A^{d0 d1 d2} * A_{d2 d3 d1} * B_d0^d3
346
+ # T_c = A^{d0 d1 d2} * A_{d0 d1 d3} * B_d2^d3
347
+ # can = [0,2,4, 1,3,7, 5,6, 8,9]
348
+ can = canonicalize(g, list(range(8)), None, (base3, gens3,2,1), (base2a,gens2a,1,0))
349
+ assert can == [0,2,4,1,3,7,5,6,8,9]
350
+
351
+ # Gamma anticommuting
352
+ # Gamma_{mu nu} * gamma^rho * Gamma^{nu mu alpha}
353
+ # ord = [alpha, rho, mu,-mu,nu,-nu]
354
+ # g = [3,5,1,4,2,0,6,7]
355
+ # T_c = -Gamma^{mu nu} * gamma^rho * Gamma_{alpha mu nu}
356
+ # can = [2,4,1,0,3,5,7,6]]
357
+ g = Permutation([3,5,1,4,2,0,6,7])
358
+ t0 = (base2a, gens2a, 1, None)
359
+ t1 = (base1, gens1, 1, None)
360
+ t2 = (base3a, gens3a, 1, None)
361
+ can = canonicalize(g, list(range(2, 6)), 0, t0, t1, t2)
362
+ assert can == [2,4,1,0,3,5,7,6]
363
+
364
+ # Gamma_{mu nu} * Gamma^{gamma beta} * gamma_rho * Gamma^{nu mu alpha}
365
+ # ord = [alpha, beta, gamma, -rho, mu,-mu,nu,-nu]
366
+ # 0 1 2 3 4 5 6 7
367
+ # g = [5,7,2,1,3,6,4,0,8,9]
368
+ # T_c = Gamma^{mu nu} * Gamma^{beta gamma} * gamma_rho * Gamma^alpha_{mu nu} # can = [4,6,1,2,3,0,5,7,8,9]
369
+ t0 = (base2a, gens2a, 2, None)
370
+ g = Permutation([5,7,2,1,3,6,4,0,8,9])
371
+ can = canonicalize(g, list(range(4, 8)), 0, t0, t1, t2)
372
+ assert can == [4,6,1,2,3,0,5,7,8,9]
373
+
374
+ # f^a_{b,c} antisymmetric in b,c; A_mu^a no symmetry
375
+ # f^c_{d a} * f_{c e b} * A_mu^d * A_nu^a * A^{nu e} * A^{mu b}
376
+ # ord = [mu,-mu,nu,-nu,a,-a,b,-b,c,-c,d,-d, e, -e]
377
+ # 0 1 2 3 4 5 6 7 8 9 10 11 12 13
378
+ # g = [8,11,5, 9,13,7, 1,10, 3,4, 2,12, 0,6, 14,15]
379
+ # T_c = -f^{a b c} * f_a^{d e} * A^mu_b * A_{mu d} * A^nu_c * A_{nu e}
380
+ # can = [4,6,8, 5,10,12, 0,7, 1,11, 2,9, 3,13, 15,14]
381
+ g = Permutation([8,11,5, 9,13,7, 1,10, 3,4, 2,12, 0,6, 14,15])
382
+ base_f, gens_f = bsgs_direct_product(base1, gens1, base2a, gens2a)
383
+ base_A, gens_A = bsgs_direct_product(base1, gens1, base1, gens1)
384
+ t0 = (base_f, gens_f, 2, 0)
385
+ t1 = (base_A, gens_A, 4, 0)
386
+ can = canonicalize(g, [list(range(4)), list(range(4, 14))], [0, 0], t0, t1)
387
+ assert can == [4,6,8, 5,10,12, 0,7, 1,11, 2,9, 3,13, 15,14]
388
+
389
+
390
+ def test_riemann_invariants():
391
+ baser, gensr = riemann_bsgs
392
+ # R^{d0 d1}_{d1 d0}; ord = [d0,-d0,d1,-d1]; g = [0,2,3,1,4,5]
393
+ # T_c = -R^{d0 d1}_{d0 d1}; can = [0,2,1,3,5,4]
394
+ g = Permutation([0,2,3,1,4,5])
395
+ can = canonicalize(g, list(range(2, 4)), 0, (baser, gensr, 1, 0))
396
+ assert can == [0,2,1,3,5,4]
397
+ # use a non minimal BSGS
398
+ can = canonicalize(g, list(range(2, 4)), 0, ([2, 0], [Permutation([1,0,2,3,5,4]), Permutation([2,3,0,1,4,5])], 1, 0))
399
+ assert can == [0,2,1,3,5,4]
400
+
401
+ """
402
+ The following tests in test_riemann_invariants and in
403
+ test_riemann_invariants1 have been checked using xperm.c from XPerm in
404
+ in [1] and with an older version contained in [2]
405
+
406
+ [1] xperm.c part of xPerm written by J. M. Martin-Garcia
407
+ http://www.xact.es/index.html
408
+ [2] test_xperm.cc in cadabra by Kasper Peeters, http://cadabra.phi-sci.com/
409
+ """
410
+ # R_d11^d1_d0^d5 * R^{d6 d4 d0}_d5 * R_{d7 d2 d8 d9} *
411
+ # R_{d10 d3 d6 d4} * R^{d2 d7 d11}_d1 * R^{d8 d9 d3 d10}
412
+ # ord: contravariant d_k ->2*k, covariant d_k -> 2*k+1
413
+ # T_c = R^{d0 d1 d2 d3} * R_{d0 d1}^{d4 d5} * R_{d2 d3}^{d6 d7} *
414
+ # R_{d4 d5}^{d8 d9} * R_{d6 d7}^{d10 d11} * R_{d8 d9 d10 d11}
415
+ g = Permutation([23,2,1,10,12,8,0,11,15,5,17,19,21,7,13,9,4,14,22,3,16,18,6,20,24,25])
416
+ can = canonicalize(g, list(range(24)), 0, (baser, gensr, 6, 0))
417
+ assert can == [0,2,4,6,1,3,8,10,5,7,12,14,9,11,16,18,13,15,20,22,17,19,21,23,24,25]
418
+
419
+ # use a non minimal BSGS
420
+ can = canonicalize(g, list(range(24)), 0, ([2, 0], [Permutation([1,0,2,3,5,4]), Permutation([2,3,0,1,4,5])], 6, 0))
421
+ assert can == [0,2,4,6,1,3,8,10,5,7,12,14,9,11,16,18,13,15,20,22,17,19,21,23,24,25]
422
+
423
+ g = Permutation([0,2,5,7,4,6,9,11,8,10,13,15,12,14,17,19,16,18,21,23,20,22,25,27,24,26,29,31,28,30,33,35,32,34,37,39,36,38,1,3,40,41])
424
+ can = canonicalize(g, list(range(40)), 0, (baser, gensr, 10, 0))
425
+ assert can == [0,2,4,6,1,3,8,10,5,7,12,14,9,11,16,18,13,15,20,22,17,19,24,26,21,23,28,30,25,27,32,34,29,31,36,38,33,35,37,39,40,41]
426
+
427
+
428
+ @XFAIL
429
+ def test_riemann_invariants1():
430
+ skip('takes too much time')
431
+ baser, gensr = riemann_bsgs
432
+ g = Permutation([17, 44, 11, 3, 0, 19, 23, 15, 38, 4, 25, 27, 43, 36, 22, 14, 8, 30, 41, 20, 2, 10, 12, 28, 18, 1, 29, 13, 37, 42, 33, 7, 9, 31, 24, 26, 39, 5, 34, 47, 32, 6, 21, 40, 35, 46, 45, 16, 48, 49])
433
+ can = canonicalize(g, list(range(48)), 0, (baser, gensr, 12, 0))
434
+ assert can == [0, 2, 4, 6, 1, 3, 8, 10, 5, 7, 12, 14, 9, 11, 16, 18, 13, 15, 20, 22, 17, 19, 24, 26, 21, 23, 28, 30, 25, 27, 32, 34, 29, 31, 36, 38, 33, 35, 40, 42, 37, 39, 44, 46, 41, 43, 45, 47, 48, 49]
435
+
436
+ g = Permutation([0,2,4,6, 7,8,10,12, 14,16,18,20, 19,22,24,26, 5,21,28,30, 32,34,36,38, 40,42,44,46, 13,48,50,52, 15,49,54,56, 17,33,41,58, 9,23,60,62, 29,35,63,64, 3,45,66,68, 25,37,47,57, 11,31,69,70, 27,39,53,72, 1,59,73,74, 55,61,67,76, 43,65,75,78, 51,71,77,79, 80,81])
437
+ can = canonicalize(g, list(range(80)), 0, (baser, gensr, 20, 0))
438
+ assert can == [0,2,4,6, 1,8,10,12, 3,14,16,18, 5,20,22,24, 7,26,28,30, 9,15,32,34, 11,36,23,38, 13,40,42,44, 17,39,29,46, 19,48,43,50, 21,45,52,54, 25,56,33,58, 27,60,53,62, 31,51,64,66, 35,65,47,68, 37,70,49,72, 41,74,57,76, 55,67,59,78, 61,69,71,75, 63,79,73,77, 80,81]
439
+
440
+
441
+ def test_riemann_products():
442
+ baser, gensr = riemann_bsgs
443
+ base1, gens1 = get_symmetric_group_sgs(1)
444
+ base2, gens2 = get_symmetric_group_sgs(2)
445
+ base2a, gens2a = get_symmetric_group_sgs(2, 1)
446
+
447
+ # R^{a b d0}_d0 = 0
448
+ g = Permutation([0,1,2,3,4,5])
449
+ can = canonicalize(g, list(range(2,4)), 0, (baser, gensr, 1, 0))
450
+ assert can == 0
451
+
452
+ # R^{d0 b a}_d0 ; ord = [a,b,d0,-d0}; g = [2,1,0,3,4,5]
453
+ # T_c = -R^{a d0 b}_d0; can = [0,2,1,3,5,4]
454
+ g = Permutation([2,1,0,3,4,5])
455
+ can = canonicalize(g, list(range(2, 4)), 0, (baser, gensr, 1, 0))
456
+ assert can == [0,2,1,3,5,4]
457
+
458
+ # R^d1_d2^b_d0 * R^{d0 a}_d1^d2; ord=[a,b,d0,-d0,d1,-d1,d2,-d2]
459
+ # g = [4,7,1,3,2,0,5,6,8,9]
460
+ # T_c = -R^{a d0 d1 d2}* R^b_{d0 d1 d2}
461
+ # can = [0,2,4,6,1,3,5,7,9,8]
462
+ g = Permutation([4,7,1,3,2,0,5,6,8,9])
463
+ can = canonicalize(g, list(range(2,8)), 0, (baser, gensr, 2, 0))
464
+ assert can == [0,2,4,6,1,3,5,7,9,8]
465
+ can1 = canonicalize_naive(g, list(range(2,8)), 0, (baser, gensr, 2, 0))
466
+ assert can == can1
467
+
468
+ # A symmetric commuting
469
+ # R^{d6 d5}_d2^d1 * R^{d4 d0 d2 d3} * A_{d6 d0} A_{d3 d1} * A_{d4 d5}
470
+ # g = [12,10,5,2, 8,0,4,6, 13,1, 7,3, 9,11,14,15]
471
+ # T_c = -R^{d0 d1 d2 d3} * R_d0^{d4 d5 d6} * A_{d1 d4}*A_{d2 d5}*A_{d3 d6}
472
+
473
+ g = Permutation([12,10,5,2,8,0,4,6,13,1,7,3,9,11,14,15])
474
+ can = canonicalize(g, list(range(14)), 0, ((baser,gensr,2,0)), (base2,gens2,3,0))
475
+ assert can == [0, 2, 4, 6, 1, 8, 10, 12, 3, 9, 5, 11, 7, 13, 15, 14]
476
+
477
+ # R^{d2 a0 a2 d0} * R^d1_d2^{a1 a3} * R^{a4 a5}_{d0 d1}
478
+ # ord = [a0,a1,a2,a3,a4,a5,d0,-d0,d1,-d1,d2,-d2]
479
+ # 0 1 2 3 4 5 6 7 8 9 10 11
480
+ # can = [0, 6, 2, 8, 1, 3, 7, 10, 4, 5, 9, 11, 12, 13]
481
+ # T_c = R^{a0 d0 a2 d1}*R^{a1 a3}_d0^d2*R^{a4 a5}_{d1 d2}
482
+ g = Permutation([10,0,2,6,8,11,1,3,4,5,7,9,12,13])
483
+ can = canonicalize(g, list(range(6,12)), 0, (baser, gensr, 3, 0))
484
+ assert can == [0, 6, 2, 8, 1, 3, 7, 10, 4, 5, 9, 11, 12, 13]
485
+ #can1 = canonicalize_naive(g, list(range(6,12)), 0, (baser, gensr, 3, 0))
486
+ #assert can == can1
487
+
488
+ # A^n_{i, j} antisymmetric in i,j
489
+ # A_m0^d0_a1 * A_m1^a0_d0; ord = [m0,m1,a0,a1,d0,-d0]
490
+ # g = [0,4,3,1,2,5,6,7]
491
+ # T_c = -A_{m a1}^d0 * A_m1^a0_d0
492
+ # can = [0,3,4,1,2,5,7,6]
493
+ base, gens = bsgs_direct_product(base1, gens1, base2a, gens2a)
494
+ dummies = list(range(4, 6))
495
+ g = Permutation([0,4,3,1,2,5,6,7])
496
+ can = canonicalize(g, dummies, 0, (base, gens, 2, 0))
497
+ assert can == [0, 3, 4, 1, 2, 5, 7, 6]
498
+
499
+
500
+ # A^n_{i, j} symmetric in i,j
501
+ # A^m0_a0^d2 * A^n0_d2^d1 * A^n1_d1^d0 * A_{m0 d0}^a1
502
+ # ordering: first the free indices; then first n, then d
503
+ # ord=[n0,n1,a0,a1, m0,-m0,d0,-d0,d1,-d1,d2,-d2]
504
+ # 0 1 2 3 4 5 6 7 8 9 10 11]
505
+ # g = [4,2,10, 0,11,8, 1,9,6, 5,7,3, 12,13]
506
+ # if the dummy indices m_i and d_i were separated,
507
+ # one gets
508
+ # T_c = A^{n0 d0 d1} * A^n1_d0^d2 * A^m0^a0_d1 * A_m0^a1_d2
509
+ # can = [0, 6, 8, 1, 7, 10, 4, 2, 9, 5, 3, 11, 12, 13]
510
+ # If they are not, so can is
511
+ # T_c = A^{n0 m0 d0} A^n1_m0^d1 A^{d2 a0}_d0 A_d2^a1_d1
512
+ # can = [0, 4, 6, 1, 5, 8, 10, 2, 7, 11, 3, 9, 12, 13]
513
+ # case with single type of indices
514
+
515
+ base, gens = bsgs_direct_product(base1, gens1, base2, gens2)
516
+ dummies = list(range(4, 12))
517
+ g = Permutation([4,2,10, 0,11,8, 1,9,6, 5,7,3, 12,13])
518
+ can = canonicalize(g, dummies, 0, (base, gens, 4, 0))
519
+ assert can == [0, 4, 6, 1, 5, 8, 10, 2, 7, 11, 3, 9, 12, 13]
520
+ # case with separated indices
521
+ dummies = [list(range(4, 6)), list(range(6,12))]
522
+ sym = [0, 0]
523
+ can = canonicalize(g, dummies, sym, (base, gens, 4, 0))
524
+ assert can == [0, 6, 8, 1, 7, 10, 4, 2, 9, 5, 3, 11, 12, 13]
525
+ # case with separated indices with the second type of index
526
+ # with antisymmetric metric: there is a sign change
527
+ sym = [0, 1]
528
+ can = canonicalize(g, dummies, sym, (base, gens, 4, 0))
529
+ assert can == [0, 6, 8, 1, 7, 10, 4, 2, 9, 5, 3, 11, 13, 12]
530
+
531
+ def test_graph_certificate():
532
+ # test tensor invariants constructed from random regular graphs;
533
+ # checked graph isomorphism with networkx
534
+ import random
535
+ def randomize_graph(size, g):
536
+ p = list(range(size))
537
+ random.shuffle(p)
538
+ g1a = {}
539
+ for k, v in g1.items():
540
+ g1a[p[k]] = [p[i] for i in v]
541
+ return g1a
542
+
543
+ g1 = {0: [2, 3, 7], 1: [4, 5, 7], 2: [0, 4, 6], 3: [0, 6, 7], 4: [1, 2, 5], 5: [1, 4, 6], 6: [2, 3, 5], 7: [0, 1, 3]}
544
+ g2 = {0: [2, 3, 7], 1: [2, 4, 5], 2: [0, 1, 5], 3: [0, 6, 7], 4: [1, 5, 6], 5: [1, 2, 4], 6: [3, 4, 7], 7: [0, 3, 6]}
545
+
546
+ c1 = graph_certificate(g1)
547
+ c2 = graph_certificate(g2)
548
+ assert c1 != c2
549
+ g1a = randomize_graph(8, g1)
550
+ c1a = graph_certificate(g1a)
551
+ assert c1 == c1a
552
+
553
+ g1 = {0: [8, 1, 9, 7], 1: [0, 9, 3, 4], 2: [3, 4, 6, 7], 3: [1, 2, 5, 6], 4: [8, 1, 2, 5], 5: [9, 3, 4, 7], 6: [8, 2, 3, 7], 7: [0, 2, 5, 6], 8: [0, 9, 4, 6], 9: [8, 0, 5, 1]}
554
+ g2 = {0: [1, 2, 5, 6], 1: [0, 9, 5, 7], 2: [0, 4, 6, 7], 3: [8, 9, 6, 7], 4: [8, 2, 6, 7], 5: [0, 9, 8, 1], 6: [0, 2, 3, 4], 7: [1, 2, 3, 4], 8: [9, 3, 4, 5], 9: [8, 1, 3, 5]}
555
+ c1 = graph_certificate(g1)
556
+ c2 = graph_certificate(g2)
557
+ assert c1 != c2
558
+ g1a = randomize_graph(10, g1)
559
+ c1a = graph_certificate(g1a)
560
+ assert c1 == c1a
.venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_testutil.py ADDED
@@ -0,0 +1,55 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.combinatorics.named_groups import SymmetricGroup, AlternatingGroup,\
2
+ CyclicGroup
3
+ from sympy.combinatorics.testutil import _verify_bsgs, _cmp_perm_lists,\
4
+ _naive_list_centralizer, _verify_centralizer,\
5
+ _verify_normal_closure
6
+ from sympy.combinatorics.permutations import Permutation
7
+ from sympy.combinatorics.perm_groups import PermutationGroup
8
+ from sympy.core.random import shuffle
9
+
10
+
11
+ def test_cmp_perm_lists():
12
+ S = SymmetricGroup(4)
13
+ els = list(S.generate_dimino())
14
+ other = els.copy()
15
+ shuffle(other)
16
+ assert _cmp_perm_lists(els, other) is True
17
+
18
+
19
+ def test_naive_list_centralizer():
20
+ # verified by GAP
21
+ S = SymmetricGroup(3)
22
+ A = AlternatingGroup(3)
23
+ assert _naive_list_centralizer(S, S) == [Permutation([0, 1, 2])]
24
+ assert PermutationGroup(_naive_list_centralizer(S, A)).is_subgroup(A)
25
+
26
+
27
+ def test_verify_bsgs():
28
+ S = SymmetricGroup(5)
29
+ S.schreier_sims()
30
+ base = S.base
31
+ strong_gens = S.strong_gens
32
+ assert _verify_bsgs(S, base, strong_gens) is True
33
+ assert _verify_bsgs(S, base[:-1], strong_gens) is False
34
+ assert _verify_bsgs(S, base, S.generators) is False
35
+
36
+
37
+ def test_verify_centralizer():
38
+ # verified by GAP
39
+ S = SymmetricGroup(3)
40
+ A = AlternatingGroup(3)
41
+ triv = PermutationGroup([Permutation([0, 1, 2])])
42
+ assert _verify_centralizer(S, S, centr=triv)
43
+ assert _verify_centralizer(S, A, centr=A)
44
+
45
+
46
+ def test_verify_normal_closure():
47
+ # verified by GAP
48
+ S = SymmetricGroup(3)
49
+ A = AlternatingGroup(3)
50
+ assert _verify_normal_closure(S, A, closure=A)
51
+ S = SymmetricGroup(5)
52
+ A = AlternatingGroup(5)
53
+ C = CyclicGroup(5)
54
+ assert _verify_normal_closure(S, A, closure=A)
55
+ assert _verify_normal_closure(S, C, closure=A)
.venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_util.py ADDED
@@ -0,0 +1,120 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.combinatorics.named_groups import SymmetricGroup, DihedralGroup,\
2
+ AlternatingGroup
3
+ from sympy.combinatorics.permutations import Permutation
4
+ from sympy.combinatorics.util import _check_cycles_alt_sym, _strip,\
5
+ _distribute_gens_by_base, _strong_gens_from_distr,\
6
+ _orbits_transversals_from_bsgs, _handle_precomputed_bsgs, _base_ordering,\
7
+ _remove_gens
8
+ from sympy.combinatorics.testutil import _verify_bsgs
9
+
10
+
11
+ def test_check_cycles_alt_sym():
12
+ perm1 = Permutation([[0, 1, 2, 3, 4, 5, 6], [7], [8], [9]])
13
+ perm2 = Permutation([[0, 1, 2, 3, 4, 5], [6, 7, 8, 9]])
14
+ perm3 = Permutation([[0, 1, 2, 3, 4], [5, 6, 7, 8, 9]])
15
+ assert _check_cycles_alt_sym(perm1) is True
16
+ assert _check_cycles_alt_sym(perm2) is False
17
+ assert _check_cycles_alt_sym(perm3) is False
18
+
19
+
20
+ def test_strip():
21
+ D = DihedralGroup(5)
22
+ D.schreier_sims()
23
+ member = Permutation([4, 0, 1, 2, 3])
24
+ not_member1 = Permutation([0, 1, 4, 3, 2])
25
+ not_member2 = Permutation([3, 1, 4, 2, 0])
26
+ identity = Permutation([0, 1, 2, 3, 4])
27
+ res1 = _strip(member, D.base, D.basic_orbits, D.basic_transversals)
28
+ res2 = _strip(not_member1, D.base, D.basic_orbits, D.basic_transversals)
29
+ res3 = _strip(not_member2, D.base, D.basic_orbits, D.basic_transversals)
30
+ assert res1[0] == identity
31
+ assert res1[1] == len(D.base) + 1
32
+ assert res2[0] == not_member1
33
+ assert res2[1] == len(D.base) + 1
34
+ assert res3[0] != identity
35
+ assert res3[1] == 2
36
+
37
+
38
+ def test_distribute_gens_by_base():
39
+ base = [0, 1, 2]
40
+ gens = [Permutation([0, 1, 2, 3]), Permutation([0, 1, 3, 2]),
41
+ Permutation([0, 2, 3, 1]), Permutation([3, 2, 1, 0])]
42
+ assert _distribute_gens_by_base(base, gens) == [gens,
43
+ [Permutation([0, 1, 2, 3]),
44
+ Permutation([0, 1, 3, 2]),
45
+ Permutation([0, 2, 3, 1])],
46
+ [Permutation([0, 1, 2, 3]),
47
+ Permutation([0, 1, 3, 2])]]
48
+
49
+
50
+ def test_strong_gens_from_distr():
51
+ strong_gens_distr = [[Permutation([0, 2, 1]), Permutation([1, 2, 0]),
52
+ Permutation([1, 0, 2])], [Permutation([0, 2, 1])]]
53
+ assert _strong_gens_from_distr(strong_gens_distr) == \
54
+ [Permutation([0, 2, 1]),
55
+ Permutation([1, 2, 0]),
56
+ Permutation([1, 0, 2])]
57
+
58
+
59
+ def test_orbits_transversals_from_bsgs():
60
+ S = SymmetricGroup(4)
61
+ S.schreier_sims()
62
+ base = S.base
63
+ strong_gens = S.strong_gens
64
+ strong_gens_distr = _distribute_gens_by_base(base, strong_gens)
65
+ result = _orbits_transversals_from_bsgs(base, strong_gens_distr)
66
+ orbits = result[0]
67
+ transversals = result[1]
68
+ base_len = len(base)
69
+ for i in range(base_len):
70
+ for el in orbits[i]:
71
+ assert transversals[i][el](base[i]) == el
72
+ for j in range(i):
73
+ assert transversals[i][el](base[j]) == base[j]
74
+ order = 1
75
+ for i in range(base_len):
76
+ order *= len(orbits[i])
77
+ assert S.order() == order
78
+
79
+
80
+ def test_handle_precomputed_bsgs():
81
+ A = AlternatingGroup(5)
82
+ A.schreier_sims()
83
+ base = A.base
84
+ strong_gens = A.strong_gens
85
+ result = _handle_precomputed_bsgs(base, strong_gens)
86
+ strong_gens_distr = _distribute_gens_by_base(base, strong_gens)
87
+ assert strong_gens_distr == result[2]
88
+ transversals = result[0]
89
+ orbits = result[1]
90
+ base_len = len(base)
91
+ for i in range(base_len):
92
+ for el in orbits[i]:
93
+ assert transversals[i][el](base[i]) == el
94
+ for j in range(i):
95
+ assert transversals[i][el](base[j]) == base[j]
96
+ order = 1
97
+ for i in range(base_len):
98
+ order *= len(orbits[i])
99
+ assert A.order() == order
100
+
101
+
102
+ def test_base_ordering():
103
+ base = [2, 4, 5]
104
+ degree = 7
105
+ assert _base_ordering(base, degree) == [3, 4, 0, 5, 1, 2, 6]
106
+
107
+
108
+ def test_remove_gens():
109
+ S = SymmetricGroup(10)
110
+ base, strong_gens = S.schreier_sims_incremental()
111
+ new_gens = _remove_gens(base, strong_gens)
112
+ assert _verify_bsgs(S, base, new_gens) is True
113
+ A = AlternatingGroup(7)
114
+ base, strong_gens = A.schreier_sims_incremental()
115
+ new_gens = _remove_gens(base, strong_gens)
116
+ assert _verify_bsgs(A, base, new_gens) is True
117
+ D = DihedralGroup(2)
118
+ base, strong_gens = D.schreier_sims_incremental()
119
+ new_gens = _remove_gens(base, strong_gens)
120
+ assert _verify_bsgs(D, base, new_gens) is True
.venv/lib/python3.13/site-packages/sympy/functions/combinatorial/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ # Stub __init__.py for sympy.functions.combinatorial
.venv/lib/python3.13/site-packages/sympy/functions/combinatorial/factorials.py ADDED
@@ -0,0 +1,1133 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from __future__ import annotations
2
+ from functools import reduce
3
+
4
+ from sympy.core import S, sympify, Dummy, Mod
5
+ from sympy.core.cache import cacheit
6
+ from sympy.core.function import DefinedFunction, ArgumentIndexError, PoleError
7
+ from sympy.core.logic import fuzzy_and
8
+ from sympy.core.numbers import Integer, pi, I
9
+ from sympy.core.relational import Eq
10
+ from sympy.external.gmpy import gmpy as _gmpy
11
+ from sympy.ntheory import sieve
12
+ from sympy.ntheory.residue_ntheory import binomial_mod
13
+ from sympy.polys.polytools import Poly
14
+
15
+ from math import factorial as _factorial, prod, sqrt as _sqrt
16
+
17
+ class CombinatorialFunction(DefinedFunction):
18
+ """Base class for combinatorial functions. """
19
+
20
+ def _eval_simplify(self, **kwargs):
21
+ from sympy.simplify.combsimp import combsimp
22
+ # combinatorial function with non-integer arguments is
23
+ # automatically passed to gammasimp
24
+ expr = combsimp(self)
25
+ measure = kwargs['measure']
26
+ if measure(expr) <= kwargs['ratio']*measure(self):
27
+ return expr
28
+ return self
29
+
30
+
31
+ ###############################################################################
32
+ ######################## FACTORIAL and MULTI-FACTORIAL ########################
33
+ ###############################################################################
34
+
35
+
36
+ class factorial(CombinatorialFunction):
37
+ r"""Implementation of factorial function over nonnegative integers.
38
+ By convention (consistent with the gamma function and the binomial
39
+ coefficients), factorial of a negative integer is complex infinity.
40
+
41
+ The factorial is very important in combinatorics where it gives
42
+ the number of ways in which `n` objects can be permuted. It also
43
+ arises in calculus, probability, number theory, etc.
44
+
45
+ There is strict relation of factorial with gamma function. In
46
+ fact `n! = gamma(n+1)` for nonnegative integers. Rewrite of this
47
+ kind is very useful in case of combinatorial simplification.
48
+
49
+ Computation of the factorial is done using two algorithms. For
50
+ small arguments a precomputed look up table is used. However for bigger
51
+ input algorithm Prime-Swing is used. It is the fastest algorithm
52
+ known and computes `n!` via prime factorization of special class
53
+ of numbers, called here the 'Swing Numbers'.
54
+
55
+ Examples
56
+ ========
57
+
58
+ >>> from sympy import Symbol, factorial, S
59
+ >>> n = Symbol('n', integer=True)
60
+
61
+ >>> factorial(0)
62
+ 1
63
+
64
+ >>> factorial(7)
65
+ 5040
66
+
67
+ >>> factorial(-2)
68
+ zoo
69
+
70
+ >>> factorial(n)
71
+ factorial(n)
72
+
73
+ >>> factorial(2*n)
74
+ factorial(2*n)
75
+
76
+ >>> factorial(S(1)/2)
77
+ factorial(1/2)
78
+
79
+ See Also
80
+ ========
81
+
82
+ factorial2, RisingFactorial, FallingFactorial
83
+ """
84
+
85
+ def fdiff(self, argindex=1):
86
+ from sympy.functions.special.gamma_functions import (gamma, polygamma)
87
+ if argindex == 1:
88
+ return gamma(self.args[0] + 1)*polygamma(0, self.args[0] + 1)
89
+ else:
90
+ raise ArgumentIndexError(self, argindex)
91
+
92
+ _small_swing = [
93
+ 1, 1, 1, 3, 3, 15, 5, 35, 35, 315, 63, 693, 231, 3003, 429, 6435, 6435, 109395,
94
+ 12155, 230945, 46189, 969969, 88179, 2028117, 676039, 16900975, 1300075,
95
+ 35102025, 5014575, 145422675, 9694845, 300540195, 300540195
96
+ ]
97
+
98
+ _small_factorials: list[int] = []
99
+
100
+ @classmethod
101
+ def _swing(cls, n):
102
+ if n < 33:
103
+ return cls._small_swing[n]
104
+ else:
105
+ N, primes = int(_sqrt(n)), []
106
+
107
+ for prime in sieve.primerange(3, N + 1):
108
+ p, q = 1, n
109
+
110
+ while True:
111
+ q //= prime
112
+
113
+ if q > 0:
114
+ if q & 1 == 1:
115
+ p *= prime
116
+ else:
117
+ break
118
+
119
+ if p > 1:
120
+ primes.append(p)
121
+
122
+ for prime in sieve.primerange(N + 1, n//3 + 1):
123
+ if (n // prime) & 1 == 1:
124
+ primes.append(prime)
125
+
126
+ L_product = prod(sieve.primerange(n//2 + 1, n + 1))
127
+ R_product = prod(primes)
128
+
129
+ return L_product*R_product
130
+
131
+ @classmethod
132
+ def _recursive(cls, n):
133
+ if n < 2:
134
+ return 1
135
+ else:
136
+ return (cls._recursive(n//2)**2)*cls._swing(n)
137
+
138
+ @classmethod
139
+ def eval(cls, n):
140
+ n = sympify(n)
141
+
142
+ if n.is_Number:
143
+ if n.is_zero:
144
+ return S.One
145
+ elif n is S.Infinity:
146
+ return S.Infinity
147
+ elif n.is_Integer:
148
+ if n.is_negative:
149
+ return S.ComplexInfinity
150
+ else:
151
+ n = n.p
152
+
153
+ if n < 20:
154
+ if not cls._small_factorials:
155
+ result = 1
156
+ for i in range(1, 20):
157
+ result *= i
158
+ cls._small_factorials.append(result)
159
+ result = cls._small_factorials[n-1]
160
+
161
+ # GMPY factorial is faster, use it when available
162
+ #
163
+ # XXX: There is a sympy.external.gmpy.factorial function
164
+ # which provides gmpy.fac if available or the flint version
165
+ # if flint is used. It could be used here to avoid the
166
+ # conditional logic but it needs to be checked whether the
167
+ # pure Python fallback used there is as fast as the
168
+ # fallback used here (perhaps the fallback here should be
169
+ # moved to sympy.external.ntheory).
170
+ elif _gmpy is not None:
171
+ result = _gmpy.fac(n)
172
+
173
+ else:
174
+ bits = bin(n).count('1')
175
+ result = cls._recursive(n)*2**(n - bits)
176
+
177
+ return Integer(result)
178
+
179
+ def _facmod(self, n, q):
180
+ res, N = 1, int(_sqrt(n))
181
+
182
+ # Exponent of prime p in n! is e_p(n) = [n/p] + [n/p**2] + ...
183
+ # for p > sqrt(n), e_p(n) < sqrt(n), the primes with [n/p] = m,
184
+ # occur consecutively and are grouped together in pw[m] for
185
+ # simultaneous exponentiation at a later stage
186
+ pw = [1]*N
187
+
188
+ m = 2 # to initialize the if condition below
189
+ for prime in sieve.primerange(2, n + 1):
190
+ if m > 1:
191
+ m, y = 0, n // prime
192
+ while y:
193
+ m += y
194
+ y //= prime
195
+ if m < N:
196
+ pw[m] = pw[m]*prime % q
197
+ else:
198
+ res = res*pow(prime, m, q) % q
199
+
200
+ for ex, bs in enumerate(pw):
201
+ if ex == 0 or bs == 1:
202
+ continue
203
+ if bs == 0:
204
+ return 0
205
+ res = res*pow(bs, ex, q) % q
206
+
207
+ return res
208
+
209
+ def _eval_Mod(self, q):
210
+ n = self.args[0]
211
+ if n.is_integer and n.is_nonnegative and q.is_integer:
212
+ aq = abs(q)
213
+ d = aq - n
214
+ if d.is_nonpositive:
215
+ return S.Zero
216
+ else:
217
+ isprime = aq.is_prime
218
+ if d == 1:
219
+ # Apply Wilson's theorem (if a natural number n > 1
220
+ # is a prime number, then (n-1)! = -1 mod n) and
221
+ # its inverse (if n > 4 is a composite number, then
222
+ # (n-1)! = 0 mod n)
223
+ if isprime:
224
+ return -1 % q
225
+ elif isprime is False and (aq - 6).is_nonnegative:
226
+ return S.Zero
227
+ elif n.is_Integer and q.is_Integer:
228
+ n, d, aq = map(int, (n, d, aq))
229
+ if isprime and (d - 1 < n):
230
+ fc = self._facmod(d - 1, aq)
231
+ fc = pow(fc, aq - 2, aq)
232
+ if d%2:
233
+ fc = -fc
234
+ else:
235
+ fc = self._facmod(n, aq)
236
+
237
+ return fc % q
238
+
239
+ def _eval_rewrite_as_gamma(self, n, piecewise=True, **kwargs):
240
+ from sympy.functions.special.gamma_functions import gamma
241
+ return gamma(n + 1)
242
+
243
+ def _eval_rewrite_as_Product(self, n, **kwargs):
244
+ from sympy.concrete.products import Product
245
+ if n.is_nonnegative and n.is_integer:
246
+ i = Dummy('i', integer=True)
247
+ return Product(i, (i, 1, n))
248
+
249
+ def _eval_is_integer(self):
250
+ if self.args[0].is_integer and self.args[0].is_nonnegative:
251
+ return True
252
+
253
+ def _eval_is_positive(self):
254
+ if self.args[0].is_integer and self.args[0].is_nonnegative:
255
+ return True
256
+
257
+ def _eval_is_even(self):
258
+ x = self.args[0]
259
+ if x.is_integer and x.is_nonnegative:
260
+ return (x - 2).is_nonnegative
261
+
262
+ def _eval_is_composite(self):
263
+ x = self.args[0]
264
+ if x.is_integer and x.is_nonnegative:
265
+ return (x - 3).is_nonnegative
266
+
267
+ def _eval_is_real(self):
268
+ x = self.args[0]
269
+ if x.is_nonnegative or x.is_noninteger:
270
+ return True
271
+
272
+ def _eval_as_leading_term(self, x, logx, cdir):
273
+ arg = self.args[0].as_leading_term(x)
274
+ arg0 = arg.subs(x, 0)
275
+ if arg0.is_zero:
276
+ return S.One
277
+ elif not arg0.is_infinite:
278
+ return self.func(arg)
279
+ raise PoleError("Cannot expand %s around 0" % (self))
280
+
281
+ class MultiFactorial(CombinatorialFunction):
282
+ pass
283
+
284
+
285
+ class subfactorial(CombinatorialFunction):
286
+ r"""The subfactorial counts the derangements of $n$ items and is
287
+ defined for non-negative integers as:
288
+
289
+ .. math:: !n = \begin{cases} 1 & n = 0 \\ 0 & n = 1 \\
290
+ (n-1)(!(n-1) + !(n-2)) & n > 1 \end{cases}
291
+
292
+ It can also be written as ``int(round(n!/exp(1)))`` but the
293
+ recursive definition with caching is implemented for this function.
294
+
295
+ An interesting analytic expression is the following [2]_
296
+
297
+ .. math:: !x = \Gamma(x + 1, -1)/e
298
+
299
+ which is valid for non-negative integers `x`. The above formula
300
+ is not very useful in case of non-integers. `\Gamma(x + 1, -1)` is
301
+ single-valued only for integral arguments `x`, elsewhere on the positive
302
+ real axis it has an infinite number of branches none of which are real.
303
+
304
+ References
305
+ ==========
306
+
307
+ .. [1] https://en.wikipedia.org/wiki/Subfactorial
308
+ .. [2] https://mathworld.wolfram.com/Subfactorial.html
309
+
310
+ Examples
311
+ ========
312
+
313
+ >>> from sympy import subfactorial
314
+ >>> from sympy.abc import n
315
+ >>> subfactorial(n + 1)
316
+ subfactorial(n + 1)
317
+ >>> subfactorial(5)
318
+ 44
319
+
320
+ See Also
321
+ ========
322
+
323
+ factorial, uppergamma,
324
+ sympy.utilities.iterables.generate_derangements
325
+ """
326
+
327
+ @classmethod
328
+ @cacheit
329
+ def _eval(self, n):
330
+ if not n:
331
+ return S.One
332
+ elif n == 1:
333
+ return S.Zero
334
+ else:
335
+ z1, z2 = 1, 0
336
+ for i in range(2, n + 1):
337
+ z1, z2 = z2, (i - 1)*(z2 + z1)
338
+ return z2
339
+
340
+ @classmethod
341
+ def eval(cls, arg):
342
+ if arg.is_Number:
343
+ if arg.is_Integer and arg.is_nonnegative:
344
+ return cls._eval(arg)
345
+ elif arg is S.NaN:
346
+ return S.NaN
347
+ elif arg is S.Infinity:
348
+ return S.Infinity
349
+
350
+ def _eval_is_even(self):
351
+ if self.args[0].is_odd and self.args[0].is_nonnegative:
352
+ return True
353
+
354
+ def _eval_is_integer(self):
355
+ if self.args[0].is_integer and self.args[0].is_nonnegative:
356
+ return True
357
+
358
+ def _eval_rewrite_as_factorial(self, arg, **kwargs):
359
+ from sympy.concrete.summations import summation
360
+ i = Dummy('i')
361
+ f = S.NegativeOne**i / factorial(i)
362
+ return factorial(arg) * summation(f, (i, 0, arg))
363
+
364
+ def _eval_rewrite_as_gamma(self, arg, piecewise=True, **kwargs):
365
+ from sympy.functions.elementary.exponential import exp
366
+ from sympy.functions.special.gamma_functions import (gamma, lowergamma)
367
+ return (S.NegativeOne**(arg + 1)*exp(-I*pi*arg)*lowergamma(arg + 1, -1)
368
+ + gamma(arg + 1))*exp(-1)
369
+
370
+ def _eval_rewrite_as_uppergamma(self, arg, **kwargs):
371
+ from sympy.functions.special.gamma_functions import uppergamma
372
+ return uppergamma(arg + 1, -1)/S.Exp1
373
+
374
+ def _eval_is_nonnegative(self):
375
+ if self.args[0].is_integer and self.args[0].is_nonnegative:
376
+ return True
377
+
378
+ def _eval_is_odd(self):
379
+ if self.args[0].is_even and self.args[0].is_nonnegative:
380
+ return True
381
+
382
+
383
+ class factorial2(CombinatorialFunction):
384
+ r"""The double factorial `n!!`, not to be confused with `(n!)!`
385
+
386
+ The double factorial is defined for nonnegative integers and for odd
387
+ negative integers as:
388
+
389
+ .. math:: n!! = \begin{cases} 1 & n = 0 \\
390
+ n(n-2)(n-4) \cdots 1 & n\ \text{positive odd} \\
391
+ n(n-2)(n-4) \cdots 2 & n\ \text{positive even} \\
392
+ (n+2)!!/(n+2) & n\ \text{negative odd} \end{cases}
393
+
394
+ References
395
+ ==========
396
+
397
+ .. [1] https://en.wikipedia.org/wiki/Double_factorial
398
+
399
+ Examples
400
+ ========
401
+
402
+ >>> from sympy import factorial2, var
403
+ >>> n = var('n')
404
+ >>> n
405
+ n
406
+ >>> factorial2(n + 1)
407
+ factorial2(n + 1)
408
+ >>> factorial2(5)
409
+ 15
410
+ >>> factorial2(-1)
411
+ 1
412
+ >>> factorial2(-5)
413
+ 1/3
414
+
415
+ See Also
416
+ ========
417
+
418
+ factorial, RisingFactorial, FallingFactorial
419
+ """
420
+
421
+ @classmethod
422
+ def eval(cls, arg):
423
+ # TODO: extend this to complex numbers?
424
+
425
+ if arg.is_Number:
426
+ if not arg.is_Integer:
427
+ raise ValueError("argument must be nonnegative integer "
428
+ "or negative odd integer")
429
+
430
+ # This implementation is faster than the recursive one
431
+ # It also avoids "maximum recursion depth exceeded" runtime error
432
+ if arg.is_nonnegative:
433
+ if arg.is_even:
434
+ k = arg / 2
435
+ return 2**k * factorial(k)
436
+ return factorial(arg) / factorial2(arg - 1)
437
+
438
+
439
+ if arg.is_odd:
440
+ return arg*(S.NegativeOne)**((1 - arg)/2) / factorial2(-arg)
441
+ raise ValueError("argument must be nonnegative integer "
442
+ "or negative odd integer")
443
+
444
+
445
+ def _eval_is_even(self):
446
+ # Double factorial is even for every positive even input
447
+ n = self.args[0]
448
+ if n.is_integer:
449
+ if n.is_odd:
450
+ return False
451
+ if n.is_even:
452
+ if n.is_positive:
453
+ return True
454
+ if n.is_zero:
455
+ return False
456
+
457
+ def _eval_is_integer(self):
458
+ # Double factorial is an integer for every nonnegative input, and for
459
+ # -1 and -3
460
+ n = self.args[0]
461
+ if n.is_integer:
462
+ if (n + 1).is_nonnegative:
463
+ return True
464
+ if n.is_odd:
465
+ return (n + 3).is_nonnegative
466
+
467
+ def _eval_is_odd(self):
468
+ # Double factorial is odd for every odd input not smaller than -3, and
469
+ # for 0
470
+ n = self.args[0]
471
+ if n.is_odd:
472
+ return (n + 3).is_nonnegative
473
+ if n.is_even:
474
+ if n.is_positive:
475
+ return False
476
+ if n.is_zero:
477
+ return True
478
+
479
+ def _eval_is_positive(self):
480
+ # Double factorial is positive for every nonnegative input, and for
481
+ # every odd negative input which is of the form -1-4k for an
482
+ # nonnegative integer k
483
+ n = self.args[0]
484
+ if n.is_integer:
485
+ if (n + 1).is_nonnegative:
486
+ return True
487
+ if n.is_odd:
488
+ return ((n + 1) / 2).is_even
489
+
490
+ def _eval_rewrite_as_gamma(self, n, piecewise=True, **kwargs):
491
+ from sympy.functions.elementary.miscellaneous import sqrt
492
+ from sympy.functions.elementary.piecewise import Piecewise
493
+ from sympy.functions.special.gamma_functions import gamma
494
+ return 2**(n/2)*gamma(n/2 + 1) * Piecewise((1, Eq(Mod(n, 2), 0)),
495
+ (sqrt(2/pi), Eq(Mod(n, 2), 1)))
496
+
497
+
498
+ ###############################################################################
499
+ ######################## RISING and FALLING FACTORIALS ########################
500
+ ###############################################################################
501
+
502
+
503
+ class RisingFactorial(CombinatorialFunction):
504
+ r"""
505
+ Rising factorial (also called Pochhammer symbol [1]_) is a double valued
506
+ function arising in concrete mathematics, hypergeometric functions
507
+ and series expansions. It is defined by:
508
+
509
+ .. math:: \texttt{rf(y, k)} = (x)^k = x \cdot (x+1) \cdots (x+k-1)
510
+
511
+ where `x` can be arbitrary expression and `k` is an integer. For
512
+ more information check "Concrete mathematics" by Graham, pp. 66
513
+ or visit https://mathworld.wolfram.com/RisingFactorial.html page.
514
+
515
+ When `x` is a `~.Poly` instance of degree $\ge 1$ with a single variable,
516
+ `(x)^k = x(y) \cdot x(y+1) \cdots x(y+k-1)`, where `y` is the
517
+ variable of `x`. This is as described in [2]_.
518
+
519
+ Examples
520
+ ========
521
+
522
+ >>> from sympy import rf, Poly
523
+ >>> from sympy.abc import x
524
+ >>> rf(x, 0)
525
+ 1
526
+ >>> rf(1, 5)
527
+ 120
528
+ >>> rf(x, 5) == x*(1 + x)*(2 + x)*(3 + x)*(4 + x)
529
+ True
530
+ >>> rf(Poly(x**3, x), 2)
531
+ Poly(x**6 + 3*x**5 + 3*x**4 + x**3, x, domain='ZZ')
532
+
533
+ Rewriting is complicated unless the relationship between
534
+ the arguments is known, but rising factorial can
535
+ be rewritten in terms of gamma, factorial, binomial,
536
+ and falling factorial.
537
+
538
+ >>> from sympy import Symbol, factorial, ff, binomial, gamma
539
+ >>> n = Symbol('n', integer=True, positive=True)
540
+ >>> R = rf(n, n + 2)
541
+ >>> for i in (rf, ff, factorial, binomial, gamma):
542
+ ... R.rewrite(i)
543
+ ...
544
+ RisingFactorial(n, n + 2)
545
+ FallingFactorial(2*n + 1, n + 2)
546
+ factorial(2*n + 1)/factorial(n - 1)
547
+ binomial(2*n + 1, n + 2)*factorial(n + 2)
548
+ gamma(2*n + 2)/gamma(n)
549
+
550
+ See Also
551
+ ========
552
+
553
+ factorial, factorial2, FallingFactorial
554
+
555
+ References
556
+ ==========
557
+
558
+ .. [1] https://en.wikipedia.org/wiki/Pochhammer_symbol
559
+ .. [2] Peter Paule, "Greatest Factorial Factorization and Symbolic
560
+ Summation", Journal of Symbolic Computation, vol. 20, pp. 235-268,
561
+ 1995.
562
+
563
+ """
564
+
565
+ @classmethod
566
+ def eval(cls, x, k):
567
+ x = sympify(x)
568
+ k = sympify(k)
569
+
570
+ if x is S.NaN or k is S.NaN:
571
+ return S.NaN
572
+ elif x is S.One:
573
+ return factorial(k)
574
+ elif k.is_Integer:
575
+ if k.is_zero:
576
+ return S.One
577
+ else:
578
+ if k.is_positive:
579
+ if x is S.Infinity:
580
+ return S.Infinity
581
+ elif x is S.NegativeInfinity:
582
+ if k.is_odd:
583
+ return S.NegativeInfinity
584
+ else:
585
+ return S.Infinity
586
+ else:
587
+ if isinstance(x, Poly):
588
+ gens = x.gens
589
+ if len(gens)!= 1:
590
+ raise ValueError("rf only defined for "
591
+ "polynomials on one generator")
592
+ else:
593
+ return reduce(lambda r, i:
594
+ r*(x.shift(i)),
595
+ range(int(k)), 1)
596
+ else:
597
+ return reduce(lambda r, i: r*(x + i),
598
+ range(int(k)), 1)
599
+
600
+ else:
601
+ if x is S.Infinity:
602
+ return S.Infinity
603
+ elif x is S.NegativeInfinity:
604
+ return S.Infinity
605
+ else:
606
+ if isinstance(x, Poly):
607
+ gens = x.gens
608
+ if len(gens)!= 1:
609
+ raise ValueError("rf only defined for "
610
+ "polynomials on one generator")
611
+ else:
612
+ return 1/reduce(lambda r, i:
613
+ r*(x.shift(-i)),
614
+ range(1, abs(int(k)) + 1), 1)
615
+ else:
616
+ return 1/reduce(lambda r, i:
617
+ r*(x - i),
618
+ range(1, abs(int(k)) + 1), 1)
619
+
620
+ if k.is_integer == False:
621
+ if x.is_integer and x.is_negative:
622
+ return S.Zero
623
+
624
+ def _eval_rewrite_as_gamma(self, x, k, piecewise=True, **kwargs):
625
+ from sympy.functions.elementary.piecewise import Piecewise
626
+ from sympy.functions.special.gamma_functions import gamma
627
+ if not piecewise:
628
+ if (x <= 0) == True:
629
+ return S.NegativeOne**k*gamma(1 - x) / gamma(-k - x + 1)
630
+ return gamma(x + k) / gamma(x)
631
+ return Piecewise(
632
+ (gamma(x + k) / gamma(x), x > 0),
633
+ (S.NegativeOne**k*gamma(1 - x) / gamma(-k - x + 1), True))
634
+
635
+ def _eval_rewrite_as_FallingFactorial(self, x, k, **kwargs):
636
+ return FallingFactorial(x + k - 1, k)
637
+
638
+ def _eval_rewrite_as_factorial(self, x, k, **kwargs):
639
+ from sympy.functions.elementary.piecewise import Piecewise
640
+ if x.is_integer and k.is_integer:
641
+ return Piecewise(
642
+ (factorial(k + x - 1)/factorial(x - 1), x > 0),
643
+ (S.NegativeOne**k*factorial(-x)/factorial(-k - x), True))
644
+
645
+ def _eval_rewrite_as_binomial(self, x, k, **kwargs):
646
+ if k.is_integer:
647
+ return factorial(k) * binomial(x + k - 1, k)
648
+
649
+ def _eval_rewrite_as_tractable(self, x, k, limitvar=None, **kwargs):
650
+ from sympy.functions.special.gamma_functions import gamma
651
+ if limitvar:
652
+ k_lim = k.subs(limitvar, S.Infinity)
653
+ if k_lim is S.Infinity:
654
+ return (gamma(x + k).rewrite('tractable', deep=True) / gamma(x))
655
+ elif k_lim is S.NegativeInfinity:
656
+ return (S.NegativeOne**k*gamma(1 - x) / gamma(-k - x + 1).rewrite('tractable', deep=True))
657
+ return self.rewrite(gamma).rewrite('tractable', deep=True)
658
+
659
+ def _eval_is_integer(self):
660
+ return fuzzy_and((self.args[0].is_integer, self.args[1].is_integer,
661
+ self.args[1].is_nonnegative))
662
+
663
+
664
+ class FallingFactorial(CombinatorialFunction):
665
+ r"""
666
+ Falling factorial (related to rising factorial) is a double valued
667
+ function arising in concrete mathematics, hypergeometric functions
668
+ and series expansions. It is defined by
669
+
670
+ .. math:: \texttt{ff(x, k)} = (x)_k = x \cdot (x-1) \cdots (x-k+1)
671
+
672
+ where `x` can be arbitrary expression and `k` is an integer. For
673
+ more information check "Concrete mathematics" by Graham, pp. 66
674
+ or [1]_.
675
+
676
+ When `x` is a `~.Poly` instance of degree $\ge 1$ with single variable,
677
+ `(x)_k = x(y) \cdot x(y-1) \cdots x(y-k+1)`, where `y` is the
678
+ variable of `x`. This is as described in
679
+
680
+ >>> from sympy import ff, Poly, Symbol
681
+ >>> from sympy.abc import x
682
+ >>> n = Symbol('n', integer=True)
683
+
684
+ >>> ff(x, 0)
685
+ 1
686
+ >>> ff(5, 5)
687
+ 120
688
+ >>> ff(x, 5) == x*(x - 1)*(x - 2)*(x - 3)*(x - 4)
689
+ True
690
+ >>> ff(Poly(x**2, x), 2)
691
+ Poly(x**4 - 2*x**3 + x**2, x, domain='ZZ')
692
+ >>> ff(n, n)
693
+ factorial(n)
694
+
695
+ Rewriting is complicated unless the relationship between
696
+ the arguments is known, but falling factorial can
697
+ be rewritten in terms of gamma, factorial and binomial
698
+ and rising factorial.
699
+
700
+ >>> from sympy import factorial, rf, gamma, binomial, Symbol
701
+ >>> n = Symbol('n', integer=True, positive=True)
702
+ >>> F = ff(n, n - 2)
703
+ >>> for i in (rf, ff, factorial, binomial, gamma):
704
+ ... F.rewrite(i)
705
+ ...
706
+ RisingFactorial(3, n - 2)
707
+ FallingFactorial(n, n - 2)
708
+ factorial(n)/2
709
+ binomial(n, n - 2)*factorial(n - 2)
710
+ gamma(n + 1)/2
711
+
712
+ See Also
713
+ ========
714
+
715
+ factorial, factorial2, RisingFactorial
716
+
717
+ References
718
+ ==========
719
+
720
+ .. [1] https://mathworld.wolfram.com/FallingFactorial.html
721
+ .. [2] Peter Paule, "Greatest Factorial Factorization and Symbolic
722
+ Summation", Journal of Symbolic Computation, vol. 20, pp. 235-268,
723
+ 1995.
724
+
725
+ """
726
+
727
+ @classmethod
728
+ def eval(cls, x, k):
729
+ x = sympify(x)
730
+ k = sympify(k)
731
+
732
+ if x is S.NaN or k is S.NaN:
733
+ return S.NaN
734
+ elif k.is_integer and x == k:
735
+ return factorial(x)
736
+ elif k.is_Integer:
737
+ if k.is_zero:
738
+ return S.One
739
+ else:
740
+ if k.is_positive:
741
+ if x is S.Infinity:
742
+ return S.Infinity
743
+ elif x is S.NegativeInfinity:
744
+ if k.is_odd:
745
+ return S.NegativeInfinity
746
+ else:
747
+ return S.Infinity
748
+ else:
749
+ if isinstance(x, Poly):
750
+ gens = x.gens
751
+ if len(gens)!= 1:
752
+ raise ValueError("ff only defined for "
753
+ "polynomials on one generator")
754
+ else:
755
+ return reduce(lambda r, i:
756
+ r*(x.shift(-i)),
757
+ range(int(k)), 1)
758
+ else:
759
+ return reduce(lambda r, i: r*(x - i),
760
+ range(int(k)), 1)
761
+ else:
762
+ if x is S.Infinity:
763
+ return S.Infinity
764
+ elif x is S.NegativeInfinity:
765
+ return S.Infinity
766
+ else:
767
+ if isinstance(x, Poly):
768
+ gens = x.gens
769
+ if len(gens)!= 1:
770
+ raise ValueError("rf only defined for "
771
+ "polynomials on one generator")
772
+ else:
773
+ return 1/reduce(lambda r, i:
774
+ r*(x.shift(i)),
775
+ range(1, abs(int(k)) + 1), 1)
776
+ else:
777
+ return 1/reduce(lambda r, i: r*(x + i),
778
+ range(1, abs(int(k)) + 1), 1)
779
+
780
+ def _eval_rewrite_as_gamma(self, x, k, piecewise=True, **kwargs):
781
+ from sympy.functions.elementary.piecewise import Piecewise
782
+ from sympy.functions.special.gamma_functions import gamma
783
+ if not piecewise:
784
+ if (x < 0) == True:
785
+ return S.NegativeOne**k*gamma(k - x) / gamma(-x)
786
+ return gamma(x + 1) / gamma(x - k + 1)
787
+ return Piecewise(
788
+ (gamma(x + 1) / gamma(x - k + 1), x >= 0),
789
+ (S.NegativeOne**k*gamma(k - x) / gamma(-x), True))
790
+
791
+ def _eval_rewrite_as_RisingFactorial(self, x, k, **kwargs):
792
+ return rf(x - k + 1, k)
793
+
794
+ def _eval_rewrite_as_binomial(self, x, k, **kwargs):
795
+ if k.is_integer:
796
+ return factorial(k) * binomial(x, k)
797
+
798
+ def _eval_rewrite_as_factorial(self, x, k, **kwargs):
799
+ from sympy.functions.elementary.piecewise import Piecewise
800
+ if x.is_integer and k.is_integer:
801
+ return Piecewise(
802
+ (factorial(x)/factorial(-k + x), x >= 0),
803
+ (S.NegativeOne**k*factorial(k - x - 1)/factorial(-x - 1), True))
804
+
805
+ def _eval_rewrite_as_tractable(self, x, k, limitvar=None, **kwargs):
806
+ from sympy.functions.special.gamma_functions import gamma
807
+ if limitvar:
808
+ k_lim = k.subs(limitvar, S.Infinity)
809
+ if k_lim is S.Infinity:
810
+ return (S.NegativeOne**k*gamma(k - x).rewrite('tractable', deep=True) / gamma(-x))
811
+ elif k_lim is S.NegativeInfinity:
812
+ return (gamma(x + 1) / gamma(x - k + 1).rewrite('tractable', deep=True))
813
+ return self.rewrite(gamma).rewrite('tractable', deep=True)
814
+
815
+ def _eval_is_integer(self):
816
+ return fuzzy_and((self.args[0].is_integer, self.args[1].is_integer,
817
+ self.args[1].is_nonnegative))
818
+
819
+
820
+ rf = RisingFactorial
821
+ ff = FallingFactorial
822
+
823
+ ###############################################################################
824
+ ########################### BINOMIAL COEFFICIENTS #############################
825
+ ###############################################################################
826
+
827
+
828
+ class binomial(CombinatorialFunction):
829
+ r"""Implementation of the binomial coefficient. It can be defined
830
+ in two ways depending on its desired interpretation:
831
+
832
+ .. math:: \binom{n}{k} = \frac{n!}{k!(n-k)!}\ \text{or}\
833
+ \binom{n}{k} = \frac{(n)_k}{k!}
834
+
835
+ First, in a strict combinatorial sense it defines the
836
+ number of ways we can choose `k` elements from a set of
837
+ `n` elements. In this case both arguments are nonnegative
838
+ integers and binomial is computed using an efficient
839
+ algorithm based on prime factorization.
840
+
841
+ The other definition is generalization for arbitrary `n`,
842
+ however `k` must also be nonnegative. This case is very
843
+ useful when evaluating summations.
844
+
845
+ For the sake of convenience, for negative integer `k` this function
846
+ will return zero no matter the other argument.
847
+
848
+ To expand the binomial when `n` is a symbol, use either
849
+ ``expand_func()`` or ``expand(func=True)``. The former will keep
850
+ the polynomial in factored form while the latter will expand the
851
+ polynomial itself. See examples for details.
852
+
853
+ Examples
854
+ ========
855
+
856
+ >>> from sympy import Symbol, Rational, binomial, expand_func
857
+ >>> n = Symbol('n', integer=True, positive=True)
858
+
859
+ >>> binomial(15, 8)
860
+ 6435
861
+
862
+ >>> binomial(n, -1)
863
+ 0
864
+
865
+ Rows of Pascal's triangle can be generated with the binomial function:
866
+
867
+ >>> for N in range(8):
868
+ ... print([binomial(N, i) for i in range(N + 1)])
869
+ ...
870
+ [1]
871
+ [1, 1]
872
+ [1, 2, 1]
873
+ [1, 3, 3, 1]
874
+ [1, 4, 6, 4, 1]
875
+ [1, 5, 10, 10, 5, 1]
876
+ [1, 6, 15, 20, 15, 6, 1]
877
+ [1, 7, 21, 35, 35, 21, 7, 1]
878
+
879
+ As can a given diagonal, e.g. the 4th diagonal:
880
+
881
+ >>> N = -4
882
+ >>> [binomial(N, i) for i in range(1 - N)]
883
+ [1, -4, 10, -20, 35]
884
+
885
+ >>> binomial(Rational(5, 4), 3)
886
+ -5/128
887
+ >>> binomial(Rational(-5, 4), 3)
888
+ -195/128
889
+
890
+ >>> binomial(n, 3)
891
+ binomial(n, 3)
892
+
893
+ >>> binomial(n, 3).expand(func=True)
894
+ n**3/6 - n**2/2 + n/3
895
+
896
+ >>> expand_func(binomial(n, 3))
897
+ n*(n - 2)*(n - 1)/6
898
+
899
+ In many cases, we can also compute binomial coefficients modulo a
900
+ prime p quickly using Lucas' Theorem [2]_, though we need to include
901
+ `evaluate=False` to postpone evaluation:
902
+
903
+ >>> from sympy import Mod
904
+ >>> Mod(binomial(156675, 4433, evaluate=False), 10**5 + 3)
905
+ 28625
906
+
907
+ Using a generalisation of Lucas's Theorem given by Granville [3]_,
908
+ we can extend this to arbitrary n:
909
+
910
+ >>> Mod(binomial(10**18, 10**12, evaluate=False), (10**5 + 3)**2)
911
+ 3744312326
912
+
913
+ References
914
+ ==========
915
+
916
+ .. [1] https://www.johndcook.com/blog/binomial_coefficients/
917
+ .. [2] https://en.wikipedia.org/wiki/Lucas%27s_theorem
918
+ .. [3] Binomial coefficients modulo prime powers, Andrew Granville,
919
+ Available: https://web.archive.org/web/20170202003812/http://www.dms.umontreal.ca/~andrew/PDF/BinCoeff.pdf
920
+ """
921
+
922
+ def fdiff(self, argindex=1):
923
+ from sympy.functions.special.gamma_functions import polygamma
924
+ if argindex == 1:
925
+ # https://functions.wolfram.com/GammaBetaErf/Binomial/20/01/01/
926
+ n, k = self.args
927
+ return binomial(n, k)*(polygamma(0, n + 1) - \
928
+ polygamma(0, n - k + 1))
929
+ elif argindex == 2:
930
+ # https://functions.wolfram.com/GammaBetaErf/Binomial/20/01/02/
931
+ n, k = self.args
932
+ return binomial(n, k)*(polygamma(0, n - k + 1) - \
933
+ polygamma(0, k + 1))
934
+ else:
935
+ raise ArgumentIndexError(self, argindex)
936
+
937
+ @classmethod
938
+ def _eval(self, n, k):
939
+ # n.is_Number and k.is_Integer and k != 1 and n != k
940
+
941
+ if k.is_Integer:
942
+ if n.is_Integer and n >= 0:
943
+ n, k = int(n), int(k)
944
+
945
+ if k > n:
946
+ return S.Zero
947
+ elif k > n // 2:
948
+ k = n - k
949
+
950
+ # XXX: This conditional logic should be moved to
951
+ # sympy.external.gmpy and the pure Python version of bincoef
952
+ # should be moved to sympy.external.ntheory.
953
+ if _gmpy is not None:
954
+ return Integer(_gmpy.bincoef(n, k))
955
+
956
+ d, result = n - k, 1
957
+ for i in range(1, k + 1):
958
+ d += 1
959
+ result = result * d // i
960
+ return Integer(result)
961
+ else:
962
+ d, result = n - k, 1
963
+ for i in range(1, k + 1):
964
+ d += 1
965
+ result *= d
966
+ return result / _factorial(k)
967
+
968
+ @classmethod
969
+ def eval(cls, n, k):
970
+ n, k = map(sympify, (n, k))
971
+ d = n - k
972
+ n_nonneg, n_isint = n.is_nonnegative, n.is_integer
973
+ if k.is_zero or ((n_nonneg or n_isint is False)
974
+ and d.is_zero):
975
+ return S.One
976
+ if (k - 1).is_zero or ((n_nonneg or n_isint is False)
977
+ and (d - 1).is_zero):
978
+ return n
979
+ if k.is_integer:
980
+ if k.is_negative or (n_nonneg and n_isint and d.is_negative):
981
+ return S.Zero
982
+ elif n.is_number:
983
+ res = cls._eval(n, k)
984
+ return res.expand(basic=True) if res else res
985
+ elif n_nonneg is False and n_isint:
986
+ # a special case when binomial evaluates to complex infinity
987
+ return S.ComplexInfinity
988
+ elif k.is_number:
989
+ from sympy.functions.special.gamma_functions import gamma
990
+ return gamma(n + 1)/(gamma(k + 1)*gamma(n - k + 1))
991
+
992
+ def _eval_Mod(self, q):
993
+ n, k = self.args
994
+
995
+ if any(x.is_integer is False for x in (n, k, q)):
996
+ raise ValueError("Integers expected for binomial Mod")
997
+
998
+ if all(x.is_Integer for x in (n, k, q)):
999
+ n, k = map(int, (n, k))
1000
+ aq, res = abs(q), 1
1001
+
1002
+ # handle negative integers k or n
1003
+ if k < 0:
1004
+ return S.Zero
1005
+ if n < 0:
1006
+ n = -n + k - 1
1007
+ res = -1 if k%2 else 1
1008
+
1009
+ # non negative integers k and n
1010
+ if k > n:
1011
+ return S.Zero
1012
+
1013
+ isprime = aq.is_prime
1014
+ aq = int(aq)
1015
+ if isprime:
1016
+ if aq < n:
1017
+ # use Lucas Theorem
1018
+ N, K = n, k
1019
+ while N or K:
1020
+ res = res*binomial(N % aq, K % aq) % aq
1021
+ N, K = N // aq, K // aq
1022
+
1023
+ else:
1024
+ # use Factorial Modulo
1025
+ d = n - k
1026
+ if k > d:
1027
+ k, d = d, k
1028
+ kf = 1
1029
+ for i in range(2, k + 1):
1030
+ kf = kf*i % aq
1031
+ df = kf
1032
+ for i in range(k + 1, d + 1):
1033
+ df = df*i % aq
1034
+ res *= df
1035
+ for i in range(d + 1, n + 1):
1036
+ res = res*i % aq
1037
+
1038
+ res *= pow(kf*df % aq, aq - 2, aq)
1039
+ res %= aq
1040
+
1041
+ elif _sqrt(q) < k and q != 1:
1042
+ res = binomial_mod(n, k, q)
1043
+
1044
+ else:
1045
+ # Binomial Factorization is performed by calculating the
1046
+ # exponents of primes <= n in `n! /(k! (n - k)!)`,
1047
+ # for non-negative integers n and k. As the exponent of
1048
+ # prime in n! is e_p(n) = [n/p] + [n/p**2] + ...
1049
+ # the exponent of prime in binomial(n, k) would be
1050
+ # e_p(n) - e_p(k) - e_p(n - k)
1051
+ M = int(_sqrt(n))
1052
+ for prime in sieve.primerange(2, n + 1):
1053
+ if prime > n - k:
1054
+ res = res*prime % aq
1055
+ elif prime > n // 2:
1056
+ continue
1057
+ elif prime > M:
1058
+ if n % prime < k % prime:
1059
+ res = res*prime % aq
1060
+ else:
1061
+ N, K = n, k
1062
+ exp = a = 0
1063
+
1064
+ while N > 0:
1065
+ a = int((N % prime) < (K % prime + a))
1066
+ N, K = N // prime, K // prime
1067
+ exp += a
1068
+
1069
+ if exp > 0:
1070
+ res *= pow(prime, exp, aq)
1071
+ res %= aq
1072
+
1073
+ return S(res % q)
1074
+
1075
+ def _eval_expand_func(self, **hints):
1076
+ """
1077
+ Function to expand binomial(n, k) when m is positive integer
1078
+ Also,
1079
+ n is self.args[0] and k is self.args[1] while using binomial(n, k)
1080
+ """
1081
+ n = self.args[0]
1082
+ if n.is_Number:
1083
+ return binomial(*self.args)
1084
+
1085
+ k = self.args[1]
1086
+ if (n-k).is_Integer:
1087
+ k = n - k
1088
+
1089
+ if k.is_Integer:
1090
+ if k.is_zero:
1091
+ return S.One
1092
+ elif k.is_negative:
1093
+ return S.Zero
1094
+ else:
1095
+ n, result = self.args[0], 1
1096
+ for i in range(1, k + 1):
1097
+ result *= n - k + i
1098
+ return result / _factorial(k)
1099
+ else:
1100
+ return binomial(*self.args)
1101
+
1102
+ def _eval_rewrite_as_factorial(self, n, k, **kwargs):
1103
+ return factorial(n)/(factorial(k)*factorial(n - k))
1104
+
1105
+ def _eval_rewrite_as_gamma(self, n, k, piecewise=True, **kwargs):
1106
+ from sympy.functions.special.gamma_functions import gamma
1107
+ return gamma(n + 1)/(gamma(k + 1)*gamma(n - k + 1))
1108
+
1109
+ def _eval_rewrite_as_tractable(self, n, k, limitvar=None, **kwargs):
1110
+ return self._eval_rewrite_as_gamma(n, k).rewrite('tractable')
1111
+
1112
+ def _eval_rewrite_as_FallingFactorial(self, n, k, **kwargs):
1113
+ if k.is_integer:
1114
+ return ff(n, k) / factorial(k)
1115
+
1116
+ def _eval_is_integer(self):
1117
+ n, k = self.args
1118
+ if n.is_integer and k.is_integer:
1119
+ return True
1120
+ elif k.is_integer is False:
1121
+ return False
1122
+
1123
+ def _eval_is_nonnegative(self):
1124
+ n, k = self.args
1125
+ if n.is_integer and k.is_integer:
1126
+ if n.is_nonnegative or k.is_negative or k.is_even:
1127
+ return True
1128
+ elif k.is_even is False:
1129
+ return False
1130
+
1131
+ def _eval_as_leading_term(self, x, logx, cdir):
1132
+ from sympy.functions.special.gamma_functions import gamma
1133
+ return self.rewrite(gamma)._eval_as_leading_term(x, logx=logx, cdir=cdir)
.venv/lib/python3.13/site-packages/sympy/functions/combinatorial/numbers.py ADDED
The diff for this file is too large to render. See raw diff
 
.venv/lib/python3.13/site-packages/sympy/functions/combinatorial/tests/__init__.py ADDED
File without changes
.venv/lib/python3.13/site-packages/sympy/functions/combinatorial/tests/test_comb_factorials.py ADDED
@@ -0,0 +1,653 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from sympy.concrete.products import Product
2
+ from sympy.core.function import expand_func
3
+ from sympy.core.mod import Mod
4
+ from sympy.core.mul import Mul
5
+ from sympy.core import EulerGamma
6
+ from sympy.core.numbers import (Float, I, Rational, nan, oo, pi, zoo)
7
+ from sympy.core.relational import Eq
8
+ from sympy.core.singleton import S
9
+ from sympy.core.symbol import (Dummy, Symbol, symbols)
10
+ from sympy.functions.combinatorial.factorials import (ff, rf, binomial, factorial, factorial2)
11
+ from sympy.functions.elementary.miscellaneous import sqrt
12
+ from sympy.functions.elementary.piecewise import Piecewise
13
+ from sympy.functions.special.gamma_functions import (gamma, polygamma)
14
+ from sympy.polys.polytools import Poly
15
+ from sympy.series.order import O
16
+ from sympy.simplify.simplify import simplify
17
+ from sympy.core.expr import unchanged
18
+ from sympy.core.function import ArgumentIndexError
19
+ from sympy.functions.combinatorial.factorials import subfactorial
20
+ from sympy.functions.special.gamma_functions import uppergamma
21
+ from sympy.testing.pytest import XFAIL, raises, slow
22
+
23
+ #Solves and Fixes Issue #10388 - This is the updated test for the same solved issue
24
+
25
+ def test_rf_eval_apply():
26
+ x, y = symbols('x,y')
27
+ n, k = symbols('n k', integer=True)
28
+ m = Symbol('m', integer=True, nonnegative=True)
29
+
30
+ assert rf(nan, y) is nan
31
+ assert rf(x, nan) is nan
32
+
33
+ assert unchanged(rf, x, y)
34
+
35
+ assert rf(oo, 0) == 1
36
+ assert rf(-oo, 0) == 1
37
+
38
+ assert rf(oo, 6) is oo
39
+ assert rf(-oo, 7) is -oo
40
+ assert rf(-oo, 6) is oo
41
+
42
+ assert rf(oo, -6) is oo
43
+ assert rf(-oo, -7) is oo
44
+
45
+ assert rf(-1, pi) == 0
46
+ assert rf(-5, 1 + I) == 0
47
+
48
+ assert unchanged(rf, -3, k)
49
+ assert unchanged(rf, x, Symbol('k', integer=False))
50
+ assert rf(-3, Symbol('k', integer=False)) == 0
51
+ assert rf(Symbol('x', negative=True, integer=True), Symbol('k', integer=False)) == 0
52
+
53
+ assert rf(x, 0) == 1
54
+ assert rf(x, 1) == x
55
+ assert rf(x, 2) == x*(x + 1)
56
+ assert rf(x, 3) == x*(x + 1)*(x + 2)
57
+ assert rf(x, 5) == x*(x + 1)*(x + 2)*(x + 3)*(x + 4)
58
+
59
+ assert rf(x, -1) == 1/(x - 1)
60
+ assert rf(x, -2) == 1/((x - 1)*(x - 2))
61
+ assert rf(x, -3) == 1/((x - 1)*(x - 2)*(x - 3))
62
+
63
+ assert rf(1, 100) == factorial(100)
64
+
65
+ assert rf(x**2 + 3*x, 2) == (x**2 + 3*x)*(x**2 + 3*x + 1)
66
+ assert isinstance(rf(x**2 + 3*x, 2), Mul)
67
+ assert rf(x**3 + x, -2) == 1/((x**3 + x - 1)*(x**3 + x - 2))
68
+
69
+ assert rf(Poly(x**2 + 3*x, x), 2) == Poly(x**4 + 8*x**3 + 19*x**2 + 12*x, x)
70
+ assert isinstance(rf(Poly(x**2 + 3*x, x), 2), Poly)
71
+ raises(ValueError, lambda: rf(Poly(x**2 + 3*x, x, y), 2))
72
+ assert rf(Poly(x**3 + x, x), -2) == 1/(x**6 - 9*x**5 + 35*x**4 - 75*x**3 + 94*x**2 - 66*x + 20)
73
+ raises(ValueError, lambda: rf(Poly(x**3 + x, x, y), -2))
74
+
75
+ assert rf(x, m).is_integer is None
76
+ assert rf(n, k).is_integer is None
77
+ assert rf(n, m).is_integer is True
78
+ assert rf(n, k + pi).is_integer is False
79
+ assert rf(n, m + pi).is_integer is False
80
+ assert rf(pi, m).is_integer is False
81
+
82
+ def check(x, k, o, n):
83
+ a, b = Dummy(), Dummy()
84
+ r = lambda x, k: o(a, b).rewrite(n).subs({a:x,b:k})
85
+ for i in range(-5,5):
86
+ for j in range(-5,5):
87
+ assert o(i, j) == r(i, j), (o, n, i, j)
88
+ check(x, k, rf, ff)
89
+ check(x, k, rf, binomial)
90
+ check(n, k, rf, factorial)
91
+ check(x, y, rf, factorial)
92
+ check(x, y, rf, binomial)
93
+
94
+ assert rf(x, k).rewrite(ff) == ff(x + k - 1, k)
95
+ assert rf(x, k).rewrite(gamma) == Piecewise(
96
+ (gamma(k + x)/gamma(x), x > 0),
97
+ ((-1)**k*gamma(1 - x)/gamma(-k - x + 1), True))
98
+ assert rf(5, k).rewrite(gamma) == gamma(k + 5)/24
99
+ assert rf(x, k).rewrite(binomial) == factorial(k)*binomial(x + k - 1, k)
100
+ assert rf(n, k).rewrite(factorial) == Piecewise(
101
+ (factorial(k + n - 1)/factorial(n - 1), n > 0),
102
+ ((-1)**k*factorial(-n)/factorial(-k - n), True))
103
+ assert rf(5, k).rewrite(factorial) == factorial(k + 4)/24
104
+ assert rf(x, y).rewrite(factorial) == rf(x, y)
105
+ assert rf(x, y).rewrite(binomial) == rf(x, y)
106
+
107
+ import random
108
+ from mpmath import rf as mpmath_rf
109
+ for i in range(100):
110
+ x = -500 + 500 * random.random()
111
+ k = -500 + 500 * random.random()
112
+ assert (abs(mpmath_rf(x, k) - rf(x, k)) < 10**(-15))
113
+
114
+
115
+ def test_ff_eval_apply():
116
+ x, y = symbols('x,y')
117
+ n, k = symbols('n k', integer=True)
118
+ m = Symbol('m', integer=True, nonnegative=True)
119
+
120
+ assert ff(nan, y) is nan
121
+ assert ff(x, nan) is nan
122
+
123
+ assert unchanged(ff, x, y)
124
+
125
+ assert ff(oo, 0) == 1
126
+ assert ff(-oo, 0) == 1
127
+
128
+ assert ff(oo, 6) is oo
129
+ assert ff(-oo, 7) is -oo
130
+ assert ff(-oo, 6) is oo
131
+
132
+ assert ff(oo, -6) is oo
133
+ assert ff(-oo, -7) is oo
134
+
135
+ assert ff(x, 0) == 1
136
+ assert ff(x, 1) == x
137
+ assert ff(x, 2) == x*(x - 1)
138
+ assert ff(x, 3) == x*(x - 1)*(x - 2)
139
+ assert ff(x, 5) == x*(x - 1)*(x - 2)*(x - 3)*(x - 4)
140
+
141
+ assert ff(x, -1) == 1/(x + 1)
142
+ assert ff(x, -2) == 1/((x + 1)*(x + 2))
143
+ assert ff(x, -3) == 1/((x + 1)*(x + 2)*(x + 3))
144
+
145
+ assert ff(100, 100) == factorial(100)
146
+
147
+ assert ff(2*x**2 - 5*x, 2) == (2*x**2 - 5*x)*(2*x**2 - 5*x - 1)
148
+ assert isinstance(ff(2*x**2 - 5*x, 2), Mul)
149
+ assert ff(x**2 + 3*x, -2) == 1/((x**2 + 3*x + 1)*(x**2 + 3*x + 2))
150
+
151
+ assert ff(Poly(2*x**2 - 5*x, x), 2) == Poly(4*x**4 - 28*x**3 + 59*x**2 - 35*x, x)
152
+ assert isinstance(ff(Poly(2*x**2 - 5*x, x), 2), Poly)
153
+ raises(ValueError, lambda: ff(Poly(2*x**2 - 5*x, x, y), 2))
154
+ assert ff(Poly(x**2 + 3*x, x), -2) == 1/(x**4 + 12*x**3 + 49*x**2 + 78*x + 40)
155
+ raises(ValueError, lambda: ff(Poly(x**2 + 3*x, x, y), -2))
156
+
157
+
158
+ assert ff(x, m).is_integer is None
159
+ assert ff(n, k).is_integer is None
160
+ assert ff(n, m).is_integer is True
161
+ assert ff(n, k + pi).is_integer is False
162
+ assert ff(n, m + pi).is_integer is False
163
+ assert ff(pi, m).is_integer is False
164
+
165
+ assert isinstance(ff(x, x), ff)
166
+ assert ff(n, n) == factorial(n)
167
+
168
+ def check(x, k, o, n):
169
+ a, b = Dummy(), Dummy()
170
+ r = lambda x, k: o(a, b).rewrite(n).subs({a:x,b:k})
171
+ for i in range(-5,5):
172
+ for j in range(-5,5):
173
+ assert o(i, j) == r(i, j), (o, n)
174
+ check(x, k, ff, rf)
175
+ check(x, k, ff, gamma)
176
+ check(n, k, ff, factorial)
177
+ check(x, k, ff, binomial)
178
+ check(x, y, ff, factorial)
179
+ check(x, y, ff, binomial)
180
+
181
+ assert ff(x, k).rewrite(rf) == rf(x - k + 1, k)
182
+ assert ff(x, k).rewrite(gamma) == Piecewise(
183
+ (gamma(x + 1)/gamma(-k + x + 1), x >= 0),
184
+ ((-1)**k*gamma(k - x)/gamma(-x), True))
185
+ assert ff(5, k).rewrite(gamma) == 120/gamma(6 - k)
186
+ assert ff(n, k).rewrite(factorial) == Piecewise(
187
+ (factorial(n)/factorial(-k + n), n >= 0),
188
+ ((-1)**k*factorial(k - n - 1)/factorial(-n - 1), True))
189
+ assert ff(5, k).rewrite(factorial) == 120/factorial(5 - k)
190
+ assert ff(x, k).rewrite(binomial) == factorial(k) * binomial(x, k)
191
+ assert ff(x, y).rewrite(factorial) == ff(x, y)
192
+ assert ff(x, y).rewrite(binomial) == ff(x, y)
193
+
194
+ import random
195
+ from mpmath import ff as mpmath_ff
196
+ for i in range(100):
197
+ x = -500 + 500 * random.random()
198
+ k = -500 + 500 * random.random()
199
+ a = mpmath_ff(x, k)
200
+ b = ff(x, k)
201
+ assert (abs(a - b) < abs(a) * 10**(-15))
202
+
203
+
204
+ def test_rf_ff_eval_hiprec():
205
+ maple = Float('6.9109401292234329956525265438452')
206
+ us = ff(18, Rational(2, 3)).evalf(32)
207
+ assert abs(us - maple)/us < 1e-31
208
+
209
+ maple = Float('6.8261540131125511557924466355367')
210
+ us = rf(18, Rational(2, 3)).evalf(32)
211
+ assert abs(us - maple)/us < 1e-31
212
+
213
+ maple = Float('34.007346127440197150854651814225')
214
+ us = rf(Float('4.4', 32), Float('2.2', 32))
215
+ assert abs(us - maple)/us < 1e-31
216
+
217
+
218
+ def test_rf_lambdify_mpmath():
219
+ from sympy.utilities.lambdify import lambdify
220
+ x, y = symbols('x,y')
221
+ f = lambdify((x,y), rf(x, y), 'mpmath')
222
+ maple = Float('34.007346127440197')
223
+ us = f(4.4, 2.2)
224
+ assert abs(us - maple)/us < 1e-15
225
+
226
+
227
+ def test_factorial():
228
+ x = Symbol('x')
229
+ n = Symbol('n', integer=True)
230
+ k = Symbol('k', integer=True, nonnegative=True)
231
+ r = Symbol('r', integer=False)
232
+ s = Symbol('s', integer=False, negative=True)
233
+ t = Symbol('t', nonnegative=True)
234
+ u = Symbol('u', noninteger=True)
235
+
236
+ assert factorial(-2) is zoo
237
+ assert factorial(0) == 1
238
+ assert factorial(7) == 5040
239
+ assert factorial(19) == 121645100408832000
240
+ assert factorial(31) == 8222838654177922817725562880000000
241
+ assert factorial(n).func == factorial
242
+ assert factorial(2*n).func == factorial
243
+
244
+ assert factorial(x).is_integer is None
245
+ assert factorial(n).is_integer is None
246
+ assert factorial(k).is_integer
247
+ assert factorial(r).is_integer is None
248
+
249
+ assert factorial(n).is_positive is None
250
+ assert factorial(k).is_positive
251
+
252
+ assert factorial(x).is_real is None
253
+ assert factorial(n).is_real is None
254
+ assert factorial(k).is_real is True
255
+ assert factorial(r).is_real is None
256
+ assert factorial(s).is_real is True
257
+ assert factorial(t).is_real is True
258
+ assert factorial(u).is_real is True
259
+
260
+ assert factorial(x).is_composite is None
261
+ assert factorial(n).is_composite is None
262
+ assert factorial(k).is_composite is None
263
+ assert factorial(k + 3).is_composite is True
264
+ assert factorial(r).is_composite is None
265
+ assert factorial(s).is_composite is None
266
+ assert factorial(t).is_composite is None
267
+ assert factorial(u).is_composite is None
268
+
269
+ assert factorial(oo) is oo
270
+
271
+
272
+ def test_factorial_Mod():
273
+ pr = Symbol('pr', prime=True)
274
+ p, q = 10**9 + 9, 10**9 + 33 # prime modulo
275
+ r, s = 10**7 + 5, 33333333 # composite modulo
276
+ assert Mod(factorial(pr - 1), pr) == pr - 1
277
+ assert Mod(factorial(pr - 1), -pr) == -1
278
+ assert Mod(factorial(r - 1, evaluate=False), r) == 0
279
+ assert Mod(factorial(s - 1, evaluate=False), s) == 0
280
+ assert Mod(factorial(p - 1, evaluate=False), p) == p - 1
281
+ assert Mod(factorial(q - 1, evaluate=False), q) == q - 1
282
+ assert Mod(factorial(p - 50, evaluate=False), p) == 854928834
283
+ assert Mod(factorial(q - 1800, evaluate=False), q) == 905504050
284
+ assert Mod(factorial(153, evaluate=False), r) == Mod(factorial(153), r)
285
+ assert Mod(factorial(255, evaluate=False), s) == Mod(factorial(255), s)
286
+ assert Mod(factorial(4, evaluate=False), 3) == S.Zero
287
+ assert Mod(factorial(5, evaluate=False), 6) == S.Zero
288
+
289
+
290
+ def test_factorial_diff():
291
+ n = Symbol('n', integer=True)
292
+
293
+ assert factorial(n).diff(n) == \
294
+ gamma(1 + n)*polygamma(0, 1 + n)
295
+ assert factorial(n**2).diff(n) == \
296
+ 2*n*gamma(1 + n**2)*polygamma(0, 1 + n**2)
297
+ raises(ArgumentIndexError, lambda: factorial(n**2).fdiff(2))
298
+
299
+
300
+ def test_factorial_series():
301
+ n = Symbol('n', integer=True)
302
+
303
+ assert factorial(n).series(n, 0, 3) == \
304
+ 1 - n*EulerGamma + n**2*(EulerGamma**2/2 + pi**2/12) + O(n**3)
305
+
306
+
307
+ def test_factorial_rewrite():
308
+ n = Symbol('n', integer=True)
309
+ k = Symbol('k', integer=True, nonnegative=True)
310
+
311
+ assert factorial(n).rewrite(gamma) == gamma(n + 1)
312
+ _i = Dummy('i')
313
+ assert factorial(k).rewrite(Product).dummy_eq(Product(_i, (_i, 1, k)))
314
+ assert factorial(n).rewrite(Product) == factorial(n)
315
+
316
+
317
+ def test_factorial2():
318
+ n = Symbol('n', integer=True)
319
+
320
+ assert factorial2(-1) == 1
321
+ assert factorial2(0) == 1
322
+ assert factorial2(7) == 105
323
+ assert factorial2(8) == 384
324
+
325
+ # The following is exhaustive
326
+ tt = Symbol('tt', integer=True, nonnegative=True)
327
+ tte = Symbol('tte', even=True, nonnegative=True)
328
+ tpe = Symbol('tpe', even=True, positive=True)
329
+ tto = Symbol('tto', odd=True, nonnegative=True)
330
+ tf = Symbol('tf', integer=True, nonnegative=False)
331
+ tfe = Symbol('tfe', even=True, nonnegative=False)
332
+ tfo = Symbol('tfo', odd=True, nonnegative=False)
333
+ ft = Symbol('ft', integer=False, nonnegative=True)
334
+ ff = Symbol('ff', integer=False, nonnegative=False)
335
+ fn = Symbol('fn', integer=False)
336
+ nt = Symbol('nt', nonnegative=True)
337
+ nf = Symbol('nf', nonnegative=False)
338
+ nn = Symbol('nn')
339
+ z = Symbol('z', zero=True)
340
+ #Solves and Fixes Issue #10388 - This is the updated test for the same solved issue
341
+ raises(ValueError, lambda: factorial2(oo))
342
+ raises(ValueError, lambda: factorial2(Rational(5, 2)))
343
+ raises(ValueError, lambda: factorial2(-4))
344
+ assert factorial2(n).is_integer is None
345
+ assert factorial2(tt - 1).is_integer
346
+ assert factorial2(tte - 1).is_integer
347
+ assert factorial2(tpe - 3).is_integer
348
+ assert factorial2(tto - 4).is_integer
349
+ assert factorial2(tto - 2).is_integer
350
+ assert factorial2(tf).is_integer is None
351
+ assert factorial2(tfe).is_integer is None
352
+ assert factorial2(tfo).is_integer is None
353
+ assert factorial2(ft).is_integer is None
354
+ assert factorial2(ff).is_integer is None
355
+ assert factorial2(fn).is_integer is None
356
+ assert factorial2(nt).is_integer is None
357
+ assert factorial2(nf).is_integer is None
358
+ assert factorial2(nn).is_integer is None
359
+
360
+ assert factorial2(n).is_positive is None
361
+ assert factorial2(tt - 1).is_positive is True
362
+ assert factorial2(tte - 1).is_positive is True
363
+ assert factorial2(tpe - 3).is_positive is True
364
+ assert factorial2(tpe - 1).is_positive is True
365
+ assert factorial2(tto - 2).is_positive is True
366
+ assert factorial2(tto - 1).is_positive is True
367
+ assert factorial2(tf).is_positive is None
368
+ assert factorial2(tfe).is_positive is None
369
+ assert factorial2(tfo).is_positive is None
370
+ assert factorial2(ft).is_positive is None
371
+ assert factorial2(ff).is_positive is None
372
+ assert factorial2(fn).is_positive is None
373
+ assert factorial2(nt).is_positive is None
374
+ assert factorial2(nf).is_positive is None
375
+ assert factorial2(nn).is_positive is None
376
+
377
+ assert factorial2(tt).is_even is None
378
+ assert factorial2(tt).is_odd is None
379
+ assert factorial2(tte).is_even is None
380
+ assert factorial2(tte).is_odd is None
381
+ assert factorial2(tte + 2).is_even is True
382
+ assert factorial2(tpe).is_even is True
383
+ assert factorial2(tpe).is_odd is False
384
+ assert factorial2(tto).is_odd is True
385
+ assert factorial2(tf).is_even is None
386
+ assert factorial2(tf).is_odd is None
387
+ assert factorial2(tfe).is_even is None
388
+ assert factorial2(tfe).is_odd is None
389
+ assert factorial2(tfo).is_even is False
390
+ assert factorial2(tfo).is_odd is None
391
+ assert factorial2(z).is_even is False
392
+ assert factorial2(z).is_odd is True
393
+
394
+
395
+ def test_factorial2_rewrite():
396
+ n = Symbol('n', integer=True)
397
+ assert factorial2(n).rewrite(gamma) == \
398
+ 2**(n/2)*Piecewise((1, Eq(Mod(n, 2), 0)), (sqrt(2)/sqrt(pi), Eq(Mod(n, 2), 1)))*gamma(n/2 + 1)
399
+ assert factorial2(2*n).rewrite(gamma) == 2**n*gamma(n + 1)
400
+ assert factorial2(2*n + 1).rewrite(gamma) == \
401
+ sqrt(2)*2**(n + S.Half)*gamma(n + Rational(3, 2))/sqrt(pi)
402
+
403
+
404
+ def test_binomial():
405
+ x = Symbol('x')
406
+ n = Symbol('n', integer=True)
407
+ nz = Symbol('nz', integer=True, nonzero=True)
408
+ k = Symbol('k', integer=True)
409
+ kp = Symbol('kp', integer=True, positive=True)
410
+ kn = Symbol('kn', integer=True, negative=True)
411
+ u = Symbol('u', negative=True)
412
+ v = Symbol('v', nonnegative=True)
413
+ p = Symbol('p', positive=True)
414
+ z = Symbol('z', zero=True)
415
+ nt = Symbol('nt', integer=False)
416
+ kt = Symbol('kt', integer=False)
417
+ a = Symbol('a', integer=True, nonnegative=True)
418
+ b = Symbol('b', integer=True, nonnegative=True)
419
+
420
+ assert binomial(0, 0) == 1
421
+ assert binomial(1, 1) == 1
422
+ assert binomial(10, 10) == 1
423
+ assert binomial(n, z) == 1
424
+ assert binomial(1, 2) == 0
425
+ assert binomial(-1, 2) == 1
426
+ assert binomial(1, -1) == 0
427
+ assert binomial(-1, 1) == -1
428
+ assert binomial(-1, -1) == 0
429
+ assert binomial(S.Half, S.Half) == 1
430
+ assert binomial(-10, 1) == -10
431
+ assert binomial(-10, 7) == -11440
432
+ assert binomial(n, -1) == 0 # holds for all integers (negative, zero, positive)
433
+ assert binomial(kp, -1) == 0
434
+ assert binomial(nz, 0) == 1
435
+ assert expand_func(binomial(n, 1)) == n
436
+ assert expand_func(binomial(n, 2)) == n*(n - 1)/2
437
+ assert expand_func(binomial(n, n - 2)) == n*(n - 1)/2
438
+ assert expand_func(binomial(n, n - 1)) == n
439
+ assert binomial(n, 3).func == binomial
440
+ assert binomial(n, 3).expand(func=True) == n**3/6 - n**2/2 + n/3
441
+ assert expand_func(binomial(n, 3)) == n*(n - 2)*(n - 1)/6
442
+ assert binomial(n, n).func == binomial # e.g. (-1, -1) == 0, (2, 2) == 1
443
+ assert binomial(n, n + 1).func == binomial # e.g. (-1, 0) == 1
444
+ assert binomial(kp, kp + 1) == 0
445
+ assert binomial(kn, kn) == 0 # issue #14529
446
+ assert binomial(n, u).func == binomial
447
+ assert binomial(kp, u).func == binomial
448
+ assert binomial(n, p).func == binomial
449
+ assert binomial(n, k).func == binomial
450
+ assert binomial(n, n + p).func == binomial
451
+ assert binomial(kp, kp + p).func == binomial
452
+
453
+ assert expand_func(binomial(n, n - 3)) == n*(n - 2)*(n - 1)/6
454
+
455
+ assert binomial(n, k).is_integer
456
+ assert binomial(nt, k).is_integer is None
457
+ assert binomial(x, nt).is_integer is False
458
+
459
+ assert binomial(gamma(25), 6) == 79232165267303928292058750056084441948572511312165380965440075720159859792344339983120618959044048198214221915637090855535036339620413440000
460
+ assert binomial(1324, 47) == 906266255662694632984994480774946083064699457235920708992926525848438478406790323869952
461
+ assert binomial(1735, 43) == 190910140420204130794758005450919715396159959034348676124678207874195064798202216379800
462
+ assert binomial(2512, 53) == 213894469313832631145798303740098720367984955243020898718979538096223399813295457822575338958939834177325304000
463
+ assert binomial(3383, 52) == 27922807788818096863529701501764372757272890613101645521813434902890007725667814813832027795881839396839287659777235
464
+ assert binomial(4321, 51) == 124595639629264868916081001263541480185227731958274383287107643816863897851139048158022599533438936036467601690983780576
465
+
466
+ assert binomial(a, b).is_nonnegative is True
467
+ assert binomial(-1, 2, evaluate=False).is_nonnegative is True
468
+ assert binomial(10, 5, evaluate=False).is_nonnegative is True
469
+ assert binomial(10, -3, evaluate=False).is_nonnegative is True
470
+ assert binomial(-10, -3, evaluate=False).is_nonnegative is True
471
+ assert binomial(-10, 2, evaluate=False).is_nonnegative is True
472
+ assert binomial(-10, 1, evaluate=False).is_nonnegative is False
473
+ assert binomial(-10, 7, evaluate=False).is_nonnegative is False
474
+
475
+ # issue #14625
476
+ for _ in (pi, -pi, nt, v, a):
477
+ assert binomial(_, _) == 1
478
+ assert binomial(_, _ - 1) == _
479
+ assert isinstance(binomial(u, u), binomial)
480
+ assert isinstance(binomial(u, u - 1), binomial)
481
+ assert isinstance(binomial(x, x), binomial)
482
+ assert isinstance(binomial(x, x - 1), binomial)
483
+
484
+ #issue #18802
485
+ assert expand_func(binomial(x + 1, x)) == x + 1
486
+ assert expand_func(binomial(x, x - 1)) == x
487
+ assert expand_func(binomial(x + 1, x - 1)) == x*(x + 1)/2
488
+ assert expand_func(binomial(x**2 + 1, x**2)) == x**2 + 1
489
+
490
+ # issue #13980 and #13981
491
+ assert binomial(-7, -5) == 0
492
+ assert binomial(-23, -12) == 0
493
+ assert binomial(Rational(13, 2), -10) == 0
494
+ assert binomial(-49, -51) == 0
495
+
496
+ assert binomial(19, Rational(-7, 2)) == S(-68719476736)/(911337863661225*pi)
497
+ assert binomial(0, Rational(3, 2)) == S(-2)/(3*pi)
498
+ assert binomial(-3, Rational(-7, 2)) is zoo
499
+ assert binomial(kn, kt) is zoo
500
+
501
+ assert binomial(nt, kt).func == binomial
502
+ assert binomial(nt, Rational(15, 6)) == 8*gamma(nt + 1)/(15*sqrt(pi)*gamma(nt - Rational(3, 2)))
503
+ assert binomial(Rational(20, 3), Rational(-10, 8)) == gamma(Rational(23, 3))/(gamma(Rational(-1, 4))*gamma(Rational(107, 12)))
504
+ assert binomial(Rational(19, 2), Rational(-7, 2)) == Rational(-1615, 8388608)
505
+ assert binomial(Rational(-13, 5), Rational(-7, 8)) == gamma(Rational(-8, 5))/(gamma(Rational(-29, 40))*gamma(Rational(1, 8)))
506
+ assert binomial(Rational(-19, 8), Rational(-13, 5)) == gamma(Rational(-11, 8))/(gamma(Rational(-8, 5))*gamma(Rational(49, 40)))
507
+
508
+ # binomial for complexes
509
+ assert binomial(I, Rational(-89, 8)) == gamma(1 + I)/(gamma(Rational(-81, 8))*gamma(Rational(97, 8) + I))
510
+ assert binomial(I, 2*I) == gamma(1 + I)/(gamma(1 - I)*gamma(1 + 2*I))
511
+ assert binomial(-7, I) is zoo
512
+ assert binomial(Rational(-7, 6), I) == gamma(Rational(-1, 6))/(gamma(Rational(-1, 6) - I)*gamma(1 + I))
513
+ assert binomial((1+2*I), (1+3*I)) == gamma(2 + 2*I)/(gamma(1 - I)*gamma(2 + 3*I))
514
+ assert binomial(I, 5) == Rational(1, 3) - I/S(12)
515
+ assert binomial((2*I + 3), 7) == -13*I/S(63)
516
+ assert isinstance(binomial(I, n), binomial)
517
+ assert expand_func(binomial(3, 2, evaluate=False)) == 3
518
+ assert expand_func(binomial(n, 0, evaluate=False)) == 1
519
+ assert expand_func(binomial(n, -2, evaluate=False)) == 0
520
+ assert expand_func(binomial(n, k)) == binomial(n, k)
521
+
522
+
523
+ def test_binomial_Mod():
524
+ p, q = 10**5 + 3, 10**9 + 33 # prime modulo
525
+ r = 10**7 + 5 # composite modulo
526
+
527
+ # A few tests to get coverage
528
+ # Lucas Theorem
529
+ assert Mod(binomial(156675, 4433, evaluate=False), p) == Mod(binomial(156675, 4433), p)
530
+
531
+ # factorial Mod
532
+ assert Mod(binomial(1234, 432, evaluate=False), q) == Mod(binomial(1234, 432), q)
533
+
534
+ # binomial factorize
535
+ assert Mod(binomial(253, 113, evaluate=False), r) == Mod(binomial(253, 113), r)
536
+
537
+ # using Granville's generalisation of Lucas' Theorem
538
+ assert Mod(binomial(10**18, 10**12, evaluate=False), p*p) == 3744312326
539
+
540
+
541
+ @slow
542
+ def test_binomial_Mod_slow():
543
+ p, q = 10**5 + 3, 10**9 + 33 # prime modulo
544
+ r, s = 10**7 + 5, 33333333 # composite modulo
545
+
546
+ n, k, m = symbols('n k m')
547
+ assert (binomial(n, k) % q).subs({n: s, k: p}) == Mod(binomial(s, p), q)
548
+ assert (binomial(n, k) % m).subs({n: 8, k: 5, m: 13}) == 4
549
+ assert (binomial(9, k) % 7).subs(k, 2) == 1
550
+
551
+ # Lucas Theorem
552
+ assert Mod(binomial(123456, 43253, evaluate=False), p) == Mod(binomial(123456, 43253), p)
553
+ assert Mod(binomial(-178911, 237, evaluate=False), p) == Mod(-binomial(178911 + 237 - 1, 237), p)
554
+ assert Mod(binomial(-178911, 238, evaluate=False), p) == Mod(binomial(178911 + 238 - 1, 238), p)
555
+
556
+ # factorial Mod
557
+ assert Mod(binomial(9734, 451, evaluate=False), q) == Mod(binomial(9734, 451), q)
558
+ assert Mod(binomial(-10733, 4459, evaluate=False), q) == Mod(binomial(-10733, 4459), q)
559
+ assert Mod(binomial(-15733, 4458, evaluate=False), q) == Mod(binomial(-15733, 4458), q)
560
+ assert Mod(binomial(23, -38, evaluate=False), q) is S.Zero
561
+ assert Mod(binomial(23, 38, evaluate=False), q) is S.Zero
562
+
563
+ # binomial factorize
564
+ assert Mod(binomial(753, 119, evaluate=False), r) == Mod(binomial(753, 119), r)
565
+ assert Mod(binomial(3781, 948, evaluate=False), s) == Mod(binomial(3781, 948), s)
566
+ assert Mod(binomial(25773, 1793, evaluate=False), s) == Mod(binomial(25773, 1793), s)
567
+ assert Mod(binomial(-753, 118, evaluate=False), r) == Mod(binomial(-753, 118), r)
568
+ assert Mod(binomial(-25773, 1793, evaluate=False), s) == Mod(binomial(-25773, 1793), s)
569
+
570
+
571
+ def test_binomial_diff():
572
+ n = Symbol('n', integer=True)
573
+ k = Symbol('k', integer=True)
574
+
575
+ assert binomial(n, k).diff(n) == \
576
+ (-polygamma(0, 1 + n - k) + polygamma(0, 1 + n))*binomial(n, k)
577
+ assert binomial(n**2, k**3).diff(n) == \
578
+ 2*n*(-polygamma(
579
+ 0, 1 + n**2 - k**3) + polygamma(0, 1 + n**2))*binomial(n**2, k**3)
580
+
581
+ assert binomial(n, k).diff(k) == \
582
+ (-polygamma(0, 1 + k) + polygamma(0, 1 + n - k))*binomial(n, k)
583
+ assert binomial(n**2, k**3).diff(k) == \
584
+ 3*k**2*(-polygamma(
585
+ 0, 1 + k**3) + polygamma(0, 1 + n**2 - k**3))*binomial(n**2, k**3)
586
+ raises(ArgumentIndexError, lambda: binomial(n, k).fdiff(3))
587
+
588
+
589
+ def test_binomial_rewrite():
590
+ n = Symbol('n', integer=True)
591
+ k = Symbol('k', integer=True)
592
+ x = Symbol('x')
593
+
594
+ assert binomial(n, k).rewrite(
595
+ factorial) == factorial(n)/(factorial(k)*factorial(n - k))
596
+ assert binomial(
597
+ n, k).rewrite(gamma) == gamma(n + 1)/(gamma(k + 1)*gamma(n - k + 1))
598
+ assert binomial(n, k).rewrite(ff) == ff(n, k) / factorial(k)
599
+ assert binomial(n, x).rewrite(ff) == binomial(n, x)
600
+
601
+
602
+ @XFAIL
603
+ def test_factorial_simplify_fail():
604
+ # simplify(factorial(x + 1).diff(x) - ((x + 1)*factorial(x)).diff(x))) == 0
605
+ from sympy.abc import x
606
+ assert simplify(x*polygamma(0, x + 1) - x*polygamma(0, x + 2) +
607
+ polygamma(0, x + 1) - polygamma(0, x + 2) + 1) == 0
608
+
609
+
610
+ def test_subfactorial():
611
+ assert all(subfactorial(i) == ans for i, ans in enumerate(
612
+ [1, 0, 1, 2, 9, 44, 265, 1854, 14833, 133496]))
613
+ assert subfactorial(oo) is oo
614
+ assert subfactorial(nan) is nan
615
+ assert subfactorial(23) == 9510425471055777937262
616
+ assert unchanged(subfactorial, 2.2)
617
+
618
+ x = Symbol('x')
619
+ assert subfactorial(x).rewrite(uppergamma) == uppergamma(x + 1, -1)/S.Exp1
620
+
621
+ tt = Symbol('tt', integer=True, nonnegative=True)
622
+ tf = Symbol('tf', integer=True, nonnegative=False)
623
+ tn = Symbol('tf', integer=True)
624
+ ft = Symbol('ft', integer=False, nonnegative=True)
625
+ ff = Symbol('ff', integer=False, nonnegative=False)
626
+ fn = Symbol('ff', integer=False)
627
+ nt = Symbol('nt', nonnegative=True)
628
+ nf = Symbol('nf', nonnegative=False)
629
+ nn = Symbol('nf')
630
+ te = Symbol('te', even=True, nonnegative=True)
631
+ to = Symbol('to', odd=True, nonnegative=True)
632
+ assert subfactorial(tt).is_integer
633
+ assert subfactorial(tf).is_integer is None
634
+ assert subfactorial(tn).is_integer is None
635
+ assert subfactorial(ft).is_integer is None
636
+ assert subfactorial(ff).is_integer is None
637
+ assert subfactorial(fn).is_integer is None
638
+ assert subfactorial(nt).is_integer is None
639
+ assert subfactorial(nf).is_integer is None
640
+ assert subfactorial(nn).is_integer is None
641
+ assert subfactorial(tt).is_nonnegative
642
+ assert subfactorial(tf).is_nonnegative is None
643
+ assert subfactorial(tn).is_nonnegative is None
644
+ assert subfactorial(ft).is_nonnegative is None
645
+ assert subfactorial(ff).is_nonnegative is None
646
+ assert subfactorial(fn).is_nonnegative is None
647
+ assert subfactorial(nt).is_nonnegative is None
648
+ assert subfactorial(nf).is_nonnegative is None
649
+ assert subfactorial(nn).is_nonnegative is None
650
+ assert subfactorial(tt).is_even is None
651
+ assert subfactorial(tt).is_odd is None
652
+ assert subfactorial(te).is_odd is True
653
+ assert subfactorial(to).is_even is True
.venv/lib/python3.13/site-packages/sympy/functions/combinatorial/tests/test_comb_numbers.py ADDED
@@ -0,0 +1,1250 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import string
2
+
3
+ from sympy.concrete.products import Product
4
+ from sympy.concrete.summations import Sum
5
+ from sympy.core.function import (diff, expand_func)
6
+ from sympy.core import (EulerGamma, TribonacciConstant)
7
+ from sympy.core.numbers import (Float, I, Rational, oo, pi)
8
+ from sympy.core.singleton import S
9
+ from sympy.core.symbol import (Dummy, Symbol, symbols)
10
+ from sympy.functions.combinatorial.numbers import carmichael
11
+ from sympy.functions.elementary.complexes import (im, re)
12
+ from sympy.functions.elementary.integers import floor
13
+ from sympy.polys.polytools import cancel
14
+ from sympy.series.limits import limit, Limit
15
+ from sympy.series.order import O
16
+ from sympy.functions import (
17
+ bernoulli, harmonic, bell, fibonacci, tribonacci, lucas, euler, catalan,
18
+ genocchi, andre, partition, divisor_sigma, udivisor_sigma, legendre_symbol,
19
+ jacobi_symbol, kronecker_symbol, mobius,
20
+ primenu, primeomega, totient, reduced_totient, primepi,
21
+ motzkin, binomial, gamma, sqrt, cbrt, hyper, log, digamma,
22
+ trigamma, polygamma, factorial, sin, cos, cot, polylog, zeta, dirichlet_eta)
23
+ from sympy.functions.combinatorial.numbers import _nT
24
+ from sympy.ntheory.factor_ import factorint
25
+
26
+ from sympy.core.expr import unchanged
27
+ from sympy.core.numbers import GoldenRatio, Integer
28
+
29
+ from sympy.testing.pytest import raises, nocache_fail, warns_deprecated_sympy
30
+ from sympy.abc import x
31
+
32
+
33
+ def test_carmichael():
34
+ with warns_deprecated_sympy():
35
+ assert carmichael.is_prime(2821) == False
36
+
37
+
38
+ def test_bernoulli():
39
+ assert bernoulli(0) == 1
40
+ assert bernoulli(1) == Rational(1, 2)
41
+ assert bernoulli(2) == Rational(1, 6)
42
+ assert bernoulli(3) == 0
43
+ assert bernoulli(4) == Rational(-1, 30)
44
+ assert bernoulli(5) == 0
45
+ assert bernoulli(6) == Rational(1, 42)
46
+ assert bernoulli(7) == 0
47
+ assert bernoulli(8) == Rational(-1, 30)
48
+ assert bernoulli(10) == Rational(5, 66)
49
+ assert bernoulli(1000001) == 0
50
+
51
+ assert bernoulli(0, x) == 1
52
+ assert bernoulli(1, x) == x - S.Half
53
+ assert bernoulli(2, x) == x**2 - x + Rational(1, 6)
54
+ assert bernoulli(3, x) == x**3 - (3*x**2)/2 + x/2
55
+
56
+ # Should be fast; computed with mpmath
57
+ b = bernoulli(1000)
58
+ assert b.p % 10**10 == 7950421099
59
+ assert b.q == 342999030
60
+
61
+ b = bernoulli(10**6, evaluate=False).evalf()
62
+ assert str(b) == '-2.23799235765713e+4767529'
63
+
64
+ # Issue #8527
65
+ l = Symbol('l', integer=True)
66
+ m = Symbol('m', integer=True, nonnegative=True)
67
+ n = Symbol('n', integer=True, positive=True)
68
+ assert isinstance(bernoulli(2 * l + 1), bernoulli)
69
+ assert isinstance(bernoulli(2 * m + 1), bernoulli)
70
+ assert bernoulli(2 * n + 1) == 0
71
+
72
+ assert bernoulli(x, 1) == bernoulli(x)
73
+
74
+ assert str(bernoulli(0.0, 2.3).evalf(n=10)) == '1.000000000'
75
+ assert str(bernoulli(1.0).evalf(n=10)) == '0.5000000000'
76
+ assert str(bernoulli(1.2).evalf(n=10)) == '0.4195995367'
77
+ assert str(bernoulli(1.2, 0.8).evalf(n=10)) == '0.2144830348'
78
+ assert str(bernoulli(1.2, -0.8).evalf(n=10)) == '-1.158865646 - 0.6745558744*I'
79
+ assert str(bernoulli(3.0, 1j).evalf(n=10)) == '1.5 - 0.5*I'
80
+ assert str(bernoulli(I).evalf(n=10)) == '0.9268485643 - 0.5821580598*I'
81
+ assert str(bernoulli(I, I).evalf(n=10)) == '0.1267792071 + 0.01947413152*I'
82
+ assert bernoulli(x).evalf() == bernoulli(x)
83
+
84
+
85
+ def test_bernoulli_rewrite():
86
+ from sympy.functions.elementary.piecewise import Piecewise
87
+ n = Symbol('n', integer=True, nonnegative=True)
88
+
89
+ assert bernoulli(-1).rewrite(zeta) == pi**2/6
90
+ assert bernoulli(-2).rewrite(zeta) == 2*zeta(3)
91
+ assert not bernoulli(n, -3).rewrite(zeta).has(harmonic)
92
+ assert bernoulli(-4, x).rewrite(zeta) == 4*zeta(5, x)
93
+ assert isinstance(bernoulli(n, x).rewrite(zeta), Piecewise)
94
+ assert bernoulli(n+1, x).rewrite(zeta) == -(n+1) * zeta(-n, x)
95
+
96
+
97
+ def test_fibonacci():
98
+ assert [fibonacci(n) for n in range(-3, 5)] == [2, -1, 1, 0, 1, 1, 2, 3]
99
+ assert fibonacci(100) == 354224848179261915075
100
+ assert [lucas(n) for n in range(-3, 5)] == [-4, 3, -1, 2, 1, 3, 4, 7]
101
+ assert lucas(100) == 792070839848372253127
102
+
103
+ assert fibonacci(1, x) == 1
104
+ assert fibonacci(2, x) == x
105
+ assert fibonacci(3, x) == x**2 + 1
106
+ assert fibonacci(4, x) == x**3 + 2*x
107
+
108
+ # issue #8800
109
+ n = Dummy('n')
110
+ assert fibonacci(n).limit(n, S.Infinity) is S.Infinity
111
+ assert lucas(n).limit(n, S.Infinity) is S.Infinity
112
+
113
+ assert fibonacci(n).rewrite(sqrt) == \
114
+ 2**(-n)*sqrt(5)*((1 + sqrt(5))**n - (-sqrt(5) + 1)**n) / 5
115
+ assert fibonacci(n).rewrite(sqrt).subs(n, 10).expand() == fibonacci(10)
116
+ assert fibonacci(n).rewrite(GoldenRatio).subs(n,10).evalf() == \
117
+ Float(fibonacci(10))
118
+ assert lucas(n).rewrite(sqrt) == \
119
+ (fibonacci(n-1).rewrite(sqrt) + fibonacci(n+1).rewrite(sqrt)).simplify()
120
+ assert lucas(n).rewrite(sqrt).subs(n, 10).expand() == lucas(10)
121
+ raises(ValueError, lambda: fibonacci(-3, x))
122
+
123
+
124
+ def test_tribonacci():
125
+ assert [tribonacci(n) for n in range(8)] == [0, 1, 1, 2, 4, 7, 13, 24]
126
+ assert tribonacci(100) == 98079530178586034536500564
127
+
128
+ assert tribonacci(0, x) == 0
129
+ assert tribonacci(1, x) == 1
130
+ assert tribonacci(2, x) == x**2
131
+ assert tribonacci(3, x) == x**4 + x
132
+ assert tribonacci(4, x) == x**6 + 2*x**3 + 1
133
+ assert tribonacci(5, x) == x**8 + 3*x**5 + 3*x**2
134
+
135
+ n = Dummy('n')
136
+ assert tribonacci(n).limit(n, S.Infinity) is S.Infinity
137
+
138
+ w = (-1 + S.ImaginaryUnit * sqrt(3)) / 2
139
+ a = (1 + cbrt(19 + 3*sqrt(33)) + cbrt(19 - 3*sqrt(33))) / 3
140
+ b = (1 + w*cbrt(19 + 3*sqrt(33)) + w**2*cbrt(19 - 3*sqrt(33))) / 3
141
+ c = (1 + w**2*cbrt(19 + 3*sqrt(33)) + w*cbrt(19 - 3*sqrt(33))) / 3
142
+ assert tribonacci(n).rewrite(sqrt) == \
143
+ (a**(n + 1)/((a - b)*(a - c))
144
+ + b**(n + 1)/((b - a)*(b - c))
145
+ + c**(n + 1)/((c - a)*(c - b)))
146
+ assert tribonacci(n).rewrite(sqrt).subs(n, 4).simplify() == tribonacci(4)
147
+ assert tribonacci(n).rewrite(GoldenRatio).subs(n,10).evalf() == \
148
+ Float(tribonacci(10))
149
+ assert tribonacci(n).rewrite(TribonacciConstant) == floor(
150
+ 3*TribonacciConstant**n*(102*sqrt(33) + 586)**Rational(1, 3)/
151
+ (-2*(102*sqrt(33) + 586)**Rational(1, 3) + 4 + (102*sqrt(33)
152
+ + 586)**Rational(2, 3)) + S.Half)
153
+ raises(ValueError, lambda: tribonacci(-1, x))
154
+
155
+
156
+ @nocache_fail
157
+ def test_bell():
158
+ assert [bell(n) for n in range(8)] == [1, 1, 2, 5, 15, 52, 203, 877]
159
+
160
+ assert bell(0, x) == 1
161
+ assert bell(1, x) == x
162
+ assert bell(2, x) == x**2 + x
163
+ assert bell(5, x) == x**5 + 10*x**4 + 25*x**3 + 15*x**2 + x
164
+ assert bell(oo) is S.Infinity
165
+ raises(ValueError, lambda: bell(oo, x))
166
+
167
+ raises(ValueError, lambda: bell(-1))
168
+ raises(ValueError, lambda: bell(S.Half))
169
+
170
+ X = symbols('x:6')
171
+ # X = (x0, x1, .. x5)
172
+ # at the same time: X[1] = x1, X[2] = x2 for standard readablity.
173
+ # but we must supply zero-based indexed object X[1:] = (x1, .. x5)
174
+
175
+ assert bell(6, 2, X[1:]) == 6*X[5]*X[1] + 15*X[4]*X[2] + 10*X[3]**2
176
+ assert bell(
177
+ 6, 3, X[1:]) == 15*X[4]*X[1]**2 + 60*X[3]*X[2]*X[1] + 15*X[2]**3
178
+
179
+ X = (1, 10, 100, 1000, 10000)
180
+ assert bell(6, 2, X) == (6 + 15 + 10)*10000
181
+
182
+ X = (1, 2, 3, 3, 5)
183
+ assert bell(6, 2, X) == 6*5 + 15*3*2 + 10*3**2
184
+
185
+ X = (1, 2, 3, 5)
186
+ assert bell(6, 3, X) == 15*5 + 60*3*2 + 15*2**3
187
+
188
+ # Dobinski's formula
189
+ n = Symbol('n', integer=True, nonnegative=True)
190
+ # For large numbers, this is too slow
191
+ # For nonintegers, there are significant precision errors
192
+ for i in [0, 2, 3, 7, 13, 42, 55]:
193
+ # Running without the cache this is either very slow or goes into an
194
+ # infinite loop.
195
+ assert bell(i).evalf() == bell(n).rewrite(Sum).evalf(subs={n: i})
196
+
197
+ m = Symbol("m")
198
+ assert bell(m).rewrite(Sum) == bell(m)
199
+ assert bell(n, m).rewrite(Sum) == bell(n, m)
200
+ # issue 9184
201
+ n = Dummy('n')
202
+ assert bell(n).limit(n, S.Infinity) is S.Infinity
203
+
204
+
205
+ def test_harmonic():
206
+ n = Symbol("n")
207
+ m = Symbol("m")
208
+
209
+ assert harmonic(n, 0) == n
210
+ assert harmonic(n).evalf() == harmonic(n)
211
+ assert harmonic(n, 1) == harmonic(n)
212
+ assert harmonic(1, n) == 1
213
+
214
+ assert harmonic(0, 1) == 0
215
+ assert harmonic(1, 1) == 1
216
+ assert harmonic(2, 1) == Rational(3, 2)
217
+ assert harmonic(3, 1) == Rational(11, 6)
218
+ assert harmonic(4, 1) == Rational(25, 12)
219
+ assert harmonic(0, 2) == 0
220
+ assert harmonic(1, 2) == 1
221
+ assert harmonic(2, 2) == Rational(5, 4)
222
+ assert harmonic(3, 2) == Rational(49, 36)
223
+ assert harmonic(4, 2) == Rational(205, 144)
224
+ assert harmonic(0, 3) == 0
225
+ assert harmonic(1, 3) == 1
226
+ assert harmonic(2, 3) == Rational(9, 8)
227
+ assert harmonic(3, 3) == Rational(251, 216)
228
+ assert harmonic(4, 3) == Rational(2035, 1728)
229
+
230
+ assert harmonic(oo, -1) is S.NaN
231
+ assert harmonic(oo, 0) is oo
232
+ assert harmonic(oo, S.Half) is oo
233
+ assert harmonic(oo, 1) is oo
234
+ assert harmonic(oo, 2) == (pi**2)/6
235
+ assert harmonic(oo, 3) == zeta(3)
236
+ assert harmonic(oo, Dummy(negative=True)) is S.NaN
237
+ ip = Dummy(integer=True, positive=True)
238
+ if (1/ip <= 1) is True: #---------------------------------+
239
+ assert None, 'delete this if-block and the next line' #|
240
+ ip = Dummy(even=True, positive=True) #--------------------+
241
+ assert harmonic(oo, 1/ip) is oo
242
+ assert harmonic(oo, 1 + ip) is zeta(1 + ip)
243
+
244
+ assert harmonic(0, m) == 0
245
+ assert harmonic(-1, -1) == 0
246
+ assert harmonic(-1, 0) == -1
247
+ assert harmonic(-1, 1) is S.ComplexInfinity
248
+ assert harmonic(-1, 2) is S.NaN
249
+ assert harmonic(-3, -2) == -5
250
+ assert harmonic(-3, -3) == 9
251
+
252
+
253
+ def test_harmonic_rational():
254
+ ne = S(6)
255
+ no = S(5)
256
+ pe = S(8)
257
+ po = S(9)
258
+ qe = S(10)
259
+ qo = S(13)
260
+
261
+ Heee = harmonic(ne + pe/qe)
262
+ Aeee = (-log(10) + 2*(Rational(-1, 4) + sqrt(5)/4)*log(sqrt(-sqrt(5)/8 + Rational(5, 8)))
263
+ + 2*(-sqrt(5)/4 - Rational(1, 4))*log(sqrt(sqrt(5)/8 + Rational(5, 8)))
264
+ + pi*sqrt(2*sqrt(5)/5 + 1)/2 + Rational(13944145, 4720968))
265
+
266
+ Heeo = harmonic(ne + pe/qo)
267
+ Aeeo = (-log(26) + 2*log(sin(pi*Rational(3, 13)))*cos(pi*Rational(4, 13)) + 2*log(sin(pi*Rational(2, 13)))*cos(pi*Rational(32, 13))
268
+ + 2*log(sin(pi*Rational(5, 13)))*cos(pi*Rational(80, 13)) - 2*log(sin(pi*Rational(6, 13)))*cos(pi*Rational(5, 13))
269
+ - 2*log(sin(pi*Rational(4, 13)))*cos(pi/13) + pi*cot(pi*Rational(5, 13))/2 - 2*log(sin(pi/13))*cos(pi*Rational(3, 13))
270
+ + Rational(2422020029, 702257080))
271
+
272
+ Heoe = harmonic(ne + po/qe)
273
+ Aeoe = (-log(20) + 2*(Rational(1, 4) + sqrt(5)/4)*log(Rational(-1, 4) + sqrt(5)/4)
274
+ + 2*(Rational(-1, 4) + sqrt(5)/4)*log(sqrt(-sqrt(5)/8 + Rational(5, 8)))
275
+ + 2*(-sqrt(5)/4 - Rational(1, 4))*log(sqrt(sqrt(5)/8 + Rational(5, 8)))
276
+ + 2*(-sqrt(5)/4 + Rational(1, 4))*log(Rational(1, 4) + sqrt(5)/4)
277
+ + Rational(11818877030, 4286604231) + pi*sqrt(2*sqrt(5) + 5)/2)
278
+
279
+ Heoo = harmonic(ne + po/qo)
280
+ Aeoo = (-log(26) + 2*log(sin(pi*Rational(3, 13)))*cos(pi*Rational(54, 13)) + 2*log(sin(pi*Rational(4, 13)))*cos(pi*Rational(6, 13))
281
+ + 2*log(sin(pi*Rational(6, 13)))*cos(pi*Rational(108, 13)) - 2*log(sin(pi*Rational(5, 13)))*cos(pi/13)
282
+ - 2*log(sin(pi/13))*cos(pi*Rational(5, 13)) + pi*cot(pi*Rational(4, 13))/2
283
+ - 2*log(sin(pi*Rational(2, 13)))*cos(pi*Rational(3, 13)) + Rational(11669332571, 3628714320))
284
+
285
+ Hoee = harmonic(no + pe/qe)
286
+ Aoee = (-log(10) + 2*(Rational(-1, 4) + sqrt(5)/4)*log(sqrt(-sqrt(5)/8 + Rational(5, 8)))
287
+ + 2*(-sqrt(5)/4 - Rational(1, 4))*log(sqrt(sqrt(5)/8 + Rational(5, 8)))
288
+ + pi*sqrt(2*sqrt(5)/5 + 1)/2 + Rational(779405, 277704))
289
+
290
+ Hoeo = harmonic(no + pe/qo)
291
+ Aoeo = (-log(26) + 2*log(sin(pi*Rational(3, 13)))*cos(pi*Rational(4, 13)) + 2*log(sin(pi*Rational(2, 13)))*cos(pi*Rational(32, 13))
292
+ + 2*log(sin(pi*Rational(5, 13)))*cos(pi*Rational(80, 13)) - 2*log(sin(pi*Rational(6, 13)))*cos(pi*Rational(5, 13))
293
+ - 2*log(sin(pi*Rational(4, 13)))*cos(pi/13) + pi*cot(pi*Rational(5, 13))/2
294
+ - 2*log(sin(pi/13))*cos(pi*Rational(3, 13)) + Rational(53857323, 16331560))
295
+
296
+ Hooe = harmonic(no + po/qe)
297
+ Aooe = (-log(20) + 2*(Rational(1, 4) + sqrt(5)/4)*log(Rational(-1, 4) + sqrt(5)/4)
298
+ + 2*(Rational(-1, 4) + sqrt(5)/4)*log(sqrt(-sqrt(5)/8 + Rational(5, 8)))
299
+ + 2*(-sqrt(5)/4 - Rational(1, 4))*log(sqrt(sqrt(5)/8 + Rational(5, 8)))
300
+ + 2*(-sqrt(5)/4 + Rational(1, 4))*log(Rational(1, 4) + sqrt(5)/4)
301
+ + Rational(486853480, 186374097) + pi*sqrt(2*sqrt(5) + 5)/2)
302
+
303
+ Hooo = harmonic(no + po/qo)
304
+ Aooo = (-log(26) + 2*log(sin(pi*Rational(3, 13)))*cos(pi*Rational(54, 13)) + 2*log(sin(pi*Rational(4, 13)))*cos(pi*Rational(6, 13))
305
+ + 2*log(sin(pi*Rational(6, 13)))*cos(pi*Rational(108, 13)) - 2*log(sin(pi*Rational(5, 13)))*cos(pi/13)
306
+ - 2*log(sin(pi/13))*cos(pi*Rational(5, 13)) + pi*cot(pi*Rational(4, 13))/2
307
+ - 2*log(sin(pi*Rational(2, 13)))*cos(3*pi/13) + Rational(383693479, 125128080))
308
+
309
+ H = [Heee, Heeo, Heoe, Heoo, Hoee, Hoeo, Hooe, Hooo]
310
+ A = [Aeee, Aeeo, Aeoe, Aeoo, Aoee, Aoeo, Aooe, Aooo]
311
+ for h, a in zip(H, A):
312
+ e = expand_func(h).doit()
313
+ assert cancel(e/a) == 1
314
+ assert abs(h.n() - a.n()) < 1e-12
315
+
316
+
317
+ def test_harmonic_evalf():
318
+ assert str(harmonic(1.5).evalf(n=10)) == '1.280372306'
319
+ assert str(harmonic(1.5, 2).evalf(n=10)) == '1.154576311' # issue 7443
320
+ assert str(harmonic(4.0, -3).evalf(n=10)) == '100.0000000'
321
+ assert str(harmonic(7.0, 1.0).evalf(n=10)) == '2.592857143'
322
+ assert str(harmonic(1, pi).evalf(n=10)) == '1.000000000'
323
+ assert str(harmonic(2, pi).evalf(n=10)) == '1.113314732'
324
+ assert str(harmonic(1000.0, pi).evalf(n=10)) == '1.176241563'
325
+ assert str(harmonic(I).evalf(n=10)) == '0.6718659855 + 1.076674047*I'
326
+ assert str(harmonic(I, I).evalf(n=10)) == '-0.3970915266 + 1.9629689*I'
327
+
328
+ assert harmonic(-1.0, 1).evalf() is S.NaN
329
+ assert harmonic(-2.0, 2.0).evalf() is S.NaN
330
+
331
+ def test_harmonic_rewrite():
332
+ from sympy.functions.elementary.piecewise import Piecewise
333
+ n = Symbol("n")
334
+ m = Symbol("m", integer=True, positive=True)
335
+ x1 = Symbol("x1", positive=True)
336
+ x2 = Symbol("x2", negative=True)
337
+
338
+ assert harmonic(n).rewrite(digamma) == polygamma(0, n + 1) + EulerGamma
339
+ assert harmonic(n).rewrite(trigamma) == polygamma(0, n + 1) + EulerGamma
340
+ assert harmonic(n).rewrite(polygamma) == polygamma(0, n + 1) + EulerGamma
341
+
342
+ assert harmonic(n,3).rewrite(polygamma) == polygamma(2, n + 1)/2 - polygamma(2, 1)/2
343
+ assert isinstance(harmonic(n,m).rewrite(polygamma), Piecewise)
344
+
345
+ assert expand_func(harmonic(n+4)) == harmonic(n) + 1/(n + 4) + 1/(n + 3) + 1/(n + 2) + 1/(n + 1)
346
+ assert expand_func(harmonic(n-4)) == harmonic(n) - 1/(n - 1) - 1/(n - 2) - 1/(n - 3) - 1/n
347
+
348
+ assert harmonic(n, m).rewrite("tractable") == harmonic(n, m).rewrite(polygamma)
349
+ assert harmonic(n, x1).rewrite("tractable") == harmonic(n, x1)
350
+ assert harmonic(n, x1 + 1).rewrite("tractable") == zeta(x1 + 1) - zeta(x1 + 1, n + 1)
351
+ assert harmonic(n, x2).rewrite("tractable") == zeta(x2) - zeta(x2, n + 1)
352
+
353
+ _k = Dummy("k")
354
+ assert harmonic(n).rewrite(Sum).dummy_eq(Sum(1/_k, (_k, 1, n)))
355
+ assert harmonic(n, m).rewrite(Sum).dummy_eq(Sum(_k**(-m), (_k, 1, n)))
356
+
357
+
358
+ def test_harmonic_calculus():
359
+ y = Symbol("y", positive=True)
360
+ z = Symbol("z", negative=True)
361
+ assert harmonic(x, 1).limit(x, 0) == 0
362
+ assert harmonic(x, y).limit(x, 0) == 0
363
+ assert harmonic(x, 1).series(x, y, 2) == \
364
+ harmonic(y) + (x - y)*zeta(2, y + 1) + O((x - y)**2, (x, y))
365
+ assert limit(harmonic(x, y), x, oo) == harmonic(oo, y)
366
+ assert limit(harmonic(x, y + 1), x, oo) == zeta(y + 1)
367
+ assert limit(harmonic(x, y - 1), x, oo) == harmonic(oo, y - 1)
368
+ assert limit(harmonic(x, z), x, oo) == Limit(harmonic(x, z), x, oo, dir='-')
369
+ assert limit(harmonic(x, z + 1), x, oo) == oo
370
+ assert limit(harmonic(x, z + 2), x, oo) == harmonic(oo, z + 2)
371
+ assert limit(harmonic(x, z - 1), x, oo) == Limit(harmonic(x, z - 1), x, oo, dir='-')
372
+
373
+
374
+ def test_euler():
375
+ assert euler(0) == 1
376
+ assert euler(1) == 0
377
+ assert euler(2) == -1
378
+ assert euler(3) == 0
379
+ assert euler(4) == 5
380
+ assert euler(6) == -61
381
+ assert euler(8) == 1385
382
+
383
+ assert euler(20, evaluate=False) != 370371188237525
384
+
385
+ n = Symbol('n', integer=True)
386
+ assert euler(n) != -1
387
+ assert euler(n).subs(n, 2) == -1
388
+
389
+ assert euler(-1) == S.Pi / 2
390
+ assert euler(-1, 1) == 2*log(2)
391
+ assert euler(-2).evalf() == (2*S.Catalan).evalf()
392
+ assert euler(-3).evalf() == (S.Pi**3 / 16).evalf()
393
+ assert str(euler(2.3).evalf(n=10)) == '-1.052850274'
394
+ assert str(euler(1.2, 3.4).evalf(n=10)) == '3.575613489'
395
+ assert str(euler(I).evalf(n=10)) == '1.248446443 - 0.7675445124*I'
396
+ assert str(euler(I, I).evalf(n=10)) == '0.04812930469 + 0.01052411008*I'
397
+
398
+ assert euler(20).evalf() == 370371188237525.0
399
+ assert euler(20, evaluate=False).evalf() == 370371188237525.0
400
+
401
+ assert euler(n).rewrite(Sum) == euler(n)
402
+ n = Symbol('n', integer=True, nonnegative=True)
403
+ assert euler(2*n + 1).rewrite(Sum) == 0
404
+ _j = Dummy('j')
405
+ _k = Dummy('k')
406
+ assert euler(2*n).rewrite(Sum).dummy_eq(
407
+ I*Sum((-1)**_j*2**(-_k)*I**(-_k)*(-2*_j + _k)**(2*n + 1)*
408
+ binomial(_k, _j)/_k, (_j, 0, _k), (_k, 1, 2*n + 1)))
409
+
410
+
411
+ def test_euler_odd():
412
+ n = Symbol('n', odd=True, positive=True)
413
+ assert euler(n) == 0
414
+ n = Symbol('n', odd=True)
415
+ assert euler(n) != 0
416
+
417
+
418
+ def test_euler_polynomials():
419
+ assert euler(0, x) == 1
420
+ assert euler(1, x) == x - S.Half
421
+ assert euler(2, x) == x**2 - x
422
+ assert euler(3, x) == x**3 - (3*x**2)/2 + Rational(1, 4)
423
+ m = Symbol('m')
424
+ assert isinstance(euler(m, x), euler)
425
+ from sympy.core.numbers import Float
426
+ A = Float('-0.46237208575048694923364757452876131e8') # from Maple
427
+ B = euler(19, S.Pi).evalf(32)
428
+ assert abs((A - B)/A) < 1e-31
429
+ z = Float(0.1) + Float(0.2)*I
430
+ expected = Float(-3126.54721663773 ) + Float(565.736261497056) * I
431
+ assert abs(euler(13, z) - expected) < 1e-10
432
+
433
+
434
+ def test_euler_polynomial_rewrite():
435
+ m = Symbol('m')
436
+ A = euler(m, x).rewrite('Sum')
437
+ assert A.subs({m:3, x:5}).doit() == euler(3, 5)
438
+
439
+
440
+ def test_catalan():
441
+ n = Symbol('n', integer=True)
442
+ m = Symbol('m', integer=True, positive=True)
443
+ k = Symbol('k', integer=True, nonnegative=True)
444
+ p = Symbol('p', nonnegative=True)
445
+
446
+ catalans = [1, 1, 2, 5, 14, 42, 132, 429, 1430, 4862, 16796, 58786]
447
+ for i, c in enumerate(catalans):
448
+ assert catalan(i) == c
449
+ assert catalan(n).rewrite(factorial).subs(n, i) == c
450
+ assert catalan(n).rewrite(Product).subs(n, i).doit() == c
451
+
452
+ assert unchanged(catalan, x)
453
+ assert catalan(2*x).rewrite(binomial) == binomial(4*x, 2*x)/(2*x + 1)
454
+ assert catalan(S.Half).rewrite(gamma) == 8/(3*pi)
455
+ assert catalan(S.Half).rewrite(factorial).rewrite(gamma) ==\
456
+ 8 / (3 * pi)
457
+ assert catalan(3*x).rewrite(gamma) == 4**(
458
+ 3*x)*gamma(3*x + S.Half)/(sqrt(pi)*gamma(3*x + 2))
459
+ assert catalan(x).rewrite(hyper) == hyper((-x + 1, -x), (2,), 1)
460
+
461
+ assert catalan(n).rewrite(factorial) == factorial(2*n) / (factorial(n + 1)
462
+ * factorial(n))
463
+ assert isinstance(catalan(n).rewrite(Product), catalan)
464
+ assert isinstance(catalan(m).rewrite(Product), Product)
465
+
466
+ assert diff(catalan(x), x) == (polygamma(
467
+ 0, x + S.Half) - polygamma(0, x + 2) + log(4))*catalan(x)
468
+
469
+ assert catalan(x).evalf() == catalan(x)
470
+ c = catalan(S.Half).evalf()
471
+ assert str(c) == '0.848826363156775'
472
+ c = catalan(I).evalf(3)
473
+ assert str((re(c), im(c))) == '(0.398, -0.0209)'
474
+
475
+ # Assumptions
476
+ assert catalan(p).is_positive is True
477
+ assert catalan(k).is_integer is True
478
+ assert catalan(m+3).is_composite is True
479
+
480
+
481
+ def test_genocchi():
482
+ genocchis = [0, -1, -1, 0, 1, 0, -3, 0, 17]
483
+ for n, g in enumerate(genocchis):
484
+ assert genocchi(n) == g
485
+
486
+ m = Symbol('m', integer=True)
487
+ n = Symbol('n', integer=True, positive=True)
488
+ assert unchanged(genocchi, m)
489
+ assert genocchi(2*n + 1) == 0
490
+ gn = 2 * (1 - 2**n) * bernoulli(n)
491
+ assert genocchi(n).rewrite(bernoulli).factor() == gn.factor()
492
+ gnx = 2 * (bernoulli(n, x) - 2**n * bernoulli(n, (x+1) / 2))
493
+ assert genocchi(n, x).rewrite(bernoulli).factor() == gnx.factor()
494
+ assert genocchi(2 * n).is_odd
495
+ assert genocchi(2 * n).is_even is False
496
+ assert genocchi(2 * n + 1).is_even
497
+ assert genocchi(n).is_integer
498
+ assert genocchi(4 * n).is_positive
499
+ # these are the only 2 prime Genocchi numbers
500
+ assert genocchi(6, evaluate=False).is_prime == S(-3).is_prime
501
+ assert genocchi(8, evaluate=False).is_prime
502
+ assert genocchi(4 * n + 2).is_negative
503
+ assert genocchi(4 * n + 1).is_negative is False
504
+ assert genocchi(4 * n - 2).is_negative
505
+
506
+ g0 = genocchi(0, evaluate=False)
507
+ assert g0.is_positive is False
508
+ assert g0.is_negative is False
509
+ assert g0.is_even is True
510
+ assert g0.is_odd is False
511
+
512
+ assert genocchi(0, x) == 0
513
+ assert genocchi(1, x) == -1
514
+ assert genocchi(2, x) == 1 - 2*x
515
+ assert genocchi(3, x) == 3*x - 3*x**2
516
+ assert genocchi(4, x) == -1 + 6*x**2 - 4*x**3
517
+ y = Symbol("y")
518
+ assert genocchi(5, (x+y)**100) == -5*(x+y)**400 + 10*(x+y)**300 - 5*(x+y)**100
519
+
520
+ assert str(genocchi(5.0, 4.0).evalf(n=10)) == '-660.0000000'
521
+ assert str(genocchi(Rational(5, 4)).evalf(n=10)) == '-1.104286457'
522
+ assert str(genocchi(-2).evalf(n=10)) == '3.606170709'
523
+ assert str(genocchi(1.3, 3.7).evalf(n=10)) == '-1.847375373'
524
+ assert str(genocchi(I, 1.0).evalf(n=10)) == '-0.3161917278 - 1.45311955*I'
525
+
526
+ n = Symbol('n')
527
+ assert genocchi(n, x).rewrite(dirichlet_eta) == -2*n * dirichlet_eta(1-n, x)
528
+
529
+
530
+ def test_andre():
531
+ nums = [1, 1, 1, 2, 5, 16, 61, 272, 1385, 7936, 50521]
532
+ for n, a in enumerate(nums):
533
+ assert andre(n) == a
534
+ assert andre(S.Infinity) == S.Infinity
535
+ assert andre(-1) == -log(2)
536
+ assert andre(-2) == -2*S.Catalan
537
+ assert andre(-3) == 3*zeta(3)/16
538
+ assert andre(-5) == -15*zeta(5)/256
539
+ # In fact andre(-2*n) is related to the Dirichlet *beta* function
540
+ # at 2*n, but SymPy doesn't implement that (or general L-functions)
541
+ assert unchanged(andre, -4)
542
+
543
+ n = Symbol('n', integer=True, nonnegative=True)
544
+ assert unchanged(andre, n)
545
+ assert andre(n).is_integer is True
546
+ assert andre(n).is_positive is True
547
+
548
+ assert str(andre(10, evaluate=False).evalf(n=10)) == '50521.00000'
549
+ assert str(andre(-1, evaluate=False).evalf(n=10)) == '-0.6931471806'
550
+ assert str(andre(-2, evaluate=False).evalf(n=10)) == '-1.831931188'
551
+ assert str(andre(-4, evaluate=False).evalf(n=10)) == '1.977889103'
552
+ assert str(andre(I, evaluate=False).evalf(n=10)) == '2.378417833 + 0.6343322845*I'
553
+
554
+ assert andre(x).rewrite(polylog) == \
555
+ (-I)**(x+1) * polylog(-x, I) + I**(x+1) * polylog(-x, -I)
556
+ assert andre(x).rewrite(zeta) == \
557
+ 2 * gamma(x+1) / (2*pi)**(x+1) * \
558
+ (zeta(x+1, Rational(1,4)) - cos(pi*x) * zeta(x+1, Rational(3,4)))
559
+
560
+
561
+ @nocache_fail
562
+ def test_partition():
563
+ partition_nums = [1, 1, 2, 3, 5, 7, 11, 15, 22]
564
+ for n, p in enumerate(partition_nums):
565
+ assert partition(n) == p
566
+
567
+ x = Symbol('x')
568
+ y = Symbol('y', real=True)
569
+ m = Symbol('m', integer=True)
570
+ n = Symbol('n', integer=True, negative=True)
571
+ p = Symbol('p', integer=True, nonnegative=True)
572
+ assert partition(m).is_integer
573
+ assert not partition(m).is_negative
574
+ assert partition(m).is_nonnegative
575
+ assert partition(n).is_zero
576
+ assert partition(p).is_positive
577
+ assert partition(x).subs(x, 7) == 15
578
+ assert partition(y).subs(y, 8) == 22
579
+ raises(TypeError, lambda: partition(Rational(5, 4)))
580
+ assert partition(9, evaluate=False) % 5 == 0
581
+ assert partition(5*m + 4) % 5 == 0
582
+ assert partition(47, evaluate=False) % 7 == 0
583
+ assert partition(7*m + 5) % 7 == 0
584
+ assert partition(50, evaluate=False) % 11 == 0
585
+ assert partition(11*m + 6) % 11 == 0
586
+
587
+
588
+ def test_divisor_sigma():
589
+ # error
590
+ m = Symbol('m', integer=False)
591
+ raises(TypeError, lambda: divisor_sigma(m))
592
+ raises(TypeError, lambda: divisor_sigma(4.5))
593
+ raises(TypeError, lambda: divisor_sigma(1, m))
594
+ raises(TypeError, lambda: divisor_sigma(1, 4.5))
595
+ m = Symbol('m', positive=False)
596
+ raises(ValueError, lambda: divisor_sigma(m))
597
+ raises(ValueError, lambda: divisor_sigma(0))
598
+ m = Symbol('m', negative=True)
599
+ raises(ValueError, lambda: divisor_sigma(1, m))
600
+ raises(ValueError, lambda: divisor_sigma(1, -1))
601
+
602
+ # special case
603
+ p = Symbol('p', prime=True)
604
+ k = Symbol('k', integer=True)
605
+ assert divisor_sigma(p, 1) == p + 1
606
+ assert divisor_sigma(p, k) == p**k + 1
607
+
608
+ # property
609
+ n = Symbol('n', integer=True, positive=True)
610
+ assert divisor_sigma(n).is_integer is True
611
+ assert divisor_sigma(n).is_positive is True
612
+
613
+ # symbolic
614
+ k = Symbol('k', integer=True, zero=False)
615
+ assert divisor_sigma(4, k) == 2**(2*k) + 2**k + 1
616
+ assert divisor_sigma(6, k) == (2**k + 1) * (3**k + 1)
617
+
618
+ # Integer
619
+ assert divisor_sigma(23450) == 50592
620
+ assert divisor_sigma(23450, 0) == 24
621
+ assert divisor_sigma(23450, 1) == 50592
622
+ assert divisor_sigma(23450, 2) == 730747500
623
+ assert divisor_sigma(23450, 3) == 14666785333344
624
+
625
+
626
+ def test_udivisor_sigma():
627
+ # error
628
+ m = Symbol('m', integer=False)
629
+ raises(TypeError, lambda: udivisor_sigma(m))
630
+ raises(TypeError, lambda: udivisor_sigma(4.5))
631
+ raises(TypeError, lambda: udivisor_sigma(1, m))
632
+ raises(TypeError, lambda: udivisor_sigma(1, 4.5))
633
+ m = Symbol('m', positive=False)
634
+ raises(ValueError, lambda: udivisor_sigma(m))
635
+ raises(ValueError, lambda: udivisor_sigma(0))
636
+ m = Symbol('m', negative=True)
637
+ raises(ValueError, lambda: udivisor_sigma(1, m))
638
+ raises(ValueError, lambda: udivisor_sigma(1, -1))
639
+
640
+ # special case
641
+ p = Symbol('p', prime=True)
642
+ k = Symbol('k', integer=True)
643
+ assert udivisor_sigma(p, 1) == p + 1
644
+ assert udivisor_sigma(p, k) == p**k + 1
645
+
646
+ # property
647
+ n = Symbol('n', integer=True, positive=True)
648
+ assert udivisor_sigma(n).is_integer is True
649
+ assert udivisor_sigma(n).is_positive is True
650
+
651
+ # Integer
652
+ A034444 = [1, 2, 2, 2, 2, 4, 2, 2, 2, 4, 2, 4, 2, 4, 4, 2, 2, 4, 2, 4,
653
+ 4, 4, 2, 4, 2, 4, 2, 4, 2, 8, 2, 2, 4, 4, 4, 4, 2, 4, 4, 4,
654
+ 2, 8, 2, 4, 4, 4, 2, 4, 2, 4, 4, 4, 2, 4, 4, 4, 4, 4, 2, 8]
655
+ for n, val in enumerate(A034444, 1):
656
+ assert udivisor_sigma(n, 0) == val
657
+ A034448 = [1, 3, 4, 5, 6, 12, 8, 9, 10, 18, 12, 20, 14, 24, 24, 17, 18,
658
+ 30, 20, 30, 32, 36, 24, 36, 26, 42, 28, 40, 30, 72, 32, 33,
659
+ 48, 54, 48, 50, 38, 60, 56, 54, 42, 96, 44, 60, 60, 72, 48]
660
+ for n, val in enumerate(A034448, 1):
661
+ assert udivisor_sigma(n, 1) == val
662
+ A034676 = [1, 5, 10, 17, 26, 50, 50, 65, 82, 130, 122, 170, 170, 250,
663
+ 260, 257, 290, 410, 362, 442, 500, 610, 530, 650, 626, 850,
664
+ 730, 850, 842, 1300, 962, 1025, 1220, 1450, 1300, 1394, 1370]
665
+ for n, val in enumerate(A034676, 1):
666
+ assert udivisor_sigma(n, 2) == val
667
+
668
+
669
+ def test_legendre_symbol():
670
+ # error
671
+ m = Symbol('m', integer=False)
672
+ raises(TypeError, lambda: legendre_symbol(m, 3))
673
+ raises(TypeError, lambda: legendre_symbol(4.5, 3))
674
+ raises(TypeError, lambda: legendre_symbol(1, m))
675
+ raises(TypeError, lambda: legendre_symbol(1, 4.5))
676
+ m = Symbol('m', prime=False)
677
+ raises(ValueError, lambda: legendre_symbol(1, m))
678
+ raises(ValueError, lambda: legendre_symbol(1, 6))
679
+ m = Symbol('m', odd=False)
680
+ raises(ValueError, lambda: legendre_symbol(1, m))
681
+ raises(ValueError, lambda: legendre_symbol(1, 2))
682
+
683
+ # special case
684
+ p = Symbol('p', prime=True)
685
+ k = Symbol('k', integer=True)
686
+ assert legendre_symbol(p*k, p) == 0
687
+ assert legendre_symbol(1, p) == 1
688
+
689
+ # property
690
+ n = Symbol('n')
691
+ m = Symbol('m')
692
+ assert legendre_symbol(m, n).is_integer is True
693
+ assert legendre_symbol(m, n).is_prime is False
694
+
695
+ # Integer
696
+ assert legendre_symbol(5, 11) == 1
697
+ assert legendre_symbol(25, 41) == 1
698
+ assert legendre_symbol(67, 101) == -1
699
+ assert legendre_symbol(0, 13) == 0
700
+ assert legendre_symbol(9, 3) == 0
701
+
702
+
703
+ def test_jacobi_symbol():
704
+ # error
705
+ m = Symbol('m', integer=False)
706
+ raises(TypeError, lambda: jacobi_symbol(m, 3))
707
+ raises(TypeError, lambda: jacobi_symbol(4.5, 3))
708
+ raises(TypeError, lambda: jacobi_symbol(1, m))
709
+ raises(TypeError, lambda: jacobi_symbol(1, 4.5))
710
+ m = Symbol('m', positive=False)
711
+ raises(ValueError, lambda: jacobi_symbol(1, m))
712
+ raises(ValueError, lambda: jacobi_symbol(1, -6))
713
+ m = Symbol('m', odd=False)
714
+ raises(ValueError, lambda: jacobi_symbol(1, m))
715
+ raises(ValueError, lambda: jacobi_symbol(1, 2))
716
+
717
+ # special case
718
+ p = Symbol('p', integer=True)
719
+ k = Symbol('k', integer=True)
720
+ assert jacobi_symbol(p*k, p) == 0
721
+ assert jacobi_symbol(1, p) == 1
722
+ assert jacobi_symbol(1, 1) == 1
723
+ assert jacobi_symbol(0, 1) == 1
724
+
725
+ # property
726
+ n = Symbol('n')
727
+ m = Symbol('m')
728
+ assert jacobi_symbol(m, n).is_integer is True
729
+ assert jacobi_symbol(m, n).is_prime is False
730
+
731
+ # Integer
732
+ assert jacobi_symbol(25, 41) == 1
733
+ assert jacobi_symbol(-23, 83) == -1
734
+ assert jacobi_symbol(3, 9) == 0
735
+ assert jacobi_symbol(42, 97) == -1
736
+ assert jacobi_symbol(3, 5) == -1
737
+ assert jacobi_symbol(7, 9) == 1
738
+ assert jacobi_symbol(0, 3) == 0
739
+ assert jacobi_symbol(0, 1) == 1
740
+ assert jacobi_symbol(2, 1) == 1
741
+ assert jacobi_symbol(1, 3) == 1
742
+
743
+
744
+ def test_kronecker_symbol():
745
+ # error
746
+ m = Symbol('m', integer=False)
747
+ raises(TypeError, lambda: kronecker_symbol(m, 3))
748
+ raises(TypeError, lambda: kronecker_symbol(4.5, 3))
749
+ raises(TypeError, lambda: kronecker_symbol(1, m))
750
+ raises(TypeError, lambda: kronecker_symbol(1, 4.5))
751
+
752
+ # special case
753
+ p = Symbol('p', integer=True)
754
+ assert kronecker_symbol(1, p) == 1
755
+ assert kronecker_symbol(1, 1) == 1
756
+ assert kronecker_symbol(0, 1) == 1
757
+
758
+ # property
759
+ n = Symbol('n')
760
+ m = Symbol('m')
761
+ assert kronecker_symbol(m, n).is_integer is True
762
+ assert kronecker_symbol(m, n).is_prime is False
763
+
764
+ # Integer
765
+ for n in range(3, 10, 2):
766
+ for a in range(-n, n):
767
+ val = kronecker_symbol(a, n)
768
+ assert val == jacobi_symbol(a, n)
769
+ minus = kronecker_symbol(a, -n)
770
+ if a < 0:
771
+ assert -minus == val
772
+ else:
773
+ assert minus == val
774
+ even = kronecker_symbol(a, 2 * n)
775
+ if a % 2 == 0:
776
+ assert even == 0
777
+ elif a % 8 in [1, 7]:
778
+ assert even == val
779
+ else:
780
+ assert -even == val
781
+ assert kronecker_symbol(1, 0) == kronecker_symbol(-1, 0) == 1
782
+ assert kronecker_symbol(0, 0) == 0
783
+
784
+
785
+ def test_mobius():
786
+ # error
787
+ m = Symbol('m', integer=False)
788
+ raises(TypeError, lambda: mobius(m))
789
+ raises(TypeError, lambda: mobius(4.5))
790
+ m = Symbol('m', positive=False)
791
+ raises(ValueError, lambda: mobius(m))
792
+ raises(ValueError, lambda: mobius(-3))
793
+
794
+ # special case
795
+ p = Symbol('p', prime=True)
796
+ assert mobius(p) == -1
797
+
798
+ # property
799
+ n = Symbol('n', integer=True, positive=True)
800
+ assert mobius(n).is_integer is True
801
+ assert mobius(n).is_prime is False
802
+
803
+ # symbolic
804
+ n = Symbol('n', integer=True, positive=True)
805
+ k = Symbol('k', integer=True, positive=True)
806
+ assert mobius(n**2) == 0
807
+ assert mobius(4*n) == 0
808
+ assert isinstance(mobius(n**k), mobius)
809
+ assert mobius(n**(k+1)) == 0
810
+ assert isinstance(mobius(3**k), mobius)
811
+ assert mobius(3**(k+1)) == 0
812
+ m = Symbol('m')
813
+ assert isinstance(mobius(4*m), mobius)
814
+
815
+ # Integer
816
+ assert mobius(13*7) == 1
817
+ assert mobius(1) == 1
818
+ assert mobius(13*7*5) == -1
819
+ assert mobius(13**2) == 0
820
+ A008683 = [1, -1, -1, 0, -1, 1, -1, 0, 0, 1, -1, 0, -1, 1, 1, 0, -1, 0,
821
+ -1, 0, 1, 1, -1, 0, 0, 1, 0, 0, -1, -1, -1, 0, 1, 1, 1, 0, -1,
822
+ 1, 1, 0, -1, -1, -1, 0, 0, 1, -1, 0, 0, 0, 1, 0, -1, 0, 1, 0]
823
+ for n, val in enumerate(A008683, 1):
824
+ assert mobius(n) == val
825
+
826
+
827
+ def test_primenu():
828
+ # error
829
+ m = Symbol('m', integer=False)
830
+ raises(TypeError, lambda: primenu(m))
831
+ raises(TypeError, lambda: primenu(4.5))
832
+ m = Symbol('m', positive=False)
833
+ raises(ValueError, lambda: primenu(m))
834
+ raises(ValueError, lambda: primenu(0))
835
+
836
+ # special case
837
+ p = Symbol('p', prime=True)
838
+ assert primenu(p) == 1
839
+
840
+ # property
841
+ n = Symbol('n', integer=True, positive=True)
842
+ assert primenu(n).is_integer is True
843
+ assert primenu(n).is_nonnegative is True
844
+
845
+ # Integer
846
+ assert primenu(7*13) == 2
847
+ assert primenu(2*17*19) == 3
848
+ assert primenu(2**3 * 17 * 19**2) == 3
849
+ A001221 = [0, 1, 1, 1, 1, 2, 1, 1, 1, 2, 1, 2, 1, 2, 2, 1, 1, 2,
850
+ 1, 2, 2, 2, 1, 2, 1, 2, 1, 2, 1, 3, 1, 1, 2, 2, 2, 2]
851
+ for n, val in enumerate(A001221, 1):
852
+ assert primenu(n) == val
853
+
854
+
855
+ def test_primeomega():
856
+ # error
857
+ m = Symbol('m', integer=False)
858
+ raises(TypeError, lambda: primeomega(m))
859
+ raises(TypeError, lambda: primeomega(4.5))
860
+ m = Symbol('m', positive=False)
861
+ raises(ValueError, lambda: primeomega(m))
862
+ raises(ValueError, lambda: primeomega(0))
863
+
864
+ # special case
865
+ p = Symbol('p', prime=True)
866
+ assert primeomega(p) == 1
867
+
868
+ # property
869
+ n = Symbol('n', integer=True, positive=True)
870
+ assert primeomega(n).is_integer is True
871
+ assert primeomega(n).is_nonnegative is True
872
+
873
+ # Integer
874
+ assert primeomega(7*13) == 2
875
+ assert primeomega(2*17*19) == 3
876
+ assert primeomega(2**3 * 17 * 19**2) == 6
877
+ A001222 = [0, 1, 1, 2, 1, 2, 1, 3, 2, 2, 1, 3, 1, 2, 2, 4, 1, 3,
878
+ 1, 3, 2, 2, 1, 4, 2, 2, 3, 3, 1, 3, 1, 5, 2, 2, 2, 4]
879
+ for n, val in enumerate(A001222, 1):
880
+ assert primeomega(n) == val
881
+
882
+
883
+ def test_totient():
884
+ # error
885
+ m = Symbol('m', integer=False)
886
+ raises(TypeError, lambda: totient(m))
887
+ raises(TypeError, lambda: totient(4.5))
888
+ m = Symbol('m', positive=False)
889
+ raises(ValueError, lambda: totient(m))
890
+ raises(ValueError, lambda: totient(0))
891
+
892
+ # special case
893
+ p = Symbol('p', prime=True)
894
+ assert totient(p) == p - 1
895
+
896
+ # property
897
+ n = Symbol('n', integer=True, positive=True)
898
+ assert totient(n).is_integer is True
899
+ assert totient(n).is_positive is True
900
+
901
+ # Integer
902
+ assert totient(7*13) == totient(factorint(7*13)) == (7-1)*(13-1)
903
+ assert totient(2*17*19) == totient(factorint(2*17*19)) == (17-1)*(19-1)
904
+ assert totient(2**3 * 17 * 19**2) == totient({2: 3, 17: 1, 19: 2}) == 2**2 * (17-1) * 19*(19-1)
905
+ A000010 = [1, 1, 2, 2, 4, 2, 6, 4, 6, 4, 10, 4, 12, 6, 8, 8, 16,
906
+ 6, 18, 8, 12, 10, 22, 8, 20, 12, 18, 12, 28, 8, 30, 16,
907
+ 20, 16, 24, 12, 36, 18, 24, 16, 40, 12, 42, 20, 24, 22]
908
+ for n, val in enumerate(A000010, 1):
909
+ assert totient(n) == val
910
+
911
+
912
+ def test_reduced_totient():
913
+ # error
914
+ m = Symbol('m', integer=False)
915
+ raises(TypeError, lambda: reduced_totient(m))
916
+ raises(TypeError, lambda: reduced_totient(4.5))
917
+ m = Symbol('m', positive=False)
918
+ raises(ValueError, lambda: reduced_totient(m))
919
+ raises(ValueError, lambda: reduced_totient(0))
920
+
921
+ # special case
922
+ p = Symbol('p', prime=True)
923
+ assert reduced_totient(p) == p - 1
924
+
925
+ # property
926
+ n = Symbol('n', integer=True, positive=True)
927
+ assert reduced_totient(n).is_integer is True
928
+ assert reduced_totient(n).is_positive is True
929
+
930
+ # Integer
931
+ assert reduced_totient(7*13) == reduced_totient(factorint(7*13)) == 12
932
+ assert reduced_totient(2*17*19) == reduced_totient(factorint(2*17*19)) == 144
933
+ assert reduced_totient(2**2 * 11) == reduced_totient({2: 2, 11: 1}) == 10
934
+ assert reduced_totient(2**3 * 17 * 19**2) == reduced_totient({2: 3, 17: 1, 19: 2}) == 2736
935
+ A002322 = [1, 1, 2, 2, 4, 2, 6, 2, 6, 4, 10, 2, 12, 6, 4, 4, 16, 6,
936
+ 18, 4, 6, 10, 22, 2, 20, 12, 18, 6, 28, 4, 30, 8, 10, 16,
937
+ 12, 6, 36, 18, 12, 4, 40, 6, 42, 10, 12, 22, 46, 4, 42]
938
+ for n, val in enumerate(A002322, 1):
939
+ assert reduced_totient(n) == val
940
+
941
+
942
+ def test_primepi():
943
+ # error
944
+ z = Symbol('z', real=False)
945
+ raises(TypeError, lambda: primepi(z))
946
+ raises(TypeError, lambda: primepi(I))
947
+
948
+ # property
949
+ n = Symbol('n', integer=True, positive=True)
950
+ assert primepi(n).is_integer is True
951
+ assert primepi(n).is_nonnegative is True
952
+
953
+ # infinity
954
+ assert primepi(oo) == oo
955
+ assert primepi(-oo) == 0
956
+
957
+ # symbol
958
+ x = Symbol('x')
959
+ assert isinstance(primepi(x), primepi)
960
+
961
+ # Integer
962
+ assert primepi(0) == 0
963
+ A000720 = [0, 1, 2, 2, 3, 3, 4, 4, 4, 4, 5, 5, 6, 6, 6, 6, 7, 7, 8,
964
+ 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 11, 11, 11, 11, 11, 11,
965
+ 12, 12, 12, 12, 13, 13, 14, 14, 14, 14, 15, 15, 15, 15]
966
+ for n, val in enumerate(A000720, 1):
967
+ assert primepi(n) == primepi(n + 0.5) == val
968
+
969
+
970
+ def test__nT():
971
+ assert [_nT(i, j) for i in range(5) for j in range(i + 2)] == [
972
+ 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 2, 1, 1, 0]
973
+ check = [_nT(10, i) for i in range(11)]
974
+ assert check == [0, 1, 5, 8, 9, 7, 5, 3, 2, 1, 1]
975
+ assert all(type(i) is int for i in check)
976
+ assert _nT(10, 5) == 7
977
+ assert _nT(100, 98) == 2
978
+ assert _nT(100, 100) == 1
979
+ assert _nT(10, 3) == 8
980
+
981
+
982
+ def test_nC_nP_nT():
983
+ from sympy.utilities.iterables import (
984
+ multiset_permutations, multiset_combinations, multiset_partitions,
985
+ partitions, subsets, permutations)
986
+ from sympy.functions.combinatorial.numbers import (
987
+ nP, nC, nT, stirling, _stirling1, _stirling2, _multiset_histogram, _AOP_product)
988
+
989
+ from sympy.combinatorics.permutations import Permutation
990
+ from sympy.core.random import choice
991
+
992
+ c = string.ascii_lowercase
993
+ for i in range(100):
994
+ s = ''.join(choice(c) for i in range(7))
995
+ u = len(s) == len(set(s))
996
+ try:
997
+ tot = 0
998
+ for i in range(8):
999
+ check = nP(s, i)
1000
+ tot += check
1001
+ assert len(list(multiset_permutations(s, i))) == check
1002
+ if u:
1003
+ assert nP(len(s), i) == check
1004
+ assert nP(s) == tot
1005
+ except AssertionError:
1006
+ print(s, i, 'failed perm test')
1007
+ raise ValueError()
1008
+
1009
+ for i in range(100):
1010
+ s = ''.join(choice(c) for i in range(7))
1011
+ u = len(s) == len(set(s))
1012
+ try:
1013
+ tot = 0
1014
+ for i in range(8):
1015
+ check = nC(s, i)
1016
+ tot += check
1017
+ assert len(list(multiset_combinations(s, i))) == check
1018
+ if u:
1019
+ assert nC(len(s), i) == check
1020
+ assert nC(s) == tot
1021
+ if u:
1022
+ assert nC(len(s)) == tot
1023
+ except AssertionError:
1024
+ print(s, i, 'failed combo test')
1025
+ raise ValueError()
1026
+
1027
+ for i in range(1, 10):
1028
+ tot = 0
1029
+ for j in range(1, i + 2):
1030
+ check = nT(i, j)
1031
+ assert check.is_Integer
1032
+ tot += check
1033
+ assert sum(1 for p in partitions(i, j, size=True) if p[0] == j) == check
1034
+ assert nT(i) == tot
1035
+
1036
+ for i in range(1, 10):
1037
+ tot = 0
1038
+ for j in range(1, i + 2):
1039
+ check = nT(range(i), j)
1040
+ tot += check
1041
+ assert len(list(multiset_partitions(list(range(i)), j))) == check
1042
+ assert nT(range(i)) == tot
1043
+
1044
+ for i in range(100):
1045
+ s = ''.join(choice(c) for i in range(7))
1046
+ u = len(s) == len(set(s))
1047
+ try:
1048
+ tot = 0
1049
+ for i in range(1, 8):
1050
+ check = nT(s, i)
1051
+ tot += check
1052
+ assert len(list(multiset_partitions(s, i))) == check
1053
+ if u:
1054
+ assert nT(range(len(s)), i) == check
1055
+ if u:
1056
+ assert nT(range(len(s))) == tot
1057
+ assert nT(s) == tot
1058
+ except AssertionError:
1059
+ print(s, i, 'failed partition test')
1060
+ raise ValueError()
1061
+
1062
+ # tests for Stirling numbers of the first kind that are not tested in the
1063
+ # above
1064
+ assert [stirling(9, i, kind=1) for i in range(11)] == [
1065
+ 0, 40320, 109584, 118124, 67284, 22449, 4536, 546, 36, 1, 0]
1066
+ perms = list(permutations(range(4)))
1067
+ assert [sum(1 for p in perms if Permutation(p).cycles == i)
1068
+ for i in range(5)] == [0, 6, 11, 6, 1] == [
1069
+ stirling(4, i, kind=1) for i in range(5)]
1070
+ # http://oeis.org/A008275
1071
+ assert [stirling(n, k, signed=1)
1072
+ for n in range(10) for k in range(1, n + 1)] == [
1073
+ 1, -1,
1074
+ 1, 2, -3,
1075
+ 1, -6, 11, -6,
1076
+ 1, 24, -50, 35, -10,
1077
+ 1, -120, 274, -225, 85, -15,
1078
+ 1, 720, -1764, 1624, -735, 175, -21,
1079
+ 1, -5040, 13068, -13132, 6769, -1960, 322, -28,
1080
+ 1, 40320, -109584, 118124, -67284, 22449, -4536, 546, -36, 1]
1081
+ # https://en.wikipedia.org/wiki/Stirling_numbers_of_the_first_kind
1082
+ assert [stirling(n, k, kind=1)
1083
+ for n in range(10) for k in range(n+1)] == [
1084
+ 1,
1085
+ 0, 1,
1086
+ 0, 1, 1,
1087
+ 0, 2, 3, 1,
1088
+ 0, 6, 11, 6, 1,
1089
+ 0, 24, 50, 35, 10, 1,
1090
+ 0, 120, 274, 225, 85, 15, 1,
1091
+ 0, 720, 1764, 1624, 735, 175, 21, 1,
1092
+ 0, 5040, 13068, 13132, 6769, 1960, 322, 28, 1,
1093
+ 0, 40320, 109584, 118124, 67284, 22449, 4536, 546, 36, 1]
1094
+ # https://en.wikipedia.org/wiki/Stirling_numbers_of_the_second_kind
1095
+ assert [stirling(n, k, kind=2)
1096
+ for n in range(10) for k in range(n+1)] == [
1097
+ 1,
1098
+ 0, 1,
1099
+ 0, 1, 1,
1100
+ 0, 1, 3, 1,
1101
+ 0, 1, 7, 6, 1,
1102
+ 0, 1, 15, 25, 10, 1,
1103
+ 0, 1, 31, 90, 65, 15, 1,
1104
+ 0, 1, 63, 301, 350, 140, 21, 1,
1105
+ 0, 1, 127, 966, 1701, 1050, 266, 28, 1,
1106
+ 0, 1, 255, 3025, 7770, 6951, 2646, 462, 36, 1]
1107
+ assert stirling(3, 4, kind=1) == stirling(3, 4, kind=1) == 0
1108
+ raises(ValueError, lambda: stirling(-2, 2))
1109
+
1110
+ # Assertion that the return type is SymPy Integer.
1111
+ assert isinstance(_stirling1(6, 3), Integer)
1112
+ assert isinstance(_stirling2(6, 3), Integer)
1113
+
1114
+ def delta(p):
1115
+ if len(p) == 1:
1116
+ return oo
1117
+ return min(abs(i[0] - i[1]) for i in subsets(p, 2))
1118
+ parts = multiset_partitions(range(5), 3)
1119
+ d = 2
1120
+ assert (sum(1 for p in parts if all(delta(i) >= d for i in p)) ==
1121
+ stirling(5, 3, d=d) == 7)
1122
+
1123
+ # other coverage tests
1124
+ assert nC('abb', 2) == nC('aab', 2) == 2
1125
+ assert nP(3, 3, replacement=True) == nP('aabc', 3, replacement=True) == 27
1126
+ assert nP(3, 4) == 0
1127
+ assert nP('aabc', 5) == 0
1128
+ assert nC(4, 2, replacement=True) == nC('abcdd', 2, replacement=True) == \
1129
+ len(list(multiset_combinations('aabbccdd', 2))) == 10
1130
+ assert nC('abcdd') == sum(nC('abcdd', i) for i in range(6)) == 24
1131
+ assert nC(list('abcdd'), 4) == 4
1132
+ assert nT('aaaa') == nT(4) == len(list(partitions(4))) == 5
1133
+ assert nT('aaab') == len(list(multiset_partitions('aaab'))) == 7
1134
+ assert nC('aabb'*3, 3) == 4 # aaa, bbb, abb, baa
1135
+ assert dict(_AOP_product((4,1,1,1))) == {
1136
+ 0: 1, 1: 4, 2: 7, 3: 8, 4: 8, 5: 7, 6: 4, 7: 1}
1137
+ # the following was the first t that showed a problem in a previous form of
1138
+ # the function, so it's not as random as it may appear
1139
+ t = (3, 9, 4, 6, 6, 5, 5, 2, 10, 4)
1140
+ assert sum(_AOP_product(t)[i] for i in range(55)) == 58212000
1141
+ raises(ValueError, lambda: _multiset_histogram({1:'a'}))
1142
+
1143
+
1144
+ def test_PR_14617():
1145
+ from sympy.functions.combinatorial.numbers import nT
1146
+ for n in (0, []):
1147
+ for k in (-1, 0, 1):
1148
+ if k == 0:
1149
+ assert nT(n, k) == 1
1150
+ else:
1151
+ assert nT(n, k) == 0
1152
+
1153
+
1154
+ def test_issue_8496():
1155
+ n = Symbol("n")
1156
+ k = Symbol("k")
1157
+
1158
+ raises(TypeError, lambda: catalan(n, k))
1159
+
1160
+
1161
+ def test_issue_8601():
1162
+ n = Symbol('n', integer=True, negative=True)
1163
+
1164
+ assert catalan(n - 1) is S.Zero
1165
+ assert catalan(Rational(-1, 2)) is S.ComplexInfinity
1166
+ assert catalan(-S.One) == Rational(-1, 2)
1167
+ c1 = catalan(-5.6).evalf()
1168
+ assert str(c1) == '6.93334070531408e-5'
1169
+ c2 = catalan(-35.4).evalf()
1170
+ assert str(c2) == '-4.14189164517449e-24'
1171
+
1172
+
1173
+ def test_motzkin():
1174
+ assert motzkin.is_motzkin(4) == True
1175
+ assert motzkin.is_motzkin(9) == True
1176
+ assert motzkin.is_motzkin(10) == False
1177
+ assert motzkin.find_motzkin_numbers_in_range(10,200) == [21, 51, 127]
1178
+ assert motzkin.find_motzkin_numbers_in_range(10,400) == [21, 51, 127, 323]
1179
+ assert motzkin.find_motzkin_numbers_in_range(10,1600) == [21, 51, 127, 323, 835]
1180
+ assert motzkin.find_first_n_motzkins(5) == [1, 1, 2, 4, 9]
1181
+ assert motzkin.find_first_n_motzkins(7) == [1, 1, 2, 4, 9, 21, 51]
1182
+ assert motzkin.find_first_n_motzkins(10) == [1, 1, 2, 4, 9, 21, 51, 127, 323, 835]
1183
+ raises(ValueError, lambda: motzkin.eval(77.58))
1184
+ raises(ValueError, lambda: motzkin.eval(-8))
1185
+ raises(ValueError, lambda: motzkin.find_motzkin_numbers_in_range(-2,7))
1186
+ raises(ValueError, lambda: motzkin.find_motzkin_numbers_in_range(13,7))
1187
+ raises(ValueError, lambda: motzkin.find_first_n_motzkins(112.8))
1188
+
1189
+
1190
+ def test_nD_derangements():
1191
+ from sympy.utilities.iterables import (partitions, multiset,
1192
+ multiset_derangements, multiset_permutations)
1193
+ from sympy.functions.combinatorial.numbers import nD
1194
+
1195
+ got = []
1196
+ for i in partitions(8, k=4):
1197
+ s = []
1198
+ it = 0
1199
+ for k, v in i.items():
1200
+ for i in range(v):
1201
+ s.extend([it]*k)
1202
+ it += 1
1203
+ ms = multiset(s)
1204
+ c1 = sum(1 for i in multiset_permutations(s) if
1205
+ all(i != j for i, j in zip(i, s)))
1206
+ assert c1 == nD(ms) == nD(ms, 0) == nD(ms, 1)
1207
+ v = [tuple(i) for i in multiset_derangements(s)]
1208
+ c2 = len(v)
1209
+ assert c2 == len(set(v))
1210
+ assert c1 == c2
1211
+ got.append(c1)
1212
+ assert got == [1, 4, 6, 12, 24, 24, 61, 126, 315, 780, 297, 772,
1213
+ 2033, 5430, 14833]
1214
+
1215
+ assert nD('1112233456', brute=True) == nD('1112233456') == 16356
1216
+ assert nD('') == nD([]) == nD({}) == 0
1217
+ assert nD({1: 0}) == 0
1218
+ raises(ValueError, lambda: nD({1: -1}))
1219
+ assert nD('112') == 0
1220
+ assert nD(i='112') == 0
1221
+ assert [nD(n=i) for i in range(6)] == [0, 0, 1, 2, 9, 44]
1222
+ assert nD((i for i in range(4))) == nD('0123') == 9
1223
+ assert nD(m=(i for i in range(4))) == 3
1224
+ assert nD(m={0: 1, 1: 1, 2: 1, 3: 1}) == 3
1225
+ assert nD(m=[0, 1, 2, 3]) == 3
1226
+ raises(TypeError, lambda: nD(m=0))
1227
+ raises(TypeError, lambda: nD(-1))
1228
+ assert nD({-1: 1, -2: 1}) == 1
1229
+ assert nD(m={0: 3}) == 0
1230
+ raises(ValueError, lambda: nD(i='123', n=3))
1231
+ raises(ValueError, lambda: nD(i='123', m=(1,2)))
1232
+ raises(ValueError, lambda: nD(n=0, m=(1,2)))
1233
+ raises(ValueError, lambda: nD({1: -1}))
1234
+ raises(ValueError, lambda: nD(m={-1: 1, 2: 1}))
1235
+ raises(ValueError, lambda: nD(m={1: -1, 2: 1}))
1236
+ raises(ValueError, lambda: nD(m=[-1, 2]))
1237
+ raises(TypeError, lambda: nD({1: x}))
1238
+ raises(TypeError, lambda: nD(m={1: x}))
1239
+ raises(TypeError, lambda: nD(m={x: 1}))
1240
+
1241
+
1242
+ def test_deprecated_ntheory_symbolic_functions():
1243
+ from sympy.testing.pytest import warns_deprecated_sympy
1244
+
1245
+ with warns_deprecated_sympy():
1246
+ assert not carmichael.is_carmichael(3)
1247
+ with warns_deprecated_sympy():
1248
+ assert carmichael.find_carmichael_numbers_in_range(10, 20) == []
1249
+ with warns_deprecated_sympy():
1250
+ assert carmichael.find_first_n_carmichaels(1)
.venv/lib/python3.13/site-packages/sympy/functions/elementary/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ # Stub __init__.py for sympy.functions.elementary
.venv/lib/python3.13/site-packages/sympy/functions/elementary/_trigonometric_special.py ADDED
@@ -0,0 +1,261 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ r"""A module for special angle formulas for trigonometric functions
2
+
3
+ TODO
4
+ ====
5
+
6
+ This module should be developed in the future to contain direct square root
7
+ representation of
8
+
9
+ .. math
10
+ F(\frac{n}{m} \pi)
11
+
12
+ for every
13
+
14
+ - $m \in \{ 3, 5, 17, 257, 65537 \}$
15
+ - $n \in \mathbb{N}$, $0 \le n < m$
16
+ - $F \in \{\sin, \cos, \tan, \csc, \sec, \cot\}$
17
+
18
+ Without multi-step rewrites
19
+ (e.g. $\tan \to \cos/\sin \to \cos/\sqrt \to \ sqrt$)
20
+ or using chebyshev identities
21
+ (e.g. $\cos \to \cos + \cos^2 + \cdots \to \sqrt{} + \sqrt{}^2 + \cdots $),
22
+ which are trivial to implement in sympy,
23
+ and had used to give overly complicated expressions.
24
+
25
+ The reference can be found below, if anyone may need help implementing them.
26
+
27
+ References
28
+ ==========
29
+
30
+ .. [*] Gottlieb, Christian. (1999). The Simple and straightforward construction
31
+ of the regular 257-gon. The Mathematical Intelligencer. 21. 31-37.
32
+ 10.1007/BF03024829.
33
+ .. [*] https://resources.wolframcloud.com/FunctionRepository/resources/Cos2PiOverFermatPrime
34
+ """
35
+ from __future__ import annotations
36
+ from typing import Callable
37
+ from functools import reduce
38
+ from sympy.core.expr import Expr
39
+ from sympy.core.singleton import S
40
+ from sympy.core.intfunc import igcdex
41
+ from sympy.core.numbers import Integer
42
+ from sympy.functions.elementary.miscellaneous import sqrt
43
+ from sympy.core.cache import cacheit
44
+
45
+
46
+ def migcdex(*x: int) -> tuple[tuple[int, ...], int]:
47
+ r"""Compute extended gcd for multiple integers.
48
+
49
+ Explanation
50
+ ===========
51
+
52
+ Given the integers $x_1, \cdots, x_n$ and
53
+ an extended gcd for multiple arguments are defined as a solution
54
+ $(y_1, \cdots, y_n), g$ for the diophantine equation
55
+ $x_1 y_1 + \cdots + x_n y_n = g$ such that
56
+ $g = \gcd(x_1, \cdots, x_n)$.
57
+
58
+ Examples
59
+ ========
60
+
61
+ >>> from sympy.functions.elementary._trigonometric_special import migcdex
62
+ >>> migcdex()
63
+ ((), 0)
64
+ >>> migcdex(4)
65
+ ((1,), 4)
66
+ >>> migcdex(4, 6)
67
+ ((-1, 1), 2)
68
+ >>> migcdex(6, 10, 15)
69
+ ((1, 1, -1), 1)
70
+ """
71
+ if not x:
72
+ return (), 0
73
+
74
+ if len(x) == 1:
75
+ return (1,), x[0]
76
+
77
+ if len(x) == 2:
78
+ u, v, h = igcdex(x[0], x[1])
79
+ return (u, v), h
80
+
81
+ y, g = migcdex(*x[1:])
82
+ u, v, h = igcdex(x[0], g)
83
+ return (u, *(v * i for i in y)), h
84
+
85
+
86
+ def ipartfrac(*denoms: int) -> tuple[int, ...]:
87
+ r"""Compute the partial fraction decomposition.
88
+
89
+ Explanation
90
+ ===========
91
+
92
+ Given a rational number $\frac{1}{q_1 \cdots q_n}$ where all
93
+ $q_1, \cdots, q_n$ are pairwise coprime,
94
+
95
+ A partial fraction decomposition is defined as
96
+
97
+ .. math::
98
+ \frac{1}{q_1 \cdots q_n} = \frac{p_1}{q_1} + \cdots + \frac{p_n}{q_n}
99
+
100
+ And it can be derived from solving the following diophantine equation for
101
+ the $p_1, \cdots, p_n$
102
+
103
+ .. math::
104
+ 1 = p_1 \prod_{i \ne 1}q_i + \cdots + p_n \prod_{i \ne n}q_i
105
+
106
+ Where $q_1, \cdots, q_n$ being pairwise coprime implies
107
+ $\gcd(\prod_{i \ne 1}q_i, \cdots, \prod_{i \ne n}q_i) = 1$,
108
+ which guarantees the existence of the solution.
109
+
110
+ It is sufficient to compute partial fraction decomposition only
111
+ for numerator $1$ because partial fraction decomposition for any
112
+ $\frac{n}{q_1 \cdots q_n}$ can be easily computed by multiplying
113
+ the result by $n$ afterwards.
114
+
115
+ Parameters
116
+ ==========
117
+
118
+ denoms : int
119
+ The pairwise coprime integer denominators $q_i$ which defines the
120
+ rational number $\frac{1}{q_1 \cdots q_n}$
121
+
122
+ Returns
123
+ =======
124
+
125
+ tuple[int, ...]
126
+ The list of numerators which semantically corresponds to $p_i$ of the
127
+ partial fraction decomposition
128
+ $\frac{1}{q_1 \cdots q_n} = \frac{p_1}{q_1} + \cdots + \frac{p_n}{q_n}$
129
+
130
+ Examples
131
+ ========
132
+
133
+ >>> from sympy import Rational, Mul
134
+ >>> from sympy.functions.elementary._trigonometric_special import ipartfrac
135
+
136
+ >>> denoms = 2, 3, 5
137
+ >>> numers = ipartfrac(2, 3, 5)
138
+ >>> numers
139
+ (1, 7, -14)
140
+
141
+ >>> Rational(1, Mul(*denoms))
142
+ 1/30
143
+ >>> out = 0
144
+ >>> for n, d in zip(numers, denoms):
145
+ ... out += Rational(n, d)
146
+ >>> out
147
+ 1/30
148
+ """
149
+ if not denoms:
150
+ return ()
151
+
152
+ def mul(x: int, y: int) -> int:
153
+ return x * y
154
+
155
+ denom = reduce(mul, denoms)
156
+ a = [denom // x for x in denoms]
157
+ h, _ = migcdex(*a)
158
+ return h
159
+
160
+
161
+ def fermat_coords(n: int) -> list[int] | None:
162
+ """If n can be factored in terms of Fermat primes with
163
+ multiplicity of each being 1, return those primes, else
164
+ None
165
+ """
166
+ primes = []
167
+ for p in [3, 5, 17, 257, 65537]:
168
+ quotient, remainder = divmod(n, p)
169
+ if remainder == 0:
170
+ n = quotient
171
+ primes.append(p)
172
+ if n == 1:
173
+ return primes
174
+ return None
175
+
176
+
177
+ @cacheit
178
+ def cos_3() -> Expr:
179
+ r"""Computes $\cos \frac{\pi}{3}$ in square roots"""
180
+ return S.Half
181
+
182
+
183
+ @cacheit
184
+ def cos_5() -> Expr:
185
+ r"""Computes $\cos \frac{\pi}{5}$ in square roots"""
186
+ return (sqrt(5) + 1) / 4
187
+
188
+
189
+ @cacheit
190
+ def cos_17() -> Expr:
191
+ r"""Computes $\cos \frac{\pi}{17}$ in square roots"""
192
+ return sqrt(
193
+ (15 + sqrt(17)) / 32 + sqrt(2) * (sqrt(17 - sqrt(17)) +
194
+ sqrt(sqrt(2) * (-8 * sqrt(17 + sqrt(17)) - (1 - sqrt(17))
195
+ * sqrt(17 - sqrt(17))) + 6 * sqrt(17) + 34)) / 32)
196
+
197
+
198
+ @cacheit
199
+ def cos_257() -> Expr:
200
+ r"""Computes $\cos \frac{\pi}{257}$ in square roots
201
+
202
+ References
203
+ ==========
204
+
205
+ .. [*] https://math.stackexchange.com/questions/516142/how-does-cos2-pi-257-look-like-in-real-radicals
206
+ .. [*] https://r-knott.surrey.ac.uk/Fibonacci/simpleTrig.html
207
+ """
208
+ def f1(a: Expr, b: Expr) -> tuple[Expr, Expr]:
209
+ return (a + sqrt(a**2 + b)) / 2, (a - sqrt(a**2 + b)) / 2
210
+
211
+ def f2(a: Expr, b: Expr) -> Expr:
212
+ return (a - sqrt(a**2 + b))/2
213
+
214
+ t1, t2 = f1(S.NegativeOne, Integer(256))
215
+ z1, z3 = f1(t1, Integer(64))
216
+ z2, z4 = f1(t2, Integer(64))
217
+ y1, y5 = f1(z1, 4*(5 + t1 + 2*z1))
218
+ y6, y2 = f1(z2, 4*(5 + t2 + 2*z2))
219
+ y3, y7 = f1(z3, 4*(5 + t1 + 2*z3))
220
+ y8, y4 = f1(z4, 4*(5 + t2 + 2*z4))
221
+ x1, x9 = f1(y1, -4*(t1 + y1 + y3 + 2*y6))
222
+ x2, x10 = f1(y2, -4*(t2 + y2 + y4 + 2*y7))
223
+ x3, x11 = f1(y3, -4*(t1 + y3 + y5 + 2*y8))
224
+ x4, x12 = f1(y4, -4*(t2 + y4 + y6 + 2*y1))
225
+ x5, x13 = f1(y5, -4*(t1 + y5 + y7 + 2*y2))
226
+ x6, x14 = f1(y6, -4*(t2 + y6 + y8 + 2*y3))
227
+ x15, x7 = f1(y7, -4*(t1 + y7 + y1 + 2*y4))
228
+ x8, x16 = f1(y8, -4*(t2 + y8 + y2 + 2*y5))
229
+ v1 = f2(x1, -4*(x1 + x2 + x3 + x6))
230
+ v2 = f2(x2, -4*(x2 + x3 + x4 + x7))
231
+ v3 = f2(x8, -4*(x8 + x9 + x10 + x13))
232
+ v4 = f2(x9, -4*(x9 + x10 + x11 + x14))
233
+ v5 = f2(x10, -4*(x10 + x11 + x12 + x15))
234
+ v6 = f2(x16, -4*(x16 + x1 + x2 + x5))
235
+ u1 = -f2(-v1, -4*(v2 + v3))
236
+ u2 = -f2(-v4, -4*(v5 + v6))
237
+ w1 = -2*f2(-u1, -4*u2)
238
+ return sqrt(sqrt(2)*sqrt(w1 + 4)/8 + S.Half)
239
+
240
+
241
+ def cos_table() -> dict[int, Callable[[], Expr]]:
242
+ r"""Lazily evaluated table for $\cos \frac{\pi}{n}$ in square roots for
243
+ $n \in \{3, 5, 17, 257, 65537\}$.
244
+
245
+ Notes
246
+ =====
247
+
248
+ 65537 is the only other known Fermat prime and it is nearly impossible to
249
+ build in the current SymPy due to performance issues.
250
+
251
+ References
252
+ ==========
253
+
254
+ https://r-knott.surrey.ac.uk/Fibonacci/simpleTrig.html
255
+ """
256
+ return {
257
+ 3: cos_3,
258
+ 5: cos_5,
259
+ 17: cos_17,
260
+ 257: cos_257
261
+ }
.venv/lib/python3.13/site-packages/sympy/functions/elementary/benchmarks/__init__.py ADDED
File without changes