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/concrete/tests/__init__.py +0 -0
- .venv/lib/python3.13/site-packages/sympy/concrete/tests/test_delta.py +499 -0
- .venv/lib/python3.13/site-packages/sympy/concrete/tests/test_gosper.py +204 -0
- .venv/lib/python3.13/site-packages/sympy/concrete/tests/test_guess.py +82 -0
- .venv/lib/python3.13/site-packages/sympy/concrete/tests/test_products.py +410 -0
- .venv/lib/python3.13/site-packages/sympy/concrete/tests/test_sums_products.py +1676 -0
- .venv/lib/python3.13/site-packages/sympy/holonomic/tests/__init__.py +0 -0
- .venv/lib/python3.13/site-packages/sympy/holonomic/tests/test_holonomic.py +851 -0
- .venv/lib/python3.13/site-packages/sympy/holonomic/tests/test_recurrence.py +41 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/__init__.py +57 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/algebraicfield.py +638 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/characteristiczero.py +15 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/complexfield.py +198 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/compositedomain.py +52 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/domain.py +1382 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/domainelement.py +38 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/expressiondomain.py +278 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/expressionrawdomain.py +57 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/field.py +118 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/finitefield.py +368 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/fractionfield.py +181 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/gaussiandomains.py +706 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/gmpyfinitefield.py +16 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/gmpyintegerring.py +105 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/gmpyrationalfield.py +100 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/groundtypes.py +99 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/integerring.py +276 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/modularinteger.py +237 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/mpelements.py +181 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/old_fractionfield.py +188 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/old_polynomialring.py +490 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/polynomialring.py +203 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/pythonfinitefield.py +16 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/pythonintegerring.py +98 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/pythonrational.py +22 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/pythonrationalfield.py +73 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/quotientring.py +202 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/rationalfield.py +200 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/realfield.py +220 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/ring.py +118 -0
- .venv/lib/python3.13/site-packages/sympy/polys/domains/simpledomain.py +15 -0
- .venv/lib/python3.13/site-packages/sympy/polys/tests/__init__.py +0 -0
- .venv/lib/python3.13/site-packages/sympy/polys/tests/test_appellseqs.py +91 -0
- .venv/lib/python3.13/site-packages/sympy/polys/tests/test_constructor.py +236 -0
- .venv/lib/python3.13/site-packages/sympy/polys/tests/test_densearith.py +1007 -0
- .venv/lib/python3.13/site-packages/sympy/polys/tests/test_densebasic.py +730 -0
- .venv/lib/python3.13/site-packages/sympy/polys/tests/test_densetools.py +714 -0
- .venv/lib/python3.13/site-packages/sympy/polys/tests/test_dispersion.py +95 -0
- .venv/lib/python3.13/site-packages/sympy/polys/tests/test_distributedmodules.py +208 -0
- .venv/lib/python3.13/site-packages/sympy/polys/tests/test_euclidtools.py +712 -0
.venv/lib/python3.13/site-packages/sympy/concrete/tests/__init__.py
ADDED
|
File without changes
|
.venv/lib/python3.13/site-packages/sympy/concrete/tests/test_delta.py
ADDED
|
@@ -0,0 +1,499 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.concrete import Sum
|
| 2 |
+
from sympy.concrete.delta import deltaproduct as dp, deltasummation as ds, _extract_delta
|
| 3 |
+
from sympy.core import Eq, S, symbols, oo
|
| 4 |
+
from sympy.functions import KroneckerDelta as KD, Piecewise, piecewise_fold
|
| 5 |
+
from sympy.logic import And
|
| 6 |
+
from sympy.testing.pytest import raises
|
| 7 |
+
|
| 8 |
+
i, j, k, l, m = symbols("i j k l m", integer=True, finite=True)
|
| 9 |
+
x, y = symbols("x y", commutative=False)
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
def test_deltaproduct_trivial():
|
| 13 |
+
assert dp(x, (j, 1, 0)) == 1
|
| 14 |
+
assert dp(x, (j, 1, 3)) == x**3
|
| 15 |
+
assert dp(x + y, (j, 1, 3)) == (x + y)**3
|
| 16 |
+
assert dp(x*y, (j, 1, 3)) == (x*y)**3
|
| 17 |
+
assert dp(KD(i, j), (k, 1, 3)) == KD(i, j)
|
| 18 |
+
assert dp(x*KD(i, j), (k, 1, 3)) == x**3*KD(i, j)
|
| 19 |
+
assert dp(x*y*KD(i, j), (k, 1, 3)) == (x*y)**3*KD(i, j)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def test_deltaproduct_basic():
|
| 23 |
+
assert dp(KD(i, j), (j, 1, 3)) == 0
|
| 24 |
+
assert dp(KD(i, j), (j, 1, 1)) == KD(i, 1)
|
| 25 |
+
assert dp(KD(i, j), (j, 2, 2)) == KD(i, 2)
|
| 26 |
+
assert dp(KD(i, j), (j, 3, 3)) == KD(i, 3)
|
| 27 |
+
assert dp(KD(i, j), (j, 1, k)) == KD(i, 1)*KD(k, 1) + KD(k, 0)
|
| 28 |
+
assert dp(KD(i, j), (j, k, 3)) == KD(i, 3)*KD(k, 3) + KD(k, 4)
|
| 29 |
+
assert dp(KD(i, j), (j, k, l)) == KD(i, l)*KD(k, l) + KD(k, l + 1)
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def test_deltaproduct_mul_x_kd():
|
| 33 |
+
assert dp(x*KD(i, j), (j, 1, 3)) == 0
|
| 34 |
+
assert dp(x*KD(i, j), (j, 1, 1)) == x*KD(i, 1)
|
| 35 |
+
assert dp(x*KD(i, j), (j, 2, 2)) == x*KD(i, 2)
|
| 36 |
+
assert dp(x*KD(i, j), (j, 3, 3)) == x*KD(i, 3)
|
| 37 |
+
assert dp(x*KD(i, j), (j, 1, k)) == x*KD(i, 1)*KD(k, 1) + KD(k, 0)
|
| 38 |
+
assert dp(x*KD(i, j), (j, k, 3)) == x*KD(i, 3)*KD(k, 3) + KD(k, 4)
|
| 39 |
+
assert dp(x*KD(i, j), (j, k, l)) == x*KD(i, l)*KD(k, l) + KD(k, l + 1)
|
| 40 |
+
|
| 41 |
+
|
| 42 |
+
def test_deltaproduct_mul_add_x_y_kd():
|
| 43 |
+
assert dp((x + y)*KD(i, j), (j, 1, 3)) == 0
|
| 44 |
+
assert dp((x + y)*KD(i, j), (j, 1, 1)) == (x + y)*KD(i, 1)
|
| 45 |
+
assert dp((x + y)*KD(i, j), (j, 2, 2)) == (x + y)*KD(i, 2)
|
| 46 |
+
assert dp((x + y)*KD(i, j), (j, 3, 3)) == (x + y)*KD(i, 3)
|
| 47 |
+
assert dp((x + y)*KD(i, j), (j, 1, k)) == \
|
| 48 |
+
(x + y)*KD(i, 1)*KD(k, 1) + KD(k, 0)
|
| 49 |
+
assert dp((x + y)*KD(i, j), (j, k, 3)) == \
|
| 50 |
+
(x + y)*KD(i, 3)*KD(k, 3) + KD(k, 4)
|
| 51 |
+
assert dp((x + y)*KD(i, j), (j, k, l)) == \
|
| 52 |
+
(x + y)*KD(i, l)*KD(k, l) + KD(k, l + 1)
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def test_deltaproduct_add_kd_kd():
|
| 56 |
+
assert dp(KD(i, k) + KD(j, k), (k, 1, 3)) == 0
|
| 57 |
+
assert dp(KD(i, k) + KD(j, k), (k, 1, 1)) == KD(i, 1) + KD(j, 1)
|
| 58 |
+
assert dp(KD(i, k) + KD(j, k), (k, 2, 2)) == KD(i, 2) + KD(j, 2)
|
| 59 |
+
assert dp(KD(i, k) + KD(j, k), (k, 3, 3)) == KD(i, 3) + KD(j, 3)
|
| 60 |
+
assert dp(KD(i, k) + KD(j, k), (k, 1, l)) == KD(l, 0) + \
|
| 61 |
+
KD(i, 1)*KD(l, 1) + KD(j, 1)*KD(l, 1) + \
|
| 62 |
+
KD(i, 1)*KD(j, 2)*KD(l, 2) + KD(j, 1)*KD(i, 2)*KD(l, 2)
|
| 63 |
+
assert dp(KD(i, k) + KD(j, k), (k, l, 3)) == KD(l, 4) + \
|
| 64 |
+
KD(i, 3)*KD(l, 3) + KD(j, 3)*KD(l, 3) + \
|
| 65 |
+
KD(i, 2)*KD(j, 3)*KD(l, 2) + KD(i, 3)*KD(j, 2)*KD(l, 2)
|
| 66 |
+
assert dp(KD(i, k) + KD(j, k), (k, l, m)) == KD(l, m + 1) + \
|
| 67 |
+
KD(i, m)*KD(l, m) + KD(j, m)*KD(l, m) + \
|
| 68 |
+
KD(i, m)*KD(j, m - 1)*KD(l, m - 1) + KD(i, m - 1)*KD(j, m)*KD(l, m - 1)
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
def test_deltaproduct_mul_x_add_kd_kd():
|
| 72 |
+
assert dp(x*(KD(i, k) + KD(j, k)), (k, 1, 3)) == 0
|
| 73 |
+
assert dp(x*(KD(i, k) + KD(j, k)), (k, 1, 1)) == x*(KD(i, 1) + KD(j, 1))
|
| 74 |
+
assert dp(x*(KD(i, k) + KD(j, k)), (k, 2, 2)) == x*(KD(i, 2) + KD(j, 2))
|
| 75 |
+
assert dp(x*(KD(i, k) + KD(j, k)), (k, 3, 3)) == x*(KD(i, 3) + KD(j, 3))
|
| 76 |
+
assert dp(x*(KD(i, k) + KD(j, k)), (k, 1, l)) == KD(l, 0) + \
|
| 77 |
+
x*KD(i, 1)*KD(l, 1) + x*KD(j, 1)*KD(l, 1) + \
|
| 78 |
+
x**2*KD(i, 1)*KD(j, 2)*KD(l, 2) + x**2*KD(j, 1)*KD(i, 2)*KD(l, 2)
|
| 79 |
+
assert dp(x*(KD(i, k) + KD(j, k)), (k, l, 3)) == KD(l, 4) + \
|
| 80 |
+
x*KD(i, 3)*KD(l, 3) + x*KD(j, 3)*KD(l, 3) + \
|
| 81 |
+
x**2*KD(i, 2)*KD(j, 3)*KD(l, 2) + x**2*KD(i, 3)*KD(j, 2)*KD(l, 2)
|
| 82 |
+
assert dp(x*(KD(i, k) + KD(j, k)), (k, l, m)) == KD(l, m + 1) + \
|
| 83 |
+
x*KD(i, m)*KD(l, m) + x*KD(j, m)*KD(l, m) + \
|
| 84 |
+
x**2*KD(i, m - 1)*KD(j, m)*KD(l, m - 1) + \
|
| 85 |
+
x**2*KD(i, m)*KD(j, m - 1)*KD(l, m - 1)
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
def test_deltaproduct_mul_add_x_y_add_kd_kd():
|
| 89 |
+
assert dp((x + y)*(KD(i, k) + KD(j, k)), (k, 1, 3)) == 0
|
| 90 |
+
assert dp((x + y)*(KD(i, k) + KD(j, k)), (k, 1, 1)) == \
|
| 91 |
+
(x + y)*(KD(i, 1) + KD(j, 1))
|
| 92 |
+
assert dp((x + y)*(KD(i, k) + KD(j, k)), (k, 2, 2)) == \
|
| 93 |
+
(x + y)*(KD(i, 2) + KD(j, 2))
|
| 94 |
+
assert dp((x + y)*(KD(i, k) + KD(j, k)), (k, 3, 3)) == \
|
| 95 |
+
(x + y)*(KD(i, 3) + KD(j, 3))
|
| 96 |
+
assert dp((x + y)*(KD(i, k) + KD(j, k)), (k, 1, l)) == KD(l, 0) + \
|
| 97 |
+
(x + y)*KD(i, 1)*KD(l, 1) + (x + y)*KD(j, 1)*KD(l, 1) + \
|
| 98 |
+
(x + y)**2*KD(i, 1)*KD(j, 2)*KD(l, 2) + \
|
| 99 |
+
(x + y)**2*KD(j, 1)*KD(i, 2)*KD(l, 2)
|
| 100 |
+
assert dp((x + y)*(KD(i, k) + KD(j, k)), (k, l, 3)) == KD(l, 4) + \
|
| 101 |
+
(x + y)*KD(i, 3)*KD(l, 3) + (x + y)*KD(j, 3)*KD(l, 3) + \
|
| 102 |
+
(x + y)**2*KD(i, 2)*KD(j, 3)*KD(l, 2) + \
|
| 103 |
+
(x + y)**2*KD(i, 3)*KD(j, 2)*KD(l, 2)
|
| 104 |
+
assert dp((x + y)*(KD(i, k) + KD(j, k)), (k, l, m)) == KD(l, m + 1) + \
|
| 105 |
+
(x + y)*KD(i, m)*KD(l, m) + (x + y)*KD(j, m)*KD(l, m) + \
|
| 106 |
+
(x + y)**2*KD(i, m - 1)*KD(j, m)*KD(l, m - 1) + \
|
| 107 |
+
(x + y)**2*KD(i, m)*KD(j, m - 1)*KD(l, m - 1)
|
| 108 |
+
|
| 109 |
+
|
| 110 |
+
def test_deltaproduct_add_mul_x_y_mul_x_kd():
|
| 111 |
+
assert dp(x*y + x*KD(i, j), (j, 1, 3)) == (x*y)**3 + \
|
| 112 |
+
x*(x*y)**2*KD(i, 1) + (x*y)*x*(x*y)*KD(i, 2) + (x*y)**2*x*KD(i, 3)
|
| 113 |
+
assert dp(x*y + x*KD(i, j), (j, 1, 1)) == x*y + x*KD(i, 1)
|
| 114 |
+
assert dp(x*y + x*KD(i, j), (j, 2, 2)) == x*y + x*KD(i, 2)
|
| 115 |
+
assert dp(x*y + x*KD(i, j), (j, 3, 3)) == x*y + x*KD(i, 3)
|
| 116 |
+
assert dp(x*y + x*KD(i, j), (j, 1, k)) == \
|
| 117 |
+
(x*y)**k + Piecewise(
|
| 118 |
+
((x*y)**(i - 1)*x*(x*y)**(k - i), And(1 <= i, i <= k)),
|
| 119 |
+
(0, True)
|
| 120 |
+
)
|
| 121 |
+
assert dp(x*y + x*KD(i, j), (j, k, 3)) == \
|
| 122 |
+
(x*y)**(-k + 4) + Piecewise(
|
| 123 |
+
((x*y)**(i - k)*x*(x*y)**(3 - i), And(k <= i, i <= 3)),
|
| 124 |
+
(0, True)
|
| 125 |
+
)
|
| 126 |
+
assert dp(x*y + x*KD(i, j), (j, k, l)) == \
|
| 127 |
+
(x*y)**(-k + l + 1) + Piecewise(
|
| 128 |
+
((x*y)**(i - k)*x*(x*y)**(l - i), And(k <= i, i <= l)),
|
| 129 |
+
(0, True)
|
| 130 |
+
)
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
def test_deltaproduct_mul_x_add_y_kd():
|
| 134 |
+
assert dp(x*(y + KD(i, j)), (j, 1, 3)) == (x*y)**3 + \
|
| 135 |
+
x*(x*y)**2*KD(i, 1) + (x*y)*x*(x*y)*KD(i, 2) + (x*y)**2*x*KD(i, 3)
|
| 136 |
+
assert dp(x*(y + KD(i, j)), (j, 1, 1)) == x*(y + KD(i, 1))
|
| 137 |
+
assert dp(x*(y + KD(i, j)), (j, 2, 2)) == x*(y + KD(i, 2))
|
| 138 |
+
assert dp(x*(y + KD(i, j)), (j, 3, 3)) == x*(y + KD(i, 3))
|
| 139 |
+
assert dp(x*(y + KD(i, j)), (j, 1, k)) == \
|
| 140 |
+
(x*y)**k + Piecewise(
|
| 141 |
+
((x*y)**(i - 1)*x*(x*y)**(k - i), And(1 <= i, i <= k)),
|
| 142 |
+
(0, True)
|
| 143 |
+
).expand()
|
| 144 |
+
assert dp(x*(y + KD(i, j)), (j, k, 3)) == \
|
| 145 |
+
((x*y)**(-k + 4) + Piecewise(
|
| 146 |
+
((x*y)**(i - k)*x*(x*y)**(3 - i), And(k <= i, i <= 3)),
|
| 147 |
+
(0, True)
|
| 148 |
+
)).expand()
|
| 149 |
+
assert dp(x*(y + KD(i, j)), (j, k, l)) == \
|
| 150 |
+
((x*y)**(-k + l + 1) + Piecewise(
|
| 151 |
+
((x*y)**(i - k)*x*(x*y)**(l - i), And(k <= i, i <= l)),
|
| 152 |
+
(0, True)
|
| 153 |
+
)).expand()
|
| 154 |
+
|
| 155 |
+
|
| 156 |
+
def test_deltaproduct_mul_x_add_y_twokd():
|
| 157 |
+
assert dp(x*(y + 2*KD(i, j)), (j, 1, 3)) == (x*y)**3 + \
|
| 158 |
+
2*x*(x*y)**2*KD(i, 1) + 2*x*y*x*x*y*KD(i, 2) + 2*(x*y)**2*x*KD(i, 3)
|
| 159 |
+
assert dp(x*(y + 2*KD(i, j)), (j, 1, 1)) == x*(y + 2*KD(i, 1))
|
| 160 |
+
assert dp(x*(y + 2*KD(i, j)), (j, 2, 2)) == x*(y + 2*KD(i, 2))
|
| 161 |
+
assert dp(x*(y + 2*KD(i, j)), (j, 3, 3)) == x*(y + 2*KD(i, 3))
|
| 162 |
+
assert dp(x*(y + 2*KD(i, j)), (j, 1, k)) == \
|
| 163 |
+
(x*y)**k + Piecewise(
|
| 164 |
+
(2*(x*y)**(i - 1)*x*(x*y)**(k - i), And(1 <= i, i <= k)),
|
| 165 |
+
(0, True)
|
| 166 |
+
).expand()
|
| 167 |
+
assert dp(x*(y + 2*KD(i, j)), (j, k, 3)) == \
|
| 168 |
+
((x*y)**(-k + 4) + Piecewise(
|
| 169 |
+
(2*(x*y)**(i - k)*x*(x*y)**(3 - i), And(k <= i, i <= 3)),
|
| 170 |
+
(0, True)
|
| 171 |
+
)).expand()
|
| 172 |
+
assert dp(x*(y + 2*KD(i, j)), (j, k, l)) == \
|
| 173 |
+
((x*y)**(-k + l + 1) + Piecewise(
|
| 174 |
+
(2*(x*y)**(i - k)*x*(x*y)**(l - i), And(k <= i, i <= l)),
|
| 175 |
+
(0, True)
|
| 176 |
+
)).expand()
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
def test_deltaproduct_mul_add_x_y_add_y_kd():
|
| 180 |
+
assert dp((x + y)*(y + KD(i, j)), (j, 1, 3)) == ((x + y)*y)**3 + \
|
| 181 |
+
(x + y)*((x + y)*y)**2*KD(i, 1) + \
|
| 182 |
+
(x + y)*y*(x + y)**2*y*KD(i, 2) + \
|
| 183 |
+
((x + y)*y)**2*(x + y)*KD(i, 3)
|
| 184 |
+
assert dp((x + y)*(y + KD(i, j)), (j, 1, 1)) == (x + y)*(y + KD(i, 1))
|
| 185 |
+
assert dp((x + y)*(y + KD(i, j)), (j, 2, 2)) == (x + y)*(y + KD(i, 2))
|
| 186 |
+
assert dp((x + y)*(y + KD(i, j)), (j, 3, 3)) == (x + y)*(y + KD(i, 3))
|
| 187 |
+
assert dp((x + y)*(y + KD(i, j)), (j, 1, k)) == \
|
| 188 |
+
((x + y)*y)**k + Piecewise(
|
| 189 |
+
(((x + y)*y)**(-1)*((x + y)*y)**i*(x + y)*((x + y)*y
|
| 190 |
+
)**k*((x + y)*y)**(-i), (i >= 1) & (i <= k)), (0, True))
|
| 191 |
+
assert dp((x + y)*(y + KD(i, j)), (j, k, 3)) == (
|
| 192 |
+
(x + y)*y)**4*((x + y)*y)**(-k) + Piecewise((((x + y)*y)**i*(
|
| 193 |
+
(x + y)*y)**(-k)*(x + y)*((x + y)*y)**3*((x + y)*y)**(-i),
|
| 194 |
+
(i >= k) & (i <= 3)), (0, True))
|
| 195 |
+
assert dp((x + y)*(y + KD(i, j)), (j, k, l)) == \
|
| 196 |
+
(x + y)*y*((x + y)*y)**l*((x + y)*y)**(-k) + Piecewise(
|
| 197 |
+
(((x + y)*y)**i*((x + y)*y)**(-k)*(x + y)*((x + y)*y
|
| 198 |
+
)**l*((x + y)*y)**(-i), (i >= k) & (i <= l)), (0, True))
|
| 199 |
+
|
| 200 |
+
|
| 201 |
+
def test_deltaproduct_mul_add_x_kd_add_y_kd():
|
| 202 |
+
assert dp((x + KD(i, k))*(y + KD(i, j)), (j, 1, 3)) == \
|
| 203 |
+
KD(i, 1)*(KD(i, k) + x)*((KD(i, k) + x)*y)**2 + \
|
| 204 |
+
KD(i, 2)*(KD(i, k) + x)*y*(KD(i, k) + x)**2*y + \
|
| 205 |
+
KD(i, 3)*((KD(i, k) + x)*y)**2*(KD(i, k) + x) + \
|
| 206 |
+
((KD(i, k) + x)*y)**3
|
| 207 |
+
assert dp((x + KD(i, k))*(y + KD(i, j)), (j, 1, 1)) == \
|
| 208 |
+
(x + KD(i, k))*(y + KD(i, 1))
|
| 209 |
+
assert dp((x + KD(i, k))*(y + KD(i, j)), (j, 2, 2)) == \
|
| 210 |
+
(x + KD(i, k))*(y + KD(i, 2))
|
| 211 |
+
assert dp((x + KD(i, k))*(y + KD(i, j)), (j, 3, 3)) == \
|
| 212 |
+
(x + KD(i, k))*(y + KD(i, 3))
|
| 213 |
+
assert dp((x + KD(i, k))*(y + KD(i, j)), (j, 1, k)) == \
|
| 214 |
+
((KD(i, k) + x)*y)**k + Piecewise(
|
| 215 |
+
(((KD(i, k) + x)*y)**(-1)*((KD(i, k) + x)*y)**i*(KD(i, k) + x
|
| 216 |
+
)*((KD(i, k) + x)*y)**k*((KD(i, k) + x)*y)**(-i), (i >= 1
|
| 217 |
+
) & (i <= k)), (0, True))
|
| 218 |
+
assert dp((x + KD(i, k))*(y + KD(i, j)), (j, k, 3)) == (
|
| 219 |
+
(KD(i, k) + x)*y)**4*((KD(i, k) + x)*y)**(-k) + Piecewise(
|
| 220 |
+
(((KD(i, k) + x)*y)**i*((KD(i, k) + x)*y)**(-k)*(KD(i, k)
|
| 221 |
+
+ x)*((KD(i, k) + x)*y)**3*((KD(i, k) + x)*y)**(-i),
|
| 222 |
+
(i >= k) & (i <= 3)), (0, True))
|
| 223 |
+
assert dp((x + KD(i, k))*(y + KD(i, j)), (j, k, l)) == (
|
| 224 |
+
KD(i, k) + x)*y*((KD(i, k) + x)*y)**l*((KD(i, k) + x)*y
|
| 225 |
+
)**(-k) + Piecewise((((KD(i, k) + x)*y)**i*((KD(i, k) + x
|
| 226 |
+
)*y)**(-k)*(KD(i, k) + x)*((KD(i, k) + x)*y)**l*((KD(i, k) + x
|
| 227 |
+
)*y)**(-i), (i >= k) & (i <= l)), (0, True))
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
def test_deltasummation_trivial():
|
| 231 |
+
assert ds(x, (j, 1, 0)) == 0
|
| 232 |
+
assert ds(x, (j, 1, 3)) == 3*x
|
| 233 |
+
assert ds(x + y, (j, 1, 3)) == 3*(x + y)
|
| 234 |
+
assert ds(x*y, (j, 1, 3)) == 3*x*y
|
| 235 |
+
assert ds(KD(i, j), (k, 1, 3)) == 3*KD(i, j)
|
| 236 |
+
assert ds(x*KD(i, j), (k, 1, 3)) == 3*x*KD(i, j)
|
| 237 |
+
assert ds(x*y*KD(i, j), (k, 1, 3)) == 3*x*y*KD(i, j)
|
| 238 |
+
|
| 239 |
+
|
| 240 |
+
def test_deltasummation_basic_numerical():
|
| 241 |
+
n = symbols('n', integer=True, nonzero=True)
|
| 242 |
+
assert ds(KD(n, 0), (n, 1, 3)) == 0
|
| 243 |
+
|
| 244 |
+
# return unevaluated, until it gets implemented
|
| 245 |
+
assert ds(KD(i**2, j**2), (j, -oo, oo)) == \
|
| 246 |
+
Sum(KD(i**2, j**2), (j, -oo, oo))
|
| 247 |
+
|
| 248 |
+
assert Piecewise((KD(i, k), And(1 <= i, i <= 3)), (0, True)) == \
|
| 249 |
+
ds(KD(i, j)*KD(j, k), (j, 1, 3)) == \
|
| 250 |
+
ds(KD(j, k)*KD(i, j), (j, 1, 3))
|
| 251 |
+
|
| 252 |
+
assert ds(KD(i, k), (k, -oo, oo)) == 1
|
| 253 |
+
assert ds(KD(i, k), (k, 0, oo)) == Piecewise((1, S.Zero <= i), (0, True))
|
| 254 |
+
assert ds(KD(i, k), (k, 1, 3)) == \
|
| 255 |
+
Piecewise((1, And(1 <= i, i <= 3)), (0, True))
|
| 256 |
+
assert ds(k*KD(i, j)*KD(j, k), (k, -oo, oo)) == j*KD(i, j)
|
| 257 |
+
assert ds(j*KD(i, j), (j, -oo, oo)) == i
|
| 258 |
+
assert ds(i*KD(i, j), (i, -oo, oo)) == j
|
| 259 |
+
assert ds(x, (i, 1, 3)) == 3*x
|
| 260 |
+
assert ds((i + j)*KD(i, j), (j, -oo, oo)) == 2*i
|
| 261 |
+
|
| 262 |
+
|
| 263 |
+
def test_deltasummation_basic_symbolic():
|
| 264 |
+
assert ds(KD(i, j), (j, 1, 3)) == \
|
| 265 |
+
Piecewise((1, And(1 <= i, i <= 3)), (0, True))
|
| 266 |
+
assert ds(KD(i, j), (j, 1, 1)) == Piecewise((1, Eq(i, 1)), (0, True))
|
| 267 |
+
assert ds(KD(i, j), (j, 2, 2)) == Piecewise((1, Eq(i, 2)), (0, True))
|
| 268 |
+
assert ds(KD(i, j), (j, 3, 3)) == Piecewise((1, Eq(i, 3)), (0, True))
|
| 269 |
+
assert ds(KD(i, j), (j, 1, k)) == \
|
| 270 |
+
Piecewise((1, And(1 <= i, i <= k)), (0, True))
|
| 271 |
+
assert ds(KD(i, j), (j, k, 3)) == \
|
| 272 |
+
Piecewise((1, And(k <= i, i <= 3)), (0, True))
|
| 273 |
+
assert ds(KD(i, j), (j, k, l)) == \
|
| 274 |
+
Piecewise((1, And(k <= i, i <= l)), (0, True))
|
| 275 |
+
|
| 276 |
+
|
| 277 |
+
def test_deltasummation_mul_x_kd():
|
| 278 |
+
assert ds(x*KD(i, j), (j, 1, 3)) == \
|
| 279 |
+
Piecewise((x, And(1 <= i, i <= 3)), (0, True))
|
| 280 |
+
assert ds(x*KD(i, j), (j, 1, 1)) == Piecewise((x, Eq(i, 1)), (0, True))
|
| 281 |
+
assert ds(x*KD(i, j), (j, 2, 2)) == Piecewise((x, Eq(i, 2)), (0, True))
|
| 282 |
+
assert ds(x*KD(i, j), (j, 3, 3)) == Piecewise((x, Eq(i, 3)), (0, True))
|
| 283 |
+
assert ds(x*KD(i, j), (j, 1, k)) == \
|
| 284 |
+
Piecewise((x, And(1 <= i, i <= k)), (0, True))
|
| 285 |
+
assert ds(x*KD(i, j), (j, k, 3)) == \
|
| 286 |
+
Piecewise((x, And(k <= i, i <= 3)), (0, True))
|
| 287 |
+
assert ds(x*KD(i, j), (j, k, l)) == \
|
| 288 |
+
Piecewise((x, And(k <= i, i <= l)), (0, True))
|
| 289 |
+
|
| 290 |
+
|
| 291 |
+
def test_deltasummation_mul_add_x_y_kd():
|
| 292 |
+
assert ds((x + y)*KD(i, j), (j, 1, 3)) == \
|
| 293 |
+
Piecewise((x + y, And(1 <= i, i <= 3)), (0, True))
|
| 294 |
+
assert ds((x + y)*KD(i, j), (j, 1, 1)) == \
|
| 295 |
+
Piecewise((x + y, Eq(i, 1)), (0, True))
|
| 296 |
+
assert ds((x + y)*KD(i, j), (j, 2, 2)) == \
|
| 297 |
+
Piecewise((x + y, Eq(i, 2)), (0, True))
|
| 298 |
+
assert ds((x + y)*KD(i, j), (j, 3, 3)) == \
|
| 299 |
+
Piecewise((x + y, Eq(i, 3)), (0, True))
|
| 300 |
+
assert ds((x + y)*KD(i, j), (j, 1, k)) == \
|
| 301 |
+
Piecewise((x + y, And(1 <= i, i <= k)), (0, True))
|
| 302 |
+
assert ds((x + y)*KD(i, j), (j, k, 3)) == \
|
| 303 |
+
Piecewise((x + y, And(k <= i, i <= 3)), (0, True))
|
| 304 |
+
assert ds((x + y)*KD(i, j), (j, k, l)) == \
|
| 305 |
+
Piecewise((x + y, And(k <= i, i <= l)), (0, True))
|
| 306 |
+
|
| 307 |
+
|
| 308 |
+
def test_deltasummation_add_kd_kd():
|
| 309 |
+
assert ds(KD(i, k) + KD(j, k), (k, 1, 3)) == piecewise_fold(
|
| 310 |
+
Piecewise((1, And(1 <= i, i <= 3)), (0, True)) +
|
| 311 |
+
Piecewise((1, And(1 <= j, j <= 3)), (0, True)))
|
| 312 |
+
assert ds(KD(i, k) + KD(j, k), (k, 1, 1)) == piecewise_fold(
|
| 313 |
+
Piecewise((1, Eq(i, 1)), (0, True)) +
|
| 314 |
+
Piecewise((1, Eq(j, 1)), (0, True)))
|
| 315 |
+
assert ds(KD(i, k) + KD(j, k), (k, 2, 2)) == piecewise_fold(
|
| 316 |
+
Piecewise((1, Eq(i, 2)), (0, True)) +
|
| 317 |
+
Piecewise((1, Eq(j, 2)), (0, True)))
|
| 318 |
+
assert ds(KD(i, k) + KD(j, k), (k, 3, 3)) == piecewise_fold(
|
| 319 |
+
Piecewise((1, Eq(i, 3)), (0, True)) +
|
| 320 |
+
Piecewise((1, Eq(j, 3)), (0, True)))
|
| 321 |
+
assert ds(KD(i, k) + KD(j, k), (k, 1, l)) == piecewise_fold(
|
| 322 |
+
Piecewise((1, And(1 <= i, i <= l)), (0, True)) +
|
| 323 |
+
Piecewise((1, And(1 <= j, j <= l)), (0, True)))
|
| 324 |
+
assert ds(KD(i, k) + KD(j, k), (k, l, 3)) == piecewise_fold(
|
| 325 |
+
Piecewise((1, And(l <= i, i <= 3)), (0, True)) +
|
| 326 |
+
Piecewise((1, And(l <= j, j <= 3)), (0, True)))
|
| 327 |
+
assert ds(KD(i, k) + KD(j, k), (k, l, m)) == piecewise_fold(
|
| 328 |
+
Piecewise((1, And(l <= i, i <= m)), (0, True)) +
|
| 329 |
+
Piecewise((1, And(l <= j, j <= m)), (0, True)))
|
| 330 |
+
|
| 331 |
+
|
| 332 |
+
def test_deltasummation_add_mul_x_kd_kd():
|
| 333 |
+
assert ds(x*KD(i, k) + KD(j, k), (k, 1, 3)) == piecewise_fold(
|
| 334 |
+
Piecewise((x, And(1 <= i, i <= 3)), (0, True)) +
|
| 335 |
+
Piecewise((1, And(1 <= j, j <= 3)), (0, True)))
|
| 336 |
+
assert ds(x*KD(i, k) + KD(j, k), (k, 1, 1)) == piecewise_fold(
|
| 337 |
+
Piecewise((x, Eq(i, 1)), (0, True)) +
|
| 338 |
+
Piecewise((1, Eq(j, 1)), (0, True)))
|
| 339 |
+
assert ds(x*KD(i, k) + KD(j, k), (k, 2, 2)) == piecewise_fold(
|
| 340 |
+
Piecewise((x, Eq(i, 2)), (0, True)) +
|
| 341 |
+
Piecewise((1, Eq(j, 2)), (0, True)))
|
| 342 |
+
assert ds(x*KD(i, k) + KD(j, k), (k, 3, 3)) == piecewise_fold(
|
| 343 |
+
Piecewise((x, Eq(i, 3)), (0, True)) +
|
| 344 |
+
Piecewise((1, Eq(j, 3)), (0, True)))
|
| 345 |
+
assert ds(x*KD(i, k) + KD(j, k), (k, 1, l)) == piecewise_fold(
|
| 346 |
+
Piecewise((x, And(1 <= i, i <= l)), (0, True)) +
|
| 347 |
+
Piecewise((1, And(1 <= j, j <= l)), (0, True)))
|
| 348 |
+
assert ds(x*KD(i, k) + KD(j, k), (k, l, 3)) == piecewise_fold(
|
| 349 |
+
Piecewise((x, And(l <= i, i <= 3)), (0, True)) +
|
| 350 |
+
Piecewise((1, And(l <= j, j <= 3)), (0, True)))
|
| 351 |
+
assert ds(x*KD(i, k) + KD(j, k), (k, l, m)) == piecewise_fold(
|
| 352 |
+
Piecewise((x, And(l <= i, i <= m)), (0, True)) +
|
| 353 |
+
Piecewise((1, And(l <= j, j <= m)), (0, True)))
|
| 354 |
+
|
| 355 |
+
|
| 356 |
+
def test_deltasummation_mul_x_add_kd_kd():
|
| 357 |
+
assert ds(x*(KD(i, k) + KD(j, k)), (k, 1, 3)) == piecewise_fold(
|
| 358 |
+
Piecewise((x, And(1 <= i, i <= 3)), (0, True)) +
|
| 359 |
+
Piecewise((x, And(1 <= j, j <= 3)), (0, True)))
|
| 360 |
+
assert ds(x*(KD(i, k) + KD(j, k)), (k, 1, 1)) == piecewise_fold(
|
| 361 |
+
Piecewise((x, Eq(i, 1)), (0, True)) +
|
| 362 |
+
Piecewise((x, Eq(j, 1)), (0, True)))
|
| 363 |
+
assert ds(x*(KD(i, k) + KD(j, k)), (k, 2, 2)) == piecewise_fold(
|
| 364 |
+
Piecewise((x, Eq(i, 2)), (0, True)) +
|
| 365 |
+
Piecewise((x, Eq(j, 2)), (0, True)))
|
| 366 |
+
assert ds(x*(KD(i, k) + KD(j, k)), (k, 3, 3)) == piecewise_fold(
|
| 367 |
+
Piecewise((x, Eq(i, 3)), (0, True)) +
|
| 368 |
+
Piecewise((x, Eq(j, 3)), (0, True)))
|
| 369 |
+
assert ds(x*(KD(i, k) + KD(j, k)), (k, 1, l)) == piecewise_fold(
|
| 370 |
+
Piecewise((x, And(1 <= i, i <= l)), (0, True)) +
|
| 371 |
+
Piecewise((x, And(1 <= j, j <= l)), (0, True)))
|
| 372 |
+
assert ds(x*(KD(i, k) + KD(j, k)), (k, l, 3)) == piecewise_fold(
|
| 373 |
+
Piecewise((x, And(l <= i, i <= 3)), (0, True)) +
|
| 374 |
+
Piecewise((x, And(l <= j, j <= 3)), (0, True)))
|
| 375 |
+
assert ds(x*(KD(i, k) + KD(j, k)), (k, l, m)) == piecewise_fold(
|
| 376 |
+
Piecewise((x, And(l <= i, i <= m)), (0, True)) +
|
| 377 |
+
Piecewise((x, And(l <= j, j <= m)), (0, True)))
|
| 378 |
+
|
| 379 |
+
|
| 380 |
+
def test_deltasummation_mul_add_x_y_add_kd_kd():
|
| 381 |
+
assert ds((x + y)*(KD(i, k) + KD(j, k)), (k, 1, 3)) == piecewise_fold(
|
| 382 |
+
Piecewise((x + y, And(1 <= i, i <= 3)), (0, True)) +
|
| 383 |
+
Piecewise((x + y, And(1 <= j, j <= 3)), (0, True)))
|
| 384 |
+
assert ds((x + y)*(KD(i, k) + KD(j, k)), (k, 1, 1)) == piecewise_fold(
|
| 385 |
+
Piecewise((x + y, Eq(i, 1)), (0, True)) +
|
| 386 |
+
Piecewise((x + y, Eq(j, 1)), (0, True)))
|
| 387 |
+
assert ds((x + y)*(KD(i, k) + KD(j, k)), (k, 2, 2)) == piecewise_fold(
|
| 388 |
+
Piecewise((x + y, Eq(i, 2)), (0, True)) +
|
| 389 |
+
Piecewise((x + y, Eq(j, 2)), (0, True)))
|
| 390 |
+
assert ds((x + y)*(KD(i, k) + KD(j, k)), (k, 3, 3)) == piecewise_fold(
|
| 391 |
+
Piecewise((x + y, Eq(i, 3)), (0, True)) +
|
| 392 |
+
Piecewise((x + y, Eq(j, 3)), (0, True)))
|
| 393 |
+
assert ds((x + y)*(KD(i, k) + KD(j, k)), (k, 1, l)) == piecewise_fold(
|
| 394 |
+
Piecewise((x + y, And(1 <= i, i <= l)), (0, True)) +
|
| 395 |
+
Piecewise((x + y, And(1 <= j, j <= l)), (0, True)))
|
| 396 |
+
assert ds((x + y)*(KD(i, k) + KD(j, k)), (k, l, 3)) == piecewise_fold(
|
| 397 |
+
Piecewise((x + y, And(l <= i, i <= 3)), (0, True)) +
|
| 398 |
+
Piecewise((x + y, And(l <= j, j <= 3)), (0, True)))
|
| 399 |
+
assert ds((x + y)*(KD(i, k) + KD(j, k)), (k, l, m)) == piecewise_fold(
|
| 400 |
+
Piecewise((x + y, And(l <= i, i <= m)), (0, True)) +
|
| 401 |
+
Piecewise((x + y, And(l <= j, j <= m)), (0, True)))
|
| 402 |
+
|
| 403 |
+
|
| 404 |
+
def test_deltasummation_add_mul_x_y_mul_x_kd():
|
| 405 |
+
assert ds(x*y + x*KD(i, j), (j, 1, 3)) == \
|
| 406 |
+
Piecewise((3*x*y + x, And(1 <= i, i <= 3)), (3*x*y, True))
|
| 407 |
+
assert ds(x*y + x*KD(i, j), (j, 1, 1)) == \
|
| 408 |
+
Piecewise((x*y + x, Eq(i, 1)), (x*y, True))
|
| 409 |
+
assert ds(x*y + x*KD(i, j), (j, 2, 2)) == \
|
| 410 |
+
Piecewise((x*y + x, Eq(i, 2)), (x*y, True))
|
| 411 |
+
assert ds(x*y + x*KD(i, j), (j, 3, 3)) == \
|
| 412 |
+
Piecewise((x*y + x, Eq(i, 3)), (x*y, True))
|
| 413 |
+
assert ds(x*y + x*KD(i, j), (j, 1, k)) == \
|
| 414 |
+
Piecewise((k*x*y + x, And(1 <= i, i <= k)), (k*x*y, True))
|
| 415 |
+
assert ds(x*y + x*KD(i, j), (j, k, 3)) == \
|
| 416 |
+
Piecewise(((4 - k)*x*y + x, And(k <= i, i <= 3)), ((4 - k)*x*y, True))
|
| 417 |
+
assert ds(x*y + x*KD(i, j), (j, k, l)) == Piecewise(
|
| 418 |
+
((l - k + 1)*x*y + x, And(k <= i, i <= l)), ((l - k + 1)*x*y, True))
|
| 419 |
+
|
| 420 |
+
|
| 421 |
+
def test_deltasummation_mul_x_add_y_kd():
|
| 422 |
+
assert ds(x*(y + KD(i, j)), (j, 1, 3)) == \
|
| 423 |
+
Piecewise((3*x*y + x, And(1 <= i, i <= 3)), (3*x*y, True))
|
| 424 |
+
assert ds(x*(y + KD(i, j)), (j, 1, 1)) == \
|
| 425 |
+
Piecewise((x*y + x, Eq(i, 1)), (x*y, True))
|
| 426 |
+
assert ds(x*(y + KD(i, j)), (j, 2, 2)) == \
|
| 427 |
+
Piecewise((x*y + x, Eq(i, 2)), (x*y, True))
|
| 428 |
+
assert ds(x*(y + KD(i, j)), (j, 3, 3)) == \
|
| 429 |
+
Piecewise((x*y + x, Eq(i, 3)), (x*y, True))
|
| 430 |
+
assert ds(x*(y + KD(i, j)), (j, 1, k)) == \
|
| 431 |
+
Piecewise((k*x*y + x, And(1 <= i, i <= k)), (k*x*y, True))
|
| 432 |
+
assert ds(x*(y + KD(i, j)), (j, k, 3)) == \
|
| 433 |
+
Piecewise(((4 - k)*x*y + x, And(k <= i, i <= 3)), ((4 - k)*x*y, True))
|
| 434 |
+
assert ds(x*(y + KD(i, j)), (j, k, l)) == Piecewise(
|
| 435 |
+
((l - k + 1)*x*y + x, And(k <= i, i <= l)), ((l - k + 1)*x*y, True))
|
| 436 |
+
|
| 437 |
+
|
| 438 |
+
def test_deltasummation_mul_x_add_y_twokd():
|
| 439 |
+
assert ds(x*(y + 2*KD(i, j)), (j, 1, 3)) == \
|
| 440 |
+
Piecewise((3*x*y + 2*x, And(1 <= i, i <= 3)), (3*x*y, True))
|
| 441 |
+
assert ds(x*(y + 2*KD(i, j)), (j, 1, 1)) == \
|
| 442 |
+
Piecewise((x*y + 2*x, Eq(i, 1)), (x*y, True))
|
| 443 |
+
assert ds(x*(y + 2*KD(i, j)), (j, 2, 2)) == \
|
| 444 |
+
Piecewise((x*y + 2*x, Eq(i, 2)), (x*y, True))
|
| 445 |
+
assert ds(x*(y + 2*KD(i, j)), (j, 3, 3)) == \
|
| 446 |
+
Piecewise((x*y + 2*x, Eq(i, 3)), (x*y, True))
|
| 447 |
+
assert ds(x*(y + 2*KD(i, j)), (j, 1, k)) == \
|
| 448 |
+
Piecewise((k*x*y + 2*x, And(1 <= i, i <= k)), (k*x*y, True))
|
| 449 |
+
assert ds(x*(y + 2*KD(i, j)), (j, k, 3)) == Piecewise(
|
| 450 |
+
((4 - k)*x*y + 2*x, And(k <= i, i <= 3)), ((4 - k)*x*y, True))
|
| 451 |
+
assert ds(x*(y + 2*KD(i, j)), (j, k, l)) == Piecewise(
|
| 452 |
+
((l - k + 1)*x*y + 2*x, And(k <= i, i <= l)), ((l - k + 1)*x*y, True))
|
| 453 |
+
|
| 454 |
+
|
| 455 |
+
def test_deltasummation_mul_add_x_y_add_y_kd():
|
| 456 |
+
assert ds((x + y)*(y + KD(i, j)), (j, 1, 3)) == Piecewise(
|
| 457 |
+
(3*(x + y)*y + x + y, And(1 <= i, i <= 3)), (3*(x + y)*y, True))
|
| 458 |
+
assert ds((x + y)*(y + KD(i, j)), (j, 1, 1)) == \
|
| 459 |
+
Piecewise(((x + y)*y + x + y, Eq(i, 1)), ((x + y)*y, True))
|
| 460 |
+
assert ds((x + y)*(y + KD(i, j)), (j, 2, 2)) == \
|
| 461 |
+
Piecewise(((x + y)*y + x + y, Eq(i, 2)), ((x + y)*y, True))
|
| 462 |
+
assert ds((x + y)*(y + KD(i, j)), (j, 3, 3)) == \
|
| 463 |
+
Piecewise(((x + y)*y + x + y, Eq(i, 3)), ((x + y)*y, True))
|
| 464 |
+
assert ds((x + y)*(y + KD(i, j)), (j, 1, k)) == Piecewise(
|
| 465 |
+
(k*(x + y)*y + x + y, And(1 <= i, i <= k)), (k*(x + y)*y, True))
|
| 466 |
+
assert ds((x + y)*(y + KD(i, j)), (j, k, 3)) == Piecewise(
|
| 467 |
+
((4 - k)*(x + y)*y + x + y, And(k <= i, i <= 3)),
|
| 468 |
+
((4 - k)*(x + y)*y, True))
|
| 469 |
+
assert ds((x + y)*(y + KD(i, j)), (j, k, l)) == Piecewise(
|
| 470 |
+
((l - k + 1)*(x + y)*y + x + y, And(k <= i, i <= l)),
|
| 471 |
+
((l - k + 1)*(x + y)*y, True))
|
| 472 |
+
|
| 473 |
+
|
| 474 |
+
def test_deltasummation_mul_add_x_kd_add_y_kd():
|
| 475 |
+
assert ds((x + KD(i, k))*(y + KD(i, j)), (j, 1, 3)) == piecewise_fold(
|
| 476 |
+
Piecewise((KD(i, k) + x, And(1 <= i, i <= 3)), (0, True)) +
|
| 477 |
+
3*(KD(i, k) + x)*y)
|
| 478 |
+
assert ds((x + KD(i, k))*(y + KD(i, j)), (j, 1, 1)) == piecewise_fold(
|
| 479 |
+
Piecewise((KD(i, k) + x, Eq(i, 1)), (0, True)) +
|
| 480 |
+
(KD(i, k) + x)*y)
|
| 481 |
+
assert ds((x + KD(i, k))*(y + KD(i, j)), (j, 2, 2)) == piecewise_fold(
|
| 482 |
+
Piecewise((KD(i, k) + x, Eq(i, 2)), (0, True)) +
|
| 483 |
+
(KD(i, k) + x)*y)
|
| 484 |
+
assert ds((x + KD(i, k))*(y + KD(i, j)), (j, 3, 3)) == piecewise_fold(
|
| 485 |
+
Piecewise((KD(i, k) + x, Eq(i, 3)), (0, True)) +
|
| 486 |
+
(KD(i, k) + x)*y)
|
| 487 |
+
assert ds((x + KD(i, k))*(y + KD(i, j)), (j, 1, k)) == piecewise_fold(
|
| 488 |
+
Piecewise((KD(i, k) + x, And(1 <= i, i <= k)), (0, True)) +
|
| 489 |
+
k*(KD(i, k) + x)*y)
|
| 490 |
+
assert ds((x + KD(i, k))*(y + KD(i, j)), (j, k, 3)) == piecewise_fold(
|
| 491 |
+
Piecewise((KD(i, k) + x, And(k <= i, i <= 3)), (0, True)) +
|
| 492 |
+
(4 - k)*(KD(i, k) + x)*y)
|
| 493 |
+
assert ds((x + KD(i, k))*(y + KD(i, j)), (j, k, l)) == piecewise_fold(
|
| 494 |
+
Piecewise((KD(i, k) + x, And(k <= i, i <= l)), (0, True)) +
|
| 495 |
+
(l - k + 1)*(KD(i, k) + x)*y)
|
| 496 |
+
|
| 497 |
+
|
| 498 |
+
def test_extract_delta():
|
| 499 |
+
raises(ValueError, lambda: _extract_delta(KD(i, j) + KD(k, l), i))
|
.venv/lib/python3.13/site-packages/sympy/concrete/tests/test_gosper.py
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Tests for Gosper's algorithm for hypergeometric summation. """
|
| 2 |
+
|
| 3 |
+
from sympy.core.numbers import (Rational, pi)
|
| 4 |
+
from sympy.core.singleton import S
|
| 5 |
+
from sympy.core.symbol import Symbol
|
| 6 |
+
from sympy.functions.combinatorial.factorials import (binomial, factorial)
|
| 7 |
+
from sympy.functions.elementary.exponential import (exp, log)
|
| 8 |
+
from sympy.functions.elementary.miscellaneous import sqrt
|
| 9 |
+
from sympy.functions.special.gamma_functions import gamma
|
| 10 |
+
from sympy.polys.polytools import Poly
|
| 11 |
+
from sympy.simplify.simplify import simplify
|
| 12 |
+
from sympy.concrete.gosper import gosper_normal, gosper_sum, gosper_term
|
| 13 |
+
from sympy.abc import a, b, j, k, m, n, r, x
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def test_gosper_normal():
|
| 17 |
+
eq = 4*n + 5, 2*(4*n + 1)*(2*n + 3), n
|
| 18 |
+
assert gosper_normal(*eq) == \
|
| 19 |
+
(Poly(Rational(1, 4), n), Poly(n + Rational(3, 2)), Poly(n + Rational(1, 4)))
|
| 20 |
+
assert gosper_normal(*eq, polys=False) == \
|
| 21 |
+
(Rational(1, 4), n + Rational(3, 2), n + Rational(1, 4))
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
def test_gosper_term():
|
| 25 |
+
assert gosper_term((4*k + 1)*factorial(
|
| 26 |
+
k)/factorial(2*k + 1), k) == (-k - S.Half)/(k + Rational(1, 4))
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def test_gosper_sum():
|
| 30 |
+
assert gosper_sum(1, (k, 0, n)) == 1 + n
|
| 31 |
+
assert gosper_sum(k, (k, 0, n)) == n*(1 + n)/2
|
| 32 |
+
assert gosper_sum(k**2, (k, 0, n)) == n*(1 + n)*(1 + 2*n)/6
|
| 33 |
+
assert gosper_sum(k**3, (k, 0, n)) == n**2*(1 + n)**2/4
|
| 34 |
+
|
| 35 |
+
assert gosper_sum(2**k, (k, 0, n)) == 2*2**n - 1
|
| 36 |
+
|
| 37 |
+
assert gosper_sum(factorial(k), (k, 0, n)) is None
|
| 38 |
+
assert gosper_sum(binomial(n, k), (k, 0, n)) is None
|
| 39 |
+
|
| 40 |
+
assert gosper_sum(factorial(k)/k**2, (k, 0, n)) is None
|
| 41 |
+
assert gosper_sum((k - 3)*factorial(k), (k, 0, n)) is None
|
| 42 |
+
|
| 43 |
+
assert gosper_sum(k*factorial(k), k) == factorial(k)
|
| 44 |
+
assert gosper_sum(
|
| 45 |
+
k*factorial(k), (k, 0, n)) == n*factorial(n) + factorial(n) - 1
|
| 46 |
+
|
| 47 |
+
assert gosper_sum((-1)**k*binomial(n, k), (k, 0, n)) == 0
|
| 48 |
+
assert gosper_sum((
|
| 49 |
+
-1)**k*binomial(n, k), (k, 0, m)) == -(-1)**m*(m - n)*binomial(n, m)/n
|
| 50 |
+
|
| 51 |
+
assert gosper_sum((4*k + 1)*factorial(k)/factorial(2*k + 1), (k, 0, n)) == \
|
| 52 |
+
(2*factorial(2*n + 1) - factorial(n))/factorial(2*n + 1)
|
| 53 |
+
|
| 54 |
+
# issue 6033:
|
| 55 |
+
assert gosper_sum(
|
| 56 |
+
n*(n + a + b)*a**n*b**n/(factorial(n + a)*factorial(n + b)), \
|
| 57 |
+
(n, 0, m)).simplify() == -exp(m*log(a) + m*log(b))*gamma(a + 1) \
|
| 58 |
+
*gamma(b + 1)/(gamma(a)*gamma(b)*gamma(a + m + 1)*gamma(b + m + 1)) \
|
| 59 |
+
+ 1/(gamma(a)*gamma(b))
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
def test_gosper_sum_indefinite():
|
| 63 |
+
assert gosper_sum(k, k) == k*(k - 1)/2
|
| 64 |
+
assert gosper_sum(k**2, k) == k*(k - 1)*(2*k - 1)/6
|
| 65 |
+
|
| 66 |
+
assert gosper_sum(1/(k*(k + 1)), k) == -1/k
|
| 67 |
+
assert gosper_sum(-(27*k**4 + 158*k**3 + 430*k**2 + 678*k + 445)*gamma(2*k
|
| 68 |
+
+ 4)/(3*(3*k + 7)*gamma(3*k + 6)), k) == \
|
| 69 |
+
(3*k + 5)*(k**2 + 2*k + 5)*gamma(2*k + 4)/gamma(3*k + 6)
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def test_gosper_sum_parametric():
|
| 73 |
+
assert gosper_sum(binomial(S.Half, m - j + 1)*binomial(S.Half, m + j), (j, 1, n)) == \
|
| 74 |
+
n*(1 + m - n)*(-1 + 2*m + 2*n)*binomial(S.Half, 1 + m - n)* \
|
| 75 |
+
binomial(S.Half, m + n)/(m*(1 + 2*m))
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
def test_gosper_sum_algebraic():
|
| 79 |
+
assert gosper_sum(
|
| 80 |
+
n**2 + sqrt(2), (n, 0, m)) == (m + 1)*(2*m**2 + m + 6*sqrt(2))/6
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
def test_gosper_sum_iterated():
|
| 84 |
+
f1 = binomial(2*k, k)/4**k
|
| 85 |
+
f2 = (1 + 2*n)*binomial(2*n, n)/4**n
|
| 86 |
+
f3 = (1 + 2*n)*(3 + 2*n)*binomial(2*n, n)/(3*4**n)
|
| 87 |
+
f4 = (1 + 2*n)*(3 + 2*n)*(5 + 2*n)*binomial(2*n, n)/(15*4**n)
|
| 88 |
+
f5 = (1 + 2*n)*(3 + 2*n)*(5 + 2*n)*(7 + 2*n)*binomial(2*n, n)/(105*4**n)
|
| 89 |
+
|
| 90 |
+
assert gosper_sum(f1, (k, 0, n)) == f2
|
| 91 |
+
assert gosper_sum(f2, (n, 0, n)) == f3
|
| 92 |
+
assert gosper_sum(f3, (n, 0, n)) == f4
|
| 93 |
+
assert gosper_sum(f4, (n, 0, n)) == f5
|
| 94 |
+
|
| 95 |
+
# the AeqB tests test expressions given in
|
| 96 |
+
# www.math.upenn.edu/~wilf/AeqB.pdf
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def test_gosper_sum_AeqB_part1():
|
| 100 |
+
f1a = n**4
|
| 101 |
+
f1b = n**3*2**n
|
| 102 |
+
f1c = 1/(n**2 + sqrt(5)*n - 1)
|
| 103 |
+
f1d = n**4*4**n/binomial(2*n, n)
|
| 104 |
+
f1e = factorial(3*n)/(factorial(n)*factorial(n + 1)*factorial(n + 2)*27**n)
|
| 105 |
+
f1f = binomial(2*n, n)**2/((n + 1)*4**(2*n))
|
| 106 |
+
f1g = (4*n - 1)*binomial(2*n, n)**2/((2*n - 1)**2*4**(2*n))
|
| 107 |
+
f1h = n*factorial(n - S.Half)**2/factorial(n + 1)**2
|
| 108 |
+
|
| 109 |
+
g1a = m*(m + 1)*(2*m + 1)*(3*m**2 + 3*m - 1)/30
|
| 110 |
+
g1b = 26 + 2**(m + 1)*(m**3 - 3*m**2 + 9*m - 13)
|
| 111 |
+
g1c = (m + 1)*(m*(m**2 - 7*m + 3)*sqrt(5) - (
|
| 112 |
+
3*m**3 - 7*m**2 + 19*m - 6))/(2*m**3*sqrt(5) + m**4 + 5*m**2 - 1)/6
|
| 113 |
+
g1d = Rational(-2, 231) + 2*4**m*(m + 1)*(63*m**4 + 112*m**3 + 18*m**2 -
|
| 114 |
+
22*m + 3)/(693*binomial(2*m, m))
|
| 115 |
+
g1e = Rational(-9, 2) + (81*m**2 + 261*m + 200)*factorial(
|
| 116 |
+
3*m + 2)/(40*27**m*factorial(m)*factorial(m + 1)*factorial(m + 2))
|
| 117 |
+
g1f = (2*m + 1)**2*binomial(2*m, m)**2/(4**(2*m)*(m + 1))
|
| 118 |
+
g1g = -binomial(2*m, m)**2/4**(2*m)
|
| 119 |
+
g1h = 4*pi -(2*m + 1)**2*(3*m + 4)*factorial(m - S.Half)**2/factorial(m + 1)**2
|
| 120 |
+
|
| 121 |
+
g = gosper_sum(f1a, (n, 0, m))
|
| 122 |
+
assert g is not None and simplify(g - g1a) == 0
|
| 123 |
+
g = gosper_sum(f1b, (n, 0, m))
|
| 124 |
+
assert g is not None and simplify(g - g1b) == 0
|
| 125 |
+
g = gosper_sum(f1c, (n, 0, m))
|
| 126 |
+
assert g is not None and simplify(g - g1c) == 0
|
| 127 |
+
g = gosper_sum(f1d, (n, 0, m))
|
| 128 |
+
assert g is not None and simplify(g - g1d) == 0
|
| 129 |
+
g = gosper_sum(f1e, (n, 0, m))
|
| 130 |
+
assert g is not None and simplify(g - g1e) == 0
|
| 131 |
+
g = gosper_sum(f1f, (n, 0, m))
|
| 132 |
+
assert g is not None and simplify(g - g1f) == 0
|
| 133 |
+
g = gosper_sum(f1g, (n, 0, m))
|
| 134 |
+
assert g is not None and simplify(g - g1g) == 0
|
| 135 |
+
g = gosper_sum(f1h, (n, 0, m))
|
| 136 |
+
# need to call rewrite(gamma) here because we have terms involving
|
| 137 |
+
# factorial(1/2)
|
| 138 |
+
assert g is not None and simplify(g - g1h).rewrite(gamma) == 0
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
def test_gosper_sum_AeqB_part2():
|
| 142 |
+
f2a = n**2*a**n
|
| 143 |
+
f2b = (n - r/2)*binomial(r, n)
|
| 144 |
+
f2c = factorial(n - 1)**2/(factorial(n - x)*factorial(n + x))
|
| 145 |
+
|
| 146 |
+
g2a = -a*(a + 1)/(a - 1)**3 + a**(
|
| 147 |
+
m + 1)*(a**2*m**2 - 2*a*m**2 + m**2 - 2*a*m + 2*m + a + 1)/(a - 1)**3
|
| 148 |
+
g2b = (m - r)*binomial(r, m)/2
|
| 149 |
+
ff = factorial(1 - x)*factorial(1 + x)
|
| 150 |
+
g2c = 1/ff*(
|
| 151 |
+
1 - 1/x**2) + factorial(m)**2/(x**2*factorial(m - x)*factorial(m + x))
|
| 152 |
+
|
| 153 |
+
g = gosper_sum(f2a, (n, 0, m))
|
| 154 |
+
assert g is not None and simplify(g - g2a) == 0
|
| 155 |
+
g = gosper_sum(f2b, (n, 0, m))
|
| 156 |
+
assert g is not None and simplify(g - g2b) == 0
|
| 157 |
+
g = gosper_sum(f2c, (n, 1, m))
|
| 158 |
+
assert g is not None and simplify(g - g2c) == 0
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
def test_gosper_nan():
|
| 162 |
+
a = Symbol('a', positive=True)
|
| 163 |
+
b = Symbol('b', positive=True)
|
| 164 |
+
n = Symbol('n', integer=True)
|
| 165 |
+
m = Symbol('m', integer=True)
|
| 166 |
+
f2d = n*(n + a + b)*a**n*b**n/(factorial(n + a)*factorial(n + b))
|
| 167 |
+
g2d = 1/(factorial(a - 1)*factorial(
|
| 168 |
+
b - 1)) - a**(m + 1)*b**(m + 1)/(factorial(a + m)*factorial(b + m))
|
| 169 |
+
g = gosper_sum(f2d, (n, 0, m))
|
| 170 |
+
assert simplify(g - g2d) == 0
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
def test_gosper_sum_AeqB_part3():
|
| 174 |
+
f3a = 1/n**4
|
| 175 |
+
f3b = (6*n + 3)/(4*n**4 + 8*n**3 + 8*n**2 + 4*n + 3)
|
| 176 |
+
f3c = 2**n*(n**2 - 2*n - 1)/(n**2*(n + 1)**2)
|
| 177 |
+
f3d = n**2*4**n/((n + 1)*(n + 2))
|
| 178 |
+
f3e = 2**n/(n + 1)
|
| 179 |
+
f3f = 4*(n - 1)*(n**2 - 2*n - 1)/(n**2*(n + 1)**2*(n - 2)**2*(n - 3)**2)
|
| 180 |
+
f3g = (n**4 - 14*n**2 - 24*n - 9)*2**n/(n**2*(n + 1)**2*(n + 2)**2*
|
| 181 |
+
(n + 3)**2)
|
| 182 |
+
|
| 183 |
+
# g3a -> no closed form
|
| 184 |
+
g3b = m*(m + 2)/(2*m**2 + 4*m + 3)
|
| 185 |
+
g3c = 2**m/m**2 - 2
|
| 186 |
+
g3d = Rational(2, 3) + 4**(m + 1)*(m - 1)/(m + 2)/3
|
| 187 |
+
# g3e -> no closed form
|
| 188 |
+
g3f = -(Rational(-1, 16) + 1/((m - 2)**2*(m + 1)**2)) # the AeqB key is wrong
|
| 189 |
+
g3g = Rational(-2, 9) + 2**(m + 1)/((m + 1)**2*(m + 3)**2)
|
| 190 |
+
|
| 191 |
+
g = gosper_sum(f3a, (n, 1, m))
|
| 192 |
+
assert g is None
|
| 193 |
+
g = gosper_sum(f3b, (n, 1, m))
|
| 194 |
+
assert g is not None and simplify(g - g3b) == 0
|
| 195 |
+
g = gosper_sum(f3c, (n, 1, m - 1))
|
| 196 |
+
assert g is not None and simplify(g - g3c) == 0
|
| 197 |
+
g = gosper_sum(f3d, (n, 1, m))
|
| 198 |
+
assert g is not None and simplify(g - g3d) == 0
|
| 199 |
+
g = gosper_sum(f3e, (n, 0, m - 1))
|
| 200 |
+
assert g is None
|
| 201 |
+
g = gosper_sum(f3f, (n, 4, m))
|
| 202 |
+
assert g is not None and simplify(g - g3f) == 0
|
| 203 |
+
g = gosper_sum(f3g, (n, 1, m))
|
| 204 |
+
assert g is not None and simplify(g - g3g) == 0
|
.venv/lib/python3.13/site-packages/sympy/concrete/tests/test_guess.py
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.concrete.guess import (
|
| 2 |
+
find_simple_recurrence_vector,
|
| 3 |
+
find_simple_recurrence,
|
| 4 |
+
rationalize,
|
| 5 |
+
guess_generating_function_rational,
|
| 6 |
+
guess_generating_function,
|
| 7 |
+
guess
|
| 8 |
+
)
|
| 9 |
+
from sympy.concrete.products import Product
|
| 10 |
+
from sympy.core.function import Function
|
| 11 |
+
from sympy.core.numbers import Rational
|
| 12 |
+
from sympy.core.singleton import S
|
| 13 |
+
from sympy.core.symbol import (Symbol, symbols)
|
| 14 |
+
from sympy.core.sympify import sympify
|
| 15 |
+
from sympy.functions.combinatorial.factorials import (RisingFactorial, factorial)
|
| 16 |
+
from sympy.functions.combinatorial.numbers import fibonacci
|
| 17 |
+
from sympy.functions.elementary.exponential import exp
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def test_find_simple_recurrence_vector():
|
| 21 |
+
assert find_simple_recurrence_vector(
|
| 22 |
+
[fibonacci(k) for k in range(12)]) == [1, -1, -1]
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def test_find_simple_recurrence():
|
| 26 |
+
a = Function('a')
|
| 27 |
+
n = Symbol('n')
|
| 28 |
+
assert find_simple_recurrence([fibonacci(k) for k in range(12)]) == (
|
| 29 |
+
-a(n) - a(n + 1) + a(n + 2))
|
| 30 |
+
|
| 31 |
+
f = Function('a')
|
| 32 |
+
i = Symbol('n')
|
| 33 |
+
a = [1, 1, 1]
|
| 34 |
+
for k in range(15): a.append(5*a[-1]-3*a[-2]+8*a[-3])
|
| 35 |
+
assert find_simple_recurrence(a, A=f, N=i) == (
|
| 36 |
+
-8*f(i) + 3*f(i + 1) - 5*f(i + 2) + f(i + 3))
|
| 37 |
+
assert find_simple_recurrence([0, 2, 15, 74, 12, 3, 0,
|
| 38 |
+
1, 2, 85, 4, 5, 63]) == 0
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def test_rationalize():
|
| 42 |
+
from mpmath import cos, pi, mpf
|
| 43 |
+
assert rationalize(cos(pi/3)) == S.Half
|
| 44 |
+
assert rationalize(mpf("0.333333333333333")) == Rational(1, 3)
|
| 45 |
+
assert rationalize(mpf("-0.333333333333333")) == Rational(-1, 3)
|
| 46 |
+
assert rationalize(pi, maxcoeff = 250) == Rational(355, 113)
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def test_guess_generating_function_rational():
|
| 50 |
+
x = Symbol('x')
|
| 51 |
+
assert guess_generating_function_rational([fibonacci(k)
|
| 52 |
+
for k in range(5, 15)]) == ((3*x + 5)/(-x**2 - x + 1))
|
| 53 |
+
|
| 54 |
+
|
| 55 |
+
def test_guess_generating_function():
|
| 56 |
+
x = Symbol('x')
|
| 57 |
+
assert guess_generating_function([fibonacci(k)
|
| 58 |
+
for k in range(5, 15)])['ogf'] == ((3*x + 5)/(-x**2 - x + 1))
|
| 59 |
+
assert guess_generating_function(
|
| 60 |
+
[1, 2, 5, 14, 41, 124, 383, 1200, 3799, 12122, 38919])['ogf'] == (
|
| 61 |
+
(1/(x**4 + 2*x**2 - 4*x + 1))**S.Half)
|
| 62 |
+
assert guess_generating_function(sympify(
|
| 63 |
+
"[3/2, 11/2, 0, -121/2, -363/2, 121, 4719/2, 11495/2, -8712, -178717/2]")
|
| 64 |
+
)['ogf'] == (x + Rational(3, 2))/(11*x**2 - 3*x + 1)
|
| 65 |
+
assert guess_generating_function([factorial(k) for k in range(12)],
|
| 66 |
+
types=['egf'])['egf'] == 1/(-x + 1)
|
| 67 |
+
assert guess_generating_function([k+1 for k in range(12)],
|
| 68 |
+
types=['egf']) == {'egf': (x + 1)*exp(x), 'lgdegf': (x + 2)/(x + 1)}
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
def test_guess():
|
| 72 |
+
i0, i1 = symbols('i0 i1')
|
| 73 |
+
assert guess([1, 2, 6, 24, 120], evaluate=False) == [Product(i1 + 1, (i1, 1, i0 - 1))]
|
| 74 |
+
assert guess([1, 2, 6, 24, 120]) == [RisingFactorial(2, i0 - 1)]
|
| 75 |
+
assert guess([1, 2, 7, 42, 429, 7436, 218348, 10850216], niter=4) == [
|
| 76 |
+
2**(i0 - 1)*(Rational(27, 16))**(i0**2/2 - 3*i0/2 +
|
| 77 |
+
1)*Product(RisingFactorial(Rational(5, 3), i1 - 1)*RisingFactorial(Rational(7, 3), i1
|
| 78 |
+
- 1)/(RisingFactorial(Rational(3, 2), i1 - 1)*RisingFactorial(Rational(5, 2), i1 -
|
| 79 |
+
1)), (i1, 1, i0 - 1))]
|
| 80 |
+
assert guess([1, 0, 2]) == []
|
| 81 |
+
x, y = symbols('x y')
|
| 82 |
+
assert guess([1, 2, 6, 24, 120], variables=[x, y]) == [RisingFactorial(2, x - 1)]
|
.venv/lib/python3.13/site-packages/sympy/concrete/tests/test_products.py
ADDED
|
@@ -0,0 +1,410 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.concrete.products import (Product, product)
|
| 2 |
+
from sympy.concrete.summations import Sum
|
| 3 |
+
from sympy.core.function import (Derivative, Function, diff)
|
| 4 |
+
from sympy.core.numbers import (Rational, oo, pi)
|
| 5 |
+
from sympy.core.singleton import S
|
| 6 |
+
from sympy.core.symbol import (Dummy, Symbol, symbols)
|
| 7 |
+
from sympy.functions.combinatorial.factorials import (rf, factorial)
|
| 8 |
+
from sympy.functions.elementary.exponential import (exp, log)
|
| 9 |
+
from sympy.functions.elementary.miscellaneous import sqrt
|
| 10 |
+
from sympy.functions.elementary.trigonometric import (cos, sin)
|
| 11 |
+
from sympy.functions.special.tensor_functions import KroneckerDelta
|
| 12 |
+
from sympy.simplify.combsimp import combsimp
|
| 13 |
+
from sympy.simplify.simplify import simplify
|
| 14 |
+
from sympy.testing.pytest import raises
|
| 15 |
+
|
| 16 |
+
a, k, n, m, x = symbols('a,k,n,m,x', integer=True)
|
| 17 |
+
f = Function('f')
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def test_karr_convention():
|
| 21 |
+
# Test the Karr product convention that we want to hold.
|
| 22 |
+
# See his paper "Summation in Finite Terms" for a detailed
|
| 23 |
+
# reasoning why we really want exactly this definition.
|
| 24 |
+
# The convention is described for sums on page 309 and
|
| 25 |
+
# essentially in section 1.4, definition 3. For products
|
| 26 |
+
# we can find in analogy:
|
| 27 |
+
#
|
| 28 |
+
# \prod_{m <= i < n} f(i) 'has the obvious meaning' for m < n
|
| 29 |
+
# \prod_{m <= i < n} f(i) = 0 for m = n
|
| 30 |
+
# \prod_{m <= i < n} f(i) = 1 / \prod_{n <= i < m} f(i) for m > n
|
| 31 |
+
#
|
| 32 |
+
# It is important to note that he defines all products with
|
| 33 |
+
# the upper limit being *exclusive*.
|
| 34 |
+
# In contrast, SymPy and the usual mathematical notation has:
|
| 35 |
+
#
|
| 36 |
+
# prod_{i = a}^b f(i) = f(a) * f(a+1) * ... * f(b-1) * f(b)
|
| 37 |
+
#
|
| 38 |
+
# with the upper limit *inclusive*. So translating between
|
| 39 |
+
# the two we find that:
|
| 40 |
+
#
|
| 41 |
+
# \prod_{m <= i < n} f(i) = \prod_{i = m}^{n-1} f(i)
|
| 42 |
+
#
|
| 43 |
+
# where we intentionally used two different ways to typeset the
|
| 44 |
+
# products and its limits.
|
| 45 |
+
|
| 46 |
+
i = Symbol("i", integer=True)
|
| 47 |
+
k = Symbol("k", integer=True)
|
| 48 |
+
j = Symbol("j", integer=True, positive=True)
|
| 49 |
+
|
| 50 |
+
# A simple example with a concrete factors and symbolic limits.
|
| 51 |
+
|
| 52 |
+
# The normal product: m = k and n = k + j and therefore m < n:
|
| 53 |
+
m = k
|
| 54 |
+
n = k + j
|
| 55 |
+
|
| 56 |
+
a = m
|
| 57 |
+
b = n - 1
|
| 58 |
+
S1 = Product(i**2, (i, a, b)).doit()
|
| 59 |
+
|
| 60 |
+
# The reversed product: m = k + j and n = k and therefore m > n:
|
| 61 |
+
m = k + j
|
| 62 |
+
n = k
|
| 63 |
+
|
| 64 |
+
a = m
|
| 65 |
+
b = n - 1
|
| 66 |
+
S2 = Product(i**2, (i, a, b)).doit()
|
| 67 |
+
|
| 68 |
+
assert S1 * S2 == 1
|
| 69 |
+
|
| 70 |
+
# Test the empty product: m = k and n = k and therefore m = n:
|
| 71 |
+
m = k
|
| 72 |
+
n = k
|
| 73 |
+
|
| 74 |
+
a = m
|
| 75 |
+
b = n - 1
|
| 76 |
+
Sz = Product(i**2, (i, a, b)).doit()
|
| 77 |
+
|
| 78 |
+
assert Sz == 1
|
| 79 |
+
|
| 80 |
+
# Another example this time with an unspecified factor and
|
| 81 |
+
# numeric limits. (We can not do both tests in the same example.)
|
| 82 |
+
f = Function("f")
|
| 83 |
+
|
| 84 |
+
# The normal product with m < n:
|
| 85 |
+
m = 2
|
| 86 |
+
n = 11
|
| 87 |
+
|
| 88 |
+
a = m
|
| 89 |
+
b = n - 1
|
| 90 |
+
S1 = Product(f(i), (i, a, b)).doit()
|
| 91 |
+
|
| 92 |
+
# The reversed product with m > n:
|
| 93 |
+
m = 11
|
| 94 |
+
n = 2
|
| 95 |
+
|
| 96 |
+
a = m
|
| 97 |
+
b = n - 1
|
| 98 |
+
S2 = Product(f(i), (i, a, b)).doit()
|
| 99 |
+
|
| 100 |
+
assert simplify(S1 * S2) == 1
|
| 101 |
+
|
| 102 |
+
# Test the empty product with m = n:
|
| 103 |
+
m = 5
|
| 104 |
+
n = 5
|
| 105 |
+
|
| 106 |
+
a = m
|
| 107 |
+
b = n - 1
|
| 108 |
+
Sz = Product(f(i), (i, a, b)).doit()
|
| 109 |
+
|
| 110 |
+
assert Sz == 1
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
def test_karr_proposition_2a():
|
| 114 |
+
# Test Karr, page 309, proposition 2, part a
|
| 115 |
+
i, u, v = symbols('i u v', integer=True)
|
| 116 |
+
|
| 117 |
+
def test_the_product(m, n):
|
| 118 |
+
# g
|
| 119 |
+
g = i**3 + 2*i**2 - 3*i
|
| 120 |
+
# f = Delta g
|
| 121 |
+
f = simplify(g.subs(i, i+1) / g)
|
| 122 |
+
# The product
|
| 123 |
+
a = m
|
| 124 |
+
b = n - 1
|
| 125 |
+
P = Product(f, (i, a, b)).doit()
|
| 126 |
+
# Test if Product_{m <= i < n} f(i) = g(n) / g(m)
|
| 127 |
+
assert combsimp(P / (g.subs(i, n) / g.subs(i, m))) == 1
|
| 128 |
+
|
| 129 |
+
# m < n
|
| 130 |
+
test_the_product(u, u + v)
|
| 131 |
+
# m = n
|
| 132 |
+
test_the_product(u, u)
|
| 133 |
+
# m > n
|
| 134 |
+
test_the_product(u + v, u)
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
def test_karr_proposition_2b():
|
| 138 |
+
# Test Karr, page 309, proposition 2, part b
|
| 139 |
+
i, u, v, w = symbols('i u v w', integer=True)
|
| 140 |
+
|
| 141 |
+
def test_the_product(l, n, m):
|
| 142 |
+
# Productmand
|
| 143 |
+
s = i**3
|
| 144 |
+
# First product
|
| 145 |
+
a = l
|
| 146 |
+
b = n - 1
|
| 147 |
+
S1 = Product(s, (i, a, b)).doit()
|
| 148 |
+
# Second product
|
| 149 |
+
a = l
|
| 150 |
+
b = m - 1
|
| 151 |
+
S2 = Product(s, (i, a, b)).doit()
|
| 152 |
+
# Third product
|
| 153 |
+
a = m
|
| 154 |
+
b = n - 1
|
| 155 |
+
S3 = Product(s, (i, a, b)).doit()
|
| 156 |
+
# Test if S1 = S2 * S3 as required
|
| 157 |
+
assert combsimp(S1 / (S2 * S3)) == 1
|
| 158 |
+
|
| 159 |
+
# l < m < n
|
| 160 |
+
test_the_product(u, u + v, u + v + w)
|
| 161 |
+
# l < m = n
|
| 162 |
+
test_the_product(u, u + v, u + v)
|
| 163 |
+
# l < m > n
|
| 164 |
+
test_the_product(u, u + v + w, v)
|
| 165 |
+
# l = m < n
|
| 166 |
+
test_the_product(u, u, u + v)
|
| 167 |
+
# l = m = n
|
| 168 |
+
test_the_product(u, u, u)
|
| 169 |
+
# l = m > n
|
| 170 |
+
test_the_product(u + v, u + v, u)
|
| 171 |
+
# l > m < n
|
| 172 |
+
test_the_product(u + v, u, u + w)
|
| 173 |
+
# l > m = n
|
| 174 |
+
test_the_product(u + v, u, u)
|
| 175 |
+
# l > m > n
|
| 176 |
+
test_the_product(u + v + w, u + v, u)
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
def test_simple_products():
|
| 180 |
+
assert product(2, (k, a, n)) == 2**(n - a + 1)
|
| 181 |
+
assert product(k, (k, 1, n)) == factorial(n)
|
| 182 |
+
assert product(k**3, (k, 1, n)) == factorial(n)**3
|
| 183 |
+
|
| 184 |
+
assert product(k + 1, (k, 0, n - 1)) == factorial(n)
|
| 185 |
+
assert product(k + 1, (k, a, n - 1)) == rf(1 + a, n - a)
|
| 186 |
+
|
| 187 |
+
assert product(cos(k), (k, 0, 5)) == cos(1)*cos(2)*cos(3)*cos(4)*cos(5)
|
| 188 |
+
assert product(cos(k), (k, 3, 5)) == cos(3)*cos(4)*cos(5)
|
| 189 |
+
assert product(cos(k), (k, 1, Rational(5, 2))) != cos(1)*cos(2)
|
| 190 |
+
|
| 191 |
+
assert isinstance(product(k**k, (k, 1, n)), Product)
|
| 192 |
+
|
| 193 |
+
assert Product(x**k, (k, 1, n)).variables == [k]
|
| 194 |
+
|
| 195 |
+
raises(ValueError, lambda: Product(n))
|
| 196 |
+
raises(ValueError, lambda: Product(n, k))
|
| 197 |
+
raises(ValueError, lambda: Product(n, k, 1))
|
| 198 |
+
raises(ValueError, lambda: Product(n, k, 1, 10))
|
| 199 |
+
raises(ValueError, lambda: Product(n, (k, 1)))
|
| 200 |
+
|
| 201 |
+
assert product(1, (n, 1, oo)) == 1 # issue 8301
|
| 202 |
+
assert product(2, (n, 1, oo)) is oo
|
| 203 |
+
assert product(-1, (n, 1, oo)).func is Product
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
def test_multiple_products():
|
| 207 |
+
assert product(x, (n, 1, k), (k, 1, m)) == x**(m**2/2 + m/2)
|
| 208 |
+
assert product(f(n), (
|
| 209 |
+
n, 1, m), (m, 1, k)) == Product(f(n), (n, 1, m), (m, 1, k)).doit()
|
| 210 |
+
assert Product(f(n), (m, 1, k), (n, 1, k)).doit() == \
|
| 211 |
+
Product(Product(f(n), (m, 1, k)), (n, 1, k)).doit() == \
|
| 212 |
+
product(f(n), (m, 1, k), (n, 1, k)) == \
|
| 213 |
+
product(product(f(n), (m, 1, k)), (n, 1, k)) == \
|
| 214 |
+
Product(f(n)**k, (n, 1, k))
|
| 215 |
+
assert Product(
|
| 216 |
+
x, (x, 1, k), (k, 1, n)).doit() == Product(factorial(k), (k, 1, n))
|
| 217 |
+
|
| 218 |
+
assert Product(x**k, (n, 1, k), (k, 1, m)).variables == [n, k]
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
def test_rational_products():
|
| 222 |
+
assert product(1 + 1/k, (k, 1, n)) == rf(2, n)/factorial(n)
|
| 223 |
+
|
| 224 |
+
|
| 225 |
+
def test_special_products():
|
| 226 |
+
# Wallis product
|
| 227 |
+
assert product((4*k)**2 / (4*k**2 - 1), (k, 1, n)) == \
|
| 228 |
+
4**n*factorial(n)**2/rf(S.Half, n)/rf(Rational(3, 2), n)
|
| 229 |
+
|
| 230 |
+
# Euler's product formula for sin
|
| 231 |
+
assert product(1 + a/k**2, (k, 1, n)) == \
|
| 232 |
+
rf(1 - sqrt(-a), n)*rf(1 + sqrt(-a), n)/factorial(n)**2
|
| 233 |
+
|
| 234 |
+
|
| 235 |
+
def test__eval_product():
|
| 236 |
+
from sympy.abc import i, n
|
| 237 |
+
# issue 4809
|
| 238 |
+
a = Function('a')
|
| 239 |
+
assert product(2*a(i), (i, 1, n)) == 2**n * Product(a(i), (i, 1, n))
|
| 240 |
+
# issue 4810
|
| 241 |
+
assert product(2**i, (i, 1, n)) == 2**(n*(n + 1)/2)
|
| 242 |
+
k, m = symbols('k m', integer=True)
|
| 243 |
+
assert product(2**i, (i, k, m)) == 2**(-k**2/2 + k/2 + m**2/2 + m/2)
|
| 244 |
+
n = Symbol('n', negative=True, integer=True)
|
| 245 |
+
p = Symbol('p', positive=True, integer=True)
|
| 246 |
+
assert product(2**i, (i, n, p)) == 2**(-n**2/2 + n/2 + p**2/2 + p/2)
|
| 247 |
+
assert product(2**i, (i, p, n)) == 2**(n**2/2 + n/2 - p**2/2 + p/2)
|
| 248 |
+
|
| 249 |
+
|
| 250 |
+
def test_product_pow():
|
| 251 |
+
# issue 4817
|
| 252 |
+
assert product(2**f(k), (k, 1, n)) == 2**Sum(f(k), (k, 1, n))
|
| 253 |
+
assert product(2**(2*f(k)), (k, 1, n)) == 2**Sum(2*f(k), (k, 1, n))
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
def test_infinite_product():
|
| 257 |
+
# issue 5737
|
| 258 |
+
assert isinstance(Product(2**(1/factorial(n)), (n, 0, oo)), Product)
|
| 259 |
+
|
| 260 |
+
|
| 261 |
+
def test_conjugate_transpose():
|
| 262 |
+
p = Product(x**k, (k, 1, 3))
|
| 263 |
+
assert p.adjoint().doit() == p.doit().adjoint()
|
| 264 |
+
assert p.conjugate().doit() == p.doit().conjugate()
|
| 265 |
+
assert p.transpose().doit() == p.doit().transpose()
|
| 266 |
+
|
| 267 |
+
A, B = symbols("A B", commutative=False)
|
| 268 |
+
p = Product(A*B**k, (k, 1, 3))
|
| 269 |
+
assert p.adjoint().doit() == p.doit().adjoint()
|
| 270 |
+
assert p.conjugate().doit() == p.doit().conjugate()
|
| 271 |
+
assert p.transpose().doit() == p.doit().transpose()
|
| 272 |
+
|
| 273 |
+
p = Product(B**k*A, (k, 1, 3))
|
| 274 |
+
assert p.adjoint().doit() == p.doit().adjoint()
|
| 275 |
+
assert p.conjugate().doit() == p.doit().conjugate()
|
| 276 |
+
assert p.transpose().doit() == p.doit().transpose()
|
| 277 |
+
|
| 278 |
+
|
| 279 |
+
def test_simplify_prod():
|
| 280 |
+
y, t, b, c, v, d = symbols('y, t, b, c, v, d', integer = True)
|
| 281 |
+
|
| 282 |
+
_simplify = lambda e: simplify(e, doit=False)
|
| 283 |
+
assert _simplify(Product(x*y, (x, n, m), (y, a, k)) * \
|
| 284 |
+
Product(y, (x, n, m), (y, a, k))) == \
|
| 285 |
+
Product(x*y**2, (x, n, m), (y, a, k))
|
| 286 |
+
assert _simplify(3 * y* Product(x, (x, n, m)) * Product(x, (x, m + 1, a))) \
|
| 287 |
+
== 3 * y * Product(x, (x, n, a))
|
| 288 |
+
assert _simplify(Product(x, (x, k + 1, a)) * Product(x, (x, n, k))) == \
|
| 289 |
+
Product(x, (x, n, a))
|
| 290 |
+
assert _simplify(Product(x, (x, k + 1, a)) * Product(x + 1, (x, n, k))) == \
|
| 291 |
+
Product(x, (x, k + 1, a)) * Product(x + 1, (x, n, k))
|
| 292 |
+
assert _simplify(Product(x, (t, a, b)) * Product(y, (t, a, b)) * \
|
| 293 |
+
Product(x, (t, b+1, c))) == Product(x*y, (t, a, b)) * \
|
| 294 |
+
Product(x, (t, b+1, c))
|
| 295 |
+
assert _simplify(Product(x, (t, a, b)) * Product(x, (t, b+1, c)) * \
|
| 296 |
+
Product(y, (t, a, b))) == Product(x*y, (t, a, b)) * \
|
| 297 |
+
Product(x, (t, b+1, c))
|
| 298 |
+
assert _simplify(Product(sin(t)**2 + cos(t)**2 + 1, (t, a, b))) == \
|
| 299 |
+
Product(2, (t, a, b))
|
| 300 |
+
assert _simplify(Product(sin(t)**2 + cos(t)**2 - 1, (t, a, b))) == \
|
| 301 |
+
Product(0, (t, a, b))
|
| 302 |
+
assert _simplify(Product(v*Product(sin(t)**2 + cos(t)**2, (t, a, b)),
|
| 303 |
+
(v, c, d))) == Product(v*Product(1, (t, a, b)), (v, c, d))
|
| 304 |
+
|
| 305 |
+
|
| 306 |
+
def test_change_index():
|
| 307 |
+
b, y, c, d, z = symbols('b, y, c, d, z', integer = True)
|
| 308 |
+
|
| 309 |
+
assert Product(x, (x, a, b)).change_index(x, x + 1, y) == \
|
| 310 |
+
Product(y - 1, (y, a + 1, b + 1))
|
| 311 |
+
assert Product(x**2, (x, a, b)).change_index(x, x - 1) == \
|
| 312 |
+
Product((x + 1)**2, (x, a - 1, b - 1))
|
| 313 |
+
assert Product(x**2, (x, a, b)).change_index(x, -x, y) == \
|
| 314 |
+
Product((-y)**2, (y, -b, -a))
|
| 315 |
+
assert Product(x, (x, a, b)).change_index(x, -x - 1) == \
|
| 316 |
+
Product(-x - 1, (x, - b - 1, -a - 1))
|
| 317 |
+
assert Product(x*y, (x, a, b), (y, c, d)).change_index(x, x - 1, z) == \
|
| 318 |
+
Product((z + 1)*y, (z, a - 1, b - 1), (y, c, d))
|
| 319 |
+
|
| 320 |
+
|
| 321 |
+
def test_reorder():
|
| 322 |
+
b, y, c, d, z = symbols('b, y, c, d, z', integer = True)
|
| 323 |
+
|
| 324 |
+
assert Product(x*y, (x, a, b), (y, c, d)).reorder((0, 1)) == \
|
| 325 |
+
Product(x*y, (y, c, d), (x, a, b))
|
| 326 |
+
assert Product(x, (x, a, b), (x, c, d)).reorder((0, 1)) == \
|
| 327 |
+
Product(x, (x, c, d), (x, a, b))
|
| 328 |
+
assert Product(x*y + z, (x, a, b), (z, m, n), (y, c, d)).reorder(\
|
| 329 |
+
(2, 0), (0, 1)) == Product(x*y + z, (z, m, n), (y, c, d), (x, a, b))
|
| 330 |
+
assert Product(x*y*z, (x, a, b), (y, c, d), (z, m, n)).reorder(\
|
| 331 |
+
(0, 1), (1, 2), (0, 2)) == \
|
| 332 |
+
Product(x*y*z, (x, a, b), (z, m, n), (y, c, d))
|
| 333 |
+
assert Product(x*y*z, (x, a, b), (y, c, d), (z, m, n)).reorder(\
|
| 334 |
+
(x, y), (y, z), (x, z)) == \
|
| 335 |
+
Product(x*y*z, (x, a, b), (z, m, n), (y, c, d))
|
| 336 |
+
assert Product(x*y, (x, a, b), (y, c, d)).reorder((x, 1)) == \
|
| 337 |
+
Product(x*y, (y, c, d), (x, a, b))
|
| 338 |
+
assert Product(x*y, (x, a, b), (y, c, d)).reorder((y, x)) == \
|
| 339 |
+
Product(x*y, (y, c, d), (x, a, b))
|
| 340 |
+
|
| 341 |
+
|
| 342 |
+
def test_Product_is_convergent():
|
| 343 |
+
assert Product(1/n**2, (n, 1, oo)).is_convergent() is S.false
|
| 344 |
+
assert Product(exp(1/n**2), (n, 1, oo)).is_convergent() is S.true
|
| 345 |
+
assert Product(1/n, (n, 1, oo)).is_convergent() is S.false
|
| 346 |
+
assert Product(1 + 1/n, (n, 1, oo)).is_convergent() is S.false
|
| 347 |
+
assert Product(1 + 1/n**2, (n, 1, oo)).is_convergent() is S.true
|
| 348 |
+
|
| 349 |
+
|
| 350 |
+
def test_reverse_order():
|
| 351 |
+
x, y, a, b, c, d= symbols('x, y, a, b, c, d', integer = True)
|
| 352 |
+
|
| 353 |
+
assert Product(x, (x, 0, 3)).reverse_order(0) == Product(1/x, (x, 4, -1))
|
| 354 |
+
assert Product(x*y, (x, 1, 5), (y, 0, 6)).reverse_order(0, 1) == \
|
| 355 |
+
Product(x*y, (x, 6, 0), (y, 7, -1))
|
| 356 |
+
assert Product(x, (x, 1, 2)).reverse_order(0) == Product(1/x, (x, 3, 0))
|
| 357 |
+
assert Product(x, (x, 1, 3)).reverse_order(0) == Product(1/x, (x, 4, 0))
|
| 358 |
+
assert Product(x, (x, 1, a)).reverse_order(0) == Product(1/x, (x, a + 1, 0))
|
| 359 |
+
assert Product(x, (x, a, 5)).reverse_order(0) == Product(1/x, (x, 6, a - 1))
|
| 360 |
+
assert Product(x, (x, a + 1, a + 5)).reverse_order(0) == \
|
| 361 |
+
Product(1/x, (x, a + 6, a))
|
| 362 |
+
assert Product(x, (x, a + 1, a + 2)).reverse_order(0) == \
|
| 363 |
+
Product(1/x, (x, a + 3, a))
|
| 364 |
+
assert Product(x, (x, a + 1, a + 1)).reverse_order(0) == \
|
| 365 |
+
Product(1/x, (x, a + 2, a))
|
| 366 |
+
assert Product(x, (x, a, b)).reverse_order(0) == Product(1/x, (x, b + 1, a - 1))
|
| 367 |
+
assert Product(x, (x, a, b)).reverse_order(x) == Product(1/x, (x, b + 1, a - 1))
|
| 368 |
+
assert Product(x*y, (x, a, b), (y, 2, 5)).reverse_order(x, 1) == \
|
| 369 |
+
Product(x*y, (x, b + 1, a - 1), (y, 6, 1))
|
| 370 |
+
assert Product(x*y, (x, a, b), (y, 2, 5)).reverse_order(y, x) == \
|
| 371 |
+
Product(x*y, (x, b + 1, a - 1), (y, 6, 1))
|
| 372 |
+
|
| 373 |
+
|
| 374 |
+
def test_issue_9983():
|
| 375 |
+
n = Symbol('n', integer=True, positive=True)
|
| 376 |
+
p = Product(1 + 1/n**Rational(2, 3), (n, 1, oo))
|
| 377 |
+
assert p.is_convergent() is S.false
|
| 378 |
+
assert product(1 + 1/n**Rational(2, 3), (n, 1, oo)) == p.doit()
|
| 379 |
+
|
| 380 |
+
|
| 381 |
+
def test_issue_13546():
|
| 382 |
+
n = Symbol('n')
|
| 383 |
+
k = Symbol('k')
|
| 384 |
+
p = Product(n + 1 / 2**k, (k, 0, n-1)).doit()
|
| 385 |
+
assert p.subs(n, 2).doit() == Rational(15, 2)
|
| 386 |
+
|
| 387 |
+
|
| 388 |
+
def test_issue_14036():
|
| 389 |
+
a, n = symbols('a n')
|
| 390 |
+
assert product(1 - a**2 / (n*pi)**2, [n, 1, oo]) != 0
|
| 391 |
+
|
| 392 |
+
|
| 393 |
+
def test_rewrite_Sum():
|
| 394 |
+
assert Product(1 - S.Half**2/k**2, (k, 1, oo)).rewrite(Sum) == \
|
| 395 |
+
exp(Sum(log(1 - 1/(4*k**2)), (k, 1, oo)))
|
| 396 |
+
|
| 397 |
+
|
| 398 |
+
def test_KroneckerDelta_Product():
|
| 399 |
+
y = Symbol('y')
|
| 400 |
+
assert Product(x*KroneckerDelta(x, y), (x, 0, 1)).doit() == 0
|
| 401 |
+
|
| 402 |
+
|
| 403 |
+
def test_issue_20848():
|
| 404 |
+
_i = Dummy('i')
|
| 405 |
+
t, y, z = symbols('t y z')
|
| 406 |
+
assert diff(Product(x, (y, 1, z)), x).as_dummy() == Sum(Product(x, (y, 1, _i - 1))*Product(x, (y, _i + 1, z)), (_i, 1, z)).as_dummy()
|
| 407 |
+
assert diff(Product(x, (y, 1, z)), x).doit() == x**(z - 1)*z
|
| 408 |
+
assert diff(Product(x, (y, x, z)), x) == Derivative(Product(x, (y, x, z)), x)
|
| 409 |
+
assert diff(Product(t, (x, 1, z)), x) == S(0)
|
| 410 |
+
assert Product(sin(n*x), (n, -1, 1)).diff(x).doit() == S(0)
|
.venv/lib/python3.13/site-packages/sympy/concrete/tests/test_sums_products.py
ADDED
|
@@ -0,0 +1,1676 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from math import prod
|
| 2 |
+
|
| 3 |
+
from sympy.concrete.expr_with_intlimits import ReorderError
|
| 4 |
+
from sympy.concrete.products import (Product, product)
|
| 5 |
+
from sympy.concrete.summations import (Sum, summation, telescopic,
|
| 6 |
+
eval_sum_residue, _dummy_with_inherited_properties_concrete)
|
| 7 |
+
from sympy.core.function import (Derivative, Function)
|
| 8 |
+
from sympy.core import (Catalan, EulerGamma)
|
| 9 |
+
from sympy.core.facts import InconsistentAssumptions
|
| 10 |
+
from sympy.core.mod import Mod
|
| 11 |
+
from sympy.core.numbers import (E, I, Rational, nan, oo, pi)
|
| 12 |
+
from sympy.core.relational import Eq, Ne
|
| 13 |
+
from sympy.core.numbers import Float
|
| 14 |
+
from sympy.core.singleton import S
|
| 15 |
+
from sympy.core.symbol import (Dummy, Symbol, symbols)
|
| 16 |
+
from sympy.core.sympify import sympify
|
| 17 |
+
from sympy.functions.combinatorial.factorials import (rf, binomial, factorial)
|
| 18 |
+
from sympy.functions.combinatorial.numbers import harmonic
|
| 19 |
+
from sympy.functions.elementary.complexes import Abs, re
|
| 20 |
+
from sympy.functions.elementary.exponential import (exp, log)
|
| 21 |
+
from sympy.functions.elementary.hyperbolic import (sinh, tanh)
|
| 22 |
+
from sympy.functions.elementary.integers import floor
|
| 23 |
+
from sympy.functions.elementary.miscellaneous import sqrt
|
| 24 |
+
from sympy.functions.elementary.piecewise import Piecewise
|
| 25 |
+
from sympy.functions.elementary.trigonometric import (cos, sin, atan)
|
| 26 |
+
from sympy.functions.special.gamma_functions import (gamma, lowergamma)
|
| 27 |
+
from sympy.functions.special.tensor_functions import KroneckerDelta
|
| 28 |
+
from sympy.functions.special.zeta_functions import zeta
|
| 29 |
+
from sympy.integrals.integrals import Integral
|
| 30 |
+
from sympy.logic.boolalg import And, Or
|
| 31 |
+
from sympy.matrices.expressions.matexpr import MatrixSymbol
|
| 32 |
+
from sympy.matrices.expressions.special import Identity
|
| 33 |
+
from sympy.matrices import (Matrix, SparseMatrix,
|
| 34 |
+
ImmutableDenseMatrix, ImmutableSparseMatrix, diag)
|
| 35 |
+
from sympy.sets.contains import Contains
|
| 36 |
+
from sympy.sets.fancysets import Range
|
| 37 |
+
from sympy.sets.sets import Interval
|
| 38 |
+
from sympy.simplify.combsimp import combsimp
|
| 39 |
+
from sympy.simplify.simplify import simplify
|
| 40 |
+
from sympy.tensor.indexed import (Idx, Indexed, IndexedBase)
|
| 41 |
+
from sympy.testing.pytest import XFAIL, raises, slow
|
| 42 |
+
from sympy.abc import a, b, c, d, k, m, x, y, z
|
| 43 |
+
|
| 44 |
+
n = Symbol('n', integer=True)
|
| 45 |
+
f, g = symbols('f g', cls=Function)
|
| 46 |
+
|
| 47 |
+
def test_karr_convention():
|
| 48 |
+
# Test the Karr summation convention that we want to hold.
|
| 49 |
+
# See his paper "Summation in Finite Terms" for a detailed
|
| 50 |
+
# reasoning why we really want exactly this definition.
|
| 51 |
+
# The convention is described on page 309 and essentially
|
| 52 |
+
# in section 1.4, definition 3:
|
| 53 |
+
#
|
| 54 |
+
# \sum_{m <= i < n} f(i) 'has the obvious meaning' for m < n
|
| 55 |
+
# \sum_{m <= i < n} f(i) = 0 for m = n
|
| 56 |
+
# \sum_{m <= i < n} f(i) = - \sum_{n <= i < m} f(i) for m > n
|
| 57 |
+
#
|
| 58 |
+
# It is important to note that he defines all sums with
|
| 59 |
+
# the upper limit being *exclusive*.
|
| 60 |
+
# In contrast, SymPy and the usual mathematical notation has:
|
| 61 |
+
#
|
| 62 |
+
# sum_{i = a}^b f(i) = f(a) + f(a+1) + ... + f(b-1) + f(b)
|
| 63 |
+
#
|
| 64 |
+
# with the upper limit *inclusive*. So translating between
|
| 65 |
+
# the two we find that:
|
| 66 |
+
#
|
| 67 |
+
# \sum_{m <= i < n} f(i) = \sum_{i = m}^{n-1} f(i)
|
| 68 |
+
#
|
| 69 |
+
# where we intentionally used two different ways to typeset the
|
| 70 |
+
# sum and its limits.
|
| 71 |
+
|
| 72 |
+
i = Symbol("i", integer=True)
|
| 73 |
+
k = Symbol("k", integer=True)
|
| 74 |
+
j = Symbol("j", integer=True)
|
| 75 |
+
|
| 76 |
+
# A simple example with a concrete summand and symbolic limits.
|
| 77 |
+
|
| 78 |
+
# The normal sum: m = k and n = k + j and therefore m < n:
|
| 79 |
+
m = k
|
| 80 |
+
n = k + j
|
| 81 |
+
|
| 82 |
+
a = m
|
| 83 |
+
b = n - 1
|
| 84 |
+
S1 = Sum(i**2, (i, a, b)).doit()
|
| 85 |
+
|
| 86 |
+
# The reversed sum: m = k + j and n = k and therefore m > n:
|
| 87 |
+
m = k + j
|
| 88 |
+
n = k
|
| 89 |
+
|
| 90 |
+
a = m
|
| 91 |
+
b = n - 1
|
| 92 |
+
S2 = Sum(i**2, (i, a, b)).doit()
|
| 93 |
+
|
| 94 |
+
assert simplify(S1 + S2) == 0
|
| 95 |
+
|
| 96 |
+
# Test the empty sum: m = k and n = k and therefore m = n:
|
| 97 |
+
m = k
|
| 98 |
+
n = k
|
| 99 |
+
|
| 100 |
+
a = m
|
| 101 |
+
b = n - 1
|
| 102 |
+
Sz = Sum(i**2, (i, a, b)).doit()
|
| 103 |
+
|
| 104 |
+
assert Sz == 0
|
| 105 |
+
|
| 106 |
+
# Another example this time with an unspecified summand and
|
| 107 |
+
# numeric limits. (We can not do both tests in the same example.)
|
| 108 |
+
|
| 109 |
+
# The normal sum with m < n:
|
| 110 |
+
m = 2
|
| 111 |
+
n = 11
|
| 112 |
+
|
| 113 |
+
a = m
|
| 114 |
+
b = n - 1
|
| 115 |
+
S1 = Sum(f(i), (i, a, b)).doit()
|
| 116 |
+
|
| 117 |
+
# The reversed sum with m > n:
|
| 118 |
+
m = 11
|
| 119 |
+
n = 2
|
| 120 |
+
|
| 121 |
+
a = m
|
| 122 |
+
b = n - 1
|
| 123 |
+
S2 = Sum(f(i), (i, a, b)).doit()
|
| 124 |
+
|
| 125 |
+
assert simplify(S1 + S2) == 0
|
| 126 |
+
|
| 127 |
+
# Test the empty sum with m = n:
|
| 128 |
+
m = 5
|
| 129 |
+
n = 5
|
| 130 |
+
|
| 131 |
+
a = m
|
| 132 |
+
b = n - 1
|
| 133 |
+
Sz = Sum(f(i), (i, a, b)).doit()
|
| 134 |
+
|
| 135 |
+
assert Sz == 0
|
| 136 |
+
|
| 137 |
+
e = Piecewise((exp(-i), Mod(i, 2) > 0), (0, True))
|
| 138 |
+
s = Sum(e, (i, 0, 11))
|
| 139 |
+
assert s.n(3) == s.doit().n(3)
|
| 140 |
+
|
| 141 |
+
# issue #27893
|
| 142 |
+
n = Symbol('n', integer=True)
|
| 143 |
+
assert Sum(1/(x**2 + 1), (x, oo, 0)).doit(deep=False) == Rational(-1, 2) + pi / (2 * tanh(pi))
|
| 144 |
+
assert Sum(c**x/factorial(x), (x, oo, 0)).doit(deep=False).simplify() == exp(c) - 1 # exponential series
|
| 145 |
+
assert Sum((-1)**x/x, (x, oo,0)).doit() == -log(2) # alternating harmnic series
|
| 146 |
+
assert Sum((1/2)**x,(x, oo, -1)).doit() == S(2) # geometric series
|
| 147 |
+
assert Sum(1/x, (x, oo, 0)).doit() == oo # harmonic series, divergent
|
| 148 |
+
assert Sum((-1)**x/(2*x+1), (x, oo, -1)).doit() == pi/4 # leibniz series
|
| 149 |
+
assert Sum((((-1)**x) * c**(2*x+1)) / factorial(2*x+1), (x, oo, -1)).doit() == sin(c) # sinusoidal series
|
| 150 |
+
assert Sum((((-1)**x) * c**(2*x+1)) / (2*x+1), (x, 0, oo)).doit() \
|
| 151 |
+
== Piecewise((atan(c), Ne(c**2, -1) & (Abs(c**2) <= 1)), \
|
| 152 |
+
(Sum((-1)**x*c**(2*x + 1)/(2*x + 1), (x, 0, oo)), True)) # arctangent series
|
| 153 |
+
assert Sum(binomial(n, x) * c**x, (x, 0, oo)).doit() \
|
| 154 |
+
== Piecewise(((c + 1)**n, \
|
| 155 |
+
((n <= -1) & (Abs(c) < 1)) \
|
| 156 |
+
| ((n > 0) & (Abs(c) <= 1)) \
|
| 157 |
+
| ((n <= 0) & (n > -1) & Ne(c, -1) & (Abs(c) <= 1))), \
|
| 158 |
+
(Sum(c**x*binomial(n, x), (x, 0, oo)), True)) # binomial series
|
| 159 |
+
assert Sum(1/x**n, (x, oo, 0)).doit() \
|
| 160 |
+
== Piecewise((zeta(n), n > 1), (Sum(x**(-n), (x, oo, 0)), True)) # Euler's zeta function
|
| 161 |
+
|
| 162 |
+
def test_karr_proposition_2a():
|
| 163 |
+
# Test Karr, page 309, proposition 2, part a
|
| 164 |
+
i = Symbol("i", integer=True)
|
| 165 |
+
u = Symbol("u", integer=True)
|
| 166 |
+
v = Symbol("v", integer=True)
|
| 167 |
+
|
| 168 |
+
def test_the_sum(m, n):
|
| 169 |
+
# g
|
| 170 |
+
g = i**3 + 2*i**2 - 3*i
|
| 171 |
+
# f = Delta g
|
| 172 |
+
f = simplify(g.subs(i, i+1) - g)
|
| 173 |
+
# The sum
|
| 174 |
+
a = m
|
| 175 |
+
b = n - 1
|
| 176 |
+
S = Sum(f, (i, a, b)).doit()
|
| 177 |
+
# Test if Sum_{m <= i < n} f(i) = g(n) - g(m)
|
| 178 |
+
assert simplify(S - (g.subs(i, n) - g.subs(i, m))) == 0
|
| 179 |
+
|
| 180 |
+
# m < n
|
| 181 |
+
test_the_sum(u, u+v)
|
| 182 |
+
# m = n
|
| 183 |
+
test_the_sum(u, u )
|
| 184 |
+
# m > n
|
| 185 |
+
test_the_sum(u+v, u )
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
def test_karr_proposition_2b():
|
| 189 |
+
# Test Karr, page 309, proposition 2, part b
|
| 190 |
+
i = Symbol("i", integer=True)
|
| 191 |
+
u = Symbol("u", integer=True)
|
| 192 |
+
v = Symbol("v", integer=True)
|
| 193 |
+
w = Symbol("w", integer=True)
|
| 194 |
+
|
| 195 |
+
def test_the_sum(l, n, m):
|
| 196 |
+
# Summand
|
| 197 |
+
s = i**3
|
| 198 |
+
# First sum
|
| 199 |
+
a = l
|
| 200 |
+
b = n - 1
|
| 201 |
+
S1 = Sum(s, (i, a, b)).doit()
|
| 202 |
+
# Second sum
|
| 203 |
+
a = l
|
| 204 |
+
b = m - 1
|
| 205 |
+
S2 = Sum(s, (i, a, b)).doit()
|
| 206 |
+
# Third sum
|
| 207 |
+
a = m
|
| 208 |
+
b = n - 1
|
| 209 |
+
S3 = Sum(s, (i, a, b)).doit()
|
| 210 |
+
# Test if S1 = S2 + S3 as required
|
| 211 |
+
assert S1 - (S2 + S3) == 0
|
| 212 |
+
|
| 213 |
+
# l < m < n
|
| 214 |
+
test_the_sum(u, u+v, u+v+w)
|
| 215 |
+
# l < m = n
|
| 216 |
+
test_the_sum(u, u+v, u+v )
|
| 217 |
+
# l < m > n
|
| 218 |
+
test_the_sum(u, u+v+w, v )
|
| 219 |
+
# l = m < n
|
| 220 |
+
test_the_sum(u, u, u+v )
|
| 221 |
+
# l = m = n
|
| 222 |
+
test_the_sum(u, u, u )
|
| 223 |
+
# l = m > n
|
| 224 |
+
test_the_sum(u+v, u+v, u )
|
| 225 |
+
# l > m < n
|
| 226 |
+
test_the_sum(u+v, u, u+w )
|
| 227 |
+
# l > m = n
|
| 228 |
+
test_the_sum(u+v, u, u )
|
| 229 |
+
# l > m > n
|
| 230 |
+
test_the_sum(u+v+w, u+v, u )
|
| 231 |
+
|
| 232 |
+
|
| 233 |
+
def test_arithmetic_sums():
|
| 234 |
+
assert summation(1, (n, a, b)) == b - a + 1
|
| 235 |
+
assert Sum(S.NaN, (n, a, b)) is S.NaN
|
| 236 |
+
assert Sum(x, (n, a, a)).doit() == x
|
| 237 |
+
assert Sum(x, (x, a, a)).doit() == a
|
| 238 |
+
assert Sum(x, (n, 1, a)).doit() == a*x
|
| 239 |
+
assert Sum(x, (x, Range(1, 11))).doit() == 55
|
| 240 |
+
assert Sum(x, (x, Range(1, 11, 2))).doit() == 25
|
| 241 |
+
assert Sum(x, (x, Range(1, 10, 2))) == Sum(x, (x, Range(9, 0, -2)))
|
| 242 |
+
lo, hi = 1, 2
|
| 243 |
+
s1 = Sum(n, (n, lo, hi))
|
| 244 |
+
s2 = Sum(n, (n, hi, lo))
|
| 245 |
+
assert s1 != s2
|
| 246 |
+
assert s1.doit() == 3 and s2.doit() == 0
|
| 247 |
+
lo, hi = x, x + 1
|
| 248 |
+
s1 = Sum(n, (n, lo, hi))
|
| 249 |
+
s2 = Sum(n, (n, hi, lo))
|
| 250 |
+
assert s1 != s2
|
| 251 |
+
assert s1.doit() == 2*x + 1 and s2.doit() == 0
|
| 252 |
+
assert Sum(Integral(x, (x, 1, y)) + x, (x, 1, 2)).doit() == \
|
| 253 |
+
y**2 + 2
|
| 254 |
+
assert summation(1, (n, 1, 10)) == 10
|
| 255 |
+
assert summation(2*n, (n, 0, 10**10)) == 100000000010000000000
|
| 256 |
+
assert summation(4*n*m, (n, a, 1), (m, 1, d)).expand() == \
|
| 257 |
+
2*d + 2*d**2 + a*d + a*d**2 - d*a**2 - a**2*d**2
|
| 258 |
+
assert summation(cos(n), (n, -2, 1)) == cos(-2) + cos(-1) + cos(0) + cos(1)
|
| 259 |
+
assert summation(cos(n), (n, x, x + 2)) == cos(x) + cos(x + 1) + cos(x + 2)
|
| 260 |
+
assert isinstance(summation(cos(n), (n, x, x + S.Half)), Sum)
|
| 261 |
+
assert summation(k, (k, 0, oo)) is oo
|
| 262 |
+
assert summation(k, (k, Range(1, 11))) == 55
|
| 263 |
+
|
| 264 |
+
|
| 265 |
+
def test_polynomial_sums():
|
| 266 |
+
assert summation(n**2, (n, 3, 8)) == 199
|
| 267 |
+
assert summation(n, (n, a, b)) == \
|
| 268 |
+
((a + b)*(b - a + 1)/2).expand()
|
| 269 |
+
assert summation(n**2, (n, 1, b)) == \
|
| 270 |
+
((2*b**3 + 3*b**2 + b)/6).expand()
|
| 271 |
+
assert summation(n**3, (n, 1, b)) == \
|
| 272 |
+
((b**4 + 2*b**3 + b**2)/4).expand()
|
| 273 |
+
assert summation(n**6, (n, 1, b)) == \
|
| 274 |
+
((6*b**7 + 21*b**6 + 21*b**5 - 7*b**3 + b)/42).expand()
|
| 275 |
+
|
| 276 |
+
|
| 277 |
+
def test_geometric_sums():
|
| 278 |
+
assert summation(pi**n, (n, 0, b)) == (1 - pi**(b + 1)) / (1 - pi)
|
| 279 |
+
assert summation(2 * 3**n, (n, 0, b)) == 3**(b + 1) - 1
|
| 280 |
+
assert summation(S.Half**n, (n, 1, oo)) == 1
|
| 281 |
+
assert summation(2**n, (n, 0, b)) == 2**(b + 1) - 1
|
| 282 |
+
assert summation(2**n, (n, 1, oo)) is oo
|
| 283 |
+
assert summation(2**(-n), (n, 1, oo)) == 1
|
| 284 |
+
assert summation(3**(-n), (n, 4, oo)) == Rational(1, 54)
|
| 285 |
+
assert summation(2**(-4*n + 3), (n, 1, oo)) == Rational(8, 15)
|
| 286 |
+
assert summation(2**(n + 1), (n, 1, b)).expand() == 4*(2**b - 1)
|
| 287 |
+
|
| 288 |
+
# issue 6664:
|
| 289 |
+
assert summation(x**n, (n, 0, oo)) == \
|
| 290 |
+
Piecewise((1/(-x + 1), Abs(x) < 1), (Sum(x**n, (n, 0, oo)), True))
|
| 291 |
+
|
| 292 |
+
assert summation(-2**n, (n, 0, oo)) is -oo
|
| 293 |
+
assert summation(I**n, (n, 0, oo)) == Sum(I**n, (n, 0, oo))
|
| 294 |
+
|
| 295 |
+
# issue 6802:
|
| 296 |
+
assert summation((-1)**(2*x + 2), (x, 0, n)) == n + 1
|
| 297 |
+
assert summation((-2)**(2*x + 2), (x, 0, n)) == 4*4**(n + 1)/S(3) - Rational(4, 3)
|
| 298 |
+
assert summation((-1)**x, (x, 0, n)) == -(-1)**(n + 1)/S(2) + S.Half
|
| 299 |
+
assert summation(y**x, (x, a, b)) == \
|
| 300 |
+
Piecewise((-a + b + 1, Eq(y, 1)), ((y**a - y**(b + 1))/(-y + 1), True))
|
| 301 |
+
assert summation((-2)**(y*x + 2), (x, 0, n)) == \
|
| 302 |
+
4*Piecewise((n + 1, Eq((-2)**y, 1)),
|
| 303 |
+
((-(-2)**(y*(n + 1)) + 1)/(-(-2)**y + 1), True))
|
| 304 |
+
|
| 305 |
+
# issue 8251:
|
| 306 |
+
assert summation((1/(n + 1)**2)*n**2, (n, 0, oo)) is oo
|
| 307 |
+
|
| 308 |
+
#issue 9908:
|
| 309 |
+
assert Sum(1/(n**3 - 1), (n, -oo, -2)).doit() == summation(1/(n**3 - 1), (n, -oo, -2))
|
| 310 |
+
|
| 311 |
+
#issue 11642:
|
| 312 |
+
result = Sum(0.5**n, (n, 1, oo)).doit()
|
| 313 |
+
assert result == 1.0
|
| 314 |
+
assert result.is_Float
|
| 315 |
+
|
| 316 |
+
result = Sum(0.25**n, (n, 1, oo)).doit()
|
| 317 |
+
assert result == 1/3.
|
| 318 |
+
assert result.is_Float
|
| 319 |
+
|
| 320 |
+
result = Sum(0.99999**n, (n, 1, oo)).doit()
|
| 321 |
+
assert result == 99999.0
|
| 322 |
+
assert result.is_Float
|
| 323 |
+
|
| 324 |
+
result = Sum(S.Half**n, (n, 1, oo)).doit()
|
| 325 |
+
assert result == 1
|
| 326 |
+
assert not result.is_Float
|
| 327 |
+
|
| 328 |
+
result = Sum(Rational(3, 5)**n, (n, 1, oo)).doit()
|
| 329 |
+
assert result == Rational(3, 2)
|
| 330 |
+
assert not result.is_Float
|
| 331 |
+
|
| 332 |
+
assert Sum(1.0**n, (n, 1, oo)).doit() is oo
|
| 333 |
+
assert Sum(2.43**n, (n, 1, oo)).doit() is oo
|
| 334 |
+
|
| 335 |
+
# Issue 13979
|
| 336 |
+
i, k, q = symbols('i k q', integer=True)
|
| 337 |
+
result = summation(
|
| 338 |
+
exp(-2*I*pi*k*i/n) * exp(2*I*pi*q*i/n) / n, (i, 0, n - 1)
|
| 339 |
+
)
|
| 340 |
+
assert result.simplify() == Piecewise(
|
| 341 |
+
(1, Eq(exp(-2*I*pi*(k - q)/n), 1)), (0, True)
|
| 342 |
+
)
|
| 343 |
+
|
| 344 |
+
#Issue 23491
|
| 345 |
+
assert Sum(1/(n**2 + 1), (n, 1, oo)).doit() == S(-1)/2 + pi/(2*tanh(pi))
|
| 346 |
+
|
| 347 |
+
def test_harmonic_sums():
|
| 348 |
+
assert summation(1/k, (k, 0, n)) == Sum(1/k, (k, 0, n))
|
| 349 |
+
assert summation(1/k, (k, 1, n)) == harmonic(n)
|
| 350 |
+
assert summation(n/k, (k, 1, n)) == n*harmonic(n)
|
| 351 |
+
assert summation(1/k, (k, 5, n)) == harmonic(n) - harmonic(4)
|
| 352 |
+
|
| 353 |
+
|
| 354 |
+
def test_composite_sums():
|
| 355 |
+
f = S.Half*(7 - 6*n + Rational(1, 7)*n**3)
|
| 356 |
+
s = summation(f, (n, a, b))
|
| 357 |
+
assert not isinstance(s, Sum)
|
| 358 |
+
A = 0
|
| 359 |
+
for i in range(-3, 5):
|
| 360 |
+
A += f.subs(n, i)
|
| 361 |
+
B = s.subs(a, -3).subs(b, 4)
|
| 362 |
+
assert A == B
|
| 363 |
+
|
| 364 |
+
|
| 365 |
+
def test_hypergeometric_sums():
|
| 366 |
+
assert summation(
|
| 367 |
+
binomial(2*k, k)/4**k, (k, 0, n)) == (1 + 2*n)*binomial(2*n, n)/4**n
|
| 368 |
+
assert summation(binomial(2*k, k)/5**k, (k, -oo, oo)) == sqrt(5)
|
| 369 |
+
|
| 370 |
+
|
| 371 |
+
def test_other_sums():
|
| 372 |
+
f = m**2 + m*exp(m)
|
| 373 |
+
g = 3*exp(Rational(3, 2))/2 + exp(S.Half)/2 - exp(Rational(-1, 2))/2 - 3*exp(Rational(-3, 2))/2 + 5
|
| 374 |
+
|
| 375 |
+
assert summation(f, (m, Rational(-3, 2), Rational(3, 2))) == g
|
| 376 |
+
assert summation(f, (m, -1.5, 1.5)).evalf().epsilon_eq(g.evalf(), 1e-10)
|
| 377 |
+
|
| 378 |
+
fac = factorial
|
| 379 |
+
|
| 380 |
+
|
| 381 |
+
def NS(e, n=15, **options):
|
| 382 |
+
return str(sympify(e).evalf(n, **options))
|
| 383 |
+
|
| 384 |
+
|
| 385 |
+
def test_evalf_fast_series():
|
| 386 |
+
# Euler transformed series for sqrt(1+x)
|
| 387 |
+
assert NS(Sum(
|
| 388 |
+
fac(2*n + 1)/fac(n)**2/2**(3*n + 1), (n, 0, oo)), 100) == NS(sqrt(2), 100)
|
| 389 |
+
|
| 390 |
+
# Some series for exp(1)
|
| 391 |
+
estr = NS(E, 100)
|
| 392 |
+
assert NS(Sum(1/fac(n), (n, 0, oo)), 100) == estr
|
| 393 |
+
assert NS(1/Sum((1 - 2*n)/fac(2*n), (n, 0, oo)), 100) == estr
|
| 394 |
+
assert NS(Sum((2*n + 1)/fac(2*n), (n, 0, oo)), 100) == estr
|
| 395 |
+
assert NS(Sum((4*n + 3)/2**(2*n + 1)/fac(2*n + 1), (n, 0, oo))**2, 100) == estr
|
| 396 |
+
|
| 397 |
+
pistr = NS(pi, 100)
|
| 398 |
+
# Ramanujan series for pi
|
| 399 |
+
assert NS(9801/sqrt(8)/Sum(fac(
|
| 400 |
+
4*n)*(1103 + 26390*n)/fac(n)**4/396**(4*n), (n, 0, oo)), 100) == pistr
|
| 401 |
+
assert NS(1/Sum(
|
| 402 |
+
binomial(2*n, n)**3 * (42*n + 5)/2**(12*n + 4), (n, 0, oo)), 100) == pistr
|
| 403 |
+
# Machin's formula for pi
|
| 404 |
+
assert NS(16*Sum((-1)**n/(2*n + 1)/5**(2*n + 1), (n, 0, oo)) -
|
| 405 |
+
4*Sum((-1)**n/(2*n + 1)/239**(2*n + 1), (n, 0, oo)), 100) == pistr
|
| 406 |
+
|
| 407 |
+
# Apery's constant
|
| 408 |
+
astr = NS(zeta(3), 100)
|
| 409 |
+
P = 126392*n**5 + 412708*n**4 + 531578*n**3 + 336367*n**2 + 104000* \
|
| 410 |
+
n + 12463
|
| 411 |
+
assert NS(Sum((-1)**n * P / 24 * (fac(2*n + 1)*fac(2*n)*fac(
|
| 412 |
+
n))**3 / fac(3*n + 2) / fac(4*n + 3)**3, (n, 0, oo)), 100) == astr
|
| 413 |
+
assert NS(Sum((-1)**n * (205*n**2 + 250*n + 77)/64 * fac(n)**10 /
|
| 414 |
+
fac(2*n + 1)**5, (n, 0, oo)), 100) == astr
|
| 415 |
+
|
| 416 |
+
|
| 417 |
+
def test_evalf_fast_series_issue_4021():
|
| 418 |
+
# Catalan's constant
|
| 419 |
+
assert NS(Sum((-1)**(n - 1)*2**(8*n)*(40*n**2 - 24*n + 3)*fac(2*n)**3*
|
| 420 |
+
fac(n)**2/n**3/(2*n - 1)/fac(4*n)**2, (n, 1, oo))/64, 100) == \
|
| 421 |
+
NS(Catalan, 100)
|
| 422 |
+
astr = NS(zeta(3), 100)
|
| 423 |
+
assert NS(5*Sum(
|
| 424 |
+
(-1)**(n - 1)*fac(n)**2 / n**3 / fac(2*n), (n, 1, oo))/2, 100) == astr
|
| 425 |
+
assert NS(Sum((-1)**(n - 1)*(56*n**2 - 32*n + 5) / (2*n - 1)**2 * fac(n - 1)
|
| 426 |
+
**3 / fac(3*n), (n, 1, oo))/4, 100) == astr
|
| 427 |
+
|
| 428 |
+
|
| 429 |
+
def test_evalf_slow_series():
|
| 430 |
+
assert NS(Sum((-1)**n / n, (n, 1, oo)), 15) == NS(-log(2), 15)
|
| 431 |
+
assert NS(Sum((-1)**n / n, (n, 1, oo)), 50) == NS(-log(2), 50)
|
| 432 |
+
assert NS(Sum(1/n**2, (n, 1, oo)), 15) == NS(pi**2/6, 15)
|
| 433 |
+
assert NS(Sum(1/n**2, (n, 1, oo)), 100) == NS(pi**2/6, 100)
|
| 434 |
+
assert NS(Sum(1/n**2, (n, 1, oo)), 500) == NS(pi**2/6, 500)
|
| 435 |
+
assert NS(Sum((-1)**n / (2*n + 1)**3, (n, 0, oo)), 15) == NS(pi**3/32, 15)
|
| 436 |
+
assert NS(Sum((-1)**n / (2*n + 1)**3, (n, 0, oo)), 50) == NS(pi**3/32, 50)
|
| 437 |
+
|
| 438 |
+
|
| 439 |
+
def test_evalf_oo_to_oo():
|
| 440 |
+
# There used to be an error in certain cases
|
| 441 |
+
# Does not evaluate, but at least do not throw an error
|
| 442 |
+
# Evaluates symbolically to 0, which is not correct
|
| 443 |
+
assert Sum(1/(n**2+1), (n, -oo, oo)).evalf() == Sum(1/(n**2+1), (n, -oo, oo))
|
| 444 |
+
# This evaluates if from 1 to oo and symbolically
|
| 445 |
+
assert Sum(1/(factorial(abs(n))), (n, -oo, -1)).evalf() == Sum(1/(factorial(abs(n))), (n, -oo, -1))
|
| 446 |
+
|
| 447 |
+
|
| 448 |
+
def test_euler_maclaurin():
|
| 449 |
+
# Exact polynomial sums with E-M
|
| 450 |
+
def check_exact(f, a, b, m, n):
|
| 451 |
+
A = Sum(f, (k, a, b))
|
| 452 |
+
s, e = A.euler_maclaurin(m, n)
|
| 453 |
+
assert (e == 0) and (s.expand() == A.doit())
|
| 454 |
+
check_exact(k**4, a, b, 0, 2)
|
| 455 |
+
check_exact(k**4 + 2*k, a, b, 1, 2)
|
| 456 |
+
check_exact(k**4 + k**2, a, b, 1, 5)
|
| 457 |
+
check_exact(k**5, 2, 6, 1, 2)
|
| 458 |
+
check_exact(k**5, 2, 6, 1, 3)
|
| 459 |
+
assert Sum(x-1, (x, 0, 2)).euler_maclaurin(m=30, n=30, eps=2**-15) == (0, 0)
|
| 460 |
+
# Not exact
|
| 461 |
+
assert Sum(k**6, (k, a, b)).euler_maclaurin(0, 2)[1] != 0
|
| 462 |
+
# Numerical test
|
| 463 |
+
for mi, ni in [(2, 4), (2, 20), (10, 20), (18, 20)]:
|
| 464 |
+
A = Sum(1/k**3, (k, 1, oo))
|
| 465 |
+
s, e = A.euler_maclaurin(mi, ni)
|
| 466 |
+
assert abs((s - zeta(3)).evalf()) < e.evalf()
|
| 467 |
+
|
| 468 |
+
raises(ValueError, lambda: Sum(1, (x, 0, 1), (k, 0, 1)).euler_maclaurin())
|
| 469 |
+
|
| 470 |
+
|
| 471 |
+
@slow
|
| 472 |
+
def test_evalf_euler_maclaurin():
|
| 473 |
+
assert NS(Sum(1/k**k, (k, 1, oo)), 15) == '1.29128599706266'
|
| 474 |
+
assert NS(Sum(1/k**k, (k, 1, oo)),
|
| 475 |
+
50) == '1.2912859970626635404072825905956005414986193682745'
|
| 476 |
+
assert NS(Sum(1/k - log(1 + 1/k), (k, 1, oo)), 15) == NS(EulerGamma, 15)
|
| 477 |
+
assert NS(Sum(1/k - log(1 + 1/k), (k, 1, oo)), 50) == NS(EulerGamma, 50)
|
| 478 |
+
assert NS(Sum(log(k)/k**2, (k, 1, oo)), 15) == '0.937548254315844'
|
| 479 |
+
assert NS(Sum(log(k)/k**2, (k, 1, oo)),
|
| 480 |
+
50) == '0.93754825431584375370257409456786497789786028861483'
|
| 481 |
+
assert NS(Sum(1/k, (k, 1000000, 2000000)), 15) == '0.693147930560008'
|
| 482 |
+
assert NS(Sum(1/k, (k, 1000000, 2000000)),
|
| 483 |
+
50) == '0.69314793056000780941723211364567656807940638436025'
|
| 484 |
+
|
| 485 |
+
|
| 486 |
+
def test_evalf_symbolic():
|
| 487 |
+
# issue 6328
|
| 488 |
+
expr = Sum(f(x), (x, 1, 3)) + Sum(g(x), (x, 1, 3))
|
| 489 |
+
assert expr.evalf() == expr
|
| 490 |
+
|
| 491 |
+
|
| 492 |
+
def test_evalf_issue_3273():
|
| 493 |
+
assert Sum(0, (k, 1, oo)).evalf() == 0
|
| 494 |
+
|
| 495 |
+
|
| 496 |
+
def test_simple_products():
|
| 497 |
+
assert Product(S.NaN, (x, 1, 3)) is S.NaN
|
| 498 |
+
assert product(S.NaN, (x, 1, 3)) is S.NaN
|
| 499 |
+
assert Product(x, (n, a, a)).doit() == x
|
| 500 |
+
assert Product(x, (x, a, a)).doit() == a
|
| 501 |
+
assert Product(x, (y, 1, a)).doit() == x**a
|
| 502 |
+
|
| 503 |
+
lo, hi = 1, 2
|
| 504 |
+
s1 = Product(n, (n, lo, hi))
|
| 505 |
+
s2 = Product(n, (n, hi, lo))
|
| 506 |
+
assert s1 != s2
|
| 507 |
+
# This IS correct according to Karr product convention
|
| 508 |
+
assert s1.doit() == 2
|
| 509 |
+
assert s2.doit() == 1
|
| 510 |
+
|
| 511 |
+
lo, hi = x, x + 1
|
| 512 |
+
s1 = Product(n, (n, lo, hi))
|
| 513 |
+
s2 = Product(n, (n, hi, lo))
|
| 514 |
+
s3 = 1 / Product(n, (n, hi + 1, lo - 1))
|
| 515 |
+
assert s1 != s2
|
| 516 |
+
# This IS correct according to Karr product convention
|
| 517 |
+
assert s1.doit() == x*(x + 1)
|
| 518 |
+
assert s2.doit() == 1
|
| 519 |
+
assert s3.doit() == x*(x + 1)
|
| 520 |
+
|
| 521 |
+
assert Product(Integral(2*x, (x, 1, y)) + 2*x, (x, 1, 2)).doit() == \
|
| 522 |
+
(y**2 + 1)*(y**2 + 3)
|
| 523 |
+
assert product(2, (n, a, b)) == 2**(b - a + 1)
|
| 524 |
+
assert product(n, (n, 1, b)) == factorial(b)
|
| 525 |
+
assert product(n**3, (n, 1, b)) == factorial(b)**3
|
| 526 |
+
assert product(3**(2 + n), (n, a, b)) \
|
| 527 |
+
== 3**(2*(1 - a + b) + b/2 + (b**2)/2 + a/2 - (a**2)/2)
|
| 528 |
+
assert product(cos(n), (n, 3, 5)) == cos(3)*cos(4)*cos(5)
|
| 529 |
+
assert product(cos(n), (n, x, x + 2)) == cos(x)*cos(x + 1)*cos(x + 2)
|
| 530 |
+
assert isinstance(product(cos(n), (n, x, x + S.Half)), Product)
|
| 531 |
+
# If Product managed to evaluate this one, it most likely got it wrong!
|
| 532 |
+
assert isinstance(Product(n**n, (n, 1, b)), Product)
|
| 533 |
+
|
| 534 |
+
|
| 535 |
+
def test_rational_products():
|
| 536 |
+
assert combsimp(product(1 + 1/n, (n, a, b))) == (1 + b)/a
|
| 537 |
+
assert combsimp(product(n + 1, (n, a, b))) == gamma(2 + b)/gamma(1 + a)
|
| 538 |
+
assert combsimp(product((n + 1)/(n - 1), (n, a, b))) == b*(1 + b)/(a*(a - 1))
|
| 539 |
+
assert combsimp(product(n/(n + 1)/(n + 2), (n, a, b))) == \
|
| 540 |
+
a*gamma(a + 2)/(b + 1)/gamma(b + 3)
|
| 541 |
+
assert combsimp(product(n*(n + 1)/(n - 1)/(n - 2), (n, a, b))) == \
|
| 542 |
+
b**2*(b - 1)*(1 + b)/(a - 1)**2/(a*(a - 2))
|
| 543 |
+
|
| 544 |
+
|
| 545 |
+
def test_wallis_product():
|
| 546 |
+
# Wallis product, given in two different forms to ensure that Product
|
| 547 |
+
# can factor simple rational expressions
|
| 548 |
+
A = Product(4*n**2 / (4*n**2 - 1), (n, 1, b))
|
| 549 |
+
B = Product((2*n)*(2*n)/(2*n - 1)/(2*n + 1), (n, 1, b))
|
| 550 |
+
R = pi*gamma(b + 1)**2/(2*gamma(b + S.Half)*gamma(b + Rational(3, 2)))
|
| 551 |
+
assert simplify(A.doit()) == R
|
| 552 |
+
assert simplify(B.doit()) == R
|
| 553 |
+
# This one should eventually also be doable (Euler's product formula for sin)
|
| 554 |
+
# assert Product(1+x/n**2, (n, 1, b)) == ...
|
| 555 |
+
|
| 556 |
+
|
| 557 |
+
def test_telescopic_sums():
|
| 558 |
+
#checks also input 2 of comment 1 issue 4127
|
| 559 |
+
assert Sum(1/k - 1/(k + 1), (k, 1, n)).doit() == 1 - 1/(1 + n)
|
| 560 |
+
assert Sum(
|
| 561 |
+
f(k) - f(k + 2), (k, m, n)).doit() == -f(1 + n) - f(2 + n) + f(m) + f(1 + m)
|
| 562 |
+
assert Sum(cos(k) - cos(k + 3), (k, 1, n)).doit() == -cos(1 + n) - \
|
| 563 |
+
cos(2 + n) - cos(3 + n) + cos(1) + cos(2) + cos(3)
|
| 564 |
+
|
| 565 |
+
# dummy variable shouldn't matter
|
| 566 |
+
assert telescopic(1/m, -m/(1 + m), (m, n - 1, n)) == \
|
| 567 |
+
telescopic(1/k, -k/(1 + k), (k, n - 1, n))
|
| 568 |
+
|
| 569 |
+
assert Sum(1/x/(x - 1), (x, a, b)).doit() == 1/(a - 1) - 1/b
|
| 570 |
+
eq = 1/((5*n + 2)*(5*(n + 1) + 2))
|
| 571 |
+
assert Sum(eq, (n, 0, oo)).doit() == S(1)/10
|
| 572 |
+
nz = symbols('nz', nonzero=True)
|
| 573 |
+
v = Sum(eq.subs(5, nz), (n, 0, oo)).doit()
|
| 574 |
+
assert v.subs(nz, 5).simplify() == S(1)/10
|
| 575 |
+
# check that apart is being used in non-symbolic case
|
| 576 |
+
s = Sum(eq, (n, 0, k)).doit()
|
| 577 |
+
v = Sum(eq, (n, 0, 10**100)).doit()
|
| 578 |
+
assert v == s.subs(k, 10**100)
|
| 579 |
+
|
| 580 |
+
|
| 581 |
+
def test_sum_reconstruct():
|
| 582 |
+
s = Sum(n**2, (n, -1, 1))
|
| 583 |
+
assert s == Sum(*s.args)
|
| 584 |
+
raises(ValueError, lambda: Sum(x, x))
|
| 585 |
+
raises(ValueError, lambda: Sum(x, (x, 1)))
|
| 586 |
+
|
| 587 |
+
|
| 588 |
+
def test_limit_subs():
|
| 589 |
+
for F in (Sum, Product, Integral):
|
| 590 |
+
assert F(a*exp(a), (a, -2, 2)) == F(a*exp(a), (a, -b, b)).subs(b, 2)
|
| 591 |
+
assert F(a, (a, F(b, (b, 1, 2)), 4)).subs(F(b, (b, 1, 2)), c) == \
|
| 592 |
+
F(a, (a, c, 4))
|
| 593 |
+
assert F(x, (x, 1, x + y)).subs(x, 1) == F(x, (x, 1, y + 1))
|
| 594 |
+
|
| 595 |
+
|
| 596 |
+
def test_function_subs():
|
| 597 |
+
S = Sum(x*f(y),(x,0,oo),(y,0,oo))
|
| 598 |
+
assert S.subs(f(y),y) == Sum(x*y,(x,0,oo),(y,0,oo))
|
| 599 |
+
assert S.subs(f(x),x) == S
|
| 600 |
+
raises(ValueError, lambda: S.subs(f(y),x+y) )
|
| 601 |
+
S = Sum(x*log(y),(x,0,oo),(y,0,oo))
|
| 602 |
+
assert S.subs(log(y),y) == S
|
| 603 |
+
S = Sum(x*f(y),(x,0,oo),(y,0,oo))
|
| 604 |
+
assert S.subs(f(y),y) == Sum(x*y,(x,0,oo),(y,0,oo))
|
| 605 |
+
|
| 606 |
+
|
| 607 |
+
def test_equality():
|
| 608 |
+
# if this fails remove special handling below
|
| 609 |
+
raises(ValueError, lambda: Sum(x, x))
|
| 610 |
+
r = symbols('x', real=True)
|
| 611 |
+
for F in (Sum, Product, Integral):
|
| 612 |
+
try:
|
| 613 |
+
assert F(x, x) != F(y, y)
|
| 614 |
+
assert F(x, (x, 1, 2)) != F(x, x)
|
| 615 |
+
assert F(x, (x, x)) != F(x, x) # or else they print the same
|
| 616 |
+
assert F(1, x) != F(1, y)
|
| 617 |
+
except ValueError:
|
| 618 |
+
pass
|
| 619 |
+
assert F(a, (x, 1, 2)) != F(a, (x, 1, 3)) # diff limit
|
| 620 |
+
assert F(a, (x, 1, x)) != F(a, (y, 1, y))
|
| 621 |
+
assert F(a, (x, 1, 2)) != F(b, (x, 1, 2)) # diff expression
|
| 622 |
+
assert F(x, (x, 1, 2)) != F(r, (r, 1, 2)) # diff assumptions
|
| 623 |
+
assert F(1, (x, 1, x)) != F(1, (y, 1, x)) # only dummy is diff
|
| 624 |
+
assert F(1, (x, 1, x)).dummy_eq(F(1, (y, 1, x)))
|
| 625 |
+
|
| 626 |
+
# issue 5265
|
| 627 |
+
assert Sum(x, (x, 1, x)).subs(x, a) == Sum(x, (x, 1, a))
|
| 628 |
+
|
| 629 |
+
|
| 630 |
+
def test_Sum_doit():
|
| 631 |
+
assert Sum(n*Integral(a**2), (n, 0, 2)).doit() == a**3
|
| 632 |
+
assert Sum(n*Integral(a**2), (n, 0, 2)).doit(deep=False) == \
|
| 633 |
+
3*Integral(a**2)
|
| 634 |
+
assert summation(n*Integral(a**2), (n, 0, 2)) == 3*Integral(a**2)
|
| 635 |
+
|
| 636 |
+
# test nested sum evaluation
|
| 637 |
+
s = Sum( Sum( Sum(2,(z,1,n+1)), (y,x+1,n)), (x,1,n))
|
| 638 |
+
assert 0 == (s.doit() - n*(n+1)*(n-1)).factor()
|
| 639 |
+
|
| 640 |
+
# Integer assumes finite
|
| 641 |
+
assert Sum(KroneckerDelta(x, y), (x, -oo, oo)).doit() == Piecewise((1, And(-oo < y, y < oo)), (0, True))
|
| 642 |
+
assert Sum(KroneckerDelta(m, n), (m, -oo, oo)).doit() == 1
|
| 643 |
+
assert Sum(m*KroneckerDelta(x, y), (x, -oo, oo)).doit() == Piecewise((m, And(-oo < y, y < oo)), (0, True))
|
| 644 |
+
assert Sum(x*KroneckerDelta(m, n), (m, -oo, oo)).doit() == x
|
| 645 |
+
assert Sum(Sum(KroneckerDelta(m, n), (m, 1, 3)), (n, 1, 3)).doit() == 3
|
| 646 |
+
assert Sum(Sum(KroneckerDelta(k, m), (m, 1, 3)), (n, 1, 3)).doit() == \
|
| 647 |
+
3 * Piecewise((1, And(1 <= k, k <= 3)), (0, True))
|
| 648 |
+
assert Sum(f(n) * Sum(KroneckerDelta(m, n), (m, 0, oo)), (n, 1, 3)).doit() == \
|
| 649 |
+
f(1) + f(2) + f(3)
|
| 650 |
+
assert Sum(f(n) * Sum(KroneckerDelta(m, n), (m, 0, oo)), (n, 1, oo)).doit() == \
|
| 651 |
+
Sum(f(n), (n, 1, oo))
|
| 652 |
+
|
| 653 |
+
# issue 2597
|
| 654 |
+
nmax = symbols('N', integer=True, positive=True)
|
| 655 |
+
pw = Piecewise((1, And(1 <= n, n <= nmax)), (0, True))
|
| 656 |
+
assert Sum(pw, (n, 1, nmax)).doit() == Sum(Piecewise((1, nmax >= n),
|
| 657 |
+
(0, True)), (n, 1, nmax))
|
| 658 |
+
|
| 659 |
+
q, s = symbols('q, s')
|
| 660 |
+
assert summation(1/n**(2*s), (n, 1, oo)) == Piecewise((zeta(2*s), 2*re(s) > 1),
|
| 661 |
+
(Sum(n**(-2*s), (n, 1, oo)), True))
|
| 662 |
+
assert summation(1/(n+1)**s, (n, 0, oo)) == Piecewise((zeta(s), re(s) > 1),
|
| 663 |
+
(Sum((n + 1)**(-s), (n, 0, oo)), True))
|
| 664 |
+
assert summation(1/(n+q)**s, (n, 0, oo)) == Piecewise(
|
| 665 |
+
(zeta(s, q), And(~Contains(-q, S.Naturals0), re(s) > 1)),
|
| 666 |
+
(Sum((n + q)**(-s), (n, 0, oo)), True))
|
| 667 |
+
assert summation(1/(n+q)**s, (n, q, oo)) == Piecewise(
|
| 668 |
+
(zeta(s, 2*q), And(~Contains(-2*q, S.Naturals0), re(s) > 1)),
|
| 669 |
+
(Sum((n + q)**(-s), (n, q, oo)), True))
|
| 670 |
+
assert summation(1/n**2, (n, 1, oo)) == zeta(2)
|
| 671 |
+
assert summation(1/n**s, (n, 0, oo)) == Sum(n**(-s), (n, 0, oo))
|
| 672 |
+
assert summation(1/(n+1)**(2+I), (n, 0, oo)) == zeta(2+I)
|
| 673 |
+
t = symbols('t', real=True, positive=True)
|
| 674 |
+
assert summation(1/(n+I)**(t+1), (n, 0, oo)) == zeta(t+1, I)
|
| 675 |
+
|
| 676 |
+
|
| 677 |
+
def test_Product_doit():
|
| 678 |
+
assert Product(n*Integral(a**2), (n, 1, 3)).doit() == 2 * a**9 / 9
|
| 679 |
+
assert Product(n*Integral(a**2), (n, 1, 3)).doit(deep=False) == \
|
| 680 |
+
6*Integral(a**2)**3
|
| 681 |
+
assert product(n*Integral(a**2), (n, 1, 3)) == 6*Integral(a**2)**3
|
| 682 |
+
|
| 683 |
+
|
| 684 |
+
def test_Sum_interface():
|
| 685 |
+
assert isinstance(Sum(0, (n, 0, 2)), Sum)
|
| 686 |
+
assert Sum(nan, (n, 0, 2)) is nan
|
| 687 |
+
assert Sum(nan, (n, 0, oo)) is nan
|
| 688 |
+
assert Sum(0, (n, 0, 2)).doit() == 0
|
| 689 |
+
assert isinstance(Sum(0, (n, 0, oo)), Sum)
|
| 690 |
+
assert Sum(0, (n, 0, oo)).doit() == 0
|
| 691 |
+
raises(ValueError, lambda: Sum(1))
|
| 692 |
+
raises(ValueError, lambda: summation(1))
|
| 693 |
+
|
| 694 |
+
|
| 695 |
+
def test_diff():
|
| 696 |
+
assert Sum(x, (x, 1, 2)).diff(x) == 0
|
| 697 |
+
assert Sum(x*y, (x, 1, 2)).diff(x) == 0
|
| 698 |
+
assert Sum(x*y, (y, 1, 2)).diff(x) == Sum(y, (y, 1, 2))
|
| 699 |
+
e = Sum(x*y, (x, 1, a))
|
| 700 |
+
assert e.diff(a) == Derivative(e, a)
|
| 701 |
+
assert Sum(x*y, (x, 1, 3), (a, 2, 5)).diff(y).doit() == \
|
| 702 |
+
Sum(x*y, (x, 1, 3), (a, 2, 5)).doit().diff(y) == 24
|
| 703 |
+
assert Sum(x, (x, 1, 2)).diff(y) == 0
|
| 704 |
+
|
| 705 |
+
|
| 706 |
+
def test_hypersum():
|
| 707 |
+
assert simplify(summation(x**n/fac(n), (n, 1, oo))) == -1 + exp(x)
|
| 708 |
+
assert summation((-1)**n * x**(2*n) / fac(2*n), (n, 0, oo)) == cos(x)
|
| 709 |
+
assert simplify(summation((-1)**n*x**(2*n + 1) /
|
| 710 |
+
factorial(2*n + 1), (n, 3, oo))) == -x + sin(x) + x**3/6 - x**5/120
|
| 711 |
+
|
| 712 |
+
assert summation(1/(n + 2)**3, (n, 1, oo)) == Rational(-9, 8) + zeta(3)
|
| 713 |
+
assert summation(1/n**4, (n, 1, oo)) == pi**4/90
|
| 714 |
+
|
| 715 |
+
s = summation(x**n*n, (n, -oo, 0))
|
| 716 |
+
assert s.is_Piecewise
|
| 717 |
+
assert s.args[0].args[0] == -1/(x*(1 - 1/x)**2)
|
| 718 |
+
assert s.args[0].args[1] == (abs(1/x) < 1)
|
| 719 |
+
|
| 720 |
+
m = Symbol('n', integer=True, positive=True)
|
| 721 |
+
assert summation(binomial(m, k), (k, 0, m)) == 2**m
|
| 722 |
+
|
| 723 |
+
|
| 724 |
+
def test_issue_4170():
|
| 725 |
+
assert summation(1/factorial(k), (k, 0, oo)) == E
|
| 726 |
+
|
| 727 |
+
|
| 728 |
+
def test_is_commutative():
|
| 729 |
+
from sympy.physics.secondquant import NO, F, Fd
|
| 730 |
+
m = Symbol('m', commutative=False)
|
| 731 |
+
for f in (Sum, Product, Integral):
|
| 732 |
+
assert f(z, (z, 1, 1)).is_commutative is True
|
| 733 |
+
assert f(z*y, (z, 1, 6)).is_commutative is True
|
| 734 |
+
assert f(m*x, (x, 1, 2)).is_commutative is False
|
| 735 |
+
|
| 736 |
+
assert f(NO(Fd(x)*F(y))*z, (z, 1, 2)).is_commutative is False
|
| 737 |
+
|
| 738 |
+
|
| 739 |
+
def test_is_zero():
|
| 740 |
+
for func in [Sum, Product]:
|
| 741 |
+
assert func(0, (x, 1, 1)).is_zero is True
|
| 742 |
+
assert func(x, (x, 1, 1)).is_zero is None
|
| 743 |
+
|
| 744 |
+
assert Sum(0, (x, 1, 0)).is_zero is True
|
| 745 |
+
assert Product(0, (x, 1, 0)).is_zero is False
|
| 746 |
+
|
| 747 |
+
|
| 748 |
+
def test_is_number():
|
| 749 |
+
# is number should not rely on evaluation or assumptions,
|
| 750 |
+
# it should be equivalent to `not foo.free_symbols`
|
| 751 |
+
assert Sum(1, (x, 1, 1)).is_number is True
|
| 752 |
+
assert Sum(1, (x, 1, x)).is_number is False
|
| 753 |
+
assert Sum(0, (x, y, z)).is_number is False
|
| 754 |
+
assert Sum(x, (y, 1, 2)).is_number is False
|
| 755 |
+
assert Sum(x, (y, 1, 1)).is_number is False
|
| 756 |
+
assert Sum(x, (x, 1, 2)).is_number is True
|
| 757 |
+
assert Sum(x*y, (x, 1, 2), (y, 1, 3)).is_number is True
|
| 758 |
+
|
| 759 |
+
assert Product(2, (x, 1, 1)).is_number is True
|
| 760 |
+
assert Product(2, (x, 1, y)).is_number is False
|
| 761 |
+
assert Product(0, (x, y, z)).is_number is False
|
| 762 |
+
assert Product(1, (x, y, z)).is_number is False
|
| 763 |
+
assert Product(x, (y, 1, x)).is_number is False
|
| 764 |
+
assert Product(x, (y, 1, 2)).is_number is False
|
| 765 |
+
assert Product(x, (y, 1, 1)).is_number is False
|
| 766 |
+
assert Product(x, (x, 1, 2)).is_number is True
|
| 767 |
+
|
| 768 |
+
|
| 769 |
+
def test_free_symbols():
|
| 770 |
+
for func in [Sum, Product]:
|
| 771 |
+
assert func(1, (x, 1, 2)).free_symbols == set()
|
| 772 |
+
assert func(0, (x, 1, y)).free_symbols == {y}
|
| 773 |
+
assert func(2, (x, 1, y)).free_symbols == {y}
|
| 774 |
+
assert func(x, (x, 1, 2)).free_symbols == set()
|
| 775 |
+
assert func(x, (x, 1, y)).free_symbols == {y}
|
| 776 |
+
assert func(x, (y, 1, y)).free_symbols == {x, y}
|
| 777 |
+
assert func(x, (y, 1, 2)).free_symbols == {x}
|
| 778 |
+
assert func(x, (y, 1, 1)).free_symbols == {x}
|
| 779 |
+
assert func(x, (y, 1, z)).free_symbols == {x, z}
|
| 780 |
+
assert func(x, (x, 1, y), (y, 1, 2)).free_symbols == set()
|
| 781 |
+
assert func(x, (x, 1, y), (y, 1, z)).free_symbols == {z}
|
| 782 |
+
assert func(x, (x, 1, y), (y, 1, y)).free_symbols == {y}
|
| 783 |
+
assert func(x, (y, 1, y), (y, 1, z)).free_symbols == {x, z}
|
| 784 |
+
assert Sum(1, (x, 1, y)).free_symbols == {y}
|
| 785 |
+
# free_symbols answers whether the object *as written* has free symbols,
|
| 786 |
+
# not whether the evaluated expression has free symbols
|
| 787 |
+
assert Product(1, (x, 1, y)).free_symbols == {y}
|
| 788 |
+
# don't count free symbols that are not independent of integration
|
| 789 |
+
# variable(s)
|
| 790 |
+
assert func(f(x), (f(x), 1, 2)).free_symbols == set()
|
| 791 |
+
assert func(f(x), (f(x), 1, x)).free_symbols == {x}
|
| 792 |
+
assert func(f(x), (f(x), 1, y)).free_symbols == {y}
|
| 793 |
+
assert func(f(x), (z, 1, y)).free_symbols == {x, y}
|
| 794 |
+
|
| 795 |
+
|
| 796 |
+
def test_conjugate_transpose():
|
| 797 |
+
A, B = symbols("A B", commutative=False)
|
| 798 |
+
p = Sum(A*B**n, (n, 1, 3))
|
| 799 |
+
assert p.adjoint().doit() == p.doit().adjoint()
|
| 800 |
+
assert p.conjugate().doit() == p.doit().conjugate()
|
| 801 |
+
assert p.transpose().doit() == p.doit().transpose()
|
| 802 |
+
|
| 803 |
+
p = Sum(B**n*A, (n, 1, 3))
|
| 804 |
+
assert p.adjoint().doit() == p.doit().adjoint()
|
| 805 |
+
assert p.conjugate().doit() == p.doit().conjugate()
|
| 806 |
+
assert p.transpose().doit() == p.doit().transpose()
|
| 807 |
+
|
| 808 |
+
|
| 809 |
+
def test_noncommutativity_honoured():
|
| 810 |
+
A, B = symbols("A B", commutative=False)
|
| 811 |
+
M = symbols('M', integer=True, positive=True)
|
| 812 |
+
p = Sum(A*B**n, (n, 1, M))
|
| 813 |
+
assert p.doit() == A*Piecewise((M, Eq(B, 1)),
|
| 814 |
+
((B - B**(M + 1))*(1 - B)**(-1), True))
|
| 815 |
+
|
| 816 |
+
p = Sum(B**n*A, (n, 1, M))
|
| 817 |
+
assert p.doit() == Piecewise((M, Eq(B, 1)),
|
| 818 |
+
((B - B**(M + 1))*(1 - B)**(-1), True))*A
|
| 819 |
+
|
| 820 |
+
p = Sum(B**n*A*B**n, (n, 1, M))
|
| 821 |
+
assert p.doit() == p
|
| 822 |
+
|
| 823 |
+
|
| 824 |
+
def test_issue_4171():
|
| 825 |
+
assert summation(factorial(2*k + 1)/factorial(2*k), (k, 0, oo)) is oo
|
| 826 |
+
assert summation(2*k + 1, (k, 0, oo)) is oo
|
| 827 |
+
|
| 828 |
+
|
| 829 |
+
def test_issue_6273():
|
| 830 |
+
assert Sum(x, (x, 1, n)).n(2, subs={n: 1}) == Float(1, 2)
|
| 831 |
+
|
| 832 |
+
|
| 833 |
+
def test_issue_6274():
|
| 834 |
+
assert Sum(x, (x, 1, 0)).doit() == 0
|
| 835 |
+
assert NS(Sum(x, (x, 1, 0))) == '0'
|
| 836 |
+
assert Sum(n, (n, 10, 5)).doit() == -30
|
| 837 |
+
assert NS(Sum(n, (n, 10, 5))) == '-30.0000000000000'
|
| 838 |
+
|
| 839 |
+
|
| 840 |
+
def test_simplify_sum():
|
| 841 |
+
y, t, v = symbols('y, t, v')
|
| 842 |
+
|
| 843 |
+
_simplify = lambda e: simplify(e, doit=False)
|
| 844 |
+
assert _simplify(Sum(x*y, (x, n, m), (y, a, k)) + \
|
| 845 |
+
Sum(y, (x, n, m), (y, a, k))) == Sum(y * (x + 1), (x, n, m), (y, a, k))
|
| 846 |
+
assert _simplify(Sum(x, (x, n, m)) + Sum(x, (x, m + 1, a))) == \
|
| 847 |
+
Sum(x, (x, n, a))
|
| 848 |
+
assert _simplify(Sum(x, (x, k + 1, a)) + Sum(x, (x, n, k))) == \
|
| 849 |
+
Sum(x, (x, n, a))
|
| 850 |
+
assert _simplify(Sum(x, (x, k + 1, a)) + Sum(x + 1, (x, n, k))) == \
|
| 851 |
+
Sum(x, (x, n, a)) + Sum(1, (x, n, k))
|
| 852 |
+
assert _simplify(Sum(x, (x, 0, 3)) * 3 + 3 * Sum(x, (x, 4, 6)) + \
|
| 853 |
+
4 * Sum(z, (z, 0, 1))) == 4*Sum(z, (z, 0, 1)) + 3*Sum(x, (x, 0, 6))
|
| 854 |
+
assert _simplify(3*Sum(x**2, (x, a, b)) + Sum(x, (x, a, b))) == \
|
| 855 |
+
Sum(x*(3*x + 1), (x, a, b))
|
| 856 |
+
assert _simplify(Sum(x**3, (x, n, k)) * 3 + 3 * Sum(x, (x, n, k)) + \
|
| 857 |
+
4 * y * Sum(z, (z, n, k))) + 1 == \
|
| 858 |
+
4*y*Sum(z, (z, n, k)) + 3*Sum(x**3 + x, (x, n, k)) + 1
|
| 859 |
+
assert _simplify(Sum(x, (x, a, b)) + 1 + Sum(x, (x, b + 1, c))) == \
|
| 860 |
+
1 + Sum(x, (x, a, c))
|
| 861 |
+
assert _simplify(Sum(x, (t, a, b)) + Sum(y, (t, a, b)) + \
|
| 862 |
+
Sum(x, (t, b+1, c))) == x * Sum(1, (t, a, c)) + y * Sum(1, (t, a, b))
|
| 863 |
+
assert _simplify(Sum(x, (t, a, b)) + Sum(x, (t, b+1, c)) + \
|
| 864 |
+
Sum(y, (t, a, b))) == x * Sum(1, (t, a, c)) + y * Sum(1, (t, a, b))
|
| 865 |
+
assert _simplify(Sum(x, (t, a, b)) + 2 * Sum(x, (t, b+1, c))) == \
|
| 866 |
+
_simplify(Sum(x, (t, a, b)) + Sum(x, (t, b+1, c)) + Sum(x, (t, b+1, c)))
|
| 867 |
+
assert _simplify(Sum(x, (x, a, b))*Sum(x**2, (x, a, b))) == \
|
| 868 |
+
Sum(x, (x, a, b)) * Sum(x**2, (x, a, b))
|
| 869 |
+
assert _simplify(Sum(x, (t, a, b)) + Sum(y, (t, a, b)) + Sum(z, (t, a, b))) \
|
| 870 |
+
== (x + y + z) * Sum(1, (t, a, b)) # issue 8596
|
| 871 |
+
assert _simplify(Sum(x, (t, a, b)) + Sum(y, (t, a, b)) + Sum(z, (t, a, b)) + \
|
| 872 |
+
Sum(v, (t, a, b))) == (x + y + z + v) * Sum(1, (t, a, b)) # issue 8596
|
| 873 |
+
assert _simplify(Sum(x * y, (x, a, b)) / (3 * y)) == \
|
| 874 |
+
(Sum(x, (x, a, b)) / 3)
|
| 875 |
+
assert _simplify(Sum(f(x) * y * z, (x, a, b)) / (y * z)) \
|
| 876 |
+
== Sum(f(x), (x, a, b))
|
| 877 |
+
assert _simplify(Sum(c * x, (x, a, b)) - c * Sum(x, (x, a, b))) == 0
|
| 878 |
+
assert _simplify(c * (Sum(x, (x, a, b)) + y)) == c * (y + Sum(x, (x, a, b)))
|
| 879 |
+
assert _simplify(c * (Sum(x, (x, a, b)) + y * Sum(x, (x, a, b)))) == \
|
| 880 |
+
c * (y + 1) * Sum(x, (x, a, b))
|
| 881 |
+
assert _simplify(Sum(Sum(c * x, (x, a, b)), (y, a, b))) == \
|
| 882 |
+
c * Sum(x, (x, a, b), (y, a, b))
|
| 883 |
+
assert _simplify(Sum((3 + y) * Sum(c * x, (x, a, b)), (y, a, b))) == \
|
| 884 |
+
c * Sum((3 + y), (y, a, b)) * Sum(x, (x, a, b))
|
| 885 |
+
assert _simplify(Sum((3 + t) * Sum(c * t, (x, a, b)), (y, a, b))) == \
|
| 886 |
+
c*t*(t + 3)*Sum(1, (x, a, b))*Sum(1, (y, a, b))
|
| 887 |
+
assert _simplify(Sum(Sum(d * t, (x, a, b - 1)) + \
|
| 888 |
+
Sum(d * t, (x, b, c)), (t, a, b))) == \
|
| 889 |
+
d * Sum(1, (x, a, c)) * Sum(t, (t, a, b))
|
| 890 |
+
assert _simplify(Sum(sin(t)**2 + cos(t)**2 + 1, (t, a, b))) == \
|
| 891 |
+
2 * Sum(1, (t, a, b))
|
| 892 |
+
|
| 893 |
+
|
| 894 |
+
def test_change_index():
|
| 895 |
+
b, v, w = symbols('b, v, w', integer = True)
|
| 896 |
+
|
| 897 |
+
assert Sum(x, (x, a, b)).change_index(x, x + 1, y) == \
|
| 898 |
+
Sum(y - 1, (y, a + 1, b + 1))
|
| 899 |
+
assert Sum(x**2, (x, a, b)).change_index( x, x - 1) == \
|
| 900 |
+
Sum((x+1)**2, (x, a - 1, b - 1))
|
| 901 |
+
assert Sum(x**2, (x, a, b)).change_index( x, -x, y) == \
|
| 902 |
+
Sum((-y)**2, (y, -b, -a))
|
| 903 |
+
assert Sum(x, (x, a, b)).change_index( x, -x - 1) == \
|
| 904 |
+
Sum(-x - 1, (x, -b - 1, -a - 1))
|
| 905 |
+
assert Sum(x*y, (x, a, b), (y, c, d)).change_index( x, x - 1, z) == \
|
| 906 |
+
Sum((z + 1)*y, (z, a - 1, b - 1), (y, c, d))
|
| 907 |
+
assert Sum(x, (x, a, b)).change_index( x, x + v) == \
|
| 908 |
+
Sum(-v + x, (x, a + v, b + v))
|
| 909 |
+
assert Sum(x, (x, a, b)).change_index( x, -x - v) == \
|
| 910 |
+
Sum(-v - x, (x, -b - v, -a - v))
|
| 911 |
+
assert Sum(x, (x, a, b)).change_index(x, w*x, v) == \
|
| 912 |
+
Sum(v/w, (v, b*w, a*w))
|
| 913 |
+
raises(ValueError, lambda: Sum(x, (x, a, b)).change_index(x, 2*x))
|
| 914 |
+
|
| 915 |
+
|
| 916 |
+
def test_reorder():
|
| 917 |
+
b, y, c, d, z = symbols('b, y, c, d, z', integer = True)
|
| 918 |
+
|
| 919 |
+
assert Sum(x*y, (x, a, b), (y, c, d)).reorder((0, 1)) == \
|
| 920 |
+
Sum(x*y, (y, c, d), (x, a, b))
|
| 921 |
+
assert Sum(x, (x, a, b), (x, c, d)).reorder((0, 1)) == \
|
| 922 |
+
Sum(x, (x, c, d), (x, a, b))
|
| 923 |
+
assert Sum(x*y + z, (x, a, b), (z, m, n), (y, c, d)).reorder(\
|
| 924 |
+
(2, 0), (0, 1)) == Sum(x*y + z, (z, m, n), (y, c, d), (x, a, b))
|
| 925 |
+
assert Sum(x*y*z, (x, a, b), (y, c, d), (z, m, n)).reorder(\
|
| 926 |
+
(0, 1), (1, 2), (0, 2)) == Sum(x*y*z, (x, a, b), (z, m, n), (y, c, d))
|
| 927 |
+
assert Sum(x*y*z, (x, a, b), (y, c, d), (z, m, n)).reorder(\
|
| 928 |
+
(x, y), (y, z), (x, z)) == Sum(x*y*z, (x, a, b), (z, m, n), (y, c, d))
|
| 929 |
+
assert Sum(x*y, (x, a, b), (y, c, d)).reorder((x, 1)) == \
|
| 930 |
+
Sum(x*y, (y, c, d), (x, a, b))
|
| 931 |
+
assert Sum(x*y, (x, a, b), (y, c, d)).reorder((y, x)) == \
|
| 932 |
+
Sum(x*y, (y, c, d), (x, a, b))
|
| 933 |
+
|
| 934 |
+
|
| 935 |
+
def test_reverse_order():
|
| 936 |
+
assert Sum(x, (x, 0, 3)).reverse_order(0) == Sum(-x, (x, 4, -1))
|
| 937 |
+
assert Sum(x*y, (x, 1, 5), (y, 0, 6)).reverse_order(0, 1) == \
|
| 938 |
+
Sum(x*y, (x, 6, 0), (y, 7, -1))
|
| 939 |
+
assert Sum(x, (x, 1, 2)).reverse_order(0) == Sum(-x, (x, 3, 0))
|
| 940 |
+
assert Sum(x, (x, 1, 3)).reverse_order(0) == Sum(-x, (x, 4, 0))
|
| 941 |
+
assert Sum(x, (x, 1, a)).reverse_order(0) == Sum(-x, (x, a + 1, 0))
|
| 942 |
+
assert Sum(x, (x, a, 5)).reverse_order(0) == Sum(-x, (x, 6, a - 1))
|
| 943 |
+
assert Sum(x, (x, a + 1, a + 5)).reverse_order(0) == \
|
| 944 |
+
Sum(-x, (x, a + 6, a))
|
| 945 |
+
assert Sum(x, (x, a + 1, a + 2)).reverse_order(0) == \
|
| 946 |
+
Sum(-x, (x, a + 3, a))
|
| 947 |
+
assert Sum(x, (x, a + 1, a + 1)).reverse_order(0) == \
|
| 948 |
+
Sum(-x, (x, a + 2, a))
|
| 949 |
+
assert Sum(x, (x, a, b)).reverse_order(0) == Sum(-x, (x, b + 1, a - 1))
|
| 950 |
+
assert Sum(x, (x, a, b)).reverse_order(x) == Sum(-x, (x, b + 1, a - 1))
|
| 951 |
+
assert Sum(x*y, (x, a, b), (y, 2, 5)).reverse_order(x, 1) == \
|
| 952 |
+
Sum(x*y, (x, b + 1, a - 1), (y, 6, 1))
|
| 953 |
+
assert Sum(x*y, (x, a, b), (y, 2, 5)).reverse_order(y, x) == \
|
| 954 |
+
Sum(x*y, (x, b + 1, a - 1), (y, 6, 1))
|
| 955 |
+
|
| 956 |
+
|
| 957 |
+
def test_issue_7097():
|
| 958 |
+
assert sum(x**n/n for n in range(1, 401)) == summation(x**n/n, (n, 1, 400))
|
| 959 |
+
|
| 960 |
+
|
| 961 |
+
def test_factor_expand_subs():
|
| 962 |
+
# test factoring
|
| 963 |
+
assert Sum(4 * x, (x, 1, y)).factor() == 4 * Sum(x, (x, 1, y))
|
| 964 |
+
assert Sum(x * a, (x, 1, y)).factor() == a * Sum(x, (x, 1, y))
|
| 965 |
+
assert Sum(4 * x * a, (x, 1, y)).factor() == 4 * a * Sum(x, (x, 1, y))
|
| 966 |
+
assert Sum(4 * x * y, (x, 1, y)).factor() == 4 * y * Sum(x, (x, 1, y))
|
| 967 |
+
|
| 968 |
+
# test expand
|
| 969 |
+
_x = Symbol('x', zero=False)
|
| 970 |
+
assert Sum(x+1,(x,1,y)).expand() == Sum(x,(x,1,y)) + Sum(1,(x,1,y))
|
| 971 |
+
assert Sum(x+a*x**2,(x,1,y)).expand() == Sum(x,(x,1,y)) + Sum(a*x**2,(x,1,y))
|
| 972 |
+
assert Sum(_x**(n + 1)*(n + 1), (n, -1, oo)).expand() \
|
| 973 |
+
== Sum(n*_x*_x**n + _x*_x**n, (n, -1, oo))
|
| 974 |
+
assert Sum(x**(n + 1)*(n + 1), (n, -1, oo)).expand(power_exp=False) \
|
| 975 |
+
== Sum(n*x**(n + 1) + x**(n + 1), (n, -1, oo))
|
| 976 |
+
assert Sum(x**(n + 1)*(n + 1), (n, -1, oo)).expand(force=True) \
|
| 977 |
+
== Sum(x*x**n, (n, -1, oo)) + Sum(n*x*x**n, (n, -1, oo))
|
| 978 |
+
assert Sum(a*n+a*n**2,(n,0,4)).expand() \
|
| 979 |
+
== Sum(a*n,(n,0,4)) + Sum(a*n**2,(n,0,4))
|
| 980 |
+
assert Sum(_x**a*_x**n,(x,0,3)) \
|
| 981 |
+
== Sum(_x**(a+n),(x,0,3)).expand(power_exp=True)
|
| 982 |
+
_a, _n = symbols('a n', positive=True)
|
| 983 |
+
assert Sum(x**(_a+_n),(x,0,3)).expand(power_exp=True) \
|
| 984 |
+
== Sum(x**_a*x**_n, (x, 0, 3))
|
| 985 |
+
assert Sum(x**(_a-_n),(x,0,3)).expand(power_exp=True) \
|
| 986 |
+
== Sum(x**(_a-_n),(x,0,3)).expand(power_exp=False)
|
| 987 |
+
|
| 988 |
+
# test subs
|
| 989 |
+
assert Sum(1/(1+a*x**2),(x,0,3)).subs([(a,3)]) == Sum(1/(1+3*x**2),(x,0,3))
|
| 990 |
+
assert Sum(x*y,(x,0,y),(y,0,x)).subs([(x,3)]) == Sum(x*y,(x,0,y),(y,0,3))
|
| 991 |
+
assert Sum(x,(x,1,10)).subs([(x,y-2)]) == Sum(x,(x,1,10))
|
| 992 |
+
assert Sum(1/x,(x,1,10)).subs([(x,(3+n)**3)]) == Sum(1/x,(x,1,10))
|
| 993 |
+
assert Sum(1/x,(x,1,10)).subs([(x,3*x-2)]) == Sum(1/x,(x,1,10))
|
| 994 |
+
|
| 995 |
+
|
| 996 |
+
def test_distribution_over_equality():
|
| 997 |
+
assert Product(Eq(x*2, f(x)), (x, 1, 3)).doit() == Eq(48, f(1)*f(2)*f(3))
|
| 998 |
+
assert Sum(Eq(f(x), x**2), (x, 0, y)) == \
|
| 999 |
+
Eq(Sum(f(x), (x, 0, y)), Sum(x**2, (x, 0, y)))
|
| 1000 |
+
|
| 1001 |
+
|
| 1002 |
+
def test_issue_2787():
|
| 1003 |
+
n, k = symbols('n k', positive=True, integer=True)
|
| 1004 |
+
p = symbols('p', positive=True)
|
| 1005 |
+
binomial_dist = binomial(n, k)*p**k*(1 - p)**(n - k)
|
| 1006 |
+
s = Sum(binomial_dist*k, (k, 0, n))
|
| 1007 |
+
res = s.doit().simplify()
|
| 1008 |
+
ans = Piecewise(
|
| 1009 |
+
(n*p, x),
|
| 1010 |
+
(Sum(k*p**k*binomial(n, k)*(1 - p)**(n - k), (k, 0, n)),
|
| 1011 |
+
True)).subs(x, (Eq(n, 1) | (n > 1)) & (p/Abs(p - 1) <= 1))
|
| 1012 |
+
ans2 = Piecewise(
|
| 1013 |
+
(n*p, x),
|
| 1014 |
+
(factorial(n)*Sum(p**k*(1 - p)**(-k + n)/
|
| 1015 |
+
(factorial(-k + n)*factorial(k - 1)), (k, 0, n)),
|
| 1016 |
+
True)).subs(x, (Eq(n, 1) | (n > 1)) & (p/Abs(p - 1) <= 1))
|
| 1017 |
+
assert res in [ans, ans2] # XXX system dependent
|
| 1018 |
+
# Issue #17165: make sure that another simplify does not complicate
|
| 1019 |
+
# the result by much. Why didn't first simplify replace
|
| 1020 |
+
# Eq(n, 1) | (n > 1) with True?
|
| 1021 |
+
assert res.simplify().count_ops() <= res.count_ops() + 2
|
| 1022 |
+
|
| 1023 |
+
|
| 1024 |
+
def test_issue_4668():
|
| 1025 |
+
assert summation(1/n, (n, 2, oo)) is oo
|
| 1026 |
+
|
| 1027 |
+
|
| 1028 |
+
def test_matrix_sum():
|
| 1029 |
+
A = Matrix([[0, 1], [n, 0]])
|
| 1030 |
+
|
| 1031 |
+
result = Sum(A, (n, 0, 3)).doit()
|
| 1032 |
+
assert result == Matrix([[0, 4], [6, 0]])
|
| 1033 |
+
assert result.__class__ == ImmutableDenseMatrix
|
| 1034 |
+
|
| 1035 |
+
A = SparseMatrix([[0, 1], [n, 0]])
|
| 1036 |
+
|
| 1037 |
+
result = Sum(A, (n, 0, 3)).doit()
|
| 1038 |
+
assert result.__class__ == ImmutableSparseMatrix
|
| 1039 |
+
|
| 1040 |
+
|
| 1041 |
+
def test_failing_matrix_sum():
|
| 1042 |
+
n = Symbol('n')
|
| 1043 |
+
# TODO Implement matrix geometric series summation.
|
| 1044 |
+
A = Matrix([[0, 1, 0], [-1, 0, 0], [0, 0, 0]])
|
| 1045 |
+
assert Sum(A ** n, (n, 1, 4)).doit() == \
|
| 1046 |
+
Matrix([[0, 0, 0], [0, 0, 0], [0, 0, 0]])
|
| 1047 |
+
# issue sympy/sympy#16989
|
| 1048 |
+
assert summation(A**n, (n, 1, 1)) == A
|
| 1049 |
+
|
| 1050 |
+
|
| 1051 |
+
def test_indexed_idx_sum():
|
| 1052 |
+
i = symbols('i', cls=Idx)
|
| 1053 |
+
r = Indexed('r', i)
|
| 1054 |
+
assert Sum(r, (i, 0, 3)).doit() == sum(r.xreplace({i: j}) for j in range(4))
|
| 1055 |
+
assert Product(r, (i, 0, 3)).doit() == prod([r.xreplace({i: j}) for j in range(4)])
|
| 1056 |
+
|
| 1057 |
+
j = symbols('j', integer=True)
|
| 1058 |
+
assert Sum(r, (i, j, j+2)).doit() == sum(r.xreplace({i: j+k}) for k in range(3))
|
| 1059 |
+
assert Product(r, (i, j, j+2)).doit() == prod([r.xreplace({i: j+k}) for k in range(3)])
|
| 1060 |
+
|
| 1061 |
+
k = Idx('k', range=(1, 3))
|
| 1062 |
+
A = IndexedBase('A')
|
| 1063 |
+
assert Sum(A[k], k).doit() == sum(A[Idx(j, (1, 3))] for j in range(1, 4))
|
| 1064 |
+
assert Product(A[k], k).doit() == prod([A[Idx(j, (1, 3))] for j in range(1, 4)])
|
| 1065 |
+
|
| 1066 |
+
raises(ValueError, lambda: Sum(A[k], (k, 1, 4)))
|
| 1067 |
+
raises(ValueError, lambda: Sum(A[k], (k, 0, 3)))
|
| 1068 |
+
raises(ValueError, lambda: Sum(A[k], (k, 2, oo)))
|
| 1069 |
+
|
| 1070 |
+
raises(ValueError, lambda: Product(A[k], (k, 1, 4)))
|
| 1071 |
+
raises(ValueError, lambda: Product(A[k], (k, 0, 3)))
|
| 1072 |
+
raises(ValueError, lambda: Product(A[k], (k, 2, oo)))
|
| 1073 |
+
|
| 1074 |
+
|
| 1075 |
+
@slow
|
| 1076 |
+
def test_is_convergent():
|
| 1077 |
+
# divergence tests --
|
| 1078 |
+
assert Sum(n/(2*n + 1), (n, 1, oo)).is_convergent() is S.false
|
| 1079 |
+
assert Sum(factorial(n)/5**n, (n, 1, oo)).is_convergent() is S.false
|
| 1080 |
+
assert Sum(3**(-2*n - 1)*n**n, (n, 1, oo)).is_convergent() is S.false
|
| 1081 |
+
assert Sum((-1)**n*n, (n, 3, oo)).is_convergent() is S.false
|
| 1082 |
+
assert Sum((-1)**n, (n, 1, oo)).is_convergent() is S.false
|
| 1083 |
+
assert Sum(log(1/n), (n, 2, oo)).is_convergent() is S.false
|
| 1084 |
+
assert Sum(sin(n), (n, 1, oo)).is_convergent() is S.false
|
| 1085 |
+
|
| 1086 |
+
# Raabe's test --
|
| 1087 |
+
assert Sum(Product((3*m),(m,1,n))/Product((3*m+4),(m,1,n)),(n,1,oo)).is_convergent() is S.true
|
| 1088 |
+
|
| 1089 |
+
# root test --
|
| 1090 |
+
assert Sum((-12)**n/n, (n, 1, oo)).is_convergent() is S.false
|
| 1091 |
+
|
| 1092 |
+
# integral test --
|
| 1093 |
+
|
| 1094 |
+
# p-series test --
|
| 1095 |
+
assert Sum(1/(n**2 + 1), (n, 1, oo)).is_convergent() is S.true
|
| 1096 |
+
assert Sum(1/n**Rational(6, 5), (n, 1, oo)).is_convergent() is S.true
|
| 1097 |
+
assert Sum(2/(n*sqrt(n - 1)), (n, 2, oo)).is_convergent() is S.true
|
| 1098 |
+
assert Sum(1/(sqrt(n)*sqrt(n)), (n, 2, oo)).is_convergent() is S.false
|
| 1099 |
+
assert Sum(factorial(n) / factorial(n+2), (n, 1, oo)).is_convergent() is S.true
|
| 1100 |
+
assert Sum(rf(5,n)/rf(7,n),(n,1,oo)).is_convergent() is S.true
|
| 1101 |
+
assert Sum((rf(1, n)*rf(2, n))/(rf(3, n)*factorial(n)),(n,1,oo)).is_convergent() is S.false
|
| 1102 |
+
|
| 1103 |
+
# comparison test --
|
| 1104 |
+
assert Sum(1/(n + log(n)), (n, 1, oo)).is_convergent() is S.false
|
| 1105 |
+
assert Sum(1/(n**2*log(n)), (n, 2, oo)).is_convergent() is S.true
|
| 1106 |
+
assert Sum(1/(n*log(n)), (n, 2, oo)).is_convergent() is S.false
|
| 1107 |
+
assert Sum(2/(n*log(n)*log(log(n))**2), (n, 5, oo)).is_convergent() is S.true
|
| 1108 |
+
assert Sum(2/(n*log(n)**2), (n, 2, oo)).is_convergent() is S.true
|
| 1109 |
+
assert Sum((n - 1)/(n**2*log(n)**3), (n, 2, oo)).is_convergent() is S.true
|
| 1110 |
+
assert Sum(1/(n*log(n)*log(log(n))), (n, 5, oo)).is_convergent() is S.false
|
| 1111 |
+
assert Sum((n - 1)/(n*log(n)**3), (n, 3, oo)).is_convergent() is S.false
|
| 1112 |
+
assert Sum(2/(n**2*log(n)), (n, 2, oo)).is_convergent() is S.true
|
| 1113 |
+
assert Sum(1/(n*sqrt(log(n))*log(log(n))), (n, 100, oo)).is_convergent() is S.false
|
| 1114 |
+
assert Sum(log(log(n))/(n*log(n)**2), (n, 100, oo)).is_convergent() is S.true
|
| 1115 |
+
assert Sum(log(n)/n**2, (n, 5, oo)).is_convergent() is S.true
|
| 1116 |
+
|
| 1117 |
+
# alternating series tests --
|
| 1118 |
+
assert Sum((-1)**(n - 1)/(n**2 - 1), (n, 3, oo)).is_convergent() is S.true
|
| 1119 |
+
|
| 1120 |
+
# with -negativeInfinite Limits
|
| 1121 |
+
assert Sum(1/(n**2 + 1), (n, -oo, 1)).is_convergent() is S.true
|
| 1122 |
+
assert Sum(1/(n - 1), (n, -oo, -1)).is_convergent() is S.false
|
| 1123 |
+
assert Sum(1/(n**2 - 1), (n, -oo, -5)).is_convergent() is S.true
|
| 1124 |
+
assert Sum(1/(n**2 - 1), (n, -oo, 2)).is_convergent() is S.true
|
| 1125 |
+
assert Sum(1/(n**2 - 1), (n, -oo, oo)).is_convergent() is S.true
|
| 1126 |
+
|
| 1127 |
+
# piecewise functions
|
| 1128 |
+
f = Piecewise((n**(-2), n <= 1), (n**2, n > 1))
|
| 1129 |
+
assert Sum(f, (n, 1, oo)).is_convergent() is S.false
|
| 1130 |
+
assert Sum(f, (n, -oo, oo)).is_convergent() is S.false
|
| 1131 |
+
assert Sum(f, (n, 1, 100)).is_convergent() is S.true
|
| 1132 |
+
#assert Sum(f, (n, -oo, 1)).is_convergent() is S.true
|
| 1133 |
+
|
| 1134 |
+
# integral test
|
| 1135 |
+
|
| 1136 |
+
assert Sum(log(n)/n**3, (n, 1, oo)).is_convergent() is S.true
|
| 1137 |
+
assert Sum(-log(n)/n**3, (n, 1, oo)).is_convergent() is S.true
|
| 1138 |
+
# the following function has maxima located at (x, y) =
|
| 1139 |
+
# (1.2, 0.43), (3.0, -0.25) and (6.8, 0.050)
|
| 1140 |
+
eq = (x - 2)*(x**2 - 6*x + 4)*exp(-x)
|
| 1141 |
+
assert Sum(eq, (x, 1, oo)).is_convergent() is S.true
|
| 1142 |
+
assert Sum(eq, (x, 1, 2)).is_convergent() is S.true
|
| 1143 |
+
assert Sum(1/(x**3), (x, 1, oo)).is_convergent() is S.true
|
| 1144 |
+
assert Sum(1/(x**S.Half), (x, 1, oo)).is_convergent() is S.false
|
| 1145 |
+
|
| 1146 |
+
# issue 19545
|
| 1147 |
+
assert Sum(1/n - 3/(3*n +2), (n, 1, oo)).is_convergent() is S.true
|
| 1148 |
+
|
| 1149 |
+
# issue 19836
|
| 1150 |
+
assert Sum(4/(n + 2) - 5/(n + 1) + 1/n,(n, 7, oo)).is_convergent() is S.true
|
| 1151 |
+
|
| 1152 |
+
|
| 1153 |
+
def test_is_absolutely_convergent():
|
| 1154 |
+
assert Sum((-1)**n, (n, 1, oo)).is_absolutely_convergent() is S.false
|
| 1155 |
+
assert Sum((-1)**n/n**2, (n, 1, oo)).is_absolutely_convergent() is S.true
|
| 1156 |
+
|
| 1157 |
+
|
| 1158 |
+
@XFAIL
|
| 1159 |
+
def test_convergent_failing():
|
| 1160 |
+
# dirichlet tests
|
| 1161 |
+
assert Sum(sin(n)/n, (n, 1, oo)).is_convergent() is S.true
|
| 1162 |
+
assert Sum(sin(2*n)/n, (n, 1, oo)).is_convergent() is S.true
|
| 1163 |
+
|
| 1164 |
+
|
| 1165 |
+
def test_issue_6966():
|
| 1166 |
+
i, k, m = symbols('i k m', integer=True)
|
| 1167 |
+
z_i, q_i = symbols('z_i q_i')
|
| 1168 |
+
a_k = Sum(-q_i*z_i/k,(i,1,m))
|
| 1169 |
+
b_k = a_k.diff(z_i)
|
| 1170 |
+
assert isinstance(b_k, Sum)
|
| 1171 |
+
assert b_k == Sum(-q_i/k,(i,1,m))
|
| 1172 |
+
|
| 1173 |
+
|
| 1174 |
+
def test_issue_10156():
|
| 1175 |
+
cx = Sum(2*y**2*x, (x, 1,3))
|
| 1176 |
+
e = 2*y*Sum(2*cx*x**2, (x, 1, 9))
|
| 1177 |
+
assert e.factor() == \
|
| 1178 |
+
8*y**3*Sum(x, (x, 1, 3))*Sum(x**2, (x, 1, 9))
|
| 1179 |
+
|
| 1180 |
+
|
| 1181 |
+
def test_issue_10973():
|
| 1182 |
+
assert Sum((-n + (n**3 + 1)**(S(1)/3))/log(n), (n, 1, oo)).is_convergent() is S.true
|
| 1183 |
+
|
| 1184 |
+
|
| 1185 |
+
def test_issue_14103():
|
| 1186 |
+
assert Sum(sin(n)**2 + cos(n)**2 - 1, (n, 1, oo)).is_convergent() is S.true
|
| 1187 |
+
assert Sum(sin(pi*n), (n, 1, oo)).is_convergent() is S.true
|
| 1188 |
+
|
| 1189 |
+
|
| 1190 |
+
def test_issue_14129():
|
| 1191 |
+
x = Symbol('x', zero=False)
|
| 1192 |
+
assert Sum( k*x**k, (k, 0, n-1)).doit() == \
|
| 1193 |
+
Piecewise((n**2/2 - n/2, Eq(x, 1)), ((n*x*x**n -
|
| 1194 |
+
n*x**n - x*x**n + x)/(x - 1)**2, True))
|
| 1195 |
+
assert Sum( x**k, (k, 0, n-1)).doit() == \
|
| 1196 |
+
Piecewise((n, Eq(x, 1)), ((-x**n + 1)/(-x + 1), True))
|
| 1197 |
+
assert Sum( k*(x/y+x)**k, (k, 0, n-1)).doit() == \
|
| 1198 |
+
Piecewise((n*(n - 1)/2, Eq(x, y/(y + 1))),
|
| 1199 |
+
(x*(y + 1)*(n*x*y*(x + x/y)**(n - 1) +
|
| 1200 |
+
n*x*(x + x/y)**(n - 1) - n*y*(x + x/y)**(n - 1) -
|
| 1201 |
+
x*y*(x + x/y)**(n - 1) - x*(x + x/y)**(n - 1) + y)/
|
| 1202 |
+
(x*y + x - y)**2, True))
|
| 1203 |
+
|
| 1204 |
+
|
| 1205 |
+
def test_issue_14112():
|
| 1206 |
+
assert Sum((-1)**n/sqrt(n), (n, 1, oo)).is_absolutely_convergent() is S.false
|
| 1207 |
+
assert Sum((-1)**(2*n)/n, (n, 1, oo)).is_convergent() is S.false
|
| 1208 |
+
assert Sum((-2)**n + (-3)**n, (n, 1, oo)).is_convergent() is S.false
|
| 1209 |
+
|
| 1210 |
+
|
| 1211 |
+
def test_issue_14219():
|
| 1212 |
+
A = diag(0, 2, -3)
|
| 1213 |
+
res = diag(1, 15, -20)
|
| 1214 |
+
assert Sum(A**n, (n, 0, 3)).doit() == res
|
| 1215 |
+
|
| 1216 |
+
|
| 1217 |
+
def test_sin_times_absolutely_convergent():
|
| 1218 |
+
assert Sum(sin(n) / n**3, (n, 1, oo)).is_convergent() is S.true
|
| 1219 |
+
assert Sum(sin(n) * log(n) / n**3, (n, 1, oo)).is_convergent() is S.true
|
| 1220 |
+
|
| 1221 |
+
|
| 1222 |
+
def test_issue_14111():
|
| 1223 |
+
assert Sum(1/log(log(n)), (n, 22, oo)).is_convergent() is S.false
|
| 1224 |
+
|
| 1225 |
+
|
| 1226 |
+
def test_issue_14484():
|
| 1227 |
+
assert Sum(sin(n)/log(log(n)), (n, 22, oo)).is_convergent() is S.false
|
| 1228 |
+
|
| 1229 |
+
|
| 1230 |
+
def test_issue_14640():
|
| 1231 |
+
i, n = symbols("i n", integer=True)
|
| 1232 |
+
a, b, c = symbols("a b c", zero=False)
|
| 1233 |
+
|
| 1234 |
+
assert Sum(a**-i/(a - b), (i, 0, n)).doit() == Sum(
|
| 1235 |
+
1/(a*a**i - a**i*b), (i, 0, n)).doit() == Piecewise(
|
| 1236 |
+
(n + 1, Eq(1/a, 1)),
|
| 1237 |
+
((-a**(-n - 1) + 1)/(1 - 1/a), True))/(a - b)
|
| 1238 |
+
|
| 1239 |
+
assert Sum((b*a**i - c*a**i)**-2, (i, 0, n)).doit() == Piecewise(
|
| 1240 |
+
(n + 1, Eq(a**(-2), 1)),
|
| 1241 |
+
((-a**(-2*n - 2) + 1)/(1 - 1/a**2), True))/(b - c)**2
|
| 1242 |
+
|
| 1243 |
+
s = Sum(i*(a**(n - i) - b**(n - i))/(a - b), (i, 0, n)).doit()
|
| 1244 |
+
assert not s.has(Sum)
|
| 1245 |
+
assert s.subs({a: 2, b: 3, n: 5}) == 122
|
| 1246 |
+
|
| 1247 |
+
|
| 1248 |
+
def test_issue_15943():
|
| 1249 |
+
s = Sum(binomial(n, k)*factorial(n - k), (k, 0, n)).doit().rewrite(gamma)
|
| 1250 |
+
assert s == -E*(n + 1)*gamma(n + 1)*lowergamma(n + 1, 1)/gamma(n + 2
|
| 1251 |
+
) + E*gamma(n + 1)
|
| 1252 |
+
assert s.simplify() == E*(factorial(n) - lowergamma(n + 1, 1))
|
| 1253 |
+
|
| 1254 |
+
|
| 1255 |
+
def test_Sum_dummy_eq():
|
| 1256 |
+
assert not Sum(x, (x, a, b)).dummy_eq(1)
|
| 1257 |
+
assert not Sum(x, (x, a, b)).dummy_eq(Sum(x, (x, a, b), (a, 1, 2)))
|
| 1258 |
+
assert not Sum(x, (x, a, b)).dummy_eq(Sum(x, (x, a, c)))
|
| 1259 |
+
assert Sum(x, (x, a, b)).dummy_eq(Sum(x, (x, a, b)))
|
| 1260 |
+
d = Dummy()
|
| 1261 |
+
assert Sum(x, (x, a, d)).dummy_eq(Sum(x, (x, a, c)), c)
|
| 1262 |
+
assert not Sum(x, (x, a, d)).dummy_eq(Sum(x, (x, a, c)))
|
| 1263 |
+
assert Sum(x, (x, a, c)).dummy_eq(Sum(y, (y, a, c)))
|
| 1264 |
+
assert Sum(x, (x, a, d)).dummy_eq(Sum(y, (y, a, c)), c)
|
| 1265 |
+
assert not Sum(x, (x, a, d)).dummy_eq(Sum(y, (y, a, c)))
|
| 1266 |
+
|
| 1267 |
+
|
| 1268 |
+
def test_issue_15852():
|
| 1269 |
+
assert summation(x**y*y, (y, -oo, oo)).doit() == Sum(x**y*y, (y, -oo, oo))
|
| 1270 |
+
|
| 1271 |
+
|
| 1272 |
+
def test_exceptions():
|
| 1273 |
+
S = Sum(x, (x, a, b))
|
| 1274 |
+
raises(ValueError, lambda: S.change_index(x, x**2, y))
|
| 1275 |
+
S = Sum(x, (x, a, b), (x, 1, 4))
|
| 1276 |
+
raises(ValueError, lambda: S.index(x))
|
| 1277 |
+
S = Sum(x, (x, a, b), (y, 1, 4))
|
| 1278 |
+
raises(ValueError, lambda: S.reorder([x]))
|
| 1279 |
+
S = Sum(x, (x, y, b), (y, 1, 4))
|
| 1280 |
+
raises(ReorderError, lambda: S.reorder_limit(0, 1))
|
| 1281 |
+
S = Sum(x*y, (x, a, b), (y, 1, 4))
|
| 1282 |
+
raises(NotImplementedError, lambda: S.is_convergent())
|
| 1283 |
+
|
| 1284 |
+
|
| 1285 |
+
def test_sumproducts_assumptions():
|
| 1286 |
+
M = Symbol('M', integer=True, positive=True)
|
| 1287 |
+
|
| 1288 |
+
m = Symbol('m', integer=True)
|
| 1289 |
+
for func in [Sum, Product]:
|
| 1290 |
+
assert func(m, (m, -M, M)).is_positive is None
|
| 1291 |
+
assert func(m, (m, -M, M)).is_nonpositive is None
|
| 1292 |
+
assert func(m, (m, -M, M)).is_negative is None
|
| 1293 |
+
assert func(m, (m, -M, M)).is_nonnegative is None
|
| 1294 |
+
assert func(m, (m, -M, M)).is_finite is True
|
| 1295 |
+
|
| 1296 |
+
m = Symbol('m', integer=True, nonnegative=True)
|
| 1297 |
+
for func in [Sum, Product]:
|
| 1298 |
+
assert func(m, (m, 0, M)).is_positive is None
|
| 1299 |
+
assert func(m, (m, 0, M)).is_nonpositive is None
|
| 1300 |
+
assert func(m, (m, 0, M)).is_negative is False
|
| 1301 |
+
assert func(m, (m, 0, M)).is_nonnegative is True
|
| 1302 |
+
assert func(m, (m, 0, M)).is_finite is True
|
| 1303 |
+
|
| 1304 |
+
m = Symbol('m', integer=True, positive=True)
|
| 1305 |
+
for func in [Sum, Product]:
|
| 1306 |
+
assert func(m, (m, 1, M)).is_positive is True
|
| 1307 |
+
assert func(m, (m, 1, M)).is_nonpositive is False
|
| 1308 |
+
assert func(m, (m, 1, M)).is_negative is False
|
| 1309 |
+
assert func(m, (m, 1, M)).is_nonnegative is True
|
| 1310 |
+
assert func(m, (m, 1, M)).is_finite is True
|
| 1311 |
+
|
| 1312 |
+
m = Symbol('m', integer=True, negative=True)
|
| 1313 |
+
assert Sum(m, (m, -M, -1)).is_positive is False
|
| 1314 |
+
assert Sum(m, (m, -M, -1)).is_nonpositive is True
|
| 1315 |
+
assert Sum(m, (m, -M, -1)).is_negative is True
|
| 1316 |
+
assert Sum(m, (m, -M, -1)).is_nonnegative is False
|
| 1317 |
+
assert Sum(m, (m, -M, -1)).is_finite is True
|
| 1318 |
+
assert Product(m, (m, -M, -1)).is_positive is None
|
| 1319 |
+
assert Product(m, (m, -M, -1)).is_nonpositive is None
|
| 1320 |
+
assert Product(m, (m, -M, -1)).is_negative is None
|
| 1321 |
+
assert Product(m, (m, -M, -1)).is_nonnegative is None
|
| 1322 |
+
assert Product(m, (m, -M, -1)).is_finite is True
|
| 1323 |
+
|
| 1324 |
+
m = Symbol('m', integer=True, nonpositive=True)
|
| 1325 |
+
assert Sum(m, (m, -M, 0)).is_positive is False
|
| 1326 |
+
assert Sum(m, (m, -M, 0)).is_nonpositive is True
|
| 1327 |
+
assert Sum(m, (m, -M, 0)).is_negative is None
|
| 1328 |
+
assert Sum(m, (m, -M, 0)).is_nonnegative is None
|
| 1329 |
+
assert Sum(m, (m, -M, 0)).is_finite is True
|
| 1330 |
+
assert Product(m, (m, -M, 0)).is_positive is None
|
| 1331 |
+
assert Product(m, (m, -M, 0)).is_nonpositive is None
|
| 1332 |
+
assert Product(m, (m, -M, 0)).is_negative is None
|
| 1333 |
+
assert Product(m, (m, -M, 0)).is_nonnegative is None
|
| 1334 |
+
assert Product(m, (m, -M, 0)).is_finite is True
|
| 1335 |
+
|
| 1336 |
+
m = Symbol('m', integer=True)
|
| 1337 |
+
assert Sum(2, (m, 0, oo)).is_positive is None
|
| 1338 |
+
assert Sum(2, (m, 0, oo)).is_nonpositive is None
|
| 1339 |
+
assert Sum(2, (m, 0, oo)).is_negative is None
|
| 1340 |
+
assert Sum(2, (m, 0, oo)).is_nonnegative is None
|
| 1341 |
+
assert Sum(2, (m, 0, oo)).is_finite is None
|
| 1342 |
+
|
| 1343 |
+
assert Product(2, (m, 0, oo)).is_positive is None
|
| 1344 |
+
assert Product(2, (m, 0, oo)).is_nonpositive is None
|
| 1345 |
+
assert Product(2, (m, 0, oo)).is_negative is False
|
| 1346 |
+
assert Product(2, (m, 0, oo)).is_nonnegative is None
|
| 1347 |
+
assert Product(2, (m, 0, oo)).is_finite is None
|
| 1348 |
+
|
| 1349 |
+
assert Product(0, (x, M, M-1)).is_positive is True
|
| 1350 |
+
assert Product(0, (x, M, M-1)).is_finite is True
|
| 1351 |
+
|
| 1352 |
+
|
| 1353 |
+
def test_expand_with_assumptions():
|
| 1354 |
+
M = Symbol('M', integer=True, positive=True)
|
| 1355 |
+
x = Symbol('x', positive=True)
|
| 1356 |
+
m = Symbol('m', nonnegative=True)
|
| 1357 |
+
assert log(Product(x**m, (m, 0, M))).expand() == Sum(m*log(x), (m, 0, M))
|
| 1358 |
+
assert log(Product(exp(x**m), (m, 0, M))).expand() == Sum(x**m, (m, 0, M))
|
| 1359 |
+
assert log(Product(x**m, (m, 0, M))).rewrite(Sum).expand() == Sum(m*log(x), (m, 0, M))
|
| 1360 |
+
assert log(Product(exp(x**m), (m, 0, M))).rewrite(Sum).expand() == Sum(x**m, (m, 0, M))
|
| 1361 |
+
|
| 1362 |
+
n = Symbol('n', nonnegative=True)
|
| 1363 |
+
i, j = symbols('i,j', positive=True, integer=True)
|
| 1364 |
+
x, y = symbols('x,y', positive=True)
|
| 1365 |
+
assert log(Product(x**i*y**j, (i, 1, n), (j, 1, m))).expand() \
|
| 1366 |
+
== Sum(i*log(x) + j*log(y), (i, 1, n), (j, 1, m))
|
| 1367 |
+
|
| 1368 |
+
m = Symbol('m', nonnegative=True, integer=True)
|
| 1369 |
+
s = Sum(x**m, (m, 0, M))
|
| 1370 |
+
s_as_product = s.rewrite(Product)
|
| 1371 |
+
assert s_as_product.has(Product)
|
| 1372 |
+
assert s_as_product == log(Product(exp(x**m), (m, 0, M)))
|
| 1373 |
+
assert s_as_product.expand() == s
|
| 1374 |
+
s5 = s.subs(M, 5)
|
| 1375 |
+
s5_as_product = s5.rewrite(Product)
|
| 1376 |
+
assert s5_as_product.has(Product)
|
| 1377 |
+
assert s5_as_product.doit().expand() == s5.doit()
|
| 1378 |
+
|
| 1379 |
+
|
| 1380 |
+
def test_has_finite_limits():
|
| 1381 |
+
x = Symbol('x')
|
| 1382 |
+
assert Sum(1, (x, 1, 9)).has_finite_limits is True
|
| 1383 |
+
assert Sum(1, (x, 1, oo)).has_finite_limits is False
|
| 1384 |
+
M = Symbol('M')
|
| 1385 |
+
assert Sum(1, (x, 1, M)).has_finite_limits is None
|
| 1386 |
+
M = Symbol('M', positive=True)
|
| 1387 |
+
assert Sum(1, (x, 1, M)).has_finite_limits is True
|
| 1388 |
+
x = Symbol('x', positive=True)
|
| 1389 |
+
M = Symbol('M')
|
| 1390 |
+
assert Sum(1, (x, 1, M)).has_finite_limits is True
|
| 1391 |
+
|
| 1392 |
+
assert Sum(1, (x, 1, M), (y, -oo, oo)).has_finite_limits is False
|
| 1393 |
+
|
| 1394 |
+
def test_has_reversed_limits():
|
| 1395 |
+
assert Sum(1, (x, 1, 1)).has_reversed_limits is False
|
| 1396 |
+
assert Sum(1, (x, 1, 9)).has_reversed_limits is False
|
| 1397 |
+
assert Sum(1, (x, 1, -9)).has_reversed_limits is True
|
| 1398 |
+
assert Sum(1, (x, 1, 0)).has_reversed_limits is True
|
| 1399 |
+
assert Sum(1, (x, 1, oo)).has_reversed_limits is False
|
| 1400 |
+
M = Symbol('M')
|
| 1401 |
+
assert Sum(1, (x, 1, M)).has_reversed_limits is None
|
| 1402 |
+
M = Symbol('M', positive=True, integer=True)
|
| 1403 |
+
assert Sum(1, (x, 1, M)).has_reversed_limits is False
|
| 1404 |
+
assert Sum(1, (x, 1, M), (y, -oo, oo)).has_reversed_limits is False
|
| 1405 |
+
M = Symbol('M', negative=True)
|
| 1406 |
+
assert Sum(1, (x, 1, M)).has_reversed_limits is True
|
| 1407 |
+
|
| 1408 |
+
assert Sum(1, (x, 1, M), (y, -oo, oo)).has_reversed_limits is True
|
| 1409 |
+
assert Sum(1, (x, oo, oo)).has_reversed_limits is None
|
| 1410 |
+
|
| 1411 |
+
|
| 1412 |
+
def test_has_empty_sequence():
|
| 1413 |
+
assert Sum(1, (x, 1, 1)).has_empty_sequence is False
|
| 1414 |
+
assert Sum(1, (x, 1, 9)).has_empty_sequence is False
|
| 1415 |
+
assert Sum(1, (x, 1, -9)).has_empty_sequence is False
|
| 1416 |
+
assert Sum(1, (x, 1, 0)).has_empty_sequence is True
|
| 1417 |
+
assert Sum(1, (x, y, y - 1)).has_empty_sequence is True
|
| 1418 |
+
assert Sum(1, (x, 3, 2), (y, -oo, oo)).has_empty_sequence is True
|
| 1419 |
+
assert Sum(1, (y, -oo, oo), (x, 3, 2)).has_empty_sequence is True
|
| 1420 |
+
assert Sum(1, (x, oo, oo)).has_empty_sequence is False
|
| 1421 |
+
|
| 1422 |
+
|
| 1423 |
+
def test_empty_sequence():
|
| 1424 |
+
assert Product(x*y, (x, -oo, oo), (y, 1, 0)).doit() == 1
|
| 1425 |
+
assert Product(x*y, (y, 1, 0), (x, -oo, oo)).doit() == 1
|
| 1426 |
+
assert Sum(x, (x, -oo, oo), (y, 1, 0)).doit() == 0
|
| 1427 |
+
assert Sum(x, (y, 1, 0), (x, -oo, oo)).doit() == 0
|
| 1428 |
+
|
| 1429 |
+
|
| 1430 |
+
def test_issue_8016():
|
| 1431 |
+
k = Symbol('k', integer=True)
|
| 1432 |
+
n, m = symbols('n, m', integer=True, positive=True)
|
| 1433 |
+
s = Sum(binomial(m, k)*binomial(m, n - k)*(-1)**k, (k, 0, n))
|
| 1434 |
+
assert s.doit().simplify() == \
|
| 1435 |
+
cos(pi*n/2)*gamma(m + 1)/gamma(n/2 + 1)/gamma(m - n/2 + 1)
|
| 1436 |
+
|
| 1437 |
+
|
| 1438 |
+
def test_issue_14313():
|
| 1439 |
+
assert Sum(S.Half**floor(n/2), (n, 1, oo)).is_convergent()
|
| 1440 |
+
|
| 1441 |
+
|
| 1442 |
+
def test_issue_14563():
|
| 1443 |
+
# The assertion was failing due to no assumptions methods in Sums and Product
|
| 1444 |
+
assert 1 % Sum(1, (x, 0, 1)) == 1
|
| 1445 |
+
|
| 1446 |
+
|
| 1447 |
+
def test_issue_16735():
|
| 1448 |
+
assert Sum(5**n/gamma(n+1), (n, 1, oo)).is_convergent() is S.true
|
| 1449 |
+
|
| 1450 |
+
|
| 1451 |
+
def test_issue_14871():
|
| 1452 |
+
assert Sum((Rational(1, 10))**n*rf(0, n)/factorial(n), (n, 0, oo)).rewrite(factorial).doit() == 1
|
| 1453 |
+
|
| 1454 |
+
|
| 1455 |
+
def test_issue_17165():
|
| 1456 |
+
n = symbols("n", integer=True)
|
| 1457 |
+
x = symbols('x')
|
| 1458 |
+
s = (x*Sum(x**n, (n, -1, oo)))
|
| 1459 |
+
ssimp = s.doit().simplify()
|
| 1460 |
+
|
| 1461 |
+
assert ssimp == Piecewise((-1/(x - 1), (x > -1) & (x < 1)),
|
| 1462 |
+
(x*Sum(x**n, (n, -1, oo)), True)), ssimp
|
| 1463 |
+
assert ssimp.simplify() == ssimp
|
| 1464 |
+
|
| 1465 |
+
|
| 1466 |
+
def test_issue_19379():
|
| 1467 |
+
assert Sum(factorial(n)/factorial(n + 2), (n, 1, oo)).is_convergent() is S.true
|
| 1468 |
+
|
| 1469 |
+
|
| 1470 |
+
def test_issue_20777():
|
| 1471 |
+
assert Sum(exp(x*sin(n/m)), (n, 1, m)).doit() == Sum(exp(x*sin(n/m)), (n, 1, m))
|
| 1472 |
+
|
| 1473 |
+
|
| 1474 |
+
def test__dummy_with_inherited_properties_concrete():
|
| 1475 |
+
x = Symbol('x')
|
| 1476 |
+
|
| 1477 |
+
from sympy.core.containers import Tuple
|
| 1478 |
+
d = _dummy_with_inherited_properties_concrete(Tuple(x, 0, 5))
|
| 1479 |
+
assert d.is_real
|
| 1480 |
+
assert d.is_integer
|
| 1481 |
+
assert d.is_nonnegative
|
| 1482 |
+
assert d.is_extended_nonnegative
|
| 1483 |
+
|
| 1484 |
+
d = _dummy_with_inherited_properties_concrete(Tuple(x, 1, 9))
|
| 1485 |
+
assert d.is_real
|
| 1486 |
+
assert d.is_integer
|
| 1487 |
+
assert d.is_positive
|
| 1488 |
+
assert d.is_odd is None
|
| 1489 |
+
|
| 1490 |
+
d = _dummy_with_inherited_properties_concrete(Tuple(x, -5, 5))
|
| 1491 |
+
assert d.is_real
|
| 1492 |
+
assert d.is_integer
|
| 1493 |
+
assert d.is_positive is None
|
| 1494 |
+
assert d.is_extended_nonnegative is None
|
| 1495 |
+
assert d.is_odd is None
|
| 1496 |
+
|
| 1497 |
+
d = _dummy_with_inherited_properties_concrete(Tuple(x, -1.5, 1.5))
|
| 1498 |
+
assert d.is_real
|
| 1499 |
+
assert d.is_integer is None
|
| 1500 |
+
assert d.is_positive is None
|
| 1501 |
+
assert d.is_extended_nonnegative is None
|
| 1502 |
+
|
| 1503 |
+
N = Symbol('N', integer=True, positive=True)
|
| 1504 |
+
d = _dummy_with_inherited_properties_concrete(Tuple(x, 2, N))
|
| 1505 |
+
assert d.is_real
|
| 1506 |
+
assert d.is_positive
|
| 1507 |
+
assert d.is_integer
|
| 1508 |
+
|
| 1509 |
+
# Return None if no assumptions are added
|
| 1510 |
+
N = Symbol('N', integer=True, positive=True)
|
| 1511 |
+
d = _dummy_with_inherited_properties_concrete(Tuple(N, 2, 4))
|
| 1512 |
+
assert d is None
|
| 1513 |
+
|
| 1514 |
+
x = Symbol('x', negative=True)
|
| 1515 |
+
raises(InconsistentAssumptions,
|
| 1516 |
+
lambda: _dummy_with_inherited_properties_concrete(Tuple(x, 1, 5)))
|
| 1517 |
+
|
| 1518 |
+
|
| 1519 |
+
def test_matrixsymbol_summation_numerical_limits():
|
| 1520 |
+
A = MatrixSymbol('A', 3, 3)
|
| 1521 |
+
n = Symbol('n', integer=True)
|
| 1522 |
+
|
| 1523 |
+
assert Sum(A**n, (n, 0, 2)).doit() == Identity(3) + A + A**2
|
| 1524 |
+
assert Sum(A, (n, 0, 2)).doit() == 3*A
|
| 1525 |
+
assert Sum(n*A, (n, 0, 2)).doit() == 3*A
|
| 1526 |
+
|
| 1527 |
+
B = Matrix([[0, n, 0], [-1, 0, 0], [0, 0, 2]])
|
| 1528 |
+
ans = Matrix([[0, 6, 0], [-4, 0, 0], [0, 0, 8]]) + 4*A
|
| 1529 |
+
assert Sum(A+B, (n, 0, 3)).doit() == ans
|
| 1530 |
+
ans = A*Matrix([[0, 6, 0], [-4, 0, 0], [0, 0, 8]])
|
| 1531 |
+
assert Sum(A*B, (n, 0, 3)).doit() == ans
|
| 1532 |
+
|
| 1533 |
+
ans = (A**2*Matrix([[-2, 0, 0], [0,-2, 0], [0, 0, 4]]) +
|
| 1534 |
+
A**3*Matrix([[0, -9, 0], [3, 0, 0], [0, 0, 8]]) +
|
| 1535 |
+
A*Matrix([[0, 1, 0], [-1, 0, 0], [0, 0, 2]]))
|
| 1536 |
+
assert Sum(A**n*B**n, (n, 1, 3)).doit() == ans
|
| 1537 |
+
|
| 1538 |
+
|
| 1539 |
+
def test_issue_21651():
|
| 1540 |
+
i = Symbol('i')
|
| 1541 |
+
a = Sum(floor(2*2**(-i)), (i, S.One, 2))
|
| 1542 |
+
assert a.doit() == S.One
|
| 1543 |
+
|
| 1544 |
+
|
| 1545 |
+
@XFAIL
|
| 1546 |
+
def test_matrixsymbol_summation_symbolic_limits():
|
| 1547 |
+
N = Symbol('N', integer=True, positive=True)
|
| 1548 |
+
|
| 1549 |
+
A = MatrixSymbol('A', 3, 3)
|
| 1550 |
+
n = Symbol('n', integer=True)
|
| 1551 |
+
assert Sum(A, (n, 0, N)).doit() == (N+1)*A
|
| 1552 |
+
assert Sum(n*A, (n, 0, N)).doit() == (N**2/2+N/2)*A
|
| 1553 |
+
|
| 1554 |
+
|
| 1555 |
+
def test_summation_by_residues():
|
| 1556 |
+
x = Symbol('x')
|
| 1557 |
+
|
| 1558 |
+
# Examples from Nakhle H. Asmar, Loukas Grafakos,
|
| 1559 |
+
# Complex Analysis with Applications
|
| 1560 |
+
assert eval_sum_residue(1 / (x**2 + 1), (x, -oo, oo)) == pi/tanh(pi)
|
| 1561 |
+
assert eval_sum_residue(1 / x**6, (x, S(1), oo)) == pi**6/945
|
| 1562 |
+
assert eval_sum_residue(1 / (x**2 + 9), (x, -oo, oo)) == pi/(3*tanh(3*pi))
|
| 1563 |
+
assert eval_sum_residue(1 / (x**2 + 1)**2, (x, -oo, oo)).cancel() == \
|
| 1564 |
+
(-pi**2*tanh(pi)**2 + pi*tanh(pi) + pi**2)/(2*tanh(pi)**2)
|
| 1565 |
+
assert eval_sum_residue(x**2 / (x**2 + 1)**2, (x, -oo, oo)).cancel() == \
|
| 1566 |
+
(-pi**2 + pi*tanh(pi) + pi**2*tanh(pi)**2)/(2*tanh(pi)**2)
|
| 1567 |
+
assert eval_sum_residue(1 / (4*x**2 - 1), (x, -oo, oo)) == 0
|
| 1568 |
+
assert eval_sum_residue(x**2 / (x**2 - S(1)/4)**2, (x, -oo, oo)) == pi**2/2
|
| 1569 |
+
assert eval_sum_residue(1 / (4*x**2 - 1)**2, (x, -oo, oo)) == pi**2/8
|
| 1570 |
+
assert eval_sum_residue(1 / ((x - S(1)/2)**2 + 1), (x, -oo, oo)) == pi*tanh(pi)
|
| 1571 |
+
assert eval_sum_residue(1 / x**2, (x, S(1), oo)) == pi**2/6
|
| 1572 |
+
assert eval_sum_residue(1 / x**4, (x, S(1), oo)) == pi**4/90
|
| 1573 |
+
assert eval_sum_residue(1 / x**2 / (x**2 + 4), (x, S(1), oo)) == \
|
| 1574 |
+
-pi*(-pi/12 - 1/(16*pi) + 1/(8*tanh(2*pi)))/2
|
| 1575 |
+
|
| 1576 |
+
# Some examples made from 1 / (x**2 + 1)
|
| 1577 |
+
assert eval_sum_residue(1 / (x**2 + 1), (x, S(0), oo)) == \
|
| 1578 |
+
S(1)/2 + pi/(2*tanh(pi))
|
| 1579 |
+
assert eval_sum_residue(1 / (x**2 + 1), (x, S(1), oo)) == \
|
| 1580 |
+
-S(1)/2 + pi/(2*tanh(pi))
|
| 1581 |
+
assert eval_sum_residue(1 / (x**2 + 1), (x, S(-1), oo)) == \
|
| 1582 |
+
1 + pi/(2*tanh(pi))
|
| 1583 |
+
assert eval_sum_residue((-1)**x / (x**2 + 1), (x, -oo, oo)) == \
|
| 1584 |
+
pi/sinh(pi)
|
| 1585 |
+
assert eval_sum_residue((-1)**x / (x**2 + 1), (x, S(0), oo)) == \
|
| 1586 |
+
pi/(2*sinh(pi)) + S(1)/2
|
| 1587 |
+
assert eval_sum_residue((-1)**x / (x**2 + 1), (x, S(1), oo)) == \
|
| 1588 |
+
-S(1)/2 + pi/(2*sinh(pi))
|
| 1589 |
+
assert eval_sum_residue((-1)**x / (x**2 + 1), (x, S(-1), oo)) == \
|
| 1590 |
+
pi/(2*sinh(pi))
|
| 1591 |
+
|
| 1592 |
+
# Some examples made from shifting of 1 / (x**2 + 1)
|
| 1593 |
+
assert eval_sum_residue(1 / (x**2 + 2*x + 2), (x, S(-1), oo)) == S(1)/2 + pi/(2*tanh(pi))
|
| 1594 |
+
assert eval_sum_residue(1 / (x**2 + 4*x + 5), (x, S(-2), oo)) == S(1)/2 + pi/(2*tanh(pi))
|
| 1595 |
+
assert eval_sum_residue(1 / (x**2 - 2*x + 2), (x, S(1), oo)) == S(1)/2 + pi/(2*tanh(pi))
|
| 1596 |
+
assert eval_sum_residue(1 / (x**2 - 4*x + 5), (x, S(2), oo)) == S(1)/2 + pi/(2*tanh(pi))
|
| 1597 |
+
assert eval_sum_residue((-1)**x * -1 / (x**2 + 2*x + 2), (x, S(-1), oo)) == S(1)/2 + pi/(2*sinh(pi))
|
| 1598 |
+
assert eval_sum_residue((-1)**x * -1 / (x**2 -2*x + 2), (x, S(1), oo)) == S(1)/2 + pi/(2*sinh(pi))
|
| 1599 |
+
|
| 1600 |
+
# Some examples made from 1 / x**2
|
| 1601 |
+
assert eval_sum_residue(1 / x**2, (x, S(2), oo)) == -1 + pi**2/6
|
| 1602 |
+
assert eval_sum_residue(1 / x**2, (x, S(3), oo)) == -S(5)/4 + pi**2/6
|
| 1603 |
+
assert eval_sum_residue((-1)**x / x**2, (x, S(1), oo)) == -pi**2/12
|
| 1604 |
+
assert eval_sum_residue((-1)**x / x**2, (x, S(2), oo)) == 1 - pi**2/12
|
| 1605 |
+
|
| 1606 |
+
|
| 1607 |
+
@slow
|
| 1608 |
+
def test_summation_by_residues_failing():
|
| 1609 |
+
x = Symbol('x')
|
| 1610 |
+
|
| 1611 |
+
# Failing because of the bug in residue computation
|
| 1612 |
+
assert eval_sum_residue(x**2 / (x**4 + 1), (x, S(1), oo))
|
| 1613 |
+
assert eval_sum_residue(1 / ((x - 1)*(x - 2) + 1), (x, -oo, oo)) != 0
|
| 1614 |
+
|
| 1615 |
+
|
| 1616 |
+
def test_process_limits():
|
| 1617 |
+
from sympy.concrete.expr_with_limits import _process_limits
|
| 1618 |
+
|
| 1619 |
+
# these should be (x, Range(3)) not Range(3)
|
| 1620 |
+
raises(ValueError, lambda: _process_limits(
|
| 1621 |
+
Range(3), discrete=True))
|
| 1622 |
+
raises(ValueError, lambda: _process_limits(
|
| 1623 |
+
Range(3), discrete=False))
|
| 1624 |
+
# these should be (x, union) not union
|
| 1625 |
+
# (but then we would get a TypeError because we don't
|
| 1626 |
+
# handle non-contiguous sets: see below use of `union`)
|
| 1627 |
+
union = Or(x < 1, x > 3).as_set()
|
| 1628 |
+
raises(ValueError, lambda: _process_limits(
|
| 1629 |
+
union, discrete=True))
|
| 1630 |
+
raises(ValueError, lambda: _process_limits(
|
| 1631 |
+
union, discrete=False))
|
| 1632 |
+
|
| 1633 |
+
# error not triggered if not needed
|
| 1634 |
+
assert _process_limits((x, 1, 2)) == ([(x, 1, 2)], 1)
|
| 1635 |
+
|
| 1636 |
+
# this equivalence is used to detect Reals in _process_limits
|
| 1637 |
+
assert isinstance(S.Reals, Interval)
|
| 1638 |
+
|
| 1639 |
+
C = Integral # continuous limits
|
| 1640 |
+
assert C(x, x >= 5) == C(x, (x, 5, oo))
|
| 1641 |
+
assert C(x, x < 3) == C(x, (x, -oo, 3))
|
| 1642 |
+
ans = C(x, (x, 0, 3))
|
| 1643 |
+
assert C(x, And(x >= 0, x < 3)) == ans
|
| 1644 |
+
assert C(x, (x, Interval.Ropen(0, 3))) == ans
|
| 1645 |
+
raises(TypeError, lambda: C(x, (x, Range(3))))
|
| 1646 |
+
|
| 1647 |
+
# discrete limits
|
| 1648 |
+
for D in (Sum, Product):
|
| 1649 |
+
r, ans = Range(3, 10, 2), D(2*x + 3, (x, 0, 3))
|
| 1650 |
+
assert D(x, (x, r)) == ans
|
| 1651 |
+
assert D(x, (x, r.reversed)) == ans
|
| 1652 |
+
r, ans = Range(3, oo, 2), D(2*x + 3, (x, 0, oo))
|
| 1653 |
+
assert D(x, (x, r)) == ans
|
| 1654 |
+
assert D(x, (x, r.reversed)) == ans
|
| 1655 |
+
r, ans = Range(-oo, 5, 2), D(3 - 2*x, (x, 0, oo))
|
| 1656 |
+
assert D(x, (x, r)) == ans
|
| 1657 |
+
assert D(x, (x, r.reversed)) == ans
|
| 1658 |
+
raises(TypeError, lambda: D(x, x > 0))
|
| 1659 |
+
raises(ValueError, lambda: D(x, Interval(1, 3)))
|
| 1660 |
+
raises(NotImplementedError, lambda: D(x, (x, union)))
|
| 1661 |
+
|
| 1662 |
+
|
| 1663 |
+
def test_pr_22677():
|
| 1664 |
+
b = Symbol('b', integer=True, positive=True)
|
| 1665 |
+
assert Sum(1/x**2,(x, 0, b)).doit() == Sum(x**(-2), (x, 0, b))
|
| 1666 |
+
assert Sum(1/(x - b)**2,(x, 0, b-1)).doit() == Sum(
|
| 1667 |
+
(-b + x)**(-2), (x, 0, b - 1))
|
| 1668 |
+
|
| 1669 |
+
|
| 1670 |
+
def test_issue_23952():
|
| 1671 |
+
p, q = symbols("p q", real=True, nonnegative=True)
|
| 1672 |
+
k1, k2 = symbols("k1 k2", integer=True, nonnegative=True)
|
| 1673 |
+
n = Symbol("n", integer=True, positive=True)
|
| 1674 |
+
expr = Sum(abs(k1 - k2)*p**k1 *(1 - q)**(n - k2),
|
| 1675 |
+
(k1, 0, n), (k2, 0, n))
|
| 1676 |
+
assert expr.subs(p,0).subs(q,1).subs(n, 3).doit() == 3
|
.venv/lib/python3.13/site-packages/sympy/holonomic/tests/__init__.py
ADDED
|
File without changes
|
.venv/lib/python3.13/site-packages/sympy/holonomic/tests/test_holonomic.py
ADDED
|
@@ -0,0 +1,851 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.holonomic import (DifferentialOperator, HolonomicFunction,
|
| 2 |
+
DifferentialOperators, from_hyper,
|
| 3 |
+
from_meijerg, expr_to_holonomic)
|
| 4 |
+
from sympy.holonomic.recurrence import RecurrenceOperators, HolonomicSequence
|
| 5 |
+
from sympy.core import EulerGamma
|
| 6 |
+
from sympy.core.numbers import (I, Rational, pi)
|
| 7 |
+
from sympy.core.singleton import S
|
| 8 |
+
from sympy.core.symbol import (Symbol, symbols)
|
| 9 |
+
from sympy.functions.elementary.exponential import (exp, log)
|
| 10 |
+
from sympy.functions.elementary.hyperbolic import (asinh, cosh)
|
| 11 |
+
from sympy.functions.elementary.miscellaneous import sqrt
|
| 12 |
+
from sympy.functions.elementary.trigonometric import (cos, sin)
|
| 13 |
+
from sympy.functions.special.bessel import besselj
|
| 14 |
+
from sympy.functions.special.beta_functions import beta
|
| 15 |
+
from sympy.functions.special.error_functions import (Ci, Si, erf, erfc)
|
| 16 |
+
from sympy.functions.special.gamma_functions import gamma
|
| 17 |
+
from sympy.functions.special.hyper import (hyper, meijerg)
|
| 18 |
+
from sympy.printing.str import sstr
|
| 19 |
+
from sympy.series.order import O
|
| 20 |
+
from sympy.simplify.hyperexpand import hyperexpand
|
| 21 |
+
from sympy.polys.domains.integerring import ZZ
|
| 22 |
+
from sympy.polys.domains.rationalfield import QQ
|
| 23 |
+
from sympy.polys.domains.realfield import RR
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
def test_DifferentialOperator():
|
| 27 |
+
x = symbols('x')
|
| 28 |
+
R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx')
|
| 29 |
+
assert Dx == R.derivative_operator
|
| 30 |
+
assert Dx == DifferentialOperator([R.base.zero, R.base.one], R)
|
| 31 |
+
assert x * Dx + x**2 * Dx**2 == DifferentialOperator([0, x, x**2], R)
|
| 32 |
+
assert (x**2 + 1) + Dx + x * \
|
| 33 |
+
Dx**5 == DifferentialOperator([x**2 + 1, 1, 0, 0, 0, x], R)
|
| 34 |
+
assert (x * Dx + x**2 + 1 - Dx * (x**3 + x))**3 == (-48 * x**6) + \
|
| 35 |
+
(-57 * x**7) * Dx + (-15 * x**8) * Dx**2 + (-x**9) * Dx**3
|
| 36 |
+
p = (x * Dx**2 + (x**2 + 3) * Dx**5) * (Dx + x**2)
|
| 37 |
+
q = (2 * x) + (4 * x**2) * Dx + (x**3) * Dx**2 + \
|
| 38 |
+
(20 * x**2 + x + 60) * Dx**3 + (10 * x**3 + 30 * x) * Dx**4 + \
|
| 39 |
+
(x**4 + 3 * x**2) * Dx**5 + (x**2 + 3) * Dx**6
|
| 40 |
+
assert p == q
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def test_HolonomicFunction_addition():
|
| 44 |
+
x = symbols('x')
|
| 45 |
+
R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx')
|
| 46 |
+
p = HolonomicFunction(Dx**2 * x, x)
|
| 47 |
+
q = HolonomicFunction((2) * Dx + (x) * Dx**2, x)
|
| 48 |
+
assert p == q
|
| 49 |
+
p = HolonomicFunction(x * Dx + 1, x)
|
| 50 |
+
q = HolonomicFunction(Dx + 1, x)
|
| 51 |
+
r = HolonomicFunction((x - 2) + (x**2 - 2) * Dx + (x**2 - x) * Dx**2, x)
|
| 52 |
+
assert p + q == r
|
| 53 |
+
p = HolonomicFunction(x * Dx + Dx**2 * (x**2 + 2), x)
|
| 54 |
+
q = HolonomicFunction(Dx - 3, x)
|
| 55 |
+
r = HolonomicFunction((-54 * x**2 - 126 * x - 150) + (-135 * x**3 - 252 * x**2 - 270 * x + 140) * Dx +\
|
| 56 |
+
(-27 * x**4 - 24 * x**2 + 14 * x - 150) * Dx**2 + \
|
| 57 |
+
(9 * x**4 + 15 * x**3 + 38 * x**2 + 30 * x +40) * Dx**3, x)
|
| 58 |
+
assert p + q == r
|
| 59 |
+
p = HolonomicFunction(Dx**5 - 1, x)
|
| 60 |
+
q = HolonomicFunction(x**3 + Dx, x)
|
| 61 |
+
r = HolonomicFunction((-x**18 + 45*x**14 - 525*x**10 + 1575*x**6 - x**3 - 630*x**2) + \
|
| 62 |
+
(-x**15 + 30*x**11 - 195*x**7 + 210*x**3 - 1)*Dx + (x**18 - 45*x**14 + 525*x**10 - \
|
| 63 |
+
1575*x**6 + x**3 + 630*x**2)*Dx**5 + (x**15 - 30*x**11 + 195*x**7 - 210*x**3 + \
|
| 64 |
+
1)*Dx**6, x)
|
| 65 |
+
assert p+q == r
|
| 66 |
+
|
| 67 |
+
p = x**2 + 3*x + 8
|
| 68 |
+
q = x**3 - 7*x + 5
|
| 69 |
+
p = p*Dx - p.diff()
|
| 70 |
+
q = q*Dx - q.diff()
|
| 71 |
+
r = HolonomicFunction(p, x) + HolonomicFunction(q, x)
|
| 72 |
+
s = HolonomicFunction((6*x**2 + 18*x + 14) + (-4*x**3 - 18*x**2 - 62*x + 10)*Dx +\
|
| 73 |
+
(x**4 + 6*x**3 + 31*x**2 - 10*x - 71)*Dx**2, x)
|
| 74 |
+
assert r == s
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def test_HolonomicFunction_multiplication():
|
| 78 |
+
x = symbols('x')
|
| 79 |
+
R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx')
|
| 80 |
+
p = HolonomicFunction(Dx+x+x*Dx**2, x)
|
| 81 |
+
q = HolonomicFunction(x*Dx+Dx*x+Dx**2, x)
|
| 82 |
+
r = HolonomicFunction((8*x**6 + 4*x**4 + 6*x**2 + 3) + (24*x**5 - 4*x**3 + 24*x)*Dx + \
|
| 83 |
+
(8*x**6 + 20*x**4 + 12*x**2 + 2)*Dx**2 + (8*x**5 + 4*x**3 + 4*x)*Dx**3 + \
|
| 84 |
+
(2*x**4 + x**2)*Dx**4, x)
|
| 85 |
+
assert p*q == r
|
| 86 |
+
p = HolonomicFunction(Dx**2+1, x)
|
| 87 |
+
q = HolonomicFunction(Dx-1, x)
|
| 88 |
+
r = HolonomicFunction((2) + (-2)*Dx + (1)*Dx**2, x)
|
| 89 |
+
assert p*q == r
|
| 90 |
+
p = HolonomicFunction(Dx**2+1+x+Dx, x)
|
| 91 |
+
q = HolonomicFunction((Dx*x-1)**2, x)
|
| 92 |
+
r = HolonomicFunction((4*x**7 + 11*x**6 + 16*x**5 + 4*x**4 - 6*x**3 - 7*x**2 - 8*x - 2) + \
|
| 93 |
+
(8*x**6 + 26*x**5 + 24*x**4 - 3*x**3 - 11*x**2 - 6*x - 2)*Dx + \
|
| 94 |
+
(8*x**6 + 18*x**5 + 15*x**4 - 3*x**3 - 6*x**2 - 6*x - 2)*Dx**2 + (8*x**5 + \
|
| 95 |
+
10*x**4 + 6*x**3 - 2*x**2 - 4*x)*Dx**3 + (4*x**5 + 3*x**4 - x**2)*Dx**4, x)
|
| 96 |
+
assert p*q == r
|
| 97 |
+
p = HolonomicFunction(x*Dx**2-1, x)
|
| 98 |
+
q = HolonomicFunction(Dx*x-x, x)
|
| 99 |
+
r = HolonomicFunction((x - 3) + (-2*x + 2)*Dx + (x)*Dx**2, x)
|
| 100 |
+
assert p*q == r
|
| 101 |
+
|
| 102 |
+
|
| 103 |
+
def test_HolonomicFunction_power():
|
| 104 |
+
x = symbols('x')
|
| 105 |
+
R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx')
|
| 106 |
+
p = HolonomicFunction(Dx+x+x*Dx**2, x)
|
| 107 |
+
a = HolonomicFunction(Dx, x)
|
| 108 |
+
for n in range(10):
|
| 109 |
+
assert a == p**n
|
| 110 |
+
a *= p
|
| 111 |
+
|
| 112 |
+
|
| 113 |
+
def test_addition_initial_condition():
|
| 114 |
+
x = symbols('x')
|
| 115 |
+
R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx')
|
| 116 |
+
p = HolonomicFunction(Dx-1, x, 0, [3])
|
| 117 |
+
q = HolonomicFunction(Dx**2+1, x, 0, [1, 0])
|
| 118 |
+
r = HolonomicFunction(-1 + Dx - Dx**2 + Dx**3, x, 0, [4, 3, 2])
|
| 119 |
+
assert p + q == r
|
| 120 |
+
p = HolonomicFunction(Dx - x + Dx**2, x, 0, [1, 2])
|
| 121 |
+
q = HolonomicFunction(Dx**2 + x, x, 0, [1, 0])
|
| 122 |
+
r = HolonomicFunction((-x**4 - x**3/4 - x**2 + Rational(1, 4)) + (x**3 + x**2/4 + x*Rational(3, 4) + 1)*Dx + \
|
| 123 |
+
(x*Rational(-3, 2) + Rational(7, 4))*Dx**2 + (x**2 - x*Rational(7, 4) + Rational(1, 4))*Dx**3 + (x**2 + x/4 + S.Half)*Dx**4, x, 0, [2, 2, -2, 2])
|
| 124 |
+
assert p + q == r
|
| 125 |
+
p = HolonomicFunction(Dx**2 + 4*x*Dx + x**2, x, 0, [3, 4])
|
| 126 |
+
q = HolonomicFunction(Dx**2 + 1, x, 0, [1, 1])
|
| 127 |
+
r = HolonomicFunction((x**6 + 2*x**4 - 5*x**2 - 6) + (4*x**5 + 36*x**3 - 32*x)*Dx + \
|
| 128 |
+
(x**6 + 3*x**4 + 5*x**2 - 9)*Dx**2 + (4*x**5 + 36*x**3 - 32*x)*Dx**3 + (x**4 + \
|
| 129 |
+
10*x**2 - 3)*Dx**4, x, 0, [4, 5, -1, -17])
|
| 130 |
+
assert p + q == r
|
| 131 |
+
q = HolonomicFunction(Dx**3 + x, x, 2, [3, 0, 1])
|
| 132 |
+
p = HolonomicFunction(Dx - 1, x, 2, [1])
|
| 133 |
+
r = HolonomicFunction((-x**2 - x + 1) + (x**2 + x)*Dx + (-x - 2)*Dx**3 + \
|
| 134 |
+
(x + 1)*Dx**4, x, 2, [4, 1, 2, -5 ])
|
| 135 |
+
assert p + q == r
|
| 136 |
+
p = expr_to_holonomic(sin(x))
|
| 137 |
+
q = expr_to_holonomic(1/x, x0=1)
|
| 138 |
+
r = HolonomicFunction((x**2 + 6) + (x**3 + 2*x)*Dx + (x**2 + 6)*Dx**2 + (x**3 + 2*x)*Dx**3, \
|
| 139 |
+
x, 1, [sin(1) + 1, -1 + cos(1), -sin(1) + 2])
|
| 140 |
+
assert p + q == r
|
| 141 |
+
C_1 = symbols('C_1')
|
| 142 |
+
p = expr_to_holonomic(sqrt(x))
|
| 143 |
+
q = expr_to_holonomic(sqrt(x**2-x))
|
| 144 |
+
r = (p + q).to_expr().subs(C_1, -I/2).expand()
|
| 145 |
+
assert r == I*sqrt(x)*sqrt(-x + 1) + sqrt(x)
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
def test_multiplication_initial_condition():
|
| 149 |
+
x = symbols('x')
|
| 150 |
+
R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx')
|
| 151 |
+
p = HolonomicFunction(Dx**2 + x*Dx - 1, x, 0, [3, 1])
|
| 152 |
+
q = HolonomicFunction(Dx**2 + 1, x, 0, [1, 1])
|
| 153 |
+
r = HolonomicFunction((x**4 + 14*x**2 + 60) + 4*x*Dx + (x**4 + 9*x**2 + 20)*Dx**2 + \
|
| 154 |
+
(2*x**3 + 18*x)*Dx**3 + (x**2 + 10)*Dx**4, x, 0, [3, 4, 2, 3])
|
| 155 |
+
assert p * q == r
|
| 156 |
+
p = HolonomicFunction(Dx**2 + x, x, 0, [1, 0])
|
| 157 |
+
q = HolonomicFunction(Dx**3 - x**2, x, 0, [3, 3, 3])
|
| 158 |
+
r = HolonomicFunction((x**8 - 37*x**7/27 - 10*x**6/27 - 164*x**5/9 - 184*x**4/9 + \
|
| 159 |
+
160*x**3/27 + 404*x**2/9 + 8*x + Rational(40, 3)) + (6*x**7 - 128*x**6/9 - 98*x**5/9 - 28*x**4/9 + \
|
| 160 |
+
8*x**3/9 + 28*x**2 + x*Rational(40, 9) - 40)*Dx + (3*x**6 - 82*x**5/9 + 76*x**4/9 + 4*x**3/3 + \
|
| 161 |
+
220*x**2/9 - x*Rational(80, 3))*Dx**2 + (-2*x**6 + 128*x**5/27 - 2*x**4/3 -80*x**2/9 + Rational(200, 9))*Dx**3 + \
|
| 162 |
+
(3*x**5 - 64*x**4/9 - 28*x**3/9 + 6*x**2 - x*Rational(20, 9) - Rational(20, 3))*Dx**4 + (-4*x**3 + 64*x**2/9 + \
|
| 163 |
+
x*Rational(8, 3))*Dx**5 + (x**4 - 64*x**3/27 - 4*x**2/3 + Rational(20, 9))*Dx**6, x, 0, [3, 3, 3, -3, -12, -24])
|
| 164 |
+
assert p * q == r
|
| 165 |
+
p = HolonomicFunction(Dx - 1, x, 0, [2])
|
| 166 |
+
q = HolonomicFunction(Dx**2 + 1, x, 0, [0, 1])
|
| 167 |
+
r = HolonomicFunction(2 -2*Dx + Dx**2, x, 0, [0, 2])
|
| 168 |
+
assert p * q == r
|
| 169 |
+
q = HolonomicFunction(x*Dx**2 + 1 + 2*Dx, x, 0,[0, 1])
|
| 170 |
+
r = HolonomicFunction((x - 1) + (-2*x + 2)*Dx + x*Dx**2, x, 0, [0, 2])
|
| 171 |
+
assert p * q == r
|
| 172 |
+
p = HolonomicFunction(Dx**2 - 1, x, 0, [1, 3])
|
| 173 |
+
q = HolonomicFunction(Dx**3 + 1, x, 0, [1, 2, 1])
|
| 174 |
+
r = HolonomicFunction(6*Dx + 3*Dx**2 + 2*Dx**3 - 3*Dx**4 + Dx**6, x, 0, [1, 5, 14, 17, 17, 2])
|
| 175 |
+
assert p * q == r
|
| 176 |
+
p = expr_to_holonomic(sin(x))
|
| 177 |
+
q = expr_to_holonomic(1/x, x0=1)
|
| 178 |
+
r = HolonomicFunction(x + 2*Dx + x*Dx**2, x, 1, [sin(1), -sin(1) + cos(1)])
|
| 179 |
+
assert p * q == r
|
| 180 |
+
p = expr_to_holonomic(sqrt(x))
|
| 181 |
+
q = expr_to_holonomic(sqrt(x**2-x))
|
| 182 |
+
r = (p * q).to_expr()
|
| 183 |
+
assert r == I*x*sqrt(-x + 1)
|
| 184 |
+
|
| 185 |
+
|
| 186 |
+
def test_HolonomicFunction_composition():
|
| 187 |
+
x = symbols('x')
|
| 188 |
+
R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx')
|
| 189 |
+
p = HolonomicFunction(Dx-1, x).composition(x**2+x)
|
| 190 |
+
r = HolonomicFunction((-2*x - 1) + Dx, x)
|
| 191 |
+
assert p == r
|
| 192 |
+
p = HolonomicFunction(Dx**2+1, x).composition(x**5+x**2+1)
|
| 193 |
+
r = HolonomicFunction((125*x**12 + 150*x**9 + 60*x**6 + 8*x**3) + (-20*x**3 - 2)*Dx + \
|
| 194 |
+
(5*x**4 + 2*x)*Dx**2, x)
|
| 195 |
+
assert p == r
|
| 196 |
+
p = HolonomicFunction(Dx**2*x+x, x).composition(2*x**3+x**2+1)
|
| 197 |
+
r = HolonomicFunction((216*x**9 + 324*x**8 + 180*x**7 + 152*x**6 + 112*x**5 + \
|
| 198 |
+
36*x**4 + 4*x**3) + (24*x**4 + 16*x**3 + 3*x**2 - 6*x - 1)*Dx + (6*x**5 + 5*x**4 + \
|
| 199 |
+
x**3 + 3*x**2 + x)*Dx**2, x)
|
| 200 |
+
assert p == r
|
| 201 |
+
p = HolonomicFunction(Dx**2+1, x).composition(1-x**2)
|
| 202 |
+
r = HolonomicFunction((4*x**3) - Dx + x*Dx**2, x)
|
| 203 |
+
assert p == r
|
| 204 |
+
p = HolonomicFunction(Dx**2+1, x).composition(x - 2/(x**2 + 1))
|
| 205 |
+
r = HolonomicFunction((x**12 + 6*x**10 + 12*x**9 + 15*x**8 + 48*x**7 + 68*x**6 + \
|
| 206 |
+
72*x**5 + 111*x**4 + 112*x**3 + 54*x**2 + 12*x + 1) + (12*x**8 + 32*x**6 + \
|
| 207 |
+
24*x**4 - 4)*Dx + (x**12 + 6*x**10 + 4*x**9 + 15*x**8 + 16*x**7 + 20*x**6 + 24*x**5+ \
|
| 208 |
+
15*x**4 + 16*x**3 + 6*x**2 + 4*x + 1)*Dx**2, x)
|
| 209 |
+
assert p == r
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
def test_from_hyper():
|
| 213 |
+
x = symbols('x')
|
| 214 |
+
R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx')
|
| 215 |
+
p = hyper([1, 1], [Rational(3, 2)], x**2/4)
|
| 216 |
+
q = HolonomicFunction((4*x) + (5*x**2 - 8)*Dx + (x**3 - 4*x)*Dx**2, x, 1, [2*sqrt(3)*pi/9, -4*sqrt(3)*pi/27 + Rational(4, 3)])
|
| 217 |
+
r = from_hyper(p)
|
| 218 |
+
assert r == q
|
| 219 |
+
p = from_hyper(hyper([1], [Rational(3, 2)], x**2/4))
|
| 220 |
+
q = HolonomicFunction(-x + (-x**2/2 + 2)*Dx + x*Dx**2, x)
|
| 221 |
+
# x0 = 1
|
| 222 |
+
y0 = '[sqrt(pi)*exp(1/4)*erf(1/2), -sqrt(pi)*exp(1/4)*erf(1/2)/2 + 1]'
|
| 223 |
+
assert sstr(p.y0) == y0
|
| 224 |
+
assert q.annihilator == p.annihilator
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
def test_from_meijerg():
|
| 228 |
+
x = symbols('x')
|
| 229 |
+
R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx')
|
| 230 |
+
p = from_meijerg(meijerg(([], [Rational(3, 2)]), ([S.Half], [S.Half, 1]), x))
|
| 231 |
+
q = HolonomicFunction(x/2 - Rational(1, 4) + (-x**2 + x/4)*Dx + x**2*Dx**2 + x**3*Dx**3, x, 1, \
|
| 232 |
+
[1/sqrt(pi), 1/(2*sqrt(pi)), -1/(4*sqrt(pi))])
|
| 233 |
+
assert p == q
|
| 234 |
+
p = from_meijerg(meijerg(([], []), ([0], []), x))
|
| 235 |
+
q = HolonomicFunction(1 + Dx, x, 0, [1])
|
| 236 |
+
assert p == q
|
| 237 |
+
p = from_meijerg(meijerg(([1], []), ([S.Half], [0]), x))
|
| 238 |
+
q = HolonomicFunction((x + S.Half)*Dx + x*Dx**2, x, 1, [sqrt(pi)*erf(1), exp(-1)])
|
| 239 |
+
assert p == q
|
| 240 |
+
p = from_meijerg(meijerg(([0], [1]), ([0], []), 2*x**2))
|
| 241 |
+
q = HolonomicFunction((3*x**2 - 1)*Dx + x**3*Dx**2, x, 1, [-exp(Rational(-1, 2)) + 1, -exp(Rational(-1, 2))])
|
| 242 |
+
assert p == q
|
| 243 |
+
|
| 244 |
+
|
| 245 |
+
def test_to_Sequence():
|
| 246 |
+
x = symbols('x')
|
| 247 |
+
R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx')
|
| 248 |
+
n = symbols('n', integer=True)
|
| 249 |
+
_, Sn = RecurrenceOperators(ZZ.old_poly_ring(n), 'Sn')
|
| 250 |
+
p = HolonomicFunction(x**2*Dx**4 + x + Dx, x).to_sequence()
|
| 251 |
+
q = [(HolonomicSequence(1 + (n + 2)*Sn**2 + (n**4 + 6*n**3 + 11*n**2 + 6*n)*Sn**3), 0, 1)]
|
| 252 |
+
assert p == q
|
| 253 |
+
p = HolonomicFunction(x**2*Dx**4 + x**3 + Dx**2, x).to_sequence()
|
| 254 |
+
q = [(HolonomicSequence(1 + (n**4 + 14*n**3 + 72*n**2 + 163*n + 140)*Sn**5), 0, 0)]
|
| 255 |
+
assert p == q
|
| 256 |
+
p = HolonomicFunction(x**3*Dx**4 + 1 + Dx**2, x).to_sequence()
|
| 257 |
+
q = [(HolonomicSequence(1 + (n**4 - 2*n**3 - n**2 + 2*n)*Sn + (n**2 + 3*n + 2)*Sn**2), 0, 0)]
|
| 258 |
+
assert p == q
|
| 259 |
+
p = HolonomicFunction(3*x**3*Dx**4 + 2*x*Dx + x*Dx**3, x).to_sequence()
|
| 260 |
+
q = [(HolonomicSequence(2*n + (3*n**4 - 6*n**3 - 3*n**2 + 6*n)*Sn + (n**3 + 3*n**2 + 2*n)*Sn**2), 0, 1)]
|
| 261 |
+
assert p == q
|
| 262 |
+
|
| 263 |
+
|
| 264 |
+
def test_to_Sequence_Initial_Coniditons():
|
| 265 |
+
x = symbols('x')
|
| 266 |
+
R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx')
|
| 267 |
+
n = symbols('n', integer=True)
|
| 268 |
+
_, Sn = RecurrenceOperators(QQ.old_poly_ring(n), 'Sn')
|
| 269 |
+
p = HolonomicFunction(Dx - 1, x, 0, [1]).to_sequence()
|
| 270 |
+
q = [(HolonomicSequence(-1 + (n + 1)*Sn, 1), 0)]
|
| 271 |
+
assert p == q
|
| 272 |
+
p = HolonomicFunction(Dx**2 + 1, x, 0, [0, 1]).to_sequence()
|
| 273 |
+
q = [(HolonomicSequence(1 + (n**2 + 3*n + 2)*Sn**2, [0, 1]), 0)]
|
| 274 |
+
assert p == q
|
| 275 |
+
p = HolonomicFunction(Dx**2 + 1 + x**3*Dx, x, 0, [2, 3]).to_sequence()
|
| 276 |
+
q = [(HolonomicSequence(n + Sn**2 + (n**2 + 7*n + 12)*Sn**4, [2, 3, -1, Rational(-1, 2), Rational(1, 12)]), 1)]
|
| 277 |
+
assert p == q
|
| 278 |
+
p = HolonomicFunction(x**3*Dx**5 + 1 + Dx, x).to_sequence()
|
| 279 |
+
q = [(HolonomicSequence(1 + (n + 1)*Sn + (n**5 - 5*n**3 + 4*n)*Sn**2), 0, 3)]
|
| 280 |
+
assert p == q
|
| 281 |
+
C_0, C_1, C_2, C_3 = symbols('C_0, C_1, C_2, C_3')
|
| 282 |
+
p = expr_to_holonomic(log(1+x**2))
|
| 283 |
+
q = [(HolonomicSequence(n**2 + (n**2 + 2*n)*Sn**2, [0, 0, C_2]), 0, 1)]
|
| 284 |
+
assert p.to_sequence() == q
|
| 285 |
+
p = p.diff()
|
| 286 |
+
q = [(HolonomicSequence((n + 2) + (n + 2)*Sn**2, [C_0, 0]), 1, 0)]
|
| 287 |
+
assert p.to_sequence() == q
|
| 288 |
+
p = expr_to_holonomic(erf(x) + x).to_sequence()
|
| 289 |
+
q = [(HolonomicSequence((2*n**2 - 2*n) + (n**3 + 2*n**2 - n - 2)*Sn**2, [0, 1 + 2/sqrt(pi), 0, C_3]), 0, 2)]
|
| 290 |
+
assert p == q
|
| 291 |
+
|
| 292 |
+
def test_series():
|
| 293 |
+
x = symbols('x')
|
| 294 |
+
R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx')
|
| 295 |
+
p = HolonomicFunction(Dx**2 + 2*x*Dx, x, 0, [0, 1]).series(n=10)
|
| 296 |
+
q = x - x**3/3 + x**5/10 - x**7/42 + x**9/216 + O(x**10)
|
| 297 |
+
assert p == q
|
| 298 |
+
p = HolonomicFunction(Dx - 1, x).composition(x**2, 0, [1]) # e^(x**2)
|
| 299 |
+
q = HolonomicFunction(Dx**2 + 1, x, 0, [1, 0]) # cos(x)
|
| 300 |
+
r = (p * q).series(n=10) # expansion of cos(x) * exp(x**2)
|
| 301 |
+
s = 1 + x**2/2 + x**4/24 - 31*x**6/720 - 179*x**8/8064 + O(x**10)
|
| 302 |
+
assert r == s
|
| 303 |
+
t = HolonomicFunction((1 + x)*Dx**2 + Dx, x, 0, [0, 1]) # log(1 + x)
|
| 304 |
+
r = (p * t + q).series(n=10)
|
| 305 |
+
s = 1 + x - x**2 + 4*x**3/3 - 17*x**4/24 + 31*x**5/30 - 481*x**6/720 +\
|
| 306 |
+
71*x**7/105 - 20159*x**8/40320 + 379*x**9/840 + O(x**10)
|
| 307 |
+
assert r == s
|
| 308 |
+
p = HolonomicFunction((6+6*x-3*x**2) - (10*x-3*x**2-3*x**3)*Dx + \
|
| 309 |
+
(4-6*x**3+2*x**4)*Dx**2, x, 0, [0, 1]).series(n=7)
|
| 310 |
+
q = x + x**3/6 - 3*x**4/16 + x**5/20 - 23*x**6/960 + O(x**7)
|
| 311 |
+
assert p == q
|
| 312 |
+
p = HolonomicFunction((6+6*x-3*x**2) - (10*x-3*x**2-3*x**3)*Dx + \
|
| 313 |
+
(4-6*x**3+2*x**4)*Dx**2, x, 0, [1, 0]).series(n=7)
|
| 314 |
+
q = 1 - 3*x**2/4 - x**3/4 - 5*x**4/32 - 3*x**5/40 - 17*x**6/384 + O(x**7)
|
| 315 |
+
assert p == q
|
| 316 |
+
p = expr_to_holonomic(erf(x) + x).series(n=10)
|
| 317 |
+
C_3 = symbols('C_3')
|
| 318 |
+
q = (erf(x) + x).series(n=10)
|
| 319 |
+
assert p.subs(C_3, -2/(3*sqrt(pi))) == q
|
| 320 |
+
assert expr_to_holonomic(sqrt(x**3 + x)).series(n=10) == sqrt(x**3 + x).series(n=10)
|
| 321 |
+
assert expr_to_holonomic((2*x - 3*x**2)**Rational(1, 3)).series() == ((2*x - 3*x**2)**Rational(1, 3)).series()
|
| 322 |
+
assert expr_to_holonomic(sqrt(x**2-x)).series() == (sqrt(x**2-x)).series()
|
| 323 |
+
assert expr_to_holonomic(cos(x)**2/x**2, y0={-2: [1, 0, -1]}).series(n=10) == (cos(x)**2/x**2).series(n=10)
|
| 324 |
+
assert expr_to_holonomic(cos(x)**2/x**2, x0=1).series(n=10).together() == (cos(x)**2/x**2).series(n=10, x0=1).together()
|
| 325 |
+
assert expr_to_holonomic(cos(x-1)**2/(x-1)**2, x0=1, y0={-2: [1, 0, -1]}).series(n=10) \
|
| 326 |
+
== (cos(x-1)**2/(x-1)**2).series(x0=1, n=10)
|
| 327 |
+
|
| 328 |
+
def test_evalf_euler():
|
| 329 |
+
x = symbols('x')
|
| 330 |
+
R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx')
|
| 331 |
+
|
| 332 |
+
# log(1+x)
|
| 333 |
+
p = HolonomicFunction((1 + x)*Dx**2 + Dx, x, 0, [0, 1])
|
| 334 |
+
|
| 335 |
+
# path taken is a straight line from 0 to 1, on the real axis
|
| 336 |
+
r = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1]
|
| 337 |
+
s = '0.699525841805253' # approx. equal to log(2) i.e. 0.693147180559945
|
| 338 |
+
assert sstr(p.evalf(r, method='Euler')[-1]) == s
|
| 339 |
+
|
| 340 |
+
# path taken is a triangle 0-->1+i-->2
|
| 341 |
+
r = [0.1 + 0.1*I]
|
| 342 |
+
for i in range(9):
|
| 343 |
+
r.append(r[-1]+0.1+0.1*I)
|
| 344 |
+
for i in range(10):
|
| 345 |
+
r.append(r[-1]+0.1-0.1*I)
|
| 346 |
+
|
| 347 |
+
# close to the exact solution 1.09861228866811
|
| 348 |
+
# imaginary part also close to zero
|
| 349 |
+
s = '1.07530466271334 - 0.0251200594793912*I'
|
| 350 |
+
assert sstr(p.evalf(r, method='Euler')[-1]) == s
|
| 351 |
+
|
| 352 |
+
# sin(x)
|
| 353 |
+
p = HolonomicFunction(Dx**2 + 1, x, 0, [0, 1])
|
| 354 |
+
s = '0.905546532085401 - 6.93889390390723e-18*I'
|
| 355 |
+
assert sstr(p.evalf(r, method='Euler')[-1]) == s
|
| 356 |
+
|
| 357 |
+
# computing sin(pi/2) using this method
|
| 358 |
+
# using a linear path from 0 to pi/2
|
| 359 |
+
r = [0.1]
|
| 360 |
+
for i in range(14):
|
| 361 |
+
r.append(r[-1] + 0.1)
|
| 362 |
+
r.append(pi/2)
|
| 363 |
+
s = '1.08016557252834' # close to 1.0 (exact solution)
|
| 364 |
+
assert sstr(p.evalf(r, method='Euler')[-1]) == s
|
| 365 |
+
|
| 366 |
+
# trying different path, a rectangle (0-->i-->pi/2 + i-->pi/2)
|
| 367 |
+
# computing the same value sin(pi/2) using different path
|
| 368 |
+
r = [0.1*I]
|
| 369 |
+
for i in range(9):
|
| 370 |
+
r.append(r[-1]+0.1*I)
|
| 371 |
+
for i in range(15):
|
| 372 |
+
r.append(r[-1]+0.1)
|
| 373 |
+
r.append(pi/2+I)
|
| 374 |
+
for i in range(10):
|
| 375 |
+
r.append(r[-1]-0.1*I)
|
| 376 |
+
|
| 377 |
+
# close to 1.0
|
| 378 |
+
s = '0.976882381836257 - 1.65557671738537e-16*I'
|
| 379 |
+
assert sstr(p.evalf(r, method='Euler')[-1]) == s
|
| 380 |
+
|
| 381 |
+
# cos(x)
|
| 382 |
+
p = HolonomicFunction(Dx**2 + 1, x, 0, [1, 0])
|
| 383 |
+
# compute cos(pi) along 0-->pi
|
| 384 |
+
r = [0.05]
|
| 385 |
+
for i in range(61):
|
| 386 |
+
r.append(r[-1]+0.05)
|
| 387 |
+
r.append(pi)
|
| 388 |
+
# close to -1 (exact answer)
|
| 389 |
+
s = '-1.08140824719196'
|
| 390 |
+
assert sstr(p.evalf(r, method='Euler')[-1]) == s
|
| 391 |
+
|
| 392 |
+
# a rectangular path (0 -> i -> 2+i -> 2)
|
| 393 |
+
r = [0.1*I]
|
| 394 |
+
for i in range(9):
|
| 395 |
+
r.append(r[-1]+0.1*I)
|
| 396 |
+
for i in range(20):
|
| 397 |
+
r.append(r[-1]+0.1)
|
| 398 |
+
for i in range(10):
|
| 399 |
+
r.append(r[-1]-0.1*I)
|
| 400 |
+
|
| 401 |
+
p = HolonomicFunction(Dx**2 + 1, x, 0, [1,1]).evalf(r, method='Euler')
|
| 402 |
+
s = '0.501421652861245 - 3.88578058618805e-16*I'
|
| 403 |
+
assert sstr(p[-1]) == s
|
| 404 |
+
|
| 405 |
+
def test_evalf_rk4():
|
| 406 |
+
x = symbols('x')
|
| 407 |
+
R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx')
|
| 408 |
+
|
| 409 |
+
# log(1+x)
|
| 410 |
+
p = HolonomicFunction((1 + x)*Dx**2 + Dx, x, 0, [0, 1])
|
| 411 |
+
|
| 412 |
+
# path taken is a straight line from 0 to 1, on the real axis
|
| 413 |
+
r = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1]
|
| 414 |
+
s = '0.693146363174626' # approx. equal to log(2) i.e. 0.693147180559945
|
| 415 |
+
assert sstr(p.evalf(r)[-1]) == s
|
| 416 |
+
|
| 417 |
+
# path taken is a triangle 0-->1+i-->2
|
| 418 |
+
r = [0.1 + 0.1*I]
|
| 419 |
+
for i in range(9):
|
| 420 |
+
r.append(r[-1]+0.1+0.1*I)
|
| 421 |
+
for i in range(10):
|
| 422 |
+
r.append(r[-1]+0.1-0.1*I)
|
| 423 |
+
|
| 424 |
+
# close to the exact solution 1.09861228866811
|
| 425 |
+
# imaginary part also close to zero
|
| 426 |
+
s = '1.098616 + 1.36083e-7*I'
|
| 427 |
+
assert sstr(p.evalf(r)[-1].n(7)) == s
|
| 428 |
+
|
| 429 |
+
# sin(x)
|
| 430 |
+
p = HolonomicFunction(Dx**2 + 1, x, 0, [0, 1])
|
| 431 |
+
s = '0.90929463522785 + 1.52655665885959e-16*I'
|
| 432 |
+
assert sstr(p.evalf(r)[-1]) == s
|
| 433 |
+
|
| 434 |
+
# computing sin(pi/2) using this method
|
| 435 |
+
# using a linear path from 0 to pi/2
|
| 436 |
+
r = [0.1]
|
| 437 |
+
for i in range(14):
|
| 438 |
+
r.append(r[-1] + 0.1)
|
| 439 |
+
r.append(pi/2)
|
| 440 |
+
s = '0.999999895088917' # close to 1.0 (exact solution)
|
| 441 |
+
assert sstr(p.evalf(r)[-1]) == s
|
| 442 |
+
|
| 443 |
+
# trying different path, a rectangle (0-->i-->pi/2 + i-->pi/2)
|
| 444 |
+
# computing the same value sin(pi/2) using different path
|
| 445 |
+
r = [0.1*I]
|
| 446 |
+
for i in range(9):
|
| 447 |
+
r.append(r[-1]+0.1*I)
|
| 448 |
+
for i in range(15):
|
| 449 |
+
r.append(r[-1]+0.1)
|
| 450 |
+
r.append(pi/2+I)
|
| 451 |
+
for i in range(10):
|
| 452 |
+
r.append(r[-1]-0.1*I)
|
| 453 |
+
|
| 454 |
+
# close to 1.0
|
| 455 |
+
s = '1.00000003415141 + 6.11940487991086e-16*I'
|
| 456 |
+
assert sstr(p.evalf(r)[-1]) == s
|
| 457 |
+
|
| 458 |
+
# cos(x)
|
| 459 |
+
p = HolonomicFunction(Dx**2 + 1, x, 0, [1, 0])
|
| 460 |
+
# compute cos(pi) along 0-->pi
|
| 461 |
+
r = [0.05]
|
| 462 |
+
for i in range(61):
|
| 463 |
+
r.append(r[-1]+0.05)
|
| 464 |
+
r.append(pi)
|
| 465 |
+
# close to -1 (exact answer)
|
| 466 |
+
s = '-0.999999993238714'
|
| 467 |
+
assert sstr(p.evalf(r)[-1]) == s
|
| 468 |
+
|
| 469 |
+
# a rectangular path (0 -> i -> 2+i -> 2)
|
| 470 |
+
r = [0.1*I]
|
| 471 |
+
for i in range(9):
|
| 472 |
+
r.append(r[-1]+0.1*I)
|
| 473 |
+
for i in range(20):
|
| 474 |
+
r.append(r[-1]+0.1)
|
| 475 |
+
for i in range(10):
|
| 476 |
+
r.append(r[-1]-0.1*I)
|
| 477 |
+
|
| 478 |
+
p = HolonomicFunction(Dx**2 + 1, x, 0, [1,1]).evalf(r)
|
| 479 |
+
s = '0.493152791638442 - 1.41553435639707e-15*I'
|
| 480 |
+
assert sstr(p[-1]) == s
|
| 481 |
+
|
| 482 |
+
|
| 483 |
+
def test_expr_to_holonomic():
|
| 484 |
+
x = symbols('x')
|
| 485 |
+
R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx')
|
| 486 |
+
p = expr_to_holonomic((sin(x)/x)**2)
|
| 487 |
+
q = HolonomicFunction(8*x + (4*x**2 + 6)*Dx + 6*x*Dx**2 + x**2*Dx**3, x, 0, \
|
| 488 |
+
[1, 0, Rational(-2, 3)])
|
| 489 |
+
assert p == q
|
| 490 |
+
p = expr_to_holonomic(1/(1+x**2)**2)
|
| 491 |
+
q = HolonomicFunction(4*x + (x**2 + 1)*Dx, x, 0, [1])
|
| 492 |
+
assert p == q
|
| 493 |
+
p = expr_to_holonomic(exp(x)*sin(x)+x*log(1+x))
|
| 494 |
+
q = HolonomicFunction((2*x**3 + 10*x**2 + 20*x + 18) + (-2*x**4 - 10*x**3 - 20*x**2 \
|
| 495 |
+
- 18*x)*Dx + (2*x**5 + 6*x**4 + 7*x**3 + 8*x**2 + 10*x - 4)*Dx**2 + \
|
| 496 |
+
(-2*x**5 - 5*x**4 - 2*x**3 + 2*x**2 - x + 4)*Dx**3 + (x**5 + 2*x**4 - x**3 - \
|
| 497 |
+
7*x**2/2 + x + Rational(5, 2))*Dx**4, x, 0, [0, 1, 4, -1])
|
| 498 |
+
assert p == q
|
| 499 |
+
p = expr_to_holonomic(x*exp(x)+cos(x)+1)
|
| 500 |
+
q = HolonomicFunction((-x - 3)*Dx + (x + 2)*Dx**2 + (-x - 3)*Dx**3 + (x + 2)*Dx**4, x, \
|
| 501 |
+
0, [2, 1, 1, 3])
|
| 502 |
+
assert p == q
|
| 503 |
+
assert (x*exp(x)+cos(x)+1).series(n=10) == p.series(n=10)
|
| 504 |
+
p = expr_to_holonomic(log(1 + x)**2 + 1)
|
| 505 |
+
q = HolonomicFunction(Dx + (3*x + 3)*Dx**2 + (x**2 + 2*x + 1)*Dx**3, x, 0, [1, 0, 2])
|
| 506 |
+
assert p == q
|
| 507 |
+
p = expr_to_holonomic(erf(x)**2 + x)
|
| 508 |
+
q = HolonomicFunction((8*x**4 - 2*x**2 + 2)*Dx**2 + (6*x**3 - x/2)*Dx**3 + \
|
| 509 |
+
(x**2+ Rational(1, 4))*Dx**4, x, 0, [0, 1, 8/pi, 0])
|
| 510 |
+
assert p == q
|
| 511 |
+
p = expr_to_holonomic(cosh(x)*x)
|
| 512 |
+
q = HolonomicFunction((-x**2 + 2) -2*x*Dx + x**2*Dx**2, x, 0, [0, 1])
|
| 513 |
+
assert p == q
|
| 514 |
+
p = expr_to_holonomic(besselj(2, x))
|
| 515 |
+
q = HolonomicFunction((x**2 - 4) + x*Dx + x**2*Dx**2, x, 0, [0, 0])
|
| 516 |
+
assert p == q
|
| 517 |
+
p = expr_to_holonomic(besselj(0, x) + exp(x))
|
| 518 |
+
q = HolonomicFunction((-x**2 - x/2 + S.Half) + (x**2 - x/2 - Rational(3, 2))*Dx + (-x**2 + x/2 + 1)*Dx**2 +\
|
| 519 |
+
(x**2 + x/2)*Dx**3, x, 0, [2, 1, S.Half])
|
| 520 |
+
assert p == q
|
| 521 |
+
p = expr_to_holonomic(sin(x)**2/x)
|
| 522 |
+
q = HolonomicFunction(4 + 4*x*Dx + 3*Dx**2 + x*Dx**3, x, 0, [0, 1, 0])
|
| 523 |
+
assert p == q
|
| 524 |
+
p = expr_to_holonomic(sin(x)**2/x, x0=2)
|
| 525 |
+
q = HolonomicFunction((4) + (4*x)*Dx + (3)*Dx**2 + (x)*Dx**3, x, 2, [sin(2)**2/2,
|
| 526 |
+
sin(2)*cos(2) - sin(2)**2/4, -3*sin(2)**2/4 + cos(2)**2 - sin(2)*cos(2)])
|
| 527 |
+
assert p == q
|
| 528 |
+
p = expr_to_holonomic(log(x)/2 - Ci(2*x)/2 + Ci(2)/2)
|
| 529 |
+
q = HolonomicFunction(4*Dx + 4*x*Dx**2 + 3*Dx**3 + x*Dx**4, x, 0, \
|
| 530 |
+
[-log(2)/2 - EulerGamma/2 + Ci(2)/2, 0, 1, 0])
|
| 531 |
+
assert p == q
|
| 532 |
+
p = p.to_expr()
|
| 533 |
+
q = log(x)/2 - Ci(2*x)/2 + Ci(2)/2
|
| 534 |
+
assert p == q
|
| 535 |
+
p = expr_to_holonomic(x**S.Half, x0=1)
|
| 536 |
+
q = HolonomicFunction(x*Dx - S.Half, x, 1, [1])
|
| 537 |
+
assert p == q
|
| 538 |
+
p = expr_to_holonomic(sqrt(1 + x**2))
|
| 539 |
+
q = HolonomicFunction((-x) + (x**2 + 1)*Dx, x, 0, [1])
|
| 540 |
+
assert p == q
|
| 541 |
+
assert (expr_to_holonomic(sqrt(x) + sqrt(2*x)).to_expr()-\
|
| 542 |
+
(sqrt(x) + sqrt(2*x))).simplify() == 0
|
| 543 |
+
assert expr_to_holonomic(3*x+2*sqrt(x)).to_expr() == 3*x+2*sqrt(x)
|
| 544 |
+
p = expr_to_holonomic((x**4+x**3+5*x**2+3*x+2)/x**2, lenics=3)
|
| 545 |
+
q = HolonomicFunction((-2*x**4 - x**3 + 3*x + 4) + (x**5 + x**4 + 5*x**3 + 3*x**2 + \
|
| 546 |
+
2*x)*Dx, x, 0, {-2: [2, 3, 5]})
|
| 547 |
+
assert p == q
|
| 548 |
+
p = expr_to_holonomic(1/(x-1)**2, lenics=3, x0=1)
|
| 549 |
+
q = HolonomicFunction((2) + (x - 1)*Dx, x, 1, {-2: [1, 0, 0]})
|
| 550 |
+
assert p == q
|
| 551 |
+
a = symbols("a")
|
| 552 |
+
p = expr_to_holonomic(sqrt(a*x), x=x)
|
| 553 |
+
assert p.to_expr() == sqrt(a)*sqrt(x)
|
| 554 |
+
|
| 555 |
+
def test_to_hyper():
|
| 556 |
+
x = symbols('x')
|
| 557 |
+
R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx')
|
| 558 |
+
p = HolonomicFunction(Dx - 2, x, 0, [3]).to_hyper()
|
| 559 |
+
q = 3 * hyper([], [], 2*x)
|
| 560 |
+
assert p == q
|
| 561 |
+
p = hyperexpand(HolonomicFunction((1 + x) * Dx - 3, x, 0, [2]).to_hyper()).expand()
|
| 562 |
+
q = 2*x**3 + 6*x**2 + 6*x + 2
|
| 563 |
+
assert p == q
|
| 564 |
+
p = HolonomicFunction((1 + x)*Dx**2 + Dx, x, 0, [0, 1]).to_hyper()
|
| 565 |
+
q = -x**2*hyper((2, 2, 1), (3, 2), -x)/2 + x
|
| 566 |
+
assert p == q
|
| 567 |
+
p = HolonomicFunction(2*x*Dx + Dx**2, x, 0, [0, 2/sqrt(pi)]).to_hyper()
|
| 568 |
+
q = 2*x*hyper((S.Half,), (Rational(3, 2),), -x**2)/sqrt(pi)
|
| 569 |
+
assert p == q
|
| 570 |
+
p = hyperexpand(HolonomicFunction(2*x*Dx + Dx**2, x, 0, [1, -2/sqrt(pi)]).to_hyper())
|
| 571 |
+
q = erfc(x)
|
| 572 |
+
assert p.rewrite(erfc) == q
|
| 573 |
+
p = hyperexpand(HolonomicFunction((x**2 - 1) + x*Dx + x**2*Dx**2,
|
| 574 |
+
x, 0, [0, S.Half]).to_hyper())
|
| 575 |
+
q = besselj(1, x)
|
| 576 |
+
assert p == q
|
| 577 |
+
p = hyperexpand(HolonomicFunction(x*Dx**2 + Dx + x, x, 0, [1, 0]).to_hyper())
|
| 578 |
+
q = besselj(0, x)
|
| 579 |
+
assert p == q
|
| 580 |
+
|
| 581 |
+
def test_to_expr():
|
| 582 |
+
x = symbols('x')
|
| 583 |
+
R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx')
|
| 584 |
+
p = HolonomicFunction(Dx - 1, x, 0, [1]).to_expr()
|
| 585 |
+
q = exp(x)
|
| 586 |
+
assert p == q
|
| 587 |
+
p = HolonomicFunction(Dx**2 + 1, x, 0, [1, 0]).to_expr()
|
| 588 |
+
q = cos(x)
|
| 589 |
+
assert p == q
|
| 590 |
+
p = HolonomicFunction(Dx**2 - 1, x, 0, [1, 0]).to_expr()
|
| 591 |
+
q = cosh(x)
|
| 592 |
+
assert p == q
|
| 593 |
+
p = HolonomicFunction(2 + (4*x - 1)*Dx + \
|
| 594 |
+
(x**2 - x)*Dx**2, x, 0, [1, 2]).to_expr().expand()
|
| 595 |
+
q = 1/(x**2 - 2*x + 1)
|
| 596 |
+
assert p == q
|
| 597 |
+
p = expr_to_holonomic(sin(x)**2/x).integrate((x, 0, x)).to_expr()
|
| 598 |
+
q = (sin(x)**2/x).integrate((x, 0, x))
|
| 599 |
+
assert p == q
|
| 600 |
+
C_0, C_1, C_2, C_3 = symbols('C_0, C_1, C_2, C_3')
|
| 601 |
+
p = expr_to_holonomic(log(1+x**2)).to_expr()
|
| 602 |
+
q = C_2*log(x**2 + 1)
|
| 603 |
+
assert p == q
|
| 604 |
+
p = expr_to_holonomic(log(1+x**2)).diff().to_expr()
|
| 605 |
+
q = C_0*x/(x**2 + 1)
|
| 606 |
+
assert p == q
|
| 607 |
+
p = expr_to_holonomic(erf(x) + x).to_expr()
|
| 608 |
+
q = 3*C_3*x - 3*sqrt(pi)*C_3*erf(x)/2 + x + 2*x/sqrt(pi)
|
| 609 |
+
assert p == q
|
| 610 |
+
p = expr_to_holonomic(sqrt(x), x0=1).to_expr()
|
| 611 |
+
assert p == sqrt(x)
|
| 612 |
+
assert expr_to_holonomic(sqrt(x)).to_expr() == sqrt(x)
|
| 613 |
+
p = expr_to_holonomic(sqrt(1 + x**2)).to_expr()
|
| 614 |
+
assert p == sqrt(1+x**2)
|
| 615 |
+
p = expr_to_holonomic((2*x**2 + 1)**Rational(2, 3)).to_expr()
|
| 616 |
+
assert p == (2*x**2 + 1)**Rational(2, 3)
|
| 617 |
+
p = expr_to_holonomic(sqrt(-x**2+2*x)).to_expr()
|
| 618 |
+
assert p == sqrt(x)*sqrt(-x + 2)
|
| 619 |
+
p = expr_to_holonomic((-2*x**3+7*x)**Rational(2, 3)).to_expr()
|
| 620 |
+
q = x**Rational(2, 3)*(-2*x**2 + 7)**Rational(2, 3)
|
| 621 |
+
assert p == q
|
| 622 |
+
p = from_hyper(hyper((-2, -3), (S.Half, ), x))
|
| 623 |
+
s = hyperexpand(hyper((-2, -3), (S.Half, ), x))
|
| 624 |
+
D_0 = Symbol('D_0')
|
| 625 |
+
C_0 = Symbol('C_0')
|
| 626 |
+
assert (p.to_expr().subs({C_0:1, D_0:0}) - s).simplify() == 0
|
| 627 |
+
p.y0 = {0: [1], S.Half: [0]}
|
| 628 |
+
assert p.to_expr() == s
|
| 629 |
+
assert expr_to_holonomic(x**5).to_expr() == x**5
|
| 630 |
+
assert expr_to_holonomic(2*x**3-3*x**2).to_expr().expand() == \
|
| 631 |
+
2*x**3-3*x**2
|
| 632 |
+
a = symbols("a")
|
| 633 |
+
p = (expr_to_holonomic(1.4*x)*expr_to_holonomic(a*x, x)).to_expr()
|
| 634 |
+
q = 1.4*a*x**2
|
| 635 |
+
assert p == q
|
| 636 |
+
p = (expr_to_holonomic(1.4*x)+expr_to_holonomic(a*x, x)).to_expr()
|
| 637 |
+
q = x*(a + 1.4)
|
| 638 |
+
assert p == q
|
| 639 |
+
p = (expr_to_holonomic(1.4*x)+expr_to_holonomic(x)).to_expr()
|
| 640 |
+
assert p == 2.4*x
|
| 641 |
+
|
| 642 |
+
|
| 643 |
+
def test_integrate():
|
| 644 |
+
x = symbols('x')
|
| 645 |
+
R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx')
|
| 646 |
+
p = expr_to_holonomic(sin(x)**2/x, x0=1).integrate((x, 2, 3))
|
| 647 |
+
q = '0.166270406994788'
|
| 648 |
+
assert sstr(p) == q
|
| 649 |
+
p = expr_to_holonomic(sin(x)).integrate((x, 0, x)).to_expr()
|
| 650 |
+
q = 1 - cos(x)
|
| 651 |
+
assert p == q
|
| 652 |
+
p = expr_to_holonomic(sin(x)).integrate((x, 0, 3))
|
| 653 |
+
q = 1 - cos(3)
|
| 654 |
+
assert p == q
|
| 655 |
+
p = expr_to_holonomic(sin(x)/x, x0=1).integrate((x, 1, 2))
|
| 656 |
+
q = '0.659329913368450'
|
| 657 |
+
assert sstr(p) == q
|
| 658 |
+
p = expr_to_holonomic(sin(x)**2/x, x0=1).integrate((x, 1, 0))
|
| 659 |
+
q = '-0.423690480850035'
|
| 660 |
+
assert sstr(p) == q
|
| 661 |
+
p = expr_to_holonomic(sin(x)/x)
|
| 662 |
+
assert p.integrate(x).to_expr() == Si(x)
|
| 663 |
+
assert p.integrate((x, 0, 2)) == Si(2)
|
| 664 |
+
p = expr_to_holonomic(sin(x)**2/x)
|
| 665 |
+
q = p.to_expr()
|
| 666 |
+
assert p.integrate(x).to_expr() == q.integrate((x, 0, x))
|
| 667 |
+
assert p.integrate((x, 0, 1)) == q.integrate((x, 0, 1))
|
| 668 |
+
assert expr_to_holonomic(1/x, x0=1).integrate(x).to_expr() == log(x)
|
| 669 |
+
p = expr_to_holonomic((x + 1)**3*exp(-x), x0=-1).integrate(x).to_expr()
|
| 670 |
+
q = (-x**3 - 6*x**2 - 15*x + 6*exp(x + 1) - 16)*exp(-x)
|
| 671 |
+
assert p == q
|
| 672 |
+
p = expr_to_holonomic(cos(x)**2/x**2, y0={-2: [1, 0, -1]}).integrate(x).to_expr()
|
| 673 |
+
q = -Si(2*x) - cos(x)**2/x
|
| 674 |
+
assert p == q
|
| 675 |
+
p = expr_to_holonomic(sqrt(x**2+x)).integrate(x).to_expr()
|
| 676 |
+
q = (x**Rational(3, 2)*(2*x**2 + 3*x + 1) - x*sqrt(x + 1)*asinh(sqrt(x)))/(4*x*sqrt(x + 1))
|
| 677 |
+
assert p == q
|
| 678 |
+
p = expr_to_holonomic(sqrt(x**2+1)).integrate(x).to_expr()
|
| 679 |
+
q = (sqrt(x**2+1)).integrate(x)
|
| 680 |
+
assert (p-q).simplify() == 0
|
| 681 |
+
p = expr_to_holonomic(1/x**2, y0={-2:[1, 0, 0]})
|
| 682 |
+
r = expr_to_holonomic(1/x**2, lenics=3)
|
| 683 |
+
assert p == r
|
| 684 |
+
q = expr_to_holonomic(cos(x)**2)
|
| 685 |
+
assert (r*q).integrate(x).to_expr() == -Si(2*x) - cos(x)**2/x
|
| 686 |
+
|
| 687 |
+
|
| 688 |
+
def test_diff():
|
| 689 |
+
x, y = symbols('x, y')
|
| 690 |
+
R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx')
|
| 691 |
+
p = HolonomicFunction(x*Dx**2 + 1, x, 0, [0, 1])
|
| 692 |
+
assert p.diff().to_expr() == p.to_expr().diff().simplify()
|
| 693 |
+
p = HolonomicFunction(Dx**2 - 1, x, 0, [1, 0])
|
| 694 |
+
assert p.diff(x, 2).to_expr() == p.to_expr()
|
| 695 |
+
p = expr_to_holonomic(Si(x))
|
| 696 |
+
assert p.diff().to_expr() == sin(x)/x
|
| 697 |
+
assert p.diff(y) == 0
|
| 698 |
+
C_0, C_1, C_2, C_3 = symbols('C_0, C_1, C_2, C_3')
|
| 699 |
+
q = Si(x)
|
| 700 |
+
assert p.diff(x).to_expr() == q.diff()
|
| 701 |
+
assert p.diff(x, 2).to_expr().subs(C_0, Rational(-1, 3)).cancel() == q.diff(x, 2).cancel()
|
| 702 |
+
assert p.diff(x, 3).series().subs({C_3: Rational(-1, 3), C_0: 0}) == q.diff(x, 3).series()
|
| 703 |
+
|
| 704 |
+
|
| 705 |
+
def test_extended_domain_in_expr_to_holonomic():
|
| 706 |
+
x = symbols('x')
|
| 707 |
+
p = expr_to_holonomic(1.2*cos(3.1*x))
|
| 708 |
+
assert p.to_expr() == 1.2*cos(3.1*x)
|
| 709 |
+
assert sstr(p.integrate(x).to_expr()) == '0.387096774193548*sin(3.1*x)'
|
| 710 |
+
_, Dx = DifferentialOperators(RR.old_poly_ring(x), 'Dx')
|
| 711 |
+
p = expr_to_holonomic(1.1329138213*x)
|
| 712 |
+
q = HolonomicFunction((-1.1329138213) + (1.1329138213*x)*Dx, x, 0, {1: [1.1329138213]})
|
| 713 |
+
assert p == q
|
| 714 |
+
assert p.to_expr() == 1.1329138213*x
|
| 715 |
+
assert sstr(p.integrate((x, 1, 2))) == sstr((1.1329138213*x).integrate((x, 1, 2)))
|
| 716 |
+
y, z = symbols('y, z')
|
| 717 |
+
p = expr_to_holonomic(sin(x*y*z), x=x)
|
| 718 |
+
assert p.to_expr() == sin(x*y*z)
|
| 719 |
+
assert p.integrate(x).to_expr() == (-cos(x*y*z) + 1)/(y*z)
|
| 720 |
+
p = expr_to_holonomic(sin(x*y + z), x=x).integrate(x).to_expr()
|
| 721 |
+
q = (cos(z) - cos(x*y + z))/y
|
| 722 |
+
assert p == q
|
| 723 |
+
a = symbols('a')
|
| 724 |
+
p = expr_to_holonomic(a*x, x)
|
| 725 |
+
assert p.to_expr() == a*x
|
| 726 |
+
assert p.integrate(x).to_expr() == a*x**2/2
|
| 727 |
+
D_2, C_1 = symbols("D_2, C_1")
|
| 728 |
+
p = expr_to_holonomic(x) + expr_to_holonomic(1.2*cos(x))
|
| 729 |
+
p = p.to_expr().subs(D_2, 0)
|
| 730 |
+
assert p - x - 1.2*cos(1.0*x) == 0
|
| 731 |
+
p = expr_to_holonomic(x) * expr_to_holonomic(1.2*cos(x))
|
| 732 |
+
p = p.to_expr().subs(C_1, 0)
|
| 733 |
+
assert p - 1.2*x*cos(1.0*x) == 0
|
| 734 |
+
|
| 735 |
+
|
| 736 |
+
def test_to_meijerg():
|
| 737 |
+
x = symbols('x')
|
| 738 |
+
assert hyperexpand(expr_to_holonomic(sin(x)).to_meijerg()) == sin(x)
|
| 739 |
+
assert hyperexpand(expr_to_holonomic(cos(x)).to_meijerg()) == cos(x)
|
| 740 |
+
assert hyperexpand(expr_to_holonomic(exp(x)).to_meijerg()) == exp(x)
|
| 741 |
+
assert hyperexpand(expr_to_holonomic(log(x)).to_meijerg()).simplify() == log(x)
|
| 742 |
+
assert expr_to_holonomic(4*x**2/3 + 7).to_meijerg() == 4*x**2/3 + 7
|
| 743 |
+
assert hyperexpand(expr_to_holonomic(besselj(2, x), lenics=3).to_meijerg()) == besselj(2, x)
|
| 744 |
+
p = hyper((Rational(-1, 2), -3), (), x)
|
| 745 |
+
assert from_hyper(p).to_meijerg() == hyperexpand(p)
|
| 746 |
+
p = hyper((S.One, S(3)), (S(2), ), x)
|
| 747 |
+
assert (hyperexpand(from_hyper(p).to_meijerg()) - hyperexpand(p)).expand() == 0
|
| 748 |
+
p = from_hyper(hyper((-2, -3), (S.Half, ), x))
|
| 749 |
+
s = hyperexpand(hyper((-2, -3), (S.Half, ), x))
|
| 750 |
+
C_0 = Symbol('C_0')
|
| 751 |
+
C_1 = Symbol('C_1')
|
| 752 |
+
D_0 = Symbol('D_0')
|
| 753 |
+
assert (hyperexpand(p.to_meijerg()).subs({C_0:1, D_0:0}) - s).simplify() == 0
|
| 754 |
+
p.y0 = {0: [1], S.Half: [0]}
|
| 755 |
+
assert (hyperexpand(p.to_meijerg()) - s).simplify() == 0
|
| 756 |
+
p = expr_to_holonomic(besselj(S.Half, x), initcond=False)
|
| 757 |
+
assert (p.to_expr() - (D_0*sin(x) + C_0*cos(x) + C_1*sin(x))/sqrt(x)).simplify() == 0
|
| 758 |
+
p = expr_to_holonomic(besselj(S.Half, x), y0={Rational(-1, 2): [sqrt(2)/sqrt(pi), sqrt(2)/sqrt(pi)]})
|
| 759 |
+
assert (p.to_expr() - besselj(S.Half, x) - besselj(Rational(-1, 2), x)).simplify() == 0
|
| 760 |
+
|
| 761 |
+
|
| 762 |
+
def test_gaussian():
|
| 763 |
+
mu, x = symbols("mu x")
|
| 764 |
+
sd = symbols("sd", positive=True)
|
| 765 |
+
Q = QQ[mu, sd].get_field()
|
| 766 |
+
e = sqrt(2)*exp(-(-mu + x)**2/(2*sd**2))/(2*sqrt(pi)*sd)
|
| 767 |
+
h1 = expr_to_holonomic(e, x, domain=Q)
|
| 768 |
+
|
| 769 |
+
_, Dx = DifferentialOperators(Q.old_poly_ring(x), 'Dx')
|
| 770 |
+
h2 = HolonomicFunction((-mu/sd**2 + x/sd**2) + (1)*Dx, x)
|
| 771 |
+
|
| 772 |
+
assert h1 == h2
|
| 773 |
+
|
| 774 |
+
|
| 775 |
+
def test_beta():
|
| 776 |
+
a, b, x = symbols("a b x", positive=True)
|
| 777 |
+
e = x**(a - 1)*(-x + 1)**(b - 1)/beta(a, b)
|
| 778 |
+
Q = QQ[a, b].get_field()
|
| 779 |
+
h1 = expr_to_holonomic(e, x, domain=Q)
|
| 780 |
+
|
| 781 |
+
_, Dx = DifferentialOperators(Q.old_poly_ring(x), 'Dx')
|
| 782 |
+
h2 = HolonomicFunction((a + x*(-a - b + 2) - 1) + (x**2 - x)*Dx, x)
|
| 783 |
+
|
| 784 |
+
assert h1 == h2
|
| 785 |
+
|
| 786 |
+
|
| 787 |
+
def test_gamma():
|
| 788 |
+
a, b, x = symbols("a b x", positive=True)
|
| 789 |
+
e = b**(-a)*x**(a - 1)*exp(-x/b)/gamma(a)
|
| 790 |
+
Q = QQ[a, b].get_field()
|
| 791 |
+
h1 = expr_to_holonomic(e, x, domain=Q)
|
| 792 |
+
|
| 793 |
+
_, Dx = DifferentialOperators(Q.old_poly_ring(x), 'Dx')
|
| 794 |
+
h2 = HolonomicFunction((-a + 1 + x/b) + (x)*Dx, x)
|
| 795 |
+
|
| 796 |
+
assert h1 == h2
|
| 797 |
+
|
| 798 |
+
|
| 799 |
+
def test_symbolic_power():
|
| 800 |
+
x, n = symbols("x n")
|
| 801 |
+
Q = QQ[n].get_field()
|
| 802 |
+
_, Dx = DifferentialOperators(Q.old_poly_ring(x), 'Dx')
|
| 803 |
+
h1 = HolonomicFunction((-1) + (x)*Dx, x) ** -n
|
| 804 |
+
h2 = HolonomicFunction((n) + (x)*Dx, x)
|
| 805 |
+
|
| 806 |
+
assert h1 == h2
|
| 807 |
+
|
| 808 |
+
|
| 809 |
+
def test_negative_power():
|
| 810 |
+
x = symbols("x")
|
| 811 |
+
_, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx')
|
| 812 |
+
h1 = HolonomicFunction((-1) + (x)*Dx, x) ** -2
|
| 813 |
+
h2 = HolonomicFunction((2) + (x)*Dx, x)
|
| 814 |
+
|
| 815 |
+
assert h1 == h2
|
| 816 |
+
|
| 817 |
+
|
| 818 |
+
def test_expr_in_power():
|
| 819 |
+
x, n = symbols("x n")
|
| 820 |
+
Q = QQ[n].get_field()
|
| 821 |
+
_, Dx = DifferentialOperators(Q.old_poly_ring(x), 'Dx')
|
| 822 |
+
h1 = HolonomicFunction((-1) + (x)*Dx, x) ** (n - 3)
|
| 823 |
+
h2 = HolonomicFunction((-n + 3) + (x)*Dx, x)
|
| 824 |
+
|
| 825 |
+
assert h1 == h2
|
| 826 |
+
|
| 827 |
+
|
| 828 |
+
def test_DifferentialOperatorEqPoly():
|
| 829 |
+
x = symbols('x', integer=True)
|
| 830 |
+
R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx')
|
| 831 |
+
do = DifferentialOperator([x**2, R.base.zero, R.base.zero], R)
|
| 832 |
+
do2 = DifferentialOperator([x**2, 1, x], R)
|
| 833 |
+
assert not do == do2
|
| 834 |
+
|
| 835 |
+
# polynomial comparison issue, see https://github.com/sympy/sympy/pull/15799
|
| 836 |
+
# should work once that is solved
|
| 837 |
+
# p = do.listofpoly[0]
|
| 838 |
+
# assert do == p
|
| 839 |
+
|
| 840 |
+
p2 = do2.listofpoly[0]
|
| 841 |
+
assert not do2 == p2
|
| 842 |
+
|
| 843 |
+
|
| 844 |
+
def test_DifferentialOperatorPow():
|
| 845 |
+
x = symbols('x', integer=True)
|
| 846 |
+
R, _ = DifferentialOperators(QQ.old_poly_ring(x), 'Dx')
|
| 847 |
+
do = DifferentialOperator([x**2, R.base.zero, R.base.zero], R)
|
| 848 |
+
a = DifferentialOperator([R.base.one], R)
|
| 849 |
+
for n in range(10):
|
| 850 |
+
assert a == do**n
|
| 851 |
+
a *= do
|
.venv/lib/python3.13/site-packages/sympy/holonomic/tests/test_recurrence.py
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.holonomic.recurrence import RecurrenceOperators, RecurrenceOperator
|
| 2 |
+
from sympy.core.symbol import symbols
|
| 3 |
+
from sympy.polys.domains.rationalfield import QQ
|
| 4 |
+
|
| 5 |
+
|
| 6 |
+
def test_RecurrenceOperator():
|
| 7 |
+
n = symbols('n', integer=True)
|
| 8 |
+
R, Sn = RecurrenceOperators(QQ.old_poly_ring(n), 'Sn')
|
| 9 |
+
assert Sn*n == (n + 1)*Sn
|
| 10 |
+
assert Sn*n**2 == (n**2+1+2*n)*Sn
|
| 11 |
+
assert Sn**2*n**2 == (n**2 + 4*n + 4)*Sn**2
|
| 12 |
+
p = (Sn**3*n**2 + Sn*n)**2
|
| 13 |
+
q = (n**2 + 3*n + 2)*Sn**2 + (2*n**3 + 19*n**2 + 57*n + 52)*Sn**4 + (n**4 + 18*n**3 + \
|
| 14 |
+
117*n**2 + 324*n + 324)*Sn**6
|
| 15 |
+
assert p == q
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
def test_RecurrenceOperatorEqPoly():
|
| 19 |
+
n = symbols('n', integer=True)
|
| 20 |
+
R, Sn = RecurrenceOperators(QQ.old_poly_ring(n), 'Sn')
|
| 21 |
+
rr = RecurrenceOperator([n**2, 0, 0], R)
|
| 22 |
+
rr2 = RecurrenceOperator([n**2, 1, n], R)
|
| 23 |
+
assert not rr == rr2
|
| 24 |
+
|
| 25 |
+
# polynomial comparison issue, see https://github.com/sympy/sympy/pull/15799
|
| 26 |
+
# should work once that is solved
|
| 27 |
+
# d = rr.listofpoly[0]
|
| 28 |
+
# assert rr == d
|
| 29 |
+
|
| 30 |
+
d2 = rr2.listofpoly[0]
|
| 31 |
+
assert not rr2 == d2
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def test_RecurrenceOperatorPow():
|
| 35 |
+
n = symbols('n', integer=True)
|
| 36 |
+
R, _ = RecurrenceOperators(QQ.old_poly_ring(n), 'Sn')
|
| 37 |
+
rr = RecurrenceOperator([n**2, 0, 0], R)
|
| 38 |
+
a = RecurrenceOperator([R.base.one], R)
|
| 39 |
+
for m in range(10):
|
| 40 |
+
assert a == rr**m
|
| 41 |
+
a *= rr
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/__init__.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of mathematical domains. """
|
| 2 |
+
|
| 3 |
+
__all__ = [
|
| 4 |
+
'Domain', 'FiniteField', 'IntegerRing', 'RationalField', 'RealField',
|
| 5 |
+
'ComplexField', 'AlgebraicField', 'PolynomialRing', 'FractionField',
|
| 6 |
+
'ExpressionDomain', 'PythonRational',
|
| 7 |
+
|
| 8 |
+
'GF', 'FF', 'ZZ', 'QQ', 'ZZ_I', 'QQ_I', 'RR', 'CC', 'EX', 'EXRAW',
|
| 9 |
+
]
|
| 10 |
+
|
| 11 |
+
from .domain import Domain
|
| 12 |
+
from .finitefield import FiniteField, FF, GF
|
| 13 |
+
from .integerring import IntegerRing, ZZ
|
| 14 |
+
from .rationalfield import RationalField, QQ
|
| 15 |
+
from .algebraicfield import AlgebraicField
|
| 16 |
+
from .gaussiandomains import ZZ_I, QQ_I
|
| 17 |
+
from .realfield import RealField, RR
|
| 18 |
+
from .complexfield import ComplexField, CC
|
| 19 |
+
from .polynomialring import PolynomialRing
|
| 20 |
+
from .fractionfield import FractionField
|
| 21 |
+
from .expressiondomain import ExpressionDomain, EX
|
| 22 |
+
from .expressionrawdomain import EXRAW
|
| 23 |
+
from .pythonrational import PythonRational
|
| 24 |
+
|
| 25 |
+
|
| 26 |
+
# This is imported purely for backwards compatibility because some parts of
|
| 27 |
+
# the codebase used to import this from here and it's possible that downstream
|
| 28 |
+
# does as well:
|
| 29 |
+
from sympy.external.gmpy import GROUND_TYPES # noqa: F401
|
| 30 |
+
|
| 31 |
+
#
|
| 32 |
+
# The rest of these are obsolete and provided only for backwards
|
| 33 |
+
# compatibility:
|
| 34 |
+
#
|
| 35 |
+
|
| 36 |
+
from .pythonfinitefield import PythonFiniteField
|
| 37 |
+
from .gmpyfinitefield import GMPYFiniteField
|
| 38 |
+
from .pythonintegerring import PythonIntegerRing
|
| 39 |
+
from .gmpyintegerring import GMPYIntegerRing
|
| 40 |
+
from .pythonrationalfield import PythonRationalField
|
| 41 |
+
from .gmpyrationalfield import GMPYRationalField
|
| 42 |
+
|
| 43 |
+
FF_python = PythonFiniteField
|
| 44 |
+
FF_gmpy = GMPYFiniteField
|
| 45 |
+
|
| 46 |
+
ZZ_python = PythonIntegerRing
|
| 47 |
+
ZZ_gmpy = GMPYIntegerRing
|
| 48 |
+
|
| 49 |
+
QQ_python = PythonRationalField
|
| 50 |
+
QQ_gmpy = GMPYRationalField
|
| 51 |
+
|
| 52 |
+
__all__.extend((
|
| 53 |
+
'PythonFiniteField', 'GMPYFiniteField', 'PythonIntegerRing',
|
| 54 |
+
'GMPYIntegerRing', 'PythonRational', 'GMPYRationalField',
|
| 55 |
+
|
| 56 |
+
'FF_python', 'FF_gmpy', 'ZZ_python', 'ZZ_gmpy', 'QQ_python', 'QQ_gmpy',
|
| 57 |
+
))
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/algebraicfield.py
ADDED
|
@@ -0,0 +1,638 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`AlgebraicField` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.core.add import Add
|
| 5 |
+
from sympy.core.mul import Mul
|
| 6 |
+
from sympy.core.singleton import S
|
| 7 |
+
from sympy.core.symbol import Dummy, symbols
|
| 8 |
+
from sympy.polys.domains.characteristiczero import CharacteristicZero
|
| 9 |
+
from sympy.polys.domains.field import Field
|
| 10 |
+
from sympy.polys.domains.simpledomain import SimpleDomain
|
| 11 |
+
from sympy.polys.polyclasses import ANP
|
| 12 |
+
from sympy.polys.polyerrors import CoercionFailed, DomainError, NotAlgebraic, IsomorphismFailed
|
| 13 |
+
from sympy.utilities import public
|
| 14 |
+
|
| 15 |
+
@public
|
| 16 |
+
class AlgebraicField(Field, CharacteristicZero, SimpleDomain):
|
| 17 |
+
r"""Algebraic number field :ref:`QQ(a)`
|
| 18 |
+
|
| 19 |
+
A :ref:`QQ(a)` domain represents an `algebraic number field`_
|
| 20 |
+
`\mathbb{Q}(a)` as a :py:class:`~.Domain` in the domain system (see
|
| 21 |
+
:ref:`polys-domainsintro`).
|
| 22 |
+
|
| 23 |
+
A :py:class:`~.Poly` created from an expression involving `algebraic
|
| 24 |
+
numbers`_ will treat the algebraic numbers as generators if the generators
|
| 25 |
+
argument is not specified.
|
| 26 |
+
|
| 27 |
+
>>> from sympy import Poly, Symbol, sqrt
|
| 28 |
+
>>> x = Symbol('x')
|
| 29 |
+
>>> Poly(x**2 + sqrt(2))
|
| 30 |
+
Poly(x**2 + (sqrt(2)), x, sqrt(2), domain='ZZ')
|
| 31 |
+
|
| 32 |
+
That is a multivariate polynomial with ``sqrt(2)`` treated as one of the
|
| 33 |
+
generators (variables). If the generators are explicitly specified then
|
| 34 |
+
``sqrt(2)`` will be considered to be a coefficient but by default the
|
| 35 |
+
:ref:`EX` domain is used. To make a :py:class:`~.Poly` with a :ref:`QQ(a)`
|
| 36 |
+
domain the argument ``extension=True`` can be given.
|
| 37 |
+
|
| 38 |
+
>>> Poly(x**2 + sqrt(2), x)
|
| 39 |
+
Poly(x**2 + sqrt(2), x, domain='EX')
|
| 40 |
+
>>> Poly(x**2 + sqrt(2), x, extension=True)
|
| 41 |
+
Poly(x**2 + sqrt(2), x, domain='QQ<sqrt(2)>')
|
| 42 |
+
|
| 43 |
+
A generator of the algebraic field extension can also be specified
|
| 44 |
+
explicitly which is particularly useful if the coefficients are all
|
| 45 |
+
rational but an extension field is needed (e.g. to factor the
|
| 46 |
+
polynomial).
|
| 47 |
+
|
| 48 |
+
>>> Poly(x**2 + 1)
|
| 49 |
+
Poly(x**2 + 1, x, domain='ZZ')
|
| 50 |
+
>>> Poly(x**2 + 1, extension=sqrt(2))
|
| 51 |
+
Poly(x**2 + 1, x, domain='QQ<sqrt(2)>')
|
| 52 |
+
|
| 53 |
+
It is possible to factorise a polynomial over a :ref:`QQ(a)` domain using
|
| 54 |
+
the ``extension`` argument to :py:func:`~.factor` or by specifying the domain
|
| 55 |
+
explicitly.
|
| 56 |
+
|
| 57 |
+
>>> from sympy import factor, QQ
|
| 58 |
+
>>> factor(x**2 - 2)
|
| 59 |
+
x**2 - 2
|
| 60 |
+
>>> factor(x**2 - 2, extension=sqrt(2))
|
| 61 |
+
(x - sqrt(2))*(x + sqrt(2))
|
| 62 |
+
>>> factor(x**2 - 2, domain='QQ<sqrt(2)>')
|
| 63 |
+
(x - sqrt(2))*(x + sqrt(2))
|
| 64 |
+
>>> factor(x**2 - 2, domain=QQ.algebraic_field(sqrt(2)))
|
| 65 |
+
(x - sqrt(2))*(x + sqrt(2))
|
| 66 |
+
|
| 67 |
+
The ``extension=True`` argument can be used but will only create an
|
| 68 |
+
extension that contains the coefficients which is usually not enough to
|
| 69 |
+
factorise the polynomial.
|
| 70 |
+
|
| 71 |
+
>>> p = x**3 + sqrt(2)*x**2 - 2*x - 2*sqrt(2)
|
| 72 |
+
>>> factor(p) # treats sqrt(2) as a symbol
|
| 73 |
+
(x + sqrt(2))*(x**2 - 2)
|
| 74 |
+
>>> factor(p, extension=True)
|
| 75 |
+
(x - sqrt(2))*(x + sqrt(2))**2
|
| 76 |
+
>>> factor(x**2 - 2, extension=True) # all rational coefficients
|
| 77 |
+
x**2 - 2
|
| 78 |
+
|
| 79 |
+
It is also possible to use :ref:`QQ(a)` with the :py:func:`~.cancel`
|
| 80 |
+
and :py:func:`~.gcd` functions.
|
| 81 |
+
|
| 82 |
+
>>> from sympy import cancel, gcd
|
| 83 |
+
>>> cancel((x**2 - 2)/(x - sqrt(2)))
|
| 84 |
+
(x**2 - 2)/(x - sqrt(2))
|
| 85 |
+
>>> cancel((x**2 - 2)/(x - sqrt(2)), extension=sqrt(2))
|
| 86 |
+
x + sqrt(2)
|
| 87 |
+
>>> gcd(x**2 - 2, x - sqrt(2))
|
| 88 |
+
1
|
| 89 |
+
>>> gcd(x**2 - 2, x - sqrt(2), extension=sqrt(2))
|
| 90 |
+
x - sqrt(2)
|
| 91 |
+
|
| 92 |
+
When using the domain directly :ref:`QQ(a)` can be used as a constructor
|
| 93 |
+
to create instances which then support the operations ``+,-,*,**,/``. The
|
| 94 |
+
:py:meth:`~.Domain.algebraic_field` method is used to construct a
|
| 95 |
+
particular :ref:`QQ(a)` domain. The :py:meth:`~.Domain.from_sympy` method
|
| 96 |
+
can be used to create domain elements from normal SymPy expressions.
|
| 97 |
+
|
| 98 |
+
>>> K = QQ.algebraic_field(sqrt(2))
|
| 99 |
+
>>> K
|
| 100 |
+
QQ<sqrt(2)>
|
| 101 |
+
>>> xk = K.from_sympy(3 + 4*sqrt(2))
|
| 102 |
+
>>> xk # doctest: +SKIP
|
| 103 |
+
ANP([4, 3], [1, 0, -2], QQ)
|
| 104 |
+
|
| 105 |
+
Elements of :ref:`QQ(a)` are instances of :py:class:`~.ANP` which have
|
| 106 |
+
limited printing support. The raw display shows the internal
|
| 107 |
+
representation of the element as the list ``[4, 3]`` representing the
|
| 108 |
+
coefficients of ``1`` and ``sqrt(2)`` for this element in the form
|
| 109 |
+
``a * sqrt(2) + b * 1`` where ``a`` and ``b`` are elements of :ref:`QQ`.
|
| 110 |
+
The minimal polynomial for the generator ``(x**2 - 2)`` is also shown in
|
| 111 |
+
the :ref:`dup-representation` as the list ``[1, 0, -2]``. We can use
|
| 112 |
+
:py:meth:`~.Domain.to_sympy` to get a better printed form for the
|
| 113 |
+
elements and to see the results of operations.
|
| 114 |
+
|
| 115 |
+
>>> xk = K.from_sympy(3 + 4*sqrt(2))
|
| 116 |
+
>>> yk = K.from_sympy(2 + 3*sqrt(2))
|
| 117 |
+
>>> xk * yk # doctest: +SKIP
|
| 118 |
+
ANP([17, 30], [1, 0, -2], QQ)
|
| 119 |
+
>>> K.to_sympy(xk * yk)
|
| 120 |
+
17*sqrt(2) + 30
|
| 121 |
+
>>> K.to_sympy(xk + yk)
|
| 122 |
+
5 + 7*sqrt(2)
|
| 123 |
+
>>> K.to_sympy(xk ** 2)
|
| 124 |
+
24*sqrt(2) + 41
|
| 125 |
+
>>> K.to_sympy(xk / yk)
|
| 126 |
+
sqrt(2)/14 + 9/7
|
| 127 |
+
|
| 128 |
+
Any expression representing an algebraic number can be used to generate
|
| 129 |
+
a :ref:`QQ(a)` domain provided its `minimal polynomial`_ can be computed.
|
| 130 |
+
The function :py:func:`~.minpoly` function is used for this.
|
| 131 |
+
|
| 132 |
+
>>> from sympy import exp, I, pi, minpoly
|
| 133 |
+
>>> g = exp(2*I*pi/3)
|
| 134 |
+
>>> g
|
| 135 |
+
exp(2*I*pi/3)
|
| 136 |
+
>>> g.is_algebraic
|
| 137 |
+
True
|
| 138 |
+
>>> minpoly(g, x)
|
| 139 |
+
x**2 + x + 1
|
| 140 |
+
>>> factor(x**3 - 1, extension=g)
|
| 141 |
+
(x - 1)*(x - exp(2*I*pi/3))*(x + 1 + exp(2*I*pi/3))
|
| 142 |
+
|
| 143 |
+
It is also possible to make an algebraic field from multiple extension
|
| 144 |
+
elements.
|
| 145 |
+
|
| 146 |
+
>>> K = QQ.algebraic_field(sqrt(2), sqrt(3))
|
| 147 |
+
>>> K
|
| 148 |
+
QQ<sqrt(2) + sqrt(3)>
|
| 149 |
+
>>> p = x**4 - 5*x**2 + 6
|
| 150 |
+
>>> factor(p)
|
| 151 |
+
(x**2 - 3)*(x**2 - 2)
|
| 152 |
+
>>> factor(p, domain=K)
|
| 153 |
+
(x - sqrt(2))*(x + sqrt(2))*(x - sqrt(3))*(x + sqrt(3))
|
| 154 |
+
>>> factor(p, extension=[sqrt(2), sqrt(3)])
|
| 155 |
+
(x - sqrt(2))*(x + sqrt(2))*(x - sqrt(3))*(x + sqrt(3))
|
| 156 |
+
|
| 157 |
+
Multiple extension elements are always combined together to make a single
|
| 158 |
+
`primitive element`_. In the case of ``[sqrt(2), sqrt(3)]`` the primitive
|
| 159 |
+
element chosen is ``sqrt(2) + sqrt(3)`` which is why the domain displays
|
| 160 |
+
as ``QQ<sqrt(2) + sqrt(3)>``. The minimal polynomial for the primitive
|
| 161 |
+
element is computed using the :py:func:`~.primitive_element` function.
|
| 162 |
+
|
| 163 |
+
>>> from sympy import primitive_element
|
| 164 |
+
>>> primitive_element([sqrt(2), sqrt(3)], x)
|
| 165 |
+
(x**4 - 10*x**2 + 1, [1, 1])
|
| 166 |
+
>>> minpoly(sqrt(2) + sqrt(3), x)
|
| 167 |
+
x**4 - 10*x**2 + 1
|
| 168 |
+
|
| 169 |
+
The extension elements that generate the domain can be accessed from the
|
| 170 |
+
domain using the :py:attr:`~.ext` and :py:attr:`~.orig_ext` attributes as
|
| 171 |
+
instances of :py:class:`~.AlgebraicNumber`. The minimal polynomial for
|
| 172 |
+
the primitive element as a :py:class:`~.DMP` instance is available as
|
| 173 |
+
:py:attr:`~.mod`.
|
| 174 |
+
|
| 175 |
+
>>> K = QQ.algebraic_field(sqrt(2), sqrt(3))
|
| 176 |
+
>>> K
|
| 177 |
+
QQ<sqrt(2) + sqrt(3)>
|
| 178 |
+
>>> K.ext
|
| 179 |
+
sqrt(2) + sqrt(3)
|
| 180 |
+
>>> K.orig_ext
|
| 181 |
+
(sqrt(2), sqrt(3))
|
| 182 |
+
>>> K.mod # doctest: +SKIP
|
| 183 |
+
DMP_Python([1, 0, -10, 0, 1], QQ)
|
| 184 |
+
|
| 185 |
+
The `discriminant`_ of the field can be obtained from the
|
| 186 |
+
:py:meth:`~.discriminant` method, and an `integral basis`_ from the
|
| 187 |
+
:py:meth:`~.integral_basis` method. The latter returns a list of
|
| 188 |
+
:py:class:`~.ANP` instances by default, but can be made to return instances
|
| 189 |
+
of :py:class:`~.Expr` or :py:class:`~.AlgebraicNumber` by passing a ``fmt``
|
| 190 |
+
argument. The maximal order, or ring of integers, of the field can also be
|
| 191 |
+
obtained from the :py:meth:`~.maximal_order` method, as a
|
| 192 |
+
:py:class:`~sympy.polys.numberfields.modules.Submodule`.
|
| 193 |
+
|
| 194 |
+
>>> zeta5 = exp(2*I*pi/5)
|
| 195 |
+
>>> K = QQ.algebraic_field(zeta5)
|
| 196 |
+
>>> K
|
| 197 |
+
QQ<exp(2*I*pi/5)>
|
| 198 |
+
>>> K.discriminant()
|
| 199 |
+
125
|
| 200 |
+
>>> K = QQ.algebraic_field(sqrt(5))
|
| 201 |
+
>>> K
|
| 202 |
+
QQ<sqrt(5)>
|
| 203 |
+
>>> K.integral_basis(fmt='sympy')
|
| 204 |
+
[1, 1/2 + sqrt(5)/2]
|
| 205 |
+
>>> K.maximal_order()
|
| 206 |
+
Submodule[[2, 0], [1, 1]]/2
|
| 207 |
+
|
| 208 |
+
The factorization of a rational prime into prime ideals of the field is
|
| 209 |
+
computed by the :py:meth:`~.primes_above` method, which returns a list
|
| 210 |
+
of :py:class:`~sympy.polys.numberfields.primes.PrimeIdeal` instances.
|
| 211 |
+
|
| 212 |
+
>>> zeta7 = exp(2*I*pi/7)
|
| 213 |
+
>>> K = QQ.algebraic_field(zeta7)
|
| 214 |
+
>>> K
|
| 215 |
+
QQ<exp(2*I*pi/7)>
|
| 216 |
+
>>> K.primes_above(11)
|
| 217 |
+
[(11, _x**3 + 5*_x**2 + 4*_x - 1), (11, _x**3 - 4*_x**2 - 5*_x - 1)]
|
| 218 |
+
|
| 219 |
+
The Galois group of the Galois closure of the field can be computed (when
|
| 220 |
+
the minimal polynomial of the field is of sufficiently small degree).
|
| 221 |
+
|
| 222 |
+
>>> K.galois_group(by_name=True)[0]
|
| 223 |
+
S6TransitiveSubgroups.C6
|
| 224 |
+
|
| 225 |
+
Notes
|
| 226 |
+
=====
|
| 227 |
+
|
| 228 |
+
It is not currently possible to generate an algebraic extension over any
|
| 229 |
+
domain other than :ref:`QQ`. Ideally it would be possible to generate
|
| 230 |
+
extensions like ``QQ(x)(sqrt(x**2 - 2))``. This is equivalent to the
|
| 231 |
+
quotient ring ``QQ(x)[y]/(y**2 - x**2 + 2)`` and there are two
|
| 232 |
+
implementations of this kind of quotient ring/extension in the
|
| 233 |
+
:py:class:`~.QuotientRing` and :py:class:`~.MonogenicFiniteExtension`
|
| 234 |
+
classes. Each of those implementations needs some work to make them fully
|
| 235 |
+
usable though.
|
| 236 |
+
|
| 237 |
+
.. _algebraic number field: https://en.wikipedia.org/wiki/Algebraic_number_field
|
| 238 |
+
.. _algebraic numbers: https://en.wikipedia.org/wiki/Algebraic_number
|
| 239 |
+
.. _discriminant: https://en.wikipedia.org/wiki/Discriminant_of_an_algebraic_number_field
|
| 240 |
+
.. _integral basis: https://en.wikipedia.org/wiki/Algebraic_number_field#Integral_basis
|
| 241 |
+
.. _minimal polynomial: https://en.wikipedia.org/wiki/Minimal_polynomial_(field_theory)
|
| 242 |
+
.. _primitive element: https://en.wikipedia.org/wiki/Primitive_element_theorem
|
| 243 |
+
"""
|
| 244 |
+
|
| 245 |
+
dtype = ANP
|
| 246 |
+
|
| 247 |
+
is_AlgebraicField = is_Algebraic = True
|
| 248 |
+
is_Numerical = True
|
| 249 |
+
|
| 250 |
+
has_assoc_Ring = False
|
| 251 |
+
has_assoc_Field = True
|
| 252 |
+
|
| 253 |
+
def __init__(self, dom, *ext, alias=None):
|
| 254 |
+
r"""
|
| 255 |
+
Parameters
|
| 256 |
+
==========
|
| 257 |
+
|
| 258 |
+
dom : :py:class:`~.Domain`
|
| 259 |
+
The base field over which this is an extension field.
|
| 260 |
+
Currently only :ref:`QQ` is accepted.
|
| 261 |
+
|
| 262 |
+
*ext : One or more :py:class:`~.Expr`
|
| 263 |
+
Generators of the extension. These should be expressions that are
|
| 264 |
+
algebraic over `\mathbb{Q}`.
|
| 265 |
+
|
| 266 |
+
alias : str, :py:class:`~.Symbol`, None, optional (default=None)
|
| 267 |
+
If provided, this will be used as the alias symbol for the
|
| 268 |
+
primitive element of the :py:class:`~.AlgebraicField`.
|
| 269 |
+
If ``None``, while ``ext`` consists of exactly one
|
| 270 |
+
:py:class:`~.AlgebraicNumber`, its alias (if any) will be used.
|
| 271 |
+
"""
|
| 272 |
+
if not dom.is_QQ:
|
| 273 |
+
raise DomainError("ground domain must be a rational field")
|
| 274 |
+
|
| 275 |
+
from sympy.polys.numberfields import to_number_field
|
| 276 |
+
if len(ext) == 1 and isinstance(ext[0], tuple):
|
| 277 |
+
orig_ext = ext[0][1:]
|
| 278 |
+
else:
|
| 279 |
+
orig_ext = ext
|
| 280 |
+
|
| 281 |
+
if alias is None and len(ext) == 1:
|
| 282 |
+
alias = getattr(ext[0], 'alias', None)
|
| 283 |
+
|
| 284 |
+
self.orig_ext = orig_ext
|
| 285 |
+
"""
|
| 286 |
+
Original elements given to generate the extension.
|
| 287 |
+
|
| 288 |
+
>>> from sympy import QQ, sqrt
|
| 289 |
+
>>> K = QQ.algebraic_field(sqrt(2), sqrt(3))
|
| 290 |
+
>>> K.orig_ext
|
| 291 |
+
(sqrt(2), sqrt(3))
|
| 292 |
+
"""
|
| 293 |
+
|
| 294 |
+
self.ext = to_number_field(ext, alias=alias)
|
| 295 |
+
"""
|
| 296 |
+
Primitive element used for the extension.
|
| 297 |
+
|
| 298 |
+
>>> from sympy import QQ, sqrt
|
| 299 |
+
>>> K = QQ.algebraic_field(sqrt(2), sqrt(3))
|
| 300 |
+
>>> K.ext
|
| 301 |
+
sqrt(2) + sqrt(3)
|
| 302 |
+
"""
|
| 303 |
+
|
| 304 |
+
self.mod = self.ext.minpoly.rep
|
| 305 |
+
"""
|
| 306 |
+
Minimal polynomial for the primitive element of the extension.
|
| 307 |
+
|
| 308 |
+
>>> from sympy import QQ, sqrt
|
| 309 |
+
>>> K = QQ.algebraic_field(sqrt(2))
|
| 310 |
+
>>> K.mod
|
| 311 |
+
DMP([1, 0, -2], QQ)
|
| 312 |
+
"""
|
| 313 |
+
|
| 314 |
+
self.domain = self.dom = dom
|
| 315 |
+
|
| 316 |
+
self.ngens = 1
|
| 317 |
+
self.symbols = self.gens = (self.ext,)
|
| 318 |
+
self.unit = self([dom(1), dom(0)])
|
| 319 |
+
|
| 320 |
+
self.zero = self.dtype.zero(self.mod.to_list(), dom)
|
| 321 |
+
self.one = self.dtype.one(self.mod.to_list(), dom)
|
| 322 |
+
|
| 323 |
+
self._maximal_order = None
|
| 324 |
+
self._discriminant = None
|
| 325 |
+
self._nilradicals_mod_p = {}
|
| 326 |
+
|
| 327 |
+
def new(self, element):
|
| 328 |
+
return self.dtype(element, self.mod.to_list(), self.dom)
|
| 329 |
+
|
| 330 |
+
def __str__(self):
|
| 331 |
+
return str(self.dom) + '<' + str(self.ext) + '>'
|
| 332 |
+
|
| 333 |
+
def __hash__(self):
|
| 334 |
+
return hash((self.__class__.__name__, self.dtype, self.dom, self.ext))
|
| 335 |
+
|
| 336 |
+
def __eq__(self, other):
|
| 337 |
+
"""Returns ``True`` if two domains are equivalent. """
|
| 338 |
+
if isinstance(other, AlgebraicField):
|
| 339 |
+
return self.dtype == other.dtype and self.ext == other.ext
|
| 340 |
+
else:
|
| 341 |
+
return NotImplemented
|
| 342 |
+
|
| 343 |
+
def algebraic_field(self, *extension, alias=None):
|
| 344 |
+
r"""Returns an algebraic field, i.e. `\mathbb{Q}(\alpha, \ldots)`. """
|
| 345 |
+
return AlgebraicField(self.dom, *((self.ext,) + extension), alias=alias)
|
| 346 |
+
|
| 347 |
+
def to_alg_num(self, a):
|
| 348 |
+
"""Convert ``a`` of ``dtype`` to an :py:class:`~.AlgebraicNumber`. """
|
| 349 |
+
return self.ext.field_element(a)
|
| 350 |
+
|
| 351 |
+
def to_sympy(self, a):
|
| 352 |
+
"""Convert ``a`` of ``dtype`` to a SymPy object. """
|
| 353 |
+
# Precompute a converter to be reused:
|
| 354 |
+
if not hasattr(self, '_converter'):
|
| 355 |
+
self._converter = _make_converter(self)
|
| 356 |
+
|
| 357 |
+
return self._converter(a)
|
| 358 |
+
|
| 359 |
+
def from_sympy(self, a):
|
| 360 |
+
"""Convert SymPy's expression to ``dtype``. """
|
| 361 |
+
try:
|
| 362 |
+
return self([self.dom.from_sympy(a)])
|
| 363 |
+
except CoercionFailed:
|
| 364 |
+
pass
|
| 365 |
+
|
| 366 |
+
from sympy.polys.numberfields import to_number_field
|
| 367 |
+
|
| 368 |
+
try:
|
| 369 |
+
return self(to_number_field(a, self.ext).native_coeffs())
|
| 370 |
+
except (NotAlgebraic, IsomorphismFailed):
|
| 371 |
+
raise CoercionFailed(
|
| 372 |
+
"%s is not a valid algebraic number in %s" % (a, self))
|
| 373 |
+
|
| 374 |
+
def from_ZZ(K1, a, K0):
|
| 375 |
+
"""Convert a Python ``int`` object to ``dtype``. """
|
| 376 |
+
return K1(K1.dom.convert(a, K0))
|
| 377 |
+
|
| 378 |
+
def from_ZZ_python(K1, a, K0):
|
| 379 |
+
"""Convert a Python ``int`` object to ``dtype``. """
|
| 380 |
+
return K1(K1.dom.convert(a, K0))
|
| 381 |
+
|
| 382 |
+
def from_QQ(K1, a, K0):
|
| 383 |
+
"""Convert a Python ``Fraction`` object to ``dtype``. """
|
| 384 |
+
return K1(K1.dom.convert(a, K0))
|
| 385 |
+
|
| 386 |
+
def from_QQ_python(K1, a, K0):
|
| 387 |
+
"""Convert a Python ``Fraction`` object to ``dtype``. """
|
| 388 |
+
return K1(K1.dom.convert(a, K0))
|
| 389 |
+
|
| 390 |
+
def from_ZZ_gmpy(K1, a, K0):
|
| 391 |
+
"""Convert a GMPY ``mpz`` object to ``dtype``. """
|
| 392 |
+
return K1(K1.dom.convert(a, K0))
|
| 393 |
+
|
| 394 |
+
def from_QQ_gmpy(K1, a, K0):
|
| 395 |
+
"""Convert a GMPY ``mpq`` object to ``dtype``. """
|
| 396 |
+
return K1(K1.dom.convert(a, K0))
|
| 397 |
+
|
| 398 |
+
def from_RealField(K1, a, K0):
|
| 399 |
+
"""Convert a mpmath ``mpf`` object to ``dtype``. """
|
| 400 |
+
return K1(K1.dom.convert(a, K0))
|
| 401 |
+
|
| 402 |
+
def get_ring(self):
|
| 403 |
+
"""Returns a ring associated with ``self``. """
|
| 404 |
+
raise DomainError('there is no ring associated with %s' % self)
|
| 405 |
+
|
| 406 |
+
def is_positive(self, a):
|
| 407 |
+
"""Returns True if ``a`` is positive. """
|
| 408 |
+
return self.dom.is_positive(a.LC())
|
| 409 |
+
|
| 410 |
+
def is_negative(self, a):
|
| 411 |
+
"""Returns True if ``a`` is negative. """
|
| 412 |
+
return self.dom.is_negative(a.LC())
|
| 413 |
+
|
| 414 |
+
def is_nonpositive(self, a):
|
| 415 |
+
"""Returns True if ``a`` is non-positive. """
|
| 416 |
+
return self.dom.is_nonpositive(a.LC())
|
| 417 |
+
|
| 418 |
+
def is_nonnegative(self, a):
|
| 419 |
+
"""Returns True if ``a`` is non-negative. """
|
| 420 |
+
return self.dom.is_nonnegative(a.LC())
|
| 421 |
+
|
| 422 |
+
def numer(self, a):
|
| 423 |
+
"""Returns numerator of ``a``. """
|
| 424 |
+
return a
|
| 425 |
+
|
| 426 |
+
def denom(self, a):
|
| 427 |
+
"""Returns denominator of ``a``. """
|
| 428 |
+
return self.one
|
| 429 |
+
|
| 430 |
+
def from_AlgebraicField(K1, a, K0):
|
| 431 |
+
"""Convert AlgebraicField element 'a' to another AlgebraicField """
|
| 432 |
+
return K1.from_sympy(K0.to_sympy(a))
|
| 433 |
+
|
| 434 |
+
def from_GaussianIntegerRing(K1, a, K0):
|
| 435 |
+
"""Convert a GaussianInteger element 'a' to ``dtype``. """
|
| 436 |
+
return K1.from_sympy(K0.to_sympy(a))
|
| 437 |
+
|
| 438 |
+
def from_GaussianRationalField(K1, a, K0):
|
| 439 |
+
"""Convert a GaussianRational element 'a' to ``dtype``. """
|
| 440 |
+
return K1.from_sympy(K0.to_sympy(a))
|
| 441 |
+
|
| 442 |
+
def _do_round_two(self):
|
| 443 |
+
from sympy.polys.numberfields.basis import round_two
|
| 444 |
+
ZK, dK = round_two(self, radicals=self._nilradicals_mod_p)
|
| 445 |
+
self._maximal_order = ZK
|
| 446 |
+
self._discriminant = dK
|
| 447 |
+
|
| 448 |
+
def maximal_order(self):
|
| 449 |
+
"""
|
| 450 |
+
Compute the maximal order, or ring of integers, of the field.
|
| 451 |
+
|
| 452 |
+
Returns
|
| 453 |
+
=======
|
| 454 |
+
|
| 455 |
+
:py:class:`~sympy.polys.numberfields.modules.Submodule`.
|
| 456 |
+
|
| 457 |
+
See Also
|
| 458 |
+
========
|
| 459 |
+
|
| 460 |
+
integral_basis
|
| 461 |
+
|
| 462 |
+
"""
|
| 463 |
+
if self._maximal_order is None:
|
| 464 |
+
self._do_round_two()
|
| 465 |
+
return self._maximal_order
|
| 466 |
+
|
| 467 |
+
def integral_basis(self, fmt=None):
|
| 468 |
+
r"""
|
| 469 |
+
Get an integral basis for the field.
|
| 470 |
+
|
| 471 |
+
Parameters
|
| 472 |
+
==========
|
| 473 |
+
|
| 474 |
+
fmt : str, None, optional (default=None)
|
| 475 |
+
If ``None``, return a list of :py:class:`~.ANP` instances.
|
| 476 |
+
If ``"sympy"``, convert each element of the list to an
|
| 477 |
+
:py:class:`~.Expr`, using ``self.to_sympy()``.
|
| 478 |
+
If ``"alg"``, convert each element of the list to an
|
| 479 |
+
:py:class:`~.AlgebraicNumber`, using ``self.to_alg_num()``.
|
| 480 |
+
|
| 481 |
+
Examples
|
| 482 |
+
========
|
| 483 |
+
|
| 484 |
+
>>> from sympy import QQ, AlgebraicNumber, sqrt
|
| 485 |
+
>>> alpha = AlgebraicNumber(sqrt(5), alias='alpha')
|
| 486 |
+
>>> k = QQ.algebraic_field(alpha)
|
| 487 |
+
>>> B0 = k.integral_basis()
|
| 488 |
+
>>> B1 = k.integral_basis(fmt='sympy')
|
| 489 |
+
>>> B2 = k.integral_basis(fmt='alg')
|
| 490 |
+
>>> print(B0[1]) # doctest: +SKIP
|
| 491 |
+
ANP([mpq(1,2), mpq(1,2)], [mpq(1,1), mpq(0,1), mpq(-5,1)], QQ)
|
| 492 |
+
>>> print(B1[1])
|
| 493 |
+
1/2 + alpha/2
|
| 494 |
+
>>> print(B2[1])
|
| 495 |
+
alpha/2 + 1/2
|
| 496 |
+
|
| 497 |
+
In the last two cases we get legible expressions, which print somewhat
|
| 498 |
+
differently because of the different types involved:
|
| 499 |
+
|
| 500 |
+
>>> print(type(B1[1]))
|
| 501 |
+
<class 'sympy.core.add.Add'>
|
| 502 |
+
>>> print(type(B2[1]))
|
| 503 |
+
<class 'sympy.core.numbers.AlgebraicNumber'>
|
| 504 |
+
|
| 505 |
+
See Also
|
| 506 |
+
========
|
| 507 |
+
|
| 508 |
+
to_sympy
|
| 509 |
+
to_alg_num
|
| 510 |
+
maximal_order
|
| 511 |
+
"""
|
| 512 |
+
ZK = self.maximal_order()
|
| 513 |
+
M = ZK.QQ_matrix
|
| 514 |
+
n = M.shape[1]
|
| 515 |
+
B = [self.new(list(reversed(M[:, j].flat()))) for j in range(n)]
|
| 516 |
+
if fmt == 'sympy':
|
| 517 |
+
return [self.to_sympy(b) for b in B]
|
| 518 |
+
elif fmt == 'alg':
|
| 519 |
+
return [self.to_alg_num(b) for b in B]
|
| 520 |
+
return B
|
| 521 |
+
|
| 522 |
+
def discriminant(self):
|
| 523 |
+
"""Get the discriminant of the field."""
|
| 524 |
+
if self._discriminant is None:
|
| 525 |
+
self._do_round_two()
|
| 526 |
+
return self._discriminant
|
| 527 |
+
|
| 528 |
+
def primes_above(self, p):
|
| 529 |
+
"""Compute the prime ideals lying above a given rational prime *p*."""
|
| 530 |
+
from sympy.polys.numberfields.primes import prime_decomp
|
| 531 |
+
ZK = self.maximal_order()
|
| 532 |
+
dK = self.discriminant()
|
| 533 |
+
rad = self._nilradicals_mod_p.get(p)
|
| 534 |
+
return prime_decomp(p, ZK=ZK, dK=dK, radical=rad)
|
| 535 |
+
|
| 536 |
+
def galois_group(self, by_name=False, max_tries=30, randomize=False):
|
| 537 |
+
"""
|
| 538 |
+
Compute the Galois group of the Galois closure of this field.
|
| 539 |
+
|
| 540 |
+
Examples
|
| 541 |
+
========
|
| 542 |
+
|
| 543 |
+
If the field is Galois, the order of the group will equal the degree
|
| 544 |
+
of the field:
|
| 545 |
+
|
| 546 |
+
>>> from sympy import QQ
|
| 547 |
+
>>> from sympy.abc import x
|
| 548 |
+
>>> k = QQ.alg_field_from_poly(x**4 + 1)
|
| 549 |
+
>>> G, _ = k.galois_group()
|
| 550 |
+
>>> G.order()
|
| 551 |
+
4
|
| 552 |
+
|
| 553 |
+
If the field is not Galois, then its Galois closure is a proper
|
| 554 |
+
extension, and the order of the Galois group will be greater than the
|
| 555 |
+
degree of the field:
|
| 556 |
+
|
| 557 |
+
>>> k = QQ.alg_field_from_poly(x**4 - 2)
|
| 558 |
+
>>> G, _ = k.galois_group()
|
| 559 |
+
>>> G.order()
|
| 560 |
+
8
|
| 561 |
+
|
| 562 |
+
See Also
|
| 563 |
+
========
|
| 564 |
+
|
| 565 |
+
sympy.polys.numberfields.galoisgroups.galois_group
|
| 566 |
+
|
| 567 |
+
"""
|
| 568 |
+
return self.ext.minpoly_of_element().galois_group(
|
| 569 |
+
by_name=by_name, max_tries=max_tries, randomize=randomize)
|
| 570 |
+
|
| 571 |
+
|
| 572 |
+
def _make_converter(K):
|
| 573 |
+
"""Construct the converter to convert back to Expr"""
|
| 574 |
+
# Precompute the effect of converting to SymPy and expanding expressions
|
| 575 |
+
# like (sqrt(2) + sqrt(3))**2. Asking Expr to do the expansion on every
|
| 576 |
+
# conversion from K to Expr is slow. Here we compute the expansions for
|
| 577 |
+
# each power of the generator and collect together the resulting algebraic
|
| 578 |
+
# terms and the rational coefficients into a matrix.
|
| 579 |
+
|
| 580 |
+
ext = K.ext.as_expr()
|
| 581 |
+
todom = K.dom.from_sympy
|
| 582 |
+
toexpr = K.dom.to_sympy
|
| 583 |
+
|
| 584 |
+
if not ext.is_Add:
|
| 585 |
+
powers = [ext**n for n in range(K.mod.degree())]
|
| 586 |
+
else:
|
| 587 |
+
# primitive_element generates a QQ-linear combination of lower degree
|
| 588 |
+
# algebraic numbers to generate the higher degree extension e.g.
|
| 589 |
+
# QQ<sqrt(2)+sqrt(3)> That means that we end up having high powers of low
|
| 590 |
+
# degree algebraic numbers that can be reduced. Here we will use the
|
| 591 |
+
# minimal polynomials of the algebraic numbers to reduce those powers
|
| 592 |
+
# before converting to Expr.
|
| 593 |
+
from sympy.polys.numberfields.minpoly import minpoly
|
| 594 |
+
|
| 595 |
+
# Decompose ext as a linear combination of gens and make a symbol for
|
| 596 |
+
# each gen.
|
| 597 |
+
gens, coeffs = zip(*ext.as_coefficients_dict().items())
|
| 598 |
+
syms = symbols(f'a:{len(gens)}', cls=Dummy)
|
| 599 |
+
sym2gen = dict(zip(syms, gens))
|
| 600 |
+
|
| 601 |
+
# Make a polynomial ring that can express ext and minpolys of all gens
|
| 602 |
+
# in terms of syms.
|
| 603 |
+
R = K.dom[syms]
|
| 604 |
+
monoms = [R.ring.monomial_basis(i) for i in range(R.ngens)]
|
| 605 |
+
ext_dict = {m: todom(c) for m, c in zip(monoms, coeffs)}
|
| 606 |
+
ext_poly = R.ring.from_dict(ext_dict)
|
| 607 |
+
minpolys = [R.from_sympy(minpoly(g, s)) for s, g in sym2gen.items()]
|
| 608 |
+
|
| 609 |
+
# Compute all powers of ext_poly reduced modulo minpolys
|
| 610 |
+
powers = [R.one, ext_poly]
|
| 611 |
+
for n in range(2, K.mod.degree()):
|
| 612 |
+
ext_poly_n = (powers[-1] * ext_poly).rem(minpolys)
|
| 613 |
+
powers.append(ext_poly_n)
|
| 614 |
+
|
| 615 |
+
# Convert the powers back to Expr. This will recombine some things like
|
| 616 |
+
# sqrt(2)*sqrt(3) -> sqrt(6).
|
| 617 |
+
powers = [p.as_expr().xreplace(sym2gen) for p in powers]
|
| 618 |
+
|
| 619 |
+
# This also expands some rational powers
|
| 620 |
+
powers = [p.expand() for p in powers]
|
| 621 |
+
|
| 622 |
+
# Collect the rational coefficients and algebraic Expr that can
|
| 623 |
+
# map the ANP coefficients into an expanded SymPy expression
|
| 624 |
+
terms = [dict(t.as_coeff_Mul()[::-1] for t in Add.make_args(p)) for p in powers]
|
| 625 |
+
algebraics = set().union(*terms)
|
| 626 |
+
matrix = [[todom(t.get(a, S.Zero)) for t in terms] for a in algebraics]
|
| 627 |
+
|
| 628 |
+
# Create a function to do the conversion efficiently:
|
| 629 |
+
|
| 630 |
+
def converter(a):
|
| 631 |
+
"""Convert a to Expr using converter"""
|
| 632 |
+
ai = a.to_list()[::-1]
|
| 633 |
+
coeffs_dom = [sum(mij*aj for mij, aj in zip(mi, ai)) for mi in matrix]
|
| 634 |
+
coeffs_sympy = [toexpr(c) for c in coeffs_dom]
|
| 635 |
+
res = Add(*(Mul(c, a) for c, a in zip(coeffs_sympy, algebraics)))
|
| 636 |
+
return res
|
| 637 |
+
|
| 638 |
+
return converter
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/characteristiczero.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`CharacteristicZero` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.polys.domains.domain import Domain
|
| 5 |
+
from sympy.utilities import public
|
| 6 |
+
|
| 7 |
+
@public
|
| 8 |
+
class CharacteristicZero(Domain):
|
| 9 |
+
"""Domain that has infinite number of elements. """
|
| 10 |
+
|
| 11 |
+
has_CharacteristicZero = True
|
| 12 |
+
|
| 13 |
+
def characteristic(self):
|
| 14 |
+
"""Return the characteristic of this domain. """
|
| 15 |
+
return 0
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/complexfield.py
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`ComplexField` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.external.gmpy import SYMPY_INTS
|
| 5 |
+
from sympy.core.numbers import Float, I
|
| 6 |
+
from sympy.polys.domains.characteristiczero import CharacteristicZero
|
| 7 |
+
from sympy.polys.domains.field import Field
|
| 8 |
+
from sympy.polys.domains.gaussiandomains import QQ_I
|
| 9 |
+
from sympy.polys.domains.simpledomain import SimpleDomain
|
| 10 |
+
from sympy.polys.polyerrors import DomainError, CoercionFailed
|
| 11 |
+
from sympy.utilities import public
|
| 12 |
+
|
| 13 |
+
from mpmath import MPContext
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
@public
|
| 17 |
+
class ComplexField(Field, CharacteristicZero, SimpleDomain):
|
| 18 |
+
"""Complex numbers up to the given precision. """
|
| 19 |
+
|
| 20 |
+
rep = 'CC'
|
| 21 |
+
|
| 22 |
+
is_ComplexField = is_CC = True
|
| 23 |
+
|
| 24 |
+
is_Exact = False
|
| 25 |
+
is_Numerical = True
|
| 26 |
+
|
| 27 |
+
has_assoc_Ring = False
|
| 28 |
+
has_assoc_Field = True
|
| 29 |
+
|
| 30 |
+
_default_precision = 53
|
| 31 |
+
|
| 32 |
+
@property
|
| 33 |
+
def has_default_precision(self):
|
| 34 |
+
return self.precision == self._default_precision
|
| 35 |
+
|
| 36 |
+
@property
|
| 37 |
+
def precision(self):
|
| 38 |
+
return self._context.prec
|
| 39 |
+
|
| 40 |
+
@property
|
| 41 |
+
def dps(self):
|
| 42 |
+
return self._context.dps
|
| 43 |
+
|
| 44 |
+
@property
|
| 45 |
+
def tolerance(self):
|
| 46 |
+
return self._tolerance
|
| 47 |
+
|
| 48 |
+
def __init__(self, prec=None, dps=None, tol=None):
|
| 49 |
+
# XXX: The tolerance parameter is ignored but is kept for backward
|
| 50 |
+
# compatibility for now.
|
| 51 |
+
|
| 52 |
+
context = MPContext()
|
| 53 |
+
|
| 54 |
+
if prec is None and dps is None:
|
| 55 |
+
context.prec = self._default_precision
|
| 56 |
+
elif dps is None:
|
| 57 |
+
context.prec = prec
|
| 58 |
+
elif prec is None:
|
| 59 |
+
context.dps = dps
|
| 60 |
+
else:
|
| 61 |
+
raise TypeError("Cannot set both prec and dps")
|
| 62 |
+
|
| 63 |
+
self._context = context
|
| 64 |
+
|
| 65 |
+
self._dtype = context.mpc
|
| 66 |
+
self.zero = self.dtype(0)
|
| 67 |
+
self.one = self.dtype(1)
|
| 68 |
+
|
| 69 |
+
# XXX: Neither of these is actually used anywhere.
|
| 70 |
+
self._max_denom = max(2**context.prec // 200, 99)
|
| 71 |
+
self._tolerance = self.one / self._max_denom
|
| 72 |
+
|
| 73 |
+
@property
|
| 74 |
+
def tp(self):
|
| 75 |
+
# XXX: Domain treats tp as an alias of dtype. Here we need two separate
|
| 76 |
+
# things: dtype is a callable to make/convert instances. We use tp with
|
| 77 |
+
# isinstance to check if an object is an instance of the domain
|
| 78 |
+
# already.
|
| 79 |
+
return self._dtype
|
| 80 |
+
|
| 81 |
+
def dtype(self, x, y=0):
|
| 82 |
+
# XXX: This is needed because mpmath does not recognise fmpz.
|
| 83 |
+
# It might be better to add conversion routines to mpmath and if that
|
| 84 |
+
# happens then this can be removed.
|
| 85 |
+
if isinstance(x, SYMPY_INTS):
|
| 86 |
+
x = int(x)
|
| 87 |
+
if isinstance(y, SYMPY_INTS):
|
| 88 |
+
y = int(y)
|
| 89 |
+
return self._dtype(x, y)
|
| 90 |
+
|
| 91 |
+
def __eq__(self, other):
|
| 92 |
+
return isinstance(other, ComplexField) and self.precision == other.precision
|
| 93 |
+
|
| 94 |
+
def __hash__(self):
|
| 95 |
+
return hash((self.__class__.__name__, self._dtype, self.precision))
|
| 96 |
+
|
| 97 |
+
def to_sympy(self, element):
|
| 98 |
+
"""Convert ``element`` to SymPy number. """
|
| 99 |
+
return Float(element.real, self.dps) + I*Float(element.imag, self.dps)
|
| 100 |
+
|
| 101 |
+
def from_sympy(self, expr):
|
| 102 |
+
"""Convert SymPy's number to ``dtype``. """
|
| 103 |
+
number = expr.evalf(n=self.dps)
|
| 104 |
+
real, imag = number.as_real_imag()
|
| 105 |
+
|
| 106 |
+
if real.is_Number and imag.is_Number:
|
| 107 |
+
return self.dtype(real, imag)
|
| 108 |
+
else:
|
| 109 |
+
raise CoercionFailed("expected complex number, got %s" % expr)
|
| 110 |
+
|
| 111 |
+
def from_ZZ(self, element, base):
|
| 112 |
+
return self.dtype(element)
|
| 113 |
+
|
| 114 |
+
def from_ZZ_gmpy(self, element, base):
|
| 115 |
+
return self.dtype(int(element))
|
| 116 |
+
|
| 117 |
+
def from_ZZ_python(self, element, base):
|
| 118 |
+
return self.dtype(element)
|
| 119 |
+
|
| 120 |
+
def from_QQ(self, element, base):
|
| 121 |
+
return self.dtype(int(element.numerator)) / int(element.denominator)
|
| 122 |
+
|
| 123 |
+
def from_QQ_python(self, element, base):
|
| 124 |
+
return self.dtype(element.numerator) / element.denominator
|
| 125 |
+
|
| 126 |
+
def from_QQ_gmpy(self, element, base):
|
| 127 |
+
return self.dtype(int(element.numerator)) / int(element.denominator)
|
| 128 |
+
|
| 129 |
+
def from_GaussianIntegerRing(self, element, base):
|
| 130 |
+
return self.dtype(int(element.x), int(element.y))
|
| 131 |
+
|
| 132 |
+
def from_GaussianRationalField(self, element, base):
|
| 133 |
+
x = element.x
|
| 134 |
+
y = element.y
|
| 135 |
+
return (self.dtype(int(x.numerator)) / int(x.denominator) +
|
| 136 |
+
self.dtype(0, int(y.numerator)) / int(y.denominator))
|
| 137 |
+
|
| 138 |
+
def from_AlgebraicField(self, element, base):
|
| 139 |
+
return self.from_sympy(base.to_sympy(element).evalf(self.dps))
|
| 140 |
+
|
| 141 |
+
def from_RealField(self, element, base):
|
| 142 |
+
return self.dtype(element)
|
| 143 |
+
|
| 144 |
+
def from_ComplexField(self, element, base):
|
| 145 |
+
return self.dtype(element)
|
| 146 |
+
|
| 147 |
+
def get_ring(self):
|
| 148 |
+
"""Returns a ring associated with ``self``. """
|
| 149 |
+
raise DomainError("there is no ring associated with %s" % self)
|
| 150 |
+
|
| 151 |
+
def get_exact(self):
|
| 152 |
+
"""Returns an exact domain associated with ``self``. """
|
| 153 |
+
return QQ_I
|
| 154 |
+
|
| 155 |
+
def is_negative(self, element):
|
| 156 |
+
"""Returns ``False`` for any ``ComplexElement``. """
|
| 157 |
+
return False
|
| 158 |
+
|
| 159 |
+
def is_positive(self, element):
|
| 160 |
+
"""Returns ``False`` for any ``ComplexElement``. """
|
| 161 |
+
return False
|
| 162 |
+
|
| 163 |
+
def is_nonnegative(self, element):
|
| 164 |
+
"""Returns ``False`` for any ``ComplexElement``. """
|
| 165 |
+
return False
|
| 166 |
+
|
| 167 |
+
def is_nonpositive(self, element):
|
| 168 |
+
"""Returns ``False`` for any ``ComplexElement``. """
|
| 169 |
+
return False
|
| 170 |
+
|
| 171 |
+
def gcd(self, a, b):
|
| 172 |
+
"""Returns GCD of ``a`` and ``b``. """
|
| 173 |
+
return self.one
|
| 174 |
+
|
| 175 |
+
def lcm(self, a, b):
|
| 176 |
+
"""Returns LCM of ``a`` and ``b``. """
|
| 177 |
+
return a*b
|
| 178 |
+
|
| 179 |
+
def almosteq(self, a, b, tolerance=None):
|
| 180 |
+
"""Check if ``a`` and ``b`` are almost equal. """
|
| 181 |
+
return self._context.almosteq(a, b, tolerance)
|
| 182 |
+
|
| 183 |
+
def is_square(self, a):
|
| 184 |
+
"""Returns ``True``. Every complex number has a complex square root."""
|
| 185 |
+
return True
|
| 186 |
+
|
| 187 |
+
def exsqrt(self, a):
|
| 188 |
+
r"""Returns the principal complex square root of ``a``.
|
| 189 |
+
|
| 190 |
+
Explanation
|
| 191 |
+
===========
|
| 192 |
+
The argument of the principal square root is always within
|
| 193 |
+
$(-\frac{\pi}{2}, \frac{\pi}{2}]$. The square root may be
|
| 194 |
+
slightly inaccurate due to floating point rounding error.
|
| 195 |
+
"""
|
| 196 |
+
return a ** 0.5
|
| 197 |
+
|
| 198 |
+
CC = ComplexField()
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/compositedomain.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`CompositeDomain` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.polys.domains.domain import Domain
|
| 5 |
+
from sympy.polys.polyerrors import GeneratorsError
|
| 6 |
+
|
| 7 |
+
from sympy.utilities import public
|
| 8 |
+
|
| 9 |
+
@public
|
| 10 |
+
class CompositeDomain(Domain):
|
| 11 |
+
"""Base class for composite domains, e.g. ZZ[x], ZZ(X). """
|
| 12 |
+
|
| 13 |
+
is_Composite = True
|
| 14 |
+
|
| 15 |
+
gens, ngens, symbols, domain = [None]*4
|
| 16 |
+
|
| 17 |
+
def inject(self, *symbols):
|
| 18 |
+
"""Inject generators into this domain. """
|
| 19 |
+
if not (set(self.symbols) & set(symbols)):
|
| 20 |
+
return self.__class__(self.domain, self.symbols + symbols, self.order)
|
| 21 |
+
else:
|
| 22 |
+
raise GeneratorsError("common generators in %s and %s" % (self.symbols, symbols))
|
| 23 |
+
|
| 24 |
+
def drop(self, *symbols):
|
| 25 |
+
"""Drop generators from this domain. """
|
| 26 |
+
symset = set(symbols)
|
| 27 |
+
newsyms = tuple(s for s in self.symbols if s not in symset)
|
| 28 |
+
domain = self.domain.drop(*symbols)
|
| 29 |
+
if not newsyms:
|
| 30 |
+
return domain
|
| 31 |
+
else:
|
| 32 |
+
return self.__class__(domain, newsyms, self.order)
|
| 33 |
+
|
| 34 |
+
def set_domain(self, domain):
|
| 35 |
+
"""Set the ground domain of this domain. """
|
| 36 |
+
return self.__class__(domain, self.symbols, self.order)
|
| 37 |
+
|
| 38 |
+
@property
|
| 39 |
+
def is_Exact(self):
|
| 40 |
+
"""Returns ``True`` if this domain is exact. """
|
| 41 |
+
return self.domain.is_Exact
|
| 42 |
+
|
| 43 |
+
def get_exact(self):
|
| 44 |
+
"""Returns an exact version of this domain. """
|
| 45 |
+
return self.set_domain(self.domain.get_exact())
|
| 46 |
+
|
| 47 |
+
@property
|
| 48 |
+
def has_CharacteristicZero(self):
|
| 49 |
+
return self.domain.has_CharacteristicZero
|
| 50 |
+
|
| 51 |
+
def characteristic(self):
|
| 52 |
+
return self.domain.characteristic()
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/domain.py
ADDED
|
@@ -0,0 +1,1382 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`Domain` class. """
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
from typing import Any
|
| 5 |
+
|
| 6 |
+
from sympy.core.numbers import AlgebraicNumber
|
| 7 |
+
from sympy.core import Basic, sympify
|
| 8 |
+
from sympy.core.sorting import ordered
|
| 9 |
+
from sympy.external.gmpy import GROUND_TYPES
|
| 10 |
+
from sympy.polys.domains.domainelement import DomainElement
|
| 11 |
+
from sympy.polys.orderings import lex
|
| 12 |
+
from sympy.polys.polyerrors import UnificationFailed, CoercionFailed, DomainError
|
| 13 |
+
from sympy.polys.polyutils import _unify_gens, _not_a_coeff
|
| 14 |
+
from sympy.utilities import public
|
| 15 |
+
from sympy.utilities.iterables import is_sequence
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
@public
|
| 19 |
+
class Domain:
|
| 20 |
+
"""Superclass for all domains in the polys domains system.
|
| 21 |
+
|
| 22 |
+
See :ref:`polys-domainsintro` for an introductory explanation of the
|
| 23 |
+
domains system.
|
| 24 |
+
|
| 25 |
+
The :py:class:`~.Domain` class is an abstract base class for all of the
|
| 26 |
+
concrete domain types. There are many different :py:class:`~.Domain`
|
| 27 |
+
subclasses each of which has an associated ``dtype`` which is a class
|
| 28 |
+
representing the elements of the domain. The coefficients of a
|
| 29 |
+
:py:class:`~.Poly` are elements of a domain which must be a subclass of
|
| 30 |
+
:py:class:`~.Domain`.
|
| 31 |
+
|
| 32 |
+
Examples
|
| 33 |
+
========
|
| 34 |
+
|
| 35 |
+
The most common example domains are the integers :ref:`ZZ` and the
|
| 36 |
+
rationals :ref:`QQ`.
|
| 37 |
+
|
| 38 |
+
>>> from sympy import Poly, symbols, Domain
|
| 39 |
+
>>> x, y = symbols('x, y')
|
| 40 |
+
>>> p = Poly(x**2 + y)
|
| 41 |
+
>>> p
|
| 42 |
+
Poly(x**2 + y, x, y, domain='ZZ')
|
| 43 |
+
>>> p.domain
|
| 44 |
+
ZZ
|
| 45 |
+
>>> isinstance(p.domain, Domain)
|
| 46 |
+
True
|
| 47 |
+
>>> Poly(x**2 + y/2)
|
| 48 |
+
Poly(x**2 + 1/2*y, x, y, domain='QQ')
|
| 49 |
+
|
| 50 |
+
The domains can be used directly in which case the domain object e.g.
|
| 51 |
+
(:ref:`ZZ` or :ref:`QQ`) can be used as a constructor for elements of
|
| 52 |
+
``dtype``.
|
| 53 |
+
|
| 54 |
+
>>> from sympy import ZZ, QQ
|
| 55 |
+
>>> ZZ(2)
|
| 56 |
+
2
|
| 57 |
+
>>> ZZ.dtype # doctest: +SKIP
|
| 58 |
+
<class 'int'>
|
| 59 |
+
>>> type(ZZ(2)) # doctest: +SKIP
|
| 60 |
+
<class 'int'>
|
| 61 |
+
>>> QQ(1, 2)
|
| 62 |
+
1/2
|
| 63 |
+
>>> type(QQ(1, 2)) # doctest: +SKIP
|
| 64 |
+
<class 'sympy.polys.domains.pythonrational.PythonRational'>
|
| 65 |
+
|
| 66 |
+
The corresponding domain elements can be used with the arithmetic
|
| 67 |
+
operations ``+,-,*,**`` and depending on the domain some combination of
|
| 68 |
+
``/,//,%`` might be usable. For example in :ref:`ZZ` both ``//`` (floor
|
| 69 |
+
division) and ``%`` (modulo division) can be used but ``/`` (true
|
| 70 |
+
division) cannot. Since :ref:`QQ` is a :py:class:`~.Field` its elements
|
| 71 |
+
can be used with ``/`` but ``//`` and ``%`` should not be used. Some
|
| 72 |
+
domains have a :py:meth:`~.Domain.gcd` method.
|
| 73 |
+
|
| 74 |
+
>>> ZZ(2) + ZZ(3)
|
| 75 |
+
5
|
| 76 |
+
>>> ZZ(5) // ZZ(2)
|
| 77 |
+
2
|
| 78 |
+
>>> ZZ(5) % ZZ(2)
|
| 79 |
+
1
|
| 80 |
+
>>> QQ(1, 2) / QQ(2, 3)
|
| 81 |
+
3/4
|
| 82 |
+
>>> ZZ.gcd(ZZ(4), ZZ(2))
|
| 83 |
+
2
|
| 84 |
+
>>> QQ.gcd(QQ(2,7), QQ(5,3))
|
| 85 |
+
1/21
|
| 86 |
+
>>> ZZ.is_Field
|
| 87 |
+
False
|
| 88 |
+
>>> QQ.is_Field
|
| 89 |
+
True
|
| 90 |
+
|
| 91 |
+
There are also many other domains including:
|
| 92 |
+
|
| 93 |
+
1. :ref:`GF(p)` for finite fields of prime order.
|
| 94 |
+
2. :ref:`RR` for real (floating point) numbers.
|
| 95 |
+
3. :ref:`CC` for complex (floating point) numbers.
|
| 96 |
+
4. :ref:`QQ(a)` for algebraic number fields.
|
| 97 |
+
5. :ref:`K[x]` for polynomial rings.
|
| 98 |
+
6. :ref:`K(x)` for rational function fields.
|
| 99 |
+
7. :ref:`EX` for arbitrary expressions.
|
| 100 |
+
|
| 101 |
+
Each domain is represented by a domain object and also an implementation
|
| 102 |
+
class (``dtype``) for the elements of the domain. For example the
|
| 103 |
+
:ref:`K[x]` domains are represented by a domain object which is an
|
| 104 |
+
instance of :py:class:`~.PolynomialRing` and the elements are always
|
| 105 |
+
instances of :py:class:`~.PolyElement`. The implementation class
|
| 106 |
+
represents particular types of mathematical expressions in a way that is
|
| 107 |
+
more efficient than a normal SymPy expression which is of type
|
| 108 |
+
:py:class:`~.Expr`. The domain methods :py:meth:`~.Domain.from_sympy` and
|
| 109 |
+
:py:meth:`~.Domain.to_sympy` are used to convert from :py:class:`~.Expr`
|
| 110 |
+
to a domain element and vice versa.
|
| 111 |
+
|
| 112 |
+
>>> from sympy import Symbol, ZZ, Expr
|
| 113 |
+
>>> x = Symbol('x')
|
| 114 |
+
>>> K = ZZ[x] # polynomial ring domain
|
| 115 |
+
>>> K
|
| 116 |
+
ZZ[x]
|
| 117 |
+
>>> type(K) # class of the domain
|
| 118 |
+
<class 'sympy.polys.domains.polynomialring.PolynomialRing'>
|
| 119 |
+
>>> K.dtype # doctest: +SKIP
|
| 120 |
+
<class 'sympy.polys.rings.PolyElement'>
|
| 121 |
+
>>> p_expr = x**2 + 1 # Expr
|
| 122 |
+
>>> p_expr
|
| 123 |
+
x**2 + 1
|
| 124 |
+
>>> type(p_expr)
|
| 125 |
+
<class 'sympy.core.add.Add'>
|
| 126 |
+
>>> isinstance(p_expr, Expr)
|
| 127 |
+
True
|
| 128 |
+
>>> p_domain = K.from_sympy(p_expr)
|
| 129 |
+
>>> p_domain # domain element
|
| 130 |
+
x**2 + 1
|
| 131 |
+
>>> type(p_domain)
|
| 132 |
+
<class 'sympy.polys.rings.PolyElement'>
|
| 133 |
+
>>> K.to_sympy(p_domain) == p_expr
|
| 134 |
+
True
|
| 135 |
+
|
| 136 |
+
The :py:meth:`~.Domain.convert_from` method is used to convert domain
|
| 137 |
+
elements from one domain to another.
|
| 138 |
+
|
| 139 |
+
>>> from sympy import ZZ, QQ
|
| 140 |
+
>>> ez = ZZ(2)
|
| 141 |
+
>>> eq = QQ.convert_from(ez, ZZ)
|
| 142 |
+
>>> type(ez) # doctest: +SKIP
|
| 143 |
+
<class 'int'>
|
| 144 |
+
>>> type(eq) # doctest: +SKIP
|
| 145 |
+
<class 'sympy.polys.domains.pythonrational.PythonRational'>
|
| 146 |
+
|
| 147 |
+
Elements from different domains should not be mixed in arithmetic or other
|
| 148 |
+
operations: they should be converted to a common domain first. The domain
|
| 149 |
+
method :py:meth:`~.Domain.unify` is used to find a domain that can
|
| 150 |
+
represent all the elements of two given domains.
|
| 151 |
+
|
| 152 |
+
>>> from sympy import ZZ, QQ, symbols
|
| 153 |
+
>>> x, y = symbols('x, y')
|
| 154 |
+
>>> ZZ.unify(QQ)
|
| 155 |
+
QQ
|
| 156 |
+
>>> ZZ[x].unify(QQ)
|
| 157 |
+
QQ[x]
|
| 158 |
+
>>> ZZ[x].unify(QQ[y])
|
| 159 |
+
QQ[x,y]
|
| 160 |
+
|
| 161 |
+
If a domain is a :py:class:`~.Ring` then is might have an associated
|
| 162 |
+
:py:class:`~.Field` and vice versa. The :py:meth:`~.Domain.get_field` and
|
| 163 |
+
:py:meth:`~.Domain.get_ring` methods will find or create the associated
|
| 164 |
+
domain.
|
| 165 |
+
|
| 166 |
+
>>> from sympy import ZZ, QQ, Symbol
|
| 167 |
+
>>> x = Symbol('x')
|
| 168 |
+
>>> ZZ.has_assoc_Field
|
| 169 |
+
True
|
| 170 |
+
>>> ZZ.get_field()
|
| 171 |
+
QQ
|
| 172 |
+
>>> QQ.has_assoc_Ring
|
| 173 |
+
True
|
| 174 |
+
>>> QQ.get_ring()
|
| 175 |
+
ZZ
|
| 176 |
+
>>> K = QQ[x]
|
| 177 |
+
>>> K
|
| 178 |
+
QQ[x]
|
| 179 |
+
>>> K.get_field()
|
| 180 |
+
QQ(x)
|
| 181 |
+
|
| 182 |
+
See also
|
| 183 |
+
========
|
| 184 |
+
|
| 185 |
+
DomainElement: abstract base class for domain elements
|
| 186 |
+
construct_domain: construct a minimal domain for some expressions
|
| 187 |
+
|
| 188 |
+
"""
|
| 189 |
+
|
| 190 |
+
dtype: type | None = None
|
| 191 |
+
"""The type (class) of the elements of this :py:class:`~.Domain`:
|
| 192 |
+
|
| 193 |
+
>>> from sympy import ZZ, QQ, Symbol
|
| 194 |
+
>>> ZZ.dtype
|
| 195 |
+
<class 'int'>
|
| 196 |
+
>>> z = ZZ(2)
|
| 197 |
+
>>> z
|
| 198 |
+
2
|
| 199 |
+
>>> type(z)
|
| 200 |
+
<class 'int'>
|
| 201 |
+
>>> type(z) == ZZ.dtype
|
| 202 |
+
True
|
| 203 |
+
|
| 204 |
+
Every domain has an associated **dtype** ("datatype") which is the
|
| 205 |
+
class of the associated domain elements.
|
| 206 |
+
|
| 207 |
+
See also
|
| 208 |
+
========
|
| 209 |
+
|
| 210 |
+
of_type
|
| 211 |
+
"""
|
| 212 |
+
|
| 213 |
+
zero: Any = None
|
| 214 |
+
"""The zero element of the :py:class:`~.Domain`:
|
| 215 |
+
|
| 216 |
+
>>> from sympy import QQ
|
| 217 |
+
>>> QQ.zero
|
| 218 |
+
0
|
| 219 |
+
>>> QQ.of_type(QQ.zero)
|
| 220 |
+
True
|
| 221 |
+
|
| 222 |
+
See also
|
| 223 |
+
========
|
| 224 |
+
|
| 225 |
+
of_type
|
| 226 |
+
one
|
| 227 |
+
"""
|
| 228 |
+
|
| 229 |
+
one: Any = None
|
| 230 |
+
"""The one element of the :py:class:`~.Domain`:
|
| 231 |
+
|
| 232 |
+
>>> from sympy import QQ
|
| 233 |
+
>>> QQ.one
|
| 234 |
+
1
|
| 235 |
+
>>> QQ.of_type(QQ.one)
|
| 236 |
+
True
|
| 237 |
+
|
| 238 |
+
See also
|
| 239 |
+
========
|
| 240 |
+
|
| 241 |
+
of_type
|
| 242 |
+
zero
|
| 243 |
+
"""
|
| 244 |
+
|
| 245 |
+
is_Ring = False
|
| 246 |
+
"""Boolean flag indicating if the domain is a :py:class:`~.Ring`.
|
| 247 |
+
|
| 248 |
+
>>> from sympy import ZZ
|
| 249 |
+
>>> ZZ.is_Ring
|
| 250 |
+
True
|
| 251 |
+
|
| 252 |
+
Basically every :py:class:`~.Domain` represents a ring so this flag is
|
| 253 |
+
not that useful.
|
| 254 |
+
|
| 255 |
+
See also
|
| 256 |
+
========
|
| 257 |
+
|
| 258 |
+
is_PID
|
| 259 |
+
is_Field
|
| 260 |
+
get_ring
|
| 261 |
+
has_assoc_Ring
|
| 262 |
+
"""
|
| 263 |
+
|
| 264 |
+
is_Field = False
|
| 265 |
+
"""Boolean flag indicating if the domain is a :py:class:`~.Field`.
|
| 266 |
+
|
| 267 |
+
>>> from sympy import ZZ, QQ
|
| 268 |
+
>>> ZZ.is_Field
|
| 269 |
+
False
|
| 270 |
+
>>> QQ.is_Field
|
| 271 |
+
True
|
| 272 |
+
|
| 273 |
+
See also
|
| 274 |
+
========
|
| 275 |
+
|
| 276 |
+
is_PID
|
| 277 |
+
is_Ring
|
| 278 |
+
get_field
|
| 279 |
+
has_assoc_Field
|
| 280 |
+
"""
|
| 281 |
+
|
| 282 |
+
has_assoc_Ring = False
|
| 283 |
+
"""Boolean flag indicating if the domain has an associated
|
| 284 |
+
:py:class:`~.Ring`.
|
| 285 |
+
|
| 286 |
+
>>> from sympy import QQ
|
| 287 |
+
>>> QQ.has_assoc_Ring
|
| 288 |
+
True
|
| 289 |
+
>>> QQ.get_ring()
|
| 290 |
+
ZZ
|
| 291 |
+
|
| 292 |
+
See also
|
| 293 |
+
========
|
| 294 |
+
|
| 295 |
+
is_Field
|
| 296 |
+
get_ring
|
| 297 |
+
"""
|
| 298 |
+
|
| 299 |
+
has_assoc_Field = False
|
| 300 |
+
"""Boolean flag indicating if the domain has an associated
|
| 301 |
+
:py:class:`~.Field`.
|
| 302 |
+
|
| 303 |
+
>>> from sympy import ZZ
|
| 304 |
+
>>> ZZ.has_assoc_Field
|
| 305 |
+
True
|
| 306 |
+
>>> ZZ.get_field()
|
| 307 |
+
QQ
|
| 308 |
+
|
| 309 |
+
See also
|
| 310 |
+
========
|
| 311 |
+
|
| 312 |
+
is_Field
|
| 313 |
+
get_field
|
| 314 |
+
"""
|
| 315 |
+
|
| 316 |
+
is_FiniteField = is_FF = False
|
| 317 |
+
is_IntegerRing = is_ZZ = False
|
| 318 |
+
is_RationalField = is_QQ = False
|
| 319 |
+
is_GaussianRing = is_ZZ_I = False
|
| 320 |
+
is_GaussianField = is_QQ_I = False
|
| 321 |
+
is_RealField = is_RR = False
|
| 322 |
+
is_ComplexField = is_CC = False
|
| 323 |
+
is_AlgebraicField = is_Algebraic = False
|
| 324 |
+
is_PolynomialRing = is_Poly = False
|
| 325 |
+
is_FractionField = is_Frac = False
|
| 326 |
+
is_SymbolicDomain = is_EX = False
|
| 327 |
+
is_SymbolicRawDomain = is_EXRAW = False
|
| 328 |
+
is_FiniteExtension = False
|
| 329 |
+
|
| 330 |
+
is_Exact = True
|
| 331 |
+
is_Numerical = False
|
| 332 |
+
|
| 333 |
+
is_Simple = False
|
| 334 |
+
is_Composite = False
|
| 335 |
+
|
| 336 |
+
is_PID = False
|
| 337 |
+
"""Boolean flag indicating if the domain is a `principal ideal domain`_.
|
| 338 |
+
|
| 339 |
+
>>> from sympy import ZZ
|
| 340 |
+
>>> ZZ.has_assoc_Field
|
| 341 |
+
True
|
| 342 |
+
>>> ZZ.get_field()
|
| 343 |
+
QQ
|
| 344 |
+
|
| 345 |
+
.. _principal ideal domain: https://en.wikipedia.org/wiki/Principal_ideal_domain
|
| 346 |
+
|
| 347 |
+
See also
|
| 348 |
+
========
|
| 349 |
+
|
| 350 |
+
is_Field
|
| 351 |
+
get_field
|
| 352 |
+
"""
|
| 353 |
+
|
| 354 |
+
has_CharacteristicZero = False
|
| 355 |
+
|
| 356 |
+
rep: str | None = None
|
| 357 |
+
alias: str | None = None
|
| 358 |
+
|
| 359 |
+
def __init__(self):
|
| 360 |
+
raise NotImplementedError
|
| 361 |
+
|
| 362 |
+
def __str__(self):
|
| 363 |
+
return self.rep
|
| 364 |
+
|
| 365 |
+
def __repr__(self):
|
| 366 |
+
return str(self)
|
| 367 |
+
|
| 368 |
+
def __hash__(self):
|
| 369 |
+
return hash((self.__class__.__name__, self.dtype))
|
| 370 |
+
|
| 371 |
+
def new(self, *args):
|
| 372 |
+
return self.dtype(*args)
|
| 373 |
+
|
| 374 |
+
@property
|
| 375 |
+
def tp(self):
|
| 376 |
+
"""Alias for :py:attr:`~.Domain.dtype`"""
|
| 377 |
+
return self.dtype
|
| 378 |
+
|
| 379 |
+
def __call__(self, *args):
|
| 380 |
+
"""Construct an element of ``self`` domain from ``args``. """
|
| 381 |
+
return self.new(*args)
|
| 382 |
+
|
| 383 |
+
def normal(self, *args):
|
| 384 |
+
return self.dtype(*args)
|
| 385 |
+
|
| 386 |
+
def convert_from(self, element, base):
|
| 387 |
+
"""Convert ``element`` to ``self.dtype`` given the base domain. """
|
| 388 |
+
if base.alias is not None:
|
| 389 |
+
method = "from_" + base.alias
|
| 390 |
+
else:
|
| 391 |
+
method = "from_" + base.__class__.__name__
|
| 392 |
+
|
| 393 |
+
_convert = getattr(self, method)
|
| 394 |
+
|
| 395 |
+
if _convert is not None:
|
| 396 |
+
result = _convert(element, base)
|
| 397 |
+
|
| 398 |
+
if result is not None:
|
| 399 |
+
return result
|
| 400 |
+
|
| 401 |
+
raise CoercionFailed("Cannot convert %s of type %s from %s to %s" % (element, type(element), base, self))
|
| 402 |
+
|
| 403 |
+
def convert(self, element, base=None):
|
| 404 |
+
"""Convert ``element`` to ``self.dtype``. """
|
| 405 |
+
|
| 406 |
+
if base is not None:
|
| 407 |
+
if _not_a_coeff(element):
|
| 408 |
+
raise CoercionFailed('%s is not in any domain' % element)
|
| 409 |
+
return self.convert_from(element, base)
|
| 410 |
+
|
| 411 |
+
if self.of_type(element):
|
| 412 |
+
return element
|
| 413 |
+
|
| 414 |
+
if _not_a_coeff(element):
|
| 415 |
+
raise CoercionFailed('%s is not in any domain' % element)
|
| 416 |
+
|
| 417 |
+
from sympy.polys.domains import ZZ, QQ, RealField, ComplexField
|
| 418 |
+
|
| 419 |
+
if ZZ.of_type(element):
|
| 420 |
+
return self.convert_from(element, ZZ)
|
| 421 |
+
|
| 422 |
+
if isinstance(element, int):
|
| 423 |
+
return self.convert_from(ZZ(element), ZZ)
|
| 424 |
+
|
| 425 |
+
if GROUND_TYPES != 'python':
|
| 426 |
+
if isinstance(element, ZZ.tp):
|
| 427 |
+
return self.convert_from(element, ZZ)
|
| 428 |
+
if isinstance(element, QQ.tp):
|
| 429 |
+
return self.convert_from(element, QQ)
|
| 430 |
+
|
| 431 |
+
if isinstance(element, float):
|
| 432 |
+
parent = RealField()
|
| 433 |
+
return self.convert_from(parent(element), parent)
|
| 434 |
+
|
| 435 |
+
if isinstance(element, complex):
|
| 436 |
+
parent = ComplexField()
|
| 437 |
+
return self.convert_from(parent(element), parent)
|
| 438 |
+
|
| 439 |
+
if type(element).__name__ == 'mpf':
|
| 440 |
+
parent = RealField()
|
| 441 |
+
return self.convert_from(parent(element), parent)
|
| 442 |
+
|
| 443 |
+
if type(element).__name__ == 'mpc':
|
| 444 |
+
parent = ComplexField()
|
| 445 |
+
return self.convert_from(parent(element), parent)
|
| 446 |
+
|
| 447 |
+
if isinstance(element, DomainElement):
|
| 448 |
+
return self.convert_from(element, element.parent())
|
| 449 |
+
|
| 450 |
+
# TODO: implement this in from_ methods
|
| 451 |
+
if self.is_Numerical and getattr(element, 'is_ground', False):
|
| 452 |
+
return self.convert(element.LC())
|
| 453 |
+
|
| 454 |
+
if isinstance(element, Basic):
|
| 455 |
+
try:
|
| 456 |
+
return self.from_sympy(element)
|
| 457 |
+
except (TypeError, ValueError):
|
| 458 |
+
pass
|
| 459 |
+
else: # TODO: remove this branch
|
| 460 |
+
if not is_sequence(element):
|
| 461 |
+
try:
|
| 462 |
+
element = sympify(element, strict=True)
|
| 463 |
+
if isinstance(element, Basic):
|
| 464 |
+
return self.from_sympy(element)
|
| 465 |
+
except (TypeError, ValueError):
|
| 466 |
+
pass
|
| 467 |
+
|
| 468 |
+
raise CoercionFailed("Cannot convert %s of type %s to %s" % (element, type(element), self))
|
| 469 |
+
|
| 470 |
+
def of_type(self, element):
|
| 471 |
+
"""Check if ``a`` is of type ``dtype``. """
|
| 472 |
+
return isinstance(element, self.tp)
|
| 473 |
+
|
| 474 |
+
def __contains__(self, a):
|
| 475 |
+
"""Check if ``a`` belongs to this domain. """
|
| 476 |
+
try:
|
| 477 |
+
if _not_a_coeff(a):
|
| 478 |
+
raise CoercionFailed
|
| 479 |
+
self.convert(a) # this might raise, too
|
| 480 |
+
except CoercionFailed:
|
| 481 |
+
return False
|
| 482 |
+
|
| 483 |
+
return True
|
| 484 |
+
|
| 485 |
+
def to_sympy(self, a):
|
| 486 |
+
"""Convert domain element *a* to a SymPy expression (Expr).
|
| 487 |
+
|
| 488 |
+
Explanation
|
| 489 |
+
===========
|
| 490 |
+
|
| 491 |
+
Convert a :py:class:`~.Domain` element *a* to :py:class:`~.Expr`. Most
|
| 492 |
+
public SymPy functions work with objects of type :py:class:`~.Expr`.
|
| 493 |
+
The elements of a :py:class:`~.Domain` have a different internal
|
| 494 |
+
representation. It is not possible to mix domain elements with
|
| 495 |
+
:py:class:`~.Expr` so each domain has :py:meth:`~.Domain.to_sympy` and
|
| 496 |
+
:py:meth:`~.Domain.from_sympy` methods to convert its domain elements
|
| 497 |
+
to and from :py:class:`~.Expr`.
|
| 498 |
+
|
| 499 |
+
Parameters
|
| 500 |
+
==========
|
| 501 |
+
|
| 502 |
+
a: domain element
|
| 503 |
+
An element of this :py:class:`~.Domain`.
|
| 504 |
+
|
| 505 |
+
Returns
|
| 506 |
+
=======
|
| 507 |
+
|
| 508 |
+
expr: Expr
|
| 509 |
+
A normal SymPy expression of type :py:class:`~.Expr`.
|
| 510 |
+
|
| 511 |
+
Examples
|
| 512 |
+
========
|
| 513 |
+
|
| 514 |
+
Construct an element of the :ref:`QQ` domain and then convert it to
|
| 515 |
+
:py:class:`~.Expr`.
|
| 516 |
+
|
| 517 |
+
>>> from sympy import QQ, Expr
|
| 518 |
+
>>> q_domain = QQ(2)
|
| 519 |
+
>>> q_domain
|
| 520 |
+
2
|
| 521 |
+
>>> q_expr = QQ.to_sympy(q_domain)
|
| 522 |
+
>>> q_expr
|
| 523 |
+
2
|
| 524 |
+
|
| 525 |
+
Although the printed forms look similar these objects are not of the
|
| 526 |
+
same type.
|
| 527 |
+
|
| 528 |
+
>>> isinstance(q_domain, Expr)
|
| 529 |
+
False
|
| 530 |
+
>>> isinstance(q_expr, Expr)
|
| 531 |
+
True
|
| 532 |
+
|
| 533 |
+
Construct an element of :ref:`K[x]` and convert to
|
| 534 |
+
:py:class:`~.Expr`.
|
| 535 |
+
|
| 536 |
+
>>> from sympy import Symbol
|
| 537 |
+
>>> x = Symbol('x')
|
| 538 |
+
>>> K = QQ[x]
|
| 539 |
+
>>> x_domain = K.gens[0] # generator x as a domain element
|
| 540 |
+
>>> p_domain = x_domain**2/3 + 1
|
| 541 |
+
>>> p_domain
|
| 542 |
+
1/3*x**2 + 1
|
| 543 |
+
>>> p_expr = K.to_sympy(p_domain)
|
| 544 |
+
>>> p_expr
|
| 545 |
+
x**2/3 + 1
|
| 546 |
+
|
| 547 |
+
The :py:meth:`~.Domain.from_sympy` method is used for the opposite
|
| 548 |
+
conversion from a normal SymPy expression to a domain element.
|
| 549 |
+
|
| 550 |
+
>>> p_domain == p_expr
|
| 551 |
+
False
|
| 552 |
+
>>> K.from_sympy(p_expr) == p_domain
|
| 553 |
+
True
|
| 554 |
+
>>> K.to_sympy(p_domain) == p_expr
|
| 555 |
+
True
|
| 556 |
+
>>> K.from_sympy(K.to_sympy(p_domain)) == p_domain
|
| 557 |
+
True
|
| 558 |
+
>>> K.to_sympy(K.from_sympy(p_expr)) == p_expr
|
| 559 |
+
True
|
| 560 |
+
|
| 561 |
+
The :py:meth:`~.Domain.from_sympy` method makes it easier to construct
|
| 562 |
+
domain elements interactively.
|
| 563 |
+
|
| 564 |
+
>>> from sympy import Symbol
|
| 565 |
+
>>> x = Symbol('x')
|
| 566 |
+
>>> K = QQ[x]
|
| 567 |
+
>>> K.from_sympy(x**2/3 + 1)
|
| 568 |
+
1/3*x**2 + 1
|
| 569 |
+
|
| 570 |
+
See also
|
| 571 |
+
========
|
| 572 |
+
|
| 573 |
+
from_sympy
|
| 574 |
+
convert_from
|
| 575 |
+
"""
|
| 576 |
+
raise NotImplementedError
|
| 577 |
+
|
| 578 |
+
def from_sympy(self, a):
|
| 579 |
+
"""Convert a SymPy expression to an element of this domain.
|
| 580 |
+
|
| 581 |
+
Explanation
|
| 582 |
+
===========
|
| 583 |
+
|
| 584 |
+
See :py:meth:`~.Domain.to_sympy` for explanation and examples.
|
| 585 |
+
|
| 586 |
+
Parameters
|
| 587 |
+
==========
|
| 588 |
+
|
| 589 |
+
expr: Expr
|
| 590 |
+
A normal SymPy expression of type :py:class:`~.Expr`.
|
| 591 |
+
|
| 592 |
+
Returns
|
| 593 |
+
=======
|
| 594 |
+
|
| 595 |
+
a: domain element
|
| 596 |
+
An element of this :py:class:`~.Domain`.
|
| 597 |
+
|
| 598 |
+
See also
|
| 599 |
+
========
|
| 600 |
+
|
| 601 |
+
to_sympy
|
| 602 |
+
convert_from
|
| 603 |
+
"""
|
| 604 |
+
raise NotImplementedError
|
| 605 |
+
|
| 606 |
+
def sum(self, args):
|
| 607 |
+
return sum(args, start=self.zero)
|
| 608 |
+
|
| 609 |
+
def from_FF(K1, a, K0):
|
| 610 |
+
"""Convert ``ModularInteger(int)`` to ``dtype``. """
|
| 611 |
+
return None
|
| 612 |
+
|
| 613 |
+
def from_FF_python(K1, a, K0):
|
| 614 |
+
"""Convert ``ModularInteger(int)`` to ``dtype``. """
|
| 615 |
+
return None
|
| 616 |
+
|
| 617 |
+
def from_ZZ_python(K1, a, K0):
|
| 618 |
+
"""Convert a Python ``int`` object to ``dtype``. """
|
| 619 |
+
return None
|
| 620 |
+
|
| 621 |
+
def from_QQ_python(K1, a, K0):
|
| 622 |
+
"""Convert a Python ``Fraction`` object to ``dtype``. """
|
| 623 |
+
return None
|
| 624 |
+
|
| 625 |
+
def from_FF_gmpy(K1, a, K0):
|
| 626 |
+
"""Convert ``ModularInteger(mpz)`` to ``dtype``. """
|
| 627 |
+
return None
|
| 628 |
+
|
| 629 |
+
def from_ZZ_gmpy(K1, a, K0):
|
| 630 |
+
"""Convert a GMPY ``mpz`` object to ``dtype``. """
|
| 631 |
+
return None
|
| 632 |
+
|
| 633 |
+
def from_QQ_gmpy(K1, a, K0):
|
| 634 |
+
"""Convert a GMPY ``mpq`` object to ``dtype``. """
|
| 635 |
+
return None
|
| 636 |
+
|
| 637 |
+
def from_RealField(K1, a, K0):
|
| 638 |
+
"""Convert a real element object to ``dtype``. """
|
| 639 |
+
return None
|
| 640 |
+
|
| 641 |
+
def from_ComplexField(K1, a, K0):
|
| 642 |
+
"""Convert a complex element to ``dtype``. """
|
| 643 |
+
return None
|
| 644 |
+
|
| 645 |
+
def from_AlgebraicField(K1, a, K0):
|
| 646 |
+
"""Convert an algebraic number to ``dtype``. """
|
| 647 |
+
return None
|
| 648 |
+
|
| 649 |
+
def from_PolynomialRing(K1, a, K0):
|
| 650 |
+
"""Convert a polynomial to ``dtype``. """
|
| 651 |
+
if a.is_ground:
|
| 652 |
+
return K1.convert(a.LC, K0.dom)
|
| 653 |
+
|
| 654 |
+
def from_FractionField(K1, a, K0):
|
| 655 |
+
"""Convert a rational function to ``dtype``. """
|
| 656 |
+
return None
|
| 657 |
+
|
| 658 |
+
def from_MonogenicFiniteExtension(K1, a, K0):
|
| 659 |
+
"""Convert an ``ExtensionElement`` to ``dtype``. """
|
| 660 |
+
return K1.convert_from(a.rep, K0.ring)
|
| 661 |
+
|
| 662 |
+
def from_ExpressionDomain(K1, a, K0):
|
| 663 |
+
"""Convert a ``EX`` object to ``dtype``. """
|
| 664 |
+
return K1.from_sympy(a.ex)
|
| 665 |
+
|
| 666 |
+
def from_ExpressionRawDomain(K1, a, K0):
|
| 667 |
+
"""Convert a ``EX`` object to ``dtype``. """
|
| 668 |
+
return K1.from_sympy(a)
|
| 669 |
+
|
| 670 |
+
def from_GlobalPolynomialRing(K1, a, K0):
|
| 671 |
+
"""Convert a polynomial to ``dtype``. """
|
| 672 |
+
if a.degree() <= 0:
|
| 673 |
+
return K1.convert(a.LC(), K0.dom)
|
| 674 |
+
|
| 675 |
+
def from_GeneralizedPolynomialRing(K1, a, K0):
|
| 676 |
+
return K1.from_FractionField(a, K0)
|
| 677 |
+
|
| 678 |
+
def unify_with_symbols(K0, K1, symbols):
|
| 679 |
+
if (K0.is_Composite and (set(K0.symbols) & set(symbols))) or (K1.is_Composite and (set(K1.symbols) & set(symbols))):
|
| 680 |
+
raise UnificationFailed("Cannot unify %s with %s, given %s generators" % (K0, K1, tuple(symbols)))
|
| 681 |
+
|
| 682 |
+
return K0.unify(K1)
|
| 683 |
+
|
| 684 |
+
def unify_composite(K0, K1):
|
| 685 |
+
"""Unify two domains where at least one is composite."""
|
| 686 |
+
K0_ground = K0.dom if K0.is_Composite else K0
|
| 687 |
+
K1_ground = K1.dom if K1.is_Composite else K1
|
| 688 |
+
|
| 689 |
+
K0_symbols = K0.symbols if K0.is_Composite else ()
|
| 690 |
+
K1_symbols = K1.symbols if K1.is_Composite else ()
|
| 691 |
+
|
| 692 |
+
domain = K0_ground.unify(K1_ground)
|
| 693 |
+
symbols = _unify_gens(K0_symbols, K1_symbols)
|
| 694 |
+
order = K0.order if K0.is_Composite else K1.order
|
| 695 |
+
|
| 696 |
+
# E.g. ZZ[x].unify(QQ.frac_field(x)) -> ZZ.frac_field(x)
|
| 697 |
+
if ((K0.is_FractionField and K1.is_PolynomialRing or
|
| 698 |
+
K1.is_FractionField and K0.is_PolynomialRing) and
|
| 699 |
+
(not K0_ground.is_Field or not K1_ground.is_Field) and domain.is_Field
|
| 700 |
+
and domain.has_assoc_Ring):
|
| 701 |
+
domain = domain.get_ring()
|
| 702 |
+
|
| 703 |
+
if K0.is_Composite and (not K1.is_Composite or K0.is_FractionField or K1.is_PolynomialRing):
|
| 704 |
+
cls = K0.__class__
|
| 705 |
+
else:
|
| 706 |
+
cls = K1.__class__
|
| 707 |
+
|
| 708 |
+
# Here cls might be PolynomialRing, FractionField, GlobalPolynomialRing
|
| 709 |
+
# (dense/old Polynomialring) or dense/old FractionField.
|
| 710 |
+
|
| 711 |
+
from sympy.polys.domains.old_polynomialring import GlobalPolynomialRing
|
| 712 |
+
if cls == GlobalPolynomialRing:
|
| 713 |
+
return cls(domain, symbols)
|
| 714 |
+
|
| 715 |
+
return cls(domain, symbols, order)
|
| 716 |
+
|
| 717 |
+
def unify(K0, K1, symbols=None):
|
| 718 |
+
"""
|
| 719 |
+
Construct a minimal domain that contains elements of ``K0`` and ``K1``.
|
| 720 |
+
|
| 721 |
+
Known domains (from smallest to largest):
|
| 722 |
+
|
| 723 |
+
- ``GF(p)``
|
| 724 |
+
- ``ZZ``
|
| 725 |
+
- ``QQ``
|
| 726 |
+
- ``RR(prec, tol)``
|
| 727 |
+
- ``CC(prec, tol)``
|
| 728 |
+
- ``ALG(a, b, c)``
|
| 729 |
+
- ``K[x, y, z]``
|
| 730 |
+
- ``K(x, y, z)``
|
| 731 |
+
- ``EX``
|
| 732 |
+
|
| 733 |
+
"""
|
| 734 |
+
if symbols is not None:
|
| 735 |
+
return K0.unify_with_symbols(K1, symbols)
|
| 736 |
+
|
| 737 |
+
if K0 == K1:
|
| 738 |
+
return K0
|
| 739 |
+
|
| 740 |
+
if not (K0.has_CharacteristicZero and K1.has_CharacteristicZero):
|
| 741 |
+
# Reject unification of domains with different characteristics.
|
| 742 |
+
if K0.characteristic() != K1.characteristic():
|
| 743 |
+
raise UnificationFailed("Cannot unify %s with %s" % (K0, K1))
|
| 744 |
+
|
| 745 |
+
# We do not get here if K0 == K1. The two domains have the same
|
| 746 |
+
# characteristic but are unequal so at least one is composite and
|
| 747 |
+
# we are unifying something like GF(3).unify(GF(3)[x]).
|
| 748 |
+
return K0.unify_composite(K1)
|
| 749 |
+
|
| 750 |
+
# From here we know both domains have characteristic zero and it can be
|
| 751 |
+
# acceptable to fall back on EX.
|
| 752 |
+
|
| 753 |
+
if K0.is_EXRAW:
|
| 754 |
+
return K0
|
| 755 |
+
if K1.is_EXRAW:
|
| 756 |
+
return K1
|
| 757 |
+
|
| 758 |
+
if K0.is_EX:
|
| 759 |
+
return K0
|
| 760 |
+
if K1.is_EX:
|
| 761 |
+
return K1
|
| 762 |
+
|
| 763 |
+
if K0.is_FiniteExtension or K1.is_FiniteExtension:
|
| 764 |
+
if K1.is_FiniteExtension:
|
| 765 |
+
K0, K1 = K1, K0
|
| 766 |
+
if K1.is_FiniteExtension:
|
| 767 |
+
# Unifying two extensions.
|
| 768 |
+
# Try to ensure that K0.unify(K1) == K1.unify(K0)
|
| 769 |
+
if list(ordered([K0.modulus, K1.modulus]))[1] == K0.modulus:
|
| 770 |
+
K0, K1 = K1, K0
|
| 771 |
+
return K1.set_domain(K0)
|
| 772 |
+
else:
|
| 773 |
+
# Drop the generator from other and unify with the base domain
|
| 774 |
+
K1 = K1.drop(K0.symbol)
|
| 775 |
+
K1 = K0.domain.unify(K1)
|
| 776 |
+
return K0.set_domain(K1)
|
| 777 |
+
|
| 778 |
+
if K0.is_Composite or K1.is_Composite:
|
| 779 |
+
return K0.unify_composite(K1)
|
| 780 |
+
|
| 781 |
+
if K1.is_ComplexField:
|
| 782 |
+
K0, K1 = K1, K0
|
| 783 |
+
if K0.is_ComplexField:
|
| 784 |
+
if K1.is_ComplexField or K1.is_RealField:
|
| 785 |
+
if K0.precision >= K1.precision:
|
| 786 |
+
return K0
|
| 787 |
+
else:
|
| 788 |
+
from sympy.polys.domains.complexfield import ComplexField
|
| 789 |
+
return ComplexField(prec=K1.precision)
|
| 790 |
+
else:
|
| 791 |
+
return K0
|
| 792 |
+
|
| 793 |
+
if K1.is_RealField:
|
| 794 |
+
K0, K1 = K1, K0
|
| 795 |
+
if K0.is_RealField:
|
| 796 |
+
if K1.is_RealField:
|
| 797 |
+
if K0.precision >= K1.precision:
|
| 798 |
+
return K0
|
| 799 |
+
else:
|
| 800 |
+
return K1
|
| 801 |
+
elif K1.is_GaussianRing or K1.is_GaussianField:
|
| 802 |
+
from sympy.polys.domains.complexfield import ComplexField
|
| 803 |
+
return ComplexField(prec=K0.precision)
|
| 804 |
+
else:
|
| 805 |
+
return K0
|
| 806 |
+
|
| 807 |
+
if K1.is_AlgebraicField:
|
| 808 |
+
K0, K1 = K1, K0
|
| 809 |
+
if K0.is_AlgebraicField:
|
| 810 |
+
if K1.is_GaussianRing:
|
| 811 |
+
K1 = K1.get_field()
|
| 812 |
+
if K1.is_GaussianField:
|
| 813 |
+
K1 = K1.as_AlgebraicField()
|
| 814 |
+
if K1.is_AlgebraicField:
|
| 815 |
+
return K0.__class__(K0.dom.unify(K1.dom), *_unify_gens(K0.orig_ext, K1.orig_ext))
|
| 816 |
+
else:
|
| 817 |
+
return K0
|
| 818 |
+
|
| 819 |
+
if K0.is_GaussianField:
|
| 820 |
+
return K0
|
| 821 |
+
if K1.is_GaussianField:
|
| 822 |
+
return K1
|
| 823 |
+
|
| 824 |
+
if K0.is_GaussianRing:
|
| 825 |
+
if K1.is_RationalField:
|
| 826 |
+
K0 = K0.get_field()
|
| 827 |
+
return K0
|
| 828 |
+
if K1.is_GaussianRing:
|
| 829 |
+
if K0.is_RationalField:
|
| 830 |
+
K1 = K1.get_field()
|
| 831 |
+
return K1
|
| 832 |
+
|
| 833 |
+
if K0.is_RationalField:
|
| 834 |
+
return K0
|
| 835 |
+
if K1.is_RationalField:
|
| 836 |
+
return K1
|
| 837 |
+
|
| 838 |
+
if K0.is_IntegerRing:
|
| 839 |
+
return K0
|
| 840 |
+
if K1.is_IntegerRing:
|
| 841 |
+
return K1
|
| 842 |
+
|
| 843 |
+
from sympy.polys.domains import EX
|
| 844 |
+
return EX
|
| 845 |
+
|
| 846 |
+
def __eq__(self, other):
|
| 847 |
+
"""Returns ``True`` if two domains are equivalent. """
|
| 848 |
+
# XXX: Remove this.
|
| 849 |
+
return isinstance(other, Domain) and self.dtype == other.dtype
|
| 850 |
+
|
| 851 |
+
def __ne__(self, other):
|
| 852 |
+
"""Returns ``False`` if two domains are equivalent. """
|
| 853 |
+
return not self == other
|
| 854 |
+
|
| 855 |
+
def map(self, seq):
|
| 856 |
+
"""Rersively apply ``self`` to all elements of ``seq``. """
|
| 857 |
+
result = []
|
| 858 |
+
|
| 859 |
+
for elt in seq:
|
| 860 |
+
if isinstance(elt, list):
|
| 861 |
+
result.append(self.map(elt))
|
| 862 |
+
else:
|
| 863 |
+
result.append(self(elt))
|
| 864 |
+
|
| 865 |
+
return result
|
| 866 |
+
|
| 867 |
+
def get_ring(self):
|
| 868 |
+
"""Returns a ring associated with ``self``. """
|
| 869 |
+
raise DomainError('there is no ring associated with %s' % self)
|
| 870 |
+
|
| 871 |
+
def get_field(self):
|
| 872 |
+
"""Returns a field associated with ``self``. """
|
| 873 |
+
raise DomainError('there is no field associated with %s' % self)
|
| 874 |
+
|
| 875 |
+
def get_exact(self):
|
| 876 |
+
"""Returns an exact domain associated with ``self``. """
|
| 877 |
+
return self
|
| 878 |
+
|
| 879 |
+
def __getitem__(self, symbols):
|
| 880 |
+
"""The mathematical way to make a polynomial ring. """
|
| 881 |
+
if hasattr(symbols, '__iter__'):
|
| 882 |
+
return self.poly_ring(*symbols)
|
| 883 |
+
else:
|
| 884 |
+
return self.poly_ring(symbols)
|
| 885 |
+
|
| 886 |
+
def poly_ring(self, *symbols, order=lex):
|
| 887 |
+
"""Returns a polynomial ring, i.e. `K[X]`. """
|
| 888 |
+
from sympy.polys.domains.polynomialring import PolynomialRing
|
| 889 |
+
return PolynomialRing(self, symbols, order)
|
| 890 |
+
|
| 891 |
+
def frac_field(self, *symbols, order=lex):
|
| 892 |
+
"""Returns a fraction field, i.e. `K(X)`. """
|
| 893 |
+
from sympy.polys.domains.fractionfield import FractionField
|
| 894 |
+
return FractionField(self, symbols, order)
|
| 895 |
+
|
| 896 |
+
def old_poly_ring(self, *symbols, **kwargs):
|
| 897 |
+
"""Returns a polynomial ring, i.e. `K[X]`. """
|
| 898 |
+
from sympy.polys.domains.old_polynomialring import PolynomialRing
|
| 899 |
+
return PolynomialRing(self, *symbols, **kwargs)
|
| 900 |
+
|
| 901 |
+
def old_frac_field(self, *symbols, **kwargs):
|
| 902 |
+
"""Returns a fraction field, i.e. `K(X)`. """
|
| 903 |
+
from sympy.polys.domains.old_fractionfield import FractionField
|
| 904 |
+
return FractionField(self, *symbols, **kwargs)
|
| 905 |
+
|
| 906 |
+
def algebraic_field(self, *extension, alias=None):
|
| 907 |
+
r"""Returns an algebraic field, i.e. `K(\alpha, \ldots)`. """
|
| 908 |
+
raise DomainError("Cannot create algebraic field over %s" % self)
|
| 909 |
+
|
| 910 |
+
def alg_field_from_poly(self, poly, alias=None, root_index=-1):
|
| 911 |
+
r"""
|
| 912 |
+
Convenience method to construct an algebraic extension on a root of a
|
| 913 |
+
polynomial, chosen by root index.
|
| 914 |
+
|
| 915 |
+
Parameters
|
| 916 |
+
==========
|
| 917 |
+
|
| 918 |
+
poly : :py:class:`~.Poly`
|
| 919 |
+
The polynomial whose root generates the extension.
|
| 920 |
+
alias : str, optional (default=None)
|
| 921 |
+
Symbol name for the generator of the extension.
|
| 922 |
+
E.g. "alpha" or "theta".
|
| 923 |
+
root_index : int, optional (default=-1)
|
| 924 |
+
Specifies which root of the polynomial is desired. The ordering is
|
| 925 |
+
as defined by the :py:class:`~.ComplexRootOf` class. The default of
|
| 926 |
+
``-1`` selects the most natural choice in the common cases of
|
| 927 |
+
quadratic and cyclotomic fields (the square root on the positive
|
| 928 |
+
real or imaginary axis, resp. $\mathrm{e}^{2\pi i/n}$).
|
| 929 |
+
|
| 930 |
+
Examples
|
| 931 |
+
========
|
| 932 |
+
|
| 933 |
+
>>> from sympy import QQ, Poly
|
| 934 |
+
>>> from sympy.abc import x
|
| 935 |
+
>>> f = Poly(x**2 - 2)
|
| 936 |
+
>>> K = QQ.alg_field_from_poly(f)
|
| 937 |
+
>>> K.ext.minpoly == f
|
| 938 |
+
True
|
| 939 |
+
>>> g = Poly(8*x**3 - 6*x - 1)
|
| 940 |
+
>>> L = QQ.alg_field_from_poly(g, "alpha")
|
| 941 |
+
>>> L.ext.minpoly == g
|
| 942 |
+
True
|
| 943 |
+
>>> L.to_sympy(L([1, 1, 1]))
|
| 944 |
+
alpha**2 + alpha + 1
|
| 945 |
+
|
| 946 |
+
"""
|
| 947 |
+
from sympy.polys.rootoftools import CRootOf
|
| 948 |
+
root = CRootOf(poly, root_index)
|
| 949 |
+
alpha = AlgebraicNumber(root, alias=alias)
|
| 950 |
+
return self.algebraic_field(alpha, alias=alias)
|
| 951 |
+
|
| 952 |
+
def cyclotomic_field(self, n, ss=False, alias="zeta", gen=None, root_index=-1):
|
| 953 |
+
r"""
|
| 954 |
+
Convenience method to construct a cyclotomic field.
|
| 955 |
+
|
| 956 |
+
Parameters
|
| 957 |
+
==========
|
| 958 |
+
|
| 959 |
+
n : int
|
| 960 |
+
Construct the nth cyclotomic field.
|
| 961 |
+
ss : boolean, optional (default=False)
|
| 962 |
+
If True, append *n* as a subscript on the alias string.
|
| 963 |
+
alias : str, optional (default="zeta")
|
| 964 |
+
Symbol name for the generator.
|
| 965 |
+
gen : :py:class:`~.Symbol`, optional (default=None)
|
| 966 |
+
Desired variable for the cyclotomic polynomial that defines the
|
| 967 |
+
field. If ``None``, a dummy variable will be used.
|
| 968 |
+
root_index : int, optional (default=-1)
|
| 969 |
+
Specifies which root of the polynomial is desired. The ordering is
|
| 970 |
+
as defined by the :py:class:`~.ComplexRootOf` class. The default of
|
| 971 |
+
``-1`` selects the root $\mathrm{e}^{2\pi i/n}$.
|
| 972 |
+
|
| 973 |
+
Examples
|
| 974 |
+
========
|
| 975 |
+
|
| 976 |
+
>>> from sympy import QQ, latex
|
| 977 |
+
>>> K = QQ.cyclotomic_field(5)
|
| 978 |
+
>>> K.to_sympy(K([-1, 1]))
|
| 979 |
+
1 - zeta
|
| 980 |
+
>>> L = QQ.cyclotomic_field(7, True)
|
| 981 |
+
>>> a = L.to_sympy(L([-1, 1]))
|
| 982 |
+
>>> print(a)
|
| 983 |
+
1 - zeta7
|
| 984 |
+
>>> print(latex(a))
|
| 985 |
+
1 - \zeta_{7}
|
| 986 |
+
|
| 987 |
+
"""
|
| 988 |
+
from sympy.polys.specialpolys import cyclotomic_poly
|
| 989 |
+
if ss:
|
| 990 |
+
alias += str(n)
|
| 991 |
+
return self.alg_field_from_poly(cyclotomic_poly(n, gen), alias=alias,
|
| 992 |
+
root_index=root_index)
|
| 993 |
+
|
| 994 |
+
def inject(self, *symbols):
|
| 995 |
+
"""Inject generators into this domain. """
|
| 996 |
+
raise NotImplementedError
|
| 997 |
+
|
| 998 |
+
def drop(self, *symbols):
|
| 999 |
+
"""Drop generators from this domain. """
|
| 1000 |
+
if self.is_Simple:
|
| 1001 |
+
return self
|
| 1002 |
+
raise NotImplementedError # pragma: no cover
|
| 1003 |
+
|
| 1004 |
+
def is_zero(self, a):
|
| 1005 |
+
"""Returns True if ``a`` is zero. """
|
| 1006 |
+
return not a
|
| 1007 |
+
|
| 1008 |
+
def is_one(self, a):
|
| 1009 |
+
"""Returns True if ``a`` is one. """
|
| 1010 |
+
return a == self.one
|
| 1011 |
+
|
| 1012 |
+
def is_positive(self, a):
|
| 1013 |
+
"""Returns True if ``a`` is positive. """
|
| 1014 |
+
return a > 0
|
| 1015 |
+
|
| 1016 |
+
def is_negative(self, a):
|
| 1017 |
+
"""Returns True if ``a`` is negative. """
|
| 1018 |
+
return a < 0
|
| 1019 |
+
|
| 1020 |
+
def is_nonpositive(self, a):
|
| 1021 |
+
"""Returns True if ``a`` is non-positive. """
|
| 1022 |
+
return a <= 0
|
| 1023 |
+
|
| 1024 |
+
def is_nonnegative(self, a):
|
| 1025 |
+
"""Returns True if ``a`` is non-negative. """
|
| 1026 |
+
return a >= 0
|
| 1027 |
+
|
| 1028 |
+
def canonical_unit(self, a):
|
| 1029 |
+
if self.is_negative(a):
|
| 1030 |
+
return -self.one
|
| 1031 |
+
else:
|
| 1032 |
+
return self.one
|
| 1033 |
+
|
| 1034 |
+
def abs(self, a):
|
| 1035 |
+
"""Absolute value of ``a``, implies ``__abs__``. """
|
| 1036 |
+
return abs(a)
|
| 1037 |
+
|
| 1038 |
+
def neg(self, a):
|
| 1039 |
+
"""Returns ``a`` negated, implies ``__neg__``. """
|
| 1040 |
+
return -a
|
| 1041 |
+
|
| 1042 |
+
def pos(self, a):
|
| 1043 |
+
"""Returns ``a`` positive, implies ``__pos__``. """
|
| 1044 |
+
return +a
|
| 1045 |
+
|
| 1046 |
+
def add(self, a, b):
|
| 1047 |
+
"""Sum of ``a`` and ``b``, implies ``__add__``. """
|
| 1048 |
+
return a + b
|
| 1049 |
+
|
| 1050 |
+
def sub(self, a, b):
|
| 1051 |
+
"""Difference of ``a`` and ``b``, implies ``__sub__``. """
|
| 1052 |
+
return a - b
|
| 1053 |
+
|
| 1054 |
+
def mul(self, a, b):
|
| 1055 |
+
"""Product of ``a`` and ``b``, implies ``__mul__``. """
|
| 1056 |
+
return a * b
|
| 1057 |
+
|
| 1058 |
+
def pow(self, a, b):
|
| 1059 |
+
"""Raise ``a`` to power ``b``, implies ``__pow__``. """
|
| 1060 |
+
return a ** b
|
| 1061 |
+
|
| 1062 |
+
def exquo(self, a, b):
|
| 1063 |
+
"""Exact quotient of *a* and *b*. Analogue of ``a / b``.
|
| 1064 |
+
|
| 1065 |
+
Explanation
|
| 1066 |
+
===========
|
| 1067 |
+
|
| 1068 |
+
This is essentially the same as ``a / b`` except that an error will be
|
| 1069 |
+
raised if the division is inexact (if there is any remainder) and the
|
| 1070 |
+
result will always be a domain element. When working in a
|
| 1071 |
+
:py:class:`~.Domain` that is not a :py:class:`~.Field` (e.g. :ref:`ZZ`
|
| 1072 |
+
or :ref:`K[x]`) ``exquo`` should be used instead of ``/``.
|
| 1073 |
+
|
| 1074 |
+
The key invariant is that if ``q = K.exquo(a, b)`` (and ``exquo`` does
|
| 1075 |
+
not raise an exception) then ``a == b*q``.
|
| 1076 |
+
|
| 1077 |
+
Examples
|
| 1078 |
+
========
|
| 1079 |
+
|
| 1080 |
+
We can use ``K.exquo`` instead of ``/`` for exact division.
|
| 1081 |
+
|
| 1082 |
+
>>> from sympy import ZZ
|
| 1083 |
+
>>> ZZ.exquo(ZZ(4), ZZ(2))
|
| 1084 |
+
2
|
| 1085 |
+
>>> ZZ.exquo(ZZ(5), ZZ(2))
|
| 1086 |
+
Traceback (most recent call last):
|
| 1087 |
+
...
|
| 1088 |
+
ExactQuotientFailed: 2 does not divide 5 in ZZ
|
| 1089 |
+
|
| 1090 |
+
Over a :py:class:`~.Field` such as :ref:`QQ`, division (with nonzero
|
| 1091 |
+
divisor) is always exact so in that case ``/`` can be used instead of
|
| 1092 |
+
:py:meth:`~.Domain.exquo`.
|
| 1093 |
+
|
| 1094 |
+
>>> from sympy import QQ
|
| 1095 |
+
>>> QQ.exquo(QQ(5), QQ(2))
|
| 1096 |
+
5/2
|
| 1097 |
+
>>> QQ(5) / QQ(2)
|
| 1098 |
+
5/2
|
| 1099 |
+
|
| 1100 |
+
Parameters
|
| 1101 |
+
==========
|
| 1102 |
+
|
| 1103 |
+
a: domain element
|
| 1104 |
+
The dividend
|
| 1105 |
+
b: domain element
|
| 1106 |
+
The divisor
|
| 1107 |
+
|
| 1108 |
+
Returns
|
| 1109 |
+
=======
|
| 1110 |
+
|
| 1111 |
+
q: domain element
|
| 1112 |
+
The exact quotient
|
| 1113 |
+
|
| 1114 |
+
Raises
|
| 1115 |
+
======
|
| 1116 |
+
|
| 1117 |
+
ExactQuotientFailed: if exact division is not possible.
|
| 1118 |
+
ZeroDivisionError: when the divisor is zero.
|
| 1119 |
+
|
| 1120 |
+
See also
|
| 1121 |
+
========
|
| 1122 |
+
|
| 1123 |
+
quo: Analogue of ``a // b``
|
| 1124 |
+
rem: Analogue of ``a % b``
|
| 1125 |
+
div: Analogue of ``divmod(a, b)``
|
| 1126 |
+
|
| 1127 |
+
Notes
|
| 1128 |
+
=====
|
| 1129 |
+
|
| 1130 |
+
Since the default :py:attr:`~.Domain.dtype` for :ref:`ZZ` is ``int``
|
| 1131 |
+
(or ``mpz``) division as ``a / b`` should not be used as it would give
|
| 1132 |
+
a ``float`` which is not a domain element.
|
| 1133 |
+
|
| 1134 |
+
>>> ZZ(4) / ZZ(2) # doctest: +SKIP
|
| 1135 |
+
2.0
|
| 1136 |
+
>>> ZZ(5) / ZZ(2) # doctest: +SKIP
|
| 1137 |
+
2.5
|
| 1138 |
+
|
| 1139 |
+
On the other hand with `SYMPY_GROUND_TYPES=flint` elements of :ref:`ZZ`
|
| 1140 |
+
are ``flint.fmpz`` and division would raise an exception:
|
| 1141 |
+
|
| 1142 |
+
>>> ZZ(4) / ZZ(2) # doctest: +SKIP
|
| 1143 |
+
Traceback (most recent call last):
|
| 1144 |
+
...
|
| 1145 |
+
TypeError: unsupported operand type(s) for /: 'fmpz' and 'fmpz'
|
| 1146 |
+
|
| 1147 |
+
Using ``/`` with :ref:`ZZ` will lead to incorrect results so
|
| 1148 |
+
:py:meth:`~.Domain.exquo` should be used instead.
|
| 1149 |
+
|
| 1150 |
+
"""
|
| 1151 |
+
raise NotImplementedError
|
| 1152 |
+
|
| 1153 |
+
def quo(self, a, b):
|
| 1154 |
+
"""Quotient of *a* and *b*. Analogue of ``a // b``.
|
| 1155 |
+
|
| 1156 |
+
``K.quo(a, b)`` is equivalent to ``K.div(a, b)[0]``. See
|
| 1157 |
+
:py:meth:`~.Domain.div` for more explanation.
|
| 1158 |
+
|
| 1159 |
+
See also
|
| 1160 |
+
========
|
| 1161 |
+
|
| 1162 |
+
rem: Analogue of ``a % b``
|
| 1163 |
+
div: Analogue of ``divmod(a, b)``
|
| 1164 |
+
exquo: Analogue of ``a / b``
|
| 1165 |
+
"""
|
| 1166 |
+
raise NotImplementedError
|
| 1167 |
+
|
| 1168 |
+
def rem(self, a, b):
|
| 1169 |
+
"""Modulo division of *a* and *b*. Analogue of ``a % b``.
|
| 1170 |
+
|
| 1171 |
+
``K.rem(a, b)`` is equivalent to ``K.div(a, b)[1]``. See
|
| 1172 |
+
:py:meth:`~.Domain.div` for more explanation.
|
| 1173 |
+
|
| 1174 |
+
See also
|
| 1175 |
+
========
|
| 1176 |
+
|
| 1177 |
+
quo: Analogue of ``a // b``
|
| 1178 |
+
div: Analogue of ``divmod(a, b)``
|
| 1179 |
+
exquo: Analogue of ``a / b``
|
| 1180 |
+
"""
|
| 1181 |
+
raise NotImplementedError
|
| 1182 |
+
|
| 1183 |
+
def div(self, a, b):
|
| 1184 |
+
"""Quotient and remainder for *a* and *b*. Analogue of ``divmod(a, b)``
|
| 1185 |
+
|
| 1186 |
+
Explanation
|
| 1187 |
+
===========
|
| 1188 |
+
|
| 1189 |
+
This is essentially the same as ``divmod(a, b)`` except that is more
|
| 1190 |
+
consistent when working over some :py:class:`~.Field` domains such as
|
| 1191 |
+
:ref:`QQ`. When working over an arbitrary :py:class:`~.Domain` the
|
| 1192 |
+
:py:meth:`~.Domain.div` method should be used instead of ``divmod``.
|
| 1193 |
+
|
| 1194 |
+
The key invariant is that if ``q, r = K.div(a, b)`` then
|
| 1195 |
+
``a == b*q + r``.
|
| 1196 |
+
|
| 1197 |
+
The result of ``K.div(a, b)`` is the same as the tuple
|
| 1198 |
+
``(K.quo(a, b), K.rem(a, b))`` except that if both quotient and
|
| 1199 |
+
remainder are needed then it is more efficient to use
|
| 1200 |
+
:py:meth:`~.Domain.div`.
|
| 1201 |
+
|
| 1202 |
+
Examples
|
| 1203 |
+
========
|
| 1204 |
+
|
| 1205 |
+
We can use ``K.div`` instead of ``divmod`` for floor division and
|
| 1206 |
+
remainder.
|
| 1207 |
+
|
| 1208 |
+
>>> from sympy import ZZ, QQ
|
| 1209 |
+
>>> ZZ.div(ZZ(5), ZZ(2))
|
| 1210 |
+
(2, 1)
|
| 1211 |
+
|
| 1212 |
+
If ``K`` is a :py:class:`~.Field` then the division is always exact
|
| 1213 |
+
with a remainder of :py:attr:`~.Domain.zero`.
|
| 1214 |
+
|
| 1215 |
+
>>> QQ.div(QQ(5), QQ(2))
|
| 1216 |
+
(5/2, 0)
|
| 1217 |
+
|
| 1218 |
+
Parameters
|
| 1219 |
+
==========
|
| 1220 |
+
|
| 1221 |
+
a: domain element
|
| 1222 |
+
The dividend
|
| 1223 |
+
b: domain element
|
| 1224 |
+
The divisor
|
| 1225 |
+
|
| 1226 |
+
Returns
|
| 1227 |
+
=======
|
| 1228 |
+
|
| 1229 |
+
(q, r): tuple of domain elements
|
| 1230 |
+
The quotient and remainder
|
| 1231 |
+
|
| 1232 |
+
Raises
|
| 1233 |
+
======
|
| 1234 |
+
|
| 1235 |
+
ZeroDivisionError: when the divisor is zero.
|
| 1236 |
+
|
| 1237 |
+
See also
|
| 1238 |
+
========
|
| 1239 |
+
|
| 1240 |
+
quo: Analogue of ``a // b``
|
| 1241 |
+
rem: Analogue of ``a % b``
|
| 1242 |
+
exquo: Analogue of ``a / b``
|
| 1243 |
+
|
| 1244 |
+
Notes
|
| 1245 |
+
=====
|
| 1246 |
+
|
| 1247 |
+
If ``gmpy`` is installed then the ``gmpy.mpq`` type will be used as
|
| 1248 |
+
the :py:attr:`~.Domain.dtype` for :ref:`QQ`. The ``gmpy.mpq`` type
|
| 1249 |
+
defines ``divmod`` in a way that is undesirable so
|
| 1250 |
+
:py:meth:`~.Domain.div` should be used instead of ``divmod``.
|
| 1251 |
+
|
| 1252 |
+
>>> a = QQ(1)
|
| 1253 |
+
>>> b = QQ(3, 2)
|
| 1254 |
+
>>> a # doctest: +SKIP
|
| 1255 |
+
mpq(1,1)
|
| 1256 |
+
>>> b # doctest: +SKIP
|
| 1257 |
+
mpq(3,2)
|
| 1258 |
+
>>> divmod(a, b) # doctest: +SKIP
|
| 1259 |
+
(mpz(0), mpq(1,1))
|
| 1260 |
+
>>> QQ.div(a, b) # doctest: +SKIP
|
| 1261 |
+
(mpq(2,3), mpq(0,1))
|
| 1262 |
+
|
| 1263 |
+
Using ``//`` or ``%`` with :ref:`QQ` will lead to incorrect results so
|
| 1264 |
+
:py:meth:`~.Domain.div` should be used instead.
|
| 1265 |
+
|
| 1266 |
+
"""
|
| 1267 |
+
raise NotImplementedError
|
| 1268 |
+
|
| 1269 |
+
def invert(self, a, b):
|
| 1270 |
+
"""Returns inversion of ``a mod b``, implies something. """
|
| 1271 |
+
raise NotImplementedError
|
| 1272 |
+
|
| 1273 |
+
def revert(self, a):
|
| 1274 |
+
"""Returns ``a**(-1)`` if possible. """
|
| 1275 |
+
raise NotImplementedError
|
| 1276 |
+
|
| 1277 |
+
def numer(self, a):
|
| 1278 |
+
"""Returns numerator of ``a``. """
|
| 1279 |
+
raise NotImplementedError
|
| 1280 |
+
|
| 1281 |
+
def denom(self, a):
|
| 1282 |
+
"""Returns denominator of ``a``. """
|
| 1283 |
+
raise NotImplementedError
|
| 1284 |
+
|
| 1285 |
+
def half_gcdex(self, a, b):
|
| 1286 |
+
"""Half extended GCD of ``a`` and ``b``. """
|
| 1287 |
+
s, t, h = self.gcdex(a, b)
|
| 1288 |
+
return s, h
|
| 1289 |
+
|
| 1290 |
+
def gcdex(self, a, b):
|
| 1291 |
+
"""Extended GCD of ``a`` and ``b``. """
|
| 1292 |
+
raise NotImplementedError
|
| 1293 |
+
|
| 1294 |
+
def cofactors(self, a, b):
|
| 1295 |
+
"""Returns GCD and cofactors of ``a`` and ``b``. """
|
| 1296 |
+
gcd = self.gcd(a, b)
|
| 1297 |
+
cfa = self.quo(a, gcd)
|
| 1298 |
+
cfb = self.quo(b, gcd)
|
| 1299 |
+
return gcd, cfa, cfb
|
| 1300 |
+
|
| 1301 |
+
def gcd(self, a, b):
|
| 1302 |
+
"""Returns GCD of ``a`` and ``b``. """
|
| 1303 |
+
raise NotImplementedError
|
| 1304 |
+
|
| 1305 |
+
def lcm(self, a, b):
|
| 1306 |
+
"""Returns LCM of ``a`` and ``b``. """
|
| 1307 |
+
raise NotImplementedError
|
| 1308 |
+
|
| 1309 |
+
def log(self, a, b):
|
| 1310 |
+
"""Returns b-base logarithm of ``a``. """
|
| 1311 |
+
raise NotImplementedError
|
| 1312 |
+
|
| 1313 |
+
def sqrt(self, a):
|
| 1314 |
+
"""Returns a (possibly inexact) square root of ``a``.
|
| 1315 |
+
|
| 1316 |
+
Explanation
|
| 1317 |
+
===========
|
| 1318 |
+
There is no universal definition of "inexact square root" for all
|
| 1319 |
+
domains. It is not recommended to implement this method for domains
|
| 1320 |
+
other then :ref:`ZZ`.
|
| 1321 |
+
|
| 1322 |
+
See also
|
| 1323 |
+
========
|
| 1324 |
+
exsqrt
|
| 1325 |
+
"""
|
| 1326 |
+
raise NotImplementedError
|
| 1327 |
+
|
| 1328 |
+
def is_square(self, a):
|
| 1329 |
+
"""Returns whether ``a`` is a square in the domain.
|
| 1330 |
+
|
| 1331 |
+
Explanation
|
| 1332 |
+
===========
|
| 1333 |
+
Returns ``True`` if there is an element ``b`` in the domain such that
|
| 1334 |
+
``b * b == a``, otherwise returns ``False``. For inexact domains like
|
| 1335 |
+
:ref:`RR` and :ref:`CC`, a tiny difference in this equality can be
|
| 1336 |
+
tolerated.
|
| 1337 |
+
|
| 1338 |
+
See also
|
| 1339 |
+
========
|
| 1340 |
+
exsqrt
|
| 1341 |
+
"""
|
| 1342 |
+
raise NotImplementedError
|
| 1343 |
+
|
| 1344 |
+
def exsqrt(self, a):
|
| 1345 |
+
"""Principal square root of a within the domain if ``a`` is square.
|
| 1346 |
+
|
| 1347 |
+
Explanation
|
| 1348 |
+
===========
|
| 1349 |
+
The implementation of this method should return an element ``b`` in the
|
| 1350 |
+
domain such that ``b * b == a``, or ``None`` if there is no such ``b``.
|
| 1351 |
+
For inexact domains like :ref:`RR` and :ref:`CC`, a tiny difference in
|
| 1352 |
+
this equality can be tolerated. The choice of a "principal" square root
|
| 1353 |
+
should follow a consistent rule whenever possible.
|
| 1354 |
+
|
| 1355 |
+
See also
|
| 1356 |
+
========
|
| 1357 |
+
sqrt, is_square
|
| 1358 |
+
"""
|
| 1359 |
+
raise NotImplementedError
|
| 1360 |
+
|
| 1361 |
+
def evalf(self, a, prec=None, **options):
|
| 1362 |
+
"""Returns numerical approximation of ``a``. """
|
| 1363 |
+
return self.to_sympy(a).evalf(prec, **options)
|
| 1364 |
+
|
| 1365 |
+
n = evalf
|
| 1366 |
+
|
| 1367 |
+
def real(self, a):
|
| 1368 |
+
return a
|
| 1369 |
+
|
| 1370 |
+
def imag(self, a):
|
| 1371 |
+
return self.zero
|
| 1372 |
+
|
| 1373 |
+
def almosteq(self, a, b, tolerance=None):
|
| 1374 |
+
"""Check if ``a`` and ``b`` are almost equal. """
|
| 1375 |
+
return a == b
|
| 1376 |
+
|
| 1377 |
+
def characteristic(self):
|
| 1378 |
+
"""Return the characteristic of this domain. """
|
| 1379 |
+
raise NotImplementedError('characteristic()')
|
| 1380 |
+
|
| 1381 |
+
|
| 1382 |
+
__all__ = ['Domain']
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/domainelement.py
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Trait for implementing domain elements. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.utilities import public
|
| 5 |
+
|
| 6 |
+
@public
|
| 7 |
+
class DomainElement:
|
| 8 |
+
"""
|
| 9 |
+
Represents an element of a domain.
|
| 10 |
+
|
| 11 |
+
Mix in this trait into a class whose instances should be recognized as
|
| 12 |
+
elements of a domain. Method ``parent()`` gives that domain.
|
| 13 |
+
"""
|
| 14 |
+
|
| 15 |
+
__slots__ = ()
|
| 16 |
+
|
| 17 |
+
def parent(self):
|
| 18 |
+
"""Get the domain associated with ``self``
|
| 19 |
+
|
| 20 |
+
Examples
|
| 21 |
+
========
|
| 22 |
+
|
| 23 |
+
>>> from sympy import ZZ, symbols
|
| 24 |
+
>>> x, y = symbols('x, y')
|
| 25 |
+
>>> K = ZZ[x,y]
|
| 26 |
+
>>> p = K(x)**2 + K(y)**2
|
| 27 |
+
>>> p
|
| 28 |
+
x**2 + y**2
|
| 29 |
+
>>> p.parent()
|
| 30 |
+
ZZ[x,y]
|
| 31 |
+
|
| 32 |
+
Notes
|
| 33 |
+
=====
|
| 34 |
+
|
| 35 |
+
This is used by :py:meth:`~.Domain.convert` to identify the domain
|
| 36 |
+
associated with a domain element.
|
| 37 |
+
"""
|
| 38 |
+
raise NotImplementedError("abstract method")
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/expressiondomain.py
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`ExpressionDomain` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.core import sympify, SympifyError
|
| 5 |
+
from sympy.polys.domains.domainelement import DomainElement
|
| 6 |
+
from sympy.polys.domains.characteristiczero import CharacteristicZero
|
| 7 |
+
from sympy.polys.domains.field import Field
|
| 8 |
+
from sympy.polys.domains.simpledomain import SimpleDomain
|
| 9 |
+
from sympy.polys.polyutils import PicklableWithSlots
|
| 10 |
+
from sympy.utilities import public
|
| 11 |
+
|
| 12 |
+
eflags = {"deep": False, "mul": True, "power_exp": False, "power_base": False,
|
| 13 |
+
"basic": False, "multinomial": False, "log": False}
|
| 14 |
+
|
| 15 |
+
@public
|
| 16 |
+
class ExpressionDomain(Field, CharacteristicZero, SimpleDomain):
|
| 17 |
+
"""A class for arbitrary expressions. """
|
| 18 |
+
|
| 19 |
+
is_SymbolicDomain = is_EX = True
|
| 20 |
+
|
| 21 |
+
class Expression(DomainElement, PicklableWithSlots):
|
| 22 |
+
"""An arbitrary expression. """
|
| 23 |
+
|
| 24 |
+
__slots__ = ('ex',)
|
| 25 |
+
|
| 26 |
+
def __init__(self, ex):
|
| 27 |
+
if not isinstance(ex, self.__class__):
|
| 28 |
+
self.ex = sympify(ex)
|
| 29 |
+
else:
|
| 30 |
+
self.ex = ex.ex
|
| 31 |
+
|
| 32 |
+
def __repr__(f):
|
| 33 |
+
return 'EX(%s)' % repr(f.ex)
|
| 34 |
+
|
| 35 |
+
def __str__(f):
|
| 36 |
+
return 'EX(%s)' % str(f.ex)
|
| 37 |
+
|
| 38 |
+
def __hash__(self):
|
| 39 |
+
return hash((self.__class__.__name__, self.ex))
|
| 40 |
+
|
| 41 |
+
def parent(self):
|
| 42 |
+
return EX
|
| 43 |
+
|
| 44 |
+
def as_expr(f):
|
| 45 |
+
return f.ex
|
| 46 |
+
|
| 47 |
+
def numer(f):
|
| 48 |
+
return f.__class__(f.ex.as_numer_denom()[0])
|
| 49 |
+
|
| 50 |
+
def denom(f):
|
| 51 |
+
return f.__class__(f.ex.as_numer_denom()[1])
|
| 52 |
+
|
| 53 |
+
def simplify(f, ex):
|
| 54 |
+
return f.__class__(ex.cancel().expand(**eflags))
|
| 55 |
+
|
| 56 |
+
def __abs__(f):
|
| 57 |
+
return f.__class__(abs(f.ex))
|
| 58 |
+
|
| 59 |
+
def __neg__(f):
|
| 60 |
+
return f.__class__(-f.ex)
|
| 61 |
+
|
| 62 |
+
def _to_ex(f, g):
|
| 63 |
+
try:
|
| 64 |
+
return f.__class__(g)
|
| 65 |
+
except SympifyError:
|
| 66 |
+
return None
|
| 67 |
+
|
| 68 |
+
def __lt__(f, g):
|
| 69 |
+
return f.ex.sort_key() < g.ex.sort_key()
|
| 70 |
+
|
| 71 |
+
def __add__(f, g):
|
| 72 |
+
g = f._to_ex(g)
|
| 73 |
+
|
| 74 |
+
if g is None:
|
| 75 |
+
return NotImplemented
|
| 76 |
+
elif g == EX.zero:
|
| 77 |
+
return f
|
| 78 |
+
elif f == EX.zero:
|
| 79 |
+
return g
|
| 80 |
+
else:
|
| 81 |
+
return f.simplify(f.ex + g.ex)
|
| 82 |
+
|
| 83 |
+
def __radd__(f, g):
|
| 84 |
+
return f.simplify(f.__class__(g).ex + f.ex)
|
| 85 |
+
|
| 86 |
+
def __sub__(f, g):
|
| 87 |
+
g = f._to_ex(g)
|
| 88 |
+
|
| 89 |
+
if g is None:
|
| 90 |
+
return NotImplemented
|
| 91 |
+
elif g == EX.zero:
|
| 92 |
+
return f
|
| 93 |
+
elif f == EX.zero:
|
| 94 |
+
return -g
|
| 95 |
+
else:
|
| 96 |
+
return f.simplify(f.ex - g.ex)
|
| 97 |
+
|
| 98 |
+
def __rsub__(f, g):
|
| 99 |
+
return f.simplify(f.__class__(g).ex - f.ex)
|
| 100 |
+
|
| 101 |
+
def __mul__(f, g):
|
| 102 |
+
g = f._to_ex(g)
|
| 103 |
+
|
| 104 |
+
if g is None:
|
| 105 |
+
return NotImplemented
|
| 106 |
+
|
| 107 |
+
if EX.zero in (f, g):
|
| 108 |
+
return EX.zero
|
| 109 |
+
elif f.ex.is_Number and g.ex.is_Number:
|
| 110 |
+
return f.__class__(f.ex*g.ex)
|
| 111 |
+
|
| 112 |
+
return f.simplify(f.ex*g.ex)
|
| 113 |
+
|
| 114 |
+
def __rmul__(f, g):
|
| 115 |
+
return f.simplify(f.__class__(g).ex*f.ex)
|
| 116 |
+
|
| 117 |
+
def __pow__(f, n):
|
| 118 |
+
n = f._to_ex(n)
|
| 119 |
+
|
| 120 |
+
if n is not None:
|
| 121 |
+
return f.simplify(f.ex**n.ex)
|
| 122 |
+
else:
|
| 123 |
+
return NotImplemented
|
| 124 |
+
|
| 125 |
+
def __truediv__(f, g):
|
| 126 |
+
g = f._to_ex(g)
|
| 127 |
+
|
| 128 |
+
if g is not None:
|
| 129 |
+
return f.simplify(f.ex/g.ex)
|
| 130 |
+
else:
|
| 131 |
+
return NotImplemented
|
| 132 |
+
|
| 133 |
+
def __rtruediv__(f, g):
|
| 134 |
+
return f.simplify(f.__class__(g).ex/f.ex)
|
| 135 |
+
|
| 136 |
+
def __eq__(f, g):
|
| 137 |
+
return f.ex == f.__class__(g).ex
|
| 138 |
+
|
| 139 |
+
def __ne__(f, g):
|
| 140 |
+
return not f == g
|
| 141 |
+
|
| 142 |
+
def __bool__(f):
|
| 143 |
+
return not f.ex.is_zero
|
| 144 |
+
|
| 145 |
+
def gcd(f, g):
|
| 146 |
+
from sympy.polys import gcd
|
| 147 |
+
return f.__class__(gcd(f.ex, f.__class__(g).ex))
|
| 148 |
+
|
| 149 |
+
def lcm(f, g):
|
| 150 |
+
from sympy.polys import lcm
|
| 151 |
+
return f.__class__(lcm(f.ex, f.__class__(g).ex))
|
| 152 |
+
|
| 153 |
+
dtype = Expression
|
| 154 |
+
|
| 155 |
+
zero = Expression(0)
|
| 156 |
+
one = Expression(1)
|
| 157 |
+
|
| 158 |
+
rep = 'EX'
|
| 159 |
+
|
| 160 |
+
has_assoc_Ring = False
|
| 161 |
+
has_assoc_Field = True
|
| 162 |
+
|
| 163 |
+
def __init__(self):
|
| 164 |
+
pass
|
| 165 |
+
|
| 166 |
+
def __eq__(self, other):
|
| 167 |
+
if isinstance(other, ExpressionDomain):
|
| 168 |
+
return True
|
| 169 |
+
else:
|
| 170 |
+
return NotImplemented
|
| 171 |
+
|
| 172 |
+
def __hash__(self):
|
| 173 |
+
return hash("EX")
|
| 174 |
+
|
| 175 |
+
def to_sympy(self, a):
|
| 176 |
+
"""Convert ``a`` to a SymPy object. """
|
| 177 |
+
return a.as_expr()
|
| 178 |
+
|
| 179 |
+
def from_sympy(self, a):
|
| 180 |
+
"""Convert SymPy's expression to ``dtype``. """
|
| 181 |
+
return self.dtype(a)
|
| 182 |
+
|
| 183 |
+
def from_ZZ(K1, a, K0):
|
| 184 |
+
"""Convert a Python ``int`` object to ``dtype``. """
|
| 185 |
+
return K1(K0.to_sympy(a))
|
| 186 |
+
|
| 187 |
+
def from_ZZ_python(K1, a, K0):
|
| 188 |
+
"""Convert a Python ``int`` object to ``dtype``. """
|
| 189 |
+
return K1(K0.to_sympy(a))
|
| 190 |
+
|
| 191 |
+
def from_QQ(K1, a, K0):
|
| 192 |
+
"""Convert a Python ``Fraction`` object to ``dtype``. """
|
| 193 |
+
return K1(K0.to_sympy(a))
|
| 194 |
+
|
| 195 |
+
def from_QQ_python(K1, a, K0):
|
| 196 |
+
"""Convert a Python ``Fraction`` object to ``dtype``. """
|
| 197 |
+
return K1(K0.to_sympy(a))
|
| 198 |
+
|
| 199 |
+
def from_ZZ_gmpy(K1, a, K0):
|
| 200 |
+
"""Convert a GMPY ``mpz`` object to ``dtype``. """
|
| 201 |
+
return K1(K0.to_sympy(a))
|
| 202 |
+
|
| 203 |
+
def from_QQ_gmpy(K1, a, K0):
|
| 204 |
+
"""Convert a GMPY ``mpq`` object to ``dtype``. """
|
| 205 |
+
return K1(K0.to_sympy(a))
|
| 206 |
+
|
| 207 |
+
def from_GaussianIntegerRing(K1, a, K0):
|
| 208 |
+
"""Convert a ``GaussianRational`` object to ``dtype``. """
|
| 209 |
+
return K1(K0.to_sympy(a))
|
| 210 |
+
|
| 211 |
+
def from_GaussianRationalField(K1, a, K0):
|
| 212 |
+
"""Convert a ``GaussianRational`` object to ``dtype``. """
|
| 213 |
+
return K1(K0.to_sympy(a))
|
| 214 |
+
|
| 215 |
+
def from_AlgebraicField(K1, a, K0):
|
| 216 |
+
"""Convert an ``ANP`` object to ``dtype``. """
|
| 217 |
+
return K1(K0.to_sympy(a))
|
| 218 |
+
|
| 219 |
+
def from_RealField(K1, a, K0):
|
| 220 |
+
"""Convert a mpmath ``mpf`` object to ``dtype``. """
|
| 221 |
+
return K1(K0.to_sympy(a))
|
| 222 |
+
|
| 223 |
+
def from_ComplexField(K1, a, K0):
|
| 224 |
+
"""Convert a mpmath ``mpc`` object to ``dtype``. """
|
| 225 |
+
return K1(K0.to_sympy(a))
|
| 226 |
+
|
| 227 |
+
def from_PolynomialRing(K1, a, K0):
|
| 228 |
+
"""Convert a ``DMP`` object to ``dtype``. """
|
| 229 |
+
return K1(K0.to_sympy(a))
|
| 230 |
+
|
| 231 |
+
def from_FractionField(K1, a, K0):
|
| 232 |
+
"""Convert a ``DMF`` object to ``dtype``. """
|
| 233 |
+
return K1(K0.to_sympy(a))
|
| 234 |
+
|
| 235 |
+
def from_ExpressionDomain(K1, a, K0):
|
| 236 |
+
"""Convert a ``EX`` object to ``dtype``. """
|
| 237 |
+
return a
|
| 238 |
+
|
| 239 |
+
def get_ring(self):
|
| 240 |
+
"""Returns a ring associated with ``self``. """
|
| 241 |
+
return self # XXX: EX is not a ring but we don't have much choice here.
|
| 242 |
+
|
| 243 |
+
def get_field(self):
|
| 244 |
+
"""Returns a field associated with ``self``. """
|
| 245 |
+
return self
|
| 246 |
+
|
| 247 |
+
def is_positive(self, a):
|
| 248 |
+
"""Returns True if ``a`` is positive. """
|
| 249 |
+
return a.ex.as_coeff_mul()[0].is_positive
|
| 250 |
+
|
| 251 |
+
def is_negative(self, a):
|
| 252 |
+
"""Returns True if ``a`` is negative. """
|
| 253 |
+
return a.ex.could_extract_minus_sign()
|
| 254 |
+
|
| 255 |
+
def is_nonpositive(self, a):
|
| 256 |
+
"""Returns True if ``a`` is non-positive. """
|
| 257 |
+
return a.ex.as_coeff_mul()[0].is_nonpositive
|
| 258 |
+
|
| 259 |
+
def is_nonnegative(self, a):
|
| 260 |
+
"""Returns True if ``a`` is non-negative. """
|
| 261 |
+
return a.ex.as_coeff_mul()[0].is_nonnegative
|
| 262 |
+
|
| 263 |
+
def numer(self, a):
|
| 264 |
+
"""Returns numerator of ``a``. """
|
| 265 |
+
return a.numer()
|
| 266 |
+
|
| 267 |
+
def denom(self, a):
|
| 268 |
+
"""Returns denominator of ``a``. """
|
| 269 |
+
return a.denom()
|
| 270 |
+
|
| 271 |
+
def gcd(self, a, b):
|
| 272 |
+
return self(1)
|
| 273 |
+
|
| 274 |
+
def lcm(self, a, b):
|
| 275 |
+
return a.lcm(b)
|
| 276 |
+
|
| 277 |
+
|
| 278 |
+
EX = ExpressionDomain()
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/expressionrawdomain.py
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`ExpressionRawDomain` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.core import Expr, S, sympify, Add
|
| 5 |
+
from sympy.polys.domains.characteristiczero import CharacteristicZero
|
| 6 |
+
from sympy.polys.domains.field import Field
|
| 7 |
+
from sympy.polys.domains.simpledomain import SimpleDomain
|
| 8 |
+
from sympy.polys.polyerrors import CoercionFailed
|
| 9 |
+
from sympy.utilities import public
|
| 10 |
+
|
| 11 |
+
|
| 12 |
+
@public
|
| 13 |
+
class ExpressionRawDomain(Field, CharacteristicZero, SimpleDomain):
|
| 14 |
+
"""A class for arbitrary expressions but without automatic simplification. """
|
| 15 |
+
|
| 16 |
+
is_SymbolicRawDomain = is_EXRAW = True
|
| 17 |
+
|
| 18 |
+
dtype = Expr
|
| 19 |
+
|
| 20 |
+
zero = S.Zero
|
| 21 |
+
one = S.One
|
| 22 |
+
|
| 23 |
+
rep = 'EXRAW'
|
| 24 |
+
|
| 25 |
+
has_assoc_Ring = False
|
| 26 |
+
has_assoc_Field = True
|
| 27 |
+
|
| 28 |
+
def __init__(self):
|
| 29 |
+
pass
|
| 30 |
+
|
| 31 |
+
@classmethod
|
| 32 |
+
def new(self, a):
|
| 33 |
+
return sympify(a)
|
| 34 |
+
|
| 35 |
+
def to_sympy(self, a):
|
| 36 |
+
"""Convert ``a`` to a SymPy object. """
|
| 37 |
+
return a
|
| 38 |
+
|
| 39 |
+
def from_sympy(self, a):
|
| 40 |
+
"""Convert SymPy's expression to ``dtype``. """
|
| 41 |
+
if not isinstance(a, Expr):
|
| 42 |
+
raise CoercionFailed(f"Expecting an Expr instance but found: {type(a).__name__}")
|
| 43 |
+
return a
|
| 44 |
+
|
| 45 |
+
def convert_from(self, a, K):
|
| 46 |
+
"""Convert a domain element from another domain to EXRAW"""
|
| 47 |
+
return K.to_sympy(a)
|
| 48 |
+
|
| 49 |
+
def get_field(self):
|
| 50 |
+
"""Returns a field associated with ``self``. """
|
| 51 |
+
return self
|
| 52 |
+
|
| 53 |
+
def sum(self, items):
|
| 54 |
+
return Add(*items)
|
| 55 |
+
|
| 56 |
+
|
| 57 |
+
EXRAW = ExpressionRawDomain()
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/field.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`Field` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.polys.domains.ring import Ring
|
| 5 |
+
from sympy.polys.polyerrors import NotReversible, DomainError
|
| 6 |
+
from sympy.utilities import public
|
| 7 |
+
|
| 8 |
+
@public
|
| 9 |
+
class Field(Ring):
|
| 10 |
+
"""Represents a field domain. """
|
| 11 |
+
|
| 12 |
+
is_Field = True
|
| 13 |
+
is_PID = True
|
| 14 |
+
|
| 15 |
+
def get_ring(self):
|
| 16 |
+
"""Returns a ring associated with ``self``. """
|
| 17 |
+
raise DomainError('there is no ring associated with %s' % self)
|
| 18 |
+
|
| 19 |
+
def get_field(self):
|
| 20 |
+
"""Returns a field associated with ``self``. """
|
| 21 |
+
return self
|
| 22 |
+
|
| 23 |
+
def exquo(self, a, b):
|
| 24 |
+
"""Exact quotient of ``a`` and ``b``, implies ``__truediv__``. """
|
| 25 |
+
return a / b
|
| 26 |
+
|
| 27 |
+
def quo(self, a, b):
|
| 28 |
+
"""Quotient of ``a`` and ``b``, implies ``__truediv__``. """
|
| 29 |
+
return a / b
|
| 30 |
+
|
| 31 |
+
def rem(self, a, b):
|
| 32 |
+
"""Remainder of ``a`` and ``b``, implies nothing. """
|
| 33 |
+
return self.zero
|
| 34 |
+
|
| 35 |
+
def div(self, a, b):
|
| 36 |
+
"""Division of ``a`` and ``b``, implies ``__truediv__``. """
|
| 37 |
+
return a / b, self.zero
|
| 38 |
+
|
| 39 |
+
def gcd(self, a, b):
|
| 40 |
+
"""
|
| 41 |
+
Returns GCD of ``a`` and ``b``.
|
| 42 |
+
|
| 43 |
+
This definition of GCD over fields allows to clear denominators
|
| 44 |
+
in `primitive()`.
|
| 45 |
+
|
| 46 |
+
Examples
|
| 47 |
+
========
|
| 48 |
+
|
| 49 |
+
>>> from sympy.polys.domains import QQ
|
| 50 |
+
>>> from sympy import S, gcd, primitive
|
| 51 |
+
>>> from sympy.abc import x
|
| 52 |
+
|
| 53 |
+
>>> QQ.gcd(QQ(2, 3), QQ(4, 9))
|
| 54 |
+
2/9
|
| 55 |
+
>>> gcd(S(2)/3, S(4)/9)
|
| 56 |
+
2/9
|
| 57 |
+
>>> primitive(2*x/3 + S(4)/9)
|
| 58 |
+
(2/9, 3*x + 2)
|
| 59 |
+
|
| 60 |
+
"""
|
| 61 |
+
try:
|
| 62 |
+
ring = self.get_ring()
|
| 63 |
+
except DomainError:
|
| 64 |
+
return self.one
|
| 65 |
+
|
| 66 |
+
p = ring.gcd(self.numer(a), self.numer(b))
|
| 67 |
+
q = ring.lcm(self.denom(a), self.denom(b))
|
| 68 |
+
|
| 69 |
+
return self.convert(p, ring)/q
|
| 70 |
+
|
| 71 |
+
def gcdex(self, a, b):
|
| 72 |
+
"""
|
| 73 |
+
Returns x, y, g such that a * x + b * y == g == gcd(a, b)
|
| 74 |
+
"""
|
| 75 |
+
d = self.gcd(a, b)
|
| 76 |
+
|
| 77 |
+
if a == self.zero:
|
| 78 |
+
if b == self.zero:
|
| 79 |
+
return self.zero, self.one, self.zero
|
| 80 |
+
else:
|
| 81 |
+
return self.zero, d/b, d
|
| 82 |
+
else:
|
| 83 |
+
return d/a, self.zero, d
|
| 84 |
+
|
| 85 |
+
def lcm(self, a, b):
|
| 86 |
+
"""
|
| 87 |
+
Returns LCM of ``a`` and ``b``.
|
| 88 |
+
|
| 89 |
+
>>> from sympy.polys.domains import QQ
|
| 90 |
+
>>> from sympy import S, lcm
|
| 91 |
+
|
| 92 |
+
>>> QQ.lcm(QQ(2, 3), QQ(4, 9))
|
| 93 |
+
4/3
|
| 94 |
+
>>> lcm(S(2)/3, S(4)/9)
|
| 95 |
+
4/3
|
| 96 |
+
|
| 97 |
+
"""
|
| 98 |
+
|
| 99 |
+
try:
|
| 100 |
+
ring = self.get_ring()
|
| 101 |
+
except DomainError:
|
| 102 |
+
return a*b
|
| 103 |
+
|
| 104 |
+
p = ring.lcm(self.numer(a), self.numer(b))
|
| 105 |
+
q = ring.gcd(self.denom(a), self.denom(b))
|
| 106 |
+
|
| 107 |
+
return self.convert(p, ring)/q
|
| 108 |
+
|
| 109 |
+
def revert(self, a):
|
| 110 |
+
"""Returns ``a**(-1)`` if possible. """
|
| 111 |
+
if a:
|
| 112 |
+
return 1/a
|
| 113 |
+
else:
|
| 114 |
+
raise NotReversible('zero is not reversible')
|
| 115 |
+
|
| 116 |
+
def is_unit(self, a):
|
| 117 |
+
"""Return true if ``a`` is a invertible"""
|
| 118 |
+
return bool(a)
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/finitefield.py
ADDED
|
@@ -0,0 +1,368 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`FiniteField` class. """
|
| 2 |
+
|
| 3 |
+
import operator
|
| 4 |
+
|
| 5 |
+
from sympy.external.gmpy import GROUND_TYPES
|
| 6 |
+
from sympy.utilities.decorator import doctest_depends_on
|
| 7 |
+
|
| 8 |
+
from sympy.core.numbers import int_valued
|
| 9 |
+
from sympy.polys.domains.field import Field
|
| 10 |
+
|
| 11 |
+
from sympy.polys.domains.modularinteger import ModularIntegerFactory
|
| 12 |
+
from sympy.polys.domains.simpledomain import SimpleDomain
|
| 13 |
+
from sympy.polys.galoistools import gf_zassenhaus, gf_irred_p_rabin
|
| 14 |
+
from sympy.polys.polyerrors import CoercionFailed
|
| 15 |
+
from sympy.utilities import public
|
| 16 |
+
from sympy.polys.domains.groundtypes import SymPyInteger
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
if GROUND_TYPES == 'flint':
|
| 20 |
+
__doctest_skip__ = ['FiniteField']
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
if GROUND_TYPES == 'flint':
|
| 24 |
+
import flint
|
| 25 |
+
# Don't use python-flint < 0.5.0 because nmod was missing some features in
|
| 26 |
+
# previous versions of python-flint and fmpz_mod was not yet added.
|
| 27 |
+
_major, _minor, *_ = flint.__version__.split('.')
|
| 28 |
+
if (int(_major), int(_minor)) < (0, 5):
|
| 29 |
+
flint = None
|
| 30 |
+
else:
|
| 31 |
+
flint = None
|
| 32 |
+
|
| 33 |
+
|
| 34 |
+
def _modular_int_factory_nmod(mod):
|
| 35 |
+
# nmod only recognises int
|
| 36 |
+
index = operator.index
|
| 37 |
+
mod = index(mod)
|
| 38 |
+
nmod = flint.nmod
|
| 39 |
+
nmod_poly = flint.nmod_poly
|
| 40 |
+
|
| 41 |
+
# flint's nmod is only for moduli up to 2^64-1 (on a 64-bit machine)
|
| 42 |
+
try:
|
| 43 |
+
nmod(0, mod)
|
| 44 |
+
except OverflowError:
|
| 45 |
+
return None, None
|
| 46 |
+
|
| 47 |
+
def ctx(x):
|
| 48 |
+
try:
|
| 49 |
+
return nmod(x, mod)
|
| 50 |
+
except TypeError:
|
| 51 |
+
return nmod(index(x), mod)
|
| 52 |
+
|
| 53 |
+
def poly_ctx(cs):
|
| 54 |
+
return nmod_poly(cs, mod)
|
| 55 |
+
|
| 56 |
+
return ctx, poly_ctx
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
def _modular_int_factory_fmpz_mod(mod):
|
| 60 |
+
index = operator.index
|
| 61 |
+
fctx = flint.fmpz_mod_ctx(mod)
|
| 62 |
+
fctx_poly = flint.fmpz_mod_poly_ctx(mod)
|
| 63 |
+
fmpz_mod_poly = flint.fmpz_mod_poly
|
| 64 |
+
|
| 65 |
+
def ctx(x):
|
| 66 |
+
try:
|
| 67 |
+
return fctx(x)
|
| 68 |
+
except TypeError:
|
| 69 |
+
# x might be Integer
|
| 70 |
+
return fctx(index(x))
|
| 71 |
+
|
| 72 |
+
def poly_ctx(cs):
|
| 73 |
+
return fmpz_mod_poly(cs, fctx_poly)
|
| 74 |
+
|
| 75 |
+
return ctx, poly_ctx
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
def _modular_int_factory(mod, dom, symmetric, self):
|
| 79 |
+
# Convert the modulus to ZZ
|
| 80 |
+
try:
|
| 81 |
+
mod = dom.convert(mod)
|
| 82 |
+
except CoercionFailed:
|
| 83 |
+
raise ValueError('modulus must be an integer, got %s' % mod)
|
| 84 |
+
|
| 85 |
+
ctx, poly_ctx, is_flint = None, None, False
|
| 86 |
+
|
| 87 |
+
# Don't use flint if the modulus is not prime as it often crashes.
|
| 88 |
+
if flint is not None and mod.is_prime():
|
| 89 |
+
|
| 90 |
+
is_flint = True
|
| 91 |
+
|
| 92 |
+
# Try to use flint's nmod first
|
| 93 |
+
ctx, poly_ctx = _modular_int_factory_nmod(mod)
|
| 94 |
+
|
| 95 |
+
if ctx is None:
|
| 96 |
+
# Use fmpz_mod for larger moduli
|
| 97 |
+
ctx, poly_ctx = _modular_int_factory_fmpz_mod(mod)
|
| 98 |
+
|
| 99 |
+
if ctx is None:
|
| 100 |
+
# Use the Python implementation if flint is not available or the
|
| 101 |
+
# modulus is not prime.
|
| 102 |
+
ctx = ModularIntegerFactory(mod, dom, symmetric, self)
|
| 103 |
+
poly_ctx = None # not used
|
| 104 |
+
|
| 105 |
+
return ctx, poly_ctx, is_flint
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
@public
|
| 109 |
+
@doctest_depends_on(modules=['python', 'gmpy'])
|
| 110 |
+
class FiniteField(Field, SimpleDomain):
|
| 111 |
+
r"""Finite field of prime order :ref:`GF(p)`
|
| 112 |
+
|
| 113 |
+
A :ref:`GF(p)` domain represents a `finite field`_ `\mathbb{F}_p` of prime
|
| 114 |
+
order as :py:class:`~.Domain` in the domain system (see
|
| 115 |
+
:ref:`polys-domainsintro`).
|
| 116 |
+
|
| 117 |
+
A :py:class:`~.Poly` created from an expression with integer
|
| 118 |
+
coefficients will have the domain :ref:`ZZ`. However, if the ``modulus=p``
|
| 119 |
+
option is given then the domain will be a finite field instead.
|
| 120 |
+
|
| 121 |
+
>>> from sympy import Poly, Symbol
|
| 122 |
+
>>> x = Symbol('x')
|
| 123 |
+
>>> p = Poly(x**2 + 1)
|
| 124 |
+
>>> p
|
| 125 |
+
Poly(x**2 + 1, x, domain='ZZ')
|
| 126 |
+
>>> p.domain
|
| 127 |
+
ZZ
|
| 128 |
+
>>> p2 = Poly(x**2 + 1, modulus=2)
|
| 129 |
+
>>> p2
|
| 130 |
+
Poly(x**2 + 1, x, modulus=2)
|
| 131 |
+
>>> p2.domain
|
| 132 |
+
GF(2)
|
| 133 |
+
|
| 134 |
+
It is possible to factorise a polynomial over :ref:`GF(p)` using the
|
| 135 |
+
modulus argument to :py:func:`~.factor` or by specifying the domain
|
| 136 |
+
explicitly. The domain can also be given as a string.
|
| 137 |
+
|
| 138 |
+
>>> from sympy import factor, GF
|
| 139 |
+
>>> factor(x**2 + 1)
|
| 140 |
+
x**2 + 1
|
| 141 |
+
>>> factor(x**2 + 1, modulus=2)
|
| 142 |
+
(x + 1)**2
|
| 143 |
+
>>> factor(x**2 + 1, domain=GF(2))
|
| 144 |
+
(x + 1)**2
|
| 145 |
+
>>> factor(x**2 + 1, domain='GF(2)')
|
| 146 |
+
(x + 1)**2
|
| 147 |
+
|
| 148 |
+
It is also possible to use :ref:`GF(p)` with the :py:func:`~.cancel`
|
| 149 |
+
and :py:func:`~.gcd` functions.
|
| 150 |
+
|
| 151 |
+
>>> from sympy import cancel, gcd
|
| 152 |
+
>>> cancel((x**2 + 1)/(x + 1))
|
| 153 |
+
(x**2 + 1)/(x + 1)
|
| 154 |
+
>>> cancel((x**2 + 1)/(x + 1), domain=GF(2))
|
| 155 |
+
x + 1
|
| 156 |
+
>>> gcd(x**2 + 1, x + 1)
|
| 157 |
+
1
|
| 158 |
+
>>> gcd(x**2 + 1, x + 1, domain=GF(2))
|
| 159 |
+
x + 1
|
| 160 |
+
|
| 161 |
+
When using the domain directly :ref:`GF(p)` can be used as a constructor
|
| 162 |
+
to create instances which then support the operations ``+,-,*,**,/``
|
| 163 |
+
|
| 164 |
+
>>> from sympy import GF
|
| 165 |
+
>>> K = GF(5)
|
| 166 |
+
>>> K
|
| 167 |
+
GF(5)
|
| 168 |
+
>>> x = K(3)
|
| 169 |
+
>>> y = K(2)
|
| 170 |
+
>>> x
|
| 171 |
+
3 mod 5
|
| 172 |
+
>>> y
|
| 173 |
+
2 mod 5
|
| 174 |
+
>>> x * y
|
| 175 |
+
1 mod 5
|
| 176 |
+
>>> x / y
|
| 177 |
+
4 mod 5
|
| 178 |
+
|
| 179 |
+
Notes
|
| 180 |
+
=====
|
| 181 |
+
|
| 182 |
+
It is also possible to create a :ref:`GF(p)` domain of **non-prime**
|
| 183 |
+
order but the resulting ring is **not** a field: it is just the ring of
|
| 184 |
+
the integers modulo ``n``.
|
| 185 |
+
|
| 186 |
+
>>> K = GF(9)
|
| 187 |
+
>>> z = K(3)
|
| 188 |
+
>>> z
|
| 189 |
+
3 mod 9
|
| 190 |
+
>>> z**2
|
| 191 |
+
0 mod 9
|
| 192 |
+
|
| 193 |
+
It would be good to have a proper implementation of prime power fields
|
| 194 |
+
(``GF(p**n)``) but these are not yet implemented in SymPY.
|
| 195 |
+
|
| 196 |
+
.. _finite field: https://en.wikipedia.org/wiki/Finite_field
|
| 197 |
+
"""
|
| 198 |
+
|
| 199 |
+
rep = 'FF'
|
| 200 |
+
alias = 'FF'
|
| 201 |
+
|
| 202 |
+
is_FiniteField = is_FF = True
|
| 203 |
+
is_Numerical = True
|
| 204 |
+
|
| 205 |
+
has_assoc_Ring = False
|
| 206 |
+
has_assoc_Field = True
|
| 207 |
+
|
| 208 |
+
dom = None
|
| 209 |
+
mod = None
|
| 210 |
+
|
| 211 |
+
def __init__(self, mod, symmetric=True):
|
| 212 |
+
from sympy.polys.domains import ZZ
|
| 213 |
+
dom = ZZ
|
| 214 |
+
|
| 215 |
+
if mod <= 0:
|
| 216 |
+
raise ValueError('modulus must be a positive integer, got %s' % mod)
|
| 217 |
+
|
| 218 |
+
ctx, poly_ctx, is_flint = _modular_int_factory(mod, dom, symmetric, self)
|
| 219 |
+
|
| 220 |
+
self.dtype = ctx
|
| 221 |
+
self._poly_ctx = poly_ctx
|
| 222 |
+
self._is_flint = is_flint
|
| 223 |
+
|
| 224 |
+
self.zero = self.dtype(0)
|
| 225 |
+
self.one = self.dtype(1)
|
| 226 |
+
self.dom = dom
|
| 227 |
+
self.mod = mod
|
| 228 |
+
self.sym = symmetric
|
| 229 |
+
self._tp = type(self.zero)
|
| 230 |
+
|
| 231 |
+
@property
|
| 232 |
+
def tp(self):
|
| 233 |
+
return self._tp
|
| 234 |
+
|
| 235 |
+
@property
|
| 236 |
+
def is_Field(self):
|
| 237 |
+
is_field = getattr(self, '_is_field', None)
|
| 238 |
+
if is_field is None:
|
| 239 |
+
from sympy.ntheory.primetest import isprime
|
| 240 |
+
self._is_field = is_field = isprime(self.mod)
|
| 241 |
+
return is_field
|
| 242 |
+
|
| 243 |
+
def __str__(self):
|
| 244 |
+
return 'GF(%s)' % self.mod
|
| 245 |
+
|
| 246 |
+
def __hash__(self):
|
| 247 |
+
return hash((self.__class__.__name__, self.dtype, self.mod, self.dom))
|
| 248 |
+
|
| 249 |
+
def __eq__(self, other):
|
| 250 |
+
"""Returns ``True`` if two domains are equivalent. """
|
| 251 |
+
return isinstance(other, FiniteField) and \
|
| 252 |
+
self.mod == other.mod and self.dom == other.dom
|
| 253 |
+
|
| 254 |
+
def characteristic(self):
|
| 255 |
+
"""Return the characteristic of this domain. """
|
| 256 |
+
return self.mod
|
| 257 |
+
|
| 258 |
+
def get_field(self):
|
| 259 |
+
"""Returns a field associated with ``self``. """
|
| 260 |
+
return self
|
| 261 |
+
|
| 262 |
+
def to_sympy(self, a):
|
| 263 |
+
"""Convert ``a`` to a SymPy object. """
|
| 264 |
+
return SymPyInteger(self.to_int(a))
|
| 265 |
+
|
| 266 |
+
def from_sympy(self, a):
|
| 267 |
+
"""Convert SymPy's Integer to SymPy's ``Integer``. """
|
| 268 |
+
if a.is_Integer:
|
| 269 |
+
return self.dtype(self.dom.dtype(int(a)))
|
| 270 |
+
elif int_valued(a):
|
| 271 |
+
return self.dtype(self.dom.dtype(int(a)))
|
| 272 |
+
else:
|
| 273 |
+
raise CoercionFailed("expected an integer, got %s" % a)
|
| 274 |
+
|
| 275 |
+
def to_int(self, a):
|
| 276 |
+
"""Convert ``val`` to a Python ``int`` object. """
|
| 277 |
+
aval = int(a)
|
| 278 |
+
if self.sym and aval > self.mod // 2:
|
| 279 |
+
aval -= self.mod
|
| 280 |
+
return aval
|
| 281 |
+
|
| 282 |
+
def is_positive(self, a):
|
| 283 |
+
"""Returns True if ``a`` is positive. """
|
| 284 |
+
return bool(a)
|
| 285 |
+
|
| 286 |
+
def is_nonnegative(self, a):
|
| 287 |
+
"""Returns True if ``a`` is non-negative. """
|
| 288 |
+
return True
|
| 289 |
+
|
| 290 |
+
def is_negative(self, a):
|
| 291 |
+
"""Returns True if ``a`` is negative. """
|
| 292 |
+
return False
|
| 293 |
+
|
| 294 |
+
def is_nonpositive(self, a):
|
| 295 |
+
"""Returns True if ``a`` is non-positive. """
|
| 296 |
+
return not a
|
| 297 |
+
|
| 298 |
+
def from_FF(K1, a, K0=None):
|
| 299 |
+
"""Convert ``ModularInteger(int)`` to ``dtype``. """
|
| 300 |
+
return K1.dtype(K1.dom.from_ZZ(int(a), K0.dom))
|
| 301 |
+
|
| 302 |
+
def from_FF_python(K1, a, K0=None):
|
| 303 |
+
"""Convert ``ModularInteger(int)`` to ``dtype``. """
|
| 304 |
+
return K1.dtype(K1.dom.from_ZZ_python(int(a), K0.dom))
|
| 305 |
+
|
| 306 |
+
def from_ZZ(K1, a, K0=None):
|
| 307 |
+
"""Convert Python's ``int`` to ``dtype``. """
|
| 308 |
+
return K1.dtype(K1.dom.from_ZZ_python(a, K0))
|
| 309 |
+
|
| 310 |
+
def from_ZZ_python(K1, a, K0=None):
|
| 311 |
+
"""Convert Python's ``int`` to ``dtype``. """
|
| 312 |
+
return K1.dtype(K1.dom.from_ZZ_python(a, K0))
|
| 313 |
+
|
| 314 |
+
def from_QQ(K1, a, K0=None):
|
| 315 |
+
"""Convert Python's ``Fraction`` to ``dtype``. """
|
| 316 |
+
if a.denominator == 1:
|
| 317 |
+
return K1.from_ZZ_python(a.numerator)
|
| 318 |
+
|
| 319 |
+
def from_QQ_python(K1, a, K0=None):
|
| 320 |
+
"""Convert Python's ``Fraction`` to ``dtype``. """
|
| 321 |
+
if a.denominator == 1:
|
| 322 |
+
return K1.from_ZZ_python(a.numerator)
|
| 323 |
+
|
| 324 |
+
def from_FF_gmpy(K1, a, K0=None):
|
| 325 |
+
"""Convert ``ModularInteger(mpz)`` to ``dtype``. """
|
| 326 |
+
return K1.dtype(K1.dom.from_ZZ_gmpy(a.val, K0.dom))
|
| 327 |
+
|
| 328 |
+
def from_ZZ_gmpy(K1, a, K0=None):
|
| 329 |
+
"""Convert GMPY's ``mpz`` to ``dtype``. """
|
| 330 |
+
return K1.dtype(K1.dom.from_ZZ_gmpy(a, K0))
|
| 331 |
+
|
| 332 |
+
def from_QQ_gmpy(K1, a, K0=None):
|
| 333 |
+
"""Convert GMPY's ``mpq`` to ``dtype``. """
|
| 334 |
+
if a.denominator == 1:
|
| 335 |
+
return K1.from_ZZ_gmpy(a.numerator)
|
| 336 |
+
|
| 337 |
+
def from_RealField(K1, a, K0):
|
| 338 |
+
"""Convert mpmath's ``mpf`` to ``dtype``. """
|
| 339 |
+
p, q = K0.to_rational(a)
|
| 340 |
+
|
| 341 |
+
if q == 1:
|
| 342 |
+
return K1.dtype(K1.dom.dtype(p))
|
| 343 |
+
|
| 344 |
+
def is_square(self, a):
|
| 345 |
+
"""Returns True if ``a`` is a quadratic residue modulo p. """
|
| 346 |
+
# a is not a square <=> x**2-a is irreducible
|
| 347 |
+
poly = [int(x) for x in [self.one, self.zero, -a]]
|
| 348 |
+
return not gf_irred_p_rabin(poly, self.mod, self.dom)
|
| 349 |
+
|
| 350 |
+
def exsqrt(self, a):
|
| 351 |
+
"""Square root modulo p of ``a`` if it is a quadratic residue.
|
| 352 |
+
|
| 353 |
+
Explanation
|
| 354 |
+
===========
|
| 355 |
+
Always returns the square root that is no larger than ``p // 2``.
|
| 356 |
+
"""
|
| 357 |
+
# x**2-a is not square-free if a=0 or the field is characteristic 2
|
| 358 |
+
if self.mod == 2 or a == 0:
|
| 359 |
+
return a
|
| 360 |
+
# Otherwise, use square-free factorization routine to factorize x**2-a
|
| 361 |
+
poly = [int(x) for x in [self.one, self.zero, -a]]
|
| 362 |
+
for factor in gf_zassenhaus(poly, self.mod, self.dom):
|
| 363 |
+
if len(factor) == 2 and factor[1] <= self.mod // 2:
|
| 364 |
+
return self.dtype(factor[1])
|
| 365 |
+
return None
|
| 366 |
+
|
| 367 |
+
|
| 368 |
+
FF = GF = FiniteField
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/fractionfield.py
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`FractionField` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.polys.domains.compositedomain import CompositeDomain
|
| 5 |
+
from sympy.polys.domains.field import Field
|
| 6 |
+
from sympy.polys.polyerrors import CoercionFailed, GeneratorsError
|
| 7 |
+
from sympy.utilities import public
|
| 8 |
+
|
| 9 |
+
@public
|
| 10 |
+
class FractionField(Field, CompositeDomain):
|
| 11 |
+
"""A class for representing multivariate rational function fields. """
|
| 12 |
+
|
| 13 |
+
is_FractionField = is_Frac = True
|
| 14 |
+
|
| 15 |
+
has_assoc_Ring = True
|
| 16 |
+
has_assoc_Field = True
|
| 17 |
+
|
| 18 |
+
def __init__(self, domain_or_field, symbols=None, order=None):
|
| 19 |
+
from sympy.polys.fields import FracField
|
| 20 |
+
|
| 21 |
+
if isinstance(domain_or_field, FracField) and symbols is None and order is None:
|
| 22 |
+
field = domain_or_field
|
| 23 |
+
else:
|
| 24 |
+
field = FracField(symbols, domain_or_field, order)
|
| 25 |
+
|
| 26 |
+
self.field = field
|
| 27 |
+
self.dtype = field.dtype
|
| 28 |
+
|
| 29 |
+
self.gens = field.gens
|
| 30 |
+
self.ngens = field.ngens
|
| 31 |
+
self.symbols = field.symbols
|
| 32 |
+
self.domain = field.domain
|
| 33 |
+
|
| 34 |
+
# TODO: remove this
|
| 35 |
+
self.dom = self.domain
|
| 36 |
+
|
| 37 |
+
def new(self, element):
|
| 38 |
+
return self.field.field_new(element)
|
| 39 |
+
|
| 40 |
+
def of_type(self, element):
|
| 41 |
+
"""Check if ``a`` is of type ``dtype``. """
|
| 42 |
+
return self.field.is_element(element)
|
| 43 |
+
|
| 44 |
+
@property
|
| 45 |
+
def zero(self):
|
| 46 |
+
return self.field.zero
|
| 47 |
+
|
| 48 |
+
@property
|
| 49 |
+
def one(self):
|
| 50 |
+
return self.field.one
|
| 51 |
+
|
| 52 |
+
@property
|
| 53 |
+
def order(self):
|
| 54 |
+
return self.field.order
|
| 55 |
+
|
| 56 |
+
def __str__(self):
|
| 57 |
+
return str(self.domain) + '(' + ','.join(map(str, self.symbols)) + ')'
|
| 58 |
+
|
| 59 |
+
def __hash__(self):
|
| 60 |
+
return hash((self.__class__.__name__, self.field, self.domain, self.symbols))
|
| 61 |
+
|
| 62 |
+
def __eq__(self, other):
|
| 63 |
+
"""Returns ``True`` if two domains are equivalent. """
|
| 64 |
+
if not isinstance(other, FractionField):
|
| 65 |
+
return NotImplemented
|
| 66 |
+
return self.field == other.field
|
| 67 |
+
|
| 68 |
+
def to_sympy(self, a):
|
| 69 |
+
"""Convert ``a`` to a SymPy object. """
|
| 70 |
+
return a.as_expr()
|
| 71 |
+
|
| 72 |
+
def from_sympy(self, a):
|
| 73 |
+
"""Convert SymPy's expression to ``dtype``. """
|
| 74 |
+
return self.field.from_expr(a)
|
| 75 |
+
|
| 76 |
+
def from_ZZ(K1, a, K0):
|
| 77 |
+
"""Convert a Python ``int`` object to ``dtype``. """
|
| 78 |
+
return K1(K1.domain.convert(a, K0))
|
| 79 |
+
|
| 80 |
+
def from_ZZ_python(K1, a, K0):
|
| 81 |
+
"""Convert a Python ``int`` object to ``dtype``. """
|
| 82 |
+
return K1(K1.domain.convert(a, K0))
|
| 83 |
+
|
| 84 |
+
def from_QQ(K1, a, K0):
|
| 85 |
+
"""Convert a Python ``Fraction`` object to ``dtype``. """
|
| 86 |
+
dom = K1.domain
|
| 87 |
+
conv = dom.convert_from
|
| 88 |
+
if dom.is_ZZ:
|
| 89 |
+
return K1(conv(K0.numer(a), K0)) / K1(conv(K0.denom(a), K0))
|
| 90 |
+
else:
|
| 91 |
+
return K1(conv(a, K0))
|
| 92 |
+
|
| 93 |
+
def from_QQ_python(K1, a, K0):
|
| 94 |
+
"""Convert a Python ``Fraction`` object to ``dtype``. """
|
| 95 |
+
return K1(K1.domain.convert(a, K0))
|
| 96 |
+
|
| 97 |
+
def from_ZZ_gmpy(K1, a, K0):
|
| 98 |
+
"""Convert a GMPY ``mpz`` object to ``dtype``. """
|
| 99 |
+
return K1(K1.domain.convert(a, K0))
|
| 100 |
+
|
| 101 |
+
def from_QQ_gmpy(K1, a, K0):
|
| 102 |
+
"""Convert a GMPY ``mpq`` object to ``dtype``. """
|
| 103 |
+
return K1(K1.domain.convert(a, K0))
|
| 104 |
+
|
| 105 |
+
def from_GaussianRationalField(K1, a, K0):
|
| 106 |
+
"""Convert a ``GaussianRational`` object to ``dtype``. """
|
| 107 |
+
return K1(K1.domain.convert(a, K0))
|
| 108 |
+
|
| 109 |
+
def from_GaussianIntegerRing(K1, a, K0):
|
| 110 |
+
"""Convert a ``GaussianInteger`` object to ``dtype``. """
|
| 111 |
+
return K1(K1.domain.convert(a, K0))
|
| 112 |
+
|
| 113 |
+
def from_RealField(K1, a, K0):
|
| 114 |
+
"""Convert a mpmath ``mpf`` object to ``dtype``. """
|
| 115 |
+
return K1(K1.domain.convert(a, K0))
|
| 116 |
+
|
| 117 |
+
def from_ComplexField(K1, a, K0):
|
| 118 |
+
"""Convert a mpmath ``mpf`` object to ``dtype``. """
|
| 119 |
+
return K1(K1.domain.convert(a, K0))
|
| 120 |
+
|
| 121 |
+
def from_AlgebraicField(K1, a, K0):
|
| 122 |
+
"""Convert an algebraic number to ``dtype``. """
|
| 123 |
+
if K1.domain != K0:
|
| 124 |
+
a = K1.domain.convert_from(a, K0)
|
| 125 |
+
if a is not None:
|
| 126 |
+
return K1.new(a)
|
| 127 |
+
|
| 128 |
+
def from_PolynomialRing(K1, a, K0):
|
| 129 |
+
"""Convert a polynomial to ``dtype``. """
|
| 130 |
+
if a.is_ground:
|
| 131 |
+
return K1.convert_from(a.coeff(1), K0.domain)
|
| 132 |
+
try:
|
| 133 |
+
return K1.new(a.set_ring(K1.field.ring))
|
| 134 |
+
except (CoercionFailed, GeneratorsError):
|
| 135 |
+
# XXX: We get here if K1=ZZ(x,y) and K0=QQ[x,y]
|
| 136 |
+
# and the poly a in K0 has non-integer coefficients.
|
| 137 |
+
# It seems that K1.new can handle this but K1.new doesn't work
|
| 138 |
+
# when K0.domain is an algebraic field...
|
| 139 |
+
try:
|
| 140 |
+
return K1.new(a)
|
| 141 |
+
except (CoercionFailed, GeneratorsError):
|
| 142 |
+
return None
|
| 143 |
+
|
| 144 |
+
def from_FractionField(K1, a, K0):
|
| 145 |
+
"""Convert a rational function to ``dtype``. """
|
| 146 |
+
try:
|
| 147 |
+
return a.set_field(K1.field)
|
| 148 |
+
except (CoercionFailed, GeneratorsError):
|
| 149 |
+
return None
|
| 150 |
+
|
| 151 |
+
def get_ring(self):
|
| 152 |
+
"""Returns a field associated with ``self``. """
|
| 153 |
+
return self.field.to_ring().to_domain()
|
| 154 |
+
|
| 155 |
+
def is_positive(self, a):
|
| 156 |
+
"""Returns True if ``LC(a)`` is positive. """
|
| 157 |
+
return self.domain.is_positive(a.numer.LC)
|
| 158 |
+
|
| 159 |
+
def is_negative(self, a):
|
| 160 |
+
"""Returns True if ``LC(a)`` is negative. """
|
| 161 |
+
return self.domain.is_negative(a.numer.LC)
|
| 162 |
+
|
| 163 |
+
def is_nonpositive(self, a):
|
| 164 |
+
"""Returns True if ``LC(a)`` is non-positive. """
|
| 165 |
+
return self.domain.is_nonpositive(a.numer.LC)
|
| 166 |
+
|
| 167 |
+
def is_nonnegative(self, a):
|
| 168 |
+
"""Returns True if ``LC(a)`` is non-negative. """
|
| 169 |
+
return self.domain.is_nonnegative(a.numer.LC)
|
| 170 |
+
|
| 171 |
+
def numer(self, a):
|
| 172 |
+
"""Returns numerator of ``a``. """
|
| 173 |
+
return a.numer
|
| 174 |
+
|
| 175 |
+
def denom(self, a):
|
| 176 |
+
"""Returns denominator of ``a``. """
|
| 177 |
+
return a.denom
|
| 178 |
+
|
| 179 |
+
def factorial(self, a):
|
| 180 |
+
"""Returns factorial of ``a``. """
|
| 181 |
+
return self.dtype(self.domain.factorial(a))
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/gaussiandomains.py
ADDED
|
@@ -0,0 +1,706 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Domains of Gaussian type."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
from sympy.core.numbers import I
|
| 5 |
+
from sympy.polys.polyclasses import DMP
|
| 6 |
+
from sympy.polys.polyerrors import CoercionFailed
|
| 7 |
+
from sympy.polys.domains.integerring import ZZ
|
| 8 |
+
from sympy.polys.domains.rationalfield import QQ
|
| 9 |
+
from sympy.polys.domains.algebraicfield import AlgebraicField
|
| 10 |
+
from sympy.polys.domains.domain import Domain
|
| 11 |
+
from sympy.polys.domains.domainelement import DomainElement
|
| 12 |
+
from sympy.polys.domains.field import Field
|
| 13 |
+
from sympy.polys.domains.ring import Ring
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
class GaussianElement(DomainElement):
|
| 17 |
+
"""Base class for elements of Gaussian type domains."""
|
| 18 |
+
base: Domain
|
| 19 |
+
_parent: Domain
|
| 20 |
+
|
| 21 |
+
__slots__ = ('x', 'y')
|
| 22 |
+
|
| 23 |
+
def __new__(cls, x, y=0):
|
| 24 |
+
conv = cls.base.convert
|
| 25 |
+
return cls.new(conv(x), conv(y))
|
| 26 |
+
|
| 27 |
+
@classmethod
|
| 28 |
+
def new(cls, x, y):
|
| 29 |
+
"""Create a new GaussianElement of the same domain."""
|
| 30 |
+
obj = super().__new__(cls)
|
| 31 |
+
obj.x = x
|
| 32 |
+
obj.y = y
|
| 33 |
+
return obj
|
| 34 |
+
|
| 35 |
+
def parent(self):
|
| 36 |
+
"""The domain that this is an element of (ZZ_I or QQ_I)"""
|
| 37 |
+
return self._parent
|
| 38 |
+
|
| 39 |
+
def __hash__(self):
|
| 40 |
+
return hash((self.x, self.y))
|
| 41 |
+
|
| 42 |
+
def __eq__(self, other):
|
| 43 |
+
if isinstance(other, self.__class__):
|
| 44 |
+
return self.x == other.x and self.y == other.y
|
| 45 |
+
else:
|
| 46 |
+
return NotImplemented
|
| 47 |
+
|
| 48 |
+
def __lt__(self, other):
|
| 49 |
+
if not isinstance(other, GaussianElement):
|
| 50 |
+
return NotImplemented
|
| 51 |
+
return [self.y, self.x] < [other.y, other.x]
|
| 52 |
+
|
| 53 |
+
def __pos__(self):
|
| 54 |
+
return self
|
| 55 |
+
|
| 56 |
+
def __neg__(self):
|
| 57 |
+
return self.new(-self.x, -self.y)
|
| 58 |
+
|
| 59 |
+
def __repr__(self):
|
| 60 |
+
return "%s(%s, %s)" % (self._parent.rep, self.x, self.y)
|
| 61 |
+
|
| 62 |
+
def __str__(self):
|
| 63 |
+
return str(self._parent.to_sympy(self))
|
| 64 |
+
|
| 65 |
+
@classmethod
|
| 66 |
+
def _get_xy(cls, other):
|
| 67 |
+
if not isinstance(other, cls):
|
| 68 |
+
try:
|
| 69 |
+
other = cls._parent.convert(other)
|
| 70 |
+
except CoercionFailed:
|
| 71 |
+
return None, None
|
| 72 |
+
return other.x, other.y
|
| 73 |
+
|
| 74 |
+
def __add__(self, other):
|
| 75 |
+
x, y = self._get_xy(other)
|
| 76 |
+
if x is not None:
|
| 77 |
+
return self.new(self.x + x, self.y + y)
|
| 78 |
+
else:
|
| 79 |
+
return NotImplemented
|
| 80 |
+
|
| 81 |
+
__radd__ = __add__
|
| 82 |
+
|
| 83 |
+
def __sub__(self, other):
|
| 84 |
+
x, y = self._get_xy(other)
|
| 85 |
+
if x is not None:
|
| 86 |
+
return self.new(self.x - x, self.y - y)
|
| 87 |
+
else:
|
| 88 |
+
return NotImplemented
|
| 89 |
+
|
| 90 |
+
def __rsub__(self, other):
|
| 91 |
+
x, y = self._get_xy(other)
|
| 92 |
+
if x is not None:
|
| 93 |
+
return self.new(x - self.x, y - self.y)
|
| 94 |
+
else:
|
| 95 |
+
return NotImplemented
|
| 96 |
+
|
| 97 |
+
def __mul__(self, other):
|
| 98 |
+
x, y = self._get_xy(other)
|
| 99 |
+
if x is not None:
|
| 100 |
+
return self.new(self.x*x - self.y*y, self.x*y + self.y*x)
|
| 101 |
+
else:
|
| 102 |
+
return NotImplemented
|
| 103 |
+
|
| 104 |
+
__rmul__ = __mul__
|
| 105 |
+
|
| 106 |
+
def __pow__(self, exp):
|
| 107 |
+
if exp == 0:
|
| 108 |
+
return self.new(1, 0)
|
| 109 |
+
if exp < 0:
|
| 110 |
+
self, exp = 1/self, -exp
|
| 111 |
+
if exp == 1:
|
| 112 |
+
return self
|
| 113 |
+
pow2 = self
|
| 114 |
+
prod = self if exp % 2 else self._parent.one
|
| 115 |
+
exp //= 2
|
| 116 |
+
while exp:
|
| 117 |
+
pow2 *= pow2
|
| 118 |
+
if exp % 2:
|
| 119 |
+
prod *= pow2
|
| 120 |
+
exp //= 2
|
| 121 |
+
return prod
|
| 122 |
+
|
| 123 |
+
def __bool__(self):
|
| 124 |
+
return bool(self.x) or bool(self.y)
|
| 125 |
+
|
| 126 |
+
def quadrant(self):
|
| 127 |
+
"""Return quadrant index 0-3.
|
| 128 |
+
|
| 129 |
+
0 is included in quadrant 0.
|
| 130 |
+
"""
|
| 131 |
+
if self.y > 0:
|
| 132 |
+
return 0 if self.x > 0 else 1
|
| 133 |
+
elif self.y < 0:
|
| 134 |
+
return 2 if self.x < 0 else 3
|
| 135 |
+
else:
|
| 136 |
+
return 0 if self.x >= 0 else 2
|
| 137 |
+
|
| 138 |
+
def __rdivmod__(self, other):
|
| 139 |
+
try:
|
| 140 |
+
other = self._parent.convert(other)
|
| 141 |
+
except CoercionFailed:
|
| 142 |
+
return NotImplemented
|
| 143 |
+
else:
|
| 144 |
+
return other.__divmod__(self)
|
| 145 |
+
|
| 146 |
+
def __rtruediv__(self, other):
|
| 147 |
+
try:
|
| 148 |
+
other = QQ_I.convert(other)
|
| 149 |
+
except CoercionFailed:
|
| 150 |
+
return NotImplemented
|
| 151 |
+
else:
|
| 152 |
+
return other.__truediv__(self)
|
| 153 |
+
|
| 154 |
+
def __floordiv__(self, other):
|
| 155 |
+
qr = self.__divmod__(other)
|
| 156 |
+
return qr if qr is NotImplemented else qr[0]
|
| 157 |
+
|
| 158 |
+
def __rfloordiv__(self, other):
|
| 159 |
+
qr = self.__rdivmod__(other)
|
| 160 |
+
return qr if qr is NotImplemented else qr[0]
|
| 161 |
+
|
| 162 |
+
def __mod__(self, other):
|
| 163 |
+
qr = self.__divmod__(other)
|
| 164 |
+
return qr if qr is NotImplemented else qr[1]
|
| 165 |
+
|
| 166 |
+
def __rmod__(self, other):
|
| 167 |
+
qr = self.__rdivmod__(other)
|
| 168 |
+
return qr if qr is NotImplemented else qr[1]
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
class GaussianInteger(GaussianElement):
|
| 172 |
+
"""Gaussian integer: domain element for :ref:`ZZ_I`
|
| 173 |
+
|
| 174 |
+
>>> from sympy import ZZ_I
|
| 175 |
+
>>> z = ZZ_I(2, 3)
|
| 176 |
+
>>> z
|
| 177 |
+
(2 + 3*I)
|
| 178 |
+
>>> type(z)
|
| 179 |
+
<class 'sympy.polys.domains.gaussiandomains.GaussianInteger'>
|
| 180 |
+
"""
|
| 181 |
+
base = ZZ
|
| 182 |
+
|
| 183 |
+
def __truediv__(self, other):
|
| 184 |
+
"""Return a Gaussian rational."""
|
| 185 |
+
return QQ_I.convert(self)/other
|
| 186 |
+
|
| 187 |
+
def __divmod__(self, other):
|
| 188 |
+
if not other:
|
| 189 |
+
raise ZeroDivisionError('divmod({}, 0)'.format(self))
|
| 190 |
+
x, y = self._get_xy(other)
|
| 191 |
+
if x is None:
|
| 192 |
+
return NotImplemented
|
| 193 |
+
|
| 194 |
+
# multiply self and other by x - I*y
|
| 195 |
+
# self/other == (a + I*b)/c
|
| 196 |
+
a, b = self.x*x + self.y*y, -self.x*y + self.y*x
|
| 197 |
+
c = x*x + y*y
|
| 198 |
+
|
| 199 |
+
# find integers qx and qy such that
|
| 200 |
+
# |a - qx*c| <= c/2 and |b - qy*c| <= c/2
|
| 201 |
+
qx = (2*a + c) // (2*c) # -c <= 2*a - qx*2*c < c
|
| 202 |
+
qy = (2*b + c) // (2*c)
|
| 203 |
+
|
| 204 |
+
q = GaussianInteger(qx, qy)
|
| 205 |
+
# |self/other - q| < 1 since
|
| 206 |
+
# |a/c - qx|**2 + |b/c - qy|**2 <= 1/4 + 1/4 < 1
|
| 207 |
+
|
| 208 |
+
return q, self - q*other # |r| < |other|
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
class GaussianRational(GaussianElement):
|
| 212 |
+
"""Gaussian rational: domain element for :ref:`QQ_I`
|
| 213 |
+
|
| 214 |
+
>>> from sympy import QQ_I, QQ
|
| 215 |
+
>>> z = QQ_I(QQ(2, 3), QQ(4, 5))
|
| 216 |
+
>>> z
|
| 217 |
+
(2/3 + 4/5*I)
|
| 218 |
+
>>> type(z)
|
| 219 |
+
<class 'sympy.polys.domains.gaussiandomains.GaussianRational'>
|
| 220 |
+
"""
|
| 221 |
+
base = QQ
|
| 222 |
+
|
| 223 |
+
def __truediv__(self, other):
|
| 224 |
+
"""Return a Gaussian rational."""
|
| 225 |
+
if not other:
|
| 226 |
+
raise ZeroDivisionError('{} / 0'.format(self))
|
| 227 |
+
x, y = self._get_xy(other)
|
| 228 |
+
if x is None:
|
| 229 |
+
return NotImplemented
|
| 230 |
+
c = x*x + y*y
|
| 231 |
+
|
| 232 |
+
return GaussianRational((self.x*x + self.y*y)/c,
|
| 233 |
+
(-self.x*y + self.y*x)/c)
|
| 234 |
+
|
| 235 |
+
def __divmod__(self, other):
|
| 236 |
+
try:
|
| 237 |
+
other = self._parent.convert(other)
|
| 238 |
+
except CoercionFailed:
|
| 239 |
+
return NotImplemented
|
| 240 |
+
if not other:
|
| 241 |
+
raise ZeroDivisionError('{} % 0'.format(self))
|
| 242 |
+
else:
|
| 243 |
+
return self/other, QQ_I.zero
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
class GaussianDomain():
|
| 247 |
+
"""Base class for Gaussian domains."""
|
| 248 |
+
dom: Domain
|
| 249 |
+
|
| 250 |
+
is_Numerical = True
|
| 251 |
+
is_Exact = True
|
| 252 |
+
|
| 253 |
+
has_assoc_Ring = True
|
| 254 |
+
has_assoc_Field = True
|
| 255 |
+
|
| 256 |
+
def to_sympy(self, a):
|
| 257 |
+
"""Convert ``a`` to a SymPy object. """
|
| 258 |
+
conv = self.dom.to_sympy
|
| 259 |
+
return conv(a.x) + I*conv(a.y)
|
| 260 |
+
|
| 261 |
+
def from_sympy(self, a):
|
| 262 |
+
"""Convert a SymPy object to ``self.dtype``."""
|
| 263 |
+
r, b = a.as_coeff_Add()
|
| 264 |
+
x = self.dom.from_sympy(r) # may raise CoercionFailed
|
| 265 |
+
if not b:
|
| 266 |
+
return self.new(x, 0)
|
| 267 |
+
r, b = b.as_coeff_Mul()
|
| 268 |
+
y = self.dom.from_sympy(r)
|
| 269 |
+
if b is I:
|
| 270 |
+
return self.new(x, y)
|
| 271 |
+
else:
|
| 272 |
+
raise CoercionFailed("{} is not Gaussian".format(a))
|
| 273 |
+
|
| 274 |
+
def inject(self, *gens):
|
| 275 |
+
"""Inject generators into this domain. """
|
| 276 |
+
return self.poly_ring(*gens)
|
| 277 |
+
|
| 278 |
+
def canonical_unit(self, d):
|
| 279 |
+
unit = self.units[-d.quadrant()] # - for inverse power
|
| 280 |
+
return unit
|
| 281 |
+
|
| 282 |
+
def is_negative(self, element):
|
| 283 |
+
"""Returns ``False`` for any ``GaussianElement``. """
|
| 284 |
+
return False
|
| 285 |
+
|
| 286 |
+
def is_positive(self, element):
|
| 287 |
+
"""Returns ``False`` for any ``GaussianElement``. """
|
| 288 |
+
return False
|
| 289 |
+
|
| 290 |
+
def is_nonnegative(self, element):
|
| 291 |
+
"""Returns ``False`` for any ``GaussianElement``. """
|
| 292 |
+
return False
|
| 293 |
+
|
| 294 |
+
def is_nonpositive(self, element):
|
| 295 |
+
"""Returns ``False`` for any ``GaussianElement``. """
|
| 296 |
+
return False
|
| 297 |
+
|
| 298 |
+
def from_ZZ_gmpy(K1, a, K0):
|
| 299 |
+
"""Convert a GMPY mpz to ``self.dtype``."""
|
| 300 |
+
return K1(a)
|
| 301 |
+
|
| 302 |
+
def from_ZZ(K1, a, K0):
|
| 303 |
+
"""Convert a ZZ_python element to ``self.dtype``."""
|
| 304 |
+
return K1(a)
|
| 305 |
+
|
| 306 |
+
def from_ZZ_python(K1, a, K0):
|
| 307 |
+
"""Convert a ZZ_python element to ``self.dtype``."""
|
| 308 |
+
return K1(a)
|
| 309 |
+
|
| 310 |
+
def from_QQ(K1, a, K0):
|
| 311 |
+
"""Convert a GMPY mpq to ``self.dtype``."""
|
| 312 |
+
return K1(a)
|
| 313 |
+
|
| 314 |
+
def from_QQ_gmpy(K1, a, K0):
|
| 315 |
+
"""Convert a GMPY mpq to ``self.dtype``."""
|
| 316 |
+
return K1(a)
|
| 317 |
+
|
| 318 |
+
def from_QQ_python(K1, a, K0):
|
| 319 |
+
"""Convert a QQ_python element to ``self.dtype``."""
|
| 320 |
+
return K1(a)
|
| 321 |
+
|
| 322 |
+
def from_AlgebraicField(K1, a, K0):
|
| 323 |
+
"""Convert an element from ZZ<I> or QQ<I> to ``self.dtype``."""
|
| 324 |
+
if K0.ext.args[0] == I:
|
| 325 |
+
return K1.from_sympy(K0.to_sympy(a))
|
| 326 |
+
|
| 327 |
+
|
| 328 |
+
class GaussianIntegerRing(GaussianDomain, Ring):
|
| 329 |
+
r"""Ring of Gaussian integers ``ZZ_I``
|
| 330 |
+
|
| 331 |
+
The :ref:`ZZ_I` domain represents the `Gaussian integers`_ `\mathbb{Z}[i]`
|
| 332 |
+
as a :py:class:`~.Domain` in the domain system (see
|
| 333 |
+
:ref:`polys-domainsintro`).
|
| 334 |
+
|
| 335 |
+
By default a :py:class:`~.Poly` created from an expression with
|
| 336 |
+
coefficients that are combinations of integers and ``I`` (`\sqrt{-1}`)
|
| 337 |
+
will have the domain :ref:`ZZ_I`.
|
| 338 |
+
|
| 339 |
+
>>> from sympy import Poly, Symbol, I
|
| 340 |
+
>>> x = Symbol('x')
|
| 341 |
+
>>> p = Poly(x**2 + I)
|
| 342 |
+
>>> p
|
| 343 |
+
Poly(x**2 + I, x, domain='ZZ_I')
|
| 344 |
+
>>> p.domain
|
| 345 |
+
ZZ_I
|
| 346 |
+
|
| 347 |
+
The :ref:`ZZ_I` domain can be used to factorise polynomials that are
|
| 348 |
+
reducible over the Gaussian integers.
|
| 349 |
+
|
| 350 |
+
>>> from sympy import factor
|
| 351 |
+
>>> factor(x**2 + 1)
|
| 352 |
+
x**2 + 1
|
| 353 |
+
>>> factor(x**2 + 1, domain='ZZ_I')
|
| 354 |
+
(x - I)*(x + I)
|
| 355 |
+
|
| 356 |
+
The corresponding `field of fractions`_ is the domain of the Gaussian
|
| 357 |
+
rationals :ref:`QQ_I`. Conversely :ref:`ZZ_I` is the `ring of integers`_
|
| 358 |
+
of :ref:`QQ_I`.
|
| 359 |
+
|
| 360 |
+
>>> from sympy import ZZ_I, QQ_I
|
| 361 |
+
>>> ZZ_I.get_field()
|
| 362 |
+
QQ_I
|
| 363 |
+
>>> QQ_I.get_ring()
|
| 364 |
+
ZZ_I
|
| 365 |
+
|
| 366 |
+
When using the domain directly :ref:`ZZ_I` can be used as a constructor.
|
| 367 |
+
|
| 368 |
+
>>> ZZ_I(3, 4)
|
| 369 |
+
(3 + 4*I)
|
| 370 |
+
>>> ZZ_I(5)
|
| 371 |
+
(5 + 0*I)
|
| 372 |
+
|
| 373 |
+
The domain elements of :ref:`ZZ_I` are instances of
|
| 374 |
+
:py:class:`~.GaussianInteger` which support the rings operations
|
| 375 |
+
``+,-,*,**``.
|
| 376 |
+
|
| 377 |
+
>>> z1 = ZZ_I(5, 1)
|
| 378 |
+
>>> z2 = ZZ_I(2, 3)
|
| 379 |
+
>>> z1
|
| 380 |
+
(5 + 1*I)
|
| 381 |
+
>>> z2
|
| 382 |
+
(2 + 3*I)
|
| 383 |
+
>>> z1 + z2
|
| 384 |
+
(7 + 4*I)
|
| 385 |
+
>>> z1 * z2
|
| 386 |
+
(7 + 17*I)
|
| 387 |
+
>>> z1 ** 2
|
| 388 |
+
(24 + 10*I)
|
| 389 |
+
|
| 390 |
+
Both floor (``//``) and modulo (``%``) division work with
|
| 391 |
+
:py:class:`~.GaussianInteger` (see the :py:meth:`~.Domain.div` method).
|
| 392 |
+
|
| 393 |
+
>>> z3, z4 = ZZ_I(5), ZZ_I(1, 3)
|
| 394 |
+
>>> z3 // z4 # floor division
|
| 395 |
+
(1 + -1*I)
|
| 396 |
+
>>> z3 % z4 # modulo division (remainder)
|
| 397 |
+
(1 + -2*I)
|
| 398 |
+
>>> (z3//z4)*z4 + z3%z4 == z3
|
| 399 |
+
True
|
| 400 |
+
|
| 401 |
+
True division (``/``) in :ref:`ZZ_I` gives an element of :ref:`QQ_I`. The
|
| 402 |
+
:py:meth:`~.Domain.exquo` method can be used to divide in :ref:`ZZ_I` when
|
| 403 |
+
exact division is possible.
|
| 404 |
+
|
| 405 |
+
>>> z1 / z2
|
| 406 |
+
(1 + -1*I)
|
| 407 |
+
>>> ZZ_I.exquo(z1, z2)
|
| 408 |
+
(1 + -1*I)
|
| 409 |
+
>>> z3 / z4
|
| 410 |
+
(1/2 + -3/2*I)
|
| 411 |
+
>>> ZZ_I.exquo(z3, z4)
|
| 412 |
+
Traceback (most recent call last):
|
| 413 |
+
...
|
| 414 |
+
ExactQuotientFailed: (1 + 3*I) does not divide (5 + 0*I) in ZZ_I
|
| 415 |
+
|
| 416 |
+
The :py:meth:`~.Domain.gcd` method can be used to compute the `gcd`_ of any
|
| 417 |
+
two elements.
|
| 418 |
+
|
| 419 |
+
>>> ZZ_I.gcd(ZZ_I(10), ZZ_I(2))
|
| 420 |
+
(2 + 0*I)
|
| 421 |
+
>>> ZZ_I.gcd(ZZ_I(5), ZZ_I(2, 1))
|
| 422 |
+
(2 + 1*I)
|
| 423 |
+
|
| 424 |
+
.. _Gaussian integers: https://en.wikipedia.org/wiki/Gaussian_integer
|
| 425 |
+
.. _gcd: https://en.wikipedia.org/wiki/Greatest_common_divisor
|
| 426 |
+
|
| 427 |
+
"""
|
| 428 |
+
dom = ZZ
|
| 429 |
+
mod = DMP([ZZ.one, ZZ.zero, ZZ.one], ZZ)
|
| 430 |
+
dtype = GaussianInteger
|
| 431 |
+
zero = dtype(ZZ(0), ZZ(0))
|
| 432 |
+
one = dtype(ZZ(1), ZZ(0))
|
| 433 |
+
imag_unit = dtype(ZZ(0), ZZ(1))
|
| 434 |
+
units = (one, imag_unit, -one, -imag_unit) # powers of i
|
| 435 |
+
|
| 436 |
+
rep = 'ZZ_I'
|
| 437 |
+
|
| 438 |
+
is_GaussianRing = True
|
| 439 |
+
is_ZZ_I = True
|
| 440 |
+
is_PID = True
|
| 441 |
+
|
| 442 |
+
def __init__(self): # override Domain.__init__
|
| 443 |
+
"""For constructing ZZ_I."""
|
| 444 |
+
|
| 445 |
+
def __eq__(self, other):
|
| 446 |
+
"""Returns ``True`` if two domains are equivalent. """
|
| 447 |
+
if isinstance(other, GaussianIntegerRing):
|
| 448 |
+
return True
|
| 449 |
+
else:
|
| 450 |
+
return NotImplemented
|
| 451 |
+
|
| 452 |
+
def __hash__(self):
|
| 453 |
+
"""Compute hash code of ``self``. """
|
| 454 |
+
return hash('ZZ_I')
|
| 455 |
+
|
| 456 |
+
@property
|
| 457 |
+
def has_CharacteristicZero(self):
|
| 458 |
+
return True
|
| 459 |
+
|
| 460 |
+
def characteristic(self):
|
| 461 |
+
return 0
|
| 462 |
+
|
| 463 |
+
def get_ring(self):
|
| 464 |
+
"""Returns a ring associated with ``self``. """
|
| 465 |
+
return self
|
| 466 |
+
|
| 467 |
+
def get_field(self):
|
| 468 |
+
"""Returns a field associated with ``self``. """
|
| 469 |
+
return QQ_I
|
| 470 |
+
|
| 471 |
+
def normalize(self, d, *args):
|
| 472 |
+
"""Return first quadrant element associated with ``d``.
|
| 473 |
+
|
| 474 |
+
Also multiply the other arguments by the same power of i.
|
| 475 |
+
"""
|
| 476 |
+
unit = self.canonical_unit(d)
|
| 477 |
+
d *= unit
|
| 478 |
+
args = tuple(a*unit for a in args)
|
| 479 |
+
return (d,) + args if args else d
|
| 480 |
+
|
| 481 |
+
def gcd(self, a, b):
|
| 482 |
+
"""Greatest common divisor of a and b over ZZ_I."""
|
| 483 |
+
while b:
|
| 484 |
+
a, b = b, a % b
|
| 485 |
+
return self.normalize(a)
|
| 486 |
+
|
| 487 |
+
def gcdex(self, a, b):
|
| 488 |
+
"""Return x, y, g such that x * a + y * b = g = gcd(a, b)"""
|
| 489 |
+
x_a = self.one
|
| 490 |
+
x_b = self.zero
|
| 491 |
+
y_a = self.zero
|
| 492 |
+
y_b = self.one
|
| 493 |
+
while b:
|
| 494 |
+
q = a // b
|
| 495 |
+
a, b = b, a - q * b
|
| 496 |
+
x_a, x_b = x_b, x_a - q * x_b
|
| 497 |
+
y_a, y_b = y_b, y_a - q * y_b
|
| 498 |
+
|
| 499 |
+
a, x_a, y_a = self.normalize(a, x_a, y_a)
|
| 500 |
+
return x_a, y_a, a
|
| 501 |
+
|
| 502 |
+
def lcm(self, a, b):
|
| 503 |
+
"""Least common multiple of a and b over ZZ_I."""
|
| 504 |
+
return (a * b) // self.gcd(a, b)
|
| 505 |
+
|
| 506 |
+
def from_GaussianIntegerRing(K1, a, K0):
|
| 507 |
+
"""Convert a ZZ_I element to ZZ_I."""
|
| 508 |
+
return a
|
| 509 |
+
|
| 510 |
+
def from_GaussianRationalField(K1, a, K0):
|
| 511 |
+
"""Convert a QQ_I element to ZZ_I."""
|
| 512 |
+
return K1.new(ZZ.convert(a.x), ZZ.convert(a.y))
|
| 513 |
+
|
| 514 |
+
ZZ_I = GaussianInteger._parent = GaussianIntegerRing()
|
| 515 |
+
|
| 516 |
+
|
| 517 |
+
class GaussianRationalField(GaussianDomain, Field):
|
| 518 |
+
r"""Field of Gaussian rationals ``QQ_I``
|
| 519 |
+
|
| 520 |
+
The :ref:`QQ_I` domain represents the `Gaussian rationals`_ `\mathbb{Q}(i)`
|
| 521 |
+
as a :py:class:`~.Domain` in the domain system (see
|
| 522 |
+
:ref:`polys-domainsintro`).
|
| 523 |
+
|
| 524 |
+
By default a :py:class:`~.Poly` created from an expression with
|
| 525 |
+
coefficients that are combinations of rationals and ``I`` (`\sqrt{-1}`)
|
| 526 |
+
will have the domain :ref:`QQ_I`.
|
| 527 |
+
|
| 528 |
+
>>> from sympy import Poly, Symbol, I
|
| 529 |
+
>>> x = Symbol('x')
|
| 530 |
+
>>> p = Poly(x**2 + I/2)
|
| 531 |
+
>>> p
|
| 532 |
+
Poly(x**2 + I/2, x, domain='QQ_I')
|
| 533 |
+
>>> p.domain
|
| 534 |
+
QQ_I
|
| 535 |
+
|
| 536 |
+
The polys option ``gaussian=True`` can be used to specify that the domain
|
| 537 |
+
should be :ref:`QQ_I` even if the coefficients do not contain ``I`` or are
|
| 538 |
+
all integers.
|
| 539 |
+
|
| 540 |
+
>>> Poly(x**2)
|
| 541 |
+
Poly(x**2, x, domain='ZZ')
|
| 542 |
+
>>> Poly(x**2 + I)
|
| 543 |
+
Poly(x**2 + I, x, domain='ZZ_I')
|
| 544 |
+
>>> Poly(x**2/2)
|
| 545 |
+
Poly(1/2*x**2, x, domain='QQ')
|
| 546 |
+
>>> Poly(x**2, gaussian=True)
|
| 547 |
+
Poly(x**2, x, domain='QQ_I')
|
| 548 |
+
>>> Poly(x**2 + I, gaussian=True)
|
| 549 |
+
Poly(x**2 + I, x, domain='QQ_I')
|
| 550 |
+
>>> Poly(x**2/2, gaussian=True)
|
| 551 |
+
Poly(1/2*x**2, x, domain='QQ_I')
|
| 552 |
+
|
| 553 |
+
The :ref:`QQ_I` domain can be used to factorise polynomials that are
|
| 554 |
+
reducible over the Gaussian rationals.
|
| 555 |
+
|
| 556 |
+
>>> from sympy import factor, QQ_I
|
| 557 |
+
>>> factor(x**2/4 + 1)
|
| 558 |
+
(x**2 + 4)/4
|
| 559 |
+
>>> factor(x**2/4 + 1, domain='QQ_I')
|
| 560 |
+
(x - 2*I)*(x + 2*I)/4
|
| 561 |
+
>>> factor(x**2/4 + 1, domain=QQ_I)
|
| 562 |
+
(x - 2*I)*(x + 2*I)/4
|
| 563 |
+
|
| 564 |
+
It is also possible to specify the :ref:`QQ_I` domain explicitly with
|
| 565 |
+
polys functions like :py:func:`~.apart`.
|
| 566 |
+
|
| 567 |
+
>>> from sympy import apart
|
| 568 |
+
>>> apart(1/(1 + x**2))
|
| 569 |
+
1/(x**2 + 1)
|
| 570 |
+
>>> apart(1/(1 + x**2), domain=QQ_I)
|
| 571 |
+
I/(2*(x + I)) - I/(2*(x - I))
|
| 572 |
+
|
| 573 |
+
The corresponding `ring of integers`_ is the domain of the Gaussian
|
| 574 |
+
integers :ref:`ZZ_I`. Conversely :ref:`QQ_I` is the `field of fractions`_
|
| 575 |
+
of :ref:`ZZ_I`.
|
| 576 |
+
|
| 577 |
+
>>> from sympy import ZZ_I, QQ_I, QQ
|
| 578 |
+
>>> ZZ_I.get_field()
|
| 579 |
+
QQ_I
|
| 580 |
+
>>> QQ_I.get_ring()
|
| 581 |
+
ZZ_I
|
| 582 |
+
|
| 583 |
+
When using the domain directly :ref:`QQ_I` can be used as a constructor.
|
| 584 |
+
|
| 585 |
+
>>> QQ_I(3, 4)
|
| 586 |
+
(3 + 4*I)
|
| 587 |
+
>>> QQ_I(5)
|
| 588 |
+
(5 + 0*I)
|
| 589 |
+
>>> QQ_I(QQ(2, 3), QQ(4, 5))
|
| 590 |
+
(2/3 + 4/5*I)
|
| 591 |
+
|
| 592 |
+
The domain elements of :ref:`QQ_I` are instances of
|
| 593 |
+
:py:class:`~.GaussianRational` which support the field operations
|
| 594 |
+
``+,-,*,**,/``.
|
| 595 |
+
|
| 596 |
+
>>> z1 = QQ_I(5, 1)
|
| 597 |
+
>>> z2 = QQ_I(2, QQ(1, 2))
|
| 598 |
+
>>> z1
|
| 599 |
+
(5 + 1*I)
|
| 600 |
+
>>> z2
|
| 601 |
+
(2 + 1/2*I)
|
| 602 |
+
>>> z1 + z2
|
| 603 |
+
(7 + 3/2*I)
|
| 604 |
+
>>> z1 * z2
|
| 605 |
+
(19/2 + 9/2*I)
|
| 606 |
+
>>> z2 ** 2
|
| 607 |
+
(15/4 + 2*I)
|
| 608 |
+
|
| 609 |
+
True division (``/``) in :ref:`QQ_I` gives an element of :ref:`QQ_I` and
|
| 610 |
+
is always exact.
|
| 611 |
+
|
| 612 |
+
>>> z1 / z2
|
| 613 |
+
(42/17 + -2/17*I)
|
| 614 |
+
>>> QQ_I.exquo(z1, z2)
|
| 615 |
+
(42/17 + -2/17*I)
|
| 616 |
+
>>> z1 == (z1/z2)*z2
|
| 617 |
+
True
|
| 618 |
+
|
| 619 |
+
Both floor (``//``) and modulo (``%``) division can be used with
|
| 620 |
+
:py:class:`~.GaussianRational` (see :py:meth:`~.Domain.div`)
|
| 621 |
+
but division is always exact so there is no remainder.
|
| 622 |
+
|
| 623 |
+
>>> z1 // z2
|
| 624 |
+
(42/17 + -2/17*I)
|
| 625 |
+
>>> z1 % z2
|
| 626 |
+
(0 + 0*I)
|
| 627 |
+
>>> QQ_I.div(z1, z2)
|
| 628 |
+
((42/17 + -2/17*I), (0 + 0*I))
|
| 629 |
+
>>> (z1//z2)*z2 + z1%z2 == z1
|
| 630 |
+
True
|
| 631 |
+
|
| 632 |
+
.. _Gaussian rationals: https://en.wikipedia.org/wiki/Gaussian_rational
|
| 633 |
+
"""
|
| 634 |
+
dom = QQ
|
| 635 |
+
mod = DMP([QQ.one, QQ.zero, QQ.one], QQ)
|
| 636 |
+
dtype = GaussianRational
|
| 637 |
+
zero = dtype(QQ(0), QQ(0))
|
| 638 |
+
one = dtype(QQ(1), QQ(0))
|
| 639 |
+
imag_unit = dtype(QQ(0), QQ(1))
|
| 640 |
+
units = (one, imag_unit, -one, -imag_unit) # powers of i
|
| 641 |
+
|
| 642 |
+
rep = 'QQ_I'
|
| 643 |
+
|
| 644 |
+
is_GaussianField = True
|
| 645 |
+
is_QQ_I = True
|
| 646 |
+
|
| 647 |
+
def __init__(self): # override Domain.__init__
|
| 648 |
+
"""For constructing QQ_I."""
|
| 649 |
+
|
| 650 |
+
def __eq__(self, other):
|
| 651 |
+
"""Returns ``True`` if two domains are equivalent. """
|
| 652 |
+
if isinstance(other, GaussianRationalField):
|
| 653 |
+
return True
|
| 654 |
+
else:
|
| 655 |
+
return NotImplemented
|
| 656 |
+
|
| 657 |
+
def __hash__(self):
|
| 658 |
+
"""Compute hash code of ``self``. """
|
| 659 |
+
return hash('QQ_I')
|
| 660 |
+
|
| 661 |
+
@property
|
| 662 |
+
def has_CharacteristicZero(self):
|
| 663 |
+
return True
|
| 664 |
+
|
| 665 |
+
def characteristic(self):
|
| 666 |
+
return 0
|
| 667 |
+
|
| 668 |
+
def get_ring(self):
|
| 669 |
+
"""Returns a ring associated with ``self``. """
|
| 670 |
+
return ZZ_I
|
| 671 |
+
|
| 672 |
+
def get_field(self):
|
| 673 |
+
"""Returns a field associated with ``self``. """
|
| 674 |
+
return self
|
| 675 |
+
|
| 676 |
+
def as_AlgebraicField(self):
|
| 677 |
+
"""Get equivalent domain as an ``AlgebraicField``. """
|
| 678 |
+
return AlgebraicField(self.dom, I)
|
| 679 |
+
|
| 680 |
+
def numer(self, a):
|
| 681 |
+
"""Get the numerator of ``a``."""
|
| 682 |
+
ZZ_I = self.get_ring()
|
| 683 |
+
return ZZ_I.convert(a * self.denom(a))
|
| 684 |
+
|
| 685 |
+
def denom(self, a):
|
| 686 |
+
"""Get the denominator of ``a``."""
|
| 687 |
+
ZZ = self.dom.get_ring()
|
| 688 |
+
QQ = self.dom
|
| 689 |
+
ZZ_I = self.get_ring()
|
| 690 |
+
denom_ZZ = ZZ.lcm(QQ.denom(a.x), QQ.denom(a.y))
|
| 691 |
+
return ZZ_I(denom_ZZ, ZZ.zero)
|
| 692 |
+
|
| 693 |
+
def from_GaussianIntegerRing(K1, a, K0):
|
| 694 |
+
"""Convert a ZZ_I element to QQ_I."""
|
| 695 |
+
return K1.new(a.x, a.y)
|
| 696 |
+
|
| 697 |
+
def from_GaussianRationalField(K1, a, K0):
|
| 698 |
+
"""Convert a QQ_I element to QQ_I."""
|
| 699 |
+
return a
|
| 700 |
+
|
| 701 |
+
def from_ComplexField(K1, a, K0):
|
| 702 |
+
"""Convert a ComplexField element to QQ_I."""
|
| 703 |
+
return K1.new(QQ.convert(a.real), QQ.convert(a.imag))
|
| 704 |
+
|
| 705 |
+
|
| 706 |
+
QQ_I = GaussianRational._parent = GaussianRationalField()
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/gmpyfinitefield.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`GMPYFiniteField` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.polys.domains.finitefield import FiniteField
|
| 5 |
+
from sympy.polys.domains.gmpyintegerring import GMPYIntegerRing
|
| 6 |
+
|
| 7 |
+
from sympy.utilities import public
|
| 8 |
+
|
| 9 |
+
@public
|
| 10 |
+
class GMPYFiniteField(FiniteField):
|
| 11 |
+
"""Finite field based on GMPY integers. """
|
| 12 |
+
|
| 13 |
+
alias = 'FF_gmpy'
|
| 14 |
+
|
| 15 |
+
def __init__(self, mod, symmetric=True):
|
| 16 |
+
super().__init__(mod, GMPYIntegerRing(), symmetric)
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/gmpyintegerring.py
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`GMPYIntegerRing` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.polys.domains.groundtypes import (
|
| 5 |
+
GMPYInteger, SymPyInteger,
|
| 6 |
+
factorial as gmpy_factorial,
|
| 7 |
+
gmpy_gcdex, gmpy_gcd, gmpy_lcm, sqrt as gmpy_sqrt,
|
| 8 |
+
)
|
| 9 |
+
from sympy.core.numbers import int_valued
|
| 10 |
+
from sympy.polys.domains.integerring import IntegerRing
|
| 11 |
+
from sympy.polys.polyerrors import CoercionFailed
|
| 12 |
+
from sympy.utilities import public
|
| 13 |
+
|
| 14 |
+
@public
|
| 15 |
+
class GMPYIntegerRing(IntegerRing):
|
| 16 |
+
"""Integer ring based on GMPY's ``mpz`` type.
|
| 17 |
+
|
| 18 |
+
This will be the implementation of :ref:`ZZ` if ``gmpy`` or ``gmpy2`` is
|
| 19 |
+
installed. Elements will be of type ``gmpy.mpz``.
|
| 20 |
+
"""
|
| 21 |
+
|
| 22 |
+
dtype = GMPYInteger
|
| 23 |
+
zero = dtype(0)
|
| 24 |
+
one = dtype(1)
|
| 25 |
+
tp = type(one)
|
| 26 |
+
alias = 'ZZ_gmpy'
|
| 27 |
+
|
| 28 |
+
def __init__(self):
|
| 29 |
+
"""Allow instantiation of this domain. """
|
| 30 |
+
|
| 31 |
+
def to_sympy(self, a):
|
| 32 |
+
"""Convert ``a`` to a SymPy object. """
|
| 33 |
+
return SymPyInteger(int(a))
|
| 34 |
+
|
| 35 |
+
def from_sympy(self, a):
|
| 36 |
+
"""Convert SymPy's Integer to ``dtype``. """
|
| 37 |
+
if a.is_Integer:
|
| 38 |
+
return GMPYInteger(a.p)
|
| 39 |
+
elif int_valued(a):
|
| 40 |
+
return GMPYInteger(int(a))
|
| 41 |
+
else:
|
| 42 |
+
raise CoercionFailed("expected an integer, got %s" % a)
|
| 43 |
+
|
| 44 |
+
def from_FF_python(K1, a, K0):
|
| 45 |
+
"""Convert ``ModularInteger(int)`` to GMPY's ``mpz``. """
|
| 46 |
+
return K0.to_int(a)
|
| 47 |
+
|
| 48 |
+
def from_ZZ_python(K1, a, K0):
|
| 49 |
+
"""Convert Python's ``int`` to GMPY's ``mpz``. """
|
| 50 |
+
return GMPYInteger(a)
|
| 51 |
+
|
| 52 |
+
def from_QQ(K1, a, K0):
|
| 53 |
+
"""Convert Python's ``Fraction`` to GMPY's ``mpz``. """
|
| 54 |
+
if a.denominator == 1:
|
| 55 |
+
return GMPYInteger(a.numerator)
|
| 56 |
+
|
| 57 |
+
def from_QQ_python(K1, a, K0):
|
| 58 |
+
"""Convert Python's ``Fraction`` to GMPY's ``mpz``. """
|
| 59 |
+
if a.denominator == 1:
|
| 60 |
+
return GMPYInteger(a.numerator)
|
| 61 |
+
|
| 62 |
+
def from_FF_gmpy(K1, a, K0):
|
| 63 |
+
"""Convert ``ModularInteger(mpz)`` to GMPY's ``mpz``. """
|
| 64 |
+
return K0.to_int(a)
|
| 65 |
+
|
| 66 |
+
def from_ZZ_gmpy(K1, a, K0):
|
| 67 |
+
"""Convert GMPY's ``mpz`` to GMPY's ``mpz``. """
|
| 68 |
+
return a
|
| 69 |
+
|
| 70 |
+
def from_QQ_gmpy(K1, a, K0):
|
| 71 |
+
"""Convert GMPY ``mpq`` to GMPY's ``mpz``. """
|
| 72 |
+
if a.denominator == 1:
|
| 73 |
+
return a.numerator
|
| 74 |
+
|
| 75 |
+
def from_RealField(K1, a, K0):
|
| 76 |
+
"""Convert mpmath's ``mpf`` to GMPY's ``mpz``. """
|
| 77 |
+
p, q = K0.to_rational(a)
|
| 78 |
+
|
| 79 |
+
if q == 1:
|
| 80 |
+
return GMPYInteger(p)
|
| 81 |
+
|
| 82 |
+
def from_GaussianIntegerRing(K1, a, K0):
|
| 83 |
+
if a.y == 0:
|
| 84 |
+
return a.x
|
| 85 |
+
|
| 86 |
+
def gcdex(self, a, b):
|
| 87 |
+
"""Compute extended GCD of ``a`` and ``b``. """
|
| 88 |
+
h, s, t = gmpy_gcdex(a, b)
|
| 89 |
+
return s, t, h
|
| 90 |
+
|
| 91 |
+
def gcd(self, a, b):
|
| 92 |
+
"""Compute GCD of ``a`` and ``b``. """
|
| 93 |
+
return gmpy_gcd(a, b)
|
| 94 |
+
|
| 95 |
+
def lcm(self, a, b):
|
| 96 |
+
"""Compute LCM of ``a`` and ``b``. """
|
| 97 |
+
return gmpy_lcm(a, b)
|
| 98 |
+
|
| 99 |
+
def sqrt(self, a):
|
| 100 |
+
"""Compute square root of ``a``. """
|
| 101 |
+
return gmpy_sqrt(a)
|
| 102 |
+
|
| 103 |
+
def factorial(self, a):
|
| 104 |
+
"""Compute factorial of ``a``. """
|
| 105 |
+
return gmpy_factorial(a)
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/gmpyrationalfield.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`GMPYRationalField` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.polys.domains.groundtypes import (
|
| 5 |
+
GMPYRational, SymPyRational,
|
| 6 |
+
gmpy_numer, gmpy_denom, factorial as gmpy_factorial,
|
| 7 |
+
)
|
| 8 |
+
from sympy.polys.domains.rationalfield import RationalField
|
| 9 |
+
from sympy.polys.polyerrors import CoercionFailed
|
| 10 |
+
from sympy.utilities import public
|
| 11 |
+
|
| 12 |
+
@public
|
| 13 |
+
class GMPYRationalField(RationalField):
|
| 14 |
+
"""Rational field based on GMPY's ``mpq`` type.
|
| 15 |
+
|
| 16 |
+
This will be the implementation of :ref:`QQ` if ``gmpy`` or ``gmpy2`` is
|
| 17 |
+
installed. Elements will be of type ``gmpy.mpq``.
|
| 18 |
+
"""
|
| 19 |
+
|
| 20 |
+
dtype = GMPYRational
|
| 21 |
+
zero = dtype(0)
|
| 22 |
+
one = dtype(1)
|
| 23 |
+
tp = type(one)
|
| 24 |
+
alias = 'QQ_gmpy'
|
| 25 |
+
|
| 26 |
+
def __init__(self):
|
| 27 |
+
pass
|
| 28 |
+
|
| 29 |
+
def get_ring(self):
|
| 30 |
+
"""Returns ring associated with ``self``. """
|
| 31 |
+
from sympy.polys.domains import GMPYIntegerRing
|
| 32 |
+
return GMPYIntegerRing()
|
| 33 |
+
|
| 34 |
+
def to_sympy(self, a):
|
| 35 |
+
"""Convert ``a`` to a SymPy object. """
|
| 36 |
+
return SymPyRational(int(gmpy_numer(a)),
|
| 37 |
+
int(gmpy_denom(a)))
|
| 38 |
+
|
| 39 |
+
def from_sympy(self, a):
|
| 40 |
+
"""Convert SymPy's Integer to ``dtype``. """
|
| 41 |
+
if a.is_Rational:
|
| 42 |
+
return GMPYRational(a.p, a.q)
|
| 43 |
+
elif a.is_Float:
|
| 44 |
+
from sympy.polys.domains import RR
|
| 45 |
+
return GMPYRational(*map(int, RR.to_rational(a)))
|
| 46 |
+
else:
|
| 47 |
+
raise CoercionFailed("expected ``Rational`` object, got %s" % a)
|
| 48 |
+
|
| 49 |
+
def from_ZZ_python(K1, a, K0):
|
| 50 |
+
"""Convert a Python ``int`` object to ``dtype``. """
|
| 51 |
+
return GMPYRational(a)
|
| 52 |
+
|
| 53 |
+
def from_QQ_python(K1, a, K0):
|
| 54 |
+
"""Convert a Python ``Fraction`` object to ``dtype``. """
|
| 55 |
+
return GMPYRational(a.numerator, a.denominator)
|
| 56 |
+
|
| 57 |
+
def from_ZZ_gmpy(K1, a, K0):
|
| 58 |
+
"""Convert a GMPY ``mpz`` object to ``dtype``. """
|
| 59 |
+
return GMPYRational(a)
|
| 60 |
+
|
| 61 |
+
def from_QQ_gmpy(K1, a, K0):
|
| 62 |
+
"""Convert a GMPY ``mpq`` object to ``dtype``. """
|
| 63 |
+
return a
|
| 64 |
+
|
| 65 |
+
def from_GaussianRationalField(K1, a, K0):
|
| 66 |
+
"""Convert a ``GaussianElement`` object to ``dtype``. """
|
| 67 |
+
if a.y == 0:
|
| 68 |
+
return GMPYRational(a.x)
|
| 69 |
+
|
| 70 |
+
def from_RealField(K1, a, K0):
|
| 71 |
+
"""Convert a mpmath ``mpf`` object to ``dtype``. """
|
| 72 |
+
return GMPYRational(*map(int, K0.to_rational(a)))
|
| 73 |
+
|
| 74 |
+
def exquo(self, a, b):
|
| 75 |
+
"""Exact quotient of ``a`` and ``b``, implies ``__truediv__``. """
|
| 76 |
+
return GMPYRational(a) / GMPYRational(b)
|
| 77 |
+
|
| 78 |
+
def quo(self, a, b):
|
| 79 |
+
"""Quotient of ``a`` and ``b``, implies ``__truediv__``. """
|
| 80 |
+
return GMPYRational(a) / GMPYRational(b)
|
| 81 |
+
|
| 82 |
+
def rem(self, a, b):
|
| 83 |
+
"""Remainder of ``a`` and ``b``, implies nothing. """
|
| 84 |
+
return self.zero
|
| 85 |
+
|
| 86 |
+
def div(self, a, b):
|
| 87 |
+
"""Division of ``a`` and ``b``, implies ``__truediv__``. """
|
| 88 |
+
return GMPYRational(a) / GMPYRational(b), self.zero
|
| 89 |
+
|
| 90 |
+
def numer(self, a):
|
| 91 |
+
"""Returns numerator of ``a``. """
|
| 92 |
+
return a.numerator
|
| 93 |
+
|
| 94 |
+
def denom(self, a):
|
| 95 |
+
"""Returns denominator of ``a``. """
|
| 96 |
+
return a.denominator
|
| 97 |
+
|
| 98 |
+
def factorial(self, a):
|
| 99 |
+
"""Returns factorial of ``a``. """
|
| 100 |
+
return GMPYRational(gmpy_factorial(int(a)))
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/groundtypes.py
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Ground types for various mathematical domains in SymPy. """
|
| 2 |
+
|
| 3 |
+
import builtins
|
| 4 |
+
from sympy.external.gmpy import GROUND_TYPES, factorial, sqrt, is_square, sqrtrem
|
| 5 |
+
|
| 6 |
+
PythonInteger = builtins.int
|
| 7 |
+
PythonReal = builtins.float
|
| 8 |
+
PythonComplex = builtins.complex
|
| 9 |
+
|
| 10 |
+
from .pythonrational import PythonRational
|
| 11 |
+
|
| 12 |
+
from sympy.core.intfunc import (
|
| 13 |
+
igcdex as python_gcdex,
|
| 14 |
+
igcd2 as python_gcd,
|
| 15 |
+
ilcm as python_lcm,
|
| 16 |
+
)
|
| 17 |
+
|
| 18 |
+
from sympy.core.numbers import (Float as SymPyReal, Integer as SymPyInteger, Rational as SymPyRational)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class _GMPYInteger:
|
| 22 |
+
def __init__(self, obj):
|
| 23 |
+
pass
|
| 24 |
+
|
| 25 |
+
class _GMPYRational:
|
| 26 |
+
def __init__(self, obj):
|
| 27 |
+
pass
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
if GROUND_TYPES == 'gmpy':
|
| 31 |
+
|
| 32 |
+
from gmpy2 import (
|
| 33 |
+
mpz as GMPYInteger,
|
| 34 |
+
mpq as GMPYRational,
|
| 35 |
+
numer as gmpy_numer,
|
| 36 |
+
denom as gmpy_denom,
|
| 37 |
+
gcdext as gmpy_gcdex,
|
| 38 |
+
gcd as gmpy_gcd,
|
| 39 |
+
lcm as gmpy_lcm,
|
| 40 |
+
qdiv as gmpy_qdiv,
|
| 41 |
+
)
|
| 42 |
+
gcdex = gmpy_gcdex
|
| 43 |
+
gcd = gmpy_gcd
|
| 44 |
+
lcm = gmpy_lcm
|
| 45 |
+
|
| 46 |
+
elif GROUND_TYPES == 'flint':
|
| 47 |
+
|
| 48 |
+
from flint import fmpz as _fmpz
|
| 49 |
+
|
| 50 |
+
GMPYInteger = _GMPYInteger
|
| 51 |
+
GMPYRational = _GMPYRational
|
| 52 |
+
gmpy_numer = None
|
| 53 |
+
gmpy_denom = None
|
| 54 |
+
gmpy_gcdex = None
|
| 55 |
+
gmpy_gcd = None
|
| 56 |
+
gmpy_lcm = None
|
| 57 |
+
gmpy_qdiv = None
|
| 58 |
+
|
| 59 |
+
def gcd(a, b):
|
| 60 |
+
return a.gcd(b)
|
| 61 |
+
|
| 62 |
+
def gcdex(a, b):
|
| 63 |
+
x, y, g = python_gcdex(a, b)
|
| 64 |
+
return _fmpz(x), _fmpz(y), _fmpz(g)
|
| 65 |
+
|
| 66 |
+
def lcm(a, b):
|
| 67 |
+
return a.lcm(b)
|
| 68 |
+
|
| 69 |
+
else:
|
| 70 |
+
GMPYInteger = _GMPYInteger
|
| 71 |
+
GMPYRational = _GMPYRational
|
| 72 |
+
gmpy_numer = None
|
| 73 |
+
gmpy_denom = None
|
| 74 |
+
gmpy_gcdex = None
|
| 75 |
+
gmpy_gcd = None
|
| 76 |
+
gmpy_lcm = None
|
| 77 |
+
gmpy_qdiv = None
|
| 78 |
+
gcdex = python_gcdex
|
| 79 |
+
gcd = python_gcd
|
| 80 |
+
lcm = python_lcm
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
__all__ = [
|
| 84 |
+
'PythonInteger', 'PythonReal', 'PythonComplex',
|
| 85 |
+
|
| 86 |
+
'PythonRational',
|
| 87 |
+
|
| 88 |
+
'python_gcdex', 'python_gcd', 'python_lcm',
|
| 89 |
+
|
| 90 |
+
'SymPyReal', 'SymPyInteger', 'SymPyRational',
|
| 91 |
+
|
| 92 |
+
'GMPYInteger', 'GMPYRational', 'gmpy_numer',
|
| 93 |
+
'gmpy_denom', 'gmpy_gcdex', 'gmpy_gcd', 'gmpy_lcm',
|
| 94 |
+
'gmpy_qdiv',
|
| 95 |
+
|
| 96 |
+
'factorial', 'sqrt', 'is_square', 'sqrtrem',
|
| 97 |
+
|
| 98 |
+
'GMPYInteger', 'GMPYRational',
|
| 99 |
+
]
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/integerring.py
ADDED
|
@@ -0,0 +1,276 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`IntegerRing` class. """
|
| 2 |
+
|
| 3 |
+
from sympy.external.gmpy import MPZ, GROUND_TYPES
|
| 4 |
+
|
| 5 |
+
from sympy.core.numbers import int_valued
|
| 6 |
+
from sympy.polys.domains.groundtypes import (
|
| 7 |
+
SymPyInteger,
|
| 8 |
+
factorial,
|
| 9 |
+
gcdex, gcd, lcm, sqrt, is_square, sqrtrem,
|
| 10 |
+
)
|
| 11 |
+
|
| 12 |
+
from sympy.polys.domains.characteristiczero import CharacteristicZero
|
| 13 |
+
from sympy.polys.domains.ring import Ring
|
| 14 |
+
from sympy.polys.domains.simpledomain import SimpleDomain
|
| 15 |
+
from sympy.polys.polyerrors import CoercionFailed
|
| 16 |
+
from sympy.utilities import public
|
| 17 |
+
|
| 18 |
+
import math
|
| 19 |
+
|
| 20 |
+
@public
|
| 21 |
+
class IntegerRing(Ring, CharacteristicZero, SimpleDomain):
|
| 22 |
+
r"""The domain ``ZZ`` representing the integers `\mathbb{Z}`.
|
| 23 |
+
|
| 24 |
+
The :py:class:`IntegerRing` class represents the ring of integers as a
|
| 25 |
+
:py:class:`~.Domain` in the domain system. :py:class:`IntegerRing` is a
|
| 26 |
+
super class of :py:class:`PythonIntegerRing` and
|
| 27 |
+
:py:class:`GMPYIntegerRing` one of which will be the implementation for
|
| 28 |
+
:ref:`ZZ` depending on whether or not ``gmpy`` or ``gmpy2`` is installed.
|
| 29 |
+
|
| 30 |
+
See also
|
| 31 |
+
========
|
| 32 |
+
|
| 33 |
+
Domain
|
| 34 |
+
"""
|
| 35 |
+
|
| 36 |
+
rep = 'ZZ'
|
| 37 |
+
alias = 'ZZ'
|
| 38 |
+
dtype = MPZ
|
| 39 |
+
zero = dtype(0)
|
| 40 |
+
one = dtype(1)
|
| 41 |
+
tp = type(one)
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
is_IntegerRing = is_ZZ = True
|
| 45 |
+
is_Numerical = True
|
| 46 |
+
is_PID = True
|
| 47 |
+
|
| 48 |
+
has_assoc_Ring = True
|
| 49 |
+
has_assoc_Field = True
|
| 50 |
+
|
| 51 |
+
def __init__(self):
|
| 52 |
+
"""Allow instantiation of this domain. """
|
| 53 |
+
|
| 54 |
+
def __eq__(self, other):
|
| 55 |
+
"""Returns ``True`` if two domains are equivalent. """
|
| 56 |
+
if isinstance(other, IntegerRing):
|
| 57 |
+
return True
|
| 58 |
+
else:
|
| 59 |
+
return NotImplemented
|
| 60 |
+
|
| 61 |
+
def __hash__(self):
|
| 62 |
+
"""Compute a hash value for this domain. """
|
| 63 |
+
return hash('ZZ')
|
| 64 |
+
|
| 65 |
+
def to_sympy(self, a):
|
| 66 |
+
"""Convert ``a`` to a SymPy object. """
|
| 67 |
+
return SymPyInteger(int(a))
|
| 68 |
+
|
| 69 |
+
def from_sympy(self, a):
|
| 70 |
+
"""Convert SymPy's Integer to ``dtype``. """
|
| 71 |
+
if a.is_Integer:
|
| 72 |
+
return MPZ(a.p)
|
| 73 |
+
elif int_valued(a):
|
| 74 |
+
return MPZ(int(a))
|
| 75 |
+
else:
|
| 76 |
+
raise CoercionFailed("expected an integer, got %s" % a)
|
| 77 |
+
|
| 78 |
+
def get_field(self):
|
| 79 |
+
r"""Return the associated field of fractions :ref:`QQ`
|
| 80 |
+
|
| 81 |
+
Returns
|
| 82 |
+
=======
|
| 83 |
+
|
| 84 |
+
:ref:`QQ`:
|
| 85 |
+
The associated field of fractions :ref:`QQ`, a
|
| 86 |
+
:py:class:`~.Domain` representing the rational numbers
|
| 87 |
+
`\mathbb{Q}`.
|
| 88 |
+
|
| 89 |
+
Examples
|
| 90 |
+
========
|
| 91 |
+
|
| 92 |
+
>>> from sympy import ZZ
|
| 93 |
+
>>> ZZ.get_field()
|
| 94 |
+
QQ
|
| 95 |
+
"""
|
| 96 |
+
from sympy.polys.domains import QQ
|
| 97 |
+
return QQ
|
| 98 |
+
|
| 99 |
+
def algebraic_field(self, *extension, alias=None):
|
| 100 |
+
r"""Returns an algebraic field, i.e. `\mathbb{Q}(\alpha, \ldots)`.
|
| 101 |
+
|
| 102 |
+
Parameters
|
| 103 |
+
==========
|
| 104 |
+
|
| 105 |
+
*extension : One or more :py:class:`~.Expr`.
|
| 106 |
+
Generators of the extension. These should be expressions that are
|
| 107 |
+
algebraic over `\mathbb{Q}`.
|
| 108 |
+
|
| 109 |
+
alias : str, :py:class:`~.Symbol`, None, optional (default=None)
|
| 110 |
+
If provided, this will be used as the alias symbol for the
|
| 111 |
+
primitive element of the returned :py:class:`~.AlgebraicField`.
|
| 112 |
+
|
| 113 |
+
Returns
|
| 114 |
+
=======
|
| 115 |
+
|
| 116 |
+
:py:class:`~.AlgebraicField`
|
| 117 |
+
A :py:class:`~.Domain` representing the algebraic field extension.
|
| 118 |
+
|
| 119 |
+
Examples
|
| 120 |
+
========
|
| 121 |
+
|
| 122 |
+
>>> from sympy import ZZ, sqrt
|
| 123 |
+
>>> ZZ.algebraic_field(sqrt(2))
|
| 124 |
+
QQ<sqrt(2)>
|
| 125 |
+
"""
|
| 126 |
+
return self.get_field().algebraic_field(*extension, alias=alias)
|
| 127 |
+
|
| 128 |
+
def from_AlgebraicField(K1, a, K0):
|
| 129 |
+
"""Convert a :py:class:`~.ANP` object to :ref:`ZZ`.
|
| 130 |
+
|
| 131 |
+
See :py:meth:`~.Domain.convert`.
|
| 132 |
+
"""
|
| 133 |
+
if a.is_ground:
|
| 134 |
+
return K1.convert(a.LC(), K0.dom)
|
| 135 |
+
|
| 136 |
+
def log(self, a, b):
|
| 137 |
+
r"""Logarithm of *a* to the base *b*.
|
| 138 |
+
|
| 139 |
+
Parameters
|
| 140 |
+
==========
|
| 141 |
+
|
| 142 |
+
a: number
|
| 143 |
+
b: number
|
| 144 |
+
|
| 145 |
+
Returns
|
| 146 |
+
=======
|
| 147 |
+
|
| 148 |
+
$\\lfloor\log(a, b)\\rfloor$:
|
| 149 |
+
Floor of the logarithm of *a* to the base *b*
|
| 150 |
+
|
| 151 |
+
Examples
|
| 152 |
+
========
|
| 153 |
+
|
| 154 |
+
>>> from sympy import ZZ
|
| 155 |
+
>>> ZZ.log(ZZ(8), ZZ(2))
|
| 156 |
+
3
|
| 157 |
+
>>> ZZ.log(ZZ(9), ZZ(2))
|
| 158 |
+
3
|
| 159 |
+
|
| 160 |
+
Notes
|
| 161 |
+
=====
|
| 162 |
+
|
| 163 |
+
This function uses ``math.log`` which is based on ``float`` so it will
|
| 164 |
+
fail for large integer arguments.
|
| 165 |
+
"""
|
| 166 |
+
return self.dtype(int(math.log(int(a), b)))
|
| 167 |
+
|
| 168 |
+
def from_FF(K1, a, K0):
|
| 169 |
+
"""Convert ``ModularInteger(int)`` to GMPY's ``mpz``. """
|
| 170 |
+
return MPZ(K0.to_int(a))
|
| 171 |
+
|
| 172 |
+
def from_FF_python(K1, a, K0):
|
| 173 |
+
"""Convert ``ModularInteger(int)`` to GMPY's ``mpz``. """
|
| 174 |
+
return MPZ(K0.to_int(a))
|
| 175 |
+
|
| 176 |
+
def from_ZZ(K1, a, K0):
|
| 177 |
+
"""Convert Python's ``int`` to GMPY's ``mpz``. """
|
| 178 |
+
return MPZ(a)
|
| 179 |
+
|
| 180 |
+
def from_ZZ_python(K1, a, K0):
|
| 181 |
+
"""Convert Python's ``int`` to GMPY's ``mpz``. """
|
| 182 |
+
return MPZ(a)
|
| 183 |
+
|
| 184 |
+
def from_QQ(K1, a, K0):
|
| 185 |
+
"""Convert Python's ``Fraction`` to GMPY's ``mpz``. """
|
| 186 |
+
if a.denominator == 1:
|
| 187 |
+
return MPZ(a.numerator)
|
| 188 |
+
|
| 189 |
+
def from_QQ_python(K1, a, K0):
|
| 190 |
+
"""Convert Python's ``Fraction`` to GMPY's ``mpz``. """
|
| 191 |
+
if a.denominator == 1:
|
| 192 |
+
return MPZ(a.numerator)
|
| 193 |
+
|
| 194 |
+
def from_FF_gmpy(K1, a, K0):
|
| 195 |
+
"""Convert ``ModularInteger(mpz)`` to GMPY's ``mpz``. """
|
| 196 |
+
return MPZ(K0.to_int(a))
|
| 197 |
+
|
| 198 |
+
def from_ZZ_gmpy(K1, a, K0):
|
| 199 |
+
"""Convert GMPY's ``mpz`` to GMPY's ``mpz``. """
|
| 200 |
+
return a
|
| 201 |
+
|
| 202 |
+
def from_QQ_gmpy(K1, a, K0):
|
| 203 |
+
"""Convert GMPY ``mpq`` to GMPY's ``mpz``. """
|
| 204 |
+
if a.denominator == 1:
|
| 205 |
+
return a.numerator
|
| 206 |
+
|
| 207 |
+
def from_RealField(K1, a, K0):
|
| 208 |
+
"""Convert mpmath's ``mpf`` to GMPY's ``mpz``. """
|
| 209 |
+
p, q = K0.to_rational(a)
|
| 210 |
+
|
| 211 |
+
if q == 1:
|
| 212 |
+
# XXX: If MPZ is flint.fmpz and p is a gmpy2.mpz, then we need
|
| 213 |
+
# to convert via int because fmpz and mpz do not know about each
|
| 214 |
+
# other.
|
| 215 |
+
return MPZ(int(p))
|
| 216 |
+
|
| 217 |
+
def from_GaussianIntegerRing(K1, a, K0):
|
| 218 |
+
if a.y == 0:
|
| 219 |
+
return a.x
|
| 220 |
+
|
| 221 |
+
def from_EX(K1, a, K0):
|
| 222 |
+
"""Convert ``Expression`` to GMPY's ``mpz``. """
|
| 223 |
+
if a.is_Integer:
|
| 224 |
+
return K1.from_sympy(a)
|
| 225 |
+
|
| 226 |
+
def gcdex(self, a, b):
|
| 227 |
+
"""Compute extended GCD of ``a`` and ``b``. """
|
| 228 |
+
h, s, t = gcdex(a, b)
|
| 229 |
+
# XXX: This conditional logic should be handled somewhere else.
|
| 230 |
+
if GROUND_TYPES == 'gmpy':
|
| 231 |
+
return s, t, h
|
| 232 |
+
else:
|
| 233 |
+
return h, s, t
|
| 234 |
+
|
| 235 |
+
def gcd(self, a, b):
|
| 236 |
+
"""Compute GCD of ``a`` and ``b``. """
|
| 237 |
+
return gcd(a, b)
|
| 238 |
+
|
| 239 |
+
def lcm(self, a, b):
|
| 240 |
+
"""Compute LCM of ``a`` and ``b``. """
|
| 241 |
+
return lcm(a, b)
|
| 242 |
+
|
| 243 |
+
def sqrt(self, a):
|
| 244 |
+
"""Compute square root of ``a``. """
|
| 245 |
+
return sqrt(a)
|
| 246 |
+
|
| 247 |
+
def is_square(self, a):
|
| 248 |
+
"""Return ``True`` if ``a`` is a square.
|
| 249 |
+
|
| 250 |
+
Explanation
|
| 251 |
+
===========
|
| 252 |
+
An integer is a square if and only if there exists an integer
|
| 253 |
+
``b`` such that ``b * b == a``.
|
| 254 |
+
"""
|
| 255 |
+
return is_square(a)
|
| 256 |
+
|
| 257 |
+
def exsqrt(self, a):
|
| 258 |
+
"""Non-negative square root of ``a`` if ``a`` is a square.
|
| 259 |
+
|
| 260 |
+
See also
|
| 261 |
+
========
|
| 262 |
+
is_square
|
| 263 |
+
"""
|
| 264 |
+
if a < 0:
|
| 265 |
+
return None
|
| 266 |
+
root, rem = sqrtrem(a)
|
| 267 |
+
if rem != 0:
|
| 268 |
+
return None
|
| 269 |
+
return root
|
| 270 |
+
|
| 271 |
+
def factorial(self, a):
|
| 272 |
+
"""Compute factorial of ``a``. """
|
| 273 |
+
return factorial(a)
|
| 274 |
+
|
| 275 |
+
|
| 276 |
+
ZZ = IntegerRing()
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/modularinteger.py
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`ModularInteger` class. """
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
from typing import Any
|
| 5 |
+
|
| 6 |
+
import operator
|
| 7 |
+
|
| 8 |
+
from sympy.polys.polyutils import PicklableWithSlots
|
| 9 |
+
from sympy.polys.polyerrors import CoercionFailed
|
| 10 |
+
from sympy.polys.domains.domainelement import DomainElement
|
| 11 |
+
|
| 12 |
+
from sympy.utilities import public
|
| 13 |
+
from sympy.utilities.exceptions import sympy_deprecation_warning
|
| 14 |
+
|
| 15 |
+
@public
|
| 16 |
+
class ModularInteger(PicklableWithSlots, DomainElement):
|
| 17 |
+
"""A class representing a modular integer. """
|
| 18 |
+
|
| 19 |
+
mod, dom, sym, _parent = None, None, None, None
|
| 20 |
+
|
| 21 |
+
__slots__ = ('val',)
|
| 22 |
+
|
| 23 |
+
def parent(self):
|
| 24 |
+
return self._parent
|
| 25 |
+
|
| 26 |
+
def __init__(self, val):
|
| 27 |
+
if isinstance(val, self.__class__):
|
| 28 |
+
self.val = val.val % self.mod
|
| 29 |
+
else:
|
| 30 |
+
self.val = self.dom.convert(val) % self.mod
|
| 31 |
+
|
| 32 |
+
def modulus(self):
|
| 33 |
+
return self.mod
|
| 34 |
+
|
| 35 |
+
def __hash__(self):
|
| 36 |
+
return hash((self.val, self.mod))
|
| 37 |
+
|
| 38 |
+
def __repr__(self):
|
| 39 |
+
return "%s(%s)" % (self.__class__.__name__, self.val)
|
| 40 |
+
|
| 41 |
+
def __str__(self):
|
| 42 |
+
return "%s mod %s" % (self.val, self.mod)
|
| 43 |
+
|
| 44 |
+
def __int__(self):
|
| 45 |
+
return int(self.val)
|
| 46 |
+
|
| 47 |
+
def to_int(self):
|
| 48 |
+
|
| 49 |
+
sympy_deprecation_warning(
|
| 50 |
+
"""ModularInteger.to_int() is deprecated.
|
| 51 |
+
|
| 52 |
+
Use int(a) or K = GF(p) and K.to_int(a) instead of a.to_int().
|
| 53 |
+
""",
|
| 54 |
+
deprecated_since_version="1.13",
|
| 55 |
+
active_deprecations_target="modularinteger-to-int",
|
| 56 |
+
)
|
| 57 |
+
|
| 58 |
+
if self.sym:
|
| 59 |
+
if self.val <= self.mod // 2:
|
| 60 |
+
return self.val
|
| 61 |
+
else:
|
| 62 |
+
return self.val - self.mod
|
| 63 |
+
else:
|
| 64 |
+
return self.val
|
| 65 |
+
|
| 66 |
+
def __pos__(self):
|
| 67 |
+
return self
|
| 68 |
+
|
| 69 |
+
def __neg__(self):
|
| 70 |
+
return self.__class__(-self.val)
|
| 71 |
+
|
| 72 |
+
@classmethod
|
| 73 |
+
def _get_val(cls, other):
|
| 74 |
+
if isinstance(other, cls):
|
| 75 |
+
return other.val
|
| 76 |
+
else:
|
| 77 |
+
try:
|
| 78 |
+
return cls.dom.convert(other)
|
| 79 |
+
except CoercionFailed:
|
| 80 |
+
return None
|
| 81 |
+
|
| 82 |
+
def __add__(self, other):
|
| 83 |
+
val = self._get_val(other)
|
| 84 |
+
|
| 85 |
+
if val is not None:
|
| 86 |
+
return self.__class__(self.val + val)
|
| 87 |
+
else:
|
| 88 |
+
return NotImplemented
|
| 89 |
+
|
| 90 |
+
def __radd__(self, other):
|
| 91 |
+
return self.__add__(other)
|
| 92 |
+
|
| 93 |
+
def __sub__(self, other):
|
| 94 |
+
val = self._get_val(other)
|
| 95 |
+
|
| 96 |
+
if val is not None:
|
| 97 |
+
return self.__class__(self.val - val)
|
| 98 |
+
else:
|
| 99 |
+
return NotImplemented
|
| 100 |
+
|
| 101 |
+
def __rsub__(self, other):
|
| 102 |
+
return (-self).__add__(other)
|
| 103 |
+
|
| 104 |
+
def __mul__(self, other):
|
| 105 |
+
val = self._get_val(other)
|
| 106 |
+
|
| 107 |
+
if val is not None:
|
| 108 |
+
return self.__class__(self.val * val)
|
| 109 |
+
else:
|
| 110 |
+
return NotImplemented
|
| 111 |
+
|
| 112 |
+
def __rmul__(self, other):
|
| 113 |
+
return self.__mul__(other)
|
| 114 |
+
|
| 115 |
+
def __truediv__(self, other):
|
| 116 |
+
val = self._get_val(other)
|
| 117 |
+
|
| 118 |
+
if val is not None:
|
| 119 |
+
return self.__class__(self.val * self._invert(val))
|
| 120 |
+
else:
|
| 121 |
+
return NotImplemented
|
| 122 |
+
|
| 123 |
+
def __rtruediv__(self, other):
|
| 124 |
+
return self.invert().__mul__(other)
|
| 125 |
+
|
| 126 |
+
def __mod__(self, other):
|
| 127 |
+
val = self._get_val(other)
|
| 128 |
+
|
| 129 |
+
if val is not None:
|
| 130 |
+
return self.__class__(self.val % val)
|
| 131 |
+
else:
|
| 132 |
+
return NotImplemented
|
| 133 |
+
|
| 134 |
+
def __rmod__(self, other):
|
| 135 |
+
val = self._get_val(other)
|
| 136 |
+
|
| 137 |
+
if val is not None:
|
| 138 |
+
return self.__class__(val % self.val)
|
| 139 |
+
else:
|
| 140 |
+
return NotImplemented
|
| 141 |
+
|
| 142 |
+
def __pow__(self, exp):
|
| 143 |
+
if not exp:
|
| 144 |
+
return self.__class__(self.dom.one)
|
| 145 |
+
|
| 146 |
+
if exp < 0:
|
| 147 |
+
val, exp = self.invert().val, -exp
|
| 148 |
+
else:
|
| 149 |
+
val = self.val
|
| 150 |
+
|
| 151 |
+
return self.__class__(pow(val, int(exp), self.mod))
|
| 152 |
+
|
| 153 |
+
def _compare(self, other, op):
|
| 154 |
+
val = self._get_val(other)
|
| 155 |
+
|
| 156 |
+
if val is None:
|
| 157 |
+
return NotImplemented
|
| 158 |
+
|
| 159 |
+
return op(self.val, val % self.mod)
|
| 160 |
+
|
| 161 |
+
def _compare_deprecated(self, other, op):
|
| 162 |
+
val = self._get_val(other)
|
| 163 |
+
|
| 164 |
+
if val is None:
|
| 165 |
+
return NotImplemented
|
| 166 |
+
|
| 167 |
+
sympy_deprecation_warning(
|
| 168 |
+
"""Ordered comparisons with modular integers are deprecated.
|
| 169 |
+
|
| 170 |
+
Use e.g. int(a) < int(b) instead of a < b.
|
| 171 |
+
""",
|
| 172 |
+
deprecated_since_version="1.13",
|
| 173 |
+
active_deprecations_target="modularinteger-compare",
|
| 174 |
+
stacklevel=4,
|
| 175 |
+
)
|
| 176 |
+
|
| 177 |
+
return op(self.val, val % self.mod)
|
| 178 |
+
|
| 179 |
+
def __eq__(self, other):
|
| 180 |
+
return self._compare(other, operator.eq)
|
| 181 |
+
|
| 182 |
+
def __ne__(self, other):
|
| 183 |
+
return self._compare(other, operator.ne)
|
| 184 |
+
|
| 185 |
+
def __lt__(self, other):
|
| 186 |
+
return self._compare_deprecated(other, operator.lt)
|
| 187 |
+
|
| 188 |
+
def __le__(self, other):
|
| 189 |
+
return self._compare_deprecated(other, operator.le)
|
| 190 |
+
|
| 191 |
+
def __gt__(self, other):
|
| 192 |
+
return self._compare_deprecated(other, operator.gt)
|
| 193 |
+
|
| 194 |
+
def __ge__(self, other):
|
| 195 |
+
return self._compare_deprecated(other, operator.ge)
|
| 196 |
+
|
| 197 |
+
def __bool__(self):
|
| 198 |
+
return bool(self.val)
|
| 199 |
+
|
| 200 |
+
@classmethod
|
| 201 |
+
def _invert(cls, value):
|
| 202 |
+
return cls.dom.invert(value, cls.mod)
|
| 203 |
+
|
| 204 |
+
def invert(self):
|
| 205 |
+
return self.__class__(self._invert(self.val))
|
| 206 |
+
|
| 207 |
+
_modular_integer_cache: dict[tuple[Any, Any, Any], type[ModularInteger]] = {}
|
| 208 |
+
|
| 209 |
+
def ModularIntegerFactory(_mod, _dom, _sym, parent):
|
| 210 |
+
"""Create custom class for specific integer modulus."""
|
| 211 |
+
try:
|
| 212 |
+
_mod = _dom.convert(_mod)
|
| 213 |
+
except CoercionFailed:
|
| 214 |
+
ok = False
|
| 215 |
+
else:
|
| 216 |
+
ok = True
|
| 217 |
+
|
| 218 |
+
if not ok or _mod < 1:
|
| 219 |
+
raise ValueError("modulus must be a positive integer, got %s" % _mod)
|
| 220 |
+
|
| 221 |
+
key = _mod, _dom, _sym
|
| 222 |
+
|
| 223 |
+
try:
|
| 224 |
+
cls = _modular_integer_cache[key]
|
| 225 |
+
except KeyError:
|
| 226 |
+
class cls(ModularInteger):
|
| 227 |
+
mod, dom, sym = _mod, _dom, _sym
|
| 228 |
+
_parent = parent
|
| 229 |
+
|
| 230 |
+
if _sym:
|
| 231 |
+
cls.__name__ = "SymmetricModularIntegerMod%s" % _mod
|
| 232 |
+
else:
|
| 233 |
+
cls.__name__ = "ModularIntegerMod%s" % _mod
|
| 234 |
+
|
| 235 |
+
_modular_integer_cache[key] = cls
|
| 236 |
+
|
| 237 |
+
return cls
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/mpelements.py
ADDED
|
@@ -0,0 +1,181 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
#
|
| 2 |
+
# This module is deprecated and should not be used any more. The actual
|
| 3 |
+
# implementation of RR and CC now uses mpmath's mpf and mpc types directly.
|
| 4 |
+
#
|
| 5 |
+
"""Real and complex elements. """
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
from sympy.external.gmpy import MPQ
|
| 9 |
+
from sympy.polys.domains.domainelement import DomainElement
|
| 10 |
+
from sympy.utilities import public
|
| 11 |
+
|
| 12 |
+
from mpmath.ctx_mp_python import PythonMPContext, _mpf, _mpc, _constant
|
| 13 |
+
from mpmath.libmp import (MPZ_ONE, fzero, fone, finf, fninf, fnan,
|
| 14 |
+
round_nearest, mpf_mul, repr_dps, int_types,
|
| 15 |
+
from_int, from_float, from_str, to_rational)
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
@public
|
| 19 |
+
class RealElement(_mpf, DomainElement):
|
| 20 |
+
"""An element of a real domain. """
|
| 21 |
+
|
| 22 |
+
__slots__ = ('__mpf__',)
|
| 23 |
+
|
| 24 |
+
def _set_mpf(self, val):
|
| 25 |
+
self.__mpf__ = val
|
| 26 |
+
|
| 27 |
+
_mpf_ = property(lambda self: self.__mpf__, _set_mpf)
|
| 28 |
+
|
| 29 |
+
def parent(self):
|
| 30 |
+
return self.context._parent
|
| 31 |
+
|
| 32 |
+
@public
|
| 33 |
+
class ComplexElement(_mpc, DomainElement):
|
| 34 |
+
"""An element of a complex domain. """
|
| 35 |
+
|
| 36 |
+
__slots__ = ('__mpc__',)
|
| 37 |
+
|
| 38 |
+
def _set_mpc(self, val):
|
| 39 |
+
self.__mpc__ = val
|
| 40 |
+
|
| 41 |
+
_mpc_ = property(lambda self: self.__mpc__, _set_mpc)
|
| 42 |
+
|
| 43 |
+
def parent(self):
|
| 44 |
+
return self.context._parent
|
| 45 |
+
|
| 46 |
+
new = object.__new__
|
| 47 |
+
|
| 48 |
+
@public
|
| 49 |
+
class MPContext(PythonMPContext):
|
| 50 |
+
|
| 51 |
+
def __init__(ctx, prec=53, dps=None, tol=None, real=False):
|
| 52 |
+
ctx._prec_rounding = [prec, round_nearest]
|
| 53 |
+
|
| 54 |
+
if dps is None:
|
| 55 |
+
ctx._set_prec(prec)
|
| 56 |
+
else:
|
| 57 |
+
ctx._set_dps(dps)
|
| 58 |
+
|
| 59 |
+
ctx.mpf = RealElement
|
| 60 |
+
ctx.mpc = ComplexElement
|
| 61 |
+
ctx.mpf._ctxdata = [ctx.mpf, new, ctx._prec_rounding]
|
| 62 |
+
ctx.mpc._ctxdata = [ctx.mpc, new, ctx._prec_rounding]
|
| 63 |
+
|
| 64 |
+
if real:
|
| 65 |
+
ctx.mpf.context = ctx
|
| 66 |
+
else:
|
| 67 |
+
ctx.mpc.context = ctx
|
| 68 |
+
|
| 69 |
+
ctx.constant = _constant
|
| 70 |
+
ctx.constant._ctxdata = [ctx.mpf, new, ctx._prec_rounding]
|
| 71 |
+
ctx.constant.context = ctx
|
| 72 |
+
|
| 73 |
+
ctx.types = [ctx.mpf, ctx.mpc, ctx.constant]
|
| 74 |
+
ctx.trap_complex = True
|
| 75 |
+
ctx.pretty = True
|
| 76 |
+
|
| 77 |
+
if tol is None:
|
| 78 |
+
ctx.tol = ctx._make_tol()
|
| 79 |
+
elif tol is False:
|
| 80 |
+
ctx.tol = fzero
|
| 81 |
+
else:
|
| 82 |
+
ctx.tol = ctx._convert_tol(tol)
|
| 83 |
+
|
| 84 |
+
ctx.tolerance = ctx.make_mpf(ctx.tol)
|
| 85 |
+
|
| 86 |
+
if not ctx.tolerance:
|
| 87 |
+
ctx.max_denom = 1000000
|
| 88 |
+
else:
|
| 89 |
+
ctx.max_denom = int(1/ctx.tolerance)
|
| 90 |
+
|
| 91 |
+
ctx.zero = ctx.make_mpf(fzero)
|
| 92 |
+
ctx.one = ctx.make_mpf(fone)
|
| 93 |
+
ctx.j = ctx.make_mpc((fzero, fone))
|
| 94 |
+
ctx.inf = ctx.make_mpf(finf)
|
| 95 |
+
ctx.ninf = ctx.make_mpf(fninf)
|
| 96 |
+
ctx.nan = ctx.make_mpf(fnan)
|
| 97 |
+
|
| 98 |
+
def _make_tol(ctx):
|
| 99 |
+
hundred = (0, 25, 2, 5)
|
| 100 |
+
eps = (0, MPZ_ONE, 1-ctx.prec, 1)
|
| 101 |
+
return mpf_mul(hundred, eps)
|
| 102 |
+
|
| 103 |
+
def make_tol(ctx):
|
| 104 |
+
return ctx.make_mpf(ctx._make_tol())
|
| 105 |
+
|
| 106 |
+
def _convert_tol(ctx, tol):
|
| 107 |
+
if isinstance(tol, int_types):
|
| 108 |
+
return from_int(tol)
|
| 109 |
+
if isinstance(tol, float):
|
| 110 |
+
return from_float(tol)
|
| 111 |
+
if hasattr(tol, "_mpf_"):
|
| 112 |
+
return tol._mpf_
|
| 113 |
+
prec, rounding = ctx._prec_rounding
|
| 114 |
+
if isinstance(tol, str):
|
| 115 |
+
return from_str(tol, prec, rounding)
|
| 116 |
+
raise ValueError("expected a real number, got %s" % tol)
|
| 117 |
+
|
| 118 |
+
def _convert_fallback(ctx, x, strings):
|
| 119 |
+
raise TypeError("cannot create mpf from " + repr(x))
|
| 120 |
+
|
| 121 |
+
@property
|
| 122 |
+
def _repr_digits(ctx):
|
| 123 |
+
return repr_dps(ctx._prec)
|
| 124 |
+
|
| 125 |
+
@property
|
| 126 |
+
def _str_digits(ctx):
|
| 127 |
+
return ctx._dps
|
| 128 |
+
|
| 129 |
+
def to_rational(ctx, s, limit=True):
|
| 130 |
+
p, q = to_rational(s._mpf_)
|
| 131 |
+
|
| 132 |
+
# Needed for GROUND_TYPES=flint if gmpy2 is installed because mpmath's
|
| 133 |
+
# to_rational() function returns a gmpy2.mpz instance and if MPQ is
|
| 134 |
+
# flint.fmpq then MPQ(p, q) will fail.
|
| 135 |
+
p = int(p)
|
| 136 |
+
|
| 137 |
+
if not limit or q <= ctx.max_denom:
|
| 138 |
+
return p, q
|
| 139 |
+
|
| 140 |
+
p0, q0, p1, q1 = 0, 1, 1, 0
|
| 141 |
+
n, d = p, q
|
| 142 |
+
|
| 143 |
+
while True:
|
| 144 |
+
a = n//d
|
| 145 |
+
q2 = q0 + a*q1
|
| 146 |
+
if q2 > ctx.max_denom:
|
| 147 |
+
break
|
| 148 |
+
p0, q0, p1, q1 = p1, q1, p0 + a*p1, q2
|
| 149 |
+
n, d = d, n - a*d
|
| 150 |
+
|
| 151 |
+
k = (ctx.max_denom - q0)//q1
|
| 152 |
+
|
| 153 |
+
number = MPQ(p, q)
|
| 154 |
+
bound1 = MPQ(p0 + k*p1, q0 + k*q1)
|
| 155 |
+
bound2 = MPQ(p1, q1)
|
| 156 |
+
|
| 157 |
+
if not bound2 or not bound1:
|
| 158 |
+
return p, q
|
| 159 |
+
elif abs(bound2 - number) <= abs(bound1 - number):
|
| 160 |
+
return bound2.numerator, bound2.denominator
|
| 161 |
+
else:
|
| 162 |
+
return bound1.numerator, bound1.denominator
|
| 163 |
+
|
| 164 |
+
def almosteq(ctx, s, t, rel_eps=None, abs_eps=None):
|
| 165 |
+
t = ctx.convert(t)
|
| 166 |
+
if abs_eps is None and rel_eps is None:
|
| 167 |
+
rel_eps = abs_eps = ctx.tolerance or ctx.make_tol()
|
| 168 |
+
if abs_eps is None:
|
| 169 |
+
abs_eps = ctx.convert(rel_eps)
|
| 170 |
+
elif rel_eps is None:
|
| 171 |
+
rel_eps = ctx.convert(abs_eps)
|
| 172 |
+
diff = abs(s-t)
|
| 173 |
+
if diff <= abs_eps:
|
| 174 |
+
return True
|
| 175 |
+
abss = abs(s)
|
| 176 |
+
abst = abs(t)
|
| 177 |
+
if abss < abst:
|
| 178 |
+
err = diff/abst
|
| 179 |
+
else:
|
| 180 |
+
err = diff/abss
|
| 181 |
+
return err <= rel_eps
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/old_fractionfield.py
ADDED
|
@@ -0,0 +1,188 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`FractionField` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.polys.domains.field import Field
|
| 5 |
+
from sympy.polys.domains.compositedomain import CompositeDomain
|
| 6 |
+
from sympy.polys.polyclasses import DMF
|
| 7 |
+
from sympy.polys.polyerrors import GeneratorsNeeded
|
| 8 |
+
from sympy.polys.polyutils import dict_from_basic, basic_from_dict, _dict_reorder
|
| 9 |
+
from sympy.utilities import public
|
| 10 |
+
|
| 11 |
+
@public
|
| 12 |
+
class FractionField(Field, CompositeDomain):
|
| 13 |
+
"""A class for representing rational function fields. """
|
| 14 |
+
|
| 15 |
+
dtype = DMF
|
| 16 |
+
is_FractionField = is_Frac = True
|
| 17 |
+
|
| 18 |
+
has_assoc_Ring = True
|
| 19 |
+
has_assoc_Field = True
|
| 20 |
+
|
| 21 |
+
def __init__(self, dom, *gens):
|
| 22 |
+
if not gens:
|
| 23 |
+
raise GeneratorsNeeded("generators not specified")
|
| 24 |
+
|
| 25 |
+
lev = len(gens) - 1
|
| 26 |
+
self.ngens = len(gens)
|
| 27 |
+
|
| 28 |
+
self.zero = self.dtype.zero(lev, dom)
|
| 29 |
+
self.one = self.dtype.one(lev, dom)
|
| 30 |
+
|
| 31 |
+
self.domain = self.dom = dom
|
| 32 |
+
self.symbols = self.gens = gens
|
| 33 |
+
|
| 34 |
+
def set_domain(self, dom):
|
| 35 |
+
"""Make a new fraction field with given domain. """
|
| 36 |
+
return self.__class__(dom, *self.gens)
|
| 37 |
+
|
| 38 |
+
def new(self, element):
|
| 39 |
+
return self.dtype(element, self.dom, len(self.gens) - 1)
|
| 40 |
+
|
| 41 |
+
def __str__(self):
|
| 42 |
+
return str(self.dom) + '(' + ','.join(map(str, self.gens)) + ')'
|
| 43 |
+
|
| 44 |
+
def __hash__(self):
|
| 45 |
+
return hash((self.__class__.__name__, self.dtype, self.dom, self.gens))
|
| 46 |
+
|
| 47 |
+
def __eq__(self, other):
|
| 48 |
+
"""Returns ``True`` if two domains are equivalent. """
|
| 49 |
+
return isinstance(other, FractionField) and \
|
| 50 |
+
self.dtype == other.dtype and self.dom == other.dom and self.gens == other.gens
|
| 51 |
+
|
| 52 |
+
def to_sympy(self, a):
|
| 53 |
+
"""Convert ``a`` to a SymPy object. """
|
| 54 |
+
return (basic_from_dict(a.numer().to_sympy_dict(), *self.gens) /
|
| 55 |
+
basic_from_dict(a.denom().to_sympy_dict(), *self.gens))
|
| 56 |
+
|
| 57 |
+
def from_sympy(self, a):
|
| 58 |
+
"""Convert SymPy's expression to ``dtype``. """
|
| 59 |
+
p, q = a.as_numer_denom()
|
| 60 |
+
|
| 61 |
+
num, _ = dict_from_basic(p, gens=self.gens)
|
| 62 |
+
den, _ = dict_from_basic(q, gens=self.gens)
|
| 63 |
+
|
| 64 |
+
for k, v in num.items():
|
| 65 |
+
num[k] = self.dom.from_sympy(v)
|
| 66 |
+
|
| 67 |
+
for k, v in den.items():
|
| 68 |
+
den[k] = self.dom.from_sympy(v)
|
| 69 |
+
|
| 70 |
+
return self((num, den)).cancel()
|
| 71 |
+
|
| 72 |
+
def from_ZZ(K1, a, K0):
|
| 73 |
+
"""Convert a Python ``int`` object to ``dtype``. """
|
| 74 |
+
return K1(K1.dom.convert(a, K0))
|
| 75 |
+
|
| 76 |
+
def from_ZZ_python(K1, a, K0):
|
| 77 |
+
"""Convert a Python ``int`` object to ``dtype``. """
|
| 78 |
+
return K1(K1.dom.convert(a, K0))
|
| 79 |
+
|
| 80 |
+
def from_QQ_python(K1, a, K0):
|
| 81 |
+
"""Convert a Python ``Fraction`` object to ``dtype``. """
|
| 82 |
+
return K1(K1.dom.convert(a, K0))
|
| 83 |
+
|
| 84 |
+
def from_ZZ_gmpy(K1, a, K0):
|
| 85 |
+
"""Convert a GMPY ``mpz`` object to ``dtype``. """
|
| 86 |
+
return K1(K1.dom.convert(a, K0))
|
| 87 |
+
|
| 88 |
+
def from_QQ_gmpy(K1, a, K0):
|
| 89 |
+
"""Convert a GMPY ``mpq`` object to ``dtype``. """
|
| 90 |
+
return K1(K1.dom.convert(a, K0))
|
| 91 |
+
|
| 92 |
+
def from_RealField(K1, a, K0):
|
| 93 |
+
"""Convert a mpmath ``mpf`` object to ``dtype``. """
|
| 94 |
+
return K1(K1.dom.convert(a, K0))
|
| 95 |
+
|
| 96 |
+
def from_GlobalPolynomialRing(K1, a, K0):
|
| 97 |
+
"""Convert a ``DMF`` object to ``dtype``. """
|
| 98 |
+
if K1.gens == K0.gens:
|
| 99 |
+
if K1.dom == K0.dom:
|
| 100 |
+
return K1(a.to_list())
|
| 101 |
+
else:
|
| 102 |
+
return K1(a.convert(K1.dom).to_list())
|
| 103 |
+
else:
|
| 104 |
+
monoms, coeffs = _dict_reorder(a.to_dict(), K0.gens, K1.gens)
|
| 105 |
+
|
| 106 |
+
if K1.dom != K0.dom:
|
| 107 |
+
coeffs = [ K1.dom.convert(c, K0.dom) for c in coeffs ]
|
| 108 |
+
|
| 109 |
+
return K1(dict(zip(monoms, coeffs)))
|
| 110 |
+
|
| 111 |
+
def from_FractionField(K1, a, K0):
|
| 112 |
+
"""
|
| 113 |
+
Convert a fraction field element to another fraction field.
|
| 114 |
+
|
| 115 |
+
Examples
|
| 116 |
+
========
|
| 117 |
+
|
| 118 |
+
>>> from sympy.polys.polyclasses import DMF
|
| 119 |
+
>>> from sympy.polys.domains import ZZ, QQ
|
| 120 |
+
>>> from sympy.abc import x
|
| 121 |
+
|
| 122 |
+
>>> f = DMF(([ZZ(1), ZZ(2)], [ZZ(1), ZZ(1)]), ZZ)
|
| 123 |
+
|
| 124 |
+
>>> QQx = QQ.old_frac_field(x)
|
| 125 |
+
>>> ZZx = ZZ.old_frac_field(x)
|
| 126 |
+
|
| 127 |
+
>>> QQx.from_FractionField(f, ZZx)
|
| 128 |
+
DMF([1, 2], [1, 1], QQ)
|
| 129 |
+
|
| 130 |
+
"""
|
| 131 |
+
if K1.gens == K0.gens:
|
| 132 |
+
if K1.dom == K0.dom:
|
| 133 |
+
return a
|
| 134 |
+
else:
|
| 135 |
+
return K1((a.numer().convert(K1.dom).to_list(),
|
| 136 |
+
a.denom().convert(K1.dom).to_list()))
|
| 137 |
+
elif set(K0.gens).issubset(K1.gens):
|
| 138 |
+
nmonoms, ncoeffs = _dict_reorder(
|
| 139 |
+
a.numer().to_dict(), K0.gens, K1.gens)
|
| 140 |
+
dmonoms, dcoeffs = _dict_reorder(
|
| 141 |
+
a.denom().to_dict(), K0.gens, K1.gens)
|
| 142 |
+
|
| 143 |
+
if K1.dom != K0.dom:
|
| 144 |
+
ncoeffs = [ K1.dom.convert(c, K0.dom) for c in ncoeffs ]
|
| 145 |
+
dcoeffs = [ K1.dom.convert(c, K0.dom) for c in dcoeffs ]
|
| 146 |
+
|
| 147 |
+
return K1((dict(zip(nmonoms, ncoeffs)), dict(zip(dmonoms, dcoeffs))))
|
| 148 |
+
|
| 149 |
+
def get_ring(self):
|
| 150 |
+
"""Returns a ring associated with ``self``. """
|
| 151 |
+
from sympy.polys.domains import PolynomialRing
|
| 152 |
+
return PolynomialRing(self.dom, *self.gens)
|
| 153 |
+
|
| 154 |
+
def poly_ring(self, *gens):
|
| 155 |
+
"""Returns a polynomial ring, i.e. `K[X]`. """
|
| 156 |
+
raise NotImplementedError('nested domains not allowed')
|
| 157 |
+
|
| 158 |
+
def frac_field(self, *gens):
|
| 159 |
+
"""Returns a fraction field, i.e. `K(X)`. """
|
| 160 |
+
raise NotImplementedError('nested domains not allowed')
|
| 161 |
+
|
| 162 |
+
def is_positive(self, a):
|
| 163 |
+
"""Returns True if ``a`` is positive. """
|
| 164 |
+
return self.dom.is_positive(a.numer().LC())
|
| 165 |
+
|
| 166 |
+
def is_negative(self, a):
|
| 167 |
+
"""Returns True if ``a`` is negative. """
|
| 168 |
+
return self.dom.is_negative(a.numer().LC())
|
| 169 |
+
|
| 170 |
+
def is_nonpositive(self, a):
|
| 171 |
+
"""Returns True if ``a`` is non-positive. """
|
| 172 |
+
return self.dom.is_nonpositive(a.numer().LC())
|
| 173 |
+
|
| 174 |
+
def is_nonnegative(self, a):
|
| 175 |
+
"""Returns True if ``a`` is non-negative. """
|
| 176 |
+
return self.dom.is_nonnegative(a.numer().LC())
|
| 177 |
+
|
| 178 |
+
def numer(self, a):
|
| 179 |
+
"""Returns numerator of ``a``. """
|
| 180 |
+
return a.numer()
|
| 181 |
+
|
| 182 |
+
def denom(self, a):
|
| 183 |
+
"""Returns denominator of ``a``. """
|
| 184 |
+
return a.denom()
|
| 185 |
+
|
| 186 |
+
def factorial(self, a):
|
| 187 |
+
"""Returns factorial of ``a``. """
|
| 188 |
+
return self.dtype(self.dom.factorial(a))
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/old_polynomialring.py
ADDED
|
@@ -0,0 +1,490 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`PolynomialRing` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.polys.agca.modules import FreeModulePolyRing
|
| 5 |
+
from sympy.polys.domains.compositedomain import CompositeDomain
|
| 6 |
+
from sympy.polys.domains.old_fractionfield import FractionField
|
| 7 |
+
from sympy.polys.domains.ring import Ring
|
| 8 |
+
from sympy.polys.orderings import monomial_key, build_product_order
|
| 9 |
+
from sympy.polys.polyclasses import DMP, DMF
|
| 10 |
+
from sympy.polys.polyerrors import (GeneratorsNeeded, PolynomialError,
|
| 11 |
+
CoercionFailed, ExactQuotientFailed, NotReversible)
|
| 12 |
+
from sympy.polys.polyutils import dict_from_basic, basic_from_dict, _dict_reorder
|
| 13 |
+
from sympy.utilities import public
|
| 14 |
+
from sympy.utilities.iterables import iterable
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
@public
|
| 18 |
+
class PolynomialRingBase(Ring, CompositeDomain):
|
| 19 |
+
"""
|
| 20 |
+
Base class for generalized polynomial rings.
|
| 21 |
+
|
| 22 |
+
This base class should be used for uniform access to generalized polynomial
|
| 23 |
+
rings. Subclasses only supply information about the element storage etc.
|
| 24 |
+
|
| 25 |
+
Do not instantiate.
|
| 26 |
+
"""
|
| 27 |
+
|
| 28 |
+
has_assoc_Ring = True
|
| 29 |
+
has_assoc_Field = True
|
| 30 |
+
|
| 31 |
+
default_order = "grevlex"
|
| 32 |
+
|
| 33 |
+
def __init__(self, dom, *gens, **opts):
|
| 34 |
+
if not gens:
|
| 35 |
+
raise GeneratorsNeeded("generators not specified")
|
| 36 |
+
|
| 37 |
+
lev = len(gens) - 1
|
| 38 |
+
self.ngens = len(gens)
|
| 39 |
+
|
| 40 |
+
self.zero = self.dtype.zero(lev, dom)
|
| 41 |
+
self.one = self.dtype.one(lev, dom)
|
| 42 |
+
|
| 43 |
+
self.domain = self.dom = dom
|
| 44 |
+
self.symbols = self.gens = gens
|
| 45 |
+
# NOTE 'order' may not be set if inject was called through CompositeDomain
|
| 46 |
+
self.order = opts.get('order', monomial_key(self.default_order))
|
| 47 |
+
|
| 48 |
+
def set_domain(self, dom):
|
| 49 |
+
"""Return a new polynomial ring with given domain. """
|
| 50 |
+
return self.__class__(dom, *self.gens, order=self.order)
|
| 51 |
+
|
| 52 |
+
def new(self, element):
|
| 53 |
+
return self.dtype(element, self.dom, len(self.gens) - 1)
|
| 54 |
+
|
| 55 |
+
def _ground_new(self, element):
|
| 56 |
+
return self.one.ground_new(element)
|
| 57 |
+
|
| 58 |
+
def _from_dict(self, element):
|
| 59 |
+
return DMP.from_dict(element, len(self.gens) - 1, self.dom)
|
| 60 |
+
|
| 61 |
+
def __str__(self):
|
| 62 |
+
s_order = str(self.order)
|
| 63 |
+
orderstr = (
|
| 64 |
+
" order=" + s_order) if s_order != self.default_order else ""
|
| 65 |
+
return str(self.dom) + '[' + ','.join(map(str, self.gens)) + orderstr + ']'
|
| 66 |
+
|
| 67 |
+
def __hash__(self):
|
| 68 |
+
return hash((self.__class__.__name__, self.dtype, self.dom,
|
| 69 |
+
self.gens, self.order))
|
| 70 |
+
|
| 71 |
+
def __eq__(self, other):
|
| 72 |
+
"""Returns ``True`` if two domains are equivalent. """
|
| 73 |
+
return isinstance(other, PolynomialRingBase) and \
|
| 74 |
+
self.dtype == other.dtype and self.dom == other.dom and \
|
| 75 |
+
self.gens == other.gens and self.order == other.order
|
| 76 |
+
|
| 77 |
+
def from_ZZ(K1, a, K0):
|
| 78 |
+
"""Convert a Python ``int`` object to ``dtype``. """
|
| 79 |
+
return K1._ground_new(K1.dom.convert(a, K0))
|
| 80 |
+
|
| 81 |
+
def from_ZZ_python(K1, a, K0):
|
| 82 |
+
"""Convert a Python ``int`` object to ``dtype``. """
|
| 83 |
+
return K1._ground_new(K1.dom.convert(a, K0))
|
| 84 |
+
|
| 85 |
+
def from_QQ(K1, a, K0):
|
| 86 |
+
"""Convert a Python ``Fraction`` object to ``dtype``. """
|
| 87 |
+
return K1._ground_new(K1.dom.convert(a, K0))
|
| 88 |
+
|
| 89 |
+
def from_QQ_python(K1, a, K0):
|
| 90 |
+
"""Convert a Python ``Fraction`` object to ``dtype``. """
|
| 91 |
+
return K1._ground_new(K1.dom.convert(a, K0))
|
| 92 |
+
|
| 93 |
+
def from_ZZ_gmpy(K1, a, K0):
|
| 94 |
+
"""Convert a GMPY ``mpz`` object to ``dtype``. """
|
| 95 |
+
return K1._ground_new(K1.dom.convert(a, K0))
|
| 96 |
+
|
| 97 |
+
def from_QQ_gmpy(K1, a, K0):
|
| 98 |
+
"""Convert a GMPY ``mpq`` object to ``dtype``. """
|
| 99 |
+
return K1._ground_new(K1.dom.convert(a, K0))
|
| 100 |
+
|
| 101 |
+
def from_RealField(K1, a, K0):
|
| 102 |
+
"""Convert a mpmath ``mpf`` object to ``dtype``. """
|
| 103 |
+
return K1._ground_new(K1.dom.convert(a, K0))
|
| 104 |
+
|
| 105 |
+
def from_AlgebraicField(K1, a, K0):
|
| 106 |
+
"""Convert a ``ANP`` object to ``dtype``. """
|
| 107 |
+
if K1.dom == K0:
|
| 108 |
+
return K1._ground_new(a)
|
| 109 |
+
|
| 110 |
+
def from_PolynomialRing(K1, a, K0):
|
| 111 |
+
"""Convert a ``PolyElement`` object to ``dtype``. """
|
| 112 |
+
if K1.gens == K0.symbols:
|
| 113 |
+
if K1.dom == K0.dom:
|
| 114 |
+
return K1(dict(a)) # set the correct ring
|
| 115 |
+
else:
|
| 116 |
+
convert_dom = lambda c: K1.dom.convert_from(c, K0.dom)
|
| 117 |
+
return K1._from_dict({m: convert_dom(c) for m, c in a.items()})
|
| 118 |
+
else:
|
| 119 |
+
monoms, coeffs = _dict_reorder(a.to_dict(), K0.symbols, K1.gens)
|
| 120 |
+
|
| 121 |
+
if K1.dom != K0.dom:
|
| 122 |
+
coeffs = [ K1.dom.convert(c, K0.dom) for c in coeffs ]
|
| 123 |
+
|
| 124 |
+
return K1._from_dict(dict(zip(monoms, coeffs)))
|
| 125 |
+
|
| 126 |
+
def from_GlobalPolynomialRing(K1, a, K0):
|
| 127 |
+
"""Convert a ``DMP`` object to ``dtype``. """
|
| 128 |
+
if K1.gens == K0.gens:
|
| 129 |
+
if K1.dom != K0.dom:
|
| 130 |
+
a = a.convert(K1.dom)
|
| 131 |
+
return K1(a.to_list())
|
| 132 |
+
else:
|
| 133 |
+
monoms, coeffs = _dict_reorder(a.to_dict(), K0.gens, K1.gens)
|
| 134 |
+
|
| 135 |
+
if K1.dom != K0.dom:
|
| 136 |
+
coeffs = [ K1.dom.convert(c, K0.dom) for c in coeffs ]
|
| 137 |
+
|
| 138 |
+
return K1(dict(zip(monoms, coeffs)))
|
| 139 |
+
|
| 140 |
+
def get_field(self):
|
| 141 |
+
"""Returns a field associated with ``self``. """
|
| 142 |
+
return FractionField(self.dom, *self.gens)
|
| 143 |
+
|
| 144 |
+
def poly_ring(self, *gens):
|
| 145 |
+
"""Returns a polynomial ring, i.e. ``K[X]``. """
|
| 146 |
+
raise NotImplementedError('nested domains not allowed')
|
| 147 |
+
|
| 148 |
+
def frac_field(self, *gens):
|
| 149 |
+
"""Returns a fraction field, i.e. ``K(X)``. """
|
| 150 |
+
raise NotImplementedError('nested domains not allowed')
|
| 151 |
+
|
| 152 |
+
def revert(self, a):
|
| 153 |
+
try:
|
| 154 |
+
return self.exquo(self.one, a)
|
| 155 |
+
except (ExactQuotientFailed, ZeroDivisionError):
|
| 156 |
+
raise NotReversible('%s is not a unit' % a)
|
| 157 |
+
|
| 158 |
+
def gcdex(self, a, b):
|
| 159 |
+
"""Extended GCD of ``a`` and ``b``. """
|
| 160 |
+
return a.gcdex(b)
|
| 161 |
+
|
| 162 |
+
def gcd(self, a, b):
|
| 163 |
+
"""Returns GCD of ``a`` and ``b``. """
|
| 164 |
+
return a.gcd(b)
|
| 165 |
+
|
| 166 |
+
def lcm(self, a, b):
|
| 167 |
+
"""Returns LCM of ``a`` and ``b``. """
|
| 168 |
+
return a.lcm(b)
|
| 169 |
+
|
| 170 |
+
def factorial(self, a):
|
| 171 |
+
"""Returns factorial of ``a``. """
|
| 172 |
+
return self.dtype(self.dom.factorial(a))
|
| 173 |
+
|
| 174 |
+
def _vector_to_sdm(self, v, order):
|
| 175 |
+
"""
|
| 176 |
+
For internal use by the modules class.
|
| 177 |
+
|
| 178 |
+
Convert an iterable of elements of this ring into a sparse distributed
|
| 179 |
+
module element.
|
| 180 |
+
"""
|
| 181 |
+
raise NotImplementedError
|
| 182 |
+
|
| 183 |
+
def _sdm_to_dics(self, s, n):
|
| 184 |
+
"""Helper for _sdm_to_vector."""
|
| 185 |
+
from sympy.polys.distributedmodules import sdm_to_dict
|
| 186 |
+
dic = sdm_to_dict(s)
|
| 187 |
+
res = [{} for _ in range(n)]
|
| 188 |
+
for k, v in dic.items():
|
| 189 |
+
res[k[0]][k[1:]] = v
|
| 190 |
+
return res
|
| 191 |
+
|
| 192 |
+
def _sdm_to_vector(self, s, n):
|
| 193 |
+
"""
|
| 194 |
+
For internal use by the modules class.
|
| 195 |
+
|
| 196 |
+
Convert a sparse distributed module into a list of length ``n``.
|
| 197 |
+
|
| 198 |
+
Examples
|
| 199 |
+
========
|
| 200 |
+
|
| 201 |
+
>>> from sympy import QQ, ilex
|
| 202 |
+
>>> from sympy.abc import x, y
|
| 203 |
+
>>> R = QQ.old_poly_ring(x, y, order=ilex)
|
| 204 |
+
>>> L = [((1, 1, 1), QQ(1)), ((0, 1, 0), QQ(1)), ((0, 0, 1), QQ(2))]
|
| 205 |
+
>>> R._sdm_to_vector(L, 2)
|
| 206 |
+
[DMF([[1], [2, 0]], [[1]], QQ), DMF([[1, 0], []], [[1]], QQ)]
|
| 207 |
+
"""
|
| 208 |
+
dics = self._sdm_to_dics(s, n)
|
| 209 |
+
# NOTE this works for global and local rings!
|
| 210 |
+
return [self(x) for x in dics]
|
| 211 |
+
|
| 212 |
+
def free_module(self, rank):
|
| 213 |
+
"""
|
| 214 |
+
Generate a free module of rank ``rank`` over ``self``.
|
| 215 |
+
|
| 216 |
+
Examples
|
| 217 |
+
========
|
| 218 |
+
|
| 219 |
+
>>> from sympy.abc import x
|
| 220 |
+
>>> from sympy import QQ
|
| 221 |
+
>>> QQ.old_poly_ring(x).free_module(2)
|
| 222 |
+
QQ[x]**2
|
| 223 |
+
"""
|
| 224 |
+
return FreeModulePolyRing(self, rank)
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
def _vector_to_sdm_helper(v, order):
|
| 228 |
+
"""Helper method for common code in Global and Local poly rings."""
|
| 229 |
+
from sympy.polys.distributedmodules import sdm_from_dict
|
| 230 |
+
d = {}
|
| 231 |
+
for i, e in enumerate(v):
|
| 232 |
+
for key, value in e.to_dict().items():
|
| 233 |
+
d[(i,) + key] = value
|
| 234 |
+
return sdm_from_dict(d, order)
|
| 235 |
+
|
| 236 |
+
|
| 237 |
+
@public
|
| 238 |
+
class GlobalPolynomialRing(PolynomialRingBase):
|
| 239 |
+
"""A true polynomial ring, with objects DMP. """
|
| 240 |
+
|
| 241 |
+
is_PolynomialRing = is_Poly = True
|
| 242 |
+
dtype = DMP
|
| 243 |
+
|
| 244 |
+
def new(self, element):
|
| 245 |
+
if isinstance(element, dict):
|
| 246 |
+
return DMP.from_dict(element, len(self.gens) - 1, self.dom)
|
| 247 |
+
elif element in self.dom:
|
| 248 |
+
return self._ground_new(self.dom.convert(element))
|
| 249 |
+
else:
|
| 250 |
+
return self.dtype(element, self.dom, len(self.gens) - 1)
|
| 251 |
+
|
| 252 |
+
def from_FractionField(K1, a, K0):
|
| 253 |
+
"""
|
| 254 |
+
Convert a ``DMF`` object to ``DMP``.
|
| 255 |
+
|
| 256 |
+
Examples
|
| 257 |
+
========
|
| 258 |
+
|
| 259 |
+
>>> from sympy.polys.polyclasses import DMP, DMF
|
| 260 |
+
>>> from sympy.polys.domains import ZZ
|
| 261 |
+
>>> from sympy.abc import x
|
| 262 |
+
|
| 263 |
+
>>> f = DMF(([ZZ(1), ZZ(1)], [ZZ(1)]), ZZ)
|
| 264 |
+
>>> K = ZZ.old_frac_field(x)
|
| 265 |
+
|
| 266 |
+
>>> F = ZZ.old_poly_ring(x).from_FractionField(f, K)
|
| 267 |
+
|
| 268 |
+
>>> F == DMP([ZZ(1), ZZ(1)], ZZ)
|
| 269 |
+
True
|
| 270 |
+
>>> type(F) # doctest: +SKIP
|
| 271 |
+
<class 'sympy.polys.polyclasses.DMP_Python'>
|
| 272 |
+
|
| 273 |
+
"""
|
| 274 |
+
if a.denom().is_one:
|
| 275 |
+
return K1.from_GlobalPolynomialRing(a.numer(), K0)
|
| 276 |
+
|
| 277 |
+
def to_sympy(self, a):
|
| 278 |
+
"""Convert ``a`` to a SymPy object. """
|
| 279 |
+
return basic_from_dict(a.to_sympy_dict(), *self.gens)
|
| 280 |
+
|
| 281 |
+
def from_sympy(self, a):
|
| 282 |
+
"""Convert SymPy's expression to ``dtype``. """
|
| 283 |
+
try:
|
| 284 |
+
rep, _ = dict_from_basic(a, gens=self.gens)
|
| 285 |
+
except PolynomialError:
|
| 286 |
+
raise CoercionFailed("Cannot convert %s to type %s" % (a, self))
|
| 287 |
+
|
| 288 |
+
for k, v in rep.items():
|
| 289 |
+
rep[k] = self.dom.from_sympy(v)
|
| 290 |
+
|
| 291 |
+
return DMP.from_dict(rep, self.ngens - 1, self.dom)
|
| 292 |
+
|
| 293 |
+
def is_positive(self, a):
|
| 294 |
+
"""Returns True if ``LC(a)`` is positive. """
|
| 295 |
+
return self.dom.is_positive(a.LC())
|
| 296 |
+
|
| 297 |
+
def is_negative(self, a):
|
| 298 |
+
"""Returns True if ``LC(a)`` is negative. """
|
| 299 |
+
return self.dom.is_negative(a.LC())
|
| 300 |
+
|
| 301 |
+
def is_nonpositive(self, a):
|
| 302 |
+
"""Returns True if ``LC(a)`` is non-positive. """
|
| 303 |
+
return self.dom.is_nonpositive(a.LC())
|
| 304 |
+
|
| 305 |
+
def is_nonnegative(self, a):
|
| 306 |
+
"""Returns True if ``LC(a)`` is non-negative. """
|
| 307 |
+
return self.dom.is_nonnegative(a.LC())
|
| 308 |
+
|
| 309 |
+
def _vector_to_sdm(self, v, order):
|
| 310 |
+
"""
|
| 311 |
+
Examples
|
| 312 |
+
========
|
| 313 |
+
|
| 314 |
+
>>> from sympy import lex, QQ
|
| 315 |
+
>>> from sympy.abc import x, y
|
| 316 |
+
>>> R = QQ.old_poly_ring(x, y)
|
| 317 |
+
>>> f = R.convert(x + 2*y)
|
| 318 |
+
>>> g = R.convert(x * y)
|
| 319 |
+
>>> R._vector_to_sdm([f, g], lex)
|
| 320 |
+
[((1, 1, 1), 1), ((0, 1, 0), 1), ((0, 0, 1), 2)]
|
| 321 |
+
"""
|
| 322 |
+
return _vector_to_sdm_helper(v, order)
|
| 323 |
+
|
| 324 |
+
|
| 325 |
+
class GeneralizedPolynomialRing(PolynomialRingBase):
|
| 326 |
+
"""A generalized polynomial ring, with objects DMF. """
|
| 327 |
+
|
| 328 |
+
dtype = DMF
|
| 329 |
+
|
| 330 |
+
def new(self, a):
|
| 331 |
+
"""Construct an element of ``self`` domain from ``a``. """
|
| 332 |
+
res = self.dtype(a, self.dom, len(self.gens) - 1)
|
| 333 |
+
|
| 334 |
+
# make sure res is actually in our ring
|
| 335 |
+
if res.denom().terms(order=self.order)[0][0] != (0,)*len(self.gens):
|
| 336 |
+
from sympy.printing.str import sstr
|
| 337 |
+
raise CoercionFailed("denominator %s not allowed in %s"
|
| 338 |
+
% (sstr(res), self))
|
| 339 |
+
return res
|
| 340 |
+
|
| 341 |
+
def __contains__(self, a):
|
| 342 |
+
try:
|
| 343 |
+
a = self.convert(a)
|
| 344 |
+
except CoercionFailed:
|
| 345 |
+
return False
|
| 346 |
+
return a.denom().terms(order=self.order)[0][0] == (0,)*len(self.gens)
|
| 347 |
+
|
| 348 |
+
def to_sympy(self, a):
|
| 349 |
+
"""Convert ``a`` to a SymPy object. """
|
| 350 |
+
return (basic_from_dict(a.numer().to_sympy_dict(), *self.gens) /
|
| 351 |
+
basic_from_dict(a.denom().to_sympy_dict(), *self.gens))
|
| 352 |
+
|
| 353 |
+
def from_sympy(self, a):
|
| 354 |
+
"""Convert SymPy's expression to ``dtype``. """
|
| 355 |
+
p, q = a.as_numer_denom()
|
| 356 |
+
|
| 357 |
+
num, _ = dict_from_basic(p, gens=self.gens)
|
| 358 |
+
den, _ = dict_from_basic(q, gens=self.gens)
|
| 359 |
+
|
| 360 |
+
for k, v in num.items():
|
| 361 |
+
num[k] = self.dom.from_sympy(v)
|
| 362 |
+
|
| 363 |
+
for k, v in den.items():
|
| 364 |
+
den[k] = self.dom.from_sympy(v)
|
| 365 |
+
|
| 366 |
+
return self((num, den)).cancel()
|
| 367 |
+
|
| 368 |
+
def exquo(self, a, b):
|
| 369 |
+
"""Exact quotient of ``a`` and ``b``. """
|
| 370 |
+
# Elements are DMF that will always divide (except 0). The result is
|
| 371 |
+
# not guaranteed to be in this ring, so we have to check that.
|
| 372 |
+
r = a / b
|
| 373 |
+
|
| 374 |
+
try:
|
| 375 |
+
r = self.new((r.num, r.den))
|
| 376 |
+
except CoercionFailed:
|
| 377 |
+
raise ExactQuotientFailed(a, b, self)
|
| 378 |
+
|
| 379 |
+
return r
|
| 380 |
+
|
| 381 |
+
def from_FractionField(K1, a, K0):
|
| 382 |
+
dmf = K1.get_field().from_FractionField(a, K0)
|
| 383 |
+
return K1((dmf.num, dmf.den))
|
| 384 |
+
|
| 385 |
+
def _vector_to_sdm(self, v, order):
|
| 386 |
+
"""
|
| 387 |
+
Turn an iterable into a sparse distributed module.
|
| 388 |
+
|
| 389 |
+
Note that the vector is multiplied by a unit first to make all entries
|
| 390 |
+
polynomials.
|
| 391 |
+
|
| 392 |
+
Examples
|
| 393 |
+
========
|
| 394 |
+
|
| 395 |
+
>>> from sympy import ilex, QQ
|
| 396 |
+
>>> from sympy.abc import x, y
|
| 397 |
+
>>> R = QQ.old_poly_ring(x, y, order=ilex)
|
| 398 |
+
>>> f = R.convert((x + 2*y) / (1 + x))
|
| 399 |
+
>>> g = R.convert(x * y)
|
| 400 |
+
>>> R._vector_to_sdm([f, g], ilex)
|
| 401 |
+
[((0, 0, 1), 2), ((0, 1, 0), 1), ((1, 1, 1), 1), ((1,
|
| 402 |
+
2, 1), 1)]
|
| 403 |
+
"""
|
| 404 |
+
# NOTE this is quite inefficient...
|
| 405 |
+
u = self.one.numer()
|
| 406 |
+
for x in v:
|
| 407 |
+
u *= x.denom()
|
| 408 |
+
return _vector_to_sdm_helper([x.numer()*u/x.denom() for x in v], order)
|
| 409 |
+
|
| 410 |
+
|
| 411 |
+
@public
|
| 412 |
+
def PolynomialRing(dom, *gens, **opts):
|
| 413 |
+
r"""
|
| 414 |
+
Create a generalized multivariate polynomial ring.
|
| 415 |
+
|
| 416 |
+
A generalized polynomial ring is defined by a ground field `K`, a set
|
| 417 |
+
of generators (typically `x_1, \ldots, x_n`) and a monomial order `<`.
|
| 418 |
+
The monomial order can be global, local or mixed. In any case it induces
|
| 419 |
+
a total ordering on the monomials, and there exists for every (non-zero)
|
| 420 |
+
polynomial `f \in K[x_1, \ldots, x_n]` a well-defined "leading monomial"
|
| 421 |
+
`LM(f) = LM(f, >)`. One can then define a multiplicative subset
|
| 422 |
+
`S = S_> = \{f \in K[x_1, \ldots, x_n] | LM(f) = 1\}`. The generalized
|
| 423 |
+
polynomial ring corresponding to the monomial order is
|
| 424 |
+
`R = S^{-1}K[x_1, \ldots, x_n]`.
|
| 425 |
+
|
| 426 |
+
If `>` is a so-called global order, that is `1` is the smallest monomial,
|
| 427 |
+
then we just have `S = K` and `R = K[x_1, \ldots, x_n]`.
|
| 428 |
+
|
| 429 |
+
Examples
|
| 430 |
+
========
|
| 431 |
+
|
| 432 |
+
A few examples may make this clearer.
|
| 433 |
+
|
| 434 |
+
>>> from sympy.abc import x, y
|
| 435 |
+
>>> from sympy import QQ
|
| 436 |
+
|
| 437 |
+
Our first ring uses global lexicographic order.
|
| 438 |
+
|
| 439 |
+
>>> R1 = QQ.old_poly_ring(x, y, order=(("lex", x, y),))
|
| 440 |
+
|
| 441 |
+
The second ring uses local lexicographic order. Note that when using a
|
| 442 |
+
single (non-product) order, you can just specify the name and omit the
|
| 443 |
+
variables:
|
| 444 |
+
|
| 445 |
+
>>> R2 = QQ.old_poly_ring(x, y, order="ilex")
|
| 446 |
+
|
| 447 |
+
The third and fourth rings use a mixed orders:
|
| 448 |
+
|
| 449 |
+
>>> o1 = (("ilex", x), ("lex", y))
|
| 450 |
+
>>> o2 = (("lex", x), ("ilex", y))
|
| 451 |
+
>>> R3 = QQ.old_poly_ring(x, y, order=o1)
|
| 452 |
+
>>> R4 = QQ.old_poly_ring(x, y, order=o2)
|
| 453 |
+
|
| 454 |
+
We will investigate what elements of `K(x, y)` are contained in the various
|
| 455 |
+
rings.
|
| 456 |
+
|
| 457 |
+
>>> L = [x, 1/x, y/(1 + x), 1/(1 + y), 1/(1 + x*y)]
|
| 458 |
+
>>> test = lambda R: [f in R for f in L]
|
| 459 |
+
|
| 460 |
+
The first ring is just `K[x, y]`:
|
| 461 |
+
|
| 462 |
+
>>> test(R1)
|
| 463 |
+
[True, False, False, False, False]
|
| 464 |
+
|
| 465 |
+
The second ring is R1 localised at the maximal ideal (x, y):
|
| 466 |
+
|
| 467 |
+
>>> test(R2)
|
| 468 |
+
[True, False, True, True, True]
|
| 469 |
+
|
| 470 |
+
The third ring is R1 localised at the prime ideal (x):
|
| 471 |
+
|
| 472 |
+
>>> test(R3)
|
| 473 |
+
[True, False, True, False, True]
|
| 474 |
+
|
| 475 |
+
Finally the fourth ring is R1 localised at `S = K[x, y] \setminus yK[y]`:
|
| 476 |
+
|
| 477 |
+
>>> test(R4)
|
| 478 |
+
[True, False, False, True, False]
|
| 479 |
+
"""
|
| 480 |
+
|
| 481 |
+
order = opts.get("order", GeneralizedPolynomialRing.default_order)
|
| 482 |
+
if iterable(order):
|
| 483 |
+
order = build_product_order(order, gens)
|
| 484 |
+
order = monomial_key(order)
|
| 485 |
+
opts['order'] = order
|
| 486 |
+
|
| 487 |
+
if order.is_global:
|
| 488 |
+
return GlobalPolynomialRing(dom, *gens, **opts)
|
| 489 |
+
else:
|
| 490 |
+
return GeneralizedPolynomialRing(dom, *gens, **opts)
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/polynomialring.py
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`PolynomialRing` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.polys.domains.ring import Ring
|
| 5 |
+
from sympy.polys.domains.compositedomain import CompositeDomain
|
| 6 |
+
|
| 7 |
+
from sympy.polys.polyerrors import CoercionFailed, GeneratorsError
|
| 8 |
+
from sympy.utilities import public
|
| 9 |
+
|
| 10 |
+
@public
|
| 11 |
+
class PolynomialRing(Ring, CompositeDomain):
|
| 12 |
+
"""A class for representing multivariate polynomial rings. """
|
| 13 |
+
|
| 14 |
+
is_PolynomialRing = is_Poly = True
|
| 15 |
+
|
| 16 |
+
has_assoc_Ring = True
|
| 17 |
+
has_assoc_Field = True
|
| 18 |
+
|
| 19 |
+
def __init__(self, domain_or_ring, symbols=None, order=None):
|
| 20 |
+
from sympy.polys.rings import PolyRing
|
| 21 |
+
|
| 22 |
+
if isinstance(domain_or_ring, PolyRing) and symbols is None and order is None:
|
| 23 |
+
ring = domain_or_ring
|
| 24 |
+
else:
|
| 25 |
+
ring = PolyRing(symbols, domain_or_ring, order)
|
| 26 |
+
|
| 27 |
+
self.ring = ring
|
| 28 |
+
self.dtype = ring.dtype
|
| 29 |
+
|
| 30 |
+
self.gens = ring.gens
|
| 31 |
+
self.ngens = ring.ngens
|
| 32 |
+
self.symbols = ring.symbols
|
| 33 |
+
self.domain = ring.domain
|
| 34 |
+
|
| 35 |
+
|
| 36 |
+
if symbols:
|
| 37 |
+
if ring.domain.is_Field and ring.domain.is_Exact and len(symbols)==1:
|
| 38 |
+
self.is_PID = True
|
| 39 |
+
|
| 40 |
+
# TODO: remove this
|
| 41 |
+
self.dom = self.domain
|
| 42 |
+
|
| 43 |
+
def new(self, element):
|
| 44 |
+
return self.ring.ring_new(element)
|
| 45 |
+
|
| 46 |
+
def of_type(self, element):
|
| 47 |
+
"""Check if ``a`` is of type ``dtype``. """
|
| 48 |
+
return self.ring.is_element(element)
|
| 49 |
+
|
| 50 |
+
@property
|
| 51 |
+
def zero(self):
|
| 52 |
+
return self.ring.zero
|
| 53 |
+
|
| 54 |
+
@property
|
| 55 |
+
def one(self):
|
| 56 |
+
return self.ring.one
|
| 57 |
+
|
| 58 |
+
@property
|
| 59 |
+
def order(self):
|
| 60 |
+
return self.ring.order
|
| 61 |
+
|
| 62 |
+
def __str__(self):
|
| 63 |
+
return str(self.domain) + '[' + ','.join(map(str, self.symbols)) + ']'
|
| 64 |
+
|
| 65 |
+
def __hash__(self):
|
| 66 |
+
return hash((self.__class__.__name__, self.ring, self.domain, self.symbols))
|
| 67 |
+
|
| 68 |
+
def __eq__(self, other):
|
| 69 |
+
"""Returns `True` if two domains are equivalent. """
|
| 70 |
+
if not isinstance(other, PolynomialRing):
|
| 71 |
+
return NotImplemented
|
| 72 |
+
return self.ring == other.ring
|
| 73 |
+
|
| 74 |
+
def is_unit(self, a):
|
| 75 |
+
"""Returns ``True`` if ``a`` is a unit of ``self``"""
|
| 76 |
+
if not a.is_ground:
|
| 77 |
+
return False
|
| 78 |
+
K = self.domain
|
| 79 |
+
return K.is_unit(K.convert_from(a, self))
|
| 80 |
+
|
| 81 |
+
def canonical_unit(self, a):
|
| 82 |
+
u = self.domain.canonical_unit(a.LC)
|
| 83 |
+
return self.ring.ground_new(u)
|
| 84 |
+
|
| 85 |
+
def to_sympy(self, a):
|
| 86 |
+
"""Convert `a` to a SymPy object. """
|
| 87 |
+
return a.as_expr()
|
| 88 |
+
|
| 89 |
+
def from_sympy(self, a):
|
| 90 |
+
"""Convert SymPy's expression to `dtype`. """
|
| 91 |
+
return self.ring.from_expr(a)
|
| 92 |
+
|
| 93 |
+
def from_ZZ(K1, a, K0):
|
| 94 |
+
"""Convert a Python `int` object to `dtype`. """
|
| 95 |
+
return K1(K1.domain.convert(a, K0))
|
| 96 |
+
|
| 97 |
+
def from_ZZ_python(K1, a, K0):
|
| 98 |
+
"""Convert a Python `int` object to `dtype`. """
|
| 99 |
+
return K1(K1.domain.convert(a, K0))
|
| 100 |
+
|
| 101 |
+
def from_QQ(K1, a, K0):
|
| 102 |
+
"""Convert a Python `Fraction` object to `dtype`. """
|
| 103 |
+
return K1(K1.domain.convert(a, K0))
|
| 104 |
+
|
| 105 |
+
def from_QQ_python(K1, a, K0):
|
| 106 |
+
"""Convert a Python `Fraction` object to `dtype`. """
|
| 107 |
+
return K1(K1.domain.convert(a, K0))
|
| 108 |
+
|
| 109 |
+
def from_ZZ_gmpy(K1, a, K0):
|
| 110 |
+
"""Convert a GMPY `mpz` object to `dtype`. """
|
| 111 |
+
return K1(K1.domain.convert(a, K0))
|
| 112 |
+
|
| 113 |
+
def from_QQ_gmpy(K1, a, K0):
|
| 114 |
+
"""Convert a GMPY `mpq` object to `dtype`. """
|
| 115 |
+
return K1(K1.domain.convert(a, K0))
|
| 116 |
+
|
| 117 |
+
def from_GaussianIntegerRing(K1, a, K0):
|
| 118 |
+
"""Convert a `GaussianInteger` object to `dtype`. """
|
| 119 |
+
return K1(K1.domain.convert(a, K0))
|
| 120 |
+
|
| 121 |
+
def from_GaussianRationalField(K1, a, K0):
|
| 122 |
+
"""Convert a `GaussianRational` object to `dtype`. """
|
| 123 |
+
return K1(K1.domain.convert(a, K0))
|
| 124 |
+
|
| 125 |
+
def from_RealField(K1, a, K0):
|
| 126 |
+
"""Convert a mpmath `mpf` object to `dtype`. """
|
| 127 |
+
return K1(K1.domain.convert(a, K0))
|
| 128 |
+
|
| 129 |
+
def from_ComplexField(K1, a, K0):
|
| 130 |
+
"""Convert a mpmath `mpf` object to `dtype`. """
|
| 131 |
+
return K1(K1.domain.convert(a, K0))
|
| 132 |
+
|
| 133 |
+
def from_AlgebraicField(K1, a, K0):
|
| 134 |
+
"""Convert an algebraic number to ``dtype``. """
|
| 135 |
+
if K1.domain != K0:
|
| 136 |
+
a = K1.domain.convert_from(a, K0)
|
| 137 |
+
if a is not None:
|
| 138 |
+
return K1.new(a)
|
| 139 |
+
|
| 140 |
+
def from_PolynomialRing(K1, a, K0):
|
| 141 |
+
"""Convert a polynomial to ``dtype``. """
|
| 142 |
+
try:
|
| 143 |
+
return a.set_ring(K1.ring)
|
| 144 |
+
except (CoercionFailed, GeneratorsError):
|
| 145 |
+
return None
|
| 146 |
+
|
| 147 |
+
def from_FractionField(K1, a, K0):
|
| 148 |
+
"""Convert a rational function to ``dtype``. """
|
| 149 |
+
if K1.domain == K0:
|
| 150 |
+
return K1.ring.from_list([a])
|
| 151 |
+
|
| 152 |
+
q, r = K0.numer(a).div(K0.denom(a))
|
| 153 |
+
|
| 154 |
+
if r.is_zero:
|
| 155 |
+
return K1.from_PolynomialRing(q, K0.field.ring.to_domain())
|
| 156 |
+
else:
|
| 157 |
+
return None
|
| 158 |
+
|
| 159 |
+
def from_GlobalPolynomialRing(K1, a, K0):
|
| 160 |
+
"""Convert from old poly ring to ``dtype``. """
|
| 161 |
+
if K1.symbols == K0.gens:
|
| 162 |
+
ad = a.to_dict()
|
| 163 |
+
if K1.domain != K0.domain:
|
| 164 |
+
ad = {m: K1.domain.convert(c) for m, c in ad.items()}
|
| 165 |
+
return K1(ad)
|
| 166 |
+
elif a.is_ground and K0.domain == K1:
|
| 167 |
+
return K1.convert_from(a.to_list()[0], K0.domain)
|
| 168 |
+
|
| 169 |
+
def get_field(self):
|
| 170 |
+
"""Returns a field associated with `self`. """
|
| 171 |
+
return self.ring.to_field().to_domain()
|
| 172 |
+
|
| 173 |
+
def is_positive(self, a):
|
| 174 |
+
"""Returns True if `LC(a)` is positive. """
|
| 175 |
+
return self.domain.is_positive(a.LC)
|
| 176 |
+
|
| 177 |
+
def is_negative(self, a):
|
| 178 |
+
"""Returns True if `LC(a)` is negative. """
|
| 179 |
+
return self.domain.is_negative(a.LC)
|
| 180 |
+
|
| 181 |
+
def is_nonpositive(self, a):
|
| 182 |
+
"""Returns True if `LC(a)` is non-positive. """
|
| 183 |
+
return self.domain.is_nonpositive(a.LC)
|
| 184 |
+
|
| 185 |
+
def is_nonnegative(self, a):
|
| 186 |
+
"""Returns True if `LC(a)` is non-negative. """
|
| 187 |
+
return self.domain.is_nonnegative(a.LC)
|
| 188 |
+
|
| 189 |
+
def gcdex(self, a, b):
|
| 190 |
+
"""Extended GCD of `a` and `b`. """
|
| 191 |
+
return a.gcdex(b)
|
| 192 |
+
|
| 193 |
+
def gcd(self, a, b):
|
| 194 |
+
"""Returns GCD of `a` and `b`. """
|
| 195 |
+
return a.gcd(b)
|
| 196 |
+
|
| 197 |
+
def lcm(self, a, b):
|
| 198 |
+
"""Returns LCM of `a` and `b`. """
|
| 199 |
+
return a.lcm(b)
|
| 200 |
+
|
| 201 |
+
def factorial(self, a):
|
| 202 |
+
"""Returns factorial of `a`. """
|
| 203 |
+
return self.dtype(self.domain.factorial(a))
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/pythonfinitefield.py
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`PythonFiniteField` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.polys.domains.finitefield import FiniteField
|
| 5 |
+
from sympy.polys.domains.pythonintegerring import PythonIntegerRing
|
| 6 |
+
|
| 7 |
+
from sympy.utilities import public
|
| 8 |
+
|
| 9 |
+
@public
|
| 10 |
+
class PythonFiniteField(FiniteField):
|
| 11 |
+
"""Finite field based on Python's integers. """
|
| 12 |
+
|
| 13 |
+
alias = 'FF_python'
|
| 14 |
+
|
| 15 |
+
def __init__(self, mod, symmetric=True):
|
| 16 |
+
super().__init__(mod, PythonIntegerRing(), symmetric)
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/pythonintegerring.py
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`PythonIntegerRing` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.core.numbers import int_valued
|
| 5 |
+
from sympy.polys.domains.groundtypes import (
|
| 6 |
+
PythonInteger, SymPyInteger, sqrt as python_sqrt,
|
| 7 |
+
factorial as python_factorial, python_gcdex, python_gcd, python_lcm,
|
| 8 |
+
)
|
| 9 |
+
from sympy.polys.domains.integerring import IntegerRing
|
| 10 |
+
from sympy.polys.polyerrors import CoercionFailed
|
| 11 |
+
from sympy.utilities import public
|
| 12 |
+
|
| 13 |
+
@public
|
| 14 |
+
class PythonIntegerRing(IntegerRing):
|
| 15 |
+
"""Integer ring based on Python's ``int`` type.
|
| 16 |
+
|
| 17 |
+
This will be used as :ref:`ZZ` if ``gmpy`` and ``gmpy2`` are not
|
| 18 |
+
installed. Elements are instances of the standard Python ``int`` type.
|
| 19 |
+
"""
|
| 20 |
+
|
| 21 |
+
dtype = PythonInteger
|
| 22 |
+
zero = dtype(0)
|
| 23 |
+
one = dtype(1)
|
| 24 |
+
alias = 'ZZ_python'
|
| 25 |
+
|
| 26 |
+
def __init__(self):
|
| 27 |
+
"""Allow instantiation of this domain. """
|
| 28 |
+
|
| 29 |
+
def to_sympy(self, a):
|
| 30 |
+
"""Convert ``a`` to a SymPy object. """
|
| 31 |
+
return SymPyInteger(a)
|
| 32 |
+
|
| 33 |
+
def from_sympy(self, a):
|
| 34 |
+
"""Convert SymPy's Integer to ``dtype``. """
|
| 35 |
+
if a.is_Integer:
|
| 36 |
+
return PythonInteger(a.p)
|
| 37 |
+
elif int_valued(a):
|
| 38 |
+
return PythonInteger(int(a))
|
| 39 |
+
else:
|
| 40 |
+
raise CoercionFailed("expected an integer, got %s" % a)
|
| 41 |
+
|
| 42 |
+
def from_FF_python(K1, a, K0):
|
| 43 |
+
"""Convert ``ModularInteger(int)`` to Python's ``int``. """
|
| 44 |
+
return K0.to_int(a)
|
| 45 |
+
|
| 46 |
+
def from_ZZ_python(K1, a, K0):
|
| 47 |
+
"""Convert Python's ``int`` to Python's ``int``. """
|
| 48 |
+
return a
|
| 49 |
+
|
| 50 |
+
def from_QQ(K1, a, K0):
|
| 51 |
+
"""Convert Python's ``Fraction`` to Python's ``int``. """
|
| 52 |
+
if a.denominator == 1:
|
| 53 |
+
return a.numerator
|
| 54 |
+
|
| 55 |
+
def from_QQ_python(K1, a, K0):
|
| 56 |
+
"""Convert Python's ``Fraction`` to Python's ``int``. """
|
| 57 |
+
if a.denominator == 1:
|
| 58 |
+
return a.numerator
|
| 59 |
+
|
| 60 |
+
def from_FF_gmpy(K1, a, K0):
|
| 61 |
+
"""Convert ``ModularInteger(mpz)`` to Python's ``int``. """
|
| 62 |
+
return PythonInteger(K0.to_int(a))
|
| 63 |
+
|
| 64 |
+
def from_ZZ_gmpy(K1, a, K0):
|
| 65 |
+
"""Convert GMPY's ``mpz`` to Python's ``int``. """
|
| 66 |
+
return PythonInteger(a)
|
| 67 |
+
|
| 68 |
+
def from_QQ_gmpy(K1, a, K0):
|
| 69 |
+
"""Convert GMPY's ``mpq`` to Python's ``int``. """
|
| 70 |
+
if a.denom() == 1:
|
| 71 |
+
return PythonInteger(a.numer())
|
| 72 |
+
|
| 73 |
+
def from_RealField(K1, a, K0):
|
| 74 |
+
"""Convert mpmath's ``mpf`` to Python's ``int``. """
|
| 75 |
+
p, q = K0.to_rational(a)
|
| 76 |
+
|
| 77 |
+
if q == 1:
|
| 78 |
+
return PythonInteger(p)
|
| 79 |
+
|
| 80 |
+
def gcdex(self, a, b):
|
| 81 |
+
"""Compute extended GCD of ``a`` and ``b``. """
|
| 82 |
+
return python_gcdex(a, b)
|
| 83 |
+
|
| 84 |
+
def gcd(self, a, b):
|
| 85 |
+
"""Compute GCD of ``a`` and ``b``. """
|
| 86 |
+
return python_gcd(a, b)
|
| 87 |
+
|
| 88 |
+
def lcm(self, a, b):
|
| 89 |
+
"""Compute LCM of ``a`` and ``b``. """
|
| 90 |
+
return python_lcm(a, b)
|
| 91 |
+
|
| 92 |
+
def sqrt(self, a):
|
| 93 |
+
"""Compute square root of ``a``. """
|
| 94 |
+
return python_sqrt(a)
|
| 95 |
+
|
| 96 |
+
def factorial(self, a):
|
| 97 |
+
"""Compute factorial of ``a``. """
|
| 98 |
+
return python_factorial(a)
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/pythonrational.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Rational number type based on Python integers.
|
| 3 |
+
|
| 4 |
+
The PythonRational class from here has been moved to
|
| 5 |
+
sympy.external.pythonmpq
|
| 6 |
+
|
| 7 |
+
This module is just left here for backwards compatibility.
|
| 8 |
+
"""
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
from sympy.core.numbers import Rational
|
| 12 |
+
from sympy.core.sympify import _sympy_converter
|
| 13 |
+
from sympy.utilities import public
|
| 14 |
+
from sympy.external.pythonmpq import PythonMPQ
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
PythonRational = public(PythonMPQ)
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def sympify_pythonrational(arg):
|
| 21 |
+
return Rational(arg.numerator, arg.denominator)
|
| 22 |
+
_sympy_converter[PythonRational] = sympify_pythonrational
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/pythonrationalfield.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`PythonRationalField` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.polys.domains.groundtypes import PythonInteger, PythonRational, SymPyRational
|
| 5 |
+
from sympy.polys.domains.rationalfield import RationalField
|
| 6 |
+
from sympy.polys.polyerrors import CoercionFailed
|
| 7 |
+
from sympy.utilities import public
|
| 8 |
+
|
| 9 |
+
@public
|
| 10 |
+
class PythonRationalField(RationalField):
|
| 11 |
+
"""Rational field based on :ref:`MPQ`.
|
| 12 |
+
|
| 13 |
+
This will be used as :ref:`QQ` if ``gmpy`` and ``gmpy2`` are not
|
| 14 |
+
installed. Elements are instances of :ref:`MPQ`.
|
| 15 |
+
"""
|
| 16 |
+
|
| 17 |
+
dtype = PythonRational
|
| 18 |
+
zero = dtype(0)
|
| 19 |
+
one = dtype(1)
|
| 20 |
+
alias = 'QQ_python'
|
| 21 |
+
|
| 22 |
+
def __init__(self):
|
| 23 |
+
pass
|
| 24 |
+
|
| 25 |
+
def get_ring(self):
|
| 26 |
+
"""Returns ring associated with ``self``. """
|
| 27 |
+
from sympy.polys.domains import PythonIntegerRing
|
| 28 |
+
return PythonIntegerRing()
|
| 29 |
+
|
| 30 |
+
def to_sympy(self, a):
|
| 31 |
+
"""Convert `a` to a SymPy object. """
|
| 32 |
+
return SymPyRational(a.numerator, a.denominator)
|
| 33 |
+
|
| 34 |
+
def from_sympy(self, a):
|
| 35 |
+
"""Convert SymPy's Rational to `dtype`. """
|
| 36 |
+
if a.is_Rational:
|
| 37 |
+
return PythonRational(a.p, a.q)
|
| 38 |
+
elif a.is_Float:
|
| 39 |
+
from sympy.polys.domains import RR
|
| 40 |
+
p, q = RR.to_rational(a)
|
| 41 |
+
return PythonRational(int(p), int(q))
|
| 42 |
+
else:
|
| 43 |
+
raise CoercionFailed("expected `Rational` object, got %s" % a)
|
| 44 |
+
|
| 45 |
+
def from_ZZ_python(K1, a, K0):
|
| 46 |
+
"""Convert a Python `int` object to `dtype`. """
|
| 47 |
+
return PythonRational(a)
|
| 48 |
+
|
| 49 |
+
def from_QQ_python(K1, a, K0):
|
| 50 |
+
"""Convert a Python `Fraction` object to `dtype`. """
|
| 51 |
+
return a
|
| 52 |
+
|
| 53 |
+
def from_ZZ_gmpy(K1, a, K0):
|
| 54 |
+
"""Convert a GMPY `mpz` object to `dtype`. """
|
| 55 |
+
return PythonRational(PythonInteger(a))
|
| 56 |
+
|
| 57 |
+
def from_QQ_gmpy(K1, a, K0):
|
| 58 |
+
"""Convert a GMPY `mpq` object to `dtype`. """
|
| 59 |
+
return PythonRational(PythonInteger(a.numer()),
|
| 60 |
+
PythonInteger(a.denom()))
|
| 61 |
+
|
| 62 |
+
def from_RealField(K1, a, K0):
|
| 63 |
+
"""Convert a mpmath `mpf` object to `dtype`. """
|
| 64 |
+
p, q = K0.to_rational(a)
|
| 65 |
+
return PythonRational(int(p), int(q))
|
| 66 |
+
|
| 67 |
+
def numer(self, a):
|
| 68 |
+
"""Returns numerator of `a`. """
|
| 69 |
+
return a.numerator
|
| 70 |
+
|
| 71 |
+
def denom(self, a):
|
| 72 |
+
"""Returns denominator of `a`. """
|
| 73 |
+
return a.denominator
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/quotientring.py
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`QuotientRing` class."""
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.polys.agca.modules import FreeModuleQuotientRing
|
| 5 |
+
from sympy.polys.domains.ring import Ring
|
| 6 |
+
from sympy.polys.polyerrors import NotReversible, CoercionFailed
|
| 7 |
+
from sympy.utilities import public
|
| 8 |
+
|
| 9 |
+
# TODO
|
| 10 |
+
# - successive quotients (when quotient ideals are implemented)
|
| 11 |
+
# - poly rings over quotients?
|
| 12 |
+
# - division by non-units in integral domains?
|
| 13 |
+
|
| 14 |
+
@public
|
| 15 |
+
class QuotientRingElement:
|
| 16 |
+
"""
|
| 17 |
+
Class representing elements of (commutative) quotient rings.
|
| 18 |
+
|
| 19 |
+
Attributes:
|
| 20 |
+
|
| 21 |
+
- ring - containing ring
|
| 22 |
+
- data - element of ring.ring (i.e. base ring) representing self
|
| 23 |
+
"""
|
| 24 |
+
|
| 25 |
+
def __init__(self, ring, data):
|
| 26 |
+
self.ring = ring
|
| 27 |
+
self.data = data
|
| 28 |
+
|
| 29 |
+
def __str__(self):
|
| 30 |
+
from sympy.printing.str import sstr
|
| 31 |
+
data = self.ring.ring.to_sympy(self.data)
|
| 32 |
+
return sstr(data) + " + " + str(self.ring.base_ideal)
|
| 33 |
+
|
| 34 |
+
__repr__ = __str__
|
| 35 |
+
|
| 36 |
+
def __bool__(self):
|
| 37 |
+
return not self.ring.is_zero(self)
|
| 38 |
+
|
| 39 |
+
def __add__(self, om):
|
| 40 |
+
if not isinstance(om, self.__class__) or om.ring != self.ring:
|
| 41 |
+
try:
|
| 42 |
+
om = self.ring.convert(om)
|
| 43 |
+
except (NotImplementedError, CoercionFailed):
|
| 44 |
+
return NotImplemented
|
| 45 |
+
return self.ring(self.data + om.data)
|
| 46 |
+
|
| 47 |
+
__radd__ = __add__
|
| 48 |
+
|
| 49 |
+
def __neg__(self):
|
| 50 |
+
return self.ring(self.data*self.ring.ring.convert(-1))
|
| 51 |
+
|
| 52 |
+
def __sub__(self, om):
|
| 53 |
+
return self.__add__(-om)
|
| 54 |
+
|
| 55 |
+
def __rsub__(self, om):
|
| 56 |
+
return (-self).__add__(om)
|
| 57 |
+
|
| 58 |
+
def __mul__(self, o):
|
| 59 |
+
if not isinstance(o, self.__class__):
|
| 60 |
+
try:
|
| 61 |
+
o = self.ring.convert(o)
|
| 62 |
+
except (NotImplementedError, CoercionFailed):
|
| 63 |
+
return NotImplemented
|
| 64 |
+
return self.ring(self.data*o.data)
|
| 65 |
+
|
| 66 |
+
__rmul__ = __mul__
|
| 67 |
+
|
| 68 |
+
def __rtruediv__(self, o):
|
| 69 |
+
return self.ring.revert(self)*o
|
| 70 |
+
|
| 71 |
+
def __truediv__(self, o):
|
| 72 |
+
if not isinstance(o, self.__class__):
|
| 73 |
+
try:
|
| 74 |
+
o = self.ring.convert(o)
|
| 75 |
+
except (NotImplementedError, CoercionFailed):
|
| 76 |
+
return NotImplemented
|
| 77 |
+
return self.ring.revert(o)*self
|
| 78 |
+
|
| 79 |
+
def __pow__(self, oth):
|
| 80 |
+
if oth < 0:
|
| 81 |
+
return self.ring.revert(self) ** -oth
|
| 82 |
+
return self.ring(self.data ** oth)
|
| 83 |
+
|
| 84 |
+
def __eq__(self, om):
|
| 85 |
+
if not isinstance(om, self.__class__) or om.ring != self.ring:
|
| 86 |
+
return False
|
| 87 |
+
return self.ring.is_zero(self - om)
|
| 88 |
+
|
| 89 |
+
def __ne__(self, om):
|
| 90 |
+
return not self == om
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
class QuotientRing(Ring):
|
| 94 |
+
"""
|
| 95 |
+
Class representing (commutative) quotient rings.
|
| 96 |
+
|
| 97 |
+
You should not usually instantiate this by hand, instead use the constructor
|
| 98 |
+
from the base ring in the construction.
|
| 99 |
+
|
| 100 |
+
>>> from sympy.abc import x
|
| 101 |
+
>>> from sympy import QQ
|
| 102 |
+
>>> I = QQ.old_poly_ring(x).ideal(x**3 + 1)
|
| 103 |
+
>>> QQ.old_poly_ring(x).quotient_ring(I)
|
| 104 |
+
QQ[x]/<x**3 + 1>
|
| 105 |
+
|
| 106 |
+
Shorter versions are possible:
|
| 107 |
+
|
| 108 |
+
>>> QQ.old_poly_ring(x)/I
|
| 109 |
+
QQ[x]/<x**3 + 1>
|
| 110 |
+
|
| 111 |
+
>>> QQ.old_poly_ring(x)/[x**3 + 1]
|
| 112 |
+
QQ[x]/<x**3 + 1>
|
| 113 |
+
|
| 114 |
+
Attributes:
|
| 115 |
+
|
| 116 |
+
- ring - the base ring
|
| 117 |
+
- base_ideal - the ideal used to form the quotient
|
| 118 |
+
"""
|
| 119 |
+
|
| 120 |
+
has_assoc_Ring = True
|
| 121 |
+
has_assoc_Field = False
|
| 122 |
+
dtype = QuotientRingElement
|
| 123 |
+
|
| 124 |
+
def __init__(self, ring, ideal):
|
| 125 |
+
if not ideal.ring == ring:
|
| 126 |
+
raise ValueError('Ideal must belong to %s, got %s' % (ring, ideal))
|
| 127 |
+
self.ring = ring
|
| 128 |
+
self.base_ideal = ideal
|
| 129 |
+
self.zero = self(self.ring.zero)
|
| 130 |
+
self.one = self(self.ring.one)
|
| 131 |
+
|
| 132 |
+
def __str__(self):
|
| 133 |
+
return str(self.ring) + "/" + str(self.base_ideal)
|
| 134 |
+
|
| 135 |
+
def __hash__(self):
|
| 136 |
+
return hash((self.__class__.__name__, self.dtype, self.ring, self.base_ideal))
|
| 137 |
+
|
| 138 |
+
def new(self, a):
|
| 139 |
+
"""Construct an element of ``self`` domain from ``a``. """
|
| 140 |
+
if not isinstance(a, self.ring.dtype):
|
| 141 |
+
a = self.ring(a)
|
| 142 |
+
# TODO optionally disable reduction?
|
| 143 |
+
return self.dtype(self, self.base_ideal.reduce_element(a))
|
| 144 |
+
|
| 145 |
+
def __eq__(self, other):
|
| 146 |
+
"""Returns ``True`` if two domains are equivalent. """
|
| 147 |
+
return isinstance(other, QuotientRing) and \
|
| 148 |
+
self.ring == other.ring and self.base_ideal == other.base_ideal
|
| 149 |
+
|
| 150 |
+
def from_ZZ(K1, a, K0):
|
| 151 |
+
"""Convert a Python ``int`` object to ``dtype``. """
|
| 152 |
+
return K1(K1.ring.convert(a, K0))
|
| 153 |
+
|
| 154 |
+
from_ZZ_python = from_ZZ
|
| 155 |
+
from_QQ_python = from_ZZ_python
|
| 156 |
+
from_ZZ_gmpy = from_ZZ_python
|
| 157 |
+
from_QQ_gmpy = from_ZZ_python
|
| 158 |
+
from_RealField = from_ZZ_python
|
| 159 |
+
from_GlobalPolynomialRing = from_ZZ_python
|
| 160 |
+
from_FractionField = from_ZZ_python
|
| 161 |
+
|
| 162 |
+
def from_sympy(self, a):
|
| 163 |
+
return self(self.ring.from_sympy(a))
|
| 164 |
+
|
| 165 |
+
def to_sympy(self, a):
|
| 166 |
+
return self.ring.to_sympy(a.data)
|
| 167 |
+
|
| 168 |
+
def from_QuotientRing(self, a, K0):
|
| 169 |
+
if K0 == self:
|
| 170 |
+
return a
|
| 171 |
+
|
| 172 |
+
def poly_ring(self, *gens):
|
| 173 |
+
"""Returns a polynomial ring, i.e. ``K[X]``. """
|
| 174 |
+
raise NotImplementedError('nested domains not allowed')
|
| 175 |
+
|
| 176 |
+
def frac_field(self, *gens):
|
| 177 |
+
"""Returns a fraction field, i.e. ``K(X)``. """
|
| 178 |
+
raise NotImplementedError('nested domains not allowed')
|
| 179 |
+
|
| 180 |
+
def revert(self, a):
|
| 181 |
+
"""
|
| 182 |
+
Compute a**(-1), if possible.
|
| 183 |
+
"""
|
| 184 |
+
I = self.ring.ideal(a.data) + self.base_ideal
|
| 185 |
+
try:
|
| 186 |
+
return self(I.in_terms_of_generators(1)[0])
|
| 187 |
+
except ValueError: # 1 not in I
|
| 188 |
+
raise NotReversible('%s not a unit in %r' % (a, self))
|
| 189 |
+
|
| 190 |
+
def is_zero(self, a):
|
| 191 |
+
return self.base_ideal.contains(a.data)
|
| 192 |
+
|
| 193 |
+
def free_module(self, rank):
|
| 194 |
+
"""
|
| 195 |
+
Generate a free module of rank ``rank`` over ``self``.
|
| 196 |
+
|
| 197 |
+
>>> from sympy.abc import x
|
| 198 |
+
>>> from sympy import QQ
|
| 199 |
+
>>> (QQ.old_poly_ring(x)/[x**2 + 1]).free_module(2)
|
| 200 |
+
(QQ[x]/<x**2 + 1>)**2
|
| 201 |
+
"""
|
| 202 |
+
return FreeModuleQuotientRing(self, rank)
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/rationalfield.py
ADDED
|
@@ -0,0 +1,200 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`RationalField` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.external.gmpy import MPQ
|
| 5 |
+
|
| 6 |
+
from sympy.polys.domains.groundtypes import SymPyRational, is_square, sqrtrem
|
| 7 |
+
|
| 8 |
+
from sympy.polys.domains.characteristiczero import CharacteristicZero
|
| 9 |
+
from sympy.polys.domains.field import Field
|
| 10 |
+
from sympy.polys.domains.simpledomain import SimpleDomain
|
| 11 |
+
from sympy.polys.polyerrors import CoercionFailed
|
| 12 |
+
from sympy.utilities import public
|
| 13 |
+
|
| 14 |
+
@public
|
| 15 |
+
class RationalField(Field, CharacteristicZero, SimpleDomain):
|
| 16 |
+
r"""Abstract base class for the domain :ref:`QQ`.
|
| 17 |
+
|
| 18 |
+
The :py:class:`RationalField` class represents the field of rational
|
| 19 |
+
numbers $\mathbb{Q}$ as a :py:class:`~.Domain` in the domain system.
|
| 20 |
+
:py:class:`RationalField` is a superclass of
|
| 21 |
+
:py:class:`PythonRationalField` and :py:class:`GMPYRationalField` one of
|
| 22 |
+
which will be the implementation for :ref:`QQ` depending on whether either
|
| 23 |
+
of ``gmpy`` or ``gmpy2`` is installed or not.
|
| 24 |
+
|
| 25 |
+
See also
|
| 26 |
+
========
|
| 27 |
+
|
| 28 |
+
Domain
|
| 29 |
+
"""
|
| 30 |
+
|
| 31 |
+
rep = 'QQ'
|
| 32 |
+
alias = 'QQ'
|
| 33 |
+
|
| 34 |
+
is_RationalField = is_QQ = True
|
| 35 |
+
is_Numerical = True
|
| 36 |
+
|
| 37 |
+
has_assoc_Ring = True
|
| 38 |
+
has_assoc_Field = True
|
| 39 |
+
|
| 40 |
+
dtype = MPQ
|
| 41 |
+
zero = dtype(0)
|
| 42 |
+
one = dtype(1)
|
| 43 |
+
tp = type(one)
|
| 44 |
+
|
| 45 |
+
def __init__(self):
|
| 46 |
+
pass
|
| 47 |
+
|
| 48 |
+
def __eq__(self, other):
|
| 49 |
+
"""Returns ``True`` if two domains are equivalent. """
|
| 50 |
+
if isinstance(other, RationalField):
|
| 51 |
+
return True
|
| 52 |
+
else:
|
| 53 |
+
return NotImplemented
|
| 54 |
+
|
| 55 |
+
def __hash__(self):
|
| 56 |
+
"""Returns hash code of ``self``. """
|
| 57 |
+
return hash('QQ')
|
| 58 |
+
|
| 59 |
+
def get_ring(self):
|
| 60 |
+
"""Returns ring associated with ``self``. """
|
| 61 |
+
from sympy.polys.domains import ZZ
|
| 62 |
+
return ZZ
|
| 63 |
+
|
| 64 |
+
def to_sympy(self, a):
|
| 65 |
+
"""Convert ``a`` to a SymPy object. """
|
| 66 |
+
return SymPyRational(int(a.numerator), int(a.denominator))
|
| 67 |
+
|
| 68 |
+
def from_sympy(self, a):
|
| 69 |
+
"""Convert SymPy's Integer to ``dtype``. """
|
| 70 |
+
if a.is_Rational:
|
| 71 |
+
return MPQ(a.p, a.q)
|
| 72 |
+
elif a.is_Float:
|
| 73 |
+
from sympy.polys.domains import RR
|
| 74 |
+
return MPQ(*map(int, RR.to_rational(a)))
|
| 75 |
+
else:
|
| 76 |
+
raise CoercionFailed("expected `Rational` object, got %s" % a)
|
| 77 |
+
|
| 78 |
+
def algebraic_field(self, *extension, alias=None):
|
| 79 |
+
r"""Returns an algebraic field, i.e. `\mathbb{Q}(\alpha, \ldots)`.
|
| 80 |
+
|
| 81 |
+
Parameters
|
| 82 |
+
==========
|
| 83 |
+
|
| 84 |
+
*extension : One or more :py:class:`~.Expr`
|
| 85 |
+
Generators of the extension. These should be expressions that are
|
| 86 |
+
algebraic over `\mathbb{Q}`.
|
| 87 |
+
|
| 88 |
+
alias : str, :py:class:`~.Symbol`, None, optional (default=None)
|
| 89 |
+
If provided, this will be used as the alias symbol for the
|
| 90 |
+
primitive element of the returned :py:class:`~.AlgebraicField`.
|
| 91 |
+
|
| 92 |
+
Returns
|
| 93 |
+
=======
|
| 94 |
+
|
| 95 |
+
:py:class:`~.AlgebraicField`
|
| 96 |
+
A :py:class:`~.Domain` representing the algebraic field extension.
|
| 97 |
+
|
| 98 |
+
Examples
|
| 99 |
+
========
|
| 100 |
+
|
| 101 |
+
>>> from sympy import QQ, sqrt
|
| 102 |
+
>>> QQ.algebraic_field(sqrt(2))
|
| 103 |
+
QQ<sqrt(2)>
|
| 104 |
+
"""
|
| 105 |
+
from sympy.polys.domains import AlgebraicField
|
| 106 |
+
return AlgebraicField(self, *extension, alias=alias)
|
| 107 |
+
|
| 108 |
+
def from_AlgebraicField(K1, a, K0):
|
| 109 |
+
"""Convert a :py:class:`~.ANP` object to :ref:`QQ`.
|
| 110 |
+
|
| 111 |
+
See :py:meth:`~.Domain.convert`
|
| 112 |
+
"""
|
| 113 |
+
if a.is_ground:
|
| 114 |
+
return K1.convert(a.LC(), K0.dom)
|
| 115 |
+
|
| 116 |
+
def from_ZZ(K1, a, K0):
|
| 117 |
+
"""Convert a Python ``int`` object to ``dtype``. """
|
| 118 |
+
return MPQ(a)
|
| 119 |
+
|
| 120 |
+
def from_ZZ_python(K1, a, K0):
|
| 121 |
+
"""Convert a Python ``int`` object to ``dtype``. """
|
| 122 |
+
return MPQ(a)
|
| 123 |
+
|
| 124 |
+
def from_QQ(K1, a, K0):
|
| 125 |
+
"""Convert a Python ``Fraction`` object to ``dtype``. """
|
| 126 |
+
return MPQ(a.numerator, a.denominator)
|
| 127 |
+
|
| 128 |
+
def from_QQ_python(K1, a, K0):
|
| 129 |
+
"""Convert a Python ``Fraction`` object to ``dtype``. """
|
| 130 |
+
return MPQ(a.numerator, a.denominator)
|
| 131 |
+
|
| 132 |
+
def from_ZZ_gmpy(K1, a, K0):
|
| 133 |
+
"""Convert a GMPY ``mpz`` object to ``dtype``. """
|
| 134 |
+
return MPQ(a)
|
| 135 |
+
|
| 136 |
+
def from_QQ_gmpy(K1, a, K0):
|
| 137 |
+
"""Convert a GMPY ``mpq`` object to ``dtype``. """
|
| 138 |
+
return a
|
| 139 |
+
|
| 140 |
+
def from_GaussianRationalField(K1, a, K0):
|
| 141 |
+
"""Convert a ``GaussianElement`` object to ``dtype``. """
|
| 142 |
+
if a.y == 0:
|
| 143 |
+
return MPQ(a.x)
|
| 144 |
+
|
| 145 |
+
def from_RealField(K1, a, K0):
|
| 146 |
+
"""Convert a mpmath ``mpf`` object to ``dtype``. """
|
| 147 |
+
return MPQ(*map(int, K0.to_rational(a)))
|
| 148 |
+
|
| 149 |
+
def exquo(self, a, b):
|
| 150 |
+
"""Exact quotient of ``a`` and ``b``, implies ``__truediv__``. """
|
| 151 |
+
return MPQ(a) / MPQ(b)
|
| 152 |
+
|
| 153 |
+
def quo(self, a, b):
|
| 154 |
+
"""Quotient of ``a`` and ``b``, implies ``__truediv__``. """
|
| 155 |
+
return MPQ(a) / MPQ(b)
|
| 156 |
+
|
| 157 |
+
def rem(self, a, b):
|
| 158 |
+
"""Remainder of ``a`` and ``b``, implies nothing. """
|
| 159 |
+
return self.zero
|
| 160 |
+
|
| 161 |
+
def div(self, a, b):
|
| 162 |
+
"""Division of ``a`` and ``b``, implies ``__truediv__``. """
|
| 163 |
+
return MPQ(a) / MPQ(b), self.zero
|
| 164 |
+
|
| 165 |
+
def numer(self, a):
|
| 166 |
+
"""Returns numerator of ``a``. """
|
| 167 |
+
return a.numerator
|
| 168 |
+
|
| 169 |
+
def denom(self, a):
|
| 170 |
+
"""Returns denominator of ``a``. """
|
| 171 |
+
return a.denominator
|
| 172 |
+
|
| 173 |
+
def is_square(self, a):
|
| 174 |
+
"""Return ``True`` if ``a`` is a square.
|
| 175 |
+
|
| 176 |
+
Explanation
|
| 177 |
+
===========
|
| 178 |
+
A rational number is a square if and only if there exists
|
| 179 |
+
a rational number ``b`` such that ``b * b == a``.
|
| 180 |
+
"""
|
| 181 |
+
return is_square(a.numerator) and is_square(a.denominator)
|
| 182 |
+
|
| 183 |
+
def exsqrt(self, a):
|
| 184 |
+
"""Non-negative square root of ``a`` if ``a`` is a square.
|
| 185 |
+
|
| 186 |
+
See also
|
| 187 |
+
========
|
| 188 |
+
is_square
|
| 189 |
+
"""
|
| 190 |
+
if a.numerator < 0: # denominator is always positive
|
| 191 |
+
return None
|
| 192 |
+
p_sqrt, p_rem = sqrtrem(a.numerator)
|
| 193 |
+
if p_rem != 0:
|
| 194 |
+
return None
|
| 195 |
+
q_sqrt, q_rem = sqrtrem(a.denominator)
|
| 196 |
+
if q_rem != 0:
|
| 197 |
+
return None
|
| 198 |
+
return MPQ(p_sqrt, q_sqrt)
|
| 199 |
+
|
| 200 |
+
QQ = RationalField()
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/realfield.py
ADDED
|
@@ -0,0 +1,220 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`RealField` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.external.gmpy import SYMPY_INTS, MPQ
|
| 5 |
+
from sympy.core.numbers import Float
|
| 6 |
+
from sympy.polys.domains.field import Field
|
| 7 |
+
from sympy.polys.domains.simpledomain import SimpleDomain
|
| 8 |
+
from sympy.polys.domains.characteristiczero import CharacteristicZero
|
| 9 |
+
from sympy.polys.polyerrors import CoercionFailed
|
| 10 |
+
from sympy.utilities import public
|
| 11 |
+
|
| 12 |
+
from mpmath import MPContext
|
| 13 |
+
from mpmath.libmp import to_rational as _mpmath_to_rational
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def to_rational(s, max_denom, limit=True):
|
| 17 |
+
|
| 18 |
+
p, q = _mpmath_to_rational(s._mpf_)
|
| 19 |
+
|
| 20 |
+
# Needed for GROUND_TYPES=flint if gmpy2 is installed because mpmath's
|
| 21 |
+
# to_rational() function returns a gmpy2.mpz instance and if MPQ is
|
| 22 |
+
# flint.fmpq then MPQ(p, q) will fail.
|
| 23 |
+
p = int(p)
|
| 24 |
+
q = int(q)
|
| 25 |
+
|
| 26 |
+
if not limit or q <= max_denom:
|
| 27 |
+
return p, q
|
| 28 |
+
|
| 29 |
+
p0, q0, p1, q1 = 0, 1, 1, 0
|
| 30 |
+
n, d = p, q
|
| 31 |
+
|
| 32 |
+
while True:
|
| 33 |
+
a = n//d
|
| 34 |
+
q2 = q0 + a*q1
|
| 35 |
+
if q2 > max_denom:
|
| 36 |
+
break
|
| 37 |
+
p0, q0, p1, q1 = p1, q1, p0 + a*p1, q2
|
| 38 |
+
n, d = d, n - a*d
|
| 39 |
+
|
| 40 |
+
k = (max_denom - q0)//q1
|
| 41 |
+
|
| 42 |
+
number = MPQ(p, q)
|
| 43 |
+
bound1 = MPQ(p0 + k*p1, q0 + k*q1)
|
| 44 |
+
bound2 = MPQ(p1, q1)
|
| 45 |
+
|
| 46 |
+
if not bound2 or not bound1:
|
| 47 |
+
return p, q
|
| 48 |
+
elif abs(bound2 - number) <= abs(bound1 - number):
|
| 49 |
+
return bound2.numerator, bound2.denominator
|
| 50 |
+
else:
|
| 51 |
+
return bound1.numerator, bound1.denominator
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
@public
|
| 55 |
+
class RealField(Field, CharacteristicZero, SimpleDomain):
|
| 56 |
+
"""Real numbers up to the given precision. """
|
| 57 |
+
|
| 58 |
+
rep = 'RR'
|
| 59 |
+
|
| 60 |
+
is_RealField = is_RR = True
|
| 61 |
+
|
| 62 |
+
is_Exact = False
|
| 63 |
+
is_Numerical = True
|
| 64 |
+
is_PID = False
|
| 65 |
+
|
| 66 |
+
has_assoc_Ring = False
|
| 67 |
+
has_assoc_Field = True
|
| 68 |
+
|
| 69 |
+
_default_precision = 53
|
| 70 |
+
|
| 71 |
+
@property
|
| 72 |
+
def has_default_precision(self):
|
| 73 |
+
return self.precision == self._default_precision
|
| 74 |
+
|
| 75 |
+
@property
|
| 76 |
+
def precision(self):
|
| 77 |
+
return self._context.prec
|
| 78 |
+
|
| 79 |
+
@property
|
| 80 |
+
def dps(self):
|
| 81 |
+
return self._context.dps
|
| 82 |
+
|
| 83 |
+
@property
|
| 84 |
+
def tolerance(self):
|
| 85 |
+
return self._tolerance
|
| 86 |
+
|
| 87 |
+
def __init__(self, prec=None, dps=None, tol=None):
|
| 88 |
+
# XXX: The tol parameter is ignored but is kept for now for backwards
|
| 89 |
+
# compatibility.
|
| 90 |
+
|
| 91 |
+
context = MPContext()
|
| 92 |
+
|
| 93 |
+
if prec is None and dps is None:
|
| 94 |
+
context.prec = self._default_precision
|
| 95 |
+
elif dps is None:
|
| 96 |
+
context.prec = prec
|
| 97 |
+
elif prec is None:
|
| 98 |
+
context.dps = dps
|
| 99 |
+
else:
|
| 100 |
+
raise TypeError("Cannot set both prec and dps")
|
| 101 |
+
|
| 102 |
+
self._context = context
|
| 103 |
+
|
| 104 |
+
self._dtype = context.mpf
|
| 105 |
+
self.zero = self.dtype(0)
|
| 106 |
+
self.one = self.dtype(1)
|
| 107 |
+
|
| 108 |
+
# Only max_denom here is used for anything and is only used for
|
| 109 |
+
# to_rational.
|
| 110 |
+
self._max_denom = max(2**context.prec // 200, 99)
|
| 111 |
+
self._tolerance = self.one / self._max_denom
|
| 112 |
+
|
| 113 |
+
@property
|
| 114 |
+
def tp(self):
|
| 115 |
+
# XXX: Domain treats tp as an alias of dtype. Here we need to two
|
| 116 |
+
# separate things: dtype is a callable to make/convert instances.
|
| 117 |
+
# We use tp with isinstance to check if an object is an instance
|
| 118 |
+
# of the domain already.
|
| 119 |
+
return self._dtype
|
| 120 |
+
|
| 121 |
+
def dtype(self, arg):
|
| 122 |
+
# XXX: This is needed because mpmath does not recognise fmpz.
|
| 123 |
+
# It might be better to add conversion routines to mpmath and if that
|
| 124 |
+
# happens then this can be removed.
|
| 125 |
+
if isinstance(arg, SYMPY_INTS):
|
| 126 |
+
arg = int(arg)
|
| 127 |
+
return self._dtype(arg)
|
| 128 |
+
|
| 129 |
+
def __eq__(self, other):
|
| 130 |
+
return isinstance(other, RealField) and self.precision == other.precision
|
| 131 |
+
|
| 132 |
+
def __hash__(self):
|
| 133 |
+
return hash((self.__class__.__name__, self._dtype, self.precision))
|
| 134 |
+
|
| 135 |
+
def to_sympy(self, element):
|
| 136 |
+
"""Convert ``element`` to SymPy number. """
|
| 137 |
+
return Float(element, self.dps)
|
| 138 |
+
|
| 139 |
+
def from_sympy(self, expr):
|
| 140 |
+
"""Convert SymPy's number to ``dtype``. """
|
| 141 |
+
number = expr.evalf(n=self.dps)
|
| 142 |
+
|
| 143 |
+
if number.is_Number:
|
| 144 |
+
return self.dtype(number)
|
| 145 |
+
else:
|
| 146 |
+
raise CoercionFailed("expected real number, got %s" % expr)
|
| 147 |
+
|
| 148 |
+
def from_ZZ(self, element, base):
|
| 149 |
+
return self.dtype(element)
|
| 150 |
+
|
| 151 |
+
def from_ZZ_python(self, element, base):
|
| 152 |
+
return self.dtype(element)
|
| 153 |
+
|
| 154 |
+
def from_ZZ_gmpy(self, element, base):
|
| 155 |
+
return self.dtype(int(element))
|
| 156 |
+
|
| 157 |
+
# XXX: We need to convert the denominators to int here because mpmath does
|
| 158 |
+
# not recognise mpz. Ideally mpmath would handle this and if it changed to
|
| 159 |
+
# do so then the calls to int here could be removed.
|
| 160 |
+
|
| 161 |
+
def from_QQ(self, element, base):
|
| 162 |
+
return self.dtype(element.numerator) / int(element.denominator)
|
| 163 |
+
|
| 164 |
+
def from_QQ_python(self, element, base):
|
| 165 |
+
return self.dtype(element.numerator) / int(element.denominator)
|
| 166 |
+
|
| 167 |
+
def from_QQ_gmpy(self, element, base):
|
| 168 |
+
return self.dtype(int(element.numerator)) / int(element.denominator)
|
| 169 |
+
|
| 170 |
+
def from_AlgebraicField(self, element, base):
|
| 171 |
+
return self.from_sympy(base.to_sympy(element).evalf(self.dps))
|
| 172 |
+
|
| 173 |
+
def from_RealField(self, element, base):
|
| 174 |
+
return self.dtype(element)
|
| 175 |
+
|
| 176 |
+
def from_ComplexField(self, element, base):
|
| 177 |
+
if not element.imag:
|
| 178 |
+
return self.dtype(element.real)
|
| 179 |
+
|
| 180 |
+
def to_rational(self, element, limit=True):
|
| 181 |
+
"""Convert a real number to rational number. """
|
| 182 |
+
return to_rational(element, self._max_denom, limit=limit)
|
| 183 |
+
|
| 184 |
+
def get_ring(self):
|
| 185 |
+
"""Returns a ring associated with ``self``. """
|
| 186 |
+
return self
|
| 187 |
+
|
| 188 |
+
def get_exact(self):
|
| 189 |
+
"""Returns an exact domain associated with ``self``. """
|
| 190 |
+
from sympy.polys.domains import QQ
|
| 191 |
+
return QQ
|
| 192 |
+
|
| 193 |
+
def gcd(self, a, b):
|
| 194 |
+
"""Returns GCD of ``a`` and ``b``. """
|
| 195 |
+
return self.one
|
| 196 |
+
|
| 197 |
+
def lcm(self, a, b):
|
| 198 |
+
"""Returns LCM of ``a`` and ``b``. """
|
| 199 |
+
return a*b
|
| 200 |
+
|
| 201 |
+
def almosteq(self, a, b, tolerance=None):
|
| 202 |
+
"""Check if ``a`` and ``b`` are almost equal. """
|
| 203 |
+
return self._context.almosteq(a, b, tolerance)
|
| 204 |
+
|
| 205 |
+
def is_square(self, a):
|
| 206 |
+
"""Returns ``True`` if ``a >= 0`` and ``False`` otherwise. """
|
| 207 |
+
return a >= 0
|
| 208 |
+
|
| 209 |
+
def exsqrt(self, a):
|
| 210 |
+
"""Non-negative square root for ``a >= 0`` and ``None`` otherwise.
|
| 211 |
+
|
| 212 |
+
Explanation
|
| 213 |
+
===========
|
| 214 |
+
The square root may be slightly inaccurate due to floating point
|
| 215 |
+
rounding error.
|
| 216 |
+
"""
|
| 217 |
+
return a ** 0.5 if a >= 0 else None
|
| 218 |
+
|
| 219 |
+
|
| 220 |
+
RR = RealField()
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/ring.py
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`Ring` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.polys.domains.domain import Domain
|
| 5 |
+
from sympy.polys.polyerrors import ExactQuotientFailed, NotInvertible, NotReversible
|
| 6 |
+
|
| 7 |
+
from sympy.utilities import public
|
| 8 |
+
|
| 9 |
+
@public
|
| 10 |
+
class Ring(Domain):
|
| 11 |
+
"""Represents a ring domain. """
|
| 12 |
+
|
| 13 |
+
is_Ring = True
|
| 14 |
+
|
| 15 |
+
def get_ring(self):
|
| 16 |
+
"""Returns a ring associated with ``self``. """
|
| 17 |
+
return self
|
| 18 |
+
|
| 19 |
+
def exquo(self, a, b):
|
| 20 |
+
"""Exact quotient of ``a`` and ``b``, implies ``__floordiv__``. """
|
| 21 |
+
if a % b:
|
| 22 |
+
raise ExactQuotientFailed(a, b, self)
|
| 23 |
+
else:
|
| 24 |
+
return a // b
|
| 25 |
+
|
| 26 |
+
def quo(self, a, b):
|
| 27 |
+
"""Quotient of ``a`` and ``b``, implies ``__floordiv__``. """
|
| 28 |
+
return a // b
|
| 29 |
+
|
| 30 |
+
def rem(self, a, b):
|
| 31 |
+
"""Remainder of ``a`` and ``b``, implies ``__mod__``. """
|
| 32 |
+
return a % b
|
| 33 |
+
|
| 34 |
+
def div(self, a, b):
|
| 35 |
+
"""Division of ``a`` and ``b``, implies ``__divmod__``. """
|
| 36 |
+
return divmod(a, b)
|
| 37 |
+
|
| 38 |
+
def invert(self, a, b):
|
| 39 |
+
"""Returns inversion of ``a mod b``. """
|
| 40 |
+
s, t, h = self.gcdex(a, b)
|
| 41 |
+
|
| 42 |
+
if self.is_one(h):
|
| 43 |
+
return s % b
|
| 44 |
+
else:
|
| 45 |
+
raise NotInvertible("zero divisor")
|
| 46 |
+
|
| 47 |
+
def revert(self, a):
|
| 48 |
+
"""Returns ``a**(-1)`` if possible. """
|
| 49 |
+
if self.is_one(a) or self.is_one(-a):
|
| 50 |
+
return a
|
| 51 |
+
else:
|
| 52 |
+
raise NotReversible('only units are reversible in a ring')
|
| 53 |
+
|
| 54 |
+
def is_unit(self, a):
|
| 55 |
+
try:
|
| 56 |
+
self.revert(a)
|
| 57 |
+
return True
|
| 58 |
+
except NotReversible:
|
| 59 |
+
return False
|
| 60 |
+
|
| 61 |
+
def numer(self, a):
|
| 62 |
+
"""Returns numerator of ``a``. """
|
| 63 |
+
return a
|
| 64 |
+
|
| 65 |
+
def denom(self, a):
|
| 66 |
+
"""Returns denominator of `a`. """
|
| 67 |
+
return self.one
|
| 68 |
+
|
| 69 |
+
def free_module(self, rank):
|
| 70 |
+
"""
|
| 71 |
+
Generate a free module of rank ``rank`` over self.
|
| 72 |
+
|
| 73 |
+
>>> from sympy.abc import x
|
| 74 |
+
>>> from sympy import QQ
|
| 75 |
+
>>> QQ.old_poly_ring(x).free_module(2)
|
| 76 |
+
QQ[x]**2
|
| 77 |
+
"""
|
| 78 |
+
raise NotImplementedError
|
| 79 |
+
|
| 80 |
+
def ideal(self, *gens):
|
| 81 |
+
"""
|
| 82 |
+
Generate an ideal of ``self``.
|
| 83 |
+
|
| 84 |
+
>>> from sympy.abc import x
|
| 85 |
+
>>> from sympy import QQ
|
| 86 |
+
>>> QQ.old_poly_ring(x).ideal(x**2)
|
| 87 |
+
<x**2>
|
| 88 |
+
"""
|
| 89 |
+
from sympy.polys.agca.ideals import ModuleImplementedIdeal
|
| 90 |
+
return ModuleImplementedIdeal(self, self.free_module(1).submodule(
|
| 91 |
+
*[[x] for x in gens]))
|
| 92 |
+
|
| 93 |
+
def quotient_ring(self, e):
|
| 94 |
+
"""
|
| 95 |
+
Form a quotient ring of ``self``.
|
| 96 |
+
|
| 97 |
+
Here ``e`` can be an ideal or an iterable.
|
| 98 |
+
|
| 99 |
+
>>> from sympy.abc import x
|
| 100 |
+
>>> from sympy import QQ
|
| 101 |
+
>>> QQ.old_poly_ring(x).quotient_ring(QQ.old_poly_ring(x).ideal(x**2))
|
| 102 |
+
QQ[x]/<x**2>
|
| 103 |
+
>>> QQ.old_poly_ring(x).quotient_ring([x**2])
|
| 104 |
+
QQ[x]/<x**2>
|
| 105 |
+
|
| 106 |
+
The division operator has been overloaded for this:
|
| 107 |
+
|
| 108 |
+
>>> QQ.old_poly_ring(x)/[x**2]
|
| 109 |
+
QQ[x]/<x**2>
|
| 110 |
+
"""
|
| 111 |
+
from sympy.polys.agca.ideals import Ideal
|
| 112 |
+
from sympy.polys.domains.quotientring import QuotientRing
|
| 113 |
+
if not isinstance(e, Ideal):
|
| 114 |
+
e = self.ideal(*e)
|
| 115 |
+
return QuotientRing(self, e)
|
| 116 |
+
|
| 117 |
+
def __truediv__(self, e):
|
| 118 |
+
return self.quotient_ring(e)
|
.venv/lib/python3.13/site-packages/sympy/polys/domains/simpledomain.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Implementation of :class:`SimpleDomain` class. """
|
| 2 |
+
|
| 3 |
+
|
| 4 |
+
from sympy.polys.domains.domain import Domain
|
| 5 |
+
from sympy.utilities import public
|
| 6 |
+
|
| 7 |
+
@public
|
| 8 |
+
class SimpleDomain(Domain):
|
| 9 |
+
"""Base class for simple domains, e.g. ZZ, QQ. """
|
| 10 |
+
|
| 11 |
+
is_Simple = True
|
| 12 |
+
|
| 13 |
+
def inject(self, *gens):
|
| 14 |
+
"""Inject generators into this domain. """
|
| 15 |
+
return self.poly_ring(*gens)
|
.venv/lib/python3.13/site-packages/sympy/polys/tests/__init__.py
ADDED
|
File without changes
|
.venv/lib/python3.13/site-packages/sympy/polys/tests/test_appellseqs.py
ADDED
|
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Tests for efficient functions for generating Appell sequences."""
|
| 2 |
+
from sympy.core.numbers import Rational as Q
|
| 3 |
+
from sympy.polys.polytools import Poly
|
| 4 |
+
from sympy.testing.pytest import raises
|
| 5 |
+
from sympy.polys.appellseqs import (bernoulli_poly, bernoulli_c_poly,
|
| 6 |
+
euler_poly, genocchi_poly, andre_poly)
|
| 7 |
+
from sympy.abc import x
|
| 8 |
+
|
| 9 |
+
def test_bernoulli_poly():
|
| 10 |
+
raises(ValueError, lambda: bernoulli_poly(-1, x))
|
| 11 |
+
assert bernoulli_poly(1, x, polys=True) == Poly(x - Q(1,2))
|
| 12 |
+
|
| 13 |
+
assert bernoulli_poly(0, x) == 1
|
| 14 |
+
assert bernoulli_poly(1, x) == x - Q(1,2)
|
| 15 |
+
assert bernoulli_poly(2, x) == x**2 - x + Q(1,6)
|
| 16 |
+
assert bernoulli_poly(3, x) == x**3 - Q(3,2)*x**2 + Q(1,2)*x
|
| 17 |
+
assert bernoulli_poly(4, x) == x**4 - 2*x**3 + x**2 - Q(1,30)
|
| 18 |
+
assert bernoulli_poly(5, x) == x**5 - Q(5,2)*x**4 + Q(5,3)*x**3 - Q(1,6)*x
|
| 19 |
+
assert bernoulli_poly(6, x) == x**6 - 3*x**5 + Q(5,2)*x**4 - Q(1,2)*x**2 + Q(1,42)
|
| 20 |
+
|
| 21 |
+
assert bernoulli_poly(1).dummy_eq(x - Q(1,2))
|
| 22 |
+
assert bernoulli_poly(1, polys=True) == Poly(x - Q(1,2))
|
| 23 |
+
|
| 24 |
+
def test_bernoulli_c_poly():
|
| 25 |
+
raises(ValueError, lambda: bernoulli_c_poly(-1, x))
|
| 26 |
+
assert bernoulli_c_poly(1, x, polys=True) == Poly(x, domain='QQ')
|
| 27 |
+
|
| 28 |
+
assert bernoulli_c_poly(0, x) == 1
|
| 29 |
+
assert bernoulli_c_poly(1, x) == x
|
| 30 |
+
assert bernoulli_c_poly(2, x) == x**2 - Q(1,3)
|
| 31 |
+
assert bernoulli_c_poly(3, x) == x**3 - x
|
| 32 |
+
assert bernoulli_c_poly(4, x) == x**4 - 2*x**2 + Q(7,15)
|
| 33 |
+
assert bernoulli_c_poly(5, x) == x**5 - Q(10,3)*x**3 + Q(7,3)*x
|
| 34 |
+
assert bernoulli_c_poly(6, x) == x**6 - 5*x**4 + 7*x**2 - Q(31,21)
|
| 35 |
+
|
| 36 |
+
assert bernoulli_c_poly(1).dummy_eq(x)
|
| 37 |
+
assert bernoulli_c_poly(1, polys=True) == Poly(x, domain='QQ')
|
| 38 |
+
|
| 39 |
+
assert 2**8 * bernoulli_poly(8, (x+1)/2).expand() == bernoulli_c_poly(8, x)
|
| 40 |
+
assert 2**9 * bernoulli_poly(9, (x+1)/2).expand() == bernoulli_c_poly(9, x)
|
| 41 |
+
|
| 42 |
+
def test_genocchi_poly():
|
| 43 |
+
raises(ValueError, lambda: genocchi_poly(-1, x))
|
| 44 |
+
assert genocchi_poly(2, x, polys=True) == Poly(-2*x + 1)
|
| 45 |
+
|
| 46 |
+
assert genocchi_poly(0, x) == 0
|
| 47 |
+
assert genocchi_poly(1, x) == -1
|
| 48 |
+
assert genocchi_poly(2, x) == 1 - 2*x
|
| 49 |
+
assert genocchi_poly(3, x) == 3*x - 3*x**2
|
| 50 |
+
assert genocchi_poly(4, x) == -1 + 6*x**2 - 4*x**3
|
| 51 |
+
assert genocchi_poly(5, x) == -5*x + 10*x**3 - 5*x**4
|
| 52 |
+
assert genocchi_poly(6, x) == 3 - 15*x**2 + 15*x**4 - 6*x**5
|
| 53 |
+
|
| 54 |
+
assert genocchi_poly(2).dummy_eq(-2*x + 1)
|
| 55 |
+
assert genocchi_poly(2, polys=True) == Poly(-2*x + 1)
|
| 56 |
+
|
| 57 |
+
assert 2 * (bernoulli_poly(8, x) - bernoulli_c_poly(8, x)) == genocchi_poly(8, x)
|
| 58 |
+
assert 2 * (bernoulli_poly(9, x) - bernoulli_c_poly(9, x)) == genocchi_poly(9, x)
|
| 59 |
+
|
| 60 |
+
def test_euler_poly():
|
| 61 |
+
raises(ValueError, lambda: euler_poly(-1, x))
|
| 62 |
+
assert euler_poly(1, x, polys=True) == Poly(x - Q(1,2))
|
| 63 |
+
|
| 64 |
+
assert euler_poly(0, x) == 1
|
| 65 |
+
assert euler_poly(1, x) == x - Q(1,2)
|
| 66 |
+
assert euler_poly(2, x) == x**2 - x
|
| 67 |
+
assert euler_poly(3, x) == x**3 - Q(3,2)*x**2 + Q(1,4)
|
| 68 |
+
assert euler_poly(4, x) == x**4 - 2*x**3 + x
|
| 69 |
+
assert euler_poly(5, x) == x**5 - Q(5,2)*x**4 + Q(5,2)*x**2 - Q(1,2)
|
| 70 |
+
assert euler_poly(6, x) == x**6 - 3*x**5 + 5*x**3 - 3*x
|
| 71 |
+
|
| 72 |
+
assert euler_poly(1).dummy_eq(x - Q(1,2))
|
| 73 |
+
assert euler_poly(1, polys=True) == Poly(x - Q(1,2))
|
| 74 |
+
|
| 75 |
+
assert genocchi_poly(9, x) == euler_poly(8, x) * -9
|
| 76 |
+
assert genocchi_poly(10, x) == euler_poly(9, x) * -10
|
| 77 |
+
|
| 78 |
+
def test_andre_poly():
|
| 79 |
+
raises(ValueError, lambda: andre_poly(-1, x))
|
| 80 |
+
assert andre_poly(1, x, polys=True) == Poly(x)
|
| 81 |
+
|
| 82 |
+
assert andre_poly(0, x) == 1
|
| 83 |
+
assert andre_poly(1, x) == x
|
| 84 |
+
assert andre_poly(2, x) == x**2 - 1
|
| 85 |
+
assert andre_poly(3, x) == x**3 - 3*x
|
| 86 |
+
assert andre_poly(4, x) == x**4 - 6*x**2 + 5
|
| 87 |
+
assert andre_poly(5, x) == x**5 - 10*x**3 + 25*x
|
| 88 |
+
assert andre_poly(6, x) == x**6 - 15*x**4 + 75*x**2 - 61
|
| 89 |
+
|
| 90 |
+
assert andre_poly(1).dummy_eq(x)
|
| 91 |
+
assert andre_poly(1, polys=True) == Poly(x)
|
.venv/lib/python3.13/site-packages/sympy/polys/tests/test_constructor.py
ADDED
|
@@ -0,0 +1,236 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Tests for tools for constructing domains for expressions. """
|
| 2 |
+
|
| 3 |
+
from sympy.testing.pytest import tooslow
|
| 4 |
+
|
| 5 |
+
from sympy.polys.constructor import construct_domain
|
| 6 |
+
from sympy.polys.domains import ZZ, QQ, ZZ_I, QQ_I, RR, CC, EX
|
| 7 |
+
from sympy.polys.domains.realfield import RealField
|
| 8 |
+
from sympy.polys.domains.complexfield import ComplexField
|
| 9 |
+
|
| 10 |
+
from sympy.core import (Catalan, GoldenRatio)
|
| 11 |
+
from sympy.core.numbers import (E, Float, I, Rational, pi)
|
| 12 |
+
from sympy.core.singleton import S
|
| 13 |
+
from sympy.functions.elementary.exponential import exp
|
| 14 |
+
from sympy.functions.elementary.miscellaneous import sqrt
|
| 15 |
+
from sympy.functions.elementary.trigonometric import sin
|
| 16 |
+
from sympy import rootof
|
| 17 |
+
|
| 18 |
+
from sympy.abc import x, y
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def test_construct_domain():
|
| 22 |
+
|
| 23 |
+
assert construct_domain([1, 2, 3]) == (ZZ, [ZZ(1), ZZ(2), ZZ(3)])
|
| 24 |
+
assert construct_domain([1, 2, 3], field=True) == (QQ, [QQ(1), QQ(2), QQ(3)])
|
| 25 |
+
|
| 26 |
+
assert construct_domain([S.One, S(2), S(3)]) == (ZZ, [ZZ(1), ZZ(2), ZZ(3)])
|
| 27 |
+
assert construct_domain([S.One, S(2), S(3)], field=True) == (QQ, [QQ(1), QQ(2), QQ(3)])
|
| 28 |
+
|
| 29 |
+
assert construct_domain([S.Half, S(2)]) == (QQ, [QQ(1, 2), QQ(2)])
|
| 30 |
+
result = construct_domain([3.14, 1, S.Half])
|
| 31 |
+
assert isinstance(result[0], RealField)
|
| 32 |
+
assert result[1] == [RR(3.14), RR(1.0), RR(0.5)]
|
| 33 |
+
|
| 34 |
+
result = construct_domain([3.14, I, S.Half])
|
| 35 |
+
assert isinstance(result[0], ComplexField)
|
| 36 |
+
assert result[1] == [CC(3.14), CC(1.0j), CC(0.5)]
|
| 37 |
+
|
| 38 |
+
assert construct_domain([1.0+I]) == (CC, [CC(1.0, 1.0)])
|
| 39 |
+
assert construct_domain([2.0+3.0*I]) == (CC, [CC(2.0, 3.0)])
|
| 40 |
+
|
| 41 |
+
assert construct_domain([1, I]) == (ZZ_I, [ZZ_I(1, 0), ZZ_I(0, 1)])
|
| 42 |
+
assert construct_domain([1, I/2]) == (QQ_I, [QQ_I(1, 0), QQ_I(0, S.Half)])
|
| 43 |
+
|
| 44 |
+
assert construct_domain([3.14, sqrt(2)], extension=None) == (EX, [EX(3.14), EX(sqrt(2))])
|
| 45 |
+
assert construct_domain([3.14, sqrt(2)], extension=True) == (EX, [EX(3.14), EX(sqrt(2))])
|
| 46 |
+
|
| 47 |
+
assert construct_domain([1, sqrt(2)], extension=None) == (EX, [EX(1), EX(sqrt(2))])
|
| 48 |
+
|
| 49 |
+
assert construct_domain([x, sqrt(x)]) == (EX, [EX(x), EX(sqrt(x))])
|
| 50 |
+
assert construct_domain([x, sqrt(x), sqrt(y)]) == (EX, [EX(x), EX(sqrt(x)), EX(sqrt(y))])
|
| 51 |
+
|
| 52 |
+
alg = QQ.algebraic_field(sqrt(2))
|
| 53 |
+
|
| 54 |
+
assert construct_domain([7, S.Half, sqrt(2)], extension=True) == \
|
| 55 |
+
(alg, [alg.convert(7), alg.convert(S.Half), alg.convert(sqrt(2))])
|
| 56 |
+
|
| 57 |
+
alg = QQ.algebraic_field(sqrt(2) + sqrt(3))
|
| 58 |
+
|
| 59 |
+
assert construct_domain([7, sqrt(2), sqrt(3)], extension=True) == \
|
| 60 |
+
(alg, [alg.convert(7), alg.convert(sqrt(2)), alg.convert(sqrt(3))])
|
| 61 |
+
|
| 62 |
+
dom = ZZ[x]
|
| 63 |
+
|
| 64 |
+
assert construct_domain([2*x, 3]) == \
|
| 65 |
+
(dom, [dom.convert(2*x), dom.convert(3)])
|
| 66 |
+
|
| 67 |
+
dom = ZZ[x, y]
|
| 68 |
+
|
| 69 |
+
assert construct_domain([2*x, 3*y]) == \
|
| 70 |
+
(dom, [dom.convert(2*x), dom.convert(3*y)])
|
| 71 |
+
|
| 72 |
+
dom = QQ[x]
|
| 73 |
+
|
| 74 |
+
assert construct_domain([x/2, 3]) == \
|
| 75 |
+
(dom, [dom.convert(x/2), dom.convert(3)])
|
| 76 |
+
|
| 77 |
+
dom = QQ[x, y]
|
| 78 |
+
|
| 79 |
+
assert construct_domain([x/2, 3*y]) == \
|
| 80 |
+
(dom, [dom.convert(x/2), dom.convert(3*y)])
|
| 81 |
+
|
| 82 |
+
dom = ZZ_I[x]
|
| 83 |
+
|
| 84 |
+
assert construct_domain([2*x, I]) == \
|
| 85 |
+
(dom, [dom.convert(2*x), dom.convert(I)])
|
| 86 |
+
|
| 87 |
+
dom = ZZ_I[x, y]
|
| 88 |
+
|
| 89 |
+
assert construct_domain([2*x, I*y]) == \
|
| 90 |
+
(dom, [dom.convert(2*x), dom.convert(I*y)])
|
| 91 |
+
|
| 92 |
+
dom = QQ_I[x]
|
| 93 |
+
|
| 94 |
+
assert construct_domain([x/2, I]) == \
|
| 95 |
+
(dom, [dom.convert(x/2), dom.convert(I)])
|
| 96 |
+
|
| 97 |
+
dom = QQ_I[x, y]
|
| 98 |
+
|
| 99 |
+
assert construct_domain([x/2, I*y]) == \
|
| 100 |
+
(dom, [dom.convert(x/2), dom.convert(I*y)])
|
| 101 |
+
|
| 102 |
+
dom = RR[x]
|
| 103 |
+
|
| 104 |
+
assert construct_domain([x/2, 3.5]) == \
|
| 105 |
+
(dom, [dom.convert(x/2), dom.convert(3.5)])
|
| 106 |
+
|
| 107 |
+
dom = RR[x, y]
|
| 108 |
+
|
| 109 |
+
assert construct_domain([x/2, 3.5*y]) == \
|
| 110 |
+
(dom, [dom.convert(x/2), dom.convert(3.5*y)])
|
| 111 |
+
|
| 112 |
+
dom = CC[x]
|
| 113 |
+
|
| 114 |
+
assert construct_domain([I*x/2, 3.5]) == \
|
| 115 |
+
(dom, [dom.convert(I*x/2), dom.convert(3.5)])
|
| 116 |
+
|
| 117 |
+
dom = CC[x, y]
|
| 118 |
+
|
| 119 |
+
assert construct_domain([I*x/2, 3.5*y]) == \
|
| 120 |
+
(dom, [dom.convert(I*x/2), dom.convert(3.5*y)])
|
| 121 |
+
|
| 122 |
+
dom = CC[x]
|
| 123 |
+
|
| 124 |
+
assert construct_domain([x/2, I*3.5]) == \
|
| 125 |
+
(dom, [dom.convert(x/2), dom.convert(I*3.5)])
|
| 126 |
+
|
| 127 |
+
dom = CC[x, y]
|
| 128 |
+
|
| 129 |
+
assert construct_domain([x/2, I*3.5*y]) == \
|
| 130 |
+
(dom, [dom.convert(x/2), dom.convert(I*3.5*y)])
|
| 131 |
+
|
| 132 |
+
dom = ZZ.frac_field(x)
|
| 133 |
+
|
| 134 |
+
assert construct_domain([2/x, 3]) == \
|
| 135 |
+
(dom, [dom.convert(2/x), dom.convert(3)])
|
| 136 |
+
|
| 137 |
+
dom = ZZ.frac_field(x, y)
|
| 138 |
+
|
| 139 |
+
assert construct_domain([2/x, 3*y]) == \
|
| 140 |
+
(dom, [dom.convert(2/x), dom.convert(3*y)])
|
| 141 |
+
|
| 142 |
+
dom = RR.frac_field(x)
|
| 143 |
+
|
| 144 |
+
assert construct_domain([2/x, 3.5]) == \
|
| 145 |
+
(dom, [dom.convert(2/x), dom.convert(3.5)])
|
| 146 |
+
|
| 147 |
+
dom = RR.frac_field(x, y)
|
| 148 |
+
|
| 149 |
+
assert construct_domain([2/x, 3.5*y]) == \
|
| 150 |
+
(dom, [dom.convert(2/x), dom.convert(3.5*y)])
|
| 151 |
+
|
| 152 |
+
dom = RealField(prec=336)[x]
|
| 153 |
+
|
| 154 |
+
assert construct_domain([pi.evalf(100)*x]) == \
|
| 155 |
+
(dom, [dom.convert(pi.evalf(100)*x)])
|
| 156 |
+
|
| 157 |
+
assert construct_domain(2) == (ZZ, ZZ(2))
|
| 158 |
+
assert construct_domain(S(2)/3) == (QQ, QQ(2, 3))
|
| 159 |
+
assert construct_domain(Rational(2, 3)) == (QQ, QQ(2, 3))
|
| 160 |
+
|
| 161 |
+
assert construct_domain({}) == (ZZ, {})
|
| 162 |
+
|
| 163 |
+
|
| 164 |
+
def test_complex_exponential():
|
| 165 |
+
w = exp(-I*2*pi/3, evaluate=False)
|
| 166 |
+
alg = QQ.algebraic_field(w)
|
| 167 |
+
assert construct_domain([w**2, w, 1], extension=True) == (
|
| 168 |
+
alg,
|
| 169 |
+
[alg.convert(w**2),
|
| 170 |
+
alg.convert(w),
|
| 171 |
+
alg.convert(1)]
|
| 172 |
+
)
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
def test_rootof():
|
| 176 |
+
r1 = rootof(x**3 + x + 1, 0)
|
| 177 |
+
r2 = rootof(x**3 + x + 1, 1)
|
| 178 |
+
K1 = QQ.algebraic_field(r1)
|
| 179 |
+
K2 = QQ.algebraic_field(r2)
|
| 180 |
+
assert construct_domain([r1]) == (EX, [EX(r1)])
|
| 181 |
+
assert construct_domain([r2]) == (EX, [EX(r2)])
|
| 182 |
+
assert construct_domain([r1, r2]) == (EX, [EX(r1), EX(r2)])
|
| 183 |
+
|
| 184 |
+
assert construct_domain([r1], extension=True) == (
|
| 185 |
+
K1, [K1.from_sympy(r1)])
|
| 186 |
+
assert construct_domain([r2], extension=True) == (
|
| 187 |
+
K2, [K2.from_sympy(r2)])
|
| 188 |
+
|
| 189 |
+
|
| 190 |
+
@tooslow
|
| 191 |
+
def test_rootof_primitive_element():
|
| 192 |
+
r1 = rootof(x**3 + x + 1, 0)
|
| 193 |
+
r2 = rootof(x**3 + x + 1, 1)
|
| 194 |
+
K12 = QQ.algebraic_field(r1 + r2)
|
| 195 |
+
assert construct_domain([r1, r2], extension=True) == (
|
| 196 |
+
K12, [K12.from_sympy(r1), K12.from_sympy(r2)])
|
| 197 |
+
|
| 198 |
+
|
| 199 |
+
def test_composite_option():
|
| 200 |
+
assert construct_domain({(1,): sin(y)}, composite=False) == \
|
| 201 |
+
(EX, {(1,): EX(sin(y))})
|
| 202 |
+
|
| 203 |
+
assert construct_domain({(1,): y}, composite=False) == \
|
| 204 |
+
(EX, {(1,): EX(y)})
|
| 205 |
+
|
| 206 |
+
assert construct_domain({(1, 1): 1}, composite=False) == \
|
| 207 |
+
(ZZ, {(1, 1): 1})
|
| 208 |
+
|
| 209 |
+
assert construct_domain({(1, 0): y}, composite=False) == \
|
| 210 |
+
(EX, {(1, 0): EX(y)})
|
| 211 |
+
|
| 212 |
+
|
| 213 |
+
def test_precision():
|
| 214 |
+
f1 = Float("1.01")
|
| 215 |
+
f2 = Float("1.0000000000000000000001")
|
| 216 |
+
for u in [1, 1e-2, 1e-6, 1e-13, 1e-14, 1e-16, 1e-20, 1e-100, 1e-300,
|
| 217 |
+
f1, f2]:
|
| 218 |
+
result = construct_domain([u])
|
| 219 |
+
v = float(result[1][0])
|
| 220 |
+
assert abs(u - v) / u < 1e-14 # Test relative accuracy
|
| 221 |
+
|
| 222 |
+
result = construct_domain([f1])
|
| 223 |
+
y = result[1][0]
|
| 224 |
+
assert y-1 > 1e-50
|
| 225 |
+
|
| 226 |
+
result = construct_domain([f2])
|
| 227 |
+
y = result[1][0]
|
| 228 |
+
assert y-1 > 1e-50
|
| 229 |
+
|
| 230 |
+
|
| 231 |
+
def test_issue_11538():
|
| 232 |
+
for n in [E, pi, Catalan]:
|
| 233 |
+
assert construct_domain(n)[0] == ZZ[n]
|
| 234 |
+
assert construct_domain(x + n)[0] == ZZ[x, n]
|
| 235 |
+
assert construct_domain(GoldenRatio)[0] == EX
|
| 236 |
+
assert construct_domain(x + GoldenRatio)[0] == EX
|
.venv/lib/python3.13/site-packages/sympy/polys/tests/test_densearith.py
ADDED
|
@@ -0,0 +1,1007 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Tests for dense recursive polynomials' arithmetics. """
|
| 2 |
+
|
| 3 |
+
from sympy.external.gmpy import GROUND_TYPES
|
| 4 |
+
|
| 5 |
+
from sympy.polys.densebasic import (
|
| 6 |
+
dup_normal, dmp_normal,
|
| 7 |
+
)
|
| 8 |
+
|
| 9 |
+
from sympy.polys.densearith import (
|
| 10 |
+
dup_add_term, dmp_add_term,
|
| 11 |
+
dup_sub_term, dmp_sub_term,
|
| 12 |
+
dup_mul_term, dmp_mul_term,
|
| 13 |
+
dup_add_ground, dmp_add_ground,
|
| 14 |
+
dup_sub_ground, dmp_sub_ground,
|
| 15 |
+
dup_mul_ground, dmp_mul_ground,
|
| 16 |
+
dup_quo_ground, dmp_quo_ground,
|
| 17 |
+
dup_exquo_ground, dmp_exquo_ground,
|
| 18 |
+
dup_lshift, dup_rshift,
|
| 19 |
+
dup_abs, dmp_abs,
|
| 20 |
+
dup_neg, dmp_neg,
|
| 21 |
+
dup_add, dmp_add,
|
| 22 |
+
dup_sub, dmp_sub,
|
| 23 |
+
dup_mul, dmp_mul,
|
| 24 |
+
dup_sqr, dmp_sqr,
|
| 25 |
+
dup_pow, dmp_pow,
|
| 26 |
+
dup_add_mul, dmp_add_mul,
|
| 27 |
+
dup_sub_mul, dmp_sub_mul,
|
| 28 |
+
dup_pdiv, dup_prem, dup_pquo, dup_pexquo,
|
| 29 |
+
dmp_pdiv, dmp_prem, dmp_pquo, dmp_pexquo,
|
| 30 |
+
dup_rr_div, dmp_rr_div,
|
| 31 |
+
dup_ff_div, dmp_ff_div,
|
| 32 |
+
dup_div, dup_rem, dup_quo, dup_exquo,
|
| 33 |
+
dmp_div, dmp_rem, dmp_quo, dmp_exquo,
|
| 34 |
+
dup_max_norm, dmp_max_norm,
|
| 35 |
+
dup_l1_norm, dmp_l1_norm,
|
| 36 |
+
dup_l2_norm_squared, dmp_l2_norm_squared,
|
| 37 |
+
dup_expand, dmp_expand,
|
| 38 |
+
)
|
| 39 |
+
|
| 40 |
+
from sympy.polys.polyerrors import (
|
| 41 |
+
ExactQuotientFailed,
|
| 42 |
+
)
|
| 43 |
+
|
| 44 |
+
from sympy.polys.specialpolys import f_polys, Symbol, Poly
|
| 45 |
+
from sympy.polys.domains import FF, ZZ, QQ, CC
|
| 46 |
+
|
| 47 |
+
from sympy.testing.pytest import raises
|
| 48 |
+
|
| 49 |
+
x = Symbol('x')
|
| 50 |
+
|
| 51 |
+
f_0, f_1, f_2, f_3, f_4, f_5, f_6 = [ f.to_dense() for f in f_polys() ]
|
| 52 |
+
F_0 = dmp_mul_ground(dmp_normal(f_0, 2, QQ), QQ(1, 7), 2, QQ)
|
| 53 |
+
|
| 54 |
+
def test_dup_add_term():
|
| 55 |
+
f = dup_normal([], ZZ)
|
| 56 |
+
|
| 57 |
+
assert dup_add_term(f, ZZ(0), 0, ZZ) == dup_normal([], ZZ)
|
| 58 |
+
|
| 59 |
+
assert dup_add_term(f, ZZ(1), 0, ZZ) == dup_normal([1], ZZ)
|
| 60 |
+
assert dup_add_term(f, ZZ(1), 1, ZZ) == dup_normal([1, 0], ZZ)
|
| 61 |
+
assert dup_add_term(f, ZZ(1), 2, ZZ) == dup_normal([1, 0, 0], ZZ)
|
| 62 |
+
|
| 63 |
+
f = dup_normal([1, 1, 1], ZZ)
|
| 64 |
+
|
| 65 |
+
assert dup_add_term(f, ZZ(1), 0, ZZ) == dup_normal([1, 1, 2], ZZ)
|
| 66 |
+
assert dup_add_term(f, ZZ(1), 1, ZZ) == dup_normal([1, 2, 1], ZZ)
|
| 67 |
+
assert dup_add_term(f, ZZ(1), 2, ZZ) == dup_normal([2, 1, 1], ZZ)
|
| 68 |
+
|
| 69 |
+
assert dup_add_term(f, ZZ(1), 3, ZZ) == dup_normal([1, 1, 1, 1], ZZ)
|
| 70 |
+
assert dup_add_term(f, ZZ(1), 4, ZZ) == dup_normal([1, 0, 1, 1, 1], ZZ)
|
| 71 |
+
assert dup_add_term(f, ZZ(1), 5, ZZ) == dup_normal([1, 0, 0, 1, 1, 1], ZZ)
|
| 72 |
+
assert dup_add_term(
|
| 73 |
+
f, ZZ(1), 6, ZZ) == dup_normal([1, 0, 0, 0, 1, 1, 1], ZZ)
|
| 74 |
+
|
| 75 |
+
assert dup_add_term(f, ZZ(-1), 2, ZZ) == dup_normal([1, 1], ZZ)
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
def test_dmp_add_term():
|
| 79 |
+
assert dmp_add_term([ZZ(1), ZZ(1), ZZ(1)], ZZ(1), 2, 0, ZZ) == \
|
| 80 |
+
dup_add_term([ZZ(1), ZZ(1), ZZ(1)], ZZ(1), 2, ZZ)
|
| 81 |
+
assert dmp_add_term(f_0, [[]], 3, 2, ZZ) == f_0
|
| 82 |
+
assert dmp_add_term(F_0, [[]], 3, 2, QQ) == F_0
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def test_dup_sub_term():
|
| 86 |
+
f = dup_normal([], ZZ)
|
| 87 |
+
|
| 88 |
+
assert dup_sub_term(f, ZZ(0), 0, ZZ) == dup_normal([], ZZ)
|
| 89 |
+
|
| 90 |
+
assert dup_sub_term(f, ZZ(1), 0, ZZ) == dup_normal([-1], ZZ)
|
| 91 |
+
assert dup_sub_term(f, ZZ(1), 1, ZZ) == dup_normal([-1, 0], ZZ)
|
| 92 |
+
assert dup_sub_term(f, ZZ(1), 2, ZZ) == dup_normal([-1, 0, 0], ZZ)
|
| 93 |
+
|
| 94 |
+
f = dup_normal([1, 1, 1], ZZ)
|
| 95 |
+
|
| 96 |
+
assert dup_sub_term(f, ZZ(2), 0, ZZ) == dup_normal([ 1, 1, -1], ZZ)
|
| 97 |
+
assert dup_sub_term(f, ZZ(2), 1, ZZ) == dup_normal([ 1, -1, 1], ZZ)
|
| 98 |
+
assert dup_sub_term(f, ZZ(2), 2, ZZ) == dup_normal([-1, 1, 1], ZZ)
|
| 99 |
+
|
| 100 |
+
assert dup_sub_term(f, ZZ(1), 3, ZZ) == dup_normal([-1, 1, 1, 1], ZZ)
|
| 101 |
+
assert dup_sub_term(f, ZZ(1), 4, ZZ) == dup_normal([-1, 0, 1, 1, 1], ZZ)
|
| 102 |
+
assert dup_sub_term(f, ZZ(1), 5, ZZ) == dup_normal([-1, 0, 0, 1, 1, 1], ZZ)
|
| 103 |
+
assert dup_sub_term(
|
| 104 |
+
f, ZZ(1), 6, ZZ) == dup_normal([-1, 0, 0, 0, 1, 1, 1], ZZ)
|
| 105 |
+
|
| 106 |
+
assert dup_sub_term(f, ZZ(1), 2, ZZ) == dup_normal([1, 1], ZZ)
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
def test_dmp_sub_term():
|
| 110 |
+
assert dmp_sub_term([ZZ(1), ZZ(1), ZZ(1)], ZZ(1), 2, 0, ZZ) == \
|
| 111 |
+
dup_sub_term([ZZ(1), ZZ(1), ZZ(1)], ZZ(1), 2, ZZ)
|
| 112 |
+
assert dmp_sub_term(f_0, [[]], 3, 2, ZZ) == f_0
|
| 113 |
+
assert dmp_sub_term(F_0, [[]], 3, 2, QQ) == F_0
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
def test_dup_mul_term():
|
| 117 |
+
f = dup_normal([], ZZ)
|
| 118 |
+
|
| 119 |
+
assert dup_mul_term(f, ZZ(2), 3, ZZ) == dup_normal([], ZZ)
|
| 120 |
+
|
| 121 |
+
f = dup_normal([1, 1], ZZ)
|
| 122 |
+
|
| 123 |
+
assert dup_mul_term(f, ZZ(0), 3, ZZ) == dup_normal([], ZZ)
|
| 124 |
+
|
| 125 |
+
f = dup_normal([1, 2, 3], ZZ)
|
| 126 |
+
|
| 127 |
+
assert dup_mul_term(f, ZZ(2), 0, ZZ) == dup_normal([2, 4, 6], ZZ)
|
| 128 |
+
assert dup_mul_term(f, ZZ(2), 1, ZZ) == dup_normal([2, 4, 6, 0], ZZ)
|
| 129 |
+
assert dup_mul_term(f, ZZ(2), 2, ZZ) == dup_normal([2, 4, 6, 0, 0], ZZ)
|
| 130 |
+
assert dup_mul_term(f, ZZ(2), 3, ZZ) == dup_normal([2, 4, 6, 0, 0, 0], ZZ)
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
def test_dmp_mul_term():
|
| 134 |
+
assert dmp_mul_term([ZZ(1), ZZ(2), ZZ(3)], ZZ(2), 1, 0, ZZ) == \
|
| 135 |
+
dup_mul_term([ZZ(1), ZZ(2), ZZ(3)], ZZ(2), 1, ZZ)
|
| 136 |
+
|
| 137 |
+
assert dmp_mul_term([[]], [ZZ(2)], 3, 1, ZZ) == [[]]
|
| 138 |
+
assert dmp_mul_term([[ZZ(1)]], [], 3, 1, ZZ) == [[]]
|
| 139 |
+
|
| 140 |
+
assert dmp_mul_term([[ZZ(1), ZZ(2)], [ZZ(3)]], [ZZ(2)], 2, 1, ZZ) == \
|
| 141 |
+
[[ZZ(2), ZZ(4)], [ZZ(6)], [], []]
|
| 142 |
+
|
| 143 |
+
assert dmp_mul_term([[]], [QQ(2, 3)], 3, 1, QQ) == [[]]
|
| 144 |
+
assert dmp_mul_term([[QQ(1, 2)]], [], 3, 1, QQ) == [[]]
|
| 145 |
+
|
| 146 |
+
assert dmp_mul_term([[QQ(1, 5), QQ(2, 5)], [QQ(3, 5)]], [QQ(2, 3)], 2, 1, QQ) == \
|
| 147 |
+
[[QQ(2, 15), QQ(4, 15)], [QQ(6, 15)], [], []]
|
| 148 |
+
|
| 149 |
+
|
| 150 |
+
def test_dup_add_ground():
|
| 151 |
+
f = ZZ.map([1, 2, 3, 4])
|
| 152 |
+
g = ZZ.map([1, 2, 3, 8])
|
| 153 |
+
|
| 154 |
+
assert dup_add_ground(f, ZZ(4), ZZ) == g
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
def test_dmp_add_ground():
|
| 158 |
+
f = ZZ.map([[1], [2], [3], [4]])
|
| 159 |
+
g = ZZ.map([[1], [2], [3], [8]])
|
| 160 |
+
|
| 161 |
+
assert dmp_add_ground(f, ZZ(4), 1, ZZ) == g
|
| 162 |
+
|
| 163 |
+
|
| 164 |
+
def test_dup_sub_ground():
|
| 165 |
+
f = ZZ.map([1, 2, 3, 4])
|
| 166 |
+
g = ZZ.map([1, 2, 3, 0])
|
| 167 |
+
|
| 168 |
+
assert dup_sub_ground(f, ZZ(4), ZZ) == g
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
def test_dmp_sub_ground():
|
| 172 |
+
f = ZZ.map([[1], [2], [3], [4]])
|
| 173 |
+
g = ZZ.map([[1], [2], [3], []])
|
| 174 |
+
|
| 175 |
+
assert dmp_sub_ground(f, ZZ(4), 1, ZZ) == g
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
def test_dup_mul_ground():
|
| 179 |
+
f = dup_normal([], ZZ)
|
| 180 |
+
|
| 181 |
+
assert dup_mul_ground(f, ZZ(2), ZZ) == dup_normal([], ZZ)
|
| 182 |
+
|
| 183 |
+
f = dup_normal([1, 2, 3], ZZ)
|
| 184 |
+
|
| 185 |
+
assert dup_mul_ground(f, ZZ(0), ZZ) == dup_normal([], ZZ)
|
| 186 |
+
assert dup_mul_ground(f, ZZ(2), ZZ) == dup_normal([2, 4, 6], ZZ)
|
| 187 |
+
|
| 188 |
+
|
| 189 |
+
def test_dmp_mul_ground():
|
| 190 |
+
assert dmp_mul_ground(f_0, ZZ(2), 2, ZZ) == [
|
| 191 |
+
[[ZZ(2), ZZ(4), ZZ(6)], [ZZ(4)]],
|
| 192 |
+
[[ZZ(6)]],
|
| 193 |
+
[[ZZ(8), ZZ(10), ZZ(12)], [ZZ(2), ZZ(4), ZZ(2)], [ZZ(2)]]
|
| 194 |
+
]
|
| 195 |
+
|
| 196 |
+
assert dmp_mul_ground(F_0, QQ(1, 2), 2, QQ) == [
|
| 197 |
+
[[QQ(1, 14), QQ(2, 14), QQ(3, 14)], [QQ(2, 14)]],
|
| 198 |
+
[[QQ(3, 14)]],
|
| 199 |
+
[[QQ(4, 14), QQ(5, 14), QQ(6, 14)], [QQ(1, 14), QQ(2, 14),
|
| 200 |
+
QQ(1, 14)], [QQ(1, 14)]]
|
| 201 |
+
]
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
def test_dup_quo_ground():
|
| 205 |
+
raises(ZeroDivisionError, lambda: dup_quo_ground(dup_normal([1, 2,
|
| 206 |
+
3], ZZ), ZZ(0), ZZ))
|
| 207 |
+
|
| 208 |
+
f = dup_normal([], ZZ)
|
| 209 |
+
|
| 210 |
+
assert dup_quo_ground(f, ZZ(3), ZZ) == dup_normal([], ZZ)
|
| 211 |
+
|
| 212 |
+
f = dup_normal([6, 2, 8], ZZ)
|
| 213 |
+
|
| 214 |
+
assert dup_quo_ground(f, ZZ(1), ZZ) == f
|
| 215 |
+
assert dup_quo_ground(f, ZZ(2), ZZ) == dup_normal([3, 1, 4], ZZ)
|
| 216 |
+
|
| 217 |
+
assert dup_quo_ground(f, ZZ(3), ZZ) == dup_normal([2, 0, 2], ZZ)
|
| 218 |
+
|
| 219 |
+
f = dup_normal([6, 2, 8], QQ)
|
| 220 |
+
|
| 221 |
+
assert dup_quo_ground(f, QQ(1), QQ) == f
|
| 222 |
+
assert dup_quo_ground(f, QQ(2), QQ) == [QQ(3), QQ(1), QQ(4)]
|
| 223 |
+
assert dup_quo_ground(f, QQ(7), QQ) == [QQ(6, 7), QQ(2, 7), QQ(8, 7)]
|
| 224 |
+
|
| 225 |
+
|
| 226 |
+
def test_dup_exquo_ground():
|
| 227 |
+
raises(ZeroDivisionError, lambda: dup_exquo_ground(dup_normal([1,
|
| 228 |
+
2, 3], ZZ), ZZ(0), ZZ))
|
| 229 |
+
raises(ExactQuotientFailed, lambda: dup_exquo_ground(dup_normal([1,
|
| 230 |
+
2, 3], ZZ), ZZ(3), ZZ))
|
| 231 |
+
|
| 232 |
+
f = dup_normal([], ZZ)
|
| 233 |
+
|
| 234 |
+
assert dup_exquo_ground(f, ZZ(3), ZZ) == dup_normal([], ZZ)
|
| 235 |
+
|
| 236 |
+
f = dup_normal([6, 2, 8], ZZ)
|
| 237 |
+
|
| 238 |
+
assert dup_exquo_ground(f, ZZ(1), ZZ) == f
|
| 239 |
+
assert dup_exquo_ground(f, ZZ(2), ZZ) == dup_normal([3, 1, 4], ZZ)
|
| 240 |
+
|
| 241 |
+
f = dup_normal([6, 2, 8], QQ)
|
| 242 |
+
|
| 243 |
+
assert dup_exquo_ground(f, QQ(1), QQ) == f
|
| 244 |
+
assert dup_exquo_ground(f, QQ(2), QQ) == [QQ(3), QQ(1), QQ(4)]
|
| 245 |
+
assert dup_exquo_ground(f, QQ(7), QQ) == [QQ(6, 7), QQ(2, 7), QQ(8, 7)]
|
| 246 |
+
|
| 247 |
+
|
| 248 |
+
def test_dmp_quo_ground():
|
| 249 |
+
f = dmp_normal([[6], [2], [8]], 1, ZZ)
|
| 250 |
+
|
| 251 |
+
assert dmp_quo_ground(f, ZZ(1), 1, ZZ) == f
|
| 252 |
+
assert dmp_quo_ground(
|
| 253 |
+
f, ZZ(2), 1, ZZ) == dmp_normal([[3], [1], [4]], 1, ZZ)
|
| 254 |
+
|
| 255 |
+
assert dmp_normal(dmp_quo_ground(
|
| 256 |
+
f, ZZ(3), 1, ZZ), 1, ZZ) == dmp_normal([[2], [], [2]], 1, ZZ)
|
| 257 |
+
|
| 258 |
+
|
| 259 |
+
def test_dmp_exquo_ground():
|
| 260 |
+
f = dmp_normal([[6], [2], [8]], 1, ZZ)
|
| 261 |
+
|
| 262 |
+
assert dmp_exquo_ground(f, ZZ(1), 1, ZZ) == f
|
| 263 |
+
assert dmp_exquo_ground(
|
| 264 |
+
f, ZZ(2), 1, ZZ) == dmp_normal([[3], [1], [4]], 1, ZZ)
|
| 265 |
+
|
| 266 |
+
|
| 267 |
+
def test_dup_lshift():
|
| 268 |
+
assert dup_lshift([], 3, ZZ) == []
|
| 269 |
+
assert dup_lshift([1], 3, ZZ) == [1, 0, 0, 0]
|
| 270 |
+
|
| 271 |
+
|
| 272 |
+
def test_dup_rshift():
|
| 273 |
+
assert dup_rshift([], 3, ZZ) == []
|
| 274 |
+
assert dup_rshift([1, 0, 0, 0], 3, ZZ) == [1]
|
| 275 |
+
|
| 276 |
+
|
| 277 |
+
def test_dup_abs():
|
| 278 |
+
assert dup_abs([], ZZ) == []
|
| 279 |
+
assert dup_abs([ZZ( 1)], ZZ) == [ZZ(1)]
|
| 280 |
+
assert dup_abs([ZZ(-7)], ZZ) == [ZZ(7)]
|
| 281 |
+
assert dup_abs([ZZ(-1), ZZ(2), ZZ(3)], ZZ) == [ZZ(1), ZZ(2), ZZ(3)]
|
| 282 |
+
|
| 283 |
+
assert dup_abs([], QQ) == []
|
| 284 |
+
assert dup_abs([QQ( 1, 2)], QQ) == [QQ(1, 2)]
|
| 285 |
+
assert dup_abs([QQ(-7, 3)], QQ) == [QQ(7, 3)]
|
| 286 |
+
assert dup_abs(
|
| 287 |
+
[QQ(-1, 7), QQ(2, 7), QQ(3, 7)], QQ) == [QQ(1, 7), QQ(2, 7), QQ(3, 7)]
|
| 288 |
+
|
| 289 |
+
|
| 290 |
+
def test_dmp_abs():
|
| 291 |
+
assert dmp_abs([ZZ(-1)], 0, ZZ) == [ZZ(1)]
|
| 292 |
+
assert dmp_abs([QQ(-1, 2)], 0, QQ) == [QQ(1, 2)]
|
| 293 |
+
|
| 294 |
+
assert dmp_abs([[[]]], 2, ZZ) == [[[]]]
|
| 295 |
+
assert dmp_abs([[[ZZ(1)]]], 2, ZZ) == [[[ZZ(1)]]]
|
| 296 |
+
assert dmp_abs([[[ZZ(-7)]]], 2, ZZ) == [[[ZZ(7)]]]
|
| 297 |
+
|
| 298 |
+
assert dmp_abs([[[]]], 2, QQ) == [[[]]]
|
| 299 |
+
assert dmp_abs([[[QQ(1, 2)]]], 2, QQ) == [[[QQ(1, 2)]]]
|
| 300 |
+
assert dmp_abs([[[QQ(-7, 9)]]], 2, QQ) == [[[QQ(7, 9)]]]
|
| 301 |
+
|
| 302 |
+
|
| 303 |
+
def test_dup_neg():
|
| 304 |
+
assert dup_neg([], ZZ) == []
|
| 305 |
+
assert dup_neg([ZZ(1)], ZZ) == [ZZ(-1)]
|
| 306 |
+
assert dup_neg([ZZ(-7)], ZZ) == [ZZ(7)]
|
| 307 |
+
assert dup_neg([ZZ(-1), ZZ(2), ZZ(3)], ZZ) == [ZZ(1), ZZ(-2), ZZ(-3)]
|
| 308 |
+
|
| 309 |
+
assert dup_neg([], QQ) == []
|
| 310 |
+
assert dup_neg([QQ(1, 2)], QQ) == [QQ(-1, 2)]
|
| 311 |
+
assert dup_neg([QQ(-7, 9)], QQ) == [QQ(7, 9)]
|
| 312 |
+
assert dup_neg([QQ(
|
| 313 |
+
-1, 7), QQ(2, 7), QQ(3, 7)], QQ) == [QQ(1, 7), QQ(-2, 7), QQ(-3, 7)]
|
| 314 |
+
|
| 315 |
+
|
| 316 |
+
def test_dmp_neg():
|
| 317 |
+
assert dmp_neg([ZZ(-1)], 0, ZZ) == [ZZ(1)]
|
| 318 |
+
assert dmp_neg([QQ(-1, 2)], 0, QQ) == [QQ(1, 2)]
|
| 319 |
+
|
| 320 |
+
assert dmp_neg([[[]]], 2, ZZ) == [[[]]]
|
| 321 |
+
assert dmp_neg([[[ZZ(1)]]], 2, ZZ) == [[[ZZ(-1)]]]
|
| 322 |
+
assert dmp_neg([[[ZZ(-7)]]], 2, ZZ) == [[[ZZ(7)]]]
|
| 323 |
+
|
| 324 |
+
assert dmp_neg([[[]]], 2, QQ) == [[[]]]
|
| 325 |
+
assert dmp_neg([[[QQ(1, 9)]]], 2, QQ) == [[[QQ(-1, 9)]]]
|
| 326 |
+
assert dmp_neg([[[QQ(-7, 9)]]], 2, QQ) == [[[QQ(7, 9)]]]
|
| 327 |
+
|
| 328 |
+
|
| 329 |
+
def test_dup_add():
|
| 330 |
+
assert dup_add([], [], ZZ) == []
|
| 331 |
+
assert dup_add([ZZ(1)], [], ZZ) == [ZZ(1)]
|
| 332 |
+
assert dup_add([], [ZZ(1)], ZZ) == [ZZ(1)]
|
| 333 |
+
assert dup_add([ZZ(1)], [ZZ(1)], ZZ) == [ZZ(2)]
|
| 334 |
+
assert dup_add([ZZ(1)], [ZZ(2)], ZZ) == [ZZ(3)]
|
| 335 |
+
|
| 336 |
+
assert dup_add([ZZ(1), ZZ(2)], [ZZ(1)], ZZ) == [ZZ(1), ZZ(3)]
|
| 337 |
+
assert dup_add([ZZ(1)], [ZZ(1), ZZ(2)], ZZ) == [ZZ(1), ZZ(3)]
|
| 338 |
+
|
| 339 |
+
assert dup_add([ZZ(1), ZZ(
|
| 340 |
+
2), ZZ(3)], [ZZ(8), ZZ(9), ZZ(10)], ZZ) == [ZZ(9), ZZ(11), ZZ(13)]
|
| 341 |
+
|
| 342 |
+
assert dup_add([], [], QQ) == []
|
| 343 |
+
assert dup_add([QQ(1, 2)], [], QQ) == [QQ(1, 2)]
|
| 344 |
+
assert dup_add([], [QQ(1, 2)], QQ) == [QQ(1, 2)]
|
| 345 |
+
assert dup_add([QQ(1, 4)], [QQ(1, 4)], QQ) == [QQ(1, 2)]
|
| 346 |
+
assert dup_add([QQ(1, 4)], [QQ(1, 2)], QQ) == [QQ(3, 4)]
|
| 347 |
+
|
| 348 |
+
assert dup_add([QQ(1, 2), QQ(2, 3)], [QQ(1)], QQ) == [QQ(1, 2), QQ(5, 3)]
|
| 349 |
+
assert dup_add([QQ(1)], [QQ(1, 2), QQ(2, 3)], QQ) == [QQ(1, 2), QQ(5, 3)]
|
| 350 |
+
|
| 351 |
+
assert dup_add([QQ(1, 7), QQ(2, 7), QQ(3, 7)], [QQ(
|
| 352 |
+
8, 7), QQ(9, 7), QQ(10, 7)], QQ) == [QQ(9, 7), QQ(11, 7), QQ(13, 7)]
|
| 353 |
+
|
| 354 |
+
|
| 355 |
+
def test_dmp_add():
|
| 356 |
+
assert dmp_add([ZZ(1), ZZ(2)], [ZZ(1)], 0, ZZ) == \
|
| 357 |
+
dup_add([ZZ(1), ZZ(2)], [ZZ(1)], ZZ)
|
| 358 |
+
assert dmp_add([QQ(1, 2), QQ(2, 3)], [QQ(1)], 0, QQ) == \
|
| 359 |
+
dup_add([QQ(1, 2), QQ(2, 3)], [QQ(1)], QQ)
|
| 360 |
+
|
| 361 |
+
assert dmp_add([[[]]], [[[]]], 2, ZZ) == [[[]]]
|
| 362 |
+
assert dmp_add([[[ZZ(1)]]], [[[]]], 2, ZZ) == [[[ZZ(1)]]]
|
| 363 |
+
assert dmp_add([[[]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(1)]]]
|
| 364 |
+
assert dmp_add([[[ZZ(2)]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(3)]]]
|
| 365 |
+
assert dmp_add([[[ZZ(1)]]], [[[ZZ(2)]]], 2, ZZ) == [[[ZZ(3)]]]
|
| 366 |
+
|
| 367 |
+
assert dmp_add([[[]]], [[[]]], 2, QQ) == [[[]]]
|
| 368 |
+
assert dmp_add([[[QQ(1, 2)]]], [[[]]], 2, QQ) == [[[QQ(1, 2)]]]
|
| 369 |
+
assert dmp_add([[[]]], [[[QQ(1, 2)]]], 2, QQ) == [[[QQ(1, 2)]]]
|
| 370 |
+
assert dmp_add([[[QQ(2, 7)]]], [[[QQ(1, 7)]]], 2, QQ) == [[[QQ(3, 7)]]]
|
| 371 |
+
assert dmp_add([[[QQ(1, 7)]]], [[[QQ(2, 7)]]], 2, QQ) == [[[QQ(3, 7)]]]
|
| 372 |
+
|
| 373 |
+
|
| 374 |
+
def test_dup_sub():
|
| 375 |
+
assert dup_sub([], [], ZZ) == []
|
| 376 |
+
assert dup_sub([ZZ(1)], [], ZZ) == [ZZ(1)]
|
| 377 |
+
assert dup_sub([], [ZZ(1)], ZZ) == [ZZ(-1)]
|
| 378 |
+
assert dup_sub([ZZ(1)], [ZZ(1)], ZZ) == []
|
| 379 |
+
assert dup_sub([ZZ(1)], [ZZ(2)], ZZ) == [ZZ(-1)]
|
| 380 |
+
|
| 381 |
+
assert dup_sub([ZZ(1), ZZ(2)], [ZZ(1)], ZZ) == [ZZ(1), ZZ(1)]
|
| 382 |
+
assert dup_sub([ZZ(1)], [ZZ(1), ZZ(2)], ZZ) == [ZZ(-1), ZZ(-1)]
|
| 383 |
+
|
| 384 |
+
assert dup_sub([ZZ(3), ZZ(
|
| 385 |
+
2), ZZ(1)], [ZZ(8), ZZ(9), ZZ(10)], ZZ) == [ZZ(-5), ZZ(-7), ZZ(-9)]
|
| 386 |
+
|
| 387 |
+
assert dup_sub([], [], QQ) == []
|
| 388 |
+
assert dup_sub([QQ(1, 2)], [], QQ) == [QQ(1, 2)]
|
| 389 |
+
assert dup_sub([], [QQ(1, 2)], QQ) == [QQ(-1, 2)]
|
| 390 |
+
assert dup_sub([QQ(1, 3)], [QQ(1, 3)], QQ) == []
|
| 391 |
+
assert dup_sub([QQ(1, 3)], [QQ(2, 3)], QQ) == [QQ(-1, 3)]
|
| 392 |
+
|
| 393 |
+
assert dup_sub([QQ(1, 7), QQ(2, 7)], [QQ(1)], QQ) == [QQ(1, 7), QQ(-5, 7)]
|
| 394 |
+
assert dup_sub([QQ(1)], [QQ(1, 7), QQ(2, 7)], QQ) == [QQ(-1, 7), QQ(5, 7)]
|
| 395 |
+
|
| 396 |
+
assert dup_sub([QQ(3, 7), QQ(2, 7), QQ(1, 7)], [QQ(
|
| 397 |
+
8, 7), QQ(9, 7), QQ(10, 7)], QQ) == [QQ(-5, 7), QQ(-7, 7), QQ(-9, 7)]
|
| 398 |
+
|
| 399 |
+
|
| 400 |
+
def test_dmp_sub():
|
| 401 |
+
assert dmp_sub([ZZ(1), ZZ(2)], [ZZ(1)], 0, ZZ) == \
|
| 402 |
+
dup_sub([ZZ(1), ZZ(2)], [ZZ(1)], ZZ)
|
| 403 |
+
assert dmp_sub([QQ(1, 2), QQ(2, 3)], [QQ(1)], 0, QQ) == \
|
| 404 |
+
dup_sub([QQ(1, 2), QQ(2, 3)], [QQ(1)], QQ)
|
| 405 |
+
|
| 406 |
+
assert dmp_sub([[[]]], [[[]]], 2, ZZ) == [[[]]]
|
| 407 |
+
assert dmp_sub([[[ZZ(1)]]], [[[]]], 2, ZZ) == [[[ZZ(1)]]]
|
| 408 |
+
assert dmp_sub([[[]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(-1)]]]
|
| 409 |
+
assert dmp_sub([[[ZZ(2)]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(1)]]]
|
| 410 |
+
assert dmp_sub([[[ZZ(1)]]], [[[ZZ(2)]]], 2, ZZ) == [[[ZZ(-1)]]]
|
| 411 |
+
|
| 412 |
+
assert dmp_sub([[[]]], [[[]]], 2, QQ) == [[[]]]
|
| 413 |
+
assert dmp_sub([[[QQ(1, 2)]]], [[[]]], 2, QQ) == [[[QQ(1, 2)]]]
|
| 414 |
+
assert dmp_sub([[[]]], [[[QQ(1, 2)]]], 2, QQ) == [[[QQ(-1, 2)]]]
|
| 415 |
+
assert dmp_sub([[[QQ(2, 7)]]], [[[QQ(1, 7)]]], 2, QQ) == [[[QQ(1, 7)]]]
|
| 416 |
+
assert dmp_sub([[[QQ(1, 7)]]], [[[QQ(2, 7)]]], 2, QQ) == [[[QQ(-1, 7)]]]
|
| 417 |
+
|
| 418 |
+
|
| 419 |
+
def test_dup_add_mul():
|
| 420 |
+
assert dup_add_mul([ZZ(1), ZZ(2), ZZ(3)], [ZZ(3), ZZ(2), ZZ(1)],
|
| 421 |
+
[ZZ(1), ZZ(2)], ZZ) == [ZZ(3), ZZ(9), ZZ(7), ZZ(5)]
|
| 422 |
+
assert dmp_add_mul([[ZZ(1), ZZ(2)], [ZZ(3)]], [[ZZ(3)], [ZZ(2), ZZ(1)]],
|
| 423 |
+
[[ZZ(1)], [ZZ(2)]], 1, ZZ) == [[ZZ(3)], [ZZ(3), ZZ(9)], [ZZ(4), ZZ(5)]]
|
| 424 |
+
|
| 425 |
+
|
| 426 |
+
def test_dup_sub_mul():
|
| 427 |
+
assert dup_sub_mul([ZZ(1), ZZ(2), ZZ(3)], [ZZ(3), ZZ(2), ZZ(1)],
|
| 428 |
+
[ZZ(1), ZZ(2)], ZZ) == [ZZ(-3), ZZ(-7), ZZ(-3), ZZ(1)]
|
| 429 |
+
assert dmp_sub_mul([[ZZ(1), ZZ(2)], [ZZ(3)]], [[ZZ(3)], [ZZ(2), ZZ(1)]],
|
| 430 |
+
[[ZZ(1)], [ZZ(2)]], 1, ZZ) == [[ZZ(-3)], [ZZ(-1), ZZ(-5)], [ZZ(-4), ZZ(1)]]
|
| 431 |
+
|
| 432 |
+
|
| 433 |
+
def test_dup_mul():
|
| 434 |
+
assert dup_mul([], [], ZZ) == []
|
| 435 |
+
assert dup_mul([], [ZZ(1)], ZZ) == []
|
| 436 |
+
assert dup_mul([ZZ(1)], [], ZZ) == []
|
| 437 |
+
assert dup_mul([ZZ(1)], [ZZ(1)], ZZ) == [ZZ(1)]
|
| 438 |
+
assert dup_mul([ZZ(5)], [ZZ(7)], ZZ) == [ZZ(35)]
|
| 439 |
+
|
| 440 |
+
assert dup_mul([], [], QQ) == []
|
| 441 |
+
assert dup_mul([], [QQ(1, 2)], QQ) == []
|
| 442 |
+
assert dup_mul([QQ(1, 2)], [], QQ) == []
|
| 443 |
+
assert dup_mul([QQ(1, 2)], [QQ(4, 7)], QQ) == [QQ(2, 7)]
|
| 444 |
+
assert dup_mul([QQ(5, 7)], [QQ(3, 7)], QQ) == [QQ(15, 49)]
|
| 445 |
+
|
| 446 |
+
f = dup_normal([3, 0, 0, 6, 1, 2], ZZ)
|
| 447 |
+
g = dup_normal([4, 0, 1, 0], ZZ)
|
| 448 |
+
h = dup_normal([12, 0, 3, 24, 4, 14, 1, 2, 0], ZZ)
|
| 449 |
+
|
| 450 |
+
assert dup_mul(f, g, ZZ) == h
|
| 451 |
+
assert dup_mul(g, f, ZZ) == h
|
| 452 |
+
|
| 453 |
+
f = dup_normal([2, 0, 0, 1, 7], ZZ)
|
| 454 |
+
h = dup_normal([4, 0, 0, 4, 28, 0, 1, 14, 49], ZZ)
|
| 455 |
+
|
| 456 |
+
assert dup_mul(f, f, ZZ) == h
|
| 457 |
+
|
| 458 |
+
K = FF(6)
|
| 459 |
+
|
| 460 |
+
assert dup_mul([K(2), K(1)], [K(3), K(4)], K) == [K(5), K(4)]
|
| 461 |
+
|
| 462 |
+
p1 = dup_normal([79, -1, 78, -94, -10, 11, 32, -19, 78, 2, -89, 30, 73, 42,
|
| 463 |
+
85, 77, 83, -30, -34, -2, 95, -81, 37, -49, -46, -58, -16, 37, 35, -11,
|
| 464 |
+
-57, -15, -31, 67, -20, 27, 76, 2, 70, 67, -65, 65, -26, -93, -44, -12,
|
| 465 |
+
-92, 57, -90, -57, -11, -67, -98, -69, 97, -41, 89, 33, 89, -50, 81,
|
| 466 |
+
-31, 60, -27, 43, 29, -77, 44, 21, -91, 32, -57, 33, 3, 53, -51, -38,
|
| 467 |
+
-99, -84, 23, -50, 66, -100, 1, -75, -25, 27, -60, 98, -51, -87, 6, 8,
|
| 468 |
+
78, -28, -95, -88, 12, -35, 26, -9, 16, -92, 55, -7, -86, 68, -39, -46,
|
| 469 |
+
84, 94, 45, 60, 92, 68, -75, -74, -19, 8, 75, 78, 91, 57, 34, 14, -3,
|
| 470 |
+
-49, 65, 78, -18, 6, -29, -80, -98, 17, 13, 58, 21, 20, 9, 37, 7, -30,
|
| 471 |
+
-53, -20, 34, 67, -42, 89, -22, 73, 43, -6, 5, 51, -8, -15, -52, -22,
|
| 472 |
+
-58, -72, -3, 43, -92, 82, 83, -2, -13, -23, -60, 16, -94, -8, -28,
|
| 473 |
+
-95, -72, 63, -90, 76, 6, -43, -100, -59, 76, 3, 3, 46, -85, 75, 62,
|
| 474 |
+
-71, -76, 88, 97, -72, -1, 30, -64, 72, -48, 14, -78, 58, 63, -91, 24,
|
| 475 |
+
-87, -27, -80, -100, -44, 98, 70, 100, -29, -38, 11, 77, 100, 52, 86,
|
| 476 |
+
65, -5, -42, -81, -38, -42, 43, -2, -70, -63, -52], ZZ)
|
| 477 |
+
p2 = dup_normal([65, -19, -47, 1, 90, 81, -15, -34, 25, -75, 9, -83, 50, -5,
|
| 478 |
+
-44, 31, 1, 70, -7, 78, 74, 80, 85, 65, 21, 41, 66, 19, -40, 63, -21,
|
| 479 |
+
-27, 32, 69, 83, 34, -35, 14, 81, 57, -75, 32, -67, -89, -100, -61, 46,
|
| 480 |
+
84, -78, -29, -50, -94, -24, -32, -68, -16, 100, -7, -72, -89, 35, 82,
|
| 481 |
+
58, 81, -92, 62, 5, -47, -39, -58, -72, -13, 84, 44, 55, -25, 48, -54,
|
| 482 |
+
-31, -56, -11, -50, -84, 10, 67, 17, 13, -14, 61, 76, -64, -44, -40,
|
| 483 |
+
-96, 11, -11, -94, 2, 6, 27, -6, 68, -54, 66, -74, -14, -1, -24, -73,
|
| 484 |
+
96, 89, -11, -89, 56, -53, 72, -43, 96, 25, 63, -31, 29, 68, 83, 91,
|
| 485 |
+
-93, -19, -38, -40, 40, -12, -19, -79, 44, 100, -66, -29, -77, 62, 39,
|
| 486 |
+
-8, 11, -97, 14, 87, 64, 21, -18, 13, 15, -59, -75, -99, -88, 57, 54,
|
| 487 |
+
56, -67, 6, -63, -59, -14, 28, 87, -20, -39, 84, -91, -2, 49, -75, 11,
|
| 488 |
+
-24, -95, 36, 66, 5, 25, -72, -40, 86, 90, 37, -33, 57, -35, 29, -18,
|
| 489 |
+
4, -79, 64, -17, -27, 21, 29, -5, -44, -87, -24, 52, 78, 11, -23, -53,
|
| 490 |
+
36, 42, 21, -68, 94, -91, -51, -21, 51, -76, 72, 31, 24, -48, -80, -9,
|
| 491 |
+
37, -47, -6, -8, -63, -91, 79, -79, -100, 38, -20, 38, 100, 83, -90,
|
| 492 |
+
87, 63, -36, 82, -19, 18, -98, -38, 26, 98, -70, 79, 92, 12, 12, 70,
|
| 493 |
+
74, 36, 48, -13, 31, 31, -47, -71, -12, -64, 36, -42, 32, -86, 60, 83,
|
| 494 |
+
70, 55, 0, 1, 29, -35, 8, -82, 8, -73, -46, -50, 43, 48, -5, -86, -72,
|
| 495 |
+
44, -90, 19, 19, 5, -20, 97, -13, -66, -5, 5, -69, 64, -30, 41, 51, 36,
|
| 496 |
+
13, -99, -61, 94, -12, 74, 98, 68, 24, 46, -97, -87, -6, -27, 82, 62,
|
| 497 |
+
-11, -77, 86, 66, -47, -49, -50, 13, 18, 89, -89, 46, -80, 13, 98, -35,
|
| 498 |
+
-36, -25, 12, 20, 26, -52, 79, 27, 79, 100, 8, 62, -58, -28, 37], ZZ)
|
| 499 |
+
res = dup_normal([5135, -1566, 1376, -7466, 4579, 11710, 8001, -7183,
|
| 500 |
+
-3737, -7439, 345, -10084, 24522, -1201, 1070, -10245, 9582, 9264,
|
| 501 |
+
1903, 23312, 18953, 10037, -15268, -5450, 6442, -6243, -3777, 5110,
|
| 502 |
+
10936, -16649, -6022, 16255, 31300, 24818, 31922, 32760, 7854, 27080,
|
| 503 |
+
15766, 29596, 7139, 31945, -19810, 465, -38026, -3971, 9641, 465,
|
| 504 |
+
-19375, 5524, -30112, -11960, -12813, 13535, 30670, 5925, -43725,
|
| 505 |
+
-14089, 11503, -22782, 6371, 43881, 37465, -33529, -33590, -39798,
|
| 506 |
+
-37854, -18466, -7908, -35825, -26020, -36923, -11332, -5699, 25166,
|
| 507 |
+
-3147, 19885, 12962, -20659, -1642, 27723, -56331, -24580, -11010,
|
| 508 |
+
-20206, 20087, -23772, -16038, 38580, 20901, -50731, 32037, -4299,
|
| 509 |
+
26508, 18038, -28357, 31846, -7405, -20172, -15894, 2096, 25110,
|
| 510 |
+
-45786, 45918, -55333, -31928, -49428, -29824, -58796, -24609, -15408,
|
| 511 |
+
69, -35415, -18439, 10123, -20360, -65949, 33356, -20333, 26476,
|
| 512 |
+
-32073, 33621, 930, 28803, -42791, 44716, 38164, 12302, -1739, 11421,
|
| 513 |
+
73385, -7613, 14297, 38155, -414, 77587, 24338, -21415, 29367, 42639,
|
| 514 |
+
13901, -288, 51027, -11827, 91260, 43407, 88521, -15186, 70572, -12049,
|
| 515 |
+
5090, -12208, -56374, 15520, -623, -7742, 50825, 11199, -14894, 40892,
|
| 516 |
+
59591, -31356, -28696, -57842, -87751, -33744, -28436, -28945, -40287,
|
| 517 |
+
37957, -35638, 33401, -61534, 14870, 40292, 70366, -10803, 102290,
|
| 518 |
+
-71719, -85251, 7902, -22409, 75009, 99927, 35298, -1175, -762, -34744,
|
| 519 |
+
-10587, -47574, -62629, -19581, -43659, -54369, -32250, -39545, 15225,
|
| 520 |
+
-24454, 11241, -67308, -30148, 39929, 37639, 14383, -73475, -77636,
|
| 521 |
+
-81048, -35992, 41601, -90143, 76937, -8112, 56588, 9124, -40094,
|
| 522 |
+
-32340, 13253, 10898, -51639, 36390, 12086, -1885, 100714, -28561,
|
| 523 |
+
-23784, -18735, 18916, 16286, 10742, -87360, -13697, 10689, -19477,
|
| 524 |
+
-29770, 5060, 20189, -8297, 112407, 47071, 47743, 45519, -4109, 17468,
|
| 525 |
+
-68831, 78325, -6481, -21641, -19459, 30919, 96115, 8607, 53341, 32105,
|
| 526 |
+
-16211, 23538, 57259, -76272, -40583, 62093, 38511, -34255, -40665,
|
| 527 |
+
-40604, -37606, -15274, 33156, -13885, 103636, 118678, -14101, -92682,
|
| 528 |
+
-100791, 2634, 63791, 98266, 19286, -34590, -21067, -71130, 25380,
|
| 529 |
+
-40839, -27614, -26060, 52358, -15537, 27138, -6749, 36269, -33306,
|
| 530 |
+
13207, -91084, -5540, -57116, 69548, 44169, -57742, -41234, -103327,
|
| 531 |
+
-62904, -8566, 41149, -12866, 71188, 23980, 1838, 58230, 73950, 5594,
|
| 532 |
+
43113, -8159, -15925, 6911, 85598, -75016, -16214, -62726, -39016,
|
| 533 |
+
8618, -63882, -4299, 23182, 49959, 49342, -3238, -24913, -37138, 78361,
|
| 534 |
+
32451, 6337, -11438, -36241, -37737, 8169, -3077, -24829, 57953, 53016,
|
| 535 |
+
-31511, -91168, 12599, -41849, 41576, 55275, -62539, 47814, -62319,
|
| 536 |
+
12300, -32076, -55137, -84881, -27546, 4312, -3433, -54382, 113288,
|
| 537 |
+
-30157, 74469, 18219, 79880, -2124, 98911, 17655, -33499, -32861,
|
| 538 |
+
47242, -37393, 99765, 14831, -44483, 10800, -31617, -52710, 37406,
|
| 539 |
+
22105, 29704, -20050, 13778, 43683, 36628, 8494, 60964, -22644, 31550,
|
| 540 |
+
-17693, 33805, -124879, -12302, 19343, 20400, -30937, -21574, -34037,
|
| 541 |
+
-33380, 56539, -24993, -75513, -1527, 53563, 65407, -101, 53577, 37991,
|
| 542 |
+
18717, -23795, -8090, -47987, -94717, 41967, 5170, -14815, -94311,
|
| 543 |
+
17896, -17734, -57718, -774, -38410, 24830, 29682, 76480, 58802,
|
| 544 |
+
-46416, -20348, -61353, -68225, -68306, 23822, -31598, 42972, 36327,
|
| 545 |
+
28968, -65638, -21638, 24354, -8356, 26777, 52982, -11783, -44051,
|
| 546 |
+
-26467, -44721, -28435, -53265, -25574, -2669, 44155, 22946, -18454,
|
| 547 |
+
-30718, -11252, 58420, 8711, 67447, 4425, 41749, 67543, 43162, 11793,
|
| 548 |
+
-41907, 20477, -13080, 6559, -6104, -13244, 42853, 42935, 29793, 36730,
|
| 549 |
+
-28087, 28657, 17946, 7503, 7204, 21491, -27450, -24241, -98156,
|
| 550 |
+
-18082, -42613, -24928, 10775, -14842, -44127, 55910, 14777, 31151, -2194,
|
| 551 |
+
39206, -2100, -4211, 11827, -8918, -19471, 72567, 36447, -65590, -34861,
|
| 552 |
+
-17147, -45303, 9025, -7333, -35473, 11101, 11638, 3441, 6626, -41800,
|
| 553 |
+
9416, 13679, 33508, 40502, -60542, 16358, 8392, -43242, -35864, -34127,
|
| 554 |
+
-48721, 35878, 30598, 28630, 20279, -19983, -14638, -24455, -1851, -11344,
|
| 555 |
+
45150, 42051, 26034, -28889, -32382, -3527, -14532, 22564, -22346, 477,
|
| 556 |
+
11706, 28338, -25972, -9185, -22867, -12522, 32120, -4424, 11339, -33913,
|
| 557 |
+
-7184, 5101, -23552, -17115, -31401, -6104, 21906, 25708, 8406, 6317,
|
| 558 |
+
-7525, 5014, 20750, 20179, 22724, 11692, 13297, 2493, -253, -16841, -17339,
|
| 559 |
+
-6753, -4808, 2976, -10881, -10228, -13816, -12686, 1385, 2316, 2190, -875,
|
| 560 |
+
-1924], ZZ)
|
| 561 |
+
|
| 562 |
+
assert dup_mul(p1, p2, ZZ) == res
|
| 563 |
+
|
| 564 |
+
p1 = dup_normal([83, -61, -86, -24, 12, 43, -88, -9, 42, 55, -66, 74, 95,
|
| 565 |
+
-25, -12, 68, -99, 4, 45, 6, -15, -19, 78, 65, -55, 47, -13, 17, 86,
|
| 566 |
+
81, -58, -27, 50, -40, -24, 39, -41, -92, 75, 90, -1, 40, -15, -27,
|
| 567 |
+
-35, 68, 70, -64, -40, 78, -88, -58, -39, 69, 46, 12, 28, -94, -37,
|
| 568 |
+
-50, -80, -96, -61, 25, 1, 71, 4, 12, 48, 4, 34, -47, -75, 5, 48, 82,
|
| 569 |
+
88, 23, 98, 35, 17, -10, 48, -61, -95, 47, 65, -19, -66, -57, -6, -51,
|
| 570 |
+
-42, -89, 66, -13, 18, 37, 90, -23, 72, 96, -53, 0, 40, -73, -52, -68,
|
| 571 |
+
32, -25, -53, 79, -52, 18, 44, 73, -81, 31, -90, 70, 3, 36, 48, 76,
|
| 572 |
+
-24, -44, 23, 98, -4, 73, 69, 88, -70, 14, -68, 94, -78, -15, -64, -97,
|
| 573 |
+
-70, -35, 65, 88, 49, -53, -7, 12, -45, -7, 59, -94, 99, -2, 67, -60,
|
| 574 |
+
-71, 29, -62, -77, 1, 51, 17, 80, -20, -47, -19, 24, -9, 39, -23, 21,
|
| 575 |
+
-84, 10, 84, 56, -17, -21, -66, 85, 70, 46, -51, -22, -95, 78, -60,
|
| 576 |
+
-96, -97, -45, 72, 35, 30, -61, -92, -93, -60, -61, 4, -4, -81, -73,
|
| 577 |
+
46, 53, -11, 26, 94, 45, 14, -78, 55, 84, -68, 98, 60, 23, 100, -63,
|
| 578 |
+
68, 96, -16, 3, 56, 21, -58, 62, -67, 66, 85, 41, -79, -22, 97, -67,
|
| 579 |
+
82, 82, -96, -20, -7, 48, -67, 48, -9, -39, 78], ZZ)
|
| 580 |
+
p2 = dup_normal([52, 88, 76, 66, 9, -64, 46, -20, -28, 69, 60, 96, -36,
|
| 581 |
+
-92, -30, -11, -35, 35, 55, 63, -92, -7, 25, -58, 74, 55, -6, 4, 47,
|
| 582 |
+
-92, -65, 67, -45, 74, -76, 59, -6, 69, 39, 24, -71, -7, 39, -45, 60,
|
| 583 |
+
-68, 98, 97, -79, 17, 4, 94, -64, 68, -100, -96, -2, 3, 22, 96, 54,
|
| 584 |
+
-77, -86, 67, 6, 57, 37, 40, 89, -78, 64, -94, -45, -92, 57, 87, -26,
|
| 585 |
+
36, 19, 97, 25, 77, -87, 24, 43, -5, 35, 57, 83, 71, 35, 63, 61, 96,
|
| 586 |
+
-22, 8, -1, 96, 43, 45, 94, -93, 36, 71, -41, -99, 85, -48, 59, 52,
|
| 587 |
+
-17, 5, 87, -16, -68, -54, 76, -18, 100, 91, -42, -70, -66, -88, -12,
|
| 588 |
+
1, 95, -82, 52, 43, -29, 3, 12, 72, -99, -43, -32, -93, -51, 16, -20,
|
| 589 |
+
-12, -11, 5, 33, -38, 93, -5, -74, 25, 74, -58, 93, 59, -63, -86, 63,
|
| 590 |
+
-20, -4, -74, -73, -95, 29, -28, 93, -91, -2, -38, -62, 77, -58, -85,
|
| 591 |
+
-28, 95, 38, 19, -69, 86, 94, 25, -2, -4, 47, 34, -59, 35, -48, 29,
|
| 592 |
+
-63, -53, 34, 29, 66, 73, 6, 92, -84, 89, 15, 81, 93, 97, 51, -72, -78,
|
| 593 |
+
25, 60, 90, -45, 39, 67, -84, -62, 57, 26, -32, -56, -14, -83, 76, 5,
|
| 594 |
+
-2, 99, -100, 28, 46, 94, -7, 53, -25, 16, -23, -36, 89, -78, -63, 31,
|
| 595 |
+
1, 84, -99, -52, 76, 48, 90, -76, 44, -19, 54, -36, -9, -73, -100, -69,
|
| 596 |
+
31, 42, 25, -39, 76, -26, -8, -14, 51, 3, 37, 45, 2, -54, 13, -34, -92,
|
| 597 |
+
17, -25, -65, 53, -63, 30, 4, -70, -67, 90, 52, 51, 18, -3, 31, -45,
|
| 598 |
+
-9, 59, 63, -87, 22, -32, 29, -38, 21, 36, -82, 27, -11], ZZ)
|
| 599 |
+
res = dup_normal([4316, 4132, -3532, -7974, -11303, -10069, 5484, -3330,
|
| 600 |
+
-5874, 7734, 4673, 11327, -9884, -8031, 17343, 21035, -10570, -9285,
|
| 601 |
+
15893, 3780, -14083, 8819, 17592, 10159, 7174, -11587, 8598, -16479,
|
| 602 |
+
3602, 25596, 9781, 12163, 150, 18749, -21782, -12307, 27578, -2757,
|
| 603 |
+
-12573, 12565, 6345, -18956, 19503, -15617, 1443, -16778, 36851, 23588,
|
| 604 |
+
-28474, 5749, 40695, -7521, -53669, -2497, -18530, 6770, 57038, 3926,
|
| 605 |
+
-6927, -15399, 1848, -64649, -27728, 3644, 49608, 15187, -8902, -9480,
|
| 606 |
+
-7398, -40425, 4824, 23767, -7594, -6905, 33089, 18786, 12192, 24670,
|
| 607 |
+
31114, 35334, -4501, -14676, 7107, -59018, -21352, 20777, 19661, 20653,
|
| 608 |
+
33754, -885, -43758, 6269, 51897, -28719, -97488, -9527, 13746, 11644,
|
| 609 |
+
17644, -21720, 23782, -10481, 47867, 20752, 33810, -1875, 39918, -7710,
|
| 610 |
+
-40840, 19808, -47075, 23066, 46616, 25201, 9287, 35436, -1602, 9645,
|
| 611 |
+
-11978, 13273, 15544, 33465, 20063, 44539, 11687, 27314, -6538, -37467,
|
| 612 |
+
14031, 32970, -27086, 41323, 29551, 65910, -39027, -37800, -22232,
|
| 613 |
+
8212, 46316, -28981, -55282, 50417, -44929, -44062, 73879, 37573,
|
| 614 |
+
-2596, -10877, -21893, -133218, -33707, -25753, -9531, 17530, 61126,
|
| 615 |
+
2748, -56235, 43874, -10872, -90459, -30387, 115267, -7264, -44452,
|
| 616 |
+
122626, 14839, -599, 10337, 57166, -67467, -54957, 63669, 1202, 18488,
|
| 617 |
+
52594, 7205, -97822, 612, 78069, -5403, -63562, 47236, 36873, -154827,
|
| 618 |
+
-26188, 82427, -39521, 5628, 7416, 5276, -53095, 47050, 26121, -42207,
|
| 619 |
+
79021, -13035, 2499, -66943, 29040, -72355, -23480, 23416, -12885,
|
| 620 |
+
-44225, -42688, -4224, 19858, 55299, 15735, 11465, 101876, -39169,
|
| 621 |
+
51786, 14723, 43280, -68697, 16410, 92295, 56767, 7183, 111850, 4550,
|
| 622 |
+
115451, -38443, -19642, -35058, 10230, 93829, 8925, 63047, 3146, 29250,
|
| 623 |
+
8530, 5255, -98117, -115517, -76817, -8724, 41044, 1312, -35974, 79333,
|
| 624 |
+
-28567, 7547, -10580, -24559, -16238, 10794, -3867, 24848, 57770,
|
| 625 |
+
-51536, -35040, 71033, 29853, 62029, -7125, -125585, -32169, -47907,
|
| 626 |
+
156811, -65176, -58006, -15757, -57861, 11963, 30225, -41901, -41681,
|
| 627 |
+
31310, 27982, 18613, 61760, 60746, -59096, 33499, 30097, -17997, 24032,
|
| 628 |
+
56442, -83042, 23747, -20931, -21978, -158752, -9883, -73598, -7987,
|
| 629 |
+
-7333, -125403, -116329, 30585, 53281, 51018, -29193, 88575, 8264,
|
| 630 |
+
-40147, -16289, 113088, 12810, -6508, 101552, -13037, 34440, -41840,
|
| 631 |
+
101643, 24263, 80532, 61748, 65574, 6423, -20672, 6591, -10834, -71716,
|
| 632 |
+
86919, -92626, 39161, 28490, 81319, 46676, 106720, 43530, 26998, 57456,
|
| 633 |
+
-8862, 60989, 13982, 3119, -2224, 14743, 55415, -49093, -29303, 28999,
|
| 634 |
+
1789, 55953, -84043, -7780, -65013, 57129, -47251, 61484, 61994,
|
| 635 |
+
-78361, -82778, 22487, -26894, 9756, -74637, -15519, -4360, 30115,
|
| 636 |
+
42433, 35475, 15286, 69768, 21509, -20214, 78675, -21163, 13596, 11443,
|
| 637 |
+
-10698, -53621, -53867, -24155, 64500, -42784, -33077, -16500, 873,
|
| 638 |
+
-52788, 14546, -38011, 36974, -39849, -34029, -94311, 83068, -50437,
|
| 639 |
+
-26169, -46746, 59185, 42259, -101379, -12943, 30089, -59086, 36271,
|
| 640 |
+
22723, -30253, -52472, -70826, -23289, 3331, -31687, 14183, -857,
|
| 641 |
+
-28627, 35246, -51284, 5636, -6933, 66539, 36654, 50927, 24783, 3457,
|
| 642 |
+
33276, 45281, 45650, -4938, -9968, -22590, 47995, 69229, 5214, -58365,
|
| 643 |
+
-17907, -14651, 18668, 18009, 12649, -11851, -13387, 20339, 52472,
|
| 644 |
+
-1087, -21458, -68647, 52295, 15849, 40608, 15323, 25164, -29368,
|
| 645 |
+
10352, -7055, 7159, 21695, -5373, -54849, 101103, -24963, -10511,
|
| 646 |
+
33227, 7659, 41042, -69588, 26718, -20515, 6441, 38135, -63, 24088,
|
| 647 |
+
-35364, -12785, -18709, 47843, 48533, -48575, 17251, -19394, 32878,
|
| 648 |
+
-9010, -9050, 504, -12407, 28076, -3429, 25324, -4210, -26119, 752,
|
| 649 |
+
-29203, 28251, -11324, -32140, -3366, -25135, 18702, -31588, -7047,
|
| 650 |
+
-24267, 49987, -14975, -33169, 37744, -7720, -9035, 16964, -2807, -421,
|
| 651 |
+
14114, -17097, -13662, 40628, -12139, -9427, 5369, 17551, -13232, -16211,
|
| 652 |
+
9804, -7422, 2677, 28635, -8280, -4906, 2908, -22558, 5604, 12459, 8756,
|
| 653 |
+
-3980, -4745, -18525, 7913, 5970, -16457, 20230, -6247, -13812, 2505,
|
| 654 |
+
11899, 1409, -15094, 22540, -18863, 137, 11123, -4516, 2290, -8594, 12150,
|
| 655 |
+
-10380, 3005, 5235, -7350, 2535, -858], ZZ)
|
| 656 |
+
|
| 657 |
+
assert dup_mul(p1, p2, ZZ) == res
|
| 658 |
+
|
| 659 |
+
|
| 660 |
+
def test_dmp_mul():
|
| 661 |
+
assert dmp_mul([ZZ(5)], [ZZ(7)], 0, ZZ) == \
|
| 662 |
+
dup_mul([ZZ(5)], [ZZ(7)], ZZ)
|
| 663 |
+
assert dmp_mul([QQ(5, 7)], [QQ(3, 7)], 0, QQ) == \
|
| 664 |
+
dup_mul([QQ(5, 7)], [QQ(3, 7)], QQ)
|
| 665 |
+
|
| 666 |
+
assert dmp_mul([[[]]], [[[]]], 2, ZZ) == [[[]]]
|
| 667 |
+
assert dmp_mul([[[ZZ(1)]]], [[[]]], 2, ZZ) == [[[]]]
|
| 668 |
+
assert dmp_mul([[[]]], [[[ZZ(1)]]], 2, ZZ) == [[[]]]
|
| 669 |
+
assert dmp_mul([[[ZZ(2)]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(2)]]]
|
| 670 |
+
assert dmp_mul([[[ZZ(1)]]], [[[ZZ(2)]]], 2, ZZ) == [[[ZZ(2)]]]
|
| 671 |
+
|
| 672 |
+
assert dmp_mul([[[]]], [[[]]], 2, QQ) == [[[]]]
|
| 673 |
+
assert dmp_mul([[[QQ(1, 2)]]], [[[]]], 2, QQ) == [[[]]]
|
| 674 |
+
assert dmp_mul([[[]]], [[[QQ(1, 2)]]], 2, QQ) == [[[]]]
|
| 675 |
+
assert dmp_mul([[[QQ(2, 7)]]], [[[QQ(1, 3)]]], 2, QQ) == [[[QQ(2, 21)]]]
|
| 676 |
+
assert dmp_mul([[[QQ(1, 7)]]], [[[QQ(2, 3)]]], 2, QQ) == [[[QQ(2, 21)]]]
|
| 677 |
+
|
| 678 |
+
K = FF(6)
|
| 679 |
+
|
| 680 |
+
assert dmp_mul(
|
| 681 |
+
[[K(2)], [K(1)]], [[K(3)], [K(4)]], 1, K) == [[K(5)], [K(4)]]
|
| 682 |
+
|
| 683 |
+
|
| 684 |
+
def test_dup_sqr():
|
| 685 |
+
assert dup_sqr([], ZZ) == []
|
| 686 |
+
assert dup_sqr([ZZ(2)], ZZ) == [ZZ(4)]
|
| 687 |
+
assert dup_sqr([ZZ(1), ZZ(2)], ZZ) == [ZZ(1), ZZ(4), ZZ(4)]
|
| 688 |
+
|
| 689 |
+
assert dup_sqr([], QQ) == []
|
| 690 |
+
assert dup_sqr([QQ(2, 3)], QQ) == [QQ(4, 9)]
|
| 691 |
+
assert dup_sqr([QQ(1, 3), QQ(2, 3)], QQ) == [QQ(1, 9), QQ(4, 9), QQ(4, 9)]
|
| 692 |
+
|
| 693 |
+
f = dup_normal([2, 0, 0, 1, 7], ZZ)
|
| 694 |
+
|
| 695 |
+
assert dup_sqr(f, ZZ) == dup_normal([4, 0, 0, 4, 28, 0, 1, 14, 49], ZZ)
|
| 696 |
+
|
| 697 |
+
K = FF(9)
|
| 698 |
+
|
| 699 |
+
assert dup_sqr([K(3), K(4)], K) == [K(6), K(7)]
|
| 700 |
+
|
| 701 |
+
|
| 702 |
+
def test_dmp_sqr():
|
| 703 |
+
assert dmp_sqr([ZZ(1), ZZ(2)], 0, ZZ) == \
|
| 704 |
+
dup_sqr([ZZ(1), ZZ(2)], ZZ)
|
| 705 |
+
|
| 706 |
+
assert dmp_sqr([[[]]], 2, ZZ) == [[[]]]
|
| 707 |
+
assert dmp_sqr([[[ZZ(2)]]], 2, ZZ) == [[[ZZ(4)]]]
|
| 708 |
+
|
| 709 |
+
assert dmp_sqr([[[]]], 2, QQ) == [[[]]]
|
| 710 |
+
assert dmp_sqr([[[QQ(2, 3)]]], 2, QQ) == [[[QQ(4, 9)]]]
|
| 711 |
+
|
| 712 |
+
K = FF(9)
|
| 713 |
+
|
| 714 |
+
assert dmp_sqr([[K(3)], [K(4)]], 1, K) == [[K(6)], [K(7)]]
|
| 715 |
+
|
| 716 |
+
|
| 717 |
+
def test_dup_pow():
|
| 718 |
+
assert dup_pow([], 0, ZZ) == [ZZ(1)]
|
| 719 |
+
assert dup_pow([], 0, QQ) == [QQ(1)]
|
| 720 |
+
|
| 721 |
+
assert dup_pow([], 1, ZZ) == []
|
| 722 |
+
assert dup_pow([], 7, ZZ) == []
|
| 723 |
+
|
| 724 |
+
assert dup_pow([ZZ(1)], 0, ZZ) == [ZZ(1)]
|
| 725 |
+
assert dup_pow([ZZ(1)], 1, ZZ) == [ZZ(1)]
|
| 726 |
+
assert dup_pow([ZZ(1)], 7, ZZ) == [ZZ(1)]
|
| 727 |
+
|
| 728 |
+
assert dup_pow([ZZ(3)], 0, ZZ) == [ZZ(1)]
|
| 729 |
+
assert dup_pow([ZZ(3)], 1, ZZ) == [ZZ(3)]
|
| 730 |
+
assert dup_pow([ZZ(3)], 7, ZZ) == [ZZ(2187)]
|
| 731 |
+
|
| 732 |
+
assert dup_pow([QQ(1, 1)], 0, QQ) == [QQ(1, 1)]
|
| 733 |
+
assert dup_pow([QQ(1, 1)], 1, QQ) == [QQ(1, 1)]
|
| 734 |
+
assert dup_pow([QQ(1, 1)], 7, QQ) == [QQ(1, 1)]
|
| 735 |
+
|
| 736 |
+
assert dup_pow([QQ(3, 7)], 0, QQ) == [QQ(1, 1)]
|
| 737 |
+
assert dup_pow([QQ(3, 7)], 1, QQ) == [QQ(3, 7)]
|
| 738 |
+
assert dup_pow([QQ(3, 7)], 7, QQ) == [QQ(2187, 823543)]
|
| 739 |
+
|
| 740 |
+
f = dup_normal([2, 0, 0, 1, 7], ZZ)
|
| 741 |
+
|
| 742 |
+
assert dup_pow(f, 0, ZZ) == dup_normal([1], ZZ)
|
| 743 |
+
assert dup_pow(f, 1, ZZ) == dup_normal([2, 0, 0, 1, 7], ZZ)
|
| 744 |
+
assert dup_pow(f, 2, ZZ) == dup_normal([4, 0, 0, 4, 28, 0, 1, 14, 49], ZZ)
|
| 745 |
+
assert dup_pow(f, 3, ZZ) == dup_normal(
|
| 746 |
+
[8, 0, 0, 12, 84, 0, 6, 84, 294, 1, 21, 147, 343], ZZ)
|
| 747 |
+
|
| 748 |
+
|
| 749 |
+
def test_dmp_pow():
|
| 750 |
+
assert dmp_pow([[]], 0, 1, ZZ) == [[ZZ(1)]]
|
| 751 |
+
assert dmp_pow([[]], 0, 1, QQ) == [[QQ(1)]]
|
| 752 |
+
|
| 753 |
+
assert dmp_pow([[]], 1, 1, ZZ) == [[]]
|
| 754 |
+
assert dmp_pow([[]], 7, 1, ZZ) == [[]]
|
| 755 |
+
|
| 756 |
+
assert dmp_pow([[ZZ(1)]], 0, 1, ZZ) == [[ZZ(1)]]
|
| 757 |
+
assert dmp_pow([[ZZ(1)]], 1, 1, ZZ) == [[ZZ(1)]]
|
| 758 |
+
assert dmp_pow([[ZZ(1)]], 7, 1, ZZ) == [[ZZ(1)]]
|
| 759 |
+
|
| 760 |
+
assert dmp_pow([[QQ(3, 7)]], 0, 1, QQ) == [[QQ(1, 1)]]
|
| 761 |
+
assert dmp_pow([[QQ(3, 7)]], 1, 1, QQ) == [[QQ(3, 7)]]
|
| 762 |
+
assert dmp_pow([[QQ(3, 7)]], 7, 1, QQ) == [[QQ(2187, 823543)]]
|
| 763 |
+
|
| 764 |
+
f = dup_normal([2, 0, 0, 1, 7], ZZ)
|
| 765 |
+
|
| 766 |
+
assert dmp_pow(f, 2, 0, ZZ) == dup_pow(f, 2, ZZ)
|
| 767 |
+
|
| 768 |
+
|
| 769 |
+
def test_dup_pdiv():
|
| 770 |
+
f = dup_normal([3, 1, 1, 5], ZZ)
|
| 771 |
+
g = dup_normal([5, -3, 1], ZZ)
|
| 772 |
+
|
| 773 |
+
q = dup_normal([15, 14], ZZ)
|
| 774 |
+
r = dup_normal([52, 111], ZZ)
|
| 775 |
+
|
| 776 |
+
assert dup_pdiv(f, g, ZZ) == (q, r)
|
| 777 |
+
assert dup_pquo(f, g, ZZ) == q
|
| 778 |
+
assert dup_prem(f, g, ZZ) == r
|
| 779 |
+
|
| 780 |
+
raises(ExactQuotientFailed, lambda: dup_pexquo(f, g, ZZ))
|
| 781 |
+
|
| 782 |
+
f = dup_normal([3, 1, 1, 5], QQ)
|
| 783 |
+
g = dup_normal([5, -3, 1], QQ)
|
| 784 |
+
|
| 785 |
+
q = dup_normal([15, 14], QQ)
|
| 786 |
+
r = dup_normal([52, 111], QQ)
|
| 787 |
+
|
| 788 |
+
assert dup_pdiv(f, g, QQ) == (q, r)
|
| 789 |
+
assert dup_pquo(f, g, QQ) == q
|
| 790 |
+
assert dup_prem(f, g, QQ) == r
|
| 791 |
+
|
| 792 |
+
raises(ExactQuotientFailed, lambda: dup_pexquo(f, g, QQ))
|
| 793 |
+
|
| 794 |
+
|
| 795 |
+
def test_dmp_pdiv():
|
| 796 |
+
f = dmp_normal([[1], [], [1, 0, 0]], 1, ZZ)
|
| 797 |
+
g = dmp_normal([[1], [-1, 0]], 1, ZZ)
|
| 798 |
+
|
| 799 |
+
q = dmp_normal([[1], [1, 0]], 1, ZZ)
|
| 800 |
+
r = dmp_normal([[2, 0, 0]], 1, ZZ)
|
| 801 |
+
|
| 802 |
+
assert dmp_pdiv(f, g, 1, ZZ) == (q, r)
|
| 803 |
+
assert dmp_pquo(f, g, 1, ZZ) == q
|
| 804 |
+
assert dmp_prem(f, g, 1, ZZ) == r
|
| 805 |
+
|
| 806 |
+
raises(ExactQuotientFailed, lambda: dmp_pexquo(f, g, 1, ZZ))
|
| 807 |
+
|
| 808 |
+
f = dmp_normal([[1], [], [1, 0, 0]], 1, ZZ)
|
| 809 |
+
g = dmp_normal([[2], [-2, 0]], 1, ZZ)
|
| 810 |
+
|
| 811 |
+
q = dmp_normal([[2], [2, 0]], 1, ZZ)
|
| 812 |
+
r = dmp_normal([[8, 0, 0]], 1, ZZ)
|
| 813 |
+
|
| 814 |
+
assert dmp_pdiv(f, g, 1, ZZ) == (q, r)
|
| 815 |
+
assert dmp_pquo(f, g, 1, ZZ) == q
|
| 816 |
+
assert dmp_prem(f, g, 1, ZZ) == r
|
| 817 |
+
|
| 818 |
+
raises(ExactQuotientFailed, lambda: dmp_pexquo(f, g, 1, ZZ))
|
| 819 |
+
|
| 820 |
+
|
| 821 |
+
def test_dup_rr_div():
|
| 822 |
+
raises(ZeroDivisionError, lambda: dup_rr_div([1, 2, 3], [], ZZ))
|
| 823 |
+
|
| 824 |
+
f = dup_normal([3, 1, 1, 5], ZZ)
|
| 825 |
+
g = dup_normal([5, -3, 1], ZZ)
|
| 826 |
+
|
| 827 |
+
q, r = [], f
|
| 828 |
+
|
| 829 |
+
assert dup_rr_div(f, g, ZZ) == (q, r)
|
| 830 |
+
|
| 831 |
+
|
| 832 |
+
def test_dmp_rr_div():
|
| 833 |
+
raises(ZeroDivisionError, lambda: dmp_rr_div([[1, 2], [3]], [[]], 1, ZZ))
|
| 834 |
+
|
| 835 |
+
f = dmp_normal([[1], [], [1, 0, 0]], 1, ZZ)
|
| 836 |
+
g = dmp_normal([[1], [-1, 0]], 1, ZZ)
|
| 837 |
+
|
| 838 |
+
q = dmp_normal([[1], [1, 0]], 1, ZZ)
|
| 839 |
+
r = dmp_normal([[2, 0, 0]], 1, ZZ)
|
| 840 |
+
|
| 841 |
+
assert dmp_rr_div(f, g, 1, ZZ) == (q, r)
|
| 842 |
+
|
| 843 |
+
f = dmp_normal([[1], [], [1, 0, 0]], 1, ZZ)
|
| 844 |
+
g = dmp_normal([[-1], [1, 0]], 1, ZZ)
|
| 845 |
+
|
| 846 |
+
q = dmp_normal([[-1], [-1, 0]], 1, ZZ)
|
| 847 |
+
r = dmp_normal([[2, 0, 0]], 1, ZZ)
|
| 848 |
+
|
| 849 |
+
assert dmp_rr_div(f, g, 1, ZZ) == (q, r)
|
| 850 |
+
|
| 851 |
+
f = dmp_normal([[1], [], [1, 0, 0]], 1, ZZ)
|
| 852 |
+
g = dmp_normal([[2], [-2, 0]], 1, ZZ)
|
| 853 |
+
|
| 854 |
+
q, r = [[]], f
|
| 855 |
+
|
| 856 |
+
assert dmp_rr_div(f, g, 1, ZZ) == (q, r)
|
| 857 |
+
|
| 858 |
+
|
| 859 |
+
def test_dup_ff_div():
|
| 860 |
+
raises(ZeroDivisionError, lambda: dup_ff_div([1, 2, 3], [], QQ))
|
| 861 |
+
|
| 862 |
+
f = dup_normal([3, 1, 1, 5], QQ)
|
| 863 |
+
g = dup_normal([5, -3, 1], QQ)
|
| 864 |
+
|
| 865 |
+
q = [QQ(3, 5), QQ(14, 25)]
|
| 866 |
+
r = [QQ(52, 25), QQ(111, 25)]
|
| 867 |
+
|
| 868 |
+
assert dup_ff_div(f, g, QQ) == (q, r)
|
| 869 |
+
|
| 870 |
+
def test_dup_ff_div_gmpy2():
|
| 871 |
+
if GROUND_TYPES != 'gmpy2':
|
| 872 |
+
return
|
| 873 |
+
|
| 874 |
+
from gmpy2 import mpq
|
| 875 |
+
from sympy.polys.domains import GMPYRationalField
|
| 876 |
+
K = GMPYRationalField()
|
| 877 |
+
|
| 878 |
+
f = [mpq(1,3), mpq(3,2)]
|
| 879 |
+
g = [mpq(2,1)]
|
| 880 |
+
assert dmp_ff_div(f, g, 0, K) == ([mpq(1,6), mpq(3,4)], [])
|
| 881 |
+
|
| 882 |
+
f = [mpq(1,2), mpq(1,3), mpq(1,4), mpq(1,5)]
|
| 883 |
+
g = [mpq(-1,1), mpq(1,1), mpq(-1,1)]
|
| 884 |
+
assert dmp_ff_div(f, g, 0, K) == ([mpq(-1,2), mpq(-5,6)], [mpq(7,12), mpq(-19,30)])
|
| 885 |
+
|
| 886 |
+
def test_dmp_ff_div():
|
| 887 |
+
raises(ZeroDivisionError, lambda: dmp_ff_div([[1, 2], [3]], [[]], 1, QQ))
|
| 888 |
+
|
| 889 |
+
f = dmp_normal([[1], [], [1, 0, 0]], 1, QQ)
|
| 890 |
+
g = dmp_normal([[1], [-1, 0]], 1, QQ)
|
| 891 |
+
|
| 892 |
+
q = [[QQ(1, 1)], [QQ(1, 1), QQ(0, 1)]]
|
| 893 |
+
r = [[QQ(2, 1), QQ(0, 1), QQ(0, 1)]]
|
| 894 |
+
|
| 895 |
+
assert dmp_ff_div(f, g, 1, QQ) == (q, r)
|
| 896 |
+
|
| 897 |
+
f = dmp_normal([[1], [], [1, 0, 0]], 1, QQ)
|
| 898 |
+
g = dmp_normal([[-1], [1, 0]], 1, QQ)
|
| 899 |
+
|
| 900 |
+
q = [[QQ(-1, 1)], [QQ(-1, 1), QQ(0, 1)]]
|
| 901 |
+
r = [[QQ(2, 1), QQ(0, 1), QQ(0, 1)]]
|
| 902 |
+
|
| 903 |
+
assert dmp_ff_div(f, g, 1, QQ) == (q, r)
|
| 904 |
+
|
| 905 |
+
f = dmp_normal([[1], [], [1, 0, 0]], 1, QQ)
|
| 906 |
+
g = dmp_normal([[2], [-2, 0]], 1, QQ)
|
| 907 |
+
|
| 908 |
+
q = [[QQ(1, 2)], [QQ(1, 2), QQ(0, 1)]]
|
| 909 |
+
r = [[QQ(2, 1), QQ(0, 1), QQ(0, 1)]]
|
| 910 |
+
|
| 911 |
+
assert dmp_ff_div(f, g, 1, QQ) == (q, r)
|
| 912 |
+
|
| 913 |
+
|
| 914 |
+
def test_dup_div():
|
| 915 |
+
f, g, q, r = [5, 4, 3, 2, 1], [1, 2, 3], [5, -6, 0], [20, 1]
|
| 916 |
+
|
| 917 |
+
assert dup_div(f, g, ZZ) == (q, r)
|
| 918 |
+
assert dup_quo(f, g, ZZ) == q
|
| 919 |
+
assert dup_rem(f, g, ZZ) == r
|
| 920 |
+
|
| 921 |
+
raises(ExactQuotientFailed, lambda: dup_exquo(f, g, ZZ))
|
| 922 |
+
|
| 923 |
+
f, g, q, r = [5, 4, 3, 2, 1, 0], [1, 2, 0, 0, 9], [5, -6], [15, 2, -44, 54]
|
| 924 |
+
|
| 925 |
+
assert dup_div(f, g, ZZ) == (q, r)
|
| 926 |
+
assert dup_quo(f, g, ZZ) == q
|
| 927 |
+
assert dup_rem(f, g, ZZ) == r
|
| 928 |
+
|
| 929 |
+
raises(ExactQuotientFailed, lambda: dup_exquo(f, g, ZZ))
|
| 930 |
+
|
| 931 |
+
|
| 932 |
+
def test_dmp_div():
|
| 933 |
+
f, g, q, r = [5, 4, 3, 2, 1], [1, 2, 3], [5, -6, 0], [20, 1]
|
| 934 |
+
|
| 935 |
+
assert dmp_div(f, g, 0, ZZ) == (q, r)
|
| 936 |
+
assert dmp_quo(f, g, 0, ZZ) == q
|
| 937 |
+
assert dmp_rem(f, g, 0, ZZ) == r
|
| 938 |
+
|
| 939 |
+
raises(ExactQuotientFailed, lambda: dmp_exquo(f, g, 0, ZZ))
|
| 940 |
+
|
| 941 |
+
f, g, q, r = [[[1]]], [[[2]], [1]], [[[]]], [[[1]]]
|
| 942 |
+
|
| 943 |
+
assert dmp_div(f, g, 2, ZZ) == (q, r)
|
| 944 |
+
assert dmp_quo(f, g, 2, ZZ) == q
|
| 945 |
+
assert dmp_rem(f, g, 2, ZZ) == r
|
| 946 |
+
|
| 947 |
+
raises(ExactQuotientFailed, lambda: dmp_exquo(f, g, 2, ZZ))
|
| 948 |
+
|
| 949 |
+
|
| 950 |
+
def test_dup_max_norm():
|
| 951 |
+
assert dup_max_norm([], ZZ) == 0
|
| 952 |
+
assert dup_max_norm([1], ZZ) == 1
|
| 953 |
+
|
| 954 |
+
assert dup_max_norm([1, 4, 2, 3], ZZ) == 4
|
| 955 |
+
|
| 956 |
+
|
| 957 |
+
def test_dmp_max_norm():
|
| 958 |
+
assert dmp_max_norm([[[]]], 2, ZZ) == 0
|
| 959 |
+
assert dmp_max_norm([[[1]]], 2, ZZ) == 1
|
| 960 |
+
|
| 961 |
+
assert dmp_max_norm(f_0, 2, ZZ) == 6
|
| 962 |
+
|
| 963 |
+
|
| 964 |
+
def test_dup_l1_norm():
|
| 965 |
+
assert dup_l1_norm([], ZZ) == 0
|
| 966 |
+
assert dup_l1_norm([1], ZZ) == 1
|
| 967 |
+
assert dup_l1_norm([1, 4, 2, 3], ZZ) == 10
|
| 968 |
+
|
| 969 |
+
|
| 970 |
+
def test_dmp_l1_norm():
|
| 971 |
+
assert dmp_l1_norm([[[]]], 2, ZZ) == 0
|
| 972 |
+
assert dmp_l1_norm([[[1]]], 2, ZZ) == 1
|
| 973 |
+
|
| 974 |
+
assert dmp_l1_norm(f_0, 2, ZZ) == 31
|
| 975 |
+
|
| 976 |
+
|
| 977 |
+
def test_dup_l2_norm_squared():
|
| 978 |
+
assert dup_l2_norm_squared([], ZZ) == 0
|
| 979 |
+
assert dup_l2_norm_squared([1], ZZ) == 1
|
| 980 |
+
assert dup_l2_norm_squared([1, 4, 2, 3], ZZ) == 30
|
| 981 |
+
|
| 982 |
+
|
| 983 |
+
def test_dmp_l2_norm_squared():
|
| 984 |
+
assert dmp_l2_norm_squared([[[]]], 2, ZZ) == 0
|
| 985 |
+
assert dmp_l2_norm_squared([[[1]]], 2, ZZ) == 1
|
| 986 |
+
assert dmp_l2_norm_squared(f_0, 2, ZZ) == 111
|
| 987 |
+
|
| 988 |
+
|
| 989 |
+
def test_dup_expand():
|
| 990 |
+
assert dup_expand((), ZZ) == [1]
|
| 991 |
+
assert dup_expand(([1, 2, 3], [1, 2], [7, 5, 4, 3]), ZZ) == \
|
| 992 |
+
dup_mul([1, 2, 3], dup_mul([1, 2], [7, 5, 4, 3], ZZ), ZZ)
|
| 993 |
+
|
| 994 |
+
|
| 995 |
+
def test_dmp_expand():
|
| 996 |
+
assert dmp_expand((), 1, ZZ) == [[1]]
|
| 997 |
+
assert dmp_expand(([[1], [2], [3]], [[1], [2]], [[7], [5], [4], [3]]), 1, ZZ) == \
|
| 998 |
+
dmp_mul([[1], [2], [3]], dmp_mul([[1], [2]], [[7], [5], [
|
| 999 |
+
4], [3]], 1, ZZ), 1, ZZ)
|
| 1000 |
+
|
| 1001 |
+
def test_dup_mul_poly():
|
| 1002 |
+
p = Poly(18786186952704.0*x**165 + 9.31746684052255e+31*x**82, x, domain='RR')
|
| 1003 |
+
px = Poly(18786186952704.0*x**166 + 9.31746684052255e+31*x**83, x, domain='RR')
|
| 1004 |
+
|
| 1005 |
+
assert p * x == px
|
| 1006 |
+
assert p.set_domain(QQ) * x == px.set_domain(QQ)
|
| 1007 |
+
assert p.set_domain(CC) * x == px.set_domain(CC)
|
.venv/lib/python3.13/site-packages/sympy/polys/tests/test_densebasic.py
ADDED
|
@@ -0,0 +1,730 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Tests for dense recursive polynomials' basic tools. """
|
| 2 |
+
|
| 3 |
+
from sympy.polys.densebasic import (
|
| 4 |
+
ninf,
|
| 5 |
+
dup_LC, dmp_LC,
|
| 6 |
+
dup_TC, dmp_TC,
|
| 7 |
+
dmp_ground_LC, dmp_ground_TC,
|
| 8 |
+
dmp_true_LT,
|
| 9 |
+
dup_degree, dmp_degree,
|
| 10 |
+
dmp_degree_in, dmp_degree_list,
|
| 11 |
+
dup_strip, dmp_strip,
|
| 12 |
+
dmp_validate,
|
| 13 |
+
dup_reverse,
|
| 14 |
+
dup_copy, dmp_copy,
|
| 15 |
+
dup_normal, dmp_normal,
|
| 16 |
+
dup_convert, dmp_convert,
|
| 17 |
+
dup_from_sympy, dmp_from_sympy,
|
| 18 |
+
dup_nth, dmp_nth, dmp_ground_nth,
|
| 19 |
+
dmp_zero_p, dmp_zero,
|
| 20 |
+
dmp_one_p, dmp_one,
|
| 21 |
+
dmp_ground_p, dmp_ground,
|
| 22 |
+
dmp_negative_p, dmp_positive_p,
|
| 23 |
+
dmp_zeros, dmp_grounds,
|
| 24 |
+
dup_from_dict, dup_from_raw_dict,
|
| 25 |
+
dup_to_dict, dup_to_raw_dict,
|
| 26 |
+
dmp_from_dict, dmp_to_dict,
|
| 27 |
+
dmp_swap, dmp_permute,
|
| 28 |
+
dmp_nest, dmp_raise,
|
| 29 |
+
dup_deflate, dmp_deflate,
|
| 30 |
+
dup_multi_deflate, dmp_multi_deflate,
|
| 31 |
+
dup_inflate, dmp_inflate,
|
| 32 |
+
dmp_exclude, dmp_include,
|
| 33 |
+
dmp_inject, dmp_eject,
|
| 34 |
+
dup_terms_gcd, dmp_terms_gcd,
|
| 35 |
+
dmp_list_terms, dmp_apply_pairs,
|
| 36 |
+
dup_slice,
|
| 37 |
+
dup_random,
|
| 38 |
+
)
|
| 39 |
+
|
| 40 |
+
from sympy.polys.specialpolys import f_polys
|
| 41 |
+
from sympy.polys.domains import ZZ, QQ
|
| 42 |
+
from sympy.polys.rings import ring
|
| 43 |
+
|
| 44 |
+
from sympy.core.singleton import S
|
| 45 |
+
from sympy.testing.pytest import raises
|
| 46 |
+
|
| 47 |
+
from sympy.core.numbers import oo
|
| 48 |
+
|
| 49 |
+
f_0, f_1, f_2, f_3, f_4, f_5, f_6 = [ f.to_dense() for f in f_polys() ]
|
| 50 |
+
|
| 51 |
+
def test_dup_LC():
|
| 52 |
+
assert dup_LC([], ZZ) == 0
|
| 53 |
+
assert dup_LC([2, 3, 4, 5], ZZ) == 2
|
| 54 |
+
|
| 55 |
+
|
| 56 |
+
def test_dup_TC():
|
| 57 |
+
assert dup_TC([], ZZ) == 0
|
| 58 |
+
assert dup_TC([2, 3, 4, 5], ZZ) == 5
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def test_dmp_LC():
|
| 62 |
+
assert dmp_LC([[]], ZZ) == []
|
| 63 |
+
assert dmp_LC([[2, 3, 4], [5]], ZZ) == [2, 3, 4]
|
| 64 |
+
assert dmp_LC([[[]]], ZZ) == [[]]
|
| 65 |
+
assert dmp_LC([[[2], [3, 4]], [[5]]], ZZ) == [[2], [3, 4]]
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def test_dmp_TC():
|
| 69 |
+
assert dmp_TC([[]], ZZ) == []
|
| 70 |
+
assert dmp_TC([[2, 3, 4], [5]], ZZ) == [5]
|
| 71 |
+
assert dmp_TC([[[]]], ZZ) == [[]]
|
| 72 |
+
assert dmp_TC([[[2], [3, 4]], [[5]]], ZZ) == [[5]]
|
| 73 |
+
|
| 74 |
+
|
| 75 |
+
def test_dmp_ground_LC():
|
| 76 |
+
assert dmp_ground_LC([[]], 1, ZZ) == 0
|
| 77 |
+
assert dmp_ground_LC([[2, 3, 4], [5]], 1, ZZ) == 2
|
| 78 |
+
assert dmp_ground_LC([[[]]], 2, ZZ) == 0
|
| 79 |
+
assert dmp_ground_LC([[[2], [3, 4]], [[5]]], 2, ZZ) == 2
|
| 80 |
+
|
| 81 |
+
|
| 82 |
+
def test_dmp_ground_TC():
|
| 83 |
+
assert dmp_ground_TC([[]], 1, ZZ) == 0
|
| 84 |
+
assert dmp_ground_TC([[2, 3, 4], [5]], 1, ZZ) == 5
|
| 85 |
+
assert dmp_ground_TC([[[]]], 2, ZZ) == 0
|
| 86 |
+
assert dmp_ground_TC([[[2], [3, 4]], [[5]]], 2, ZZ) == 5
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def test_dmp_true_LT():
|
| 90 |
+
assert dmp_true_LT([[]], 1, ZZ) == ((0, 0), 0)
|
| 91 |
+
assert dmp_true_LT([[7]], 1, ZZ) == ((0, 0), 7)
|
| 92 |
+
|
| 93 |
+
assert dmp_true_LT([[1, 0]], 1, ZZ) == ((0, 1), 1)
|
| 94 |
+
assert dmp_true_LT([[1], []], 1, ZZ) == ((1, 0), 1)
|
| 95 |
+
assert dmp_true_LT([[1, 0], []], 1, ZZ) == ((1, 1), 1)
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
def test_dup_degree():
|
| 99 |
+
assert ninf == float('-inf')
|
| 100 |
+
assert dup_degree([]) is ninf
|
| 101 |
+
assert dup_degree([1]) == 0
|
| 102 |
+
assert dup_degree([1, 0]) == 1
|
| 103 |
+
assert dup_degree([1, 0, 0, 0, 1]) == 4
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def test_dmp_degree():
|
| 107 |
+
assert dmp_degree([[]], 1) is ninf
|
| 108 |
+
assert dmp_degree([[[]]], 2) is ninf
|
| 109 |
+
|
| 110 |
+
assert dmp_degree([[1]], 1) == 0
|
| 111 |
+
assert dmp_degree([[2], [1]], 1) == 1
|
| 112 |
+
|
| 113 |
+
|
| 114 |
+
def test_dmp_degree_in():
|
| 115 |
+
assert dmp_degree_in([[[]]], 0, 2) is ninf
|
| 116 |
+
assert dmp_degree_in([[[]]], 1, 2) is ninf
|
| 117 |
+
assert dmp_degree_in([[[]]], 2, 2) is ninf
|
| 118 |
+
|
| 119 |
+
assert dmp_degree_in([[[1]]], 0, 2) == 0
|
| 120 |
+
assert dmp_degree_in([[[1]]], 1, 2) == 0
|
| 121 |
+
assert dmp_degree_in([[[1]]], 2, 2) == 0
|
| 122 |
+
|
| 123 |
+
assert dmp_degree_in(f_4, 0, 2) == 9
|
| 124 |
+
assert dmp_degree_in(f_4, 1, 2) == 12
|
| 125 |
+
assert dmp_degree_in(f_4, 2, 2) == 8
|
| 126 |
+
|
| 127 |
+
assert dmp_degree_in(f_6, 0, 2) == 4
|
| 128 |
+
assert dmp_degree_in(f_6, 1, 2) == 4
|
| 129 |
+
assert dmp_degree_in(f_6, 2, 2) == 6
|
| 130 |
+
assert dmp_degree_in(f_6, 3, 3) == 3
|
| 131 |
+
|
| 132 |
+
raises(IndexError, lambda: dmp_degree_in([[1]], -5, 1))
|
| 133 |
+
|
| 134 |
+
|
| 135 |
+
def test_dmp_degree_list():
|
| 136 |
+
assert dmp_degree_list([[[[ ]]]], 3) == (-oo, -oo, -oo, -oo)
|
| 137 |
+
assert dmp_degree_list([[[[1]]]], 3) == ( 0, 0, 0, 0)
|
| 138 |
+
|
| 139 |
+
assert dmp_degree_list(f_0, 2) == (2, 2, 2)
|
| 140 |
+
assert dmp_degree_list(f_1, 2) == (3, 3, 3)
|
| 141 |
+
assert dmp_degree_list(f_2, 2) == (5, 3, 3)
|
| 142 |
+
assert dmp_degree_list(f_3, 2) == (5, 4, 7)
|
| 143 |
+
assert dmp_degree_list(f_4, 2) == (9, 12, 8)
|
| 144 |
+
assert dmp_degree_list(f_5, 2) == (3, 3, 3)
|
| 145 |
+
assert dmp_degree_list(f_6, 3) == (4, 4, 6, 3)
|
| 146 |
+
|
| 147 |
+
|
| 148 |
+
def test_dup_strip():
|
| 149 |
+
assert dup_strip([]) == []
|
| 150 |
+
assert dup_strip([0]) == []
|
| 151 |
+
assert dup_strip([0, 0, 0]) == []
|
| 152 |
+
|
| 153 |
+
assert dup_strip([1]) == [1]
|
| 154 |
+
assert dup_strip([0, 1]) == [1]
|
| 155 |
+
assert dup_strip([0, 0, 0, 1]) == [1]
|
| 156 |
+
|
| 157 |
+
assert dup_strip([1, 2, 0]) == [1, 2, 0]
|
| 158 |
+
assert dup_strip([0, 1, 2, 0]) == [1, 2, 0]
|
| 159 |
+
assert dup_strip([0, 0, 0, 1, 2, 0]) == [1, 2, 0]
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
def test_dmp_strip():
|
| 163 |
+
assert dmp_strip([0, 1, 0], 0) == [1, 0]
|
| 164 |
+
|
| 165 |
+
assert dmp_strip([[]], 1) == [[]]
|
| 166 |
+
assert dmp_strip([[], []], 1) == [[]]
|
| 167 |
+
assert dmp_strip([[], [], []], 1) == [[]]
|
| 168 |
+
|
| 169 |
+
assert dmp_strip([[[]]], 2) == [[[]]]
|
| 170 |
+
assert dmp_strip([[[]], [[]]], 2) == [[[]]]
|
| 171 |
+
assert dmp_strip([[[]], [[]], [[]]], 2) == [[[]]]
|
| 172 |
+
|
| 173 |
+
assert dmp_strip([[[1]]], 2) == [[[1]]]
|
| 174 |
+
assert dmp_strip([[[]], [[1]]], 2) == [[[1]]]
|
| 175 |
+
assert dmp_strip([[[]], [[1]], [[]]], 2) == [[[1]], [[]]]
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
def test_dmp_validate():
|
| 179 |
+
assert dmp_validate([]) == ([], 0)
|
| 180 |
+
assert dmp_validate([0, 0, 0, 1, 0]) == ([1, 0], 0)
|
| 181 |
+
|
| 182 |
+
assert dmp_validate([[[]]]) == ([[[]]], 2)
|
| 183 |
+
assert dmp_validate([[0], [], [0], [1], [0]]) == ([[1], []], 1)
|
| 184 |
+
|
| 185 |
+
raises(ValueError, lambda: dmp_validate([[0], 0, [0], [1], [0]]))
|
| 186 |
+
|
| 187 |
+
|
| 188 |
+
def test_dup_reverse():
|
| 189 |
+
assert dup_reverse([1, 2, 0, 3]) == [3, 0, 2, 1]
|
| 190 |
+
assert dup_reverse([1, 2, 3, 0]) == [3, 2, 1]
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
def test_dup_copy():
|
| 194 |
+
f = [ZZ(1), ZZ(0), ZZ(2)]
|
| 195 |
+
g = dup_copy(f)
|
| 196 |
+
|
| 197 |
+
g[0], g[2] = ZZ(7), ZZ(0)
|
| 198 |
+
|
| 199 |
+
assert f != g
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
def test_dmp_copy():
|
| 203 |
+
f = [[ZZ(1)], [ZZ(2), ZZ(0)]]
|
| 204 |
+
g = dmp_copy(f, 1)
|
| 205 |
+
|
| 206 |
+
g[0][0], g[1][1] = ZZ(7), ZZ(1)
|
| 207 |
+
|
| 208 |
+
assert f != g
|
| 209 |
+
|
| 210 |
+
|
| 211 |
+
def test_dup_normal():
|
| 212 |
+
assert dup_normal([0, 0, 2, 1, 0, 11, 0], ZZ) == \
|
| 213 |
+
[ZZ(2), ZZ(1), ZZ(0), ZZ(11), ZZ(0)]
|
| 214 |
+
|
| 215 |
+
|
| 216 |
+
def test_dmp_normal():
|
| 217 |
+
assert dmp_normal([[0], [], [0, 2, 1], [0], [11], []], 1, ZZ) == \
|
| 218 |
+
[[ZZ(2), ZZ(1)], [], [ZZ(11)], []]
|
| 219 |
+
|
| 220 |
+
|
| 221 |
+
def test_dup_convert():
|
| 222 |
+
K0, K1 = ZZ['x'], ZZ
|
| 223 |
+
|
| 224 |
+
f = [K0(1), K0(2), K0(0), K0(3)]
|
| 225 |
+
|
| 226 |
+
assert dup_convert(f, K0, K1) == \
|
| 227 |
+
[ZZ(1), ZZ(2), ZZ(0), ZZ(3)]
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
def test_dmp_convert():
|
| 231 |
+
K0, K1 = ZZ['x'], ZZ
|
| 232 |
+
|
| 233 |
+
f = [[K0(1)], [K0(2)], [], [K0(3)]]
|
| 234 |
+
|
| 235 |
+
assert dmp_convert(f, 1, K0, K1) == \
|
| 236 |
+
[[ZZ(1)], [ZZ(2)], [], [ZZ(3)]]
|
| 237 |
+
|
| 238 |
+
|
| 239 |
+
def test_dup_from_sympy():
|
| 240 |
+
assert dup_from_sympy([S.One, S(2)], ZZ) == \
|
| 241 |
+
[ZZ(1), ZZ(2)]
|
| 242 |
+
assert dup_from_sympy([S.Half, S(3)], QQ) == \
|
| 243 |
+
[QQ(1, 2), QQ(3, 1)]
|
| 244 |
+
|
| 245 |
+
|
| 246 |
+
def test_dmp_from_sympy():
|
| 247 |
+
assert dmp_from_sympy([[S.One, S(2)], [S.Zero]], 1, ZZ) == \
|
| 248 |
+
[[ZZ(1), ZZ(2)], []]
|
| 249 |
+
assert dmp_from_sympy([[S.Half, S(2)]], 1, QQ) == \
|
| 250 |
+
[[QQ(1, 2), QQ(2, 1)]]
|
| 251 |
+
|
| 252 |
+
|
| 253 |
+
def test_dup_nth():
|
| 254 |
+
assert dup_nth([1, 2, 3], 0, ZZ) == 3
|
| 255 |
+
assert dup_nth([1, 2, 3], 1, ZZ) == 2
|
| 256 |
+
assert dup_nth([1, 2, 3], 2, ZZ) == 1
|
| 257 |
+
|
| 258 |
+
assert dup_nth([1, 2, 3], 9, ZZ) == 0
|
| 259 |
+
|
| 260 |
+
raises(IndexError, lambda: dup_nth([3, 4, 5], -1, ZZ))
|
| 261 |
+
|
| 262 |
+
|
| 263 |
+
def test_dmp_nth():
|
| 264 |
+
assert dmp_nth([[1], [2], [3]], 0, 1, ZZ) == [3]
|
| 265 |
+
assert dmp_nth([[1], [2], [3]], 1, 1, ZZ) == [2]
|
| 266 |
+
assert dmp_nth([[1], [2], [3]], 2, 1, ZZ) == [1]
|
| 267 |
+
|
| 268 |
+
assert dmp_nth([[1], [2], [3]], 9, 1, ZZ) == []
|
| 269 |
+
|
| 270 |
+
raises(IndexError, lambda: dmp_nth([[3], [4], [5]], -1, 1, ZZ))
|
| 271 |
+
|
| 272 |
+
|
| 273 |
+
def test_dmp_ground_nth():
|
| 274 |
+
assert dmp_ground_nth([[]], (0, 0), 1, ZZ) == 0
|
| 275 |
+
assert dmp_ground_nth([[1], [2], [3]], (0, 0), 1, ZZ) == 3
|
| 276 |
+
assert dmp_ground_nth([[1], [2], [3]], (1, 0), 1, ZZ) == 2
|
| 277 |
+
assert dmp_ground_nth([[1], [2], [3]], (2, 0), 1, ZZ) == 1
|
| 278 |
+
|
| 279 |
+
assert dmp_ground_nth([[1], [2], [3]], (2, 1), 1, ZZ) == 0
|
| 280 |
+
assert dmp_ground_nth([[1], [2], [3]], (3, 0), 1, ZZ) == 0
|
| 281 |
+
|
| 282 |
+
raises(IndexError, lambda: dmp_ground_nth([[3], [4], [5]], (2, -1), 1, ZZ))
|
| 283 |
+
|
| 284 |
+
|
| 285 |
+
def test_dmp_zero_p():
|
| 286 |
+
assert dmp_zero_p([], 0) is True
|
| 287 |
+
assert dmp_zero_p([[]], 1) is True
|
| 288 |
+
|
| 289 |
+
assert dmp_zero_p([[[]]], 2) is True
|
| 290 |
+
assert dmp_zero_p([[[1]]], 2) is False
|
| 291 |
+
|
| 292 |
+
|
| 293 |
+
def test_dmp_zero():
|
| 294 |
+
assert dmp_zero(0) == []
|
| 295 |
+
assert dmp_zero(2) == [[[]]]
|
| 296 |
+
|
| 297 |
+
|
| 298 |
+
def test_dmp_one_p():
|
| 299 |
+
assert dmp_one_p([1], 0, ZZ) is True
|
| 300 |
+
assert dmp_one_p([[1]], 1, ZZ) is True
|
| 301 |
+
assert dmp_one_p([[[1]]], 2, ZZ) is True
|
| 302 |
+
assert dmp_one_p([[[12]]], 2, ZZ) is False
|
| 303 |
+
|
| 304 |
+
|
| 305 |
+
def test_dmp_one():
|
| 306 |
+
assert dmp_one(0, ZZ) == [ZZ(1)]
|
| 307 |
+
assert dmp_one(2, ZZ) == [[[ZZ(1)]]]
|
| 308 |
+
|
| 309 |
+
|
| 310 |
+
def test_dmp_ground_p():
|
| 311 |
+
assert dmp_ground_p([], 0, 0) is True
|
| 312 |
+
assert dmp_ground_p([[]], 0, 1) is True
|
| 313 |
+
assert dmp_ground_p([[]], 1, 1) is False
|
| 314 |
+
|
| 315 |
+
assert dmp_ground_p([[ZZ(1)]], 1, 1) is True
|
| 316 |
+
assert dmp_ground_p([[[ZZ(2)]]], 2, 2) is True
|
| 317 |
+
|
| 318 |
+
assert dmp_ground_p([[[ZZ(2)]]], 3, 2) is False
|
| 319 |
+
assert dmp_ground_p([[[ZZ(3)], []]], 3, 2) is False
|
| 320 |
+
|
| 321 |
+
assert dmp_ground_p([], None, 0) is True
|
| 322 |
+
assert dmp_ground_p([[]], None, 1) is True
|
| 323 |
+
|
| 324 |
+
assert dmp_ground_p([ZZ(1)], None, 0) is True
|
| 325 |
+
assert dmp_ground_p([[[ZZ(1)]]], None, 2) is True
|
| 326 |
+
|
| 327 |
+
assert dmp_ground_p([[[ZZ(3)], []]], None, 2) is False
|
| 328 |
+
|
| 329 |
+
|
| 330 |
+
def test_dmp_ground():
|
| 331 |
+
assert dmp_ground(ZZ(0), 2) == [[[]]]
|
| 332 |
+
|
| 333 |
+
assert dmp_ground(ZZ(7), -1) == ZZ(7)
|
| 334 |
+
assert dmp_ground(ZZ(7), 0) == [ZZ(7)]
|
| 335 |
+
assert dmp_ground(ZZ(7), 2) == [[[ZZ(7)]]]
|
| 336 |
+
|
| 337 |
+
|
| 338 |
+
def test_dmp_zeros():
|
| 339 |
+
assert dmp_zeros(4, 0, ZZ) == [[], [], [], []]
|
| 340 |
+
|
| 341 |
+
assert dmp_zeros(0, 2, ZZ) == []
|
| 342 |
+
assert dmp_zeros(1, 2, ZZ) == [[[[]]]]
|
| 343 |
+
assert dmp_zeros(2, 2, ZZ) == [[[[]]], [[[]]]]
|
| 344 |
+
assert dmp_zeros(3, 2, ZZ) == [[[[]]], [[[]]], [[[]]]]
|
| 345 |
+
|
| 346 |
+
assert dmp_zeros(3, -1, ZZ) == [0, 0, 0]
|
| 347 |
+
|
| 348 |
+
|
| 349 |
+
def test_dmp_grounds():
|
| 350 |
+
assert dmp_grounds(ZZ(7), 0, 2) == []
|
| 351 |
+
|
| 352 |
+
assert dmp_grounds(ZZ(7), 1, 2) == [[[[7]]]]
|
| 353 |
+
assert dmp_grounds(ZZ(7), 2, 2) == [[[[7]]], [[[7]]]]
|
| 354 |
+
assert dmp_grounds(ZZ(7), 3, 2) == [[[[7]]], [[[7]]], [[[7]]]]
|
| 355 |
+
|
| 356 |
+
assert dmp_grounds(ZZ(7), 3, -1) == [7, 7, 7]
|
| 357 |
+
|
| 358 |
+
|
| 359 |
+
def test_dmp_negative_p():
|
| 360 |
+
assert dmp_negative_p([[[]]], 2, ZZ) is False
|
| 361 |
+
assert dmp_negative_p([[[1], [2]]], 2, ZZ) is False
|
| 362 |
+
assert dmp_negative_p([[[-1], [2]]], 2, ZZ) is True
|
| 363 |
+
|
| 364 |
+
|
| 365 |
+
def test_dmp_positive_p():
|
| 366 |
+
assert dmp_positive_p([[[]]], 2, ZZ) is False
|
| 367 |
+
assert dmp_positive_p([[[1], [2]]], 2, ZZ) is True
|
| 368 |
+
assert dmp_positive_p([[[-1], [2]]], 2, ZZ) is False
|
| 369 |
+
|
| 370 |
+
|
| 371 |
+
def test_dup_from_to_dict():
|
| 372 |
+
assert dup_from_raw_dict({}, ZZ) == []
|
| 373 |
+
assert dup_from_dict({}, ZZ) == []
|
| 374 |
+
|
| 375 |
+
assert dup_to_raw_dict([]) == {}
|
| 376 |
+
assert dup_to_dict([]) == {}
|
| 377 |
+
|
| 378 |
+
assert dup_to_raw_dict([], ZZ, zero=True) == {0: ZZ(0)}
|
| 379 |
+
assert dup_to_dict([], ZZ, zero=True) == {(0,): ZZ(0)}
|
| 380 |
+
|
| 381 |
+
f = [3, 0, 0, 2, 0, 0, 0, 0, 8]
|
| 382 |
+
g = {8: 3, 5: 2, 0: 8}
|
| 383 |
+
h = {(8,): 3, (5,): 2, (0,): 8}
|
| 384 |
+
|
| 385 |
+
assert dup_from_raw_dict(g, ZZ) == f
|
| 386 |
+
assert dup_from_dict(h, ZZ) == f
|
| 387 |
+
|
| 388 |
+
assert dup_to_raw_dict(f) == g
|
| 389 |
+
assert dup_to_dict(f) == h
|
| 390 |
+
|
| 391 |
+
R, x,y = ring("x,y", ZZ)
|
| 392 |
+
K = R.to_domain()
|
| 393 |
+
|
| 394 |
+
f = [R(3), R(0), R(2), R(0), R(0), R(8)]
|
| 395 |
+
g = {5: R(3), 3: R(2), 0: R(8)}
|
| 396 |
+
h = {(5,): R(3), (3,): R(2), (0,): R(8)}
|
| 397 |
+
|
| 398 |
+
assert dup_from_raw_dict(g, K) == f
|
| 399 |
+
assert dup_from_dict(h, K) == f
|
| 400 |
+
|
| 401 |
+
assert dup_to_raw_dict(f) == g
|
| 402 |
+
assert dup_to_dict(f) == h
|
| 403 |
+
|
| 404 |
+
|
| 405 |
+
def test_dmp_from_to_dict():
|
| 406 |
+
assert dmp_from_dict({}, 1, ZZ) == [[]]
|
| 407 |
+
assert dmp_to_dict([[]], 1) == {}
|
| 408 |
+
|
| 409 |
+
assert dmp_to_dict([], 0, ZZ, zero=True) == {(0,): ZZ(0)}
|
| 410 |
+
assert dmp_to_dict([[]], 1, ZZ, zero=True) == {(0, 0): ZZ(0)}
|
| 411 |
+
|
| 412 |
+
f = [[3], [], [], [2], [], [], [], [], [8]]
|
| 413 |
+
g = {(8, 0): 3, (5, 0): 2, (0, 0): 8}
|
| 414 |
+
|
| 415 |
+
assert dmp_from_dict(g, 1, ZZ) == f
|
| 416 |
+
assert dmp_to_dict(f, 1) == g
|
| 417 |
+
|
| 418 |
+
|
| 419 |
+
def test_dmp_swap():
|
| 420 |
+
f = dmp_normal([[1, 0, 0], [], [1, 0], [], [1]], 1, ZZ)
|
| 421 |
+
g = dmp_normal([[1, 0, 0, 0, 0], [1, 0, 0], [1]], 1, ZZ)
|
| 422 |
+
|
| 423 |
+
assert dmp_swap(f, 1, 1, 1, ZZ) == f
|
| 424 |
+
|
| 425 |
+
assert dmp_swap(f, 0, 1, 1, ZZ) == g
|
| 426 |
+
assert dmp_swap(g, 0, 1, 1, ZZ) == f
|
| 427 |
+
|
| 428 |
+
raises(IndexError, lambda: dmp_swap(f, -1, -7, 1, ZZ))
|
| 429 |
+
|
| 430 |
+
|
| 431 |
+
def test_dmp_permute():
|
| 432 |
+
f = dmp_normal([[1, 0, 0], [], [1, 0], [], [1]], 1, ZZ)
|
| 433 |
+
g = dmp_normal([[1, 0, 0, 0, 0], [1, 0, 0], [1]], 1, ZZ)
|
| 434 |
+
|
| 435 |
+
assert dmp_permute(f, [0, 1], 1, ZZ) == f
|
| 436 |
+
assert dmp_permute(g, [0, 1], 1, ZZ) == g
|
| 437 |
+
|
| 438 |
+
assert dmp_permute(f, [1, 0], 1, ZZ) == g
|
| 439 |
+
assert dmp_permute(g, [1, 0], 1, ZZ) == f
|
| 440 |
+
|
| 441 |
+
|
| 442 |
+
def test_dmp_nest():
|
| 443 |
+
assert dmp_nest(ZZ(1), 2, ZZ) == [[[1]]]
|
| 444 |
+
|
| 445 |
+
assert dmp_nest([[1]], 0, ZZ) == [[1]]
|
| 446 |
+
assert dmp_nest([[1]], 1, ZZ) == [[[1]]]
|
| 447 |
+
assert dmp_nest([[1]], 2, ZZ) == [[[[1]]]]
|
| 448 |
+
|
| 449 |
+
|
| 450 |
+
def test_dmp_raise():
|
| 451 |
+
assert dmp_raise([], 2, 0, ZZ) == [[[]]]
|
| 452 |
+
assert dmp_raise([[1]], 0, 1, ZZ) == [[1]]
|
| 453 |
+
|
| 454 |
+
assert dmp_raise([[1, 2, 3], [], [2, 3]], 2, 1, ZZ) == \
|
| 455 |
+
[[[[1]], [[2]], [[3]]], [[[]]], [[[2]], [[3]]]]
|
| 456 |
+
|
| 457 |
+
|
| 458 |
+
def test_dup_deflate():
|
| 459 |
+
assert dup_deflate([], ZZ) == (1, [])
|
| 460 |
+
assert dup_deflate([2], ZZ) == (1, [2])
|
| 461 |
+
assert dup_deflate([1, 2, 3], ZZ) == (1, [1, 2, 3])
|
| 462 |
+
assert dup_deflate([1, 0, 2, 0, 3], ZZ) == (2, [1, 2, 3])
|
| 463 |
+
|
| 464 |
+
assert dup_deflate(dup_from_raw_dict({7: 1, 1: 1}, ZZ), ZZ) == \
|
| 465 |
+
(1, [1, 0, 0, 0, 0, 0, 1, 0])
|
| 466 |
+
assert dup_deflate(dup_from_raw_dict({7: 1, 0: 1}, ZZ), ZZ) == \
|
| 467 |
+
(7, [1, 1])
|
| 468 |
+
assert dup_deflate(dup_from_raw_dict({7: 1, 3: 1}, ZZ), ZZ) == \
|
| 469 |
+
(1, [1, 0, 0, 0, 1, 0, 0, 0])
|
| 470 |
+
|
| 471 |
+
assert dup_deflate(dup_from_raw_dict({7: 1, 4: 1}, ZZ), ZZ) == \
|
| 472 |
+
(1, [1, 0, 0, 1, 0, 0, 0, 0])
|
| 473 |
+
assert dup_deflate(dup_from_raw_dict({8: 1, 4: 1}, ZZ), ZZ) == \
|
| 474 |
+
(4, [1, 1, 0])
|
| 475 |
+
|
| 476 |
+
assert dup_deflate(dup_from_raw_dict({8: 1}, ZZ), ZZ) == \
|
| 477 |
+
(8, [1, 0])
|
| 478 |
+
assert dup_deflate(dup_from_raw_dict({7: 1}, ZZ), ZZ) == \
|
| 479 |
+
(7, [1, 0])
|
| 480 |
+
assert dup_deflate(dup_from_raw_dict({1: 1}, ZZ), ZZ) == \
|
| 481 |
+
(1, [1, 0])
|
| 482 |
+
|
| 483 |
+
|
| 484 |
+
def test_dmp_deflate():
|
| 485 |
+
assert dmp_deflate([[]], 1, ZZ) == ((1, 1), [[]])
|
| 486 |
+
assert dmp_deflate([[2]], 1, ZZ) == ((1, 1), [[2]])
|
| 487 |
+
|
| 488 |
+
f = [[1, 0, 0], [], [1, 0], [], [1]]
|
| 489 |
+
|
| 490 |
+
assert dmp_deflate(f, 1, ZZ) == ((2, 1), [[1, 0, 0], [1, 0], [1]])
|
| 491 |
+
|
| 492 |
+
|
| 493 |
+
def test_dup_multi_deflate():
|
| 494 |
+
assert dup_multi_deflate(([2],), ZZ) == (1, ([2],))
|
| 495 |
+
assert dup_multi_deflate(([], []), ZZ) == (1, ([], []))
|
| 496 |
+
|
| 497 |
+
assert dup_multi_deflate(([1, 2, 3],), ZZ) == (1, ([1, 2, 3],))
|
| 498 |
+
assert dup_multi_deflate(([1, 0, 2, 0, 3],), ZZ) == (2, ([1, 2, 3],))
|
| 499 |
+
|
| 500 |
+
assert dup_multi_deflate(([1, 0, 2, 0, 3], [2, 0, 0]), ZZ) == \
|
| 501 |
+
(2, ([1, 2, 3], [2, 0]))
|
| 502 |
+
assert dup_multi_deflate(([1, 0, 2, 0, 3], [2, 1, 0]), ZZ) == \
|
| 503 |
+
(1, ([1, 0, 2, 0, 3], [2, 1, 0]))
|
| 504 |
+
|
| 505 |
+
|
| 506 |
+
def test_dmp_multi_deflate():
|
| 507 |
+
assert dmp_multi_deflate(([[]],), 1, ZZ) == \
|
| 508 |
+
((1, 1), ([[]],))
|
| 509 |
+
assert dmp_multi_deflate(([[]], [[]]), 1, ZZ) == \
|
| 510 |
+
((1, 1), ([[]], [[]]))
|
| 511 |
+
|
| 512 |
+
assert dmp_multi_deflate(([[1]], [[]]), 1, ZZ) == \
|
| 513 |
+
((1, 1), ([[1]], [[]]))
|
| 514 |
+
assert dmp_multi_deflate(([[1]], [[2]]), 1, ZZ) == \
|
| 515 |
+
((1, 1), ([[1]], [[2]]))
|
| 516 |
+
assert dmp_multi_deflate(([[1]], [[2, 0]]), 1, ZZ) == \
|
| 517 |
+
((1, 1), ([[1]], [[2, 0]]))
|
| 518 |
+
|
| 519 |
+
assert dmp_multi_deflate(([[2, 0]], [[2, 0]]), 1, ZZ) == \
|
| 520 |
+
((1, 1), ([[2, 0]], [[2, 0]]))
|
| 521 |
+
|
| 522 |
+
assert dmp_multi_deflate(
|
| 523 |
+
([[2]], [[2, 0, 0]]), 1, ZZ) == ((1, 2), ([[2]], [[2, 0]]))
|
| 524 |
+
assert dmp_multi_deflate(
|
| 525 |
+
([[2, 0, 0]], [[2, 0, 0]]), 1, ZZ) == ((1, 2), ([[2, 0]], [[2, 0]]))
|
| 526 |
+
|
| 527 |
+
assert dmp_multi_deflate(([2, 0, 0], [1, 0, 4, 0, 1]), 0, ZZ) == \
|
| 528 |
+
((2,), ([2, 0], [1, 4, 1]))
|
| 529 |
+
|
| 530 |
+
f = [[1, 0, 0], [], [1, 0], [], [1]]
|
| 531 |
+
g = [[1, 0, 1, 0], [], [1]]
|
| 532 |
+
|
| 533 |
+
assert dmp_multi_deflate((f,), 1, ZZ) == \
|
| 534 |
+
((2, 1), ([[1, 0, 0], [1, 0], [1]],))
|
| 535 |
+
|
| 536 |
+
assert dmp_multi_deflate((f, g), 1, ZZ) == \
|
| 537 |
+
((2, 1), ([[1, 0, 0], [1, 0], [1]],
|
| 538 |
+
[[1, 0, 1, 0], [1]]))
|
| 539 |
+
|
| 540 |
+
|
| 541 |
+
def test_dup_inflate():
|
| 542 |
+
assert dup_inflate([], 17, ZZ) == []
|
| 543 |
+
|
| 544 |
+
assert dup_inflate([1, 2, 3], 1, ZZ) == [1, 2, 3]
|
| 545 |
+
assert dup_inflate([1, 2, 3], 2, ZZ) == [1, 0, 2, 0, 3]
|
| 546 |
+
assert dup_inflate([1, 2, 3], 3, ZZ) == [1, 0, 0, 2, 0, 0, 3]
|
| 547 |
+
assert dup_inflate([1, 2, 3], 4, ZZ) == [1, 0, 0, 0, 2, 0, 0, 0, 3]
|
| 548 |
+
|
| 549 |
+
raises(IndexError, lambda: dup_inflate([1, 2, 3], 0, ZZ))
|
| 550 |
+
|
| 551 |
+
|
| 552 |
+
def test_dmp_inflate():
|
| 553 |
+
assert dmp_inflate([1], (3,), 0, ZZ) == [1]
|
| 554 |
+
|
| 555 |
+
assert dmp_inflate([[]], (3, 7), 1, ZZ) == [[]]
|
| 556 |
+
assert dmp_inflate([[2]], (1, 2), 1, ZZ) == [[2]]
|
| 557 |
+
|
| 558 |
+
assert dmp_inflate([[2, 0]], (1, 1), 1, ZZ) == [[2, 0]]
|
| 559 |
+
assert dmp_inflate([[2, 0]], (1, 2), 1, ZZ) == [[2, 0, 0]]
|
| 560 |
+
assert dmp_inflate([[2, 0]], (1, 3), 1, ZZ) == [[2, 0, 0, 0]]
|
| 561 |
+
|
| 562 |
+
assert dmp_inflate([[1, 0, 0], [1], [1, 0]], (2, 1), 1, ZZ) == \
|
| 563 |
+
[[1, 0, 0], [], [1], [], [1, 0]]
|
| 564 |
+
|
| 565 |
+
raises(IndexError, lambda: dmp_inflate([[]], (-3, 7), 1, ZZ))
|
| 566 |
+
|
| 567 |
+
|
| 568 |
+
def test_dmp_exclude():
|
| 569 |
+
assert dmp_exclude([[[]]], 2, ZZ) == ([], [[[]]], 2)
|
| 570 |
+
assert dmp_exclude([[[7]]], 2, ZZ) == ([], [[[7]]], 2)
|
| 571 |
+
|
| 572 |
+
assert dmp_exclude([1, 2, 3], 0, ZZ) == ([], [1, 2, 3], 0)
|
| 573 |
+
assert dmp_exclude([[1], [2, 3]], 1, ZZ) == ([], [[1], [2, 3]], 1)
|
| 574 |
+
|
| 575 |
+
assert dmp_exclude([[1, 2, 3]], 1, ZZ) == ([0], [1, 2, 3], 0)
|
| 576 |
+
assert dmp_exclude([[1], [2], [3]], 1, ZZ) == ([1], [1, 2, 3], 0)
|
| 577 |
+
|
| 578 |
+
assert dmp_exclude([[[1, 2, 3]]], 2, ZZ) == ([0, 1], [1, 2, 3], 0)
|
| 579 |
+
assert dmp_exclude([[[1]], [[2]], [[3]]], 2, ZZ) == ([1, 2], [1, 2, 3], 0)
|
| 580 |
+
|
| 581 |
+
|
| 582 |
+
def test_dmp_include():
|
| 583 |
+
assert dmp_include([1, 2, 3], [], 0, ZZ) == [1, 2, 3]
|
| 584 |
+
|
| 585 |
+
assert dmp_include([1, 2, 3], [0], 0, ZZ) == [[1, 2, 3]]
|
| 586 |
+
assert dmp_include([1, 2, 3], [1], 0, ZZ) == [[1], [2], [3]]
|
| 587 |
+
|
| 588 |
+
assert dmp_include([1, 2, 3], [0, 1], 0, ZZ) == [[[1, 2, 3]]]
|
| 589 |
+
assert dmp_include([1, 2, 3], [1, 2], 0, ZZ) == [[[1]], [[2]], [[3]]]
|
| 590 |
+
|
| 591 |
+
|
| 592 |
+
def test_dmp_inject():
|
| 593 |
+
R, x,y = ring("x,y", ZZ)
|
| 594 |
+
K = R.to_domain()
|
| 595 |
+
|
| 596 |
+
assert dmp_inject([], 0, K) == ([[[]]], 2)
|
| 597 |
+
assert dmp_inject([[]], 1, K) == ([[[[]]]], 3)
|
| 598 |
+
|
| 599 |
+
assert dmp_inject([R(1)], 0, K) == ([[[1]]], 2)
|
| 600 |
+
assert dmp_inject([[R(1)]], 1, K) == ([[[[1]]]], 3)
|
| 601 |
+
|
| 602 |
+
assert dmp_inject([R(1), 2*x + 3*y + 4], 0, K) == ([[[1]], [[2], [3, 4]]], 2)
|
| 603 |
+
|
| 604 |
+
f = [3*x**2 + 7*x*y + 5*y**2, 2*x, R(0), x*y**2 + 11]
|
| 605 |
+
g = [[[3], [7, 0], [5, 0, 0]], [[2], []], [[]], [[1, 0, 0], [11]]]
|
| 606 |
+
|
| 607 |
+
assert dmp_inject(f, 0, K) == (g, 2)
|
| 608 |
+
|
| 609 |
+
|
| 610 |
+
def test_dmp_eject():
|
| 611 |
+
R, x,y = ring("x,y", ZZ)
|
| 612 |
+
K = R.to_domain()
|
| 613 |
+
|
| 614 |
+
assert dmp_eject([[[]]], 2, K) == []
|
| 615 |
+
assert dmp_eject([[[[]]]], 3, K) == [[]]
|
| 616 |
+
|
| 617 |
+
assert dmp_eject([[[1]]], 2, K) == [R(1)]
|
| 618 |
+
assert dmp_eject([[[[1]]]], 3, K) == [[R(1)]]
|
| 619 |
+
|
| 620 |
+
assert dmp_eject([[[1]], [[2], [3, 4]]], 2, K) == [R(1), 2*x + 3*y + 4]
|
| 621 |
+
|
| 622 |
+
f = [3*x**2 + 7*x*y + 5*y**2, 2*x, R(0), x*y**2 + 11]
|
| 623 |
+
g = [[[3], [7, 0], [5, 0, 0]], [[2], []], [[]], [[1, 0, 0], [11]]]
|
| 624 |
+
|
| 625 |
+
assert dmp_eject(g, 2, K) == f
|
| 626 |
+
|
| 627 |
+
|
| 628 |
+
def test_dup_terms_gcd():
|
| 629 |
+
assert dup_terms_gcd([], ZZ) == (0, [])
|
| 630 |
+
assert dup_terms_gcd([1, 0, 1], ZZ) == (0, [1, 0, 1])
|
| 631 |
+
assert dup_terms_gcd([1, 0, 1, 0], ZZ) == (1, [1, 0, 1])
|
| 632 |
+
|
| 633 |
+
|
| 634 |
+
def test_dmp_terms_gcd():
|
| 635 |
+
assert dmp_terms_gcd([[]], 1, ZZ) == ((0, 0), [[]])
|
| 636 |
+
|
| 637 |
+
assert dmp_terms_gcd([1, 0, 1, 0], 0, ZZ) == ((1,), [1, 0, 1])
|
| 638 |
+
assert dmp_terms_gcd([[1], [], [1], []], 1, ZZ) == ((1, 0), [[1], [], [1]])
|
| 639 |
+
|
| 640 |
+
assert dmp_terms_gcd(
|
| 641 |
+
[[1, 0], [], [1]], 1, ZZ) == ((0, 0), [[1, 0], [], [1]])
|
| 642 |
+
assert dmp_terms_gcd(
|
| 643 |
+
[[1, 0], [1, 0, 0], [], []], 1, ZZ) == ((2, 1), [[1], [1, 0]])
|
| 644 |
+
|
| 645 |
+
|
| 646 |
+
def test_dmp_list_terms():
|
| 647 |
+
assert dmp_list_terms([[[]]], 2, ZZ) == [((0, 0, 0), 0)]
|
| 648 |
+
assert dmp_list_terms([[[1]]], 2, ZZ) == [((0, 0, 0), 1)]
|
| 649 |
+
|
| 650 |
+
assert dmp_list_terms([1, 2, 4, 3, 5], 0, ZZ) == \
|
| 651 |
+
[((4,), 1), ((3,), 2), ((2,), 4), ((1,), 3), ((0,), 5)]
|
| 652 |
+
|
| 653 |
+
assert dmp_list_terms([[1], [2, 4], [3, 5, 0]], 1, ZZ) == \
|
| 654 |
+
[((2, 0), 1), ((1, 1), 2), ((1, 0), 4), ((0, 2), 3), ((0, 1), 5)]
|
| 655 |
+
|
| 656 |
+
f = [[2, 0, 0, 0], [1, 0, 0], []]
|
| 657 |
+
|
| 658 |
+
assert dmp_list_terms(f, 1, ZZ, order='lex') == [((2, 3), 2), ((1, 2), 1)]
|
| 659 |
+
assert dmp_list_terms(
|
| 660 |
+
f, 1, ZZ, order='grlex') == [((2, 3), 2), ((1, 2), 1)]
|
| 661 |
+
|
| 662 |
+
f = [[2, 0, 0, 0], [1, 0, 0, 0, 0, 0], []]
|
| 663 |
+
|
| 664 |
+
assert dmp_list_terms(f, 1, ZZ, order='lex') == [((2, 3), 2), ((1, 5), 1)]
|
| 665 |
+
assert dmp_list_terms(
|
| 666 |
+
f, 1, ZZ, order='grlex') == [((1, 5), 1), ((2, 3), 2)]
|
| 667 |
+
|
| 668 |
+
|
| 669 |
+
def test_dmp_apply_pairs():
|
| 670 |
+
h = lambda a, b: a*b
|
| 671 |
+
|
| 672 |
+
assert dmp_apply_pairs([1, 2, 3], [4, 5, 6], h, [], 0, ZZ) == [4, 10, 18]
|
| 673 |
+
|
| 674 |
+
assert dmp_apply_pairs([2, 3], [4, 5, 6], h, [], 0, ZZ) == [10, 18]
|
| 675 |
+
assert dmp_apply_pairs([1, 2, 3], [5, 6], h, [], 0, ZZ) == [10, 18]
|
| 676 |
+
|
| 677 |
+
assert dmp_apply_pairs(
|
| 678 |
+
[[1, 2], [3]], [[4, 5], [6]], h, [], 1, ZZ) == [[4, 10], [18]]
|
| 679 |
+
|
| 680 |
+
assert dmp_apply_pairs(
|
| 681 |
+
[[1, 2], [3]], [[4], [5, 6]], h, [], 1, ZZ) == [[8], [18]]
|
| 682 |
+
assert dmp_apply_pairs(
|
| 683 |
+
[[1], [2, 3]], [[4, 5], [6]], h, [], 1, ZZ) == [[5], [18]]
|
| 684 |
+
|
| 685 |
+
|
| 686 |
+
def test_dup_slice():
|
| 687 |
+
f = [1, 2, 3, 4]
|
| 688 |
+
|
| 689 |
+
assert dup_slice(f, 0, 0, ZZ) == []
|
| 690 |
+
assert dup_slice(f, 0, 1, ZZ) == [4]
|
| 691 |
+
assert dup_slice(f, 0, 2, ZZ) == [3, 4]
|
| 692 |
+
assert dup_slice(f, 0, 3, ZZ) == [2, 3, 4]
|
| 693 |
+
assert dup_slice(f, 0, 4, ZZ) == [1, 2, 3, 4]
|
| 694 |
+
|
| 695 |
+
assert dup_slice(f, 0, 4, ZZ) == f
|
| 696 |
+
assert dup_slice(f, 0, 9, ZZ) == f
|
| 697 |
+
|
| 698 |
+
assert dup_slice(f, 1, 0, ZZ) == []
|
| 699 |
+
assert dup_slice(f, 1, 1, ZZ) == []
|
| 700 |
+
assert dup_slice(f, 1, 2, ZZ) == [3, 0]
|
| 701 |
+
assert dup_slice(f, 1, 3, ZZ) == [2, 3, 0]
|
| 702 |
+
assert dup_slice(f, 1, 4, ZZ) == [1, 2, 3, 0]
|
| 703 |
+
|
| 704 |
+
assert dup_slice([1, 2], 0, 3, ZZ) == [1, 2]
|
| 705 |
+
|
| 706 |
+
g = [1, 0, 0, 2]
|
| 707 |
+
|
| 708 |
+
assert dup_slice(g, 0, 3, ZZ) == [2]
|
| 709 |
+
|
| 710 |
+
|
| 711 |
+
def test_dup_random():
|
| 712 |
+
f = dup_random(0, -10, 10, ZZ)
|
| 713 |
+
|
| 714 |
+
assert dup_degree(f) == 0
|
| 715 |
+
assert all(-10 <= c <= 10 for c in f)
|
| 716 |
+
|
| 717 |
+
f = dup_random(1, -20, 20, ZZ)
|
| 718 |
+
|
| 719 |
+
assert dup_degree(f) == 1
|
| 720 |
+
assert all(-20 <= c <= 20 for c in f)
|
| 721 |
+
|
| 722 |
+
f = dup_random(2, -30, 30, ZZ)
|
| 723 |
+
|
| 724 |
+
assert dup_degree(f) == 2
|
| 725 |
+
assert all(-30 <= c <= 30 for c in f)
|
| 726 |
+
|
| 727 |
+
f = dup_random(3, -40, 40, ZZ)
|
| 728 |
+
|
| 729 |
+
assert dup_degree(f) == 3
|
| 730 |
+
assert all(-40 <= c <= 40 for c in f)
|
.venv/lib/python3.13/site-packages/sympy/polys/tests/test_densetools.py
ADDED
|
@@ -0,0 +1,714 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Tests for dense recursive polynomials' tools. """
|
| 2 |
+
|
| 3 |
+
from sympy.polys.densebasic import (
|
| 4 |
+
dup_normal, dmp_normal,
|
| 5 |
+
dup_from_raw_dict,
|
| 6 |
+
dmp_convert, dmp_swap,
|
| 7 |
+
)
|
| 8 |
+
|
| 9 |
+
from sympy.polys.densearith import dmp_mul_ground
|
| 10 |
+
|
| 11 |
+
from sympy.polys.densetools import (
|
| 12 |
+
dup_clear_denoms, dmp_clear_denoms,
|
| 13 |
+
dup_integrate, dmp_integrate, dmp_integrate_in,
|
| 14 |
+
dup_diff, dmp_diff, dmp_diff_in,
|
| 15 |
+
dup_eval, dmp_eval, dmp_eval_in,
|
| 16 |
+
dmp_eval_tail, dmp_diff_eval_in,
|
| 17 |
+
dup_trunc, dmp_trunc, dmp_ground_trunc,
|
| 18 |
+
dup_monic, dmp_ground_monic,
|
| 19 |
+
dup_content, dmp_ground_content,
|
| 20 |
+
dup_primitive, dmp_ground_primitive,
|
| 21 |
+
dup_extract, dmp_ground_extract,
|
| 22 |
+
dup_real_imag,
|
| 23 |
+
dup_mirror, dup_scale, dup_shift, dmp_shift,
|
| 24 |
+
dup_transform,
|
| 25 |
+
dup_compose, dmp_compose,
|
| 26 |
+
dup_decompose,
|
| 27 |
+
dmp_lift,
|
| 28 |
+
dup_sign_variations,
|
| 29 |
+
dup_revert, dmp_revert,
|
| 30 |
+
)
|
| 31 |
+
from sympy.polys.polyclasses import ANP
|
| 32 |
+
|
| 33 |
+
from sympy.polys.polyerrors import (
|
| 34 |
+
MultivariatePolynomialError,
|
| 35 |
+
ExactQuotientFailed,
|
| 36 |
+
NotReversible,
|
| 37 |
+
DomainError,
|
| 38 |
+
)
|
| 39 |
+
|
| 40 |
+
from sympy.polys.specialpolys import f_polys
|
| 41 |
+
|
| 42 |
+
from sympy.polys.domains import FF, ZZ, QQ, ZZ_I, QQ_I, EX, RR
|
| 43 |
+
from sympy.polys.rings import ring
|
| 44 |
+
|
| 45 |
+
from sympy.core.numbers import I
|
| 46 |
+
from sympy.core.singleton import S
|
| 47 |
+
from sympy.functions.elementary.trigonometric import sin
|
| 48 |
+
|
| 49 |
+
from sympy.abc import x
|
| 50 |
+
from sympy.testing.pytest import raises
|
| 51 |
+
|
| 52 |
+
f_0, f_1, f_2, f_3, f_4, f_5, f_6 = [ f.to_dense() for f in f_polys() ]
|
| 53 |
+
|
| 54 |
+
def test_dup_integrate():
|
| 55 |
+
assert dup_integrate([], 1, QQ) == []
|
| 56 |
+
assert dup_integrate([], 2, QQ) == []
|
| 57 |
+
|
| 58 |
+
assert dup_integrate([QQ(1)], 1, QQ) == [QQ(1), QQ(0)]
|
| 59 |
+
assert dup_integrate([QQ(1)], 2, QQ) == [QQ(1, 2), QQ(0), QQ(0)]
|
| 60 |
+
|
| 61 |
+
assert dup_integrate([QQ(1), QQ(2), QQ(3)], 0, QQ) == \
|
| 62 |
+
[QQ(1), QQ(2), QQ(3)]
|
| 63 |
+
assert dup_integrate([QQ(1), QQ(2), QQ(3)], 1, QQ) == \
|
| 64 |
+
[QQ(1, 3), QQ(1), QQ(3), QQ(0)]
|
| 65 |
+
assert dup_integrate([QQ(1), QQ(2), QQ(3)], 2, QQ) == \
|
| 66 |
+
[QQ(1, 12), QQ(1, 3), QQ(3, 2), QQ(0), QQ(0)]
|
| 67 |
+
assert dup_integrate([QQ(1), QQ(2), QQ(3)], 3, QQ) == \
|
| 68 |
+
[QQ(1, 60), QQ(1, 12), QQ(1, 2), QQ(0), QQ(0), QQ(0)]
|
| 69 |
+
|
| 70 |
+
assert dup_integrate(dup_from_raw_dict({29: QQ(17)}, QQ), 3, QQ) == \
|
| 71 |
+
dup_from_raw_dict({32: QQ(17, 29760)}, QQ)
|
| 72 |
+
|
| 73 |
+
assert dup_integrate(dup_from_raw_dict({29: QQ(17), 5: QQ(1, 2)}, QQ), 3, QQ) == \
|
| 74 |
+
dup_from_raw_dict({32: QQ(17, 29760), 8: QQ(1, 672)}, QQ)
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def test_dmp_integrate():
|
| 78 |
+
assert dmp_integrate([QQ(1)], 2, 0, QQ) == [QQ(1, 2), QQ(0), QQ(0)]
|
| 79 |
+
|
| 80 |
+
assert dmp_integrate([[[]]], 1, 2, QQ) == [[[]]]
|
| 81 |
+
assert dmp_integrate([[[]]], 2, 2, QQ) == [[[]]]
|
| 82 |
+
|
| 83 |
+
assert dmp_integrate([[[QQ(1)]]], 1, 2, QQ) == [[[QQ(1)]], [[]]]
|
| 84 |
+
assert dmp_integrate([[[QQ(1)]]], 2, 2, QQ) == [[[QQ(1, 2)]], [[]], [[]]]
|
| 85 |
+
|
| 86 |
+
assert dmp_integrate([[QQ(1)], [QQ(2)], [QQ(3)]], 0, 1, QQ) == \
|
| 87 |
+
[[QQ(1)], [QQ(2)], [QQ(3)]]
|
| 88 |
+
assert dmp_integrate([[QQ(1)], [QQ(2)], [QQ(3)]], 1, 1, QQ) == \
|
| 89 |
+
[[QQ(1, 3)], [QQ(1)], [QQ(3)], []]
|
| 90 |
+
assert dmp_integrate([[QQ(1)], [QQ(2)], [QQ(3)]], 2, 1, QQ) == \
|
| 91 |
+
[[QQ(1, 12)], [QQ(1, 3)], [QQ(3, 2)], [], []]
|
| 92 |
+
assert dmp_integrate([[QQ(1)], [QQ(2)], [QQ(3)]], 3, 1, QQ) == \
|
| 93 |
+
[[QQ(1, 60)], [QQ(1, 12)], [QQ(1, 2)], [], [], []]
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
def test_dmp_integrate_in():
|
| 97 |
+
f = dmp_convert(f_6, 3, ZZ, QQ)
|
| 98 |
+
|
| 99 |
+
assert dmp_integrate_in(f, 2, 1, 3, QQ) == \
|
| 100 |
+
dmp_swap(
|
| 101 |
+
dmp_integrate(dmp_swap(f, 0, 1, 3, QQ), 2, 3, QQ), 0, 1, 3, QQ)
|
| 102 |
+
assert dmp_integrate_in(f, 3, 1, 3, QQ) == \
|
| 103 |
+
dmp_swap(
|
| 104 |
+
dmp_integrate(dmp_swap(f, 0, 1, 3, QQ), 3, 3, QQ), 0, 1, 3, QQ)
|
| 105 |
+
assert dmp_integrate_in(f, 2, 2, 3, QQ) == \
|
| 106 |
+
dmp_swap(
|
| 107 |
+
dmp_integrate(dmp_swap(f, 0, 2, 3, QQ), 2, 3, QQ), 0, 2, 3, QQ)
|
| 108 |
+
assert dmp_integrate_in(f, 3, 2, 3, QQ) == \
|
| 109 |
+
dmp_swap(
|
| 110 |
+
dmp_integrate(dmp_swap(f, 0, 2, 3, QQ), 3, 3, QQ), 0, 2, 3, QQ)
|
| 111 |
+
|
| 112 |
+
raises(IndexError, lambda: dmp_integrate_in(f, 1, -1, 3, QQ))
|
| 113 |
+
raises(IndexError, lambda: dmp_integrate_in(f, 1, 4, 3, QQ))
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
def test_dup_diff():
|
| 117 |
+
assert dup_diff([], 1, ZZ) == []
|
| 118 |
+
assert dup_diff([7], 1, ZZ) == []
|
| 119 |
+
assert dup_diff([2, 7], 1, ZZ) == [2]
|
| 120 |
+
assert dup_diff([1, 2, 1], 1, ZZ) == [2, 2]
|
| 121 |
+
assert dup_diff([1, 2, 3, 4], 1, ZZ) == [3, 4, 3]
|
| 122 |
+
assert dup_diff([1, -1, 0, 0, 2], 1, ZZ) == [4, -3, 0, 0]
|
| 123 |
+
|
| 124 |
+
f = dup_normal([17, 34, 56, -345, 23, 76, 0, 0, 12, 3, 7], ZZ)
|
| 125 |
+
|
| 126 |
+
assert dup_diff(f, 0, ZZ) == f
|
| 127 |
+
assert dup_diff(f, 1, ZZ) == [170, 306, 448, -2415, 138, 380, 0, 0, 24, 3]
|
| 128 |
+
assert dup_diff(f, 2, ZZ) == dup_diff(dup_diff(f, 1, ZZ), 1, ZZ)
|
| 129 |
+
assert dup_diff(
|
| 130 |
+
f, 3, ZZ) == dup_diff(dup_diff(dup_diff(f, 1, ZZ), 1, ZZ), 1, ZZ)
|
| 131 |
+
|
| 132 |
+
K = FF(3)
|
| 133 |
+
f = dup_normal([17, 34, 56, -345, 23, 76, 0, 0, 12, 3, 7], K)
|
| 134 |
+
|
| 135 |
+
assert dup_diff(f, 1, K) == dup_normal([2, 0, 1, 0, 0, 2, 0, 0, 0, 0], K)
|
| 136 |
+
assert dup_diff(f, 2, K) == dup_normal([1, 0, 0, 2, 0, 0, 0], K)
|
| 137 |
+
assert dup_diff(f, 3, K) == dup_normal([], K)
|
| 138 |
+
|
| 139 |
+
assert dup_diff(f, 0, K) == f
|
| 140 |
+
assert dup_diff(f, 2, K) == dup_diff(dup_diff(f, 1, K), 1, K)
|
| 141 |
+
assert dup_diff(
|
| 142 |
+
f, 3, K) == dup_diff(dup_diff(dup_diff(f, 1, K), 1, K), 1, K)
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
def test_dmp_diff():
|
| 146 |
+
assert dmp_diff([], 1, 0, ZZ) == []
|
| 147 |
+
assert dmp_diff([[]], 1, 1, ZZ) == [[]]
|
| 148 |
+
assert dmp_diff([[[]]], 1, 2, ZZ) == [[[]]]
|
| 149 |
+
|
| 150 |
+
assert dmp_diff([[[1], [2]]], 1, 2, ZZ) == [[[]]]
|
| 151 |
+
|
| 152 |
+
assert dmp_diff([[[1]], [[]]], 1, 2, ZZ) == [[[1]]]
|
| 153 |
+
assert dmp_diff([[[3]], [[1]], [[]]], 1, 2, ZZ) == [[[6]], [[1]]]
|
| 154 |
+
|
| 155 |
+
assert dmp_diff([1, -1, 0, 0, 2], 1, 0, ZZ) == \
|
| 156 |
+
dup_diff([1, -1, 0, 0, 2], 1, ZZ)
|
| 157 |
+
|
| 158 |
+
assert dmp_diff(f_6, 0, 3, ZZ) == f_6
|
| 159 |
+
assert dmp_diff(f_6, 1, 3, ZZ) == [[[[8460]], [[]]],
|
| 160 |
+
[[[135, 0, 0], [], [], [-135, 0, 0]]],
|
| 161 |
+
[[[]]],
|
| 162 |
+
[[[-423]], [[-47]], [[]], [[141], [], [94, 0], []], [[]]]]
|
| 163 |
+
assert dmp_diff(
|
| 164 |
+
f_6, 2, 3, ZZ) == dmp_diff(dmp_diff(f_6, 1, 3, ZZ), 1, 3, ZZ)
|
| 165 |
+
assert dmp_diff(f_6, 3, 3, ZZ) == dmp_diff(
|
| 166 |
+
dmp_diff(dmp_diff(f_6, 1, 3, ZZ), 1, 3, ZZ), 1, 3, ZZ)
|
| 167 |
+
|
| 168 |
+
K = FF(23)
|
| 169 |
+
F_6 = dmp_normal(f_6, 3, K)
|
| 170 |
+
|
| 171 |
+
assert dmp_diff(F_6, 0, 3, K) == F_6
|
| 172 |
+
assert dmp_diff(F_6, 1, 3, K) == dmp_diff(F_6, 1, 3, K)
|
| 173 |
+
assert dmp_diff(F_6, 2, 3, K) == dmp_diff(dmp_diff(F_6, 1, 3, K), 1, 3, K)
|
| 174 |
+
assert dmp_diff(F_6, 3, 3, K) == dmp_diff(
|
| 175 |
+
dmp_diff(dmp_diff(F_6, 1, 3, K), 1, 3, K), 1, 3, K)
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
def test_dmp_diff_in():
|
| 179 |
+
assert dmp_diff_in(f_6, 2, 1, 3, ZZ) == \
|
| 180 |
+
dmp_swap(dmp_diff(dmp_swap(f_6, 0, 1, 3, ZZ), 2, 3, ZZ), 0, 1, 3, ZZ)
|
| 181 |
+
assert dmp_diff_in(f_6, 3, 1, 3, ZZ) == \
|
| 182 |
+
dmp_swap(dmp_diff(dmp_swap(f_6, 0, 1, 3, ZZ), 3, 3, ZZ), 0, 1, 3, ZZ)
|
| 183 |
+
assert dmp_diff_in(f_6, 2, 2, 3, ZZ) == \
|
| 184 |
+
dmp_swap(dmp_diff(dmp_swap(f_6, 0, 2, 3, ZZ), 2, 3, ZZ), 0, 2, 3, ZZ)
|
| 185 |
+
assert dmp_diff_in(f_6, 3, 2, 3, ZZ) == \
|
| 186 |
+
dmp_swap(dmp_diff(dmp_swap(f_6, 0, 2, 3, ZZ), 3, 3, ZZ), 0, 2, 3, ZZ)
|
| 187 |
+
|
| 188 |
+
raises(IndexError, lambda: dmp_diff_in(f_6, 1, -1, 3, ZZ))
|
| 189 |
+
raises(IndexError, lambda: dmp_diff_in(f_6, 1, 4, 3, ZZ))
|
| 190 |
+
|
| 191 |
+
def test_dup_eval():
|
| 192 |
+
assert dup_eval([], 7, ZZ) == 0
|
| 193 |
+
assert dup_eval([1, 2], 0, ZZ) == 2
|
| 194 |
+
assert dup_eval([1, 2, 3], 7, ZZ) == 66
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
def test_dmp_eval():
|
| 198 |
+
assert dmp_eval([], 3, 0, ZZ) == 0
|
| 199 |
+
|
| 200 |
+
assert dmp_eval([[]], 3, 1, ZZ) == []
|
| 201 |
+
assert dmp_eval([[[]]], 3, 2, ZZ) == [[]]
|
| 202 |
+
|
| 203 |
+
assert dmp_eval([[1, 2]], 0, 1, ZZ) == [1, 2]
|
| 204 |
+
|
| 205 |
+
assert dmp_eval([[[1]]], 3, 2, ZZ) == [[1]]
|
| 206 |
+
assert dmp_eval([[[1, 2]]], 3, 2, ZZ) == [[1, 2]]
|
| 207 |
+
|
| 208 |
+
assert dmp_eval([[3, 2], [1, 2]], 3, 1, ZZ) == [10, 8]
|
| 209 |
+
assert dmp_eval([[[3, 2]], [[1, 2]]], 3, 2, ZZ) == [[10, 8]]
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
def test_dmp_eval_in():
|
| 213 |
+
assert dmp_eval_in(
|
| 214 |
+
f_6, -2, 1, 3, ZZ) == dmp_eval(dmp_swap(f_6, 0, 1, 3, ZZ), -2, 3, ZZ)
|
| 215 |
+
assert dmp_eval_in(
|
| 216 |
+
f_6, 7, 1, 3, ZZ) == dmp_eval(dmp_swap(f_6, 0, 1, 3, ZZ), 7, 3, ZZ)
|
| 217 |
+
assert dmp_eval_in(f_6, -2, 2, 3, ZZ) == dmp_swap(
|
| 218 |
+
dmp_eval(dmp_swap(f_6, 0, 2, 3, ZZ), -2, 3, ZZ), 0, 1, 2, ZZ)
|
| 219 |
+
assert dmp_eval_in(f_6, 7, 2, 3, ZZ) == dmp_swap(
|
| 220 |
+
dmp_eval(dmp_swap(f_6, 0, 2, 3, ZZ), 7, 3, ZZ), 0, 1, 2, ZZ)
|
| 221 |
+
|
| 222 |
+
f = [[[int(45)]], [[]], [[]], [[int(-9)], [-1], [], [int(3), int(0), int(10), int(0)]]]
|
| 223 |
+
|
| 224 |
+
assert dmp_eval_in(f, -2, 2, 2, ZZ) == \
|
| 225 |
+
[[45], [], [], [-9, -1, 0, -44]]
|
| 226 |
+
|
| 227 |
+
raises(IndexError, lambda: dmp_eval_in(f_6, ZZ(1), -1, 3, ZZ))
|
| 228 |
+
raises(IndexError, lambda: dmp_eval_in(f_6, ZZ(1), 4, 3, ZZ))
|
| 229 |
+
|
| 230 |
+
|
| 231 |
+
def test_dmp_eval_tail():
|
| 232 |
+
assert dmp_eval_tail([[]], [1], 1, ZZ) == []
|
| 233 |
+
assert dmp_eval_tail([[[]]], [1], 2, ZZ) == [[]]
|
| 234 |
+
assert dmp_eval_tail([[[]]], [1, 2], 2, ZZ) == []
|
| 235 |
+
|
| 236 |
+
assert dmp_eval_tail(f_0, [], 2, ZZ) == f_0
|
| 237 |
+
|
| 238 |
+
assert dmp_eval_tail(f_0, [1, -17, 8], 2, ZZ) == 84496
|
| 239 |
+
assert dmp_eval_tail(f_0, [-17, 8], 2, ZZ) == [-1409, 3, 85902]
|
| 240 |
+
assert dmp_eval_tail(f_0, [8], 2, ZZ) == [[83, 2], [3], [302, 81, 1]]
|
| 241 |
+
|
| 242 |
+
assert dmp_eval_tail(f_1, [-17, 8], 2, ZZ) == [-136, 15699, 9166, -27144]
|
| 243 |
+
|
| 244 |
+
assert dmp_eval_tail(
|
| 245 |
+
f_2, [-12, 3], 2, ZZ) == [-1377, 0, -702, -1224, 0, -624]
|
| 246 |
+
assert dmp_eval_tail(
|
| 247 |
+
f_3, [-12, 3], 2, ZZ) == [144, 82, -5181, -28872, -14868, -540]
|
| 248 |
+
|
| 249 |
+
assert dmp_eval_tail(
|
| 250 |
+
f_4, [25, -1], 2, ZZ) == [152587890625, 9765625, -59605407714843750,
|
| 251 |
+
-3839159765625, -1562475, 9536712644531250, 610349546750, -4, 24414375000, 1562520]
|
| 252 |
+
assert dmp_eval_tail(f_5, [25, -1], 2, ZZ) == [-1, -78, -2028, -17576]
|
| 253 |
+
|
| 254 |
+
assert dmp_eval_tail(f_6, [0, 2, 4], 3, ZZ) == [5040, 0, 0, 4480]
|
| 255 |
+
|
| 256 |
+
|
| 257 |
+
def test_dmp_diff_eval_in():
|
| 258 |
+
assert dmp_diff_eval_in(f_6, 2, 7, 1, 3, ZZ) == \
|
| 259 |
+
dmp_eval(dmp_diff(dmp_swap(f_6, 0, 1, 3, ZZ), 2, 3, ZZ), 7, 3, ZZ)
|
| 260 |
+
|
| 261 |
+
assert dmp_diff_eval_in(f_6, 2, 7, 0, 3, ZZ) == \
|
| 262 |
+
dmp_eval(dmp_diff(f_6, 2, 3, ZZ), 7, 3, ZZ)
|
| 263 |
+
|
| 264 |
+
raises(IndexError, lambda: dmp_diff_eval_in(f_6, 1, ZZ(1), 4, 3, ZZ))
|
| 265 |
+
|
| 266 |
+
|
| 267 |
+
def test_dup_revert():
|
| 268 |
+
f = [-QQ(1, 720), QQ(0), QQ(1, 24), QQ(0), -QQ(1, 2), QQ(0), QQ(1)]
|
| 269 |
+
g = [QQ(61, 720), QQ(0), QQ(5, 24), QQ(0), QQ(1, 2), QQ(0), QQ(1)]
|
| 270 |
+
|
| 271 |
+
assert dup_revert(f, 8, QQ) == g
|
| 272 |
+
|
| 273 |
+
raises(NotReversible, lambda: dup_revert([QQ(1), QQ(0)], 3, QQ))
|
| 274 |
+
|
| 275 |
+
|
| 276 |
+
def test_dmp_revert():
|
| 277 |
+
f = [-QQ(1, 720), QQ(0), QQ(1, 24), QQ(0), -QQ(1, 2), QQ(0), QQ(1)]
|
| 278 |
+
g = [QQ(61, 720), QQ(0), QQ(5, 24), QQ(0), QQ(1, 2), QQ(0), QQ(1)]
|
| 279 |
+
|
| 280 |
+
assert dmp_revert(f, 8, 0, QQ) == g
|
| 281 |
+
|
| 282 |
+
raises(MultivariatePolynomialError, lambda: dmp_revert([[1]], 2, 1, QQ))
|
| 283 |
+
|
| 284 |
+
|
| 285 |
+
def test_dup_trunc():
|
| 286 |
+
assert dup_trunc([1, 2, 3, 4, 5, 6], ZZ(3), ZZ) == [1, -1, 0, 1, -1, 0]
|
| 287 |
+
assert dup_trunc([6, 5, 4, 3, 2, 1], ZZ(3), ZZ) == [-1, 1, 0, -1, 1]
|
| 288 |
+
|
| 289 |
+
R = ZZ_I
|
| 290 |
+
assert dup_trunc([R(3), R(4), R(5)], R(3), R) == [R(1), R(-1)]
|
| 291 |
+
|
| 292 |
+
K = FF(5)
|
| 293 |
+
assert dup_trunc([K(3), K(4), K(5)], K(3), K) == [K(1), K(0)]
|
| 294 |
+
|
| 295 |
+
|
| 296 |
+
def test_dmp_trunc():
|
| 297 |
+
assert dmp_trunc([[]], [1, 2], 2, ZZ) == [[]]
|
| 298 |
+
assert dmp_trunc([[1, 2], [1, 4, 1], [1]], [1, 2], 1, ZZ) == [[-3], [1]]
|
| 299 |
+
|
| 300 |
+
|
| 301 |
+
def test_dmp_ground_trunc():
|
| 302 |
+
assert dmp_ground_trunc(f_0, ZZ(3), 2, ZZ) == \
|
| 303 |
+
dmp_normal(
|
| 304 |
+
[[[1, -1, 0], [-1]], [[]], [[1, -1, 0], [1, -1, 1], [1]]], 2, ZZ)
|
| 305 |
+
|
| 306 |
+
|
| 307 |
+
def test_dup_monic():
|
| 308 |
+
assert dup_monic([3, 6, 9], ZZ) == [1, 2, 3]
|
| 309 |
+
|
| 310 |
+
raises(ExactQuotientFailed, lambda: dup_monic([3, 4, 5], ZZ))
|
| 311 |
+
|
| 312 |
+
assert dup_monic([], QQ) == []
|
| 313 |
+
assert dup_monic([QQ(1)], QQ) == [QQ(1)]
|
| 314 |
+
assert dup_monic([QQ(7), QQ(1), QQ(21)], QQ) == [QQ(1), QQ(1, 7), QQ(3)]
|
| 315 |
+
|
| 316 |
+
|
| 317 |
+
def test_dmp_ground_monic():
|
| 318 |
+
assert dmp_ground_monic([3, 6, 9], 0, ZZ) == [1, 2, 3]
|
| 319 |
+
|
| 320 |
+
assert dmp_ground_monic([[3], [6], [9]], 1, ZZ) == [[1], [2], [3]]
|
| 321 |
+
|
| 322 |
+
raises(
|
| 323 |
+
ExactQuotientFailed, lambda: dmp_ground_monic([[3], [4], [5]], 1, ZZ))
|
| 324 |
+
|
| 325 |
+
assert dmp_ground_monic([[]], 1, QQ) == [[]]
|
| 326 |
+
assert dmp_ground_monic([[QQ(1)]], 1, QQ) == [[QQ(1)]]
|
| 327 |
+
assert dmp_ground_monic(
|
| 328 |
+
[[QQ(7)], [QQ(1)], [QQ(21)]], 1, QQ) == [[QQ(1)], [QQ(1, 7)], [QQ(3)]]
|
| 329 |
+
|
| 330 |
+
|
| 331 |
+
def test_dup_content():
|
| 332 |
+
assert dup_content([], ZZ) == ZZ(0)
|
| 333 |
+
assert dup_content([1], ZZ) == ZZ(1)
|
| 334 |
+
assert dup_content([-1], ZZ) == ZZ(1)
|
| 335 |
+
assert dup_content([1, 1], ZZ) == ZZ(1)
|
| 336 |
+
assert dup_content([2, 2], ZZ) == ZZ(2)
|
| 337 |
+
assert dup_content([1, 2, 1], ZZ) == ZZ(1)
|
| 338 |
+
assert dup_content([2, 4, 2], ZZ) == ZZ(2)
|
| 339 |
+
|
| 340 |
+
assert dup_content([QQ(2, 3), QQ(4, 9)], QQ) == QQ(2, 9)
|
| 341 |
+
assert dup_content([QQ(2, 3), QQ(4, 5)], QQ) == QQ(2, 15)
|
| 342 |
+
|
| 343 |
+
|
| 344 |
+
def test_dmp_ground_content():
|
| 345 |
+
assert dmp_ground_content([[]], 1, ZZ) == ZZ(0)
|
| 346 |
+
assert dmp_ground_content([[]], 1, QQ) == QQ(0)
|
| 347 |
+
assert dmp_ground_content([[1]], 1, ZZ) == ZZ(1)
|
| 348 |
+
assert dmp_ground_content([[-1]], 1, ZZ) == ZZ(1)
|
| 349 |
+
assert dmp_ground_content([[1], [1]], 1, ZZ) == ZZ(1)
|
| 350 |
+
assert dmp_ground_content([[2], [2]], 1, ZZ) == ZZ(2)
|
| 351 |
+
assert dmp_ground_content([[1], [2], [1]], 1, ZZ) == ZZ(1)
|
| 352 |
+
assert dmp_ground_content([[2], [4], [2]], 1, ZZ) == ZZ(2)
|
| 353 |
+
|
| 354 |
+
assert dmp_ground_content([[QQ(2, 3)], [QQ(4, 9)]], 1, QQ) == QQ(2, 9)
|
| 355 |
+
assert dmp_ground_content([[QQ(2, 3)], [QQ(4, 5)]], 1, QQ) == QQ(2, 15)
|
| 356 |
+
|
| 357 |
+
assert dmp_ground_content(f_0, 2, ZZ) == ZZ(1)
|
| 358 |
+
assert dmp_ground_content(
|
| 359 |
+
dmp_mul_ground(f_0, ZZ(2), 2, ZZ), 2, ZZ) == ZZ(2)
|
| 360 |
+
|
| 361 |
+
assert dmp_ground_content(f_1, 2, ZZ) == ZZ(1)
|
| 362 |
+
assert dmp_ground_content(
|
| 363 |
+
dmp_mul_ground(f_1, ZZ(3), 2, ZZ), 2, ZZ) == ZZ(3)
|
| 364 |
+
|
| 365 |
+
assert dmp_ground_content(f_2, 2, ZZ) == ZZ(1)
|
| 366 |
+
assert dmp_ground_content(
|
| 367 |
+
dmp_mul_ground(f_2, ZZ(4), 2, ZZ), 2, ZZ) == ZZ(4)
|
| 368 |
+
|
| 369 |
+
assert dmp_ground_content(f_3, 2, ZZ) == ZZ(1)
|
| 370 |
+
assert dmp_ground_content(
|
| 371 |
+
dmp_mul_ground(f_3, ZZ(5), 2, ZZ), 2, ZZ) == ZZ(5)
|
| 372 |
+
|
| 373 |
+
assert dmp_ground_content(f_4, 2, ZZ) == ZZ(1)
|
| 374 |
+
assert dmp_ground_content(
|
| 375 |
+
dmp_mul_ground(f_4, ZZ(6), 2, ZZ), 2, ZZ) == ZZ(6)
|
| 376 |
+
|
| 377 |
+
assert dmp_ground_content(f_5, 2, ZZ) == ZZ(1)
|
| 378 |
+
assert dmp_ground_content(
|
| 379 |
+
dmp_mul_ground(f_5, ZZ(7), 2, ZZ), 2, ZZ) == ZZ(7)
|
| 380 |
+
|
| 381 |
+
assert dmp_ground_content(f_6, 3, ZZ) == ZZ(1)
|
| 382 |
+
assert dmp_ground_content(
|
| 383 |
+
dmp_mul_ground(f_6, ZZ(8), 3, ZZ), 3, ZZ) == ZZ(8)
|
| 384 |
+
|
| 385 |
+
|
| 386 |
+
def test_dup_primitive():
|
| 387 |
+
assert dup_primitive([], ZZ) == (ZZ(0), [])
|
| 388 |
+
assert dup_primitive([ZZ(1)], ZZ) == (ZZ(1), [ZZ(1)])
|
| 389 |
+
assert dup_primitive([ZZ(1), ZZ(1)], ZZ) == (ZZ(1), [ZZ(1), ZZ(1)])
|
| 390 |
+
assert dup_primitive([ZZ(2), ZZ(2)], ZZ) == (ZZ(2), [ZZ(1), ZZ(1)])
|
| 391 |
+
assert dup_primitive(
|
| 392 |
+
[ZZ(1), ZZ(2), ZZ(1)], ZZ) == (ZZ(1), [ZZ(1), ZZ(2), ZZ(1)])
|
| 393 |
+
assert dup_primitive(
|
| 394 |
+
[ZZ(2), ZZ(4), ZZ(2)], ZZ) == (ZZ(2), [ZZ(1), ZZ(2), ZZ(1)])
|
| 395 |
+
|
| 396 |
+
assert dup_primitive([], QQ) == (QQ(0), [])
|
| 397 |
+
assert dup_primitive([QQ(1)], QQ) == (QQ(1), [QQ(1)])
|
| 398 |
+
assert dup_primitive([QQ(1), QQ(1)], QQ) == (QQ(1), [QQ(1), QQ(1)])
|
| 399 |
+
assert dup_primitive([QQ(2), QQ(2)], QQ) == (QQ(2), [QQ(1), QQ(1)])
|
| 400 |
+
assert dup_primitive(
|
| 401 |
+
[QQ(1), QQ(2), QQ(1)], QQ) == (QQ(1), [QQ(1), QQ(2), QQ(1)])
|
| 402 |
+
assert dup_primitive(
|
| 403 |
+
[QQ(2), QQ(4), QQ(2)], QQ) == (QQ(2), [QQ(1), QQ(2), QQ(1)])
|
| 404 |
+
|
| 405 |
+
assert dup_primitive(
|
| 406 |
+
[QQ(2, 3), QQ(4, 9)], QQ) == (QQ(2, 9), [QQ(3), QQ(2)])
|
| 407 |
+
assert dup_primitive(
|
| 408 |
+
[QQ(2, 3), QQ(4, 5)], QQ) == (QQ(2, 15), [QQ(5), QQ(6)])
|
| 409 |
+
|
| 410 |
+
|
| 411 |
+
def test_dmp_ground_primitive():
|
| 412 |
+
assert dmp_ground_primitive([ZZ(1)], 0, ZZ) == (ZZ(1), [ZZ(1)])
|
| 413 |
+
|
| 414 |
+
assert dmp_ground_primitive([[]], 1, ZZ) == (ZZ(0), [[]])
|
| 415 |
+
|
| 416 |
+
assert dmp_ground_primitive(f_0, 2, ZZ) == (ZZ(1), f_0)
|
| 417 |
+
assert dmp_ground_primitive(
|
| 418 |
+
dmp_mul_ground(f_0, ZZ(2), 2, ZZ), 2, ZZ) == (ZZ(2), f_0)
|
| 419 |
+
|
| 420 |
+
assert dmp_ground_primitive(f_1, 2, ZZ) == (ZZ(1), f_1)
|
| 421 |
+
assert dmp_ground_primitive(
|
| 422 |
+
dmp_mul_ground(f_1, ZZ(3), 2, ZZ), 2, ZZ) == (ZZ(3), f_1)
|
| 423 |
+
|
| 424 |
+
assert dmp_ground_primitive(f_2, 2, ZZ) == (ZZ(1), f_2)
|
| 425 |
+
assert dmp_ground_primitive(
|
| 426 |
+
dmp_mul_ground(f_2, ZZ(4), 2, ZZ), 2, ZZ) == (ZZ(4), f_2)
|
| 427 |
+
|
| 428 |
+
assert dmp_ground_primitive(f_3, 2, ZZ) == (ZZ(1), f_3)
|
| 429 |
+
assert dmp_ground_primitive(
|
| 430 |
+
dmp_mul_ground(f_3, ZZ(5), 2, ZZ), 2, ZZ) == (ZZ(5), f_3)
|
| 431 |
+
|
| 432 |
+
assert dmp_ground_primitive(f_4, 2, ZZ) == (ZZ(1), f_4)
|
| 433 |
+
assert dmp_ground_primitive(
|
| 434 |
+
dmp_mul_ground(f_4, ZZ(6), 2, ZZ), 2, ZZ) == (ZZ(6), f_4)
|
| 435 |
+
|
| 436 |
+
assert dmp_ground_primitive(f_5, 2, ZZ) == (ZZ(1), f_5)
|
| 437 |
+
assert dmp_ground_primitive(
|
| 438 |
+
dmp_mul_ground(f_5, ZZ(7), 2, ZZ), 2, ZZ) == (ZZ(7), f_5)
|
| 439 |
+
|
| 440 |
+
assert dmp_ground_primitive(f_6, 3, ZZ) == (ZZ(1), f_6)
|
| 441 |
+
assert dmp_ground_primitive(
|
| 442 |
+
dmp_mul_ground(f_6, ZZ(8), 3, ZZ), 3, ZZ) == (ZZ(8), f_6)
|
| 443 |
+
|
| 444 |
+
assert dmp_ground_primitive([[ZZ(2)]], 1, ZZ) == (ZZ(2), [[ZZ(1)]])
|
| 445 |
+
assert dmp_ground_primitive([[QQ(2)]], 1, QQ) == (QQ(2), [[QQ(1)]])
|
| 446 |
+
|
| 447 |
+
assert dmp_ground_primitive(
|
| 448 |
+
[[QQ(2, 3)], [QQ(4, 9)]], 1, QQ) == (QQ(2, 9), [[QQ(3)], [QQ(2)]])
|
| 449 |
+
assert dmp_ground_primitive(
|
| 450 |
+
[[QQ(2, 3)], [QQ(4, 5)]], 1, QQ) == (QQ(2, 15), [[QQ(5)], [QQ(6)]])
|
| 451 |
+
|
| 452 |
+
|
| 453 |
+
def test_dup_extract():
|
| 454 |
+
f = dup_normal([2930944, 0, 2198208, 0, 549552, 0, 45796], ZZ)
|
| 455 |
+
g = dup_normal([17585664, 0, 8792832, 0, 1099104, 0], ZZ)
|
| 456 |
+
|
| 457 |
+
F = dup_normal([64, 0, 48, 0, 12, 0, 1], ZZ)
|
| 458 |
+
G = dup_normal([384, 0, 192, 0, 24, 0], ZZ)
|
| 459 |
+
|
| 460 |
+
assert dup_extract(f, g, ZZ) == (45796, F, G)
|
| 461 |
+
|
| 462 |
+
|
| 463 |
+
def test_dmp_ground_extract():
|
| 464 |
+
f = dmp_normal(
|
| 465 |
+
[[2930944], [], [2198208], [], [549552], [], [45796]], 1, ZZ)
|
| 466 |
+
g = dmp_normal([[17585664], [], [8792832], [], [1099104], []], 1, ZZ)
|
| 467 |
+
|
| 468 |
+
F = dmp_normal([[64], [], [48], [], [12], [], [1]], 1, ZZ)
|
| 469 |
+
G = dmp_normal([[384], [], [192], [], [24], []], 1, ZZ)
|
| 470 |
+
|
| 471 |
+
assert dmp_ground_extract(f, g, 1, ZZ) == (45796, F, G)
|
| 472 |
+
|
| 473 |
+
|
| 474 |
+
def test_dup_real_imag():
|
| 475 |
+
assert dup_real_imag([], ZZ) == ([[]], [[]])
|
| 476 |
+
assert dup_real_imag([1], ZZ) == ([[1]], [[]])
|
| 477 |
+
|
| 478 |
+
assert dup_real_imag([1, 1], ZZ) == ([[1], [1]], [[1, 0]])
|
| 479 |
+
assert dup_real_imag([1, 2], ZZ) == ([[1], [2]], [[1, 0]])
|
| 480 |
+
|
| 481 |
+
assert dup_real_imag(
|
| 482 |
+
[1, 2, 3], ZZ) == ([[1], [2], [-1, 0, 3]], [[2, 0], [2, 0]])
|
| 483 |
+
|
| 484 |
+
assert dup_real_imag([ZZ(1), ZZ(0), ZZ(1), ZZ(3)], ZZ) == (
|
| 485 |
+
[[ZZ(1)], [], [ZZ(-3), ZZ(0), ZZ(1)], [ZZ(3)]],
|
| 486 |
+
[[ZZ(3), ZZ(0)], [], [ZZ(-1), ZZ(0), ZZ(1), ZZ(0)]]
|
| 487 |
+
)
|
| 488 |
+
|
| 489 |
+
raises(DomainError, lambda: dup_real_imag([EX(1), EX(2)], EX))
|
| 490 |
+
|
| 491 |
+
|
| 492 |
+
|
| 493 |
+
def test_dup_mirror():
|
| 494 |
+
assert dup_mirror([], ZZ) == []
|
| 495 |
+
assert dup_mirror([1], ZZ) == [1]
|
| 496 |
+
|
| 497 |
+
assert dup_mirror([1, 2, 3, 4, 5], ZZ) == [1, -2, 3, -4, 5]
|
| 498 |
+
assert dup_mirror([1, 2, 3, 4, 5, 6], ZZ) == [-1, 2, -3, 4, -5, 6]
|
| 499 |
+
|
| 500 |
+
|
| 501 |
+
def test_dup_scale():
|
| 502 |
+
assert dup_scale([], -1, ZZ) == []
|
| 503 |
+
assert dup_scale([1], -1, ZZ) == [1]
|
| 504 |
+
|
| 505 |
+
assert dup_scale([1, 2, 3, 4, 5], -1, ZZ) == [1, -2, 3, -4, 5]
|
| 506 |
+
assert dup_scale([1, 2, 3, 4, 5], -7, ZZ) == [2401, -686, 147, -28, 5]
|
| 507 |
+
|
| 508 |
+
|
| 509 |
+
def test_dup_shift():
|
| 510 |
+
assert dup_shift([], 1, ZZ) == []
|
| 511 |
+
assert dup_shift([1], 1, ZZ) == [1]
|
| 512 |
+
|
| 513 |
+
assert dup_shift([1, 2, 3, 4, 5], 1, ZZ) == [1, 6, 15, 20, 15]
|
| 514 |
+
assert dup_shift([1, 2, 3, 4, 5], 7, ZZ) == [1, 30, 339, 1712, 3267]
|
| 515 |
+
|
| 516 |
+
|
| 517 |
+
def test_dmp_shift():
|
| 518 |
+
assert dmp_shift([ZZ(1), ZZ(2)], [ZZ(1)], 0, ZZ) == [ZZ(1), ZZ(3)]
|
| 519 |
+
|
| 520 |
+
assert dmp_shift([[]], [ZZ(1), ZZ(2)], 1, ZZ) == [[]]
|
| 521 |
+
|
| 522 |
+
xy = [[ZZ(1), ZZ(0)], []] # x*y
|
| 523 |
+
x1y2 = [[ZZ(1), ZZ(2)], [ZZ(1), ZZ(2)]] # (x+1)*(y+2)
|
| 524 |
+
assert dmp_shift(xy, [ZZ(1), ZZ(2)], 1, ZZ) == x1y2
|
| 525 |
+
|
| 526 |
+
|
| 527 |
+
def test_dup_transform():
|
| 528 |
+
assert dup_transform([], [], [1, 1], ZZ) == []
|
| 529 |
+
assert dup_transform([], [1], [1, 1], ZZ) == []
|
| 530 |
+
assert dup_transform([], [1, 2], [1, 1], ZZ) == []
|
| 531 |
+
|
| 532 |
+
assert dup_transform([6, -5, 4, -3, 17], [1, -3, 4], [2, -3], ZZ) == \
|
| 533 |
+
[6, -82, 541, -2205, 6277, -12723, 17191, -13603, 4773]
|
| 534 |
+
|
| 535 |
+
|
| 536 |
+
def test_dup_compose():
|
| 537 |
+
assert dup_compose([], [], ZZ) == []
|
| 538 |
+
assert dup_compose([], [1], ZZ) == []
|
| 539 |
+
assert dup_compose([], [1, 2], ZZ) == []
|
| 540 |
+
|
| 541 |
+
assert dup_compose([1], [], ZZ) == [1]
|
| 542 |
+
|
| 543 |
+
assert dup_compose([1, 2, 0], [], ZZ) == []
|
| 544 |
+
assert dup_compose([1, 2, 1], [], ZZ) == [1]
|
| 545 |
+
|
| 546 |
+
assert dup_compose([1, 2, 1], [1], ZZ) == [4]
|
| 547 |
+
assert dup_compose([1, 2, 1], [7], ZZ) == [64]
|
| 548 |
+
|
| 549 |
+
assert dup_compose([1, 2, 1], [1, -1], ZZ) == [1, 0, 0]
|
| 550 |
+
assert dup_compose([1, 2, 1], [1, 1], ZZ) == [1, 4, 4]
|
| 551 |
+
assert dup_compose([1, 2, 1], [1, 2, 1], ZZ) == [1, 4, 8, 8, 4]
|
| 552 |
+
|
| 553 |
+
|
| 554 |
+
def test_dmp_compose():
|
| 555 |
+
assert dmp_compose([1, 2, 1], [1, 2, 1], 0, ZZ) == [1, 4, 8, 8, 4]
|
| 556 |
+
|
| 557 |
+
assert dmp_compose([[[]]], [[[]]], 2, ZZ) == [[[]]]
|
| 558 |
+
assert dmp_compose([[[]]], [[[1]]], 2, ZZ) == [[[]]]
|
| 559 |
+
assert dmp_compose([[[]]], [[[1]], [[2]]], 2, ZZ) == [[[]]]
|
| 560 |
+
|
| 561 |
+
assert dmp_compose([[[1]]], [], 2, ZZ) == [[[1]]]
|
| 562 |
+
|
| 563 |
+
assert dmp_compose([[1], [2], [ ]], [[]], 1, ZZ) == [[]]
|
| 564 |
+
assert dmp_compose([[1], [2], [1]], [[]], 1, ZZ) == [[1]]
|
| 565 |
+
|
| 566 |
+
assert dmp_compose([[1], [2], [1]], [[1]], 1, ZZ) == [[4]]
|
| 567 |
+
assert dmp_compose([[1], [2], [1]], [[7]], 1, ZZ) == [[64]]
|
| 568 |
+
|
| 569 |
+
assert dmp_compose([[1], [2], [1]], [[1], [-1]], 1, ZZ) == [[1], [ ], [ ]]
|
| 570 |
+
assert dmp_compose([[1], [2], [1]], [[1], [ 1]], 1, ZZ) == [[1], [4], [4]]
|
| 571 |
+
|
| 572 |
+
assert dmp_compose(
|
| 573 |
+
[[1], [2], [1]], [[1], [2], [1]], 1, ZZ) == [[1], [4], [8], [8], [4]]
|
| 574 |
+
|
| 575 |
+
|
| 576 |
+
def test_dup_decompose():
|
| 577 |
+
assert dup_decompose([1], ZZ) == [[1]]
|
| 578 |
+
|
| 579 |
+
assert dup_decompose([1, 0], ZZ) == [[1, 0]]
|
| 580 |
+
assert dup_decompose([1, 0, 0, 0], ZZ) == [[1, 0, 0, 0]]
|
| 581 |
+
|
| 582 |
+
assert dup_decompose([1, 0, 0, 0, 0], ZZ) == [[1, 0, 0], [1, 0, 0]]
|
| 583 |
+
assert dup_decompose(
|
| 584 |
+
[1, 0, 0, 0, 0, 0, 0], ZZ) == [[1, 0, 0, 0], [1, 0, 0]]
|
| 585 |
+
|
| 586 |
+
assert dup_decompose([7, 0, 0, 0, 1], ZZ) == [[7, 0, 1], [1, 0, 0]]
|
| 587 |
+
assert dup_decompose([4, 0, 3, 0, 2], ZZ) == [[4, 3, 2], [1, 0, 0]]
|
| 588 |
+
|
| 589 |
+
f = [1, 0, 20, 0, 150, 0, 500, 0, 625, -2, 0, -10, 9]
|
| 590 |
+
|
| 591 |
+
assert dup_decompose(f, ZZ) == [[1, 0, 0, -2, 9], [1, 0, 5, 0]]
|
| 592 |
+
|
| 593 |
+
f = [2, 0, 40, 0, 300, 0, 1000, 0, 1250, -4, 0, -20, 18]
|
| 594 |
+
|
| 595 |
+
assert dup_decompose(f, ZZ) == [[2, 0, 0, -4, 18], [1, 0, 5, 0]]
|
| 596 |
+
|
| 597 |
+
f = [1, 0, 20, -8, 150, -120, 524, -600, 865, -1034, 600, -170, 29]
|
| 598 |
+
|
| 599 |
+
assert dup_decompose(f, ZZ) == [[1, -8, 24, -34, 29], [1, 0, 5, 0]]
|
| 600 |
+
|
| 601 |
+
R, t = ring("t", ZZ)
|
| 602 |
+
f = [6*t**2 - 42,
|
| 603 |
+
48*t**2 + 96,
|
| 604 |
+
144*t**2 + 648*t + 288,
|
| 605 |
+
624*t**2 + 864*t + 384,
|
| 606 |
+
108*t**3 + 312*t**2 + 432*t + 192]
|
| 607 |
+
|
| 608 |
+
assert dup_decompose(f, R.to_domain()) == [f]
|
| 609 |
+
|
| 610 |
+
|
| 611 |
+
def test_dmp_lift():
|
| 612 |
+
q = [QQ(1, 1), QQ(0, 1), QQ(1, 1)]
|
| 613 |
+
|
| 614 |
+
f_a = [ANP([QQ(1, 1)], q, QQ), ANP([], q, QQ), ANP([], q, QQ),
|
| 615 |
+
ANP([QQ(1, 1), QQ(0, 1)], q, QQ), ANP([QQ(17, 1), QQ(0, 1)], q, QQ)]
|
| 616 |
+
|
| 617 |
+
f_lift = QQ.map([1, 0, 0, 0, 0, 0, 1, 34, 289])
|
| 618 |
+
|
| 619 |
+
assert dmp_lift(f_a, 0, QQ.algebraic_field(I)) == f_lift
|
| 620 |
+
|
| 621 |
+
f_g = [QQ_I(1), QQ_I(0), QQ_I(0), QQ_I(0, 1), QQ_I(0, 17)]
|
| 622 |
+
|
| 623 |
+
assert dmp_lift(f_g, 0, QQ_I) == f_lift
|
| 624 |
+
|
| 625 |
+
raises(DomainError, lambda: dmp_lift([EX(1), EX(2)], 0, EX))
|
| 626 |
+
|
| 627 |
+
|
| 628 |
+
def test_dup_sign_variations():
|
| 629 |
+
assert dup_sign_variations([], ZZ) == 0
|
| 630 |
+
assert dup_sign_variations([1, 0], ZZ) == 0
|
| 631 |
+
assert dup_sign_variations([1, 0, 2], ZZ) == 0
|
| 632 |
+
assert dup_sign_variations([1, 0, 3, 0], ZZ) == 0
|
| 633 |
+
assert dup_sign_variations([1, 0, 4, 0, 5], ZZ) == 0
|
| 634 |
+
|
| 635 |
+
assert dup_sign_variations([-1, 0, 2], ZZ) == 1
|
| 636 |
+
assert dup_sign_variations([-1, 0, 3, 0], ZZ) == 1
|
| 637 |
+
assert dup_sign_variations([-1, 0, 4, 0, 5], ZZ) == 1
|
| 638 |
+
|
| 639 |
+
assert dup_sign_variations([-1, -4, -5], ZZ) == 0
|
| 640 |
+
assert dup_sign_variations([ 1, -4, -5], ZZ) == 1
|
| 641 |
+
assert dup_sign_variations([ 1, 4, -5], ZZ) == 1
|
| 642 |
+
assert dup_sign_variations([ 1, -4, 5], ZZ) == 2
|
| 643 |
+
assert dup_sign_variations([-1, 4, -5], ZZ) == 2
|
| 644 |
+
assert dup_sign_variations([-1, 4, 5], ZZ) == 1
|
| 645 |
+
assert dup_sign_variations([-1, -4, 5], ZZ) == 1
|
| 646 |
+
assert dup_sign_variations([ 1, 4, 5], ZZ) == 0
|
| 647 |
+
|
| 648 |
+
assert dup_sign_variations([-1, 0, -4, 0, -5], ZZ) == 0
|
| 649 |
+
assert dup_sign_variations([ 1, 0, -4, 0, -5], ZZ) == 1
|
| 650 |
+
assert dup_sign_variations([ 1, 0, 4, 0, -5], ZZ) == 1
|
| 651 |
+
assert dup_sign_variations([ 1, 0, -4, 0, 5], ZZ) == 2
|
| 652 |
+
assert dup_sign_variations([-1, 0, 4, 0, -5], ZZ) == 2
|
| 653 |
+
assert dup_sign_variations([-1, 0, 4, 0, 5], ZZ) == 1
|
| 654 |
+
assert dup_sign_variations([-1, 0, -4, 0, 5], ZZ) == 1
|
| 655 |
+
assert dup_sign_variations([ 1, 0, 4, 0, 5], ZZ) == 0
|
| 656 |
+
|
| 657 |
+
|
| 658 |
+
def test_dup_clear_denoms():
|
| 659 |
+
assert dup_clear_denoms([], QQ, ZZ) == (ZZ(1), [])
|
| 660 |
+
|
| 661 |
+
assert dup_clear_denoms([QQ(1)], QQ, ZZ) == (ZZ(1), [QQ(1)])
|
| 662 |
+
assert dup_clear_denoms([QQ(7)], QQ, ZZ) == (ZZ(1), [QQ(7)])
|
| 663 |
+
|
| 664 |
+
assert dup_clear_denoms([QQ(7, 3)], QQ) == (ZZ(3), [QQ(7)])
|
| 665 |
+
assert dup_clear_denoms([QQ(7, 3)], QQ, ZZ) == (ZZ(3), [QQ(7)])
|
| 666 |
+
|
| 667 |
+
assert dup_clear_denoms(
|
| 668 |
+
[QQ(3), QQ(1), QQ(0)], QQ, ZZ) == (ZZ(1), [QQ(3), QQ(1), QQ(0)])
|
| 669 |
+
assert dup_clear_denoms(
|
| 670 |
+
[QQ(1), QQ(1, 2), QQ(0)], QQ, ZZ) == (ZZ(2), [QQ(2), QQ(1), QQ(0)])
|
| 671 |
+
|
| 672 |
+
assert dup_clear_denoms([QQ(3), QQ(
|
| 673 |
+
1), QQ(0)], QQ, ZZ, convert=True) == (ZZ(1), [ZZ(3), ZZ(1), ZZ(0)])
|
| 674 |
+
assert dup_clear_denoms([QQ(1), QQ(
|
| 675 |
+
1, 2), QQ(0)], QQ, ZZ, convert=True) == (ZZ(2), [ZZ(2), ZZ(1), ZZ(0)])
|
| 676 |
+
|
| 677 |
+
assert dup_clear_denoms(
|
| 678 |
+
[EX(S(3)/2), EX(S(9)/4)], EX) == (EX(4), [EX(6), EX(9)])
|
| 679 |
+
|
| 680 |
+
assert dup_clear_denoms([EX(7)], EX) == (EX(1), [EX(7)])
|
| 681 |
+
assert dup_clear_denoms([EX(sin(x)/x), EX(0)], EX) == (EX(x), [EX(sin(x)), EX(0)])
|
| 682 |
+
|
| 683 |
+
F = RR.frac_field(x)
|
| 684 |
+
result = dup_clear_denoms([F(8.48717/(8.0089*x + 2.83)), F(0.0)], F)
|
| 685 |
+
assert str(result) == "(x + 0.353356890459364, [1.05971731448763, 0.0])"
|
| 686 |
+
|
| 687 |
+
def test_dmp_clear_denoms():
|
| 688 |
+
assert dmp_clear_denoms([[]], 1, QQ, ZZ) == (ZZ(1), [[]])
|
| 689 |
+
|
| 690 |
+
assert dmp_clear_denoms([[QQ(1)]], 1, QQ, ZZ) == (ZZ(1), [[QQ(1)]])
|
| 691 |
+
assert dmp_clear_denoms([[QQ(7)]], 1, QQ, ZZ) == (ZZ(1), [[QQ(7)]])
|
| 692 |
+
|
| 693 |
+
assert dmp_clear_denoms([[QQ(7, 3)]], 1, QQ) == (ZZ(3), [[QQ(7)]])
|
| 694 |
+
assert dmp_clear_denoms([[QQ(7, 3)]], 1, QQ, ZZ) == (ZZ(3), [[QQ(7)]])
|
| 695 |
+
|
| 696 |
+
assert dmp_clear_denoms(
|
| 697 |
+
[[QQ(3)], [QQ(1)], []], 1, QQ, ZZ) == (ZZ(1), [[QQ(3)], [QQ(1)], []])
|
| 698 |
+
assert dmp_clear_denoms([[QQ(
|
| 699 |
+
1)], [QQ(1, 2)], []], 1, QQ, ZZ) == (ZZ(2), [[QQ(2)], [QQ(1)], []])
|
| 700 |
+
|
| 701 |
+
assert dmp_clear_denoms([QQ(3), QQ(
|
| 702 |
+
1), QQ(0)], 0, QQ, ZZ, convert=True) == (ZZ(1), [ZZ(3), ZZ(1), ZZ(0)])
|
| 703 |
+
assert dmp_clear_denoms([QQ(1), QQ(1, 2), QQ(
|
| 704 |
+
0)], 0, QQ, ZZ, convert=True) == (ZZ(2), [ZZ(2), ZZ(1), ZZ(0)])
|
| 705 |
+
|
| 706 |
+
assert dmp_clear_denoms([[QQ(3)], [QQ(
|
| 707 |
+
1)], []], 1, QQ, ZZ, convert=True) == (ZZ(1), [[QQ(3)], [QQ(1)], []])
|
| 708 |
+
assert dmp_clear_denoms([[QQ(1)], [QQ(1, 2)], []], 1, QQ, ZZ,
|
| 709 |
+
convert=True) == (ZZ(2), [[QQ(2)], [QQ(1)], []])
|
| 710 |
+
|
| 711 |
+
assert dmp_clear_denoms(
|
| 712 |
+
[[EX(S(3)/2)], [EX(S(9)/4)]], 1, EX) == (EX(4), [[EX(6)], [EX(9)]])
|
| 713 |
+
assert dmp_clear_denoms([[EX(7)]], 1, EX) == (EX(1), [[EX(7)]])
|
| 714 |
+
assert dmp_clear_denoms([[EX(sin(x)/x), EX(0)]], 1, EX) == (EX(x), [[EX(sin(x)), EX(0)]])
|
.venv/lib/python3.13/site-packages/sympy/polys/tests/test_dispersion.py
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.core import Symbol, S, oo
|
| 2 |
+
from sympy.functions.elementary.miscellaneous import sqrt
|
| 3 |
+
from sympy.polys import poly
|
| 4 |
+
from sympy.polys.dispersion import dispersion, dispersionset
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
def test_dispersion():
|
| 8 |
+
x = Symbol("x")
|
| 9 |
+
a = Symbol("a")
|
| 10 |
+
|
| 11 |
+
fp = poly(S.Zero, x)
|
| 12 |
+
assert sorted(dispersionset(fp)) == [0]
|
| 13 |
+
|
| 14 |
+
fp = poly(S(2), x)
|
| 15 |
+
assert sorted(dispersionset(fp)) == [0]
|
| 16 |
+
|
| 17 |
+
fp = poly(x + 1, x)
|
| 18 |
+
assert sorted(dispersionset(fp)) == [0]
|
| 19 |
+
assert dispersion(fp) == 0
|
| 20 |
+
|
| 21 |
+
fp = poly((x + 1)*(x + 2), x)
|
| 22 |
+
assert sorted(dispersionset(fp)) == [0, 1]
|
| 23 |
+
assert dispersion(fp) == 1
|
| 24 |
+
|
| 25 |
+
fp = poly(x*(x + 3), x)
|
| 26 |
+
assert sorted(dispersionset(fp)) == [0, 3]
|
| 27 |
+
assert dispersion(fp) == 3
|
| 28 |
+
|
| 29 |
+
fp = poly((x - 3)*(x + 3), x)
|
| 30 |
+
assert sorted(dispersionset(fp)) == [0, 6]
|
| 31 |
+
assert dispersion(fp) == 6
|
| 32 |
+
|
| 33 |
+
fp = poly(x**4 - 3*x**2 + 1, x)
|
| 34 |
+
gp = fp.shift(-3)
|
| 35 |
+
assert sorted(dispersionset(fp, gp)) == [2, 3, 4]
|
| 36 |
+
assert dispersion(fp, gp) == 4
|
| 37 |
+
assert sorted(dispersionset(gp, fp)) == []
|
| 38 |
+
assert dispersion(gp, fp) is -oo
|
| 39 |
+
|
| 40 |
+
fp = poly(x*(3*x**2+a)*(x-2536)*(x**3+a), x)
|
| 41 |
+
gp = fp.as_expr().subs(x, x-345).as_poly(x)
|
| 42 |
+
assert sorted(dispersionset(fp, gp)) == [345, 2881]
|
| 43 |
+
assert sorted(dispersionset(gp, fp)) == [2191]
|
| 44 |
+
|
| 45 |
+
gp = poly((x-2)**2*(x-3)**3*(x-5)**3, x)
|
| 46 |
+
assert sorted(dispersionset(gp)) == [0, 1, 2, 3]
|
| 47 |
+
assert sorted(dispersionset(gp, (gp+4)**2)) == [1, 2]
|
| 48 |
+
|
| 49 |
+
fp = poly(x*(x+2)*(x-1), x)
|
| 50 |
+
assert sorted(dispersionset(fp)) == [0, 1, 2, 3]
|
| 51 |
+
|
| 52 |
+
fp = poly(x**2 + sqrt(5)*x - 1, x, domain='QQ<sqrt(5)>')
|
| 53 |
+
gp = poly(x**2 + (2 + sqrt(5))*x + sqrt(5), x, domain='QQ<sqrt(5)>')
|
| 54 |
+
assert sorted(dispersionset(fp, gp)) == [2]
|
| 55 |
+
assert sorted(dispersionset(gp, fp)) == [1, 4]
|
| 56 |
+
|
| 57 |
+
# There are some difficulties if we compute over Z[a]
|
| 58 |
+
# and alpha happens to lie in Z[a] instead of simply Z.
|
| 59 |
+
# Hence we can not decide if alpha is indeed integral
|
| 60 |
+
# in general.
|
| 61 |
+
|
| 62 |
+
fp = poly(4*x**4 + (4*a + 8)*x**3 + (a**2 + 6*a + 4)*x**2 + (a**2 + 2*a)*x, x)
|
| 63 |
+
assert sorted(dispersionset(fp)) == [0, 1]
|
| 64 |
+
|
| 65 |
+
# For any specific value of a, the dispersion is 3*a
|
| 66 |
+
# but the algorithm can not find this in general.
|
| 67 |
+
# This is the point where the resultant based Ansatz
|
| 68 |
+
# is superior to the current one.
|
| 69 |
+
fp = poly(a**2*x**3 + (a**3 + a**2 + a + 1)*x, x)
|
| 70 |
+
gp = fp.as_expr().subs(x, x - 3*a).as_poly(x)
|
| 71 |
+
assert sorted(dispersionset(fp, gp)) == []
|
| 72 |
+
|
| 73 |
+
fpa = fp.as_expr().subs(a, 2).as_poly(x)
|
| 74 |
+
gpa = gp.as_expr().subs(a, 2).as_poly(x)
|
| 75 |
+
assert sorted(dispersionset(fpa, gpa)) == [6]
|
| 76 |
+
|
| 77 |
+
# Work with Expr instead of Poly
|
| 78 |
+
f = (x + 1)*(x + 2)
|
| 79 |
+
assert sorted(dispersionset(f)) == [0, 1]
|
| 80 |
+
assert dispersion(f) == 1
|
| 81 |
+
|
| 82 |
+
f = x**4 - 3*x**2 + 1
|
| 83 |
+
g = x**4 - 12*x**3 + 51*x**2 - 90*x + 55
|
| 84 |
+
assert sorted(dispersionset(f, g)) == [2, 3, 4]
|
| 85 |
+
assert dispersion(f, g) == 4
|
| 86 |
+
|
| 87 |
+
# Work with Expr and specify a generator
|
| 88 |
+
f = (x + 1)*(x + 2)
|
| 89 |
+
assert sorted(dispersionset(f, None, x)) == [0, 1]
|
| 90 |
+
assert dispersion(f, None, x) == 1
|
| 91 |
+
|
| 92 |
+
f = x**4 - 3*x**2 + 1
|
| 93 |
+
g = x**4 - 12*x**3 + 51*x**2 - 90*x + 55
|
| 94 |
+
assert sorted(dispersionset(f, g, x)) == [2, 3, 4]
|
| 95 |
+
assert dispersion(f, g, x) == 4
|
.venv/lib/python3.13/site-packages/sympy/polys/tests/test_distributedmodules.py
ADDED
|
@@ -0,0 +1,208 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Tests for sparse distributed modules. """
|
| 2 |
+
|
| 3 |
+
from sympy.polys.distributedmodules import (
|
| 4 |
+
sdm_monomial_mul, sdm_monomial_deg, sdm_monomial_divides,
|
| 5 |
+
sdm_add, sdm_LM, sdm_LT, sdm_mul_term, sdm_zero, sdm_deg,
|
| 6 |
+
sdm_LC, sdm_from_dict,
|
| 7 |
+
sdm_spoly, sdm_ecart, sdm_nf_mora, sdm_groebner,
|
| 8 |
+
sdm_from_vector, sdm_to_vector, sdm_monomial_lcm
|
| 9 |
+
)
|
| 10 |
+
|
| 11 |
+
from sympy.polys.orderings import lex, grlex, InverseOrder
|
| 12 |
+
from sympy.polys.domains import QQ
|
| 13 |
+
|
| 14 |
+
from sympy.abc import x, y, z
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def test_sdm_monomial_mul():
|
| 18 |
+
assert sdm_monomial_mul((1, 1, 0), (1, 3)) == (1, 2, 3)
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
def test_sdm_monomial_deg():
|
| 22 |
+
assert sdm_monomial_deg((5, 2, 1)) == 3
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def test_sdm_monomial_lcm():
|
| 26 |
+
assert sdm_monomial_lcm((1, 2, 3), (1, 5, 0)) == (1, 5, 3)
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def test_sdm_monomial_divides():
|
| 30 |
+
assert sdm_monomial_divides((1, 0, 0), (1, 0, 0)) is True
|
| 31 |
+
assert sdm_monomial_divides((1, 0, 0), (1, 2, 1)) is True
|
| 32 |
+
assert sdm_monomial_divides((5, 1, 1), (5, 2, 1)) is True
|
| 33 |
+
|
| 34 |
+
assert sdm_monomial_divides((1, 0, 0), (2, 0, 0)) is False
|
| 35 |
+
assert sdm_monomial_divides((1, 1, 0), (1, 0, 0)) is False
|
| 36 |
+
assert sdm_monomial_divides((5, 1, 2), (5, 0, 1)) is False
|
| 37 |
+
|
| 38 |
+
|
| 39 |
+
def test_sdm_LC():
|
| 40 |
+
assert sdm_LC([((1, 2, 3), QQ(5))], QQ) == QQ(5)
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def test_sdm_from_dict():
|
| 44 |
+
dic = {(1, 2, 1, 1): QQ(1), (1, 1, 2, 1): QQ(1), (1, 0, 2, 1): QQ(1),
|
| 45 |
+
(1, 0, 0, 3): QQ(1), (1, 1, 1, 0): QQ(1)}
|
| 46 |
+
assert sdm_from_dict(dic, grlex) == \
|
| 47 |
+
[((1, 2, 1, 1), QQ(1)), ((1, 1, 2, 1), QQ(1)),
|
| 48 |
+
((1, 0, 2, 1), QQ(1)), ((1, 0, 0, 3), QQ(1)), ((1, 1, 1, 0), QQ(1))]
|
| 49 |
+
|
| 50 |
+
# TODO test to_dict?
|
| 51 |
+
|
| 52 |
+
|
| 53 |
+
def test_sdm_add():
|
| 54 |
+
assert sdm_add([((1, 1, 1), QQ(1))], [((2, 0, 0), QQ(1))], lex, QQ) == \
|
| 55 |
+
[((2, 0, 0), QQ(1)), ((1, 1, 1), QQ(1))]
|
| 56 |
+
assert sdm_add([((1, 1, 1), QQ(1))], [((1, 1, 1), QQ(-1))], lex, QQ) == []
|
| 57 |
+
assert sdm_add([((1, 0, 0), QQ(1))], [((1, 0, 0), QQ(2))], lex, QQ) == \
|
| 58 |
+
[((1, 0, 0), QQ(3))]
|
| 59 |
+
assert sdm_add([((1, 0, 1), QQ(1))], [((1, 1, 0), QQ(1))], lex, QQ) == \
|
| 60 |
+
[((1, 1, 0), QQ(1)), ((1, 0, 1), QQ(1))]
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
def test_sdm_LM():
|
| 64 |
+
dic = {(1, 2, 3): QQ(1), (4, 0, 0): QQ(1), (4, 0, 1): QQ(1)}
|
| 65 |
+
assert sdm_LM(sdm_from_dict(dic, lex)) == (4, 0, 1)
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def test_sdm_LT():
|
| 69 |
+
dic = {(1, 2, 3): QQ(1), (4, 0, 0): QQ(2), (4, 0, 1): QQ(3)}
|
| 70 |
+
assert sdm_LT(sdm_from_dict(dic, lex)) == ((4, 0, 1), QQ(3))
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def test_sdm_mul_term():
|
| 74 |
+
assert sdm_mul_term([((1, 0, 0), QQ(1))], ((0, 0), QQ(0)), lex, QQ) == []
|
| 75 |
+
assert sdm_mul_term([], ((1, 0), QQ(1)), lex, QQ) == []
|
| 76 |
+
assert sdm_mul_term([((1, 0, 0), QQ(1))], ((1, 0), QQ(1)), lex, QQ) == \
|
| 77 |
+
[((1, 1, 0), QQ(1))]
|
| 78 |
+
f = [((2, 0, 1), QQ(4)), ((1, 1, 0), QQ(3))]
|
| 79 |
+
assert sdm_mul_term(f, ((1, 1), QQ(2)), lex, QQ) == \
|
| 80 |
+
[((2, 1, 2), QQ(8)), ((1, 2, 1), QQ(6))]
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
def test_sdm_zero():
|
| 84 |
+
assert sdm_zero() == []
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
def test_sdm_deg():
|
| 88 |
+
assert sdm_deg([((1, 2, 3), 1), ((10, 0, 1), 1), ((2, 3, 4), 4)]) == 7
|
| 89 |
+
|
| 90 |
+
|
| 91 |
+
def test_sdm_spoly():
|
| 92 |
+
f = [((2, 1, 1), QQ(1)), ((1, 0, 1), QQ(1))]
|
| 93 |
+
g = [((2, 3, 0), QQ(1))]
|
| 94 |
+
h = [((1, 2, 3), QQ(1))]
|
| 95 |
+
assert sdm_spoly(f, h, lex, QQ) == []
|
| 96 |
+
assert sdm_spoly(f, g, lex, QQ) == [((1, 2, 1), QQ(1))]
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
def test_sdm_ecart():
|
| 100 |
+
assert sdm_ecart([((1, 2, 3), 1), ((1, 0, 1), 1)]) == 0
|
| 101 |
+
assert sdm_ecart([((2, 2, 1), 1), ((1, 5, 1), 1)]) == 3
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
def test_sdm_nf_mora():
|
| 105 |
+
f = sdm_from_dict({(1, 2, 1, 1): QQ(1), (1, 1, 2, 1): QQ(1),
|
| 106 |
+
(1, 0, 2, 1): QQ(1), (1, 0, 0, 3): QQ(1), (1, 1, 1, 0): QQ(1)},
|
| 107 |
+
grlex)
|
| 108 |
+
f1 = sdm_from_dict({(1, 1, 1, 0): QQ(1), (1, 0, 2, 0): QQ(1),
|
| 109 |
+
(1, 0, 0, 0): QQ(-1)}, grlex)
|
| 110 |
+
f2 = sdm_from_dict({(1, 1, 1, 0): QQ(1)}, grlex)
|
| 111 |
+
(id0, id1, id2) = [sdm_from_dict({(i, 0, 0, 0): QQ(1)}, grlex)
|
| 112 |
+
for i in range(3)]
|
| 113 |
+
|
| 114 |
+
assert sdm_nf_mora(f, [f1, f2], grlex, QQ, phantom=(id0, [id1, id2])) == \
|
| 115 |
+
([((1, 0, 2, 1), QQ(1)), ((1, 0, 0, 3), QQ(1)), ((1, 1, 1, 0), QQ(1)),
|
| 116 |
+
((1, 1, 0, 1), QQ(1))],
|
| 117 |
+
[((1, 1, 0, 1), QQ(-1)), ((0, 0, 0, 0), QQ(1))])
|
| 118 |
+
assert sdm_nf_mora(f, [f2, f1], grlex, QQ, phantom=(id0, [id2, id1])) == \
|
| 119 |
+
([((1, 0, 2, 1), QQ(1)), ((1, 0, 0, 3), QQ(1)), ((1, 1, 1, 0), QQ(1))],
|
| 120 |
+
[((2, 1, 0, 1), QQ(-1)), ((2, 0, 1, 1), QQ(-1)), ((0, 0, 0, 0), QQ(1))])
|
| 121 |
+
|
| 122 |
+
f = sdm_from_vector([x*z, y**2 + y*z - z, y], lex, QQ, gens=[x, y, z])
|
| 123 |
+
f1 = sdm_from_vector([x, y, 1], lex, QQ, gens=[x, y, z])
|
| 124 |
+
f2 = sdm_from_vector([x*y, z, z**2], lex, QQ, gens=[x, y, z])
|
| 125 |
+
assert sdm_nf_mora(f, [f1, f2], lex, QQ) == \
|
| 126 |
+
sdm_nf_mora(f, [f2, f1], lex, QQ) == \
|
| 127 |
+
[((1, 0, 1, 1), QQ(1)), ((1, 0, 0, 1), QQ(-1)), ((0, 1, 1, 0), QQ(-1)),
|
| 128 |
+
((0, 1, 0, 1), QQ(1))]
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
def test_conversion():
|
| 132 |
+
f = [x**2 + y**2, 2*z]
|
| 133 |
+
g = [((1, 0, 0, 1), QQ(2)), ((0, 2, 0, 0), QQ(1)), ((0, 0, 2, 0), QQ(1))]
|
| 134 |
+
assert sdm_to_vector(g, [x, y, z], QQ) == f
|
| 135 |
+
assert sdm_from_vector(f, lex, QQ) == g
|
| 136 |
+
assert sdm_from_vector(
|
| 137 |
+
[x, 1], lex, QQ) == [((1, 0), QQ(1)), ((0, 1), QQ(1))]
|
| 138 |
+
assert sdm_to_vector([((1, 1, 0, 0), 1)], [x, y, z], QQ, n=3) == [0, x, 0]
|
| 139 |
+
assert sdm_from_vector([0, 0], lex, QQ, gens=[x, y]) == sdm_zero()
|
| 140 |
+
|
| 141 |
+
|
| 142 |
+
def test_nontrivial():
|
| 143 |
+
gens = [x, y, z]
|
| 144 |
+
|
| 145 |
+
def contains(I, f):
|
| 146 |
+
S = [sdm_from_vector([g], lex, QQ, gens=gens) for g in I]
|
| 147 |
+
G = sdm_groebner(S, sdm_nf_mora, lex, QQ)
|
| 148 |
+
return sdm_nf_mora(sdm_from_vector([f], lex, QQ, gens=gens),
|
| 149 |
+
G, lex, QQ) == sdm_zero()
|
| 150 |
+
|
| 151 |
+
assert contains([x, y], x)
|
| 152 |
+
assert contains([x, y], x + y)
|
| 153 |
+
assert not contains([x, y], 1)
|
| 154 |
+
assert not contains([x, y], z)
|
| 155 |
+
assert contains([x**2 + y, x**2 + x], x - y)
|
| 156 |
+
assert not contains([x + y + z, x*y + x*z + y*z, x*y*z], x**2)
|
| 157 |
+
assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x**3)
|
| 158 |
+
assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x**4)
|
| 159 |
+
assert not contains([x + y + z, x*y + x*z + y*z, x*y*z], x*y**2)
|
| 160 |
+
assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x**4 + y**3 + 2*z*y*x)
|
| 161 |
+
assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x*y*z)
|
| 162 |
+
assert contains([x, 1 + x + y, 5 - 7*y], 1)
|
| 163 |
+
assert contains(
|
| 164 |
+
[x**3 + y**3, y**3 + z**3, z**3 + x**3, x**2*y + x**2*z + y**2*z],
|
| 165 |
+
x**3)
|
| 166 |
+
assert not contains(
|
| 167 |
+
[x**3 + y**3, y**3 + z**3, z**3 + x**3, x**2*y + x**2*z + y**2*z],
|
| 168 |
+
x**2 + y**2)
|
| 169 |
+
|
| 170 |
+
# compare local order
|
| 171 |
+
assert not contains([x*(1 + x + y), y*(1 + z)], x)
|
| 172 |
+
assert not contains([x*(1 + x + y), y*(1 + z)], x + y)
|
| 173 |
+
|
| 174 |
+
|
| 175 |
+
def test_local():
|
| 176 |
+
igrlex = InverseOrder(grlex)
|
| 177 |
+
gens = [x, y, z]
|
| 178 |
+
|
| 179 |
+
def contains(I, f):
|
| 180 |
+
S = [sdm_from_vector([g], igrlex, QQ, gens=gens) for g in I]
|
| 181 |
+
G = sdm_groebner(S, sdm_nf_mora, igrlex, QQ)
|
| 182 |
+
return sdm_nf_mora(sdm_from_vector([f], lex, QQ, gens=gens),
|
| 183 |
+
G, lex, QQ) == sdm_zero()
|
| 184 |
+
assert contains([x, y], x)
|
| 185 |
+
assert contains([x, y], x + y)
|
| 186 |
+
assert not contains([x, y], 1)
|
| 187 |
+
assert not contains([x, y], z)
|
| 188 |
+
assert contains([x**2 + y, x**2 + x], x - y)
|
| 189 |
+
assert not contains([x + y + z, x*y + x*z + y*z, x*y*z], x**2)
|
| 190 |
+
assert contains([x*(1 + x + y), y*(1 + z)], x)
|
| 191 |
+
assert contains([x*(1 + x + y), y*(1 + z)], x + y)
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
def test_uncovered_line():
|
| 195 |
+
gens = [x, y]
|
| 196 |
+
f1 = sdm_zero()
|
| 197 |
+
f2 = sdm_from_vector([x, 0], lex, QQ, gens=gens)
|
| 198 |
+
f3 = sdm_from_vector([0, y], lex, QQ, gens=gens)
|
| 199 |
+
|
| 200 |
+
assert sdm_spoly(f1, f2, lex, QQ) == sdm_zero()
|
| 201 |
+
assert sdm_spoly(f3, f2, lex, QQ) == sdm_zero()
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
def test_chain_criterion():
|
| 205 |
+
gens = [x]
|
| 206 |
+
f1 = sdm_from_vector([1, x], grlex, QQ, gens=gens)
|
| 207 |
+
f2 = sdm_from_vector([0, x - 2], grlex, QQ, gens=gens)
|
| 208 |
+
assert len(sdm_groebner([f1, f2], sdm_nf_mora, grlex, QQ)) == 2
|
.venv/lib/python3.13/site-packages/sympy/polys/tests/test_euclidtools.py
ADDED
|
@@ -0,0 +1,712 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Tests for Euclidean algorithms, GCDs, LCMs and polynomial remainder sequences. """
|
| 2 |
+
|
| 3 |
+
from sympy.polys.rings import ring
|
| 4 |
+
from sympy.polys.domains import ZZ, QQ, RR
|
| 5 |
+
|
| 6 |
+
from sympy.polys.specialpolys import (
|
| 7 |
+
f_polys,
|
| 8 |
+
dmp_fateman_poly_F_1,
|
| 9 |
+
dmp_fateman_poly_F_2,
|
| 10 |
+
dmp_fateman_poly_F_3)
|
| 11 |
+
|
| 12 |
+
f_0, f_1, f_2, f_3, f_4, f_5, f_6 = f_polys()
|
| 13 |
+
|
| 14 |
+
def test_dup_gcdex():
|
| 15 |
+
R, x = ring("x", QQ)
|
| 16 |
+
|
| 17 |
+
f = x**4 - 2*x**3 - 6*x**2 + 12*x + 15
|
| 18 |
+
g = x**3 + x**2 - 4*x - 4
|
| 19 |
+
|
| 20 |
+
s = -QQ(1,5)*x + QQ(3,5)
|
| 21 |
+
t = QQ(1,5)*x**2 - QQ(6,5)*x + 2
|
| 22 |
+
h = x + 1
|
| 23 |
+
|
| 24 |
+
assert R.dup_half_gcdex(f, g) == (s, h)
|
| 25 |
+
assert R.dup_gcdex(f, g) == (s, t, h)
|
| 26 |
+
|
| 27 |
+
f = x**4 + 4*x**3 - x + 1
|
| 28 |
+
g = x**3 - x + 1
|
| 29 |
+
|
| 30 |
+
s, t, h = R.dup_gcdex(f, g)
|
| 31 |
+
S, T, H = R.dup_gcdex(g, f)
|
| 32 |
+
|
| 33 |
+
assert R.dup_add(R.dup_mul(s, f),
|
| 34 |
+
R.dup_mul(t, g)) == h
|
| 35 |
+
assert R.dup_add(R.dup_mul(S, g),
|
| 36 |
+
R.dup_mul(T, f)) == H
|
| 37 |
+
|
| 38 |
+
f = 2*x
|
| 39 |
+
g = x**2 - 16
|
| 40 |
+
|
| 41 |
+
s = QQ(1,32)*x
|
| 42 |
+
t = -QQ(1,16)
|
| 43 |
+
h = 1
|
| 44 |
+
|
| 45 |
+
assert R.dup_half_gcdex(f, g) == (s, h)
|
| 46 |
+
assert R.dup_gcdex(f, g) == (s, t, h)
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def test_dup_invert():
|
| 50 |
+
R, x = ring("x", QQ)
|
| 51 |
+
assert R.dup_invert(2*x, x**2 - 16) == QQ(1,32)*x
|
| 52 |
+
|
| 53 |
+
|
| 54 |
+
def test_dup_euclidean_prs():
|
| 55 |
+
R, x = ring("x", QQ)
|
| 56 |
+
|
| 57 |
+
f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5
|
| 58 |
+
g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21
|
| 59 |
+
|
| 60 |
+
assert R.dup_euclidean_prs(f, g) == [
|
| 61 |
+
f,
|
| 62 |
+
g,
|
| 63 |
+
-QQ(5,9)*x**4 + QQ(1,9)*x**2 - QQ(1,3),
|
| 64 |
+
-QQ(117,25)*x**2 - 9*x + QQ(441,25),
|
| 65 |
+
QQ(233150,19773)*x - QQ(102500,6591),
|
| 66 |
+
-QQ(1288744821,543589225)]
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
def test_dup_primitive_prs():
|
| 70 |
+
R, x = ring("x", ZZ)
|
| 71 |
+
|
| 72 |
+
f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5
|
| 73 |
+
g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21
|
| 74 |
+
|
| 75 |
+
assert R.dup_primitive_prs(f, g) == [
|
| 76 |
+
f,
|
| 77 |
+
g,
|
| 78 |
+
-5*x**4 + x**2 - 3,
|
| 79 |
+
13*x**2 + 25*x - 49,
|
| 80 |
+
4663*x - 6150,
|
| 81 |
+
1]
|
| 82 |
+
|
| 83 |
+
|
| 84 |
+
def test_dup_subresultants():
|
| 85 |
+
R, x = ring("x", ZZ)
|
| 86 |
+
|
| 87 |
+
assert R.dup_resultant(0, 0) == 0
|
| 88 |
+
|
| 89 |
+
assert R.dup_resultant(1, 0) == 0
|
| 90 |
+
assert R.dup_resultant(0, 1) == 0
|
| 91 |
+
|
| 92 |
+
f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5
|
| 93 |
+
g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21
|
| 94 |
+
|
| 95 |
+
a = 15*x**4 - 3*x**2 + 9
|
| 96 |
+
b = 65*x**2 + 125*x - 245
|
| 97 |
+
c = 9326*x - 12300
|
| 98 |
+
d = 260708
|
| 99 |
+
|
| 100 |
+
assert R.dup_subresultants(f, g) == [f, g, a, b, c, d]
|
| 101 |
+
assert R.dup_resultant(f, g) == R.dup_LC(d)
|
| 102 |
+
|
| 103 |
+
f = x**2 - 2*x + 1
|
| 104 |
+
g = x**2 - 1
|
| 105 |
+
|
| 106 |
+
a = 2*x - 2
|
| 107 |
+
|
| 108 |
+
assert R.dup_subresultants(f, g) == [f, g, a]
|
| 109 |
+
assert R.dup_resultant(f, g) == 0
|
| 110 |
+
|
| 111 |
+
f = x**2 + 1
|
| 112 |
+
g = x**2 - 1
|
| 113 |
+
|
| 114 |
+
a = -2
|
| 115 |
+
|
| 116 |
+
assert R.dup_subresultants(f, g) == [f, g, a]
|
| 117 |
+
assert R.dup_resultant(f, g) == 4
|
| 118 |
+
|
| 119 |
+
f = x**2 - 1
|
| 120 |
+
g = x**3 - x**2 + 2
|
| 121 |
+
|
| 122 |
+
assert R.dup_resultant(f, g) == 0
|
| 123 |
+
|
| 124 |
+
f = 3*x**3 - x
|
| 125 |
+
g = 5*x**2 + 1
|
| 126 |
+
|
| 127 |
+
assert R.dup_resultant(f, g) == 64
|
| 128 |
+
|
| 129 |
+
f = x**2 - 2*x + 7
|
| 130 |
+
g = x**3 - x + 5
|
| 131 |
+
|
| 132 |
+
assert R.dup_resultant(f, g) == 265
|
| 133 |
+
|
| 134 |
+
f = x**3 - 6*x**2 + 11*x - 6
|
| 135 |
+
g = x**3 - 15*x**2 + 74*x - 120
|
| 136 |
+
|
| 137 |
+
assert R.dup_resultant(f, g) == -8640
|
| 138 |
+
|
| 139 |
+
f = x**3 - 6*x**2 + 11*x - 6
|
| 140 |
+
g = x**3 - 10*x**2 + 29*x - 20
|
| 141 |
+
|
| 142 |
+
assert R.dup_resultant(f, g) == 0
|
| 143 |
+
|
| 144 |
+
f = x**3 - 1
|
| 145 |
+
g = x**3 + 2*x**2 + 2*x - 1
|
| 146 |
+
|
| 147 |
+
assert R.dup_resultant(f, g) == 16
|
| 148 |
+
|
| 149 |
+
f = x**8 - 2
|
| 150 |
+
g = x - 1
|
| 151 |
+
|
| 152 |
+
assert R.dup_resultant(f, g) == -1
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
def test_dmp_subresultants():
|
| 156 |
+
R, x, y = ring("x,y", ZZ)
|
| 157 |
+
|
| 158 |
+
assert R.dmp_resultant(0, 0) == 0
|
| 159 |
+
assert R.dmp_prs_resultant(0, 0)[0] == 0
|
| 160 |
+
assert R.dmp_zz_collins_resultant(0, 0) == 0
|
| 161 |
+
assert R.dmp_qq_collins_resultant(0, 0) == 0
|
| 162 |
+
|
| 163 |
+
assert R.dmp_resultant(1, 0) == 0
|
| 164 |
+
assert R.dmp_resultant(1, 0) == 0
|
| 165 |
+
assert R.dmp_resultant(1, 0) == 0
|
| 166 |
+
|
| 167 |
+
assert R.dmp_resultant(0, 1) == 0
|
| 168 |
+
assert R.dmp_prs_resultant(0, 1)[0] == 0
|
| 169 |
+
assert R.dmp_zz_collins_resultant(0, 1) == 0
|
| 170 |
+
assert R.dmp_qq_collins_resultant(0, 1) == 0
|
| 171 |
+
|
| 172 |
+
f = 3*x**2*y - y**3 - 4
|
| 173 |
+
g = x**2 + x*y**3 - 9
|
| 174 |
+
|
| 175 |
+
a = 3*x*y**4 + y**3 - 27*y + 4
|
| 176 |
+
b = -3*y**10 - 12*y**7 + y**6 - 54*y**4 + 8*y**3 + 729*y**2 - 216*y + 16
|
| 177 |
+
|
| 178 |
+
r = R.dmp_LC(b)
|
| 179 |
+
|
| 180 |
+
assert R.dmp_subresultants(f, g) == [f, g, a, b]
|
| 181 |
+
|
| 182 |
+
assert R.dmp_resultant(f, g) == r
|
| 183 |
+
assert R.dmp_prs_resultant(f, g)[0] == r
|
| 184 |
+
assert R.dmp_zz_collins_resultant(f, g) == r
|
| 185 |
+
assert R.dmp_qq_collins_resultant(f, g) == r
|
| 186 |
+
|
| 187 |
+
f = -x**3 + 5
|
| 188 |
+
g = 3*x**2*y + x**2
|
| 189 |
+
|
| 190 |
+
a = 45*y**2 + 30*y + 5
|
| 191 |
+
b = 675*y**3 + 675*y**2 + 225*y + 25
|
| 192 |
+
|
| 193 |
+
r = R.dmp_LC(b)
|
| 194 |
+
|
| 195 |
+
assert R.dmp_subresultants(f, g) == [f, g, a]
|
| 196 |
+
assert R.dmp_resultant(f, g) == r
|
| 197 |
+
assert R.dmp_prs_resultant(f, g)[0] == r
|
| 198 |
+
assert R.dmp_zz_collins_resultant(f, g) == r
|
| 199 |
+
assert R.dmp_qq_collins_resultant(f, g) == r
|
| 200 |
+
|
| 201 |
+
R, x, y, z, u, v = ring("x,y,z,u,v", ZZ)
|
| 202 |
+
|
| 203 |
+
f = 6*x**2 - 3*x*y - 2*x*z + y*z
|
| 204 |
+
g = x**2 - x*u - x*v + u*v
|
| 205 |
+
|
| 206 |
+
r = y**2*z**2 - 3*y**2*z*u - 3*y**2*z*v + 9*y**2*u*v - 2*y*z**2*u \
|
| 207 |
+
- 2*y*z**2*v + 6*y*z*u**2 + 12*y*z*u*v + 6*y*z*v**2 - 18*y*u**2*v \
|
| 208 |
+
- 18*y*u*v**2 + 4*z**2*u*v - 12*z*u**2*v - 12*z*u*v**2 + 36*u**2*v**2
|
| 209 |
+
|
| 210 |
+
assert R.dmp_zz_collins_resultant(f, g) == r.drop(x)
|
| 211 |
+
|
| 212 |
+
R, x, y, z, u, v = ring("x,y,z,u,v", QQ)
|
| 213 |
+
|
| 214 |
+
f = x**2 - QQ(1,2)*x*y - QQ(1,3)*x*z + QQ(1,6)*y*z
|
| 215 |
+
g = x**2 - x*u - x*v + u*v
|
| 216 |
+
|
| 217 |
+
r = QQ(1,36)*y**2*z**2 - QQ(1,12)*y**2*z*u - QQ(1,12)*y**2*z*v + QQ(1,4)*y**2*u*v \
|
| 218 |
+
- QQ(1,18)*y*z**2*u - QQ(1,18)*y*z**2*v + QQ(1,6)*y*z*u**2 + QQ(1,3)*y*z*u*v \
|
| 219 |
+
+ QQ(1,6)*y*z*v**2 - QQ(1,2)*y*u**2*v - QQ(1,2)*y*u*v**2 + QQ(1,9)*z**2*u*v \
|
| 220 |
+
- QQ(1,3)*z*u**2*v - QQ(1,3)*z*u*v**2 + u**2*v**2
|
| 221 |
+
|
| 222 |
+
assert R.dmp_qq_collins_resultant(f, g) == r.drop(x)
|
| 223 |
+
|
| 224 |
+
Rt, t = ring("t", ZZ)
|
| 225 |
+
Rx, x = ring("x", Rt)
|
| 226 |
+
|
| 227 |
+
f = x**6 - 5*x**4 + 5*x**2 + 4
|
| 228 |
+
g = -6*t*x**5 + x**4 + 20*t*x**3 - 3*x**2 - 10*t*x + 6
|
| 229 |
+
|
| 230 |
+
assert Rx.dup_resultant(f, g) == 2930944*t**6 + 2198208*t**4 + 549552*t**2 + 45796
|
| 231 |
+
|
| 232 |
+
|
| 233 |
+
def test_dup_discriminant():
|
| 234 |
+
R, x = ring("x", ZZ)
|
| 235 |
+
|
| 236 |
+
assert R.dup_discriminant(0) == 0
|
| 237 |
+
assert R.dup_discriminant(x) == 1
|
| 238 |
+
|
| 239 |
+
assert R.dup_discriminant(x**3 + 3*x**2 + 9*x - 13) == -11664
|
| 240 |
+
assert R.dup_discriminant(5*x**5 + x**3 + 2) == 31252160
|
| 241 |
+
assert R.dup_discriminant(x**4 + 2*x**3 + 6*x**2 - 22*x + 13) == 0
|
| 242 |
+
assert R.dup_discriminant(12*x**7 + 15*x**4 + 30*x**3 + x**2 + 1) == -220289699947514112
|
| 243 |
+
|
| 244 |
+
|
| 245 |
+
def test_dmp_discriminant():
|
| 246 |
+
R, x = ring("x", ZZ)
|
| 247 |
+
|
| 248 |
+
assert R.dmp_discriminant(0) == 0
|
| 249 |
+
|
| 250 |
+
R, x, y = ring("x,y", ZZ)
|
| 251 |
+
|
| 252 |
+
assert R.dmp_discriminant(0) == 0
|
| 253 |
+
assert R.dmp_discriminant(y) == 0
|
| 254 |
+
|
| 255 |
+
assert R.dmp_discriminant(x**3 + 3*x**2 + 9*x - 13) == -11664
|
| 256 |
+
assert R.dmp_discriminant(5*x**5 + x**3 + 2) == 31252160
|
| 257 |
+
assert R.dmp_discriminant(x**4 + 2*x**3 + 6*x**2 - 22*x + 13) == 0
|
| 258 |
+
assert R.dmp_discriminant(12*x**7 + 15*x**4 + 30*x**3 + x**2 + 1) == -220289699947514112
|
| 259 |
+
|
| 260 |
+
assert R.dmp_discriminant(x**2*y + 2*y) == (-8*y**2).drop(x)
|
| 261 |
+
assert R.dmp_discriminant(x*y**2 + 2*x) == 1
|
| 262 |
+
|
| 263 |
+
R, x, y, z = ring("x,y,z", ZZ)
|
| 264 |
+
assert R.dmp_discriminant(x*y + z) == 1
|
| 265 |
+
|
| 266 |
+
R, x, y, z, u = ring("x,y,z,u", ZZ)
|
| 267 |
+
assert R.dmp_discriminant(x**2*y + x*z + u) == (-4*y*u + z**2).drop(x)
|
| 268 |
+
|
| 269 |
+
R, x, y, z, u, v = ring("x,y,z,u,v", ZZ)
|
| 270 |
+
assert R.dmp_discriminant(x**3*y + x**2*z + x*u + v) == \
|
| 271 |
+
(-27*y**2*v**2 + 18*y*z*u*v - 4*y*u**3 - 4*z**3*v + z**2*u**2).drop(x)
|
| 272 |
+
|
| 273 |
+
|
| 274 |
+
def test_dup_gcd():
|
| 275 |
+
R, x = ring("x", ZZ)
|
| 276 |
+
|
| 277 |
+
f, g = 0, 0
|
| 278 |
+
assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (0, 0, 0)
|
| 279 |
+
|
| 280 |
+
f, g = 2, 0
|
| 281 |
+
assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, 1, 0)
|
| 282 |
+
|
| 283 |
+
f, g = -2, 0
|
| 284 |
+
assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, -1, 0)
|
| 285 |
+
|
| 286 |
+
f, g = 0, -2
|
| 287 |
+
assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, 0, -1)
|
| 288 |
+
|
| 289 |
+
f, g = 0, 2*x + 4
|
| 290 |
+
assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2*x + 4, 0, 1)
|
| 291 |
+
|
| 292 |
+
f, g = 2*x + 4, 0
|
| 293 |
+
assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2*x + 4, 1, 0)
|
| 294 |
+
|
| 295 |
+
f, g = 2, 2
|
| 296 |
+
assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, 1, 1)
|
| 297 |
+
|
| 298 |
+
f, g = -2, 2
|
| 299 |
+
assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, -1, 1)
|
| 300 |
+
|
| 301 |
+
f, g = 2, -2
|
| 302 |
+
assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, 1, -1)
|
| 303 |
+
|
| 304 |
+
f, g = -2, -2
|
| 305 |
+
assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, -1, -1)
|
| 306 |
+
|
| 307 |
+
f, g = x**2 + 2*x + 1, 1
|
| 308 |
+
assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (1, x**2 + 2*x + 1, 1)
|
| 309 |
+
|
| 310 |
+
f, g = x**2 + 2*x + 1, 2
|
| 311 |
+
assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (1, x**2 + 2*x + 1, 2)
|
| 312 |
+
|
| 313 |
+
f, g = 2*x**2 + 4*x + 2, 2
|
| 314 |
+
assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, x**2 + 2*x + 1, 1)
|
| 315 |
+
|
| 316 |
+
f, g = 2, 2*x**2 + 4*x + 2
|
| 317 |
+
assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, 1, x**2 + 2*x + 1)
|
| 318 |
+
|
| 319 |
+
f, g = 2*x**2 + 4*x + 2, x + 1
|
| 320 |
+
assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (x + 1, 2*x + 2, 1)
|
| 321 |
+
|
| 322 |
+
f, g = x + 1, 2*x**2 + 4*x + 2
|
| 323 |
+
assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (x + 1, 1, 2*x + 2)
|
| 324 |
+
|
| 325 |
+
f, g = x - 31, x
|
| 326 |
+
assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (1, f, g)
|
| 327 |
+
|
| 328 |
+
f = x**4 + 8*x**3 + 21*x**2 + 22*x + 8
|
| 329 |
+
g = x**3 + 6*x**2 + 11*x + 6
|
| 330 |
+
|
| 331 |
+
h = x**2 + 3*x + 2
|
| 332 |
+
|
| 333 |
+
cff = x**2 + 5*x + 4
|
| 334 |
+
cfg = x + 3
|
| 335 |
+
|
| 336 |
+
assert R.dup_zz_heu_gcd(f, g) == (h, cff, cfg)
|
| 337 |
+
assert R.dup_rr_prs_gcd(f, g) == (h, cff, cfg)
|
| 338 |
+
|
| 339 |
+
f = x**4 - 4
|
| 340 |
+
g = x**4 + 4*x**2 + 4
|
| 341 |
+
|
| 342 |
+
h = x**2 + 2
|
| 343 |
+
|
| 344 |
+
cff = x**2 - 2
|
| 345 |
+
cfg = x**2 + 2
|
| 346 |
+
|
| 347 |
+
assert R.dup_zz_heu_gcd(f, g) == (h, cff, cfg)
|
| 348 |
+
assert R.dup_rr_prs_gcd(f, g) == (h, cff, cfg)
|
| 349 |
+
|
| 350 |
+
f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5
|
| 351 |
+
g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21
|
| 352 |
+
|
| 353 |
+
h = 1
|
| 354 |
+
|
| 355 |
+
cff = f
|
| 356 |
+
cfg = g
|
| 357 |
+
|
| 358 |
+
assert R.dup_zz_heu_gcd(f, g) == (h, cff, cfg)
|
| 359 |
+
assert R.dup_rr_prs_gcd(f, g) == (h, cff, cfg)
|
| 360 |
+
|
| 361 |
+
R, x = ring("x", QQ)
|
| 362 |
+
|
| 363 |
+
f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5
|
| 364 |
+
g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21
|
| 365 |
+
|
| 366 |
+
h = 1
|
| 367 |
+
|
| 368 |
+
cff = f
|
| 369 |
+
cfg = g
|
| 370 |
+
|
| 371 |
+
assert R.dup_qq_heu_gcd(f, g) == (h, cff, cfg)
|
| 372 |
+
assert R.dup_ff_prs_gcd(f, g) == (h, cff, cfg)
|
| 373 |
+
|
| 374 |
+
R, x = ring("x", ZZ)
|
| 375 |
+
|
| 376 |
+
f = - 352518131239247345597970242177235495263669787845475025293906825864749649589178600387510272*x**49 \
|
| 377 |
+
+ 46818041807522713962450042363465092040687472354933295397472942006618953623327997952*x**42 \
|
| 378 |
+
+ 378182690892293941192071663536490788434899030680411695933646320291525827756032*x**35 \
|
| 379 |
+
+ 112806468807371824947796775491032386836656074179286744191026149539708928*x**28 \
|
| 380 |
+
- 12278371209708240950316872681744825481125965781519138077173235712*x**21 \
|
| 381 |
+
+ 289127344604779611146960547954288113529690984687482920704*x**14 \
|
| 382 |
+
+ 19007977035740498977629742919480623972236450681*x**7 \
|
| 383 |
+
+ 311973482284542371301330321821976049
|
| 384 |
+
|
| 385 |
+
g = 365431878023781158602430064717380211405897160759702125019136*x**21 \
|
| 386 |
+
+ 197599133478719444145775798221171663643171734081650688*x**14 \
|
| 387 |
+
- 9504116979659010018253915765478924103928886144*x**7 \
|
| 388 |
+
- 311973482284542371301330321821976049
|
| 389 |
+
|
| 390 |
+
assert R.dup_zz_heu_gcd(f, R.dup_diff(f, 1))[0] == g
|
| 391 |
+
assert R.dup_rr_prs_gcd(f, R.dup_diff(f, 1))[0] == g
|
| 392 |
+
|
| 393 |
+
R, x = ring("x", QQ)
|
| 394 |
+
|
| 395 |
+
f = QQ(1,2)*x**2 + x + QQ(1,2)
|
| 396 |
+
g = QQ(1,2)*x + QQ(1,2)
|
| 397 |
+
|
| 398 |
+
h = x + 1
|
| 399 |
+
|
| 400 |
+
assert R.dup_qq_heu_gcd(f, g) == (h, g, QQ(1,2))
|
| 401 |
+
assert R.dup_ff_prs_gcd(f, g) == (h, g, QQ(1,2))
|
| 402 |
+
|
| 403 |
+
R, x = ring("x", ZZ)
|
| 404 |
+
|
| 405 |
+
f = 1317378933230047068160*x + 2945748836994210856960
|
| 406 |
+
g = 120352542776360960*x + 269116466014453760
|
| 407 |
+
|
| 408 |
+
h = 120352542776360960*x + 269116466014453760
|
| 409 |
+
cff = 10946
|
| 410 |
+
cfg = 1
|
| 411 |
+
|
| 412 |
+
assert R.dup_zz_heu_gcd(f, g) == (h, cff, cfg)
|
| 413 |
+
|
| 414 |
+
|
| 415 |
+
def test_dmp_gcd():
|
| 416 |
+
R, x, y = ring("x,y", ZZ)
|
| 417 |
+
|
| 418 |
+
f, g = 0, 0
|
| 419 |
+
assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (0, 0, 0)
|
| 420 |
+
|
| 421 |
+
f, g = 2, 0
|
| 422 |
+
assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, 1, 0)
|
| 423 |
+
|
| 424 |
+
f, g = -2, 0
|
| 425 |
+
assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, -1, 0)
|
| 426 |
+
|
| 427 |
+
f, g = 0, -2
|
| 428 |
+
assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, 0, -1)
|
| 429 |
+
|
| 430 |
+
f, g = 0, 2*x + 4
|
| 431 |
+
assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2*x + 4, 0, 1)
|
| 432 |
+
|
| 433 |
+
f, g = 2*x + 4, 0
|
| 434 |
+
assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2*x + 4, 1, 0)
|
| 435 |
+
|
| 436 |
+
f, g = 2, 2
|
| 437 |
+
assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, 1, 1)
|
| 438 |
+
|
| 439 |
+
f, g = -2, 2
|
| 440 |
+
assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, -1, 1)
|
| 441 |
+
|
| 442 |
+
f, g = 2, -2
|
| 443 |
+
assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, 1, -1)
|
| 444 |
+
|
| 445 |
+
f, g = -2, -2
|
| 446 |
+
assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, -1, -1)
|
| 447 |
+
|
| 448 |
+
f, g = x**2 + 2*x + 1, 1
|
| 449 |
+
assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (1, x**2 + 2*x + 1, 1)
|
| 450 |
+
|
| 451 |
+
f, g = x**2 + 2*x + 1, 2
|
| 452 |
+
assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (1, x**2 + 2*x + 1, 2)
|
| 453 |
+
|
| 454 |
+
f, g = 2*x**2 + 4*x + 2, 2
|
| 455 |
+
assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, x**2 + 2*x + 1, 1)
|
| 456 |
+
|
| 457 |
+
f, g = 2, 2*x**2 + 4*x + 2
|
| 458 |
+
assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, 1, x**2 + 2*x + 1)
|
| 459 |
+
|
| 460 |
+
f, g = 2*x**2 + 4*x + 2, x + 1
|
| 461 |
+
assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (x + 1, 2*x + 2, 1)
|
| 462 |
+
|
| 463 |
+
f, g = x + 1, 2*x**2 + 4*x + 2
|
| 464 |
+
assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (x + 1, 1, 2*x + 2)
|
| 465 |
+
|
| 466 |
+
R, x, y, z, u = ring("x,y,z,u", ZZ)
|
| 467 |
+
|
| 468 |
+
f, g = u**2 + 2*u + 1, 2*u + 2
|
| 469 |
+
assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (u + 1, u + 1, 2)
|
| 470 |
+
|
| 471 |
+
f, g = z**2*u**2 + 2*z**2*u + z**2 + z*u + z, u**2 + 2*u + 1
|
| 472 |
+
h, cff, cfg = u + 1, z**2*u + z**2 + z, u + 1
|
| 473 |
+
|
| 474 |
+
assert R.dmp_zz_heu_gcd(f, g) == (h, cff, cfg)
|
| 475 |
+
assert R.dmp_rr_prs_gcd(f, g) == (h, cff, cfg)
|
| 476 |
+
|
| 477 |
+
assert R.dmp_zz_heu_gcd(g, f) == (h, cfg, cff)
|
| 478 |
+
assert R.dmp_rr_prs_gcd(g, f) == (h, cfg, cff)
|
| 479 |
+
|
| 480 |
+
R, x, y, z = ring("x,y,z", ZZ)
|
| 481 |
+
|
| 482 |
+
f, g, h = map(R.from_dense, dmp_fateman_poly_F_1(2, ZZ))
|
| 483 |
+
H, cff, cfg = R.dmp_zz_heu_gcd(f, g)
|
| 484 |
+
|
| 485 |
+
assert H == h and R.dmp_mul(H, cff) == f \
|
| 486 |
+
and R.dmp_mul(H, cfg) == g
|
| 487 |
+
|
| 488 |
+
H, cff, cfg = R.dmp_rr_prs_gcd(f, g)
|
| 489 |
+
|
| 490 |
+
assert H == h and R.dmp_mul(H, cff) == f \
|
| 491 |
+
and R.dmp_mul(H, cfg) == g
|
| 492 |
+
|
| 493 |
+
R, x, y, z, u, v = ring("x,y,z,u,v", ZZ)
|
| 494 |
+
|
| 495 |
+
f, g, h = map(R.from_dense, dmp_fateman_poly_F_1(4, ZZ))
|
| 496 |
+
H, cff, cfg = R.dmp_zz_heu_gcd(f, g)
|
| 497 |
+
|
| 498 |
+
assert H == h and R.dmp_mul(H, cff) == f \
|
| 499 |
+
and R.dmp_mul(H, cfg) == g
|
| 500 |
+
|
| 501 |
+
R, x, y, z, u, v, a, b = ring("x,y,z,u,v,a,b", ZZ)
|
| 502 |
+
|
| 503 |
+
f, g, h = map(R.from_dense, dmp_fateman_poly_F_1(6, ZZ))
|
| 504 |
+
H, cff, cfg = R.dmp_zz_heu_gcd(f, g)
|
| 505 |
+
|
| 506 |
+
assert H == h and R.dmp_mul(H, cff) == f \
|
| 507 |
+
and R.dmp_mul(H, cfg) == g
|
| 508 |
+
|
| 509 |
+
R, x, y, z, u, v, a, b, c, d = ring("x,y,z,u,v,a,b,c,d", ZZ)
|
| 510 |
+
|
| 511 |
+
f, g, h = map(R.from_dense, dmp_fateman_poly_F_1(8, ZZ))
|
| 512 |
+
H, cff, cfg = R.dmp_zz_heu_gcd(f, g)
|
| 513 |
+
|
| 514 |
+
assert H == h and R.dmp_mul(H, cff) == f \
|
| 515 |
+
and R.dmp_mul(H, cfg) == g
|
| 516 |
+
|
| 517 |
+
R, x, y, z = ring("x,y,z", ZZ)
|
| 518 |
+
|
| 519 |
+
f, g, h = map(R.from_dense, dmp_fateman_poly_F_2(2, ZZ))
|
| 520 |
+
H, cff, cfg = R.dmp_zz_heu_gcd(f, g)
|
| 521 |
+
|
| 522 |
+
assert H == h and R.dmp_mul(H, cff) == f \
|
| 523 |
+
and R.dmp_mul(H, cfg) == g
|
| 524 |
+
|
| 525 |
+
H, cff, cfg = R.dmp_rr_prs_gcd(f, g)
|
| 526 |
+
|
| 527 |
+
assert H == h and R.dmp_mul(H, cff) == f \
|
| 528 |
+
and R.dmp_mul(H, cfg) == g
|
| 529 |
+
|
| 530 |
+
f, g, h = map(R.from_dense, dmp_fateman_poly_F_3(2, ZZ))
|
| 531 |
+
H, cff, cfg = R.dmp_zz_heu_gcd(f, g)
|
| 532 |
+
|
| 533 |
+
assert H == h and R.dmp_mul(H, cff) == f \
|
| 534 |
+
and R.dmp_mul(H, cfg) == g
|
| 535 |
+
|
| 536 |
+
H, cff, cfg = R.dmp_rr_prs_gcd(f, g)
|
| 537 |
+
|
| 538 |
+
assert H == h and R.dmp_mul(H, cff) == f \
|
| 539 |
+
and R.dmp_mul(H, cfg) == g
|
| 540 |
+
|
| 541 |
+
R, x, y, z, u, v = ring("x,y,z,u,v", ZZ)
|
| 542 |
+
|
| 543 |
+
f, g, h = map(R.from_dense, dmp_fateman_poly_F_3(4, ZZ))
|
| 544 |
+
H, cff, cfg = R.dmp_inner_gcd(f, g)
|
| 545 |
+
|
| 546 |
+
assert H == h and R.dmp_mul(H, cff) == f \
|
| 547 |
+
and R.dmp_mul(H, cfg) == g
|
| 548 |
+
|
| 549 |
+
R, x, y = ring("x,y", QQ)
|
| 550 |
+
|
| 551 |
+
f = QQ(1,2)*x**2 + x + QQ(1,2)
|
| 552 |
+
g = QQ(1,2)*x + QQ(1,2)
|
| 553 |
+
|
| 554 |
+
h = x + 1
|
| 555 |
+
|
| 556 |
+
assert R.dmp_qq_heu_gcd(f, g) == (h, g, QQ(1,2))
|
| 557 |
+
assert R.dmp_ff_prs_gcd(f, g) == (h, g, QQ(1,2))
|
| 558 |
+
|
| 559 |
+
R, x, y = ring("x,y", RR)
|
| 560 |
+
|
| 561 |
+
f = 2.1*x*y**2 - 2.2*x*y + 2.1*x
|
| 562 |
+
g = 1.0*x**3
|
| 563 |
+
|
| 564 |
+
assert R.dmp_ff_prs_gcd(f, g) == \
|
| 565 |
+
(1.0*x, 2.1*y**2 - 2.2*y + 2.1, 1.0*x**2)
|
| 566 |
+
|
| 567 |
+
|
| 568 |
+
def test_dup_lcm():
|
| 569 |
+
R, x = ring("x", ZZ)
|
| 570 |
+
|
| 571 |
+
assert R.dup_lcm(2, 6) == 6
|
| 572 |
+
|
| 573 |
+
assert R.dup_lcm(2*x**3, 6*x) == 6*x**3
|
| 574 |
+
assert R.dup_lcm(2*x**3, 3*x) == 6*x**3
|
| 575 |
+
|
| 576 |
+
assert R.dup_lcm(x**2 + x, x) == x**2 + x
|
| 577 |
+
assert R.dup_lcm(x**2 + x, 2*x) == 2*x**2 + 2*x
|
| 578 |
+
assert R.dup_lcm(x**2 + 2*x, x) == x**2 + 2*x
|
| 579 |
+
assert R.dup_lcm(2*x**2 + x, x) == 2*x**2 + x
|
| 580 |
+
assert R.dup_lcm(2*x**2 + x, 2*x) == 4*x**2 + 2*x
|
| 581 |
+
|
| 582 |
+
|
| 583 |
+
def test_dmp_lcm():
|
| 584 |
+
R, x, y = ring("x,y", ZZ)
|
| 585 |
+
|
| 586 |
+
assert R.dmp_lcm(2, 6) == 6
|
| 587 |
+
assert R.dmp_lcm(x, y) == x*y
|
| 588 |
+
|
| 589 |
+
assert R.dmp_lcm(2*x**3, 6*x*y**2) == 6*x**3*y**2
|
| 590 |
+
assert R.dmp_lcm(2*x**3, 3*x*y**2) == 6*x**3*y**2
|
| 591 |
+
|
| 592 |
+
assert R.dmp_lcm(x**2*y, x*y**2) == x**2*y**2
|
| 593 |
+
|
| 594 |
+
f = 2*x*y**5 - 3*x*y**4 - 2*x*y**3 + 3*x*y**2
|
| 595 |
+
g = y**5 - 2*y**3 + y
|
| 596 |
+
h = 2*x*y**7 - 3*x*y**6 - 4*x*y**5 + 6*x*y**4 + 2*x*y**3 - 3*x*y**2
|
| 597 |
+
|
| 598 |
+
assert R.dmp_lcm(f, g) == h
|
| 599 |
+
|
| 600 |
+
f = x**3 - 3*x**2*y - 9*x*y**2 - 5*y**3
|
| 601 |
+
g = x**4 + 6*x**3*y + 12*x**2*y**2 + 10*x*y**3 + 3*y**4
|
| 602 |
+
h = x**5 + x**4*y - 18*x**3*y**2 - 50*x**2*y**3 - 47*x*y**4 - 15*y**5
|
| 603 |
+
|
| 604 |
+
assert R.dmp_lcm(f, g) == h
|
| 605 |
+
|
| 606 |
+
|
| 607 |
+
def test_dmp_content():
|
| 608 |
+
R, x,y = ring("x,y", ZZ)
|
| 609 |
+
|
| 610 |
+
assert R.dmp_content(-2) == 2
|
| 611 |
+
|
| 612 |
+
f, g, F = 3*y**2 + 2*y + 1, 1, 0
|
| 613 |
+
|
| 614 |
+
for i in range(0, 5):
|
| 615 |
+
g *= f
|
| 616 |
+
F += x**i*g
|
| 617 |
+
|
| 618 |
+
assert R.dmp_content(F) == f.drop(x)
|
| 619 |
+
|
| 620 |
+
R, x,y,z = ring("x,y,z", ZZ)
|
| 621 |
+
|
| 622 |
+
assert R.dmp_content(f_4) == 1
|
| 623 |
+
assert R.dmp_content(f_5) == 1
|
| 624 |
+
|
| 625 |
+
R, x,y,z,t = ring("x,y,z,t", ZZ)
|
| 626 |
+
assert R.dmp_content(f_6) == 1
|
| 627 |
+
|
| 628 |
+
|
| 629 |
+
def test_dmp_primitive():
|
| 630 |
+
R, x,y = ring("x,y", ZZ)
|
| 631 |
+
|
| 632 |
+
assert R.dmp_primitive(0) == (0, 0)
|
| 633 |
+
assert R.dmp_primitive(1) == (1, 1)
|
| 634 |
+
|
| 635 |
+
f, g, F = 3*y**2 + 2*y + 1, 1, 0
|
| 636 |
+
|
| 637 |
+
for i in range(0, 5):
|
| 638 |
+
g *= f
|
| 639 |
+
F += x**i*g
|
| 640 |
+
|
| 641 |
+
assert R.dmp_primitive(F) == (f.drop(x), F / f)
|
| 642 |
+
|
| 643 |
+
R, x,y,z = ring("x,y,z", ZZ)
|
| 644 |
+
|
| 645 |
+
cont, f = R.dmp_primitive(f_4)
|
| 646 |
+
assert cont == 1 and f == f_4
|
| 647 |
+
cont, f = R.dmp_primitive(f_5)
|
| 648 |
+
assert cont == 1 and f == f_5
|
| 649 |
+
|
| 650 |
+
R, x,y,z,t = ring("x,y,z,t", ZZ)
|
| 651 |
+
|
| 652 |
+
cont, f = R.dmp_primitive(f_6)
|
| 653 |
+
assert cont == 1 and f == f_6
|
| 654 |
+
|
| 655 |
+
|
| 656 |
+
def test_dup_cancel():
|
| 657 |
+
R, x = ring("x", ZZ)
|
| 658 |
+
|
| 659 |
+
f = 2*x**2 - 2
|
| 660 |
+
g = x**2 - 2*x + 1
|
| 661 |
+
|
| 662 |
+
p = 2*x + 2
|
| 663 |
+
q = x - 1
|
| 664 |
+
|
| 665 |
+
assert R.dup_cancel(f, g) == (p, q)
|
| 666 |
+
assert R.dup_cancel(f, g, include=False) == (1, 1, p, q)
|
| 667 |
+
|
| 668 |
+
f = -x - 2
|
| 669 |
+
g = 3*x - 4
|
| 670 |
+
|
| 671 |
+
F = x + 2
|
| 672 |
+
G = -3*x + 4
|
| 673 |
+
|
| 674 |
+
assert R.dup_cancel(f, g) == (f, g)
|
| 675 |
+
assert R.dup_cancel(F, G) == (f, g)
|
| 676 |
+
|
| 677 |
+
assert R.dup_cancel(0, 0) == (0, 0)
|
| 678 |
+
assert R.dup_cancel(0, 0, include=False) == (1, 1, 0, 0)
|
| 679 |
+
|
| 680 |
+
assert R.dup_cancel(x, 0) == (1, 0)
|
| 681 |
+
assert R.dup_cancel(x, 0, include=False) == (1, 1, 1, 0)
|
| 682 |
+
|
| 683 |
+
assert R.dup_cancel(0, x) == (0, 1)
|
| 684 |
+
assert R.dup_cancel(0, x, include=False) == (1, 1, 0, 1)
|
| 685 |
+
|
| 686 |
+
f = 0
|
| 687 |
+
g = x
|
| 688 |
+
one = 1
|
| 689 |
+
|
| 690 |
+
assert R.dup_cancel(f, g, include=True) == (f, one)
|
| 691 |
+
|
| 692 |
+
|
| 693 |
+
def test_dmp_cancel():
|
| 694 |
+
R, x, y = ring("x,y", ZZ)
|
| 695 |
+
|
| 696 |
+
f = 2*x**2 - 2
|
| 697 |
+
g = x**2 - 2*x + 1
|
| 698 |
+
|
| 699 |
+
p = 2*x + 2
|
| 700 |
+
q = x - 1
|
| 701 |
+
|
| 702 |
+
assert R.dmp_cancel(f, g) == (p, q)
|
| 703 |
+
assert R.dmp_cancel(f, g, include=False) == (1, 1, p, q)
|
| 704 |
+
|
| 705 |
+
assert R.dmp_cancel(0, 0) == (0, 0)
|
| 706 |
+
assert R.dmp_cancel(0, 0, include=False) == (1, 1, 0, 0)
|
| 707 |
+
|
| 708 |
+
assert R.dmp_cancel(y, 0) == (1, 0)
|
| 709 |
+
assert R.dmp_cancel(y, 0, include=False) == (1, 1, 1, 0)
|
| 710 |
+
|
| 711 |
+
assert R.dmp_cancel(0, y) == (0, 1)
|
| 712 |
+
assert R.dmp_cancel(0, y, include=False) == (1, 1, 0, 1)
|