Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- .venv/lib/python3.13/site-packages/sympy/assumptions/predicates/matrices.py +511 -0
- .venv/lib/python3.13/site-packages/sympy/assumptions/predicates/order.py +390 -0
- .venv/lib/python3.13/site-packages/sympy/codegen/tests/__init__.py +0 -0
- .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_abstract_nodes.py +14 -0
- .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_algorithms.py +180 -0
- .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_applications.py +58 -0
- .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_approximations.py +53 -0
- .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_ast.py +661 -0
- .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_cfunctions.py +186 -0
- .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_cnodes.py +112 -0
- .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_cxxnodes.py +14 -0
- .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_fnodes.py +213 -0
- .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_matrix_nodes.py +50 -0
- .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_numpy_nodes.py +69 -0
- .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_pynodes.py +13 -0
- .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_pyutils.py +7 -0
- .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_rewriting.py +479 -0
- .venv/lib/python3.13/site-packages/sympy/codegen/tests/test_scipy_nodes.py +44 -0
- .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/__init__.py +0 -0
- .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_coset_table.py +825 -0
- .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_fp_groups.py +257 -0
- .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_free_groups.py +226 -0
- .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_galois.py +82 -0
- .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_generators.py +105 -0
- .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_graycode.py +72 -0
- .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_group_constructs.py +15 -0
- .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_group_numbers.py +110 -0
- .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_homomorphisms.py +114 -0
- .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_named_groups.py +70 -0
- .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_partitions.py +118 -0
- .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_pc_groups.py +87 -0
- .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_perm_groups.py +1243 -0
- .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_permutations.py +564 -0
- .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_polyhedron.py +105 -0
- .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_prufer.py +74 -0
- .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_rewriting.py +49 -0
- .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_schur_number.py +55 -0
- .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_subsets.py +63 -0
- .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_tensor_can.py +560 -0
- .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_testutil.py +55 -0
- .venv/lib/python3.13/site-packages/sympy/combinatorics/tests/test_util.py +120 -0
- .venv/lib/python3.13/site-packages/sympy/functions/combinatorial/__init__.py +1 -0
- .venv/lib/python3.13/site-packages/sympy/functions/combinatorial/factorials.py +1133 -0
- .venv/lib/python3.13/site-packages/sympy/functions/combinatorial/numbers.py +0 -0
- .venv/lib/python3.13/site-packages/sympy/functions/combinatorial/tests/__init__.py +0 -0
- .venv/lib/python3.13/site-packages/sympy/functions/combinatorial/tests/test_comb_factorials.py +653 -0
- .venv/lib/python3.13/site-packages/sympy/functions/combinatorial/tests/test_comb_numbers.py +1250 -0
- .venv/lib/python3.13/site-packages/sympy/functions/elementary/__init__.py +1 -0
- .venv/lib/python3.13/site-packages/sympy/functions/elementary/_trigonometric_special.py +261 -0
- .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
|