diff --git a/.venv/lib/python3.13/site-packages/sympy/concrete/tests/__init__.py b/.venv/lib/python3.13/site-packages/sympy/concrete/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.13/site-packages/sympy/concrete/tests/test_delta.py b/.venv/lib/python3.13/site-packages/sympy/concrete/tests/test_delta.py new file mode 100644 index 0000000000000000000000000000000000000000..9dc6e88d16346acc7dc775446d7de3f3696d0e03 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/concrete/tests/test_delta.py @@ -0,0 +1,499 @@ +from sympy.concrete import Sum +from sympy.concrete.delta import deltaproduct as dp, deltasummation as ds, _extract_delta +from sympy.core import Eq, S, symbols, oo +from sympy.functions import KroneckerDelta as KD, Piecewise, piecewise_fold +from sympy.logic import And +from sympy.testing.pytest import raises + +i, j, k, l, m = symbols("i j k l m", integer=True, finite=True) +x, y = symbols("x y", commutative=False) + + +def test_deltaproduct_trivial(): + assert dp(x, (j, 1, 0)) == 1 + assert dp(x, (j, 1, 3)) == x**3 + assert dp(x + y, (j, 1, 3)) == (x + y)**3 + assert dp(x*y, (j, 1, 3)) == (x*y)**3 + assert dp(KD(i, j), (k, 1, 3)) == KD(i, j) + assert dp(x*KD(i, j), (k, 1, 3)) == x**3*KD(i, j) + assert dp(x*y*KD(i, j), (k, 1, 3)) == (x*y)**3*KD(i, j) + + +def test_deltaproduct_basic(): + assert dp(KD(i, j), (j, 1, 3)) == 0 + assert dp(KD(i, j), (j, 1, 1)) == KD(i, 1) + assert dp(KD(i, j), (j, 2, 2)) == KD(i, 2) + assert dp(KD(i, j), (j, 3, 3)) == KD(i, 3) + assert dp(KD(i, j), (j, 1, k)) == KD(i, 1)*KD(k, 1) + KD(k, 0) + assert dp(KD(i, j), (j, k, 3)) == KD(i, 3)*KD(k, 3) + KD(k, 4) + assert dp(KD(i, j), (j, k, l)) == KD(i, l)*KD(k, l) + KD(k, l + 1) + + +def test_deltaproduct_mul_x_kd(): + assert dp(x*KD(i, j), (j, 1, 3)) == 0 + assert dp(x*KD(i, j), (j, 1, 1)) == x*KD(i, 1) + assert dp(x*KD(i, j), (j, 2, 2)) == x*KD(i, 2) + assert dp(x*KD(i, j), (j, 3, 3)) == x*KD(i, 3) + assert dp(x*KD(i, j), (j, 1, k)) == x*KD(i, 1)*KD(k, 1) + KD(k, 0) + assert dp(x*KD(i, j), (j, k, 3)) == x*KD(i, 3)*KD(k, 3) + KD(k, 4) + assert dp(x*KD(i, j), (j, k, l)) == x*KD(i, l)*KD(k, l) + KD(k, l + 1) + + +def test_deltaproduct_mul_add_x_y_kd(): + assert dp((x + y)*KD(i, j), (j, 1, 3)) == 0 + assert dp((x + y)*KD(i, j), (j, 1, 1)) == (x + y)*KD(i, 1) + assert dp((x + y)*KD(i, j), (j, 2, 2)) == (x + y)*KD(i, 2) + assert dp((x + y)*KD(i, j), (j, 3, 3)) == (x + y)*KD(i, 3) + assert dp((x + y)*KD(i, j), (j, 1, k)) == \ + (x + y)*KD(i, 1)*KD(k, 1) + KD(k, 0) + assert dp((x + y)*KD(i, j), (j, k, 3)) == \ + (x + y)*KD(i, 3)*KD(k, 3) + KD(k, 4) + assert dp((x + y)*KD(i, j), (j, k, l)) == \ + (x + y)*KD(i, l)*KD(k, l) + KD(k, l + 1) + + +def test_deltaproduct_add_kd_kd(): + assert dp(KD(i, k) + KD(j, k), (k, 1, 3)) == 0 + assert dp(KD(i, k) + KD(j, k), (k, 1, 1)) == KD(i, 1) + KD(j, 1) + assert dp(KD(i, k) + KD(j, k), (k, 2, 2)) == KD(i, 2) + KD(j, 2) + assert dp(KD(i, k) + KD(j, k), (k, 3, 3)) == KD(i, 3) + KD(j, 3) + assert dp(KD(i, k) + KD(j, k), (k, 1, l)) == KD(l, 0) + \ + KD(i, 1)*KD(l, 1) + KD(j, 1)*KD(l, 1) + \ + KD(i, 1)*KD(j, 2)*KD(l, 2) + KD(j, 1)*KD(i, 2)*KD(l, 2) + assert dp(KD(i, k) + KD(j, k), (k, l, 3)) == KD(l, 4) + \ + KD(i, 3)*KD(l, 3) + KD(j, 3)*KD(l, 3) + \ + KD(i, 2)*KD(j, 3)*KD(l, 2) + KD(i, 3)*KD(j, 2)*KD(l, 2) + assert dp(KD(i, k) + KD(j, k), (k, l, m)) == KD(l, m + 1) + \ + KD(i, m)*KD(l, m) + KD(j, m)*KD(l, m) + \ + KD(i, m)*KD(j, m - 1)*KD(l, m - 1) + KD(i, m - 1)*KD(j, m)*KD(l, m - 1) + + +def test_deltaproduct_mul_x_add_kd_kd(): + assert dp(x*(KD(i, k) + KD(j, k)), (k, 1, 3)) == 0 + assert dp(x*(KD(i, k) + KD(j, k)), (k, 1, 1)) == x*(KD(i, 1) + KD(j, 1)) + assert dp(x*(KD(i, k) + KD(j, k)), (k, 2, 2)) == x*(KD(i, 2) + KD(j, 2)) + assert dp(x*(KD(i, k) + KD(j, k)), (k, 3, 3)) == x*(KD(i, 3) + KD(j, 3)) + assert dp(x*(KD(i, k) + KD(j, k)), (k, 1, l)) == KD(l, 0) + \ + x*KD(i, 1)*KD(l, 1) + x*KD(j, 1)*KD(l, 1) + \ + x**2*KD(i, 1)*KD(j, 2)*KD(l, 2) + x**2*KD(j, 1)*KD(i, 2)*KD(l, 2) + assert dp(x*(KD(i, k) + KD(j, k)), (k, l, 3)) == KD(l, 4) + \ + x*KD(i, 3)*KD(l, 3) + x*KD(j, 3)*KD(l, 3) + \ + x**2*KD(i, 2)*KD(j, 3)*KD(l, 2) + x**2*KD(i, 3)*KD(j, 2)*KD(l, 2) + assert dp(x*(KD(i, k) + KD(j, k)), (k, l, m)) == KD(l, m + 1) + \ + x*KD(i, m)*KD(l, m) + x*KD(j, m)*KD(l, m) + \ + x**2*KD(i, m - 1)*KD(j, m)*KD(l, m - 1) + \ + x**2*KD(i, m)*KD(j, m - 1)*KD(l, m - 1) + + +def test_deltaproduct_mul_add_x_y_add_kd_kd(): + assert dp((x + y)*(KD(i, k) + KD(j, k)), (k, 1, 3)) == 0 + assert dp((x + y)*(KD(i, k) + KD(j, k)), (k, 1, 1)) == \ + (x + y)*(KD(i, 1) + KD(j, 1)) + assert dp((x + y)*(KD(i, k) + KD(j, k)), (k, 2, 2)) == \ + (x + y)*(KD(i, 2) + KD(j, 2)) + assert dp((x + y)*(KD(i, k) + KD(j, k)), (k, 3, 3)) == \ + (x + y)*(KD(i, 3) + KD(j, 3)) + assert dp((x + y)*(KD(i, k) + KD(j, k)), (k, 1, l)) == KD(l, 0) + \ + (x + y)*KD(i, 1)*KD(l, 1) + (x + y)*KD(j, 1)*KD(l, 1) + \ + (x + y)**2*KD(i, 1)*KD(j, 2)*KD(l, 2) + \ + (x + y)**2*KD(j, 1)*KD(i, 2)*KD(l, 2) + assert dp((x + y)*(KD(i, k) + KD(j, k)), (k, l, 3)) == KD(l, 4) + \ + (x + y)*KD(i, 3)*KD(l, 3) + (x + y)*KD(j, 3)*KD(l, 3) + \ + (x + y)**2*KD(i, 2)*KD(j, 3)*KD(l, 2) + \ + (x + y)**2*KD(i, 3)*KD(j, 2)*KD(l, 2) + assert dp((x + y)*(KD(i, k) + KD(j, k)), (k, l, m)) == KD(l, m + 1) + \ + (x + y)*KD(i, m)*KD(l, m) + (x + y)*KD(j, m)*KD(l, m) + \ + (x + y)**2*KD(i, m - 1)*KD(j, m)*KD(l, m - 1) + \ + (x + y)**2*KD(i, m)*KD(j, m - 1)*KD(l, m - 1) + + +def test_deltaproduct_add_mul_x_y_mul_x_kd(): + assert dp(x*y + x*KD(i, j), (j, 1, 3)) == (x*y)**3 + \ + x*(x*y)**2*KD(i, 1) + (x*y)*x*(x*y)*KD(i, 2) + (x*y)**2*x*KD(i, 3) + assert dp(x*y + x*KD(i, j), (j, 1, 1)) == x*y + x*KD(i, 1) + assert dp(x*y + x*KD(i, j), (j, 2, 2)) == x*y + x*KD(i, 2) + assert dp(x*y + x*KD(i, j), (j, 3, 3)) == x*y + x*KD(i, 3) + assert dp(x*y + x*KD(i, j), (j, 1, k)) == \ + (x*y)**k + Piecewise( + ((x*y)**(i - 1)*x*(x*y)**(k - i), And(1 <= i, i <= k)), + (0, True) + ) + assert dp(x*y + x*KD(i, j), (j, k, 3)) == \ + (x*y)**(-k + 4) + Piecewise( + ((x*y)**(i - k)*x*(x*y)**(3 - i), And(k <= i, i <= 3)), + (0, True) + ) + assert dp(x*y + x*KD(i, j), (j, k, l)) == \ + (x*y)**(-k + l + 1) + Piecewise( + ((x*y)**(i - k)*x*(x*y)**(l - i), And(k <= i, i <= l)), + (0, True) + ) + + +def test_deltaproduct_mul_x_add_y_kd(): + assert dp(x*(y + KD(i, j)), (j, 1, 3)) == (x*y)**3 + \ + x*(x*y)**2*KD(i, 1) + (x*y)*x*(x*y)*KD(i, 2) + (x*y)**2*x*KD(i, 3) + assert dp(x*(y + KD(i, j)), (j, 1, 1)) == x*(y + KD(i, 1)) + assert dp(x*(y + KD(i, j)), (j, 2, 2)) == x*(y + KD(i, 2)) + assert dp(x*(y + KD(i, j)), (j, 3, 3)) == x*(y + KD(i, 3)) + assert dp(x*(y + KD(i, j)), (j, 1, k)) == \ + (x*y)**k + Piecewise( + ((x*y)**(i - 1)*x*(x*y)**(k - i), And(1 <= i, i <= k)), + (0, True) + ).expand() + assert dp(x*(y + KD(i, j)), (j, k, 3)) == \ + ((x*y)**(-k + 4) + Piecewise( + ((x*y)**(i - k)*x*(x*y)**(3 - i), And(k <= i, i <= 3)), + (0, True) + )).expand() + assert dp(x*(y + KD(i, j)), (j, k, l)) == \ + ((x*y)**(-k + l + 1) + Piecewise( + ((x*y)**(i - k)*x*(x*y)**(l - i), And(k <= i, i <= l)), + (0, True) + )).expand() + + +def test_deltaproduct_mul_x_add_y_twokd(): + assert dp(x*(y + 2*KD(i, j)), (j, 1, 3)) == (x*y)**3 + \ + 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) + assert dp(x*(y + 2*KD(i, j)), (j, 1, 1)) == x*(y + 2*KD(i, 1)) + assert dp(x*(y + 2*KD(i, j)), (j, 2, 2)) == x*(y + 2*KD(i, 2)) + assert dp(x*(y + 2*KD(i, j)), (j, 3, 3)) == x*(y + 2*KD(i, 3)) + assert dp(x*(y + 2*KD(i, j)), (j, 1, k)) == \ + (x*y)**k + Piecewise( + (2*(x*y)**(i - 1)*x*(x*y)**(k - i), And(1 <= i, i <= k)), + (0, True) + ).expand() + assert dp(x*(y + 2*KD(i, j)), (j, k, 3)) == \ + ((x*y)**(-k + 4) + Piecewise( + (2*(x*y)**(i - k)*x*(x*y)**(3 - i), And(k <= i, i <= 3)), + (0, True) + )).expand() + assert dp(x*(y + 2*KD(i, j)), (j, k, l)) == \ + ((x*y)**(-k + l + 1) + Piecewise( + (2*(x*y)**(i - k)*x*(x*y)**(l - i), And(k <= i, i <= l)), + (0, True) + )).expand() + + +def test_deltaproduct_mul_add_x_y_add_y_kd(): + assert dp((x + y)*(y + KD(i, j)), (j, 1, 3)) == ((x + y)*y)**3 + \ + (x + y)*((x + y)*y)**2*KD(i, 1) + \ + (x + y)*y*(x + y)**2*y*KD(i, 2) + \ + ((x + y)*y)**2*(x + y)*KD(i, 3) + assert dp((x + y)*(y + KD(i, j)), (j, 1, 1)) == (x + y)*(y + KD(i, 1)) + assert dp((x + y)*(y + KD(i, j)), (j, 2, 2)) == (x + y)*(y + KD(i, 2)) + assert dp((x + y)*(y + KD(i, j)), (j, 3, 3)) == (x + y)*(y + KD(i, 3)) + assert dp((x + y)*(y + KD(i, j)), (j, 1, k)) == \ + ((x + y)*y)**k + Piecewise( + (((x + y)*y)**(-1)*((x + y)*y)**i*(x + y)*((x + y)*y + )**k*((x + y)*y)**(-i), (i >= 1) & (i <= k)), (0, True)) + assert dp((x + y)*(y + KD(i, j)), (j, k, 3)) == ( + (x + y)*y)**4*((x + y)*y)**(-k) + Piecewise((((x + y)*y)**i*( + (x + y)*y)**(-k)*(x + y)*((x + y)*y)**3*((x + y)*y)**(-i), + (i >= k) & (i <= 3)), (0, True)) + assert dp((x + y)*(y + KD(i, j)), (j, k, l)) == \ + (x + y)*y*((x + y)*y)**l*((x + y)*y)**(-k) + Piecewise( + (((x + y)*y)**i*((x + y)*y)**(-k)*(x + y)*((x + y)*y + )**l*((x + y)*y)**(-i), (i >= k) & (i <= l)), (0, True)) + + +def test_deltaproduct_mul_add_x_kd_add_y_kd(): + assert dp((x + KD(i, k))*(y + KD(i, j)), (j, 1, 3)) == \ + KD(i, 1)*(KD(i, k) + x)*((KD(i, k) + x)*y)**2 + \ + KD(i, 2)*(KD(i, k) + x)*y*(KD(i, k) + x)**2*y + \ + KD(i, 3)*((KD(i, k) + x)*y)**2*(KD(i, k) + x) + \ + ((KD(i, k) + x)*y)**3 + assert dp((x + KD(i, k))*(y + KD(i, j)), (j, 1, 1)) == \ + (x + KD(i, k))*(y + KD(i, 1)) + assert dp((x + KD(i, k))*(y + KD(i, j)), (j, 2, 2)) == \ + (x + KD(i, k))*(y + KD(i, 2)) + assert dp((x + KD(i, k))*(y + KD(i, j)), (j, 3, 3)) == \ + (x + KD(i, k))*(y + KD(i, 3)) + assert dp((x + KD(i, k))*(y + KD(i, j)), (j, 1, k)) == \ + ((KD(i, k) + x)*y)**k + Piecewise( + (((KD(i, k) + x)*y)**(-1)*((KD(i, k) + x)*y)**i*(KD(i, k) + x + )*((KD(i, k) + x)*y)**k*((KD(i, k) + x)*y)**(-i), (i >= 1 + ) & (i <= k)), (0, True)) + assert dp((x + KD(i, k))*(y + KD(i, j)), (j, k, 3)) == ( + (KD(i, k) + x)*y)**4*((KD(i, k) + x)*y)**(-k) + Piecewise( + (((KD(i, k) + x)*y)**i*((KD(i, k) + x)*y)**(-k)*(KD(i, k) + + x)*((KD(i, k) + x)*y)**3*((KD(i, k) + x)*y)**(-i), + (i >= k) & (i <= 3)), (0, True)) + assert dp((x + KD(i, k))*(y + KD(i, j)), (j, k, l)) == ( + KD(i, k) + x)*y*((KD(i, k) + x)*y)**l*((KD(i, k) + x)*y + )**(-k) + Piecewise((((KD(i, k) + x)*y)**i*((KD(i, k) + x + )*y)**(-k)*(KD(i, k) + x)*((KD(i, k) + x)*y)**l*((KD(i, k) + x + )*y)**(-i), (i >= k) & (i <= l)), (0, True)) + + +def test_deltasummation_trivial(): + assert ds(x, (j, 1, 0)) == 0 + assert ds(x, (j, 1, 3)) == 3*x + assert ds(x + y, (j, 1, 3)) == 3*(x + y) + assert ds(x*y, (j, 1, 3)) == 3*x*y + assert ds(KD(i, j), (k, 1, 3)) == 3*KD(i, j) + assert ds(x*KD(i, j), (k, 1, 3)) == 3*x*KD(i, j) + assert ds(x*y*KD(i, j), (k, 1, 3)) == 3*x*y*KD(i, j) + + +def test_deltasummation_basic_numerical(): + n = symbols('n', integer=True, nonzero=True) + assert ds(KD(n, 0), (n, 1, 3)) == 0 + + # return unevaluated, until it gets implemented + assert ds(KD(i**2, j**2), (j, -oo, oo)) == \ + Sum(KD(i**2, j**2), (j, -oo, oo)) + + assert Piecewise((KD(i, k), And(1 <= i, i <= 3)), (0, True)) == \ + ds(KD(i, j)*KD(j, k), (j, 1, 3)) == \ + ds(KD(j, k)*KD(i, j), (j, 1, 3)) + + assert ds(KD(i, k), (k, -oo, oo)) == 1 + assert ds(KD(i, k), (k, 0, oo)) == Piecewise((1, S.Zero <= i), (0, True)) + assert ds(KD(i, k), (k, 1, 3)) == \ + Piecewise((1, And(1 <= i, i <= 3)), (0, True)) + assert ds(k*KD(i, j)*KD(j, k), (k, -oo, oo)) == j*KD(i, j) + assert ds(j*KD(i, j), (j, -oo, oo)) == i + assert ds(i*KD(i, j), (i, -oo, oo)) == j + assert ds(x, (i, 1, 3)) == 3*x + assert ds((i + j)*KD(i, j), (j, -oo, oo)) == 2*i + + +def test_deltasummation_basic_symbolic(): + assert ds(KD(i, j), (j, 1, 3)) == \ + Piecewise((1, And(1 <= i, i <= 3)), (0, True)) + assert ds(KD(i, j), (j, 1, 1)) == Piecewise((1, Eq(i, 1)), (0, True)) + assert ds(KD(i, j), (j, 2, 2)) == Piecewise((1, Eq(i, 2)), (0, True)) + assert ds(KD(i, j), (j, 3, 3)) == Piecewise((1, Eq(i, 3)), (0, True)) + assert ds(KD(i, j), (j, 1, k)) == \ + Piecewise((1, And(1 <= i, i <= k)), (0, True)) + assert ds(KD(i, j), (j, k, 3)) == \ + Piecewise((1, And(k <= i, i <= 3)), (0, True)) + assert ds(KD(i, j), (j, k, l)) == \ + Piecewise((1, And(k <= i, i <= l)), (0, True)) + + +def test_deltasummation_mul_x_kd(): + assert ds(x*KD(i, j), (j, 1, 3)) == \ + Piecewise((x, And(1 <= i, i <= 3)), (0, True)) + assert ds(x*KD(i, j), (j, 1, 1)) == Piecewise((x, Eq(i, 1)), (0, True)) + assert ds(x*KD(i, j), (j, 2, 2)) == Piecewise((x, Eq(i, 2)), (0, True)) + assert ds(x*KD(i, j), (j, 3, 3)) == Piecewise((x, Eq(i, 3)), (0, True)) + assert ds(x*KD(i, j), (j, 1, k)) == \ + Piecewise((x, And(1 <= i, i <= k)), (0, True)) + assert ds(x*KD(i, j), (j, k, 3)) == \ + Piecewise((x, And(k <= i, i <= 3)), (0, True)) + assert ds(x*KD(i, j), (j, k, l)) == \ + Piecewise((x, And(k <= i, i <= l)), (0, True)) + + +def test_deltasummation_mul_add_x_y_kd(): + assert ds((x + y)*KD(i, j), (j, 1, 3)) == \ + Piecewise((x + y, And(1 <= i, i <= 3)), (0, True)) + assert ds((x + y)*KD(i, j), (j, 1, 1)) == \ + Piecewise((x + y, Eq(i, 1)), (0, True)) + assert ds((x + y)*KD(i, j), (j, 2, 2)) == \ + Piecewise((x + y, Eq(i, 2)), (0, True)) + assert ds((x + y)*KD(i, j), (j, 3, 3)) == \ + Piecewise((x + y, Eq(i, 3)), (0, True)) + assert ds((x + y)*KD(i, j), (j, 1, k)) == \ + Piecewise((x + y, And(1 <= i, i <= k)), (0, True)) + assert ds((x + y)*KD(i, j), (j, k, 3)) == \ + Piecewise((x + y, And(k <= i, i <= 3)), (0, True)) + assert ds((x + y)*KD(i, j), (j, k, l)) == \ + Piecewise((x + y, And(k <= i, i <= l)), (0, True)) + + +def test_deltasummation_add_kd_kd(): + assert ds(KD(i, k) + KD(j, k), (k, 1, 3)) == piecewise_fold( + Piecewise((1, And(1 <= i, i <= 3)), (0, True)) + + Piecewise((1, And(1 <= j, j <= 3)), (0, True))) + assert ds(KD(i, k) + KD(j, k), (k, 1, 1)) == piecewise_fold( + Piecewise((1, Eq(i, 1)), (0, True)) + + Piecewise((1, Eq(j, 1)), (0, True))) + assert ds(KD(i, k) + KD(j, k), (k, 2, 2)) == piecewise_fold( + Piecewise((1, Eq(i, 2)), (0, True)) + + Piecewise((1, Eq(j, 2)), (0, True))) + assert ds(KD(i, k) + KD(j, k), (k, 3, 3)) == piecewise_fold( + Piecewise((1, Eq(i, 3)), (0, True)) + + Piecewise((1, Eq(j, 3)), (0, True))) + assert ds(KD(i, k) + KD(j, k), (k, 1, l)) == piecewise_fold( + Piecewise((1, And(1 <= i, i <= l)), (0, True)) + + Piecewise((1, And(1 <= j, j <= l)), (0, True))) + assert ds(KD(i, k) + KD(j, k), (k, l, 3)) == piecewise_fold( + Piecewise((1, And(l <= i, i <= 3)), (0, True)) + + Piecewise((1, And(l <= j, j <= 3)), (0, True))) + assert ds(KD(i, k) + KD(j, k), (k, l, m)) == piecewise_fold( + Piecewise((1, And(l <= i, i <= m)), (0, True)) + + Piecewise((1, And(l <= j, j <= m)), (0, True))) + + +def test_deltasummation_add_mul_x_kd_kd(): + assert ds(x*KD(i, k) + KD(j, k), (k, 1, 3)) == piecewise_fold( + Piecewise((x, And(1 <= i, i <= 3)), (0, True)) + + Piecewise((1, And(1 <= j, j <= 3)), (0, True))) + assert ds(x*KD(i, k) + KD(j, k), (k, 1, 1)) == piecewise_fold( + Piecewise((x, Eq(i, 1)), (0, True)) + + Piecewise((1, Eq(j, 1)), (0, True))) + assert ds(x*KD(i, k) + KD(j, k), (k, 2, 2)) == piecewise_fold( + Piecewise((x, Eq(i, 2)), (0, True)) + + Piecewise((1, Eq(j, 2)), (0, True))) + assert ds(x*KD(i, k) + KD(j, k), (k, 3, 3)) == piecewise_fold( + Piecewise((x, Eq(i, 3)), (0, True)) + + Piecewise((1, Eq(j, 3)), (0, True))) + assert ds(x*KD(i, k) + KD(j, k), (k, 1, l)) == piecewise_fold( + Piecewise((x, And(1 <= i, i <= l)), (0, True)) + + Piecewise((1, And(1 <= j, j <= l)), (0, True))) + assert ds(x*KD(i, k) + KD(j, k), (k, l, 3)) == piecewise_fold( + Piecewise((x, And(l <= i, i <= 3)), (0, True)) + + Piecewise((1, And(l <= j, j <= 3)), (0, True))) + assert ds(x*KD(i, k) + KD(j, k), (k, l, m)) == piecewise_fold( + Piecewise((x, And(l <= i, i <= m)), (0, True)) + + Piecewise((1, And(l <= j, j <= m)), (0, True))) + + +def test_deltasummation_mul_x_add_kd_kd(): + assert ds(x*(KD(i, k) + KD(j, k)), (k, 1, 3)) == piecewise_fold( + Piecewise((x, And(1 <= i, i <= 3)), (0, True)) + + Piecewise((x, And(1 <= j, j <= 3)), (0, True))) + assert ds(x*(KD(i, k) + KD(j, k)), (k, 1, 1)) == piecewise_fold( + Piecewise((x, Eq(i, 1)), (0, True)) + + Piecewise((x, Eq(j, 1)), (0, True))) + assert ds(x*(KD(i, k) + KD(j, k)), (k, 2, 2)) == piecewise_fold( + Piecewise((x, Eq(i, 2)), (0, True)) + + Piecewise((x, Eq(j, 2)), (0, True))) + assert ds(x*(KD(i, k) + KD(j, k)), (k, 3, 3)) == piecewise_fold( + Piecewise((x, Eq(i, 3)), (0, True)) + + Piecewise((x, Eq(j, 3)), (0, True))) + assert ds(x*(KD(i, k) + KD(j, k)), (k, 1, l)) == piecewise_fold( + Piecewise((x, And(1 <= i, i <= l)), (0, True)) + + Piecewise((x, And(1 <= j, j <= l)), (0, True))) + assert ds(x*(KD(i, k) + KD(j, k)), (k, l, 3)) == piecewise_fold( + Piecewise((x, And(l <= i, i <= 3)), (0, True)) + + Piecewise((x, And(l <= j, j <= 3)), (0, True))) + assert ds(x*(KD(i, k) + KD(j, k)), (k, l, m)) == piecewise_fold( + Piecewise((x, And(l <= i, i <= m)), (0, True)) + + Piecewise((x, And(l <= j, j <= m)), (0, True))) + + +def test_deltasummation_mul_add_x_y_add_kd_kd(): + assert ds((x + y)*(KD(i, k) + KD(j, k)), (k, 1, 3)) == piecewise_fold( + Piecewise((x + y, And(1 <= i, i <= 3)), (0, True)) + + Piecewise((x + y, And(1 <= j, j <= 3)), (0, True))) + assert ds((x + y)*(KD(i, k) + KD(j, k)), (k, 1, 1)) == piecewise_fold( + Piecewise((x + y, Eq(i, 1)), (0, True)) + + Piecewise((x + y, Eq(j, 1)), (0, True))) + assert ds((x + y)*(KD(i, k) + KD(j, k)), (k, 2, 2)) == piecewise_fold( + Piecewise((x + y, Eq(i, 2)), (0, True)) + + Piecewise((x + y, Eq(j, 2)), (0, True))) + assert ds((x + y)*(KD(i, k) + KD(j, k)), (k, 3, 3)) == piecewise_fold( + Piecewise((x + y, Eq(i, 3)), (0, True)) + + Piecewise((x + y, Eq(j, 3)), (0, True))) + assert ds((x + y)*(KD(i, k) + KD(j, k)), (k, 1, l)) == piecewise_fold( + Piecewise((x + y, And(1 <= i, i <= l)), (0, True)) + + Piecewise((x + y, And(1 <= j, j <= l)), (0, True))) + assert ds((x + y)*(KD(i, k) + KD(j, k)), (k, l, 3)) == piecewise_fold( + Piecewise((x + y, And(l <= i, i <= 3)), (0, True)) + + Piecewise((x + y, And(l <= j, j <= 3)), (0, True))) + assert ds((x + y)*(KD(i, k) + KD(j, k)), (k, l, m)) == piecewise_fold( + Piecewise((x + y, And(l <= i, i <= m)), (0, True)) + + Piecewise((x + y, And(l <= j, j <= m)), (0, True))) + + +def test_deltasummation_add_mul_x_y_mul_x_kd(): + assert ds(x*y + x*KD(i, j), (j, 1, 3)) == \ + Piecewise((3*x*y + x, And(1 <= i, i <= 3)), (3*x*y, True)) + assert ds(x*y + x*KD(i, j), (j, 1, 1)) == \ + Piecewise((x*y + x, Eq(i, 1)), (x*y, True)) + assert ds(x*y + x*KD(i, j), (j, 2, 2)) == \ + Piecewise((x*y + x, Eq(i, 2)), (x*y, True)) + assert ds(x*y + x*KD(i, j), (j, 3, 3)) == \ + Piecewise((x*y + x, Eq(i, 3)), (x*y, True)) + assert ds(x*y + x*KD(i, j), (j, 1, k)) == \ + Piecewise((k*x*y + x, And(1 <= i, i <= k)), (k*x*y, True)) + assert ds(x*y + x*KD(i, j), (j, k, 3)) == \ + Piecewise(((4 - k)*x*y + x, And(k <= i, i <= 3)), ((4 - k)*x*y, True)) + assert ds(x*y + x*KD(i, j), (j, k, l)) == Piecewise( + ((l - k + 1)*x*y + x, And(k <= i, i <= l)), ((l - k + 1)*x*y, True)) + + +def test_deltasummation_mul_x_add_y_kd(): + assert ds(x*(y + KD(i, j)), (j, 1, 3)) == \ + Piecewise((3*x*y + x, And(1 <= i, i <= 3)), (3*x*y, True)) + assert ds(x*(y + KD(i, j)), (j, 1, 1)) == \ + Piecewise((x*y + x, Eq(i, 1)), (x*y, True)) + assert ds(x*(y + KD(i, j)), (j, 2, 2)) == \ + Piecewise((x*y + x, Eq(i, 2)), (x*y, True)) + assert ds(x*(y + KD(i, j)), (j, 3, 3)) == \ + Piecewise((x*y + x, Eq(i, 3)), (x*y, True)) + assert ds(x*(y + KD(i, j)), (j, 1, k)) == \ + Piecewise((k*x*y + x, And(1 <= i, i <= k)), (k*x*y, True)) + assert ds(x*(y + KD(i, j)), (j, k, 3)) == \ + Piecewise(((4 - k)*x*y + x, And(k <= i, i <= 3)), ((4 - k)*x*y, True)) + assert ds(x*(y + KD(i, j)), (j, k, l)) == Piecewise( + ((l - k + 1)*x*y + x, And(k <= i, i <= l)), ((l - k + 1)*x*y, True)) + + +def test_deltasummation_mul_x_add_y_twokd(): + assert ds(x*(y + 2*KD(i, j)), (j, 1, 3)) == \ + Piecewise((3*x*y + 2*x, And(1 <= i, i <= 3)), (3*x*y, True)) + assert ds(x*(y + 2*KD(i, j)), (j, 1, 1)) == \ + Piecewise((x*y + 2*x, Eq(i, 1)), (x*y, True)) + assert ds(x*(y + 2*KD(i, j)), (j, 2, 2)) == \ + Piecewise((x*y + 2*x, Eq(i, 2)), (x*y, True)) + assert ds(x*(y + 2*KD(i, j)), (j, 3, 3)) == \ + Piecewise((x*y + 2*x, Eq(i, 3)), (x*y, True)) + assert ds(x*(y + 2*KD(i, j)), (j, 1, k)) == \ + Piecewise((k*x*y + 2*x, And(1 <= i, i <= k)), (k*x*y, True)) + assert ds(x*(y + 2*KD(i, j)), (j, k, 3)) == Piecewise( + ((4 - k)*x*y + 2*x, And(k <= i, i <= 3)), ((4 - k)*x*y, True)) + assert ds(x*(y + 2*KD(i, j)), (j, k, l)) == Piecewise( + ((l - k + 1)*x*y + 2*x, And(k <= i, i <= l)), ((l - k + 1)*x*y, True)) + + +def test_deltasummation_mul_add_x_y_add_y_kd(): + assert ds((x + y)*(y + KD(i, j)), (j, 1, 3)) == Piecewise( + (3*(x + y)*y + x + y, And(1 <= i, i <= 3)), (3*(x + y)*y, True)) + assert ds((x + y)*(y + KD(i, j)), (j, 1, 1)) == \ + Piecewise(((x + y)*y + x + y, Eq(i, 1)), ((x + y)*y, True)) + assert ds((x + y)*(y + KD(i, j)), (j, 2, 2)) == \ + Piecewise(((x + y)*y + x + y, Eq(i, 2)), ((x + y)*y, True)) + assert ds((x + y)*(y + KD(i, j)), (j, 3, 3)) == \ + Piecewise(((x + y)*y + x + y, Eq(i, 3)), ((x + y)*y, True)) + assert ds((x + y)*(y + KD(i, j)), (j, 1, k)) == Piecewise( + (k*(x + y)*y + x + y, And(1 <= i, i <= k)), (k*(x + y)*y, True)) + assert ds((x + y)*(y + KD(i, j)), (j, k, 3)) == Piecewise( + ((4 - k)*(x + y)*y + x + y, And(k <= i, i <= 3)), + ((4 - k)*(x + y)*y, True)) + assert ds((x + y)*(y + KD(i, j)), (j, k, l)) == Piecewise( + ((l - k + 1)*(x + y)*y + x + y, And(k <= i, i <= l)), + ((l - k + 1)*(x + y)*y, True)) + + +def test_deltasummation_mul_add_x_kd_add_y_kd(): + assert ds((x + KD(i, k))*(y + KD(i, j)), (j, 1, 3)) == piecewise_fold( + Piecewise((KD(i, k) + x, And(1 <= i, i <= 3)), (0, True)) + + 3*(KD(i, k) + x)*y) + assert ds((x + KD(i, k))*(y + KD(i, j)), (j, 1, 1)) == piecewise_fold( + Piecewise((KD(i, k) + x, Eq(i, 1)), (0, True)) + + (KD(i, k) + x)*y) + assert ds((x + KD(i, k))*(y + KD(i, j)), (j, 2, 2)) == piecewise_fold( + Piecewise((KD(i, k) + x, Eq(i, 2)), (0, True)) + + (KD(i, k) + x)*y) + assert ds((x + KD(i, k))*(y + KD(i, j)), (j, 3, 3)) == piecewise_fold( + Piecewise((KD(i, k) + x, Eq(i, 3)), (0, True)) + + (KD(i, k) + x)*y) + assert ds((x + KD(i, k))*(y + KD(i, j)), (j, 1, k)) == piecewise_fold( + Piecewise((KD(i, k) + x, And(1 <= i, i <= k)), (0, True)) + + k*(KD(i, k) + x)*y) + assert ds((x + KD(i, k))*(y + KD(i, j)), (j, k, 3)) == piecewise_fold( + Piecewise((KD(i, k) + x, And(k <= i, i <= 3)), (0, True)) + + (4 - k)*(KD(i, k) + x)*y) + assert ds((x + KD(i, k))*(y + KD(i, j)), (j, k, l)) == piecewise_fold( + Piecewise((KD(i, k) + x, And(k <= i, i <= l)), (0, True)) + + (l - k + 1)*(KD(i, k) + x)*y) + + +def test_extract_delta(): + raises(ValueError, lambda: _extract_delta(KD(i, j) + KD(k, l), i)) diff --git a/.venv/lib/python3.13/site-packages/sympy/concrete/tests/test_gosper.py b/.venv/lib/python3.13/site-packages/sympy/concrete/tests/test_gosper.py new file mode 100644 index 0000000000000000000000000000000000000000..77b642a9b7cd55f96840a8e20e517206b6a6f8f0 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/concrete/tests/test_gosper.py @@ -0,0 +1,204 @@ +"""Tests for Gosper's algorithm for hypergeometric summation. """ + +from sympy.core.numbers import (Rational, pi) +from sympy.core.singleton import S +from sympy.core.symbol import Symbol +from sympy.functions.combinatorial.factorials import (binomial, factorial) +from sympy.functions.elementary.exponential import (exp, log) +from sympy.functions.elementary.miscellaneous import sqrt +from sympy.functions.special.gamma_functions import gamma +from sympy.polys.polytools import Poly +from sympy.simplify.simplify import simplify +from sympy.concrete.gosper import gosper_normal, gosper_sum, gosper_term +from sympy.abc import a, b, j, k, m, n, r, x + + +def test_gosper_normal(): + eq = 4*n + 5, 2*(4*n + 1)*(2*n + 3), n + assert gosper_normal(*eq) == \ + (Poly(Rational(1, 4), n), Poly(n + Rational(3, 2)), Poly(n + Rational(1, 4))) + assert gosper_normal(*eq, polys=False) == \ + (Rational(1, 4), n + Rational(3, 2), n + Rational(1, 4)) + + +def test_gosper_term(): + assert gosper_term((4*k + 1)*factorial( + k)/factorial(2*k + 1), k) == (-k - S.Half)/(k + Rational(1, 4)) + + +def test_gosper_sum(): + assert gosper_sum(1, (k, 0, n)) == 1 + n + assert gosper_sum(k, (k, 0, n)) == n*(1 + n)/2 + assert gosper_sum(k**2, (k, 0, n)) == n*(1 + n)*(1 + 2*n)/6 + assert gosper_sum(k**3, (k, 0, n)) == n**2*(1 + n)**2/4 + + assert gosper_sum(2**k, (k, 0, n)) == 2*2**n - 1 + + assert gosper_sum(factorial(k), (k, 0, n)) is None + assert gosper_sum(binomial(n, k), (k, 0, n)) is None + + assert gosper_sum(factorial(k)/k**2, (k, 0, n)) is None + assert gosper_sum((k - 3)*factorial(k), (k, 0, n)) is None + + assert gosper_sum(k*factorial(k), k) == factorial(k) + assert gosper_sum( + k*factorial(k), (k, 0, n)) == n*factorial(n) + factorial(n) - 1 + + assert gosper_sum((-1)**k*binomial(n, k), (k, 0, n)) == 0 + assert gosper_sum(( + -1)**k*binomial(n, k), (k, 0, m)) == -(-1)**m*(m - n)*binomial(n, m)/n + + assert gosper_sum((4*k + 1)*factorial(k)/factorial(2*k + 1), (k, 0, n)) == \ + (2*factorial(2*n + 1) - factorial(n))/factorial(2*n + 1) + + # issue 6033: + assert gosper_sum( + n*(n + a + b)*a**n*b**n/(factorial(n + a)*factorial(n + b)), \ + (n, 0, m)).simplify() == -exp(m*log(a) + m*log(b))*gamma(a + 1) \ + *gamma(b + 1)/(gamma(a)*gamma(b)*gamma(a + m + 1)*gamma(b + m + 1)) \ + + 1/(gamma(a)*gamma(b)) + + +def test_gosper_sum_indefinite(): + assert gosper_sum(k, k) == k*(k - 1)/2 + assert gosper_sum(k**2, k) == k*(k - 1)*(2*k - 1)/6 + + assert gosper_sum(1/(k*(k + 1)), k) == -1/k + assert gosper_sum(-(27*k**4 + 158*k**3 + 430*k**2 + 678*k + 445)*gamma(2*k + + 4)/(3*(3*k + 7)*gamma(3*k + 6)), k) == \ + (3*k + 5)*(k**2 + 2*k + 5)*gamma(2*k + 4)/gamma(3*k + 6) + + +def test_gosper_sum_parametric(): + assert gosper_sum(binomial(S.Half, m - j + 1)*binomial(S.Half, m + j), (j, 1, n)) == \ + n*(1 + m - n)*(-1 + 2*m + 2*n)*binomial(S.Half, 1 + m - n)* \ + binomial(S.Half, m + n)/(m*(1 + 2*m)) + + +def test_gosper_sum_algebraic(): + assert gosper_sum( + n**2 + sqrt(2), (n, 0, m)) == (m + 1)*(2*m**2 + m + 6*sqrt(2))/6 + + +def test_gosper_sum_iterated(): + f1 = binomial(2*k, k)/4**k + f2 = (1 + 2*n)*binomial(2*n, n)/4**n + f3 = (1 + 2*n)*(3 + 2*n)*binomial(2*n, n)/(3*4**n) + f4 = (1 + 2*n)*(3 + 2*n)*(5 + 2*n)*binomial(2*n, n)/(15*4**n) + f5 = (1 + 2*n)*(3 + 2*n)*(5 + 2*n)*(7 + 2*n)*binomial(2*n, n)/(105*4**n) + + assert gosper_sum(f1, (k, 0, n)) == f2 + assert gosper_sum(f2, (n, 0, n)) == f3 + assert gosper_sum(f3, (n, 0, n)) == f4 + assert gosper_sum(f4, (n, 0, n)) == f5 + +# the AeqB tests test expressions given in +# www.math.upenn.edu/~wilf/AeqB.pdf + + +def test_gosper_sum_AeqB_part1(): + f1a = n**4 + f1b = n**3*2**n + f1c = 1/(n**2 + sqrt(5)*n - 1) + f1d = n**4*4**n/binomial(2*n, n) + f1e = factorial(3*n)/(factorial(n)*factorial(n + 1)*factorial(n + 2)*27**n) + f1f = binomial(2*n, n)**2/((n + 1)*4**(2*n)) + f1g = (4*n - 1)*binomial(2*n, n)**2/((2*n - 1)**2*4**(2*n)) + f1h = n*factorial(n - S.Half)**2/factorial(n + 1)**2 + + g1a = m*(m + 1)*(2*m + 1)*(3*m**2 + 3*m - 1)/30 + g1b = 26 + 2**(m + 1)*(m**3 - 3*m**2 + 9*m - 13) + g1c = (m + 1)*(m*(m**2 - 7*m + 3)*sqrt(5) - ( + 3*m**3 - 7*m**2 + 19*m - 6))/(2*m**3*sqrt(5) + m**4 + 5*m**2 - 1)/6 + g1d = Rational(-2, 231) + 2*4**m*(m + 1)*(63*m**4 + 112*m**3 + 18*m**2 - + 22*m + 3)/(693*binomial(2*m, m)) + g1e = Rational(-9, 2) + (81*m**2 + 261*m + 200)*factorial( + 3*m + 2)/(40*27**m*factorial(m)*factorial(m + 1)*factorial(m + 2)) + g1f = (2*m + 1)**2*binomial(2*m, m)**2/(4**(2*m)*(m + 1)) + g1g = -binomial(2*m, m)**2/4**(2*m) + g1h = 4*pi -(2*m + 1)**2*(3*m + 4)*factorial(m - S.Half)**2/factorial(m + 1)**2 + + g = gosper_sum(f1a, (n, 0, m)) + assert g is not None and simplify(g - g1a) == 0 + g = gosper_sum(f1b, (n, 0, m)) + assert g is not None and simplify(g - g1b) == 0 + g = gosper_sum(f1c, (n, 0, m)) + assert g is not None and simplify(g - g1c) == 0 + g = gosper_sum(f1d, (n, 0, m)) + assert g is not None and simplify(g - g1d) == 0 + g = gosper_sum(f1e, (n, 0, m)) + assert g is not None and simplify(g - g1e) == 0 + g = gosper_sum(f1f, (n, 0, m)) + assert g is not None and simplify(g - g1f) == 0 + g = gosper_sum(f1g, (n, 0, m)) + assert g is not None and simplify(g - g1g) == 0 + g = gosper_sum(f1h, (n, 0, m)) + # need to call rewrite(gamma) here because we have terms involving + # factorial(1/2) + assert g is not None and simplify(g - g1h).rewrite(gamma) == 0 + + +def test_gosper_sum_AeqB_part2(): + f2a = n**2*a**n + f2b = (n - r/2)*binomial(r, n) + f2c = factorial(n - 1)**2/(factorial(n - x)*factorial(n + x)) + + g2a = -a*(a + 1)/(a - 1)**3 + a**( + m + 1)*(a**2*m**2 - 2*a*m**2 + m**2 - 2*a*m + 2*m + a + 1)/(a - 1)**3 + g2b = (m - r)*binomial(r, m)/2 + ff = factorial(1 - x)*factorial(1 + x) + g2c = 1/ff*( + 1 - 1/x**2) + factorial(m)**2/(x**2*factorial(m - x)*factorial(m + x)) + + g = gosper_sum(f2a, (n, 0, m)) + assert g is not None and simplify(g - g2a) == 0 + g = gosper_sum(f2b, (n, 0, m)) + assert g is not None and simplify(g - g2b) == 0 + g = gosper_sum(f2c, (n, 1, m)) + assert g is not None and simplify(g - g2c) == 0 + + +def test_gosper_nan(): + a = Symbol('a', positive=True) + b = Symbol('b', positive=True) + n = Symbol('n', integer=True) + m = Symbol('m', integer=True) + f2d = n*(n + a + b)*a**n*b**n/(factorial(n + a)*factorial(n + b)) + g2d = 1/(factorial(a - 1)*factorial( + b - 1)) - a**(m + 1)*b**(m + 1)/(factorial(a + m)*factorial(b + m)) + g = gosper_sum(f2d, (n, 0, m)) + assert simplify(g - g2d) == 0 + + +def test_gosper_sum_AeqB_part3(): + f3a = 1/n**4 + f3b = (6*n + 3)/(4*n**4 + 8*n**3 + 8*n**2 + 4*n + 3) + f3c = 2**n*(n**2 - 2*n - 1)/(n**2*(n + 1)**2) + f3d = n**2*4**n/((n + 1)*(n + 2)) + f3e = 2**n/(n + 1) + f3f = 4*(n - 1)*(n**2 - 2*n - 1)/(n**2*(n + 1)**2*(n - 2)**2*(n - 3)**2) + f3g = (n**4 - 14*n**2 - 24*n - 9)*2**n/(n**2*(n + 1)**2*(n + 2)**2* + (n + 3)**2) + + # g3a -> no closed form + g3b = m*(m + 2)/(2*m**2 + 4*m + 3) + g3c = 2**m/m**2 - 2 + g3d = Rational(2, 3) + 4**(m + 1)*(m - 1)/(m + 2)/3 + # g3e -> no closed form + g3f = -(Rational(-1, 16) + 1/((m - 2)**2*(m + 1)**2)) # the AeqB key is wrong + g3g = Rational(-2, 9) + 2**(m + 1)/((m + 1)**2*(m + 3)**2) + + g = gosper_sum(f3a, (n, 1, m)) + assert g is None + g = gosper_sum(f3b, (n, 1, m)) + assert g is not None and simplify(g - g3b) == 0 + g = gosper_sum(f3c, (n, 1, m - 1)) + assert g is not None and simplify(g - g3c) == 0 + g = gosper_sum(f3d, (n, 1, m)) + assert g is not None and simplify(g - g3d) == 0 + g = gosper_sum(f3e, (n, 0, m - 1)) + assert g is None + g = gosper_sum(f3f, (n, 4, m)) + assert g is not None and simplify(g - g3f) == 0 + g = gosper_sum(f3g, (n, 1, m)) + assert g is not None and simplify(g - g3g) == 0 diff --git a/.venv/lib/python3.13/site-packages/sympy/concrete/tests/test_guess.py b/.venv/lib/python3.13/site-packages/sympy/concrete/tests/test_guess.py new file mode 100644 index 0000000000000000000000000000000000000000..5ac5d02b89ad62a70a29bd450b71b284b6aea76d --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/concrete/tests/test_guess.py @@ -0,0 +1,82 @@ +from sympy.concrete.guess import ( + find_simple_recurrence_vector, + find_simple_recurrence, + rationalize, + guess_generating_function_rational, + guess_generating_function, + guess + ) +from sympy.concrete.products import Product +from sympy.core.function import Function +from sympy.core.numbers import Rational +from sympy.core.singleton import S +from sympy.core.symbol import (Symbol, symbols) +from sympy.core.sympify import sympify +from sympy.functions.combinatorial.factorials import (RisingFactorial, factorial) +from sympy.functions.combinatorial.numbers import fibonacci +from sympy.functions.elementary.exponential import exp + + +def test_find_simple_recurrence_vector(): + assert find_simple_recurrence_vector( + [fibonacci(k) for k in range(12)]) == [1, -1, -1] + + +def test_find_simple_recurrence(): + a = Function('a') + n = Symbol('n') + assert find_simple_recurrence([fibonacci(k) for k in range(12)]) == ( + -a(n) - a(n + 1) + a(n + 2)) + + f = Function('a') + i = Symbol('n') + a = [1, 1, 1] + for k in range(15): a.append(5*a[-1]-3*a[-2]+8*a[-3]) + assert find_simple_recurrence(a, A=f, N=i) == ( + -8*f(i) + 3*f(i + 1) - 5*f(i + 2) + f(i + 3)) + assert find_simple_recurrence([0, 2, 15, 74, 12, 3, 0, + 1, 2, 85, 4, 5, 63]) == 0 + + +def test_rationalize(): + from mpmath import cos, pi, mpf + assert rationalize(cos(pi/3)) == S.Half + assert rationalize(mpf("0.333333333333333")) == Rational(1, 3) + assert rationalize(mpf("-0.333333333333333")) == Rational(-1, 3) + assert rationalize(pi, maxcoeff = 250) == Rational(355, 113) + + +def test_guess_generating_function_rational(): + x = Symbol('x') + assert guess_generating_function_rational([fibonacci(k) + for k in range(5, 15)]) == ((3*x + 5)/(-x**2 - x + 1)) + + +def test_guess_generating_function(): + x = Symbol('x') + assert guess_generating_function([fibonacci(k) + for k in range(5, 15)])['ogf'] == ((3*x + 5)/(-x**2 - x + 1)) + assert guess_generating_function( + [1, 2, 5, 14, 41, 124, 383, 1200, 3799, 12122, 38919])['ogf'] == ( + (1/(x**4 + 2*x**2 - 4*x + 1))**S.Half) + assert guess_generating_function(sympify( + "[3/2, 11/2, 0, -121/2, -363/2, 121, 4719/2, 11495/2, -8712, -178717/2]") + )['ogf'] == (x + Rational(3, 2))/(11*x**2 - 3*x + 1) + assert guess_generating_function([factorial(k) for k in range(12)], + types=['egf'])['egf'] == 1/(-x + 1) + assert guess_generating_function([k+1 for k in range(12)], + types=['egf']) == {'egf': (x + 1)*exp(x), 'lgdegf': (x + 2)/(x + 1)} + + +def test_guess(): + i0, i1 = symbols('i0 i1') + assert guess([1, 2, 6, 24, 120], evaluate=False) == [Product(i1 + 1, (i1, 1, i0 - 1))] + assert guess([1, 2, 6, 24, 120]) == [RisingFactorial(2, i0 - 1)] + assert guess([1, 2, 7, 42, 429, 7436, 218348, 10850216], niter=4) == [ + 2**(i0 - 1)*(Rational(27, 16))**(i0**2/2 - 3*i0/2 + + 1)*Product(RisingFactorial(Rational(5, 3), i1 - 1)*RisingFactorial(Rational(7, 3), i1 + - 1)/(RisingFactorial(Rational(3, 2), i1 - 1)*RisingFactorial(Rational(5, 2), i1 - + 1)), (i1, 1, i0 - 1))] + assert guess([1, 0, 2]) == [] + x, y = symbols('x y') + assert guess([1, 2, 6, 24, 120], variables=[x, y]) == [RisingFactorial(2, x - 1)] diff --git a/.venv/lib/python3.13/site-packages/sympy/concrete/tests/test_products.py b/.venv/lib/python3.13/site-packages/sympy/concrete/tests/test_products.py new file mode 100644 index 0000000000000000000000000000000000000000..9be053a7040014c6ed38c1279a609fcb2426258e --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/concrete/tests/test_products.py @@ -0,0 +1,410 @@ +from sympy.concrete.products import (Product, product) +from sympy.concrete.summations import Sum +from sympy.core.function import (Derivative, Function, diff) +from sympy.core.numbers import (Rational, oo, pi) +from sympy.core.singleton import S +from sympy.core.symbol import (Dummy, Symbol, symbols) +from sympy.functions.combinatorial.factorials import (rf, factorial) +from sympy.functions.elementary.exponential import (exp, log) +from sympy.functions.elementary.miscellaneous import sqrt +from sympy.functions.elementary.trigonometric import (cos, sin) +from sympy.functions.special.tensor_functions import KroneckerDelta +from sympy.simplify.combsimp import combsimp +from sympy.simplify.simplify import simplify +from sympy.testing.pytest import raises + +a, k, n, m, x = symbols('a,k,n,m,x', integer=True) +f = Function('f') + + +def test_karr_convention(): + # Test the Karr product convention that we want to hold. + # See his paper "Summation in Finite Terms" for a detailed + # reasoning why we really want exactly this definition. + # The convention is described for sums on page 309 and + # essentially in section 1.4, definition 3. For products + # we can find in analogy: + # + # \prod_{m <= i < n} f(i) 'has the obvious meaning' for m < n + # \prod_{m <= i < n} f(i) = 0 for m = n + # \prod_{m <= i < n} f(i) = 1 / \prod_{n <= i < m} f(i) for m > n + # + # It is important to note that he defines all products with + # the upper limit being *exclusive*. + # In contrast, SymPy and the usual mathematical notation has: + # + # prod_{i = a}^b f(i) = f(a) * f(a+1) * ... * f(b-1) * f(b) + # + # with the upper limit *inclusive*. So translating between + # the two we find that: + # + # \prod_{m <= i < n} f(i) = \prod_{i = m}^{n-1} f(i) + # + # where we intentionally used two different ways to typeset the + # products and its limits. + + i = Symbol("i", integer=True) + k = Symbol("k", integer=True) + j = Symbol("j", integer=True, positive=True) + + # A simple example with a concrete factors and symbolic limits. + + # The normal product: m = k and n = k + j and therefore m < n: + m = k + n = k + j + + a = m + b = n - 1 + S1 = Product(i**2, (i, a, b)).doit() + + # The reversed product: m = k + j and n = k and therefore m > n: + m = k + j + n = k + + a = m + b = n - 1 + S2 = Product(i**2, (i, a, b)).doit() + + assert S1 * S2 == 1 + + # Test the empty product: m = k and n = k and therefore m = n: + m = k + n = k + + a = m + b = n - 1 + Sz = Product(i**2, (i, a, b)).doit() + + assert Sz == 1 + + # Another example this time with an unspecified factor and + # numeric limits. (We can not do both tests in the same example.) + f = Function("f") + + # The normal product with m < n: + m = 2 + n = 11 + + a = m + b = n - 1 + S1 = Product(f(i), (i, a, b)).doit() + + # The reversed product with m > n: + m = 11 + n = 2 + + a = m + b = n - 1 + S2 = Product(f(i), (i, a, b)).doit() + + assert simplify(S1 * S2) == 1 + + # Test the empty product with m = n: + m = 5 + n = 5 + + a = m + b = n - 1 + Sz = Product(f(i), (i, a, b)).doit() + + assert Sz == 1 + + +def test_karr_proposition_2a(): + # Test Karr, page 309, proposition 2, part a + i, u, v = symbols('i u v', integer=True) + + def test_the_product(m, n): + # g + g = i**3 + 2*i**2 - 3*i + # f = Delta g + f = simplify(g.subs(i, i+1) / g) + # The product + a = m + b = n - 1 + P = Product(f, (i, a, b)).doit() + # Test if Product_{m <= i < n} f(i) = g(n) / g(m) + assert combsimp(P / (g.subs(i, n) / g.subs(i, m))) == 1 + + # m < n + test_the_product(u, u + v) + # m = n + test_the_product(u, u) + # m > n + test_the_product(u + v, u) + + +def test_karr_proposition_2b(): + # Test Karr, page 309, proposition 2, part b + i, u, v, w = symbols('i u v w', integer=True) + + def test_the_product(l, n, m): + # Productmand + s = i**3 + # First product + a = l + b = n - 1 + S1 = Product(s, (i, a, b)).doit() + # Second product + a = l + b = m - 1 + S2 = Product(s, (i, a, b)).doit() + # Third product + a = m + b = n - 1 + S3 = Product(s, (i, a, b)).doit() + # Test if S1 = S2 * S3 as required + assert combsimp(S1 / (S2 * S3)) == 1 + + # l < m < n + test_the_product(u, u + v, u + v + w) + # l < m = n + test_the_product(u, u + v, u + v) + # l < m > n + test_the_product(u, u + v + w, v) + # l = m < n + test_the_product(u, u, u + v) + # l = m = n + test_the_product(u, u, u) + # l = m > n + test_the_product(u + v, u + v, u) + # l > m < n + test_the_product(u + v, u, u + w) + # l > m = n + test_the_product(u + v, u, u) + # l > m > n + test_the_product(u + v + w, u + v, u) + + +def test_simple_products(): + assert product(2, (k, a, n)) == 2**(n - a + 1) + assert product(k, (k, 1, n)) == factorial(n) + assert product(k**3, (k, 1, n)) == factorial(n)**3 + + assert product(k + 1, (k, 0, n - 1)) == factorial(n) + assert product(k + 1, (k, a, n - 1)) == rf(1 + a, n - a) + + assert product(cos(k), (k, 0, 5)) == cos(1)*cos(2)*cos(3)*cos(4)*cos(5) + assert product(cos(k), (k, 3, 5)) == cos(3)*cos(4)*cos(5) + assert product(cos(k), (k, 1, Rational(5, 2))) != cos(1)*cos(2) + + assert isinstance(product(k**k, (k, 1, n)), Product) + + assert Product(x**k, (k, 1, n)).variables == [k] + + raises(ValueError, lambda: Product(n)) + raises(ValueError, lambda: Product(n, k)) + raises(ValueError, lambda: Product(n, k, 1)) + raises(ValueError, lambda: Product(n, k, 1, 10)) + raises(ValueError, lambda: Product(n, (k, 1))) + + assert product(1, (n, 1, oo)) == 1 # issue 8301 + assert product(2, (n, 1, oo)) is oo + assert product(-1, (n, 1, oo)).func is Product + + +def test_multiple_products(): + assert product(x, (n, 1, k), (k, 1, m)) == x**(m**2/2 + m/2) + assert product(f(n), ( + n, 1, m), (m, 1, k)) == Product(f(n), (n, 1, m), (m, 1, k)).doit() + assert Product(f(n), (m, 1, k), (n, 1, k)).doit() == \ + Product(Product(f(n), (m, 1, k)), (n, 1, k)).doit() == \ + product(f(n), (m, 1, k), (n, 1, k)) == \ + product(product(f(n), (m, 1, k)), (n, 1, k)) == \ + Product(f(n)**k, (n, 1, k)) + assert Product( + x, (x, 1, k), (k, 1, n)).doit() == Product(factorial(k), (k, 1, n)) + + assert Product(x**k, (n, 1, k), (k, 1, m)).variables == [n, k] + + +def test_rational_products(): + assert product(1 + 1/k, (k, 1, n)) == rf(2, n)/factorial(n) + + +def test_special_products(): + # Wallis product + assert product((4*k)**2 / (4*k**2 - 1), (k, 1, n)) == \ + 4**n*factorial(n)**2/rf(S.Half, n)/rf(Rational(3, 2), n) + + # Euler's product formula for sin + assert product(1 + a/k**2, (k, 1, n)) == \ + rf(1 - sqrt(-a), n)*rf(1 + sqrt(-a), n)/factorial(n)**2 + + +def test__eval_product(): + from sympy.abc import i, n + # issue 4809 + a = Function('a') + assert product(2*a(i), (i, 1, n)) == 2**n * Product(a(i), (i, 1, n)) + # issue 4810 + assert product(2**i, (i, 1, n)) == 2**(n*(n + 1)/2) + k, m = symbols('k m', integer=True) + assert product(2**i, (i, k, m)) == 2**(-k**2/2 + k/2 + m**2/2 + m/2) + n = Symbol('n', negative=True, integer=True) + p = Symbol('p', positive=True, integer=True) + assert product(2**i, (i, n, p)) == 2**(-n**2/2 + n/2 + p**2/2 + p/2) + assert product(2**i, (i, p, n)) == 2**(n**2/2 + n/2 - p**2/2 + p/2) + + +def test_product_pow(): + # issue 4817 + assert product(2**f(k), (k, 1, n)) == 2**Sum(f(k), (k, 1, n)) + assert product(2**(2*f(k)), (k, 1, n)) == 2**Sum(2*f(k), (k, 1, n)) + + +def test_infinite_product(): + # issue 5737 + assert isinstance(Product(2**(1/factorial(n)), (n, 0, oo)), Product) + + +def test_conjugate_transpose(): + p = Product(x**k, (k, 1, 3)) + assert p.adjoint().doit() == p.doit().adjoint() + assert p.conjugate().doit() == p.doit().conjugate() + assert p.transpose().doit() == p.doit().transpose() + + A, B = symbols("A B", commutative=False) + p = Product(A*B**k, (k, 1, 3)) + assert p.adjoint().doit() == p.doit().adjoint() + assert p.conjugate().doit() == p.doit().conjugate() + assert p.transpose().doit() == p.doit().transpose() + + p = Product(B**k*A, (k, 1, 3)) + assert p.adjoint().doit() == p.doit().adjoint() + assert p.conjugate().doit() == p.doit().conjugate() + assert p.transpose().doit() == p.doit().transpose() + + +def test_simplify_prod(): + y, t, b, c, v, d = symbols('y, t, b, c, v, d', integer = True) + + _simplify = lambda e: simplify(e, doit=False) + assert _simplify(Product(x*y, (x, n, m), (y, a, k)) * \ + Product(y, (x, n, m), (y, a, k))) == \ + Product(x*y**2, (x, n, m), (y, a, k)) + assert _simplify(3 * y* Product(x, (x, n, m)) * Product(x, (x, m + 1, a))) \ + == 3 * y * Product(x, (x, n, a)) + assert _simplify(Product(x, (x, k + 1, a)) * Product(x, (x, n, k))) == \ + Product(x, (x, n, a)) + assert _simplify(Product(x, (x, k + 1, a)) * Product(x + 1, (x, n, k))) == \ + Product(x, (x, k + 1, a)) * Product(x + 1, (x, n, k)) + assert _simplify(Product(x, (t, a, b)) * Product(y, (t, a, b)) * \ + Product(x, (t, b+1, c))) == Product(x*y, (t, a, b)) * \ + Product(x, (t, b+1, c)) + assert _simplify(Product(x, (t, a, b)) * Product(x, (t, b+1, c)) * \ + Product(y, (t, a, b))) == Product(x*y, (t, a, b)) * \ + Product(x, (t, b+1, c)) + assert _simplify(Product(sin(t)**2 + cos(t)**2 + 1, (t, a, b))) == \ + Product(2, (t, a, b)) + assert _simplify(Product(sin(t)**2 + cos(t)**2 - 1, (t, a, b))) == \ + Product(0, (t, a, b)) + assert _simplify(Product(v*Product(sin(t)**2 + cos(t)**2, (t, a, b)), + (v, c, d))) == Product(v*Product(1, (t, a, b)), (v, c, d)) + + +def test_change_index(): + b, y, c, d, z = symbols('b, y, c, d, z', integer = True) + + assert Product(x, (x, a, b)).change_index(x, x + 1, y) == \ + Product(y - 1, (y, a + 1, b + 1)) + assert Product(x**2, (x, a, b)).change_index(x, x - 1) == \ + Product((x + 1)**2, (x, a - 1, b - 1)) + assert Product(x**2, (x, a, b)).change_index(x, -x, y) == \ + Product((-y)**2, (y, -b, -a)) + assert Product(x, (x, a, b)).change_index(x, -x - 1) == \ + Product(-x - 1, (x, - b - 1, -a - 1)) + assert Product(x*y, (x, a, b), (y, c, d)).change_index(x, x - 1, z) == \ + Product((z + 1)*y, (z, a - 1, b - 1), (y, c, d)) + + +def test_reorder(): + b, y, c, d, z = symbols('b, y, c, d, z', integer = True) + + assert Product(x*y, (x, a, b), (y, c, d)).reorder((0, 1)) == \ + Product(x*y, (y, c, d), (x, a, b)) + assert Product(x, (x, a, b), (x, c, d)).reorder((0, 1)) == \ + Product(x, (x, c, d), (x, a, b)) + assert Product(x*y + z, (x, a, b), (z, m, n), (y, c, d)).reorder(\ + (2, 0), (0, 1)) == Product(x*y + z, (z, m, n), (y, c, d), (x, a, b)) + assert Product(x*y*z, (x, a, b), (y, c, d), (z, m, n)).reorder(\ + (0, 1), (1, 2), (0, 2)) == \ + Product(x*y*z, (x, a, b), (z, m, n), (y, c, d)) + assert Product(x*y*z, (x, a, b), (y, c, d), (z, m, n)).reorder(\ + (x, y), (y, z), (x, z)) == \ + Product(x*y*z, (x, a, b), (z, m, n), (y, c, d)) + assert Product(x*y, (x, a, b), (y, c, d)).reorder((x, 1)) == \ + Product(x*y, (y, c, d), (x, a, b)) + assert Product(x*y, (x, a, b), (y, c, d)).reorder((y, x)) == \ + Product(x*y, (y, c, d), (x, a, b)) + + +def test_Product_is_convergent(): + assert Product(1/n**2, (n, 1, oo)).is_convergent() is S.false + assert Product(exp(1/n**2), (n, 1, oo)).is_convergent() is S.true + assert Product(1/n, (n, 1, oo)).is_convergent() is S.false + assert Product(1 + 1/n, (n, 1, oo)).is_convergent() is S.false + assert Product(1 + 1/n**2, (n, 1, oo)).is_convergent() is S.true + + +def test_reverse_order(): + x, y, a, b, c, d= symbols('x, y, a, b, c, d', integer = True) + + assert Product(x, (x, 0, 3)).reverse_order(0) == Product(1/x, (x, 4, -1)) + assert Product(x*y, (x, 1, 5), (y, 0, 6)).reverse_order(0, 1) == \ + Product(x*y, (x, 6, 0), (y, 7, -1)) + assert Product(x, (x, 1, 2)).reverse_order(0) == Product(1/x, (x, 3, 0)) + assert Product(x, (x, 1, 3)).reverse_order(0) == Product(1/x, (x, 4, 0)) + assert Product(x, (x, 1, a)).reverse_order(0) == Product(1/x, (x, a + 1, 0)) + assert Product(x, (x, a, 5)).reverse_order(0) == Product(1/x, (x, 6, a - 1)) + assert Product(x, (x, a + 1, a + 5)).reverse_order(0) == \ + Product(1/x, (x, a + 6, a)) + assert Product(x, (x, a + 1, a + 2)).reverse_order(0) == \ + Product(1/x, (x, a + 3, a)) + assert Product(x, (x, a + 1, a + 1)).reverse_order(0) == \ + Product(1/x, (x, a + 2, a)) + assert Product(x, (x, a, b)).reverse_order(0) == Product(1/x, (x, b + 1, a - 1)) + assert Product(x, (x, a, b)).reverse_order(x) == Product(1/x, (x, b + 1, a - 1)) + assert Product(x*y, (x, a, b), (y, 2, 5)).reverse_order(x, 1) == \ + Product(x*y, (x, b + 1, a - 1), (y, 6, 1)) + assert Product(x*y, (x, a, b), (y, 2, 5)).reverse_order(y, x) == \ + Product(x*y, (x, b + 1, a - 1), (y, 6, 1)) + + +def test_issue_9983(): + n = Symbol('n', integer=True, positive=True) + p = Product(1 + 1/n**Rational(2, 3), (n, 1, oo)) + assert p.is_convergent() is S.false + assert product(1 + 1/n**Rational(2, 3), (n, 1, oo)) == p.doit() + + +def test_issue_13546(): + n = Symbol('n') + k = Symbol('k') + p = Product(n + 1 / 2**k, (k, 0, n-1)).doit() + assert p.subs(n, 2).doit() == Rational(15, 2) + + +def test_issue_14036(): + a, n = symbols('a n') + assert product(1 - a**2 / (n*pi)**2, [n, 1, oo]) != 0 + + +def test_rewrite_Sum(): + assert Product(1 - S.Half**2/k**2, (k, 1, oo)).rewrite(Sum) == \ + exp(Sum(log(1 - 1/(4*k**2)), (k, 1, oo))) + + +def test_KroneckerDelta_Product(): + y = Symbol('y') + assert Product(x*KroneckerDelta(x, y), (x, 0, 1)).doit() == 0 + + +def test_issue_20848(): + _i = Dummy('i') + t, y, z = symbols('t y z') + 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() + assert diff(Product(x, (y, 1, z)), x).doit() == x**(z - 1)*z + assert diff(Product(x, (y, x, z)), x) == Derivative(Product(x, (y, x, z)), x) + assert diff(Product(t, (x, 1, z)), x) == S(0) + assert Product(sin(n*x), (n, -1, 1)).diff(x).doit() == S(0) diff --git a/.venv/lib/python3.13/site-packages/sympy/concrete/tests/test_sums_products.py b/.venv/lib/python3.13/site-packages/sympy/concrete/tests/test_sums_products.py new file mode 100644 index 0000000000000000000000000000000000000000..b190afe0bd403819d3525453879d7d5d39e20a56 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/concrete/tests/test_sums_products.py @@ -0,0 +1,1676 @@ +from math import prod + +from sympy.concrete.expr_with_intlimits import ReorderError +from sympy.concrete.products import (Product, product) +from sympy.concrete.summations import (Sum, summation, telescopic, + eval_sum_residue, _dummy_with_inherited_properties_concrete) +from sympy.core.function import (Derivative, Function) +from sympy.core import (Catalan, EulerGamma) +from sympy.core.facts import InconsistentAssumptions +from sympy.core.mod import Mod +from sympy.core.numbers import (E, I, Rational, nan, oo, pi) +from sympy.core.relational import Eq, Ne +from sympy.core.numbers import Float +from sympy.core.singleton import S +from sympy.core.symbol import (Dummy, Symbol, symbols) +from sympy.core.sympify import sympify +from sympy.functions.combinatorial.factorials import (rf, binomial, factorial) +from sympy.functions.combinatorial.numbers import harmonic +from sympy.functions.elementary.complexes import Abs, re +from sympy.functions.elementary.exponential import (exp, log) +from sympy.functions.elementary.hyperbolic import (sinh, tanh) +from sympy.functions.elementary.integers import floor +from sympy.functions.elementary.miscellaneous import sqrt +from sympy.functions.elementary.piecewise import Piecewise +from sympy.functions.elementary.trigonometric import (cos, sin, atan) +from sympy.functions.special.gamma_functions import (gamma, lowergamma) +from sympy.functions.special.tensor_functions import KroneckerDelta +from sympy.functions.special.zeta_functions import zeta +from sympy.integrals.integrals import Integral +from sympy.logic.boolalg import And, Or +from sympy.matrices.expressions.matexpr import MatrixSymbol +from sympy.matrices.expressions.special import Identity +from sympy.matrices import (Matrix, SparseMatrix, + ImmutableDenseMatrix, ImmutableSparseMatrix, diag) +from sympy.sets.contains import Contains +from sympy.sets.fancysets import Range +from sympy.sets.sets import Interval +from sympy.simplify.combsimp import combsimp +from sympy.simplify.simplify import simplify +from sympy.tensor.indexed import (Idx, Indexed, IndexedBase) +from sympy.testing.pytest import XFAIL, raises, slow +from sympy.abc import a, b, c, d, k, m, x, y, z + +n = Symbol('n', integer=True) +f, g = symbols('f g', cls=Function) + +def test_karr_convention(): + # Test the Karr summation convention that we want to hold. + # See his paper "Summation in Finite Terms" for a detailed + # reasoning why we really want exactly this definition. + # The convention is described on page 309 and essentially + # in section 1.4, definition 3: + # + # \sum_{m <= i < n} f(i) 'has the obvious meaning' for m < n + # \sum_{m <= i < n} f(i) = 0 for m = n + # \sum_{m <= i < n} f(i) = - \sum_{n <= i < m} f(i) for m > n + # + # It is important to note that he defines all sums with + # the upper limit being *exclusive*. + # In contrast, SymPy and the usual mathematical notation has: + # + # sum_{i = a}^b f(i) = f(a) + f(a+1) + ... + f(b-1) + f(b) + # + # with the upper limit *inclusive*. So translating between + # the two we find that: + # + # \sum_{m <= i < n} f(i) = \sum_{i = m}^{n-1} f(i) + # + # where we intentionally used two different ways to typeset the + # sum and its limits. + + i = Symbol("i", integer=True) + k = Symbol("k", integer=True) + j = Symbol("j", integer=True) + + # A simple example with a concrete summand and symbolic limits. + + # The normal sum: m = k and n = k + j and therefore m < n: + m = k + n = k + j + + a = m + b = n - 1 + S1 = Sum(i**2, (i, a, b)).doit() + + # The reversed sum: m = k + j and n = k and therefore m > n: + m = k + j + n = k + + a = m + b = n - 1 + S2 = Sum(i**2, (i, a, b)).doit() + + assert simplify(S1 + S2) == 0 + + # Test the empty sum: m = k and n = k and therefore m = n: + m = k + n = k + + a = m + b = n - 1 + Sz = Sum(i**2, (i, a, b)).doit() + + assert Sz == 0 + + # Another example this time with an unspecified summand and + # numeric limits. (We can not do both tests in the same example.) + + # The normal sum with m < n: + m = 2 + n = 11 + + a = m + b = n - 1 + S1 = Sum(f(i), (i, a, b)).doit() + + # The reversed sum with m > n: + m = 11 + n = 2 + + a = m + b = n - 1 + S2 = Sum(f(i), (i, a, b)).doit() + + assert simplify(S1 + S2) == 0 + + # Test the empty sum with m = n: + m = 5 + n = 5 + + a = m + b = n - 1 + Sz = Sum(f(i), (i, a, b)).doit() + + assert Sz == 0 + + e = Piecewise((exp(-i), Mod(i, 2) > 0), (0, True)) + s = Sum(e, (i, 0, 11)) + assert s.n(3) == s.doit().n(3) + + # issue #27893 + n = Symbol('n', integer=True) + assert Sum(1/(x**2 + 1), (x, oo, 0)).doit(deep=False) == Rational(-1, 2) + pi / (2 * tanh(pi)) + assert Sum(c**x/factorial(x), (x, oo, 0)).doit(deep=False).simplify() == exp(c) - 1 # exponential series + assert Sum((-1)**x/x, (x, oo,0)).doit() == -log(2) # alternating harmnic series + assert Sum((1/2)**x,(x, oo, -1)).doit() == S(2) # geometric series + assert Sum(1/x, (x, oo, 0)).doit() == oo # harmonic series, divergent + assert Sum((-1)**x/(2*x+1), (x, oo, -1)).doit() == pi/4 # leibniz series + assert Sum((((-1)**x) * c**(2*x+1)) / factorial(2*x+1), (x, oo, -1)).doit() == sin(c) # sinusoidal series + assert Sum((((-1)**x) * c**(2*x+1)) / (2*x+1), (x, 0, oo)).doit() \ + == Piecewise((atan(c), Ne(c**2, -1) & (Abs(c**2) <= 1)), \ + (Sum((-1)**x*c**(2*x + 1)/(2*x + 1), (x, 0, oo)), True)) # arctangent series + assert Sum(binomial(n, x) * c**x, (x, 0, oo)).doit() \ + == Piecewise(((c + 1)**n, \ + ((n <= -1) & (Abs(c) < 1)) \ + | ((n > 0) & (Abs(c) <= 1)) \ + | ((n <= 0) & (n > -1) & Ne(c, -1) & (Abs(c) <= 1))), \ + (Sum(c**x*binomial(n, x), (x, 0, oo)), True)) # binomial series + assert Sum(1/x**n, (x, oo, 0)).doit() \ + == Piecewise((zeta(n), n > 1), (Sum(x**(-n), (x, oo, 0)), True)) # Euler's zeta function + +def test_karr_proposition_2a(): + # Test Karr, page 309, proposition 2, part a + i = Symbol("i", integer=True) + u = Symbol("u", integer=True) + v = Symbol("v", integer=True) + + def test_the_sum(m, n): + # g + g = i**3 + 2*i**2 - 3*i + # f = Delta g + f = simplify(g.subs(i, i+1) - g) + # The sum + a = m + b = n - 1 + S = Sum(f, (i, a, b)).doit() + # Test if Sum_{m <= i < n} f(i) = g(n) - g(m) + assert simplify(S - (g.subs(i, n) - g.subs(i, m))) == 0 + + # m < n + test_the_sum(u, u+v) + # m = n + test_the_sum(u, u ) + # m > n + test_the_sum(u+v, u ) + + +def test_karr_proposition_2b(): + # Test Karr, page 309, proposition 2, part b + i = Symbol("i", integer=True) + u = Symbol("u", integer=True) + v = Symbol("v", integer=True) + w = Symbol("w", integer=True) + + def test_the_sum(l, n, m): + # Summand + s = i**3 + # First sum + a = l + b = n - 1 + S1 = Sum(s, (i, a, b)).doit() + # Second sum + a = l + b = m - 1 + S2 = Sum(s, (i, a, b)).doit() + # Third sum + a = m + b = n - 1 + S3 = Sum(s, (i, a, b)).doit() + # Test if S1 = S2 + S3 as required + assert S1 - (S2 + S3) == 0 + + # l < m < n + test_the_sum(u, u+v, u+v+w) + # l < m = n + test_the_sum(u, u+v, u+v ) + # l < m > n + test_the_sum(u, u+v+w, v ) + # l = m < n + test_the_sum(u, u, u+v ) + # l = m = n + test_the_sum(u, u, u ) + # l = m > n + test_the_sum(u+v, u+v, u ) + # l > m < n + test_the_sum(u+v, u, u+w ) + # l > m = n + test_the_sum(u+v, u, u ) + # l > m > n + test_the_sum(u+v+w, u+v, u ) + + +def test_arithmetic_sums(): + assert summation(1, (n, a, b)) == b - a + 1 + assert Sum(S.NaN, (n, a, b)) is S.NaN + assert Sum(x, (n, a, a)).doit() == x + assert Sum(x, (x, a, a)).doit() == a + assert Sum(x, (n, 1, a)).doit() == a*x + assert Sum(x, (x, Range(1, 11))).doit() == 55 + assert Sum(x, (x, Range(1, 11, 2))).doit() == 25 + assert Sum(x, (x, Range(1, 10, 2))) == Sum(x, (x, Range(9, 0, -2))) + lo, hi = 1, 2 + s1 = Sum(n, (n, lo, hi)) + s2 = Sum(n, (n, hi, lo)) + assert s1 != s2 + assert s1.doit() == 3 and s2.doit() == 0 + lo, hi = x, x + 1 + s1 = Sum(n, (n, lo, hi)) + s2 = Sum(n, (n, hi, lo)) + assert s1 != s2 + assert s1.doit() == 2*x + 1 and s2.doit() == 0 + assert Sum(Integral(x, (x, 1, y)) + x, (x, 1, 2)).doit() == \ + y**2 + 2 + assert summation(1, (n, 1, 10)) == 10 + assert summation(2*n, (n, 0, 10**10)) == 100000000010000000000 + assert summation(4*n*m, (n, a, 1), (m, 1, d)).expand() == \ + 2*d + 2*d**2 + a*d + a*d**2 - d*a**2 - a**2*d**2 + assert summation(cos(n), (n, -2, 1)) == cos(-2) + cos(-1) + cos(0) + cos(1) + assert summation(cos(n), (n, x, x + 2)) == cos(x) + cos(x + 1) + cos(x + 2) + assert isinstance(summation(cos(n), (n, x, x + S.Half)), Sum) + assert summation(k, (k, 0, oo)) is oo + assert summation(k, (k, Range(1, 11))) == 55 + + +def test_polynomial_sums(): + assert summation(n**2, (n, 3, 8)) == 199 + assert summation(n, (n, a, b)) == \ + ((a + b)*(b - a + 1)/2).expand() + assert summation(n**2, (n, 1, b)) == \ + ((2*b**3 + 3*b**2 + b)/6).expand() + assert summation(n**3, (n, 1, b)) == \ + ((b**4 + 2*b**3 + b**2)/4).expand() + assert summation(n**6, (n, 1, b)) == \ + ((6*b**7 + 21*b**6 + 21*b**5 - 7*b**3 + b)/42).expand() + + +def test_geometric_sums(): + assert summation(pi**n, (n, 0, b)) == (1 - pi**(b + 1)) / (1 - pi) + assert summation(2 * 3**n, (n, 0, b)) == 3**(b + 1) - 1 + assert summation(S.Half**n, (n, 1, oo)) == 1 + assert summation(2**n, (n, 0, b)) == 2**(b + 1) - 1 + assert summation(2**n, (n, 1, oo)) is oo + assert summation(2**(-n), (n, 1, oo)) == 1 + assert summation(3**(-n), (n, 4, oo)) == Rational(1, 54) + assert summation(2**(-4*n + 3), (n, 1, oo)) == Rational(8, 15) + assert summation(2**(n + 1), (n, 1, b)).expand() == 4*(2**b - 1) + + # issue 6664: + assert summation(x**n, (n, 0, oo)) == \ + Piecewise((1/(-x + 1), Abs(x) < 1), (Sum(x**n, (n, 0, oo)), True)) + + assert summation(-2**n, (n, 0, oo)) is -oo + assert summation(I**n, (n, 0, oo)) == Sum(I**n, (n, 0, oo)) + + # issue 6802: + assert summation((-1)**(2*x + 2), (x, 0, n)) == n + 1 + assert summation((-2)**(2*x + 2), (x, 0, n)) == 4*4**(n + 1)/S(3) - Rational(4, 3) + assert summation((-1)**x, (x, 0, n)) == -(-1)**(n + 1)/S(2) + S.Half + assert summation(y**x, (x, a, b)) == \ + Piecewise((-a + b + 1, Eq(y, 1)), ((y**a - y**(b + 1))/(-y + 1), True)) + assert summation((-2)**(y*x + 2), (x, 0, n)) == \ + 4*Piecewise((n + 1, Eq((-2)**y, 1)), + ((-(-2)**(y*(n + 1)) + 1)/(-(-2)**y + 1), True)) + + # issue 8251: + assert summation((1/(n + 1)**2)*n**2, (n, 0, oo)) is oo + + #issue 9908: + assert Sum(1/(n**3 - 1), (n, -oo, -2)).doit() == summation(1/(n**3 - 1), (n, -oo, -2)) + + #issue 11642: + result = Sum(0.5**n, (n, 1, oo)).doit() + assert result == 1.0 + assert result.is_Float + + result = Sum(0.25**n, (n, 1, oo)).doit() + assert result == 1/3. + assert result.is_Float + + result = Sum(0.99999**n, (n, 1, oo)).doit() + assert result == 99999.0 + assert result.is_Float + + result = Sum(S.Half**n, (n, 1, oo)).doit() + assert result == 1 + assert not result.is_Float + + result = Sum(Rational(3, 5)**n, (n, 1, oo)).doit() + assert result == Rational(3, 2) + assert not result.is_Float + + assert Sum(1.0**n, (n, 1, oo)).doit() is oo + assert Sum(2.43**n, (n, 1, oo)).doit() is oo + + # Issue 13979 + i, k, q = symbols('i k q', integer=True) + result = summation( + exp(-2*I*pi*k*i/n) * exp(2*I*pi*q*i/n) / n, (i, 0, n - 1) + ) + assert result.simplify() == Piecewise( + (1, Eq(exp(-2*I*pi*(k - q)/n), 1)), (0, True) + ) + + #Issue 23491 + assert Sum(1/(n**2 + 1), (n, 1, oo)).doit() == S(-1)/2 + pi/(2*tanh(pi)) + +def test_harmonic_sums(): + assert summation(1/k, (k, 0, n)) == Sum(1/k, (k, 0, n)) + assert summation(1/k, (k, 1, n)) == harmonic(n) + assert summation(n/k, (k, 1, n)) == n*harmonic(n) + assert summation(1/k, (k, 5, n)) == harmonic(n) - harmonic(4) + + +def test_composite_sums(): + f = S.Half*(7 - 6*n + Rational(1, 7)*n**3) + s = summation(f, (n, a, b)) + assert not isinstance(s, Sum) + A = 0 + for i in range(-3, 5): + A += f.subs(n, i) + B = s.subs(a, -3).subs(b, 4) + assert A == B + + +def test_hypergeometric_sums(): + assert summation( + binomial(2*k, k)/4**k, (k, 0, n)) == (1 + 2*n)*binomial(2*n, n)/4**n + assert summation(binomial(2*k, k)/5**k, (k, -oo, oo)) == sqrt(5) + + +def test_other_sums(): + f = m**2 + m*exp(m) + g = 3*exp(Rational(3, 2))/2 + exp(S.Half)/2 - exp(Rational(-1, 2))/2 - 3*exp(Rational(-3, 2))/2 + 5 + + assert summation(f, (m, Rational(-3, 2), Rational(3, 2))) == g + assert summation(f, (m, -1.5, 1.5)).evalf().epsilon_eq(g.evalf(), 1e-10) + +fac = factorial + + +def NS(e, n=15, **options): + return str(sympify(e).evalf(n, **options)) + + +def test_evalf_fast_series(): + # Euler transformed series for sqrt(1+x) + assert NS(Sum( + fac(2*n + 1)/fac(n)**2/2**(3*n + 1), (n, 0, oo)), 100) == NS(sqrt(2), 100) + + # Some series for exp(1) + estr = NS(E, 100) + assert NS(Sum(1/fac(n), (n, 0, oo)), 100) == estr + assert NS(1/Sum((1 - 2*n)/fac(2*n), (n, 0, oo)), 100) == estr + assert NS(Sum((2*n + 1)/fac(2*n), (n, 0, oo)), 100) == estr + assert NS(Sum((4*n + 3)/2**(2*n + 1)/fac(2*n + 1), (n, 0, oo))**2, 100) == estr + + pistr = NS(pi, 100) + # Ramanujan series for pi + assert NS(9801/sqrt(8)/Sum(fac( + 4*n)*(1103 + 26390*n)/fac(n)**4/396**(4*n), (n, 0, oo)), 100) == pistr + assert NS(1/Sum( + binomial(2*n, n)**3 * (42*n + 5)/2**(12*n + 4), (n, 0, oo)), 100) == pistr + # Machin's formula for pi + assert NS(16*Sum((-1)**n/(2*n + 1)/5**(2*n + 1), (n, 0, oo)) - + 4*Sum((-1)**n/(2*n + 1)/239**(2*n + 1), (n, 0, oo)), 100) == pistr + + # Apery's constant + astr = NS(zeta(3), 100) + P = 126392*n**5 + 412708*n**4 + 531578*n**3 + 336367*n**2 + 104000* \ + n + 12463 + assert NS(Sum((-1)**n * P / 24 * (fac(2*n + 1)*fac(2*n)*fac( + n))**3 / fac(3*n + 2) / fac(4*n + 3)**3, (n, 0, oo)), 100) == astr + assert NS(Sum((-1)**n * (205*n**2 + 250*n + 77)/64 * fac(n)**10 / + fac(2*n + 1)**5, (n, 0, oo)), 100) == astr + + +def test_evalf_fast_series_issue_4021(): + # Catalan's constant + assert NS(Sum((-1)**(n - 1)*2**(8*n)*(40*n**2 - 24*n + 3)*fac(2*n)**3* + fac(n)**2/n**3/(2*n - 1)/fac(4*n)**2, (n, 1, oo))/64, 100) == \ + NS(Catalan, 100) + astr = NS(zeta(3), 100) + assert NS(5*Sum( + (-1)**(n - 1)*fac(n)**2 / n**3 / fac(2*n), (n, 1, oo))/2, 100) == astr + assert NS(Sum((-1)**(n - 1)*(56*n**2 - 32*n + 5) / (2*n - 1)**2 * fac(n - 1) + **3 / fac(3*n), (n, 1, oo))/4, 100) == astr + + +def test_evalf_slow_series(): + assert NS(Sum((-1)**n / n, (n, 1, oo)), 15) == NS(-log(2), 15) + assert NS(Sum((-1)**n / n, (n, 1, oo)), 50) == NS(-log(2), 50) + assert NS(Sum(1/n**2, (n, 1, oo)), 15) == NS(pi**2/6, 15) + assert NS(Sum(1/n**2, (n, 1, oo)), 100) == NS(pi**2/6, 100) + assert NS(Sum(1/n**2, (n, 1, oo)), 500) == NS(pi**2/6, 500) + assert NS(Sum((-1)**n / (2*n + 1)**3, (n, 0, oo)), 15) == NS(pi**3/32, 15) + assert NS(Sum((-1)**n / (2*n + 1)**3, (n, 0, oo)), 50) == NS(pi**3/32, 50) + + +def test_evalf_oo_to_oo(): + # There used to be an error in certain cases + # Does not evaluate, but at least do not throw an error + # Evaluates symbolically to 0, which is not correct + assert Sum(1/(n**2+1), (n, -oo, oo)).evalf() == Sum(1/(n**2+1), (n, -oo, oo)) + # This evaluates if from 1 to oo and symbolically + assert Sum(1/(factorial(abs(n))), (n, -oo, -1)).evalf() == Sum(1/(factorial(abs(n))), (n, -oo, -1)) + + +def test_euler_maclaurin(): + # Exact polynomial sums with E-M + def check_exact(f, a, b, m, n): + A = Sum(f, (k, a, b)) + s, e = A.euler_maclaurin(m, n) + assert (e == 0) and (s.expand() == A.doit()) + check_exact(k**4, a, b, 0, 2) + check_exact(k**4 + 2*k, a, b, 1, 2) + check_exact(k**4 + k**2, a, b, 1, 5) + check_exact(k**5, 2, 6, 1, 2) + check_exact(k**5, 2, 6, 1, 3) + assert Sum(x-1, (x, 0, 2)).euler_maclaurin(m=30, n=30, eps=2**-15) == (0, 0) + # Not exact + assert Sum(k**6, (k, a, b)).euler_maclaurin(0, 2)[1] != 0 + # Numerical test + for mi, ni in [(2, 4), (2, 20), (10, 20), (18, 20)]: + A = Sum(1/k**3, (k, 1, oo)) + s, e = A.euler_maclaurin(mi, ni) + assert abs((s - zeta(3)).evalf()) < e.evalf() + + raises(ValueError, lambda: Sum(1, (x, 0, 1), (k, 0, 1)).euler_maclaurin()) + + +@slow +def test_evalf_euler_maclaurin(): + assert NS(Sum(1/k**k, (k, 1, oo)), 15) == '1.29128599706266' + assert NS(Sum(1/k**k, (k, 1, oo)), + 50) == '1.2912859970626635404072825905956005414986193682745' + assert NS(Sum(1/k - log(1 + 1/k), (k, 1, oo)), 15) == NS(EulerGamma, 15) + assert NS(Sum(1/k - log(1 + 1/k), (k, 1, oo)), 50) == NS(EulerGamma, 50) + assert NS(Sum(log(k)/k**2, (k, 1, oo)), 15) == '0.937548254315844' + assert NS(Sum(log(k)/k**2, (k, 1, oo)), + 50) == '0.93754825431584375370257409456786497789786028861483' + assert NS(Sum(1/k, (k, 1000000, 2000000)), 15) == '0.693147930560008' + assert NS(Sum(1/k, (k, 1000000, 2000000)), + 50) == '0.69314793056000780941723211364567656807940638436025' + + +def test_evalf_symbolic(): + # issue 6328 + expr = Sum(f(x), (x, 1, 3)) + Sum(g(x), (x, 1, 3)) + assert expr.evalf() == expr + + +def test_evalf_issue_3273(): + assert Sum(0, (k, 1, oo)).evalf() == 0 + + +def test_simple_products(): + assert Product(S.NaN, (x, 1, 3)) is S.NaN + assert product(S.NaN, (x, 1, 3)) is S.NaN + assert Product(x, (n, a, a)).doit() == x + assert Product(x, (x, a, a)).doit() == a + assert Product(x, (y, 1, a)).doit() == x**a + + lo, hi = 1, 2 + s1 = Product(n, (n, lo, hi)) + s2 = Product(n, (n, hi, lo)) + assert s1 != s2 + # This IS correct according to Karr product convention + assert s1.doit() == 2 + assert s2.doit() == 1 + + lo, hi = x, x + 1 + s1 = Product(n, (n, lo, hi)) + s2 = Product(n, (n, hi, lo)) + s3 = 1 / Product(n, (n, hi + 1, lo - 1)) + assert s1 != s2 + # This IS correct according to Karr product convention + assert s1.doit() == x*(x + 1) + assert s2.doit() == 1 + assert s3.doit() == x*(x + 1) + + assert Product(Integral(2*x, (x, 1, y)) + 2*x, (x, 1, 2)).doit() == \ + (y**2 + 1)*(y**2 + 3) + assert product(2, (n, a, b)) == 2**(b - a + 1) + assert product(n, (n, 1, b)) == factorial(b) + assert product(n**3, (n, 1, b)) == factorial(b)**3 + assert product(3**(2 + n), (n, a, b)) \ + == 3**(2*(1 - a + b) + b/2 + (b**2)/2 + a/2 - (a**2)/2) + assert product(cos(n), (n, 3, 5)) == cos(3)*cos(4)*cos(5) + assert product(cos(n), (n, x, x + 2)) == cos(x)*cos(x + 1)*cos(x + 2) + assert isinstance(product(cos(n), (n, x, x + S.Half)), Product) + # If Product managed to evaluate this one, it most likely got it wrong! + assert isinstance(Product(n**n, (n, 1, b)), Product) + + +def test_rational_products(): + assert combsimp(product(1 + 1/n, (n, a, b))) == (1 + b)/a + assert combsimp(product(n + 1, (n, a, b))) == gamma(2 + b)/gamma(1 + a) + assert combsimp(product((n + 1)/(n - 1), (n, a, b))) == b*(1 + b)/(a*(a - 1)) + assert combsimp(product(n/(n + 1)/(n + 2), (n, a, b))) == \ + a*gamma(a + 2)/(b + 1)/gamma(b + 3) + assert combsimp(product(n*(n + 1)/(n - 1)/(n - 2), (n, a, b))) == \ + b**2*(b - 1)*(1 + b)/(a - 1)**2/(a*(a - 2)) + + +def test_wallis_product(): + # Wallis product, given in two different forms to ensure that Product + # can factor simple rational expressions + A = Product(4*n**2 / (4*n**2 - 1), (n, 1, b)) + B = Product((2*n)*(2*n)/(2*n - 1)/(2*n + 1), (n, 1, b)) + R = pi*gamma(b + 1)**2/(2*gamma(b + S.Half)*gamma(b + Rational(3, 2))) + assert simplify(A.doit()) == R + assert simplify(B.doit()) == R + # This one should eventually also be doable (Euler's product formula for sin) + # assert Product(1+x/n**2, (n, 1, b)) == ... + + +def test_telescopic_sums(): + #checks also input 2 of comment 1 issue 4127 + assert Sum(1/k - 1/(k + 1), (k, 1, n)).doit() == 1 - 1/(1 + n) + assert Sum( + f(k) - f(k + 2), (k, m, n)).doit() == -f(1 + n) - f(2 + n) + f(m) + f(1 + m) + assert Sum(cos(k) - cos(k + 3), (k, 1, n)).doit() == -cos(1 + n) - \ + cos(2 + n) - cos(3 + n) + cos(1) + cos(2) + cos(3) + + # dummy variable shouldn't matter + assert telescopic(1/m, -m/(1 + m), (m, n - 1, n)) == \ + telescopic(1/k, -k/(1 + k), (k, n - 1, n)) + + assert Sum(1/x/(x - 1), (x, a, b)).doit() == 1/(a - 1) - 1/b + eq = 1/((5*n + 2)*(5*(n + 1) + 2)) + assert Sum(eq, (n, 0, oo)).doit() == S(1)/10 + nz = symbols('nz', nonzero=True) + v = Sum(eq.subs(5, nz), (n, 0, oo)).doit() + assert v.subs(nz, 5).simplify() == S(1)/10 + # check that apart is being used in non-symbolic case + s = Sum(eq, (n, 0, k)).doit() + v = Sum(eq, (n, 0, 10**100)).doit() + assert v == s.subs(k, 10**100) + + +def test_sum_reconstruct(): + s = Sum(n**2, (n, -1, 1)) + assert s == Sum(*s.args) + raises(ValueError, lambda: Sum(x, x)) + raises(ValueError, lambda: Sum(x, (x, 1))) + + +def test_limit_subs(): + for F in (Sum, Product, Integral): + assert F(a*exp(a), (a, -2, 2)) == F(a*exp(a), (a, -b, b)).subs(b, 2) + assert F(a, (a, F(b, (b, 1, 2)), 4)).subs(F(b, (b, 1, 2)), c) == \ + F(a, (a, c, 4)) + assert F(x, (x, 1, x + y)).subs(x, 1) == F(x, (x, 1, y + 1)) + + +def test_function_subs(): + S = Sum(x*f(y),(x,0,oo),(y,0,oo)) + assert S.subs(f(y),y) == Sum(x*y,(x,0,oo),(y,0,oo)) + assert S.subs(f(x),x) == S + raises(ValueError, lambda: S.subs(f(y),x+y) ) + S = Sum(x*log(y),(x,0,oo),(y,0,oo)) + assert S.subs(log(y),y) == S + S = Sum(x*f(y),(x,0,oo),(y,0,oo)) + assert S.subs(f(y),y) == Sum(x*y,(x,0,oo),(y,0,oo)) + + +def test_equality(): + # if this fails remove special handling below + raises(ValueError, lambda: Sum(x, x)) + r = symbols('x', real=True) + for F in (Sum, Product, Integral): + try: + assert F(x, x) != F(y, y) + assert F(x, (x, 1, 2)) != F(x, x) + assert F(x, (x, x)) != F(x, x) # or else they print the same + assert F(1, x) != F(1, y) + except ValueError: + pass + assert F(a, (x, 1, 2)) != F(a, (x, 1, 3)) # diff limit + assert F(a, (x, 1, x)) != F(a, (y, 1, y)) + assert F(a, (x, 1, 2)) != F(b, (x, 1, 2)) # diff expression + assert F(x, (x, 1, 2)) != F(r, (r, 1, 2)) # diff assumptions + assert F(1, (x, 1, x)) != F(1, (y, 1, x)) # only dummy is diff + assert F(1, (x, 1, x)).dummy_eq(F(1, (y, 1, x))) + + # issue 5265 + assert Sum(x, (x, 1, x)).subs(x, a) == Sum(x, (x, 1, a)) + + +def test_Sum_doit(): + assert Sum(n*Integral(a**2), (n, 0, 2)).doit() == a**3 + assert Sum(n*Integral(a**2), (n, 0, 2)).doit(deep=False) == \ + 3*Integral(a**2) + assert summation(n*Integral(a**2), (n, 0, 2)) == 3*Integral(a**2) + + # test nested sum evaluation + s = Sum( Sum( Sum(2,(z,1,n+1)), (y,x+1,n)), (x,1,n)) + assert 0 == (s.doit() - n*(n+1)*(n-1)).factor() + + # Integer assumes finite + assert Sum(KroneckerDelta(x, y), (x, -oo, oo)).doit() == Piecewise((1, And(-oo < y, y < oo)), (0, True)) + assert Sum(KroneckerDelta(m, n), (m, -oo, oo)).doit() == 1 + assert Sum(m*KroneckerDelta(x, y), (x, -oo, oo)).doit() == Piecewise((m, And(-oo < y, y < oo)), (0, True)) + assert Sum(x*KroneckerDelta(m, n), (m, -oo, oo)).doit() == x + assert Sum(Sum(KroneckerDelta(m, n), (m, 1, 3)), (n, 1, 3)).doit() == 3 + assert Sum(Sum(KroneckerDelta(k, m), (m, 1, 3)), (n, 1, 3)).doit() == \ + 3 * Piecewise((1, And(1 <= k, k <= 3)), (0, True)) + assert Sum(f(n) * Sum(KroneckerDelta(m, n), (m, 0, oo)), (n, 1, 3)).doit() == \ + f(1) + f(2) + f(3) + assert Sum(f(n) * Sum(KroneckerDelta(m, n), (m, 0, oo)), (n, 1, oo)).doit() == \ + Sum(f(n), (n, 1, oo)) + + # issue 2597 + nmax = symbols('N', integer=True, positive=True) + pw = Piecewise((1, And(1 <= n, n <= nmax)), (0, True)) + assert Sum(pw, (n, 1, nmax)).doit() == Sum(Piecewise((1, nmax >= n), + (0, True)), (n, 1, nmax)) + + q, s = symbols('q, s') + assert summation(1/n**(2*s), (n, 1, oo)) == Piecewise((zeta(2*s), 2*re(s) > 1), + (Sum(n**(-2*s), (n, 1, oo)), True)) + assert summation(1/(n+1)**s, (n, 0, oo)) == Piecewise((zeta(s), re(s) > 1), + (Sum((n + 1)**(-s), (n, 0, oo)), True)) + assert summation(1/(n+q)**s, (n, 0, oo)) == Piecewise( + (zeta(s, q), And(~Contains(-q, S.Naturals0), re(s) > 1)), + (Sum((n + q)**(-s), (n, 0, oo)), True)) + assert summation(1/(n+q)**s, (n, q, oo)) == Piecewise( + (zeta(s, 2*q), And(~Contains(-2*q, S.Naturals0), re(s) > 1)), + (Sum((n + q)**(-s), (n, q, oo)), True)) + assert summation(1/n**2, (n, 1, oo)) == zeta(2) + assert summation(1/n**s, (n, 0, oo)) == Sum(n**(-s), (n, 0, oo)) + assert summation(1/(n+1)**(2+I), (n, 0, oo)) == zeta(2+I) + t = symbols('t', real=True, positive=True) + assert summation(1/(n+I)**(t+1), (n, 0, oo)) == zeta(t+1, I) + + +def test_Product_doit(): + assert Product(n*Integral(a**2), (n, 1, 3)).doit() == 2 * a**9 / 9 + assert Product(n*Integral(a**2), (n, 1, 3)).doit(deep=False) == \ + 6*Integral(a**2)**3 + assert product(n*Integral(a**2), (n, 1, 3)) == 6*Integral(a**2)**3 + + +def test_Sum_interface(): + assert isinstance(Sum(0, (n, 0, 2)), Sum) + assert Sum(nan, (n, 0, 2)) is nan + assert Sum(nan, (n, 0, oo)) is nan + assert Sum(0, (n, 0, 2)).doit() == 0 + assert isinstance(Sum(0, (n, 0, oo)), Sum) + assert Sum(0, (n, 0, oo)).doit() == 0 + raises(ValueError, lambda: Sum(1)) + raises(ValueError, lambda: summation(1)) + + +def test_diff(): + assert Sum(x, (x, 1, 2)).diff(x) == 0 + assert Sum(x*y, (x, 1, 2)).diff(x) == 0 + assert Sum(x*y, (y, 1, 2)).diff(x) == Sum(y, (y, 1, 2)) + e = Sum(x*y, (x, 1, a)) + assert e.diff(a) == Derivative(e, a) + assert Sum(x*y, (x, 1, 3), (a, 2, 5)).diff(y).doit() == \ + Sum(x*y, (x, 1, 3), (a, 2, 5)).doit().diff(y) == 24 + assert Sum(x, (x, 1, 2)).diff(y) == 0 + + +def test_hypersum(): + assert simplify(summation(x**n/fac(n), (n, 1, oo))) == -1 + exp(x) + assert summation((-1)**n * x**(2*n) / fac(2*n), (n, 0, oo)) == cos(x) + assert simplify(summation((-1)**n*x**(2*n + 1) / + factorial(2*n + 1), (n, 3, oo))) == -x + sin(x) + x**3/6 - x**5/120 + + assert summation(1/(n + 2)**3, (n, 1, oo)) == Rational(-9, 8) + zeta(3) + assert summation(1/n**4, (n, 1, oo)) == pi**4/90 + + s = summation(x**n*n, (n, -oo, 0)) + assert s.is_Piecewise + assert s.args[0].args[0] == -1/(x*(1 - 1/x)**2) + assert s.args[0].args[1] == (abs(1/x) < 1) + + m = Symbol('n', integer=True, positive=True) + assert summation(binomial(m, k), (k, 0, m)) == 2**m + + +def test_issue_4170(): + assert summation(1/factorial(k), (k, 0, oo)) == E + + +def test_is_commutative(): + from sympy.physics.secondquant import NO, F, Fd + m = Symbol('m', commutative=False) + for f in (Sum, Product, Integral): + assert f(z, (z, 1, 1)).is_commutative is True + assert f(z*y, (z, 1, 6)).is_commutative is True + assert f(m*x, (x, 1, 2)).is_commutative is False + + assert f(NO(Fd(x)*F(y))*z, (z, 1, 2)).is_commutative is False + + +def test_is_zero(): + for func in [Sum, Product]: + assert func(0, (x, 1, 1)).is_zero is True + assert func(x, (x, 1, 1)).is_zero is None + + assert Sum(0, (x, 1, 0)).is_zero is True + assert Product(0, (x, 1, 0)).is_zero is False + + +def test_is_number(): + # is number should not rely on evaluation or assumptions, + # it should be equivalent to `not foo.free_symbols` + assert Sum(1, (x, 1, 1)).is_number is True + assert Sum(1, (x, 1, x)).is_number is False + assert Sum(0, (x, y, z)).is_number is False + assert Sum(x, (y, 1, 2)).is_number is False + assert Sum(x, (y, 1, 1)).is_number is False + assert Sum(x, (x, 1, 2)).is_number is True + assert Sum(x*y, (x, 1, 2), (y, 1, 3)).is_number is True + + assert Product(2, (x, 1, 1)).is_number is True + assert Product(2, (x, 1, y)).is_number is False + assert Product(0, (x, y, z)).is_number is False + assert Product(1, (x, y, z)).is_number is False + assert Product(x, (y, 1, x)).is_number is False + assert Product(x, (y, 1, 2)).is_number is False + assert Product(x, (y, 1, 1)).is_number is False + assert Product(x, (x, 1, 2)).is_number is True + + +def test_free_symbols(): + for func in [Sum, Product]: + assert func(1, (x, 1, 2)).free_symbols == set() + assert func(0, (x, 1, y)).free_symbols == {y} + assert func(2, (x, 1, y)).free_symbols == {y} + assert func(x, (x, 1, 2)).free_symbols == set() + assert func(x, (x, 1, y)).free_symbols == {y} + assert func(x, (y, 1, y)).free_symbols == {x, y} + assert func(x, (y, 1, 2)).free_symbols == {x} + assert func(x, (y, 1, 1)).free_symbols == {x} + assert func(x, (y, 1, z)).free_symbols == {x, z} + assert func(x, (x, 1, y), (y, 1, 2)).free_symbols == set() + assert func(x, (x, 1, y), (y, 1, z)).free_symbols == {z} + assert func(x, (x, 1, y), (y, 1, y)).free_symbols == {y} + assert func(x, (y, 1, y), (y, 1, z)).free_symbols == {x, z} + assert Sum(1, (x, 1, y)).free_symbols == {y} + # free_symbols answers whether the object *as written* has free symbols, + # not whether the evaluated expression has free symbols + assert Product(1, (x, 1, y)).free_symbols == {y} + # don't count free symbols that are not independent of integration + # variable(s) + assert func(f(x), (f(x), 1, 2)).free_symbols == set() + assert func(f(x), (f(x), 1, x)).free_symbols == {x} + assert func(f(x), (f(x), 1, y)).free_symbols == {y} + assert func(f(x), (z, 1, y)).free_symbols == {x, y} + + +def test_conjugate_transpose(): + A, B = symbols("A B", commutative=False) + p = Sum(A*B**n, (n, 1, 3)) + assert p.adjoint().doit() == p.doit().adjoint() + assert p.conjugate().doit() == p.doit().conjugate() + assert p.transpose().doit() == p.doit().transpose() + + p = Sum(B**n*A, (n, 1, 3)) + assert p.adjoint().doit() == p.doit().adjoint() + assert p.conjugate().doit() == p.doit().conjugate() + assert p.transpose().doit() == p.doit().transpose() + + +def test_noncommutativity_honoured(): + A, B = symbols("A B", commutative=False) + M = symbols('M', integer=True, positive=True) + p = Sum(A*B**n, (n, 1, M)) + assert p.doit() == A*Piecewise((M, Eq(B, 1)), + ((B - B**(M + 1))*(1 - B)**(-1), True)) + + p = Sum(B**n*A, (n, 1, M)) + assert p.doit() == Piecewise((M, Eq(B, 1)), + ((B - B**(M + 1))*(1 - B)**(-1), True))*A + + p = Sum(B**n*A*B**n, (n, 1, M)) + assert p.doit() == p + + +def test_issue_4171(): + assert summation(factorial(2*k + 1)/factorial(2*k), (k, 0, oo)) is oo + assert summation(2*k + 1, (k, 0, oo)) is oo + + +def test_issue_6273(): + assert Sum(x, (x, 1, n)).n(2, subs={n: 1}) == Float(1, 2) + + +def test_issue_6274(): + assert Sum(x, (x, 1, 0)).doit() == 0 + assert NS(Sum(x, (x, 1, 0))) == '0' + assert Sum(n, (n, 10, 5)).doit() == -30 + assert NS(Sum(n, (n, 10, 5))) == '-30.0000000000000' + + +def test_simplify_sum(): + y, t, v = symbols('y, t, v') + + _simplify = lambda e: simplify(e, doit=False) + assert _simplify(Sum(x*y, (x, n, m), (y, a, k)) + \ + Sum(y, (x, n, m), (y, a, k))) == Sum(y * (x + 1), (x, n, m), (y, a, k)) + assert _simplify(Sum(x, (x, n, m)) + Sum(x, (x, m + 1, a))) == \ + Sum(x, (x, n, a)) + assert _simplify(Sum(x, (x, k + 1, a)) + Sum(x, (x, n, k))) == \ + Sum(x, (x, n, a)) + assert _simplify(Sum(x, (x, k + 1, a)) + Sum(x + 1, (x, n, k))) == \ + Sum(x, (x, n, a)) + Sum(1, (x, n, k)) + assert _simplify(Sum(x, (x, 0, 3)) * 3 + 3 * Sum(x, (x, 4, 6)) + \ + 4 * Sum(z, (z, 0, 1))) == 4*Sum(z, (z, 0, 1)) + 3*Sum(x, (x, 0, 6)) + assert _simplify(3*Sum(x**2, (x, a, b)) + Sum(x, (x, a, b))) == \ + Sum(x*(3*x + 1), (x, a, b)) + assert _simplify(Sum(x**3, (x, n, k)) * 3 + 3 * Sum(x, (x, n, k)) + \ + 4 * y * Sum(z, (z, n, k))) + 1 == \ + 4*y*Sum(z, (z, n, k)) + 3*Sum(x**3 + x, (x, n, k)) + 1 + assert _simplify(Sum(x, (x, a, b)) + 1 + Sum(x, (x, b + 1, c))) == \ + 1 + Sum(x, (x, a, c)) + assert _simplify(Sum(x, (t, a, b)) + Sum(y, (t, a, b)) + \ + Sum(x, (t, b+1, c))) == x * Sum(1, (t, a, c)) + y * Sum(1, (t, a, b)) + assert _simplify(Sum(x, (t, a, b)) + Sum(x, (t, b+1, c)) + \ + Sum(y, (t, a, b))) == x * Sum(1, (t, a, c)) + y * Sum(1, (t, a, b)) + assert _simplify(Sum(x, (t, a, b)) + 2 * Sum(x, (t, b+1, c))) == \ + _simplify(Sum(x, (t, a, b)) + Sum(x, (t, b+1, c)) + Sum(x, (t, b+1, c))) + assert _simplify(Sum(x, (x, a, b))*Sum(x**2, (x, a, b))) == \ + Sum(x, (x, a, b)) * Sum(x**2, (x, a, b)) + assert _simplify(Sum(x, (t, a, b)) + Sum(y, (t, a, b)) + Sum(z, (t, a, b))) \ + == (x + y + z) * Sum(1, (t, a, b)) # issue 8596 + assert _simplify(Sum(x, (t, a, b)) + Sum(y, (t, a, b)) + Sum(z, (t, a, b)) + \ + Sum(v, (t, a, b))) == (x + y + z + v) * Sum(1, (t, a, b)) # issue 8596 + assert _simplify(Sum(x * y, (x, a, b)) / (3 * y)) == \ + (Sum(x, (x, a, b)) / 3) + assert _simplify(Sum(f(x) * y * z, (x, a, b)) / (y * z)) \ + == Sum(f(x), (x, a, b)) + assert _simplify(Sum(c * x, (x, a, b)) - c * Sum(x, (x, a, b))) == 0 + assert _simplify(c * (Sum(x, (x, a, b)) + y)) == c * (y + Sum(x, (x, a, b))) + assert _simplify(c * (Sum(x, (x, a, b)) + y * Sum(x, (x, a, b)))) == \ + c * (y + 1) * Sum(x, (x, a, b)) + assert _simplify(Sum(Sum(c * x, (x, a, b)), (y, a, b))) == \ + c * Sum(x, (x, a, b), (y, a, b)) + assert _simplify(Sum((3 + y) * Sum(c * x, (x, a, b)), (y, a, b))) == \ + c * Sum((3 + y), (y, a, b)) * Sum(x, (x, a, b)) + assert _simplify(Sum((3 + t) * Sum(c * t, (x, a, b)), (y, a, b))) == \ + c*t*(t + 3)*Sum(1, (x, a, b))*Sum(1, (y, a, b)) + assert _simplify(Sum(Sum(d * t, (x, a, b - 1)) + \ + Sum(d * t, (x, b, c)), (t, a, b))) == \ + d * Sum(1, (x, a, c)) * Sum(t, (t, a, b)) + assert _simplify(Sum(sin(t)**2 + cos(t)**2 + 1, (t, a, b))) == \ + 2 * Sum(1, (t, a, b)) + + +def test_change_index(): + b, v, w = symbols('b, v, w', integer = True) + + assert Sum(x, (x, a, b)).change_index(x, x + 1, y) == \ + Sum(y - 1, (y, a + 1, b + 1)) + assert Sum(x**2, (x, a, b)).change_index( x, x - 1) == \ + Sum((x+1)**2, (x, a - 1, b - 1)) + assert Sum(x**2, (x, a, b)).change_index( x, -x, y) == \ + Sum((-y)**2, (y, -b, -a)) + assert Sum(x, (x, a, b)).change_index( x, -x - 1) == \ + Sum(-x - 1, (x, -b - 1, -a - 1)) + assert Sum(x*y, (x, a, b), (y, c, d)).change_index( x, x - 1, z) == \ + Sum((z + 1)*y, (z, a - 1, b - 1), (y, c, d)) + assert Sum(x, (x, a, b)).change_index( x, x + v) == \ + Sum(-v + x, (x, a + v, b + v)) + assert Sum(x, (x, a, b)).change_index( x, -x - v) == \ + Sum(-v - x, (x, -b - v, -a - v)) + assert Sum(x, (x, a, b)).change_index(x, w*x, v) == \ + Sum(v/w, (v, b*w, a*w)) + raises(ValueError, lambda: Sum(x, (x, a, b)).change_index(x, 2*x)) + + +def test_reorder(): + b, y, c, d, z = symbols('b, y, c, d, z', integer = True) + + assert Sum(x*y, (x, a, b), (y, c, d)).reorder((0, 1)) == \ + Sum(x*y, (y, c, d), (x, a, b)) + assert Sum(x, (x, a, b), (x, c, d)).reorder((0, 1)) == \ + Sum(x, (x, c, d), (x, a, b)) + assert Sum(x*y + z, (x, a, b), (z, m, n), (y, c, d)).reorder(\ + (2, 0), (0, 1)) == Sum(x*y + z, (z, m, n), (y, c, d), (x, a, b)) + assert Sum(x*y*z, (x, a, b), (y, c, d), (z, m, n)).reorder(\ + (0, 1), (1, 2), (0, 2)) == Sum(x*y*z, (x, a, b), (z, m, n), (y, c, d)) + assert Sum(x*y*z, (x, a, b), (y, c, d), (z, m, n)).reorder(\ + (x, y), (y, z), (x, z)) == Sum(x*y*z, (x, a, b), (z, m, n), (y, c, d)) + assert Sum(x*y, (x, a, b), (y, c, d)).reorder((x, 1)) == \ + Sum(x*y, (y, c, d), (x, a, b)) + assert Sum(x*y, (x, a, b), (y, c, d)).reorder((y, x)) == \ + Sum(x*y, (y, c, d), (x, a, b)) + + +def test_reverse_order(): + assert Sum(x, (x, 0, 3)).reverse_order(0) == Sum(-x, (x, 4, -1)) + assert Sum(x*y, (x, 1, 5), (y, 0, 6)).reverse_order(0, 1) == \ + Sum(x*y, (x, 6, 0), (y, 7, -1)) + assert Sum(x, (x, 1, 2)).reverse_order(0) == Sum(-x, (x, 3, 0)) + assert Sum(x, (x, 1, 3)).reverse_order(0) == Sum(-x, (x, 4, 0)) + assert Sum(x, (x, 1, a)).reverse_order(0) == Sum(-x, (x, a + 1, 0)) + assert Sum(x, (x, a, 5)).reverse_order(0) == Sum(-x, (x, 6, a - 1)) + assert Sum(x, (x, a + 1, a + 5)).reverse_order(0) == \ + Sum(-x, (x, a + 6, a)) + assert Sum(x, (x, a + 1, a + 2)).reverse_order(0) == \ + Sum(-x, (x, a + 3, a)) + assert Sum(x, (x, a + 1, a + 1)).reverse_order(0) == \ + Sum(-x, (x, a + 2, a)) + assert Sum(x, (x, a, b)).reverse_order(0) == Sum(-x, (x, b + 1, a - 1)) + assert Sum(x, (x, a, b)).reverse_order(x) == Sum(-x, (x, b + 1, a - 1)) + assert Sum(x*y, (x, a, b), (y, 2, 5)).reverse_order(x, 1) == \ + Sum(x*y, (x, b + 1, a - 1), (y, 6, 1)) + assert Sum(x*y, (x, a, b), (y, 2, 5)).reverse_order(y, x) == \ + Sum(x*y, (x, b + 1, a - 1), (y, 6, 1)) + + +def test_issue_7097(): + assert sum(x**n/n for n in range(1, 401)) == summation(x**n/n, (n, 1, 400)) + + +def test_factor_expand_subs(): + # test factoring + assert Sum(4 * x, (x, 1, y)).factor() == 4 * Sum(x, (x, 1, y)) + assert Sum(x * a, (x, 1, y)).factor() == a * Sum(x, (x, 1, y)) + assert Sum(4 * x * a, (x, 1, y)).factor() == 4 * a * Sum(x, (x, 1, y)) + assert Sum(4 * x * y, (x, 1, y)).factor() == 4 * y * Sum(x, (x, 1, y)) + + # test expand + _x = Symbol('x', zero=False) + assert Sum(x+1,(x,1,y)).expand() == Sum(x,(x,1,y)) + Sum(1,(x,1,y)) + assert Sum(x+a*x**2,(x,1,y)).expand() == Sum(x,(x,1,y)) + Sum(a*x**2,(x,1,y)) + assert Sum(_x**(n + 1)*(n + 1), (n, -1, oo)).expand() \ + == Sum(n*_x*_x**n + _x*_x**n, (n, -1, oo)) + assert Sum(x**(n + 1)*(n + 1), (n, -1, oo)).expand(power_exp=False) \ + == Sum(n*x**(n + 1) + x**(n + 1), (n, -1, oo)) + assert Sum(x**(n + 1)*(n + 1), (n, -1, oo)).expand(force=True) \ + == Sum(x*x**n, (n, -1, oo)) + Sum(n*x*x**n, (n, -1, oo)) + assert Sum(a*n+a*n**2,(n,0,4)).expand() \ + == Sum(a*n,(n,0,4)) + Sum(a*n**2,(n,0,4)) + assert Sum(_x**a*_x**n,(x,0,3)) \ + == Sum(_x**(a+n),(x,0,3)).expand(power_exp=True) + _a, _n = symbols('a n', positive=True) + assert Sum(x**(_a+_n),(x,0,3)).expand(power_exp=True) \ + == Sum(x**_a*x**_n, (x, 0, 3)) + assert Sum(x**(_a-_n),(x,0,3)).expand(power_exp=True) \ + == Sum(x**(_a-_n),(x,0,3)).expand(power_exp=False) + + # test subs + assert Sum(1/(1+a*x**2),(x,0,3)).subs([(a,3)]) == Sum(1/(1+3*x**2),(x,0,3)) + assert Sum(x*y,(x,0,y),(y,0,x)).subs([(x,3)]) == Sum(x*y,(x,0,y),(y,0,3)) + assert Sum(x,(x,1,10)).subs([(x,y-2)]) == Sum(x,(x,1,10)) + assert Sum(1/x,(x,1,10)).subs([(x,(3+n)**3)]) == Sum(1/x,(x,1,10)) + assert Sum(1/x,(x,1,10)).subs([(x,3*x-2)]) == Sum(1/x,(x,1,10)) + + +def test_distribution_over_equality(): + assert Product(Eq(x*2, f(x)), (x, 1, 3)).doit() == Eq(48, f(1)*f(2)*f(3)) + assert Sum(Eq(f(x), x**2), (x, 0, y)) == \ + Eq(Sum(f(x), (x, 0, y)), Sum(x**2, (x, 0, y))) + + +def test_issue_2787(): + n, k = symbols('n k', positive=True, integer=True) + p = symbols('p', positive=True) + binomial_dist = binomial(n, k)*p**k*(1 - p)**(n - k) + s = Sum(binomial_dist*k, (k, 0, n)) + res = s.doit().simplify() + ans = Piecewise( + (n*p, x), + (Sum(k*p**k*binomial(n, k)*(1 - p)**(n - k), (k, 0, n)), + True)).subs(x, (Eq(n, 1) | (n > 1)) & (p/Abs(p - 1) <= 1)) + ans2 = Piecewise( + (n*p, x), + (factorial(n)*Sum(p**k*(1 - p)**(-k + n)/ + (factorial(-k + n)*factorial(k - 1)), (k, 0, n)), + True)).subs(x, (Eq(n, 1) | (n > 1)) & (p/Abs(p - 1) <= 1)) + assert res in [ans, ans2] # XXX system dependent + # Issue #17165: make sure that another simplify does not complicate + # the result by much. Why didn't first simplify replace + # Eq(n, 1) | (n > 1) with True? + assert res.simplify().count_ops() <= res.count_ops() + 2 + + +def test_issue_4668(): + assert summation(1/n, (n, 2, oo)) is oo + + +def test_matrix_sum(): + A = Matrix([[0, 1], [n, 0]]) + + result = Sum(A, (n, 0, 3)).doit() + assert result == Matrix([[0, 4], [6, 0]]) + assert result.__class__ == ImmutableDenseMatrix + + A = SparseMatrix([[0, 1], [n, 0]]) + + result = Sum(A, (n, 0, 3)).doit() + assert result.__class__ == ImmutableSparseMatrix + + +def test_failing_matrix_sum(): + n = Symbol('n') + # TODO Implement matrix geometric series summation. + A = Matrix([[0, 1, 0], [-1, 0, 0], [0, 0, 0]]) + assert Sum(A ** n, (n, 1, 4)).doit() == \ + Matrix([[0, 0, 0], [0, 0, 0], [0, 0, 0]]) + # issue sympy/sympy#16989 + assert summation(A**n, (n, 1, 1)) == A + + +def test_indexed_idx_sum(): + i = symbols('i', cls=Idx) + r = Indexed('r', i) + assert Sum(r, (i, 0, 3)).doit() == sum(r.xreplace({i: j}) for j in range(4)) + assert Product(r, (i, 0, 3)).doit() == prod([r.xreplace({i: j}) for j in range(4)]) + + j = symbols('j', integer=True) + assert Sum(r, (i, j, j+2)).doit() == sum(r.xreplace({i: j+k}) for k in range(3)) + assert Product(r, (i, j, j+2)).doit() == prod([r.xreplace({i: j+k}) for k in range(3)]) + + k = Idx('k', range=(1, 3)) + A = IndexedBase('A') + assert Sum(A[k], k).doit() == sum(A[Idx(j, (1, 3))] for j in range(1, 4)) + assert Product(A[k], k).doit() == prod([A[Idx(j, (1, 3))] for j in range(1, 4)]) + + raises(ValueError, lambda: Sum(A[k], (k, 1, 4))) + raises(ValueError, lambda: Sum(A[k], (k, 0, 3))) + raises(ValueError, lambda: Sum(A[k], (k, 2, oo))) + + raises(ValueError, lambda: Product(A[k], (k, 1, 4))) + raises(ValueError, lambda: Product(A[k], (k, 0, 3))) + raises(ValueError, lambda: Product(A[k], (k, 2, oo))) + + +@slow +def test_is_convergent(): + # divergence tests -- + assert Sum(n/(2*n + 1), (n, 1, oo)).is_convergent() is S.false + assert Sum(factorial(n)/5**n, (n, 1, oo)).is_convergent() is S.false + assert Sum(3**(-2*n - 1)*n**n, (n, 1, oo)).is_convergent() is S.false + assert Sum((-1)**n*n, (n, 3, oo)).is_convergent() is S.false + assert Sum((-1)**n, (n, 1, oo)).is_convergent() is S.false + assert Sum(log(1/n), (n, 2, oo)).is_convergent() is S.false + assert Sum(sin(n), (n, 1, oo)).is_convergent() is S.false + + # Raabe's test -- + assert Sum(Product((3*m),(m,1,n))/Product((3*m+4),(m,1,n)),(n,1,oo)).is_convergent() is S.true + + # root test -- + assert Sum((-12)**n/n, (n, 1, oo)).is_convergent() is S.false + + # integral test -- + + # p-series test -- + assert Sum(1/(n**2 + 1), (n, 1, oo)).is_convergent() is S.true + assert Sum(1/n**Rational(6, 5), (n, 1, oo)).is_convergent() is S.true + assert Sum(2/(n*sqrt(n - 1)), (n, 2, oo)).is_convergent() is S.true + assert Sum(1/(sqrt(n)*sqrt(n)), (n, 2, oo)).is_convergent() is S.false + assert Sum(factorial(n) / factorial(n+2), (n, 1, oo)).is_convergent() is S.true + assert Sum(rf(5,n)/rf(7,n),(n,1,oo)).is_convergent() is S.true + assert Sum((rf(1, n)*rf(2, n))/(rf(3, n)*factorial(n)),(n,1,oo)).is_convergent() is S.false + + # comparison test -- + assert Sum(1/(n + log(n)), (n, 1, oo)).is_convergent() is S.false + assert Sum(1/(n**2*log(n)), (n, 2, oo)).is_convergent() is S.true + assert Sum(1/(n*log(n)), (n, 2, oo)).is_convergent() is S.false + assert Sum(2/(n*log(n)*log(log(n))**2), (n, 5, oo)).is_convergent() is S.true + assert Sum(2/(n*log(n)**2), (n, 2, oo)).is_convergent() is S.true + assert Sum((n - 1)/(n**2*log(n)**3), (n, 2, oo)).is_convergent() is S.true + assert Sum(1/(n*log(n)*log(log(n))), (n, 5, oo)).is_convergent() is S.false + assert Sum((n - 1)/(n*log(n)**3), (n, 3, oo)).is_convergent() is S.false + assert Sum(2/(n**2*log(n)), (n, 2, oo)).is_convergent() is S.true + assert Sum(1/(n*sqrt(log(n))*log(log(n))), (n, 100, oo)).is_convergent() is S.false + assert Sum(log(log(n))/(n*log(n)**2), (n, 100, oo)).is_convergent() is S.true + assert Sum(log(n)/n**2, (n, 5, oo)).is_convergent() is S.true + + # alternating series tests -- + assert Sum((-1)**(n - 1)/(n**2 - 1), (n, 3, oo)).is_convergent() is S.true + + # with -negativeInfinite Limits + assert Sum(1/(n**2 + 1), (n, -oo, 1)).is_convergent() is S.true + assert Sum(1/(n - 1), (n, -oo, -1)).is_convergent() is S.false + assert Sum(1/(n**2 - 1), (n, -oo, -5)).is_convergent() is S.true + assert Sum(1/(n**2 - 1), (n, -oo, 2)).is_convergent() is S.true + assert Sum(1/(n**2 - 1), (n, -oo, oo)).is_convergent() is S.true + + # piecewise functions + f = Piecewise((n**(-2), n <= 1), (n**2, n > 1)) + assert Sum(f, (n, 1, oo)).is_convergent() is S.false + assert Sum(f, (n, -oo, oo)).is_convergent() is S.false + assert Sum(f, (n, 1, 100)).is_convergent() is S.true + #assert Sum(f, (n, -oo, 1)).is_convergent() is S.true + + # integral test + + assert Sum(log(n)/n**3, (n, 1, oo)).is_convergent() is S.true + assert Sum(-log(n)/n**3, (n, 1, oo)).is_convergent() is S.true + # the following function has maxima located at (x, y) = + # (1.2, 0.43), (3.0, -0.25) and (6.8, 0.050) + eq = (x - 2)*(x**2 - 6*x + 4)*exp(-x) + assert Sum(eq, (x, 1, oo)).is_convergent() is S.true + assert Sum(eq, (x, 1, 2)).is_convergent() is S.true + assert Sum(1/(x**3), (x, 1, oo)).is_convergent() is S.true + assert Sum(1/(x**S.Half), (x, 1, oo)).is_convergent() is S.false + + # issue 19545 + assert Sum(1/n - 3/(3*n +2), (n, 1, oo)).is_convergent() is S.true + + # issue 19836 + assert Sum(4/(n + 2) - 5/(n + 1) + 1/n,(n, 7, oo)).is_convergent() is S.true + + +def test_is_absolutely_convergent(): + assert Sum((-1)**n, (n, 1, oo)).is_absolutely_convergent() is S.false + assert Sum((-1)**n/n**2, (n, 1, oo)).is_absolutely_convergent() is S.true + + +@XFAIL +def test_convergent_failing(): + # dirichlet tests + assert Sum(sin(n)/n, (n, 1, oo)).is_convergent() is S.true + assert Sum(sin(2*n)/n, (n, 1, oo)).is_convergent() is S.true + + +def test_issue_6966(): + i, k, m = symbols('i k m', integer=True) + z_i, q_i = symbols('z_i q_i') + a_k = Sum(-q_i*z_i/k,(i,1,m)) + b_k = a_k.diff(z_i) + assert isinstance(b_k, Sum) + assert b_k == Sum(-q_i/k,(i,1,m)) + + +def test_issue_10156(): + cx = Sum(2*y**2*x, (x, 1,3)) + e = 2*y*Sum(2*cx*x**2, (x, 1, 9)) + assert e.factor() == \ + 8*y**3*Sum(x, (x, 1, 3))*Sum(x**2, (x, 1, 9)) + + +def test_issue_10973(): + assert Sum((-n + (n**3 + 1)**(S(1)/3))/log(n), (n, 1, oo)).is_convergent() is S.true + + +def test_issue_14103(): + assert Sum(sin(n)**2 + cos(n)**2 - 1, (n, 1, oo)).is_convergent() is S.true + assert Sum(sin(pi*n), (n, 1, oo)).is_convergent() is S.true + + +def test_issue_14129(): + x = Symbol('x', zero=False) + assert Sum( k*x**k, (k, 0, n-1)).doit() == \ + Piecewise((n**2/2 - n/2, Eq(x, 1)), ((n*x*x**n - + n*x**n - x*x**n + x)/(x - 1)**2, True)) + assert Sum( x**k, (k, 0, n-1)).doit() == \ + Piecewise((n, Eq(x, 1)), ((-x**n + 1)/(-x + 1), True)) + assert Sum( k*(x/y+x)**k, (k, 0, n-1)).doit() == \ + Piecewise((n*(n - 1)/2, Eq(x, y/(y + 1))), + (x*(y + 1)*(n*x*y*(x + x/y)**(n - 1) + + n*x*(x + x/y)**(n - 1) - n*y*(x + x/y)**(n - 1) - + x*y*(x + x/y)**(n - 1) - x*(x + x/y)**(n - 1) + y)/ + (x*y + x - y)**2, True)) + + +def test_issue_14112(): + assert Sum((-1)**n/sqrt(n), (n, 1, oo)).is_absolutely_convergent() is S.false + assert Sum((-1)**(2*n)/n, (n, 1, oo)).is_convergent() is S.false + assert Sum((-2)**n + (-3)**n, (n, 1, oo)).is_convergent() is S.false + + +def test_issue_14219(): + A = diag(0, 2, -3) + res = diag(1, 15, -20) + assert Sum(A**n, (n, 0, 3)).doit() == res + + +def test_sin_times_absolutely_convergent(): + assert Sum(sin(n) / n**3, (n, 1, oo)).is_convergent() is S.true + assert Sum(sin(n) * log(n) / n**3, (n, 1, oo)).is_convergent() is S.true + + +def test_issue_14111(): + assert Sum(1/log(log(n)), (n, 22, oo)).is_convergent() is S.false + + +def test_issue_14484(): + assert Sum(sin(n)/log(log(n)), (n, 22, oo)).is_convergent() is S.false + + +def test_issue_14640(): + i, n = symbols("i n", integer=True) + a, b, c = symbols("a b c", zero=False) + + assert Sum(a**-i/(a - b), (i, 0, n)).doit() == Sum( + 1/(a*a**i - a**i*b), (i, 0, n)).doit() == Piecewise( + (n + 1, Eq(1/a, 1)), + ((-a**(-n - 1) + 1)/(1 - 1/a), True))/(a - b) + + assert Sum((b*a**i - c*a**i)**-2, (i, 0, n)).doit() == Piecewise( + (n + 1, Eq(a**(-2), 1)), + ((-a**(-2*n - 2) + 1)/(1 - 1/a**2), True))/(b - c)**2 + + s = Sum(i*(a**(n - i) - b**(n - i))/(a - b), (i, 0, n)).doit() + assert not s.has(Sum) + assert s.subs({a: 2, b: 3, n: 5}) == 122 + + +def test_issue_15943(): + s = Sum(binomial(n, k)*factorial(n - k), (k, 0, n)).doit().rewrite(gamma) + assert s == -E*(n + 1)*gamma(n + 1)*lowergamma(n + 1, 1)/gamma(n + 2 + ) + E*gamma(n + 1) + assert s.simplify() == E*(factorial(n) - lowergamma(n + 1, 1)) + + +def test_Sum_dummy_eq(): + assert not Sum(x, (x, a, b)).dummy_eq(1) + assert not Sum(x, (x, a, b)).dummy_eq(Sum(x, (x, a, b), (a, 1, 2))) + assert not Sum(x, (x, a, b)).dummy_eq(Sum(x, (x, a, c))) + assert Sum(x, (x, a, b)).dummy_eq(Sum(x, (x, a, b))) + d = Dummy() + assert Sum(x, (x, a, d)).dummy_eq(Sum(x, (x, a, c)), c) + assert not Sum(x, (x, a, d)).dummy_eq(Sum(x, (x, a, c))) + assert Sum(x, (x, a, c)).dummy_eq(Sum(y, (y, a, c))) + assert Sum(x, (x, a, d)).dummy_eq(Sum(y, (y, a, c)), c) + assert not Sum(x, (x, a, d)).dummy_eq(Sum(y, (y, a, c))) + + +def test_issue_15852(): + assert summation(x**y*y, (y, -oo, oo)).doit() == Sum(x**y*y, (y, -oo, oo)) + + +def test_exceptions(): + S = Sum(x, (x, a, b)) + raises(ValueError, lambda: S.change_index(x, x**2, y)) + S = Sum(x, (x, a, b), (x, 1, 4)) + raises(ValueError, lambda: S.index(x)) + S = Sum(x, (x, a, b), (y, 1, 4)) + raises(ValueError, lambda: S.reorder([x])) + S = Sum(x, (x, y, b), (y, 1, 4)) + raises(ReorderError, lambda: S.reorder_limit(0, 1)) + S = Sum(x*y, (x, a, b), (y, 1, 4)) + raises(NotImplementedError, lambda: S.is_convergent()) + + +def test_sumproducts_assumptions(): + M = Symbol('M', integer=True, positive=True) + + m = Symbol('m', integer=True) + for func in [Sum, Product]: + assert func(m, (m, -M, M)).is_positive is None + assert func(m, (m, -M, M)).is_nonpositive is None + assert func(m, (m, -M, M)).is_negative is None + assert func(m, (m, -M, M)).is_nonnegative is None + assert func(m, (m, -M, M)).is_finite is True + + m = Symbol('m', integer=True, nonnegative=True) + for func in [Sum, Product]: + assert func(m, (m, 0, M)).is_positive is None + assert func(m, (m, 0, M)).is_nonpositive is None + assert func(m, (m, 0, M)).is_negative is False + assert func(m, (m, 0, M)).is_nonnegative is True + assert func(m, (m, 0, M)).is_finite is True + + m = Symbol('m', integer=True, positive=True) + for func in [Sum, Product]: + assert func(m, (m, 1, M)).is_positive is True + assert func(m, (m, 1, M)).is_nonpositive is False + assert func(m, (m, 1, M)).is_negative is False + assert func(m, (m, 1, M)).is_nonnegative is True + assert func(m, (m, 1, M)).is_finite is True + + m = Symbol('m', integer=True, negative=True) + assert Sum(m, (m, -M, -1)).is_positive is False + assert Sum(m, (m, -M, -1)).is_nonpositive is True + assert Sum(m, (m, -M, -1)).is_negative is True + assert Sum(m, (m, -M, -1)).is_nonnegative is False + assert Sum(m, (m, -M, -1)).is_finite is True + assert Product(m, (m, -M, -1)).is_positive is None + assert Product(m, (m, -M, -1)).is_nonpositive is None + assert Product(m, (m, -M, -1)).is_negative is None + assert Product(m, (m, -M, -1)).is_nonnegative is None + assert Product(m, (m, -M, -1)).is_finite is True + + m = Symbol('m', integer=True, nonpositive=True) + assert Sum(m, (m, -M, 0)).is_positive is False + assert Sum(m, (m, -M, 0)).is_nonpositive is True + assert Sum(m, (m, -M, 0)).is_negative is None + assert Sum(m, (m, -M, 0)).is_nonnegative is None + assert Sum(m, (m, -M, 0)).is_finite is True + assert Product(m, (m, -M, 0)).is_positive is None + assert Product(m, (m, -M, 0)).is_nonpositive is None + assert Product(m, (m, -M, 0)).is_negative is None + assert Product(m, (m, -M, 0)).is_nonnegative is None + assert Product(m, (m, -M, 0)).is_finite is True + + m = Symbol('m', integer=True) + assert Sum(2, (m, 0, oo)).is_positive is None + assert Sum(2, (m, 0, oo)).is_nonpositive is None + assert Sum(2, (m, 0, oo)).is_negative is None + assert Sum(2, (m, 0, oo)).is_nonnegative is None + assert Sum(2, (m, 0, oo)).is_finite is None + + assert Product(2, (m, 0, oo)).is_positive is None + assert Product(2, (m, 0, oo)).is_nonpositive is None + assert Product(2, (m, 0, oo)).is_negative is False + assert Product(2, (m, 0, oo)).is_nonnegative is None + assert Product(2, (m, 0, oo)).is_finite is None + + assert Product(0, (x, M, M-1)).is_positive is True + assert Product(0, (x, M, M-1)).is_finite is True + + +def test_expand_with_assumptions(): + M = Symbol('M', integer=True, positive=True) + x = Symbol('x', positive=True) + m = Symbol('m', nonnegative=True) + assert log(Product(x**m, (m, 0, M))).expand() == Sum(m*log(x), (m, 0, M)) + assert log(Product(exp(x**m), (m, 0, M))).expand() == Sum(x**m, (m, 0, M)) + assert log(Product(x**m, (m, 0, M))).rewrite(Sum).expand() == Sum(m*log(x), (m, 0, M)) + assert log(Product(exp(x**m), (m, 0, M))).rewrite(Sum).expand() == Sum(x**m, (m, 0, M)) + + n = Symbol('n', nonnegative=True) + i, j = symbols('i,j', positive=True, integer=True) + x, y = symbols('x,y', positive=True) + assert log(Product(x**i*y**j, (i, 1, n), (j, 1, m))).expand() \ + == Sum(i*log(x) + j*log(y), (i, 1, n), (j, 1, m)) + + m = Symbol('m', nonnegative=True, integer=True) + s = Sum(x**m, (m, 0, M)) + s_as_product = s.rewrite(Product) + assert s_as_product.has(Product) + assert s_as_product == log(Product(exp(x**m), (m, 0, M))) + assert s_as_product.expand() == s + s5 = s.subs(M, 5) + s5_as_product = s5.rewrite(Product) + assert s5_as_product.has(Product) + assert s5_as_product.doit().expand() == s5.doit() + + +def test_has_finite_limits(): + x = Symbol('x') + assert Sum(1, (x, 1, 9)).has_finite_limits is True + assert Sum(1, (x, 1, oo)).has_finite_limits is False + M = Symbol('M') + assert Sum(1, (x, 1, M)).has_finite_limits is None + M = Symbol('M', positive=True) + assert Sum(1, (x, 1, M)).has_finite_limits is True + x = Symbol('x', positive=True) + M = Symbol('M') + assert Sum(1, (x, 1, M)).has_finite_limits is True + + assert Sum(1, (x, 1, M), (y, -oo, oo)).has_finite_limits is False + +def test_has_reversed_limits(): + assert Sum(1, (x, 1, 1)).has_reversed_limits is False + assert Sum(1, (x, 1, 9)).has_reversed_limits is False + assert Sum(1, (x, 1, -9)).has_reversed_limits is True + assert Sum(1, (x, 1, 0)).has_reversed_limits is True + assert Sum(1, (x, 1, oo)).has_reversed_limits is False + M = Symbol('M') + assert Sum(1, (x, 1, M)).has_reversed_limits is None + M = Symbol('M', positive=True, integer=True) + assert Sum(1, (x, 1, M)).has_reversed_limits is False + assert Sum(1, (x, 1, M), (y, -oo, oo)).has_reversed_limits is False + M = Symbol('M', negative=True) + assert Sum(1, (x, 1, M)).has_reversed_limits is True + + assert Sum(1, (x, 1, M), (y, -oo, oo)).has_reversed_limits is True + assert Sum(1, (x, oo, oo)).has_reversed_limits is None + + +def test_has_empty_sequence(): + assert Sum(1, (x, 1, 1)).has_empty_sequence is False + assert Sum(1, (x, 1, 9)).has_empty_sequence is False + assert Sum(1, (x, 1, -9)).has_empty_sequence is False + assert Sum(1, (x, 1, 0)).has_empty_sequence is True + assert Sum(1, (x, y, y - 1)).has_empty_sequence is True + assert Sum(1, (x, 3, 2), (y, -oo, oo)).has_empty_sequence is True + assert Sum(1, (y, -oo, oo), (x, 3, 2)).has_empty_sequence is True + assert Sum(1, (x, oo, oo)).has_empty_sequence is False + + +def test_empty_sequence(): + assert Product(x*y, (x, -oo, oo), (y, 1, 0)).doit() == 1 + assert Product(x*y, (y, 1, 0), (x, -oo, oo)).doit() == 1 + assert Sum(x, (x, -oo, oo), (y, 1, 0)).doit() == 0 + assert Sum(x, (y, 1, 0), (x, -oo, oo)).doit() == 0 + + +def test_issue_8016(): + k = Symbol('k', integer=True) + n, m = symbols('n, m', integer=True, positive=True) + s = Sum(binomial(m, k)*binomial(m, n - k)*(-1)**k, (k, 0, n)) + assert s.doit().simplify() == \ + cos(pi*n/2)*gamma(m + 1)/gamma(n/2 + 1)/gamma(m - n/2 + 1) + + +def test_issue_14313(): + assert Sum(S.Half**floor(n/2), (n, 1, oo)).is_convergent() + + +def test_issue_14563(): + # The assertion was failing due to no assumptions methods in Sums and Product + assert 1 % Sum(1, (x, 0, 1)) == 1 + + +def test_issue_16735(): + assert Sum(5**n/gamma(n+1), (n, 1, oo)).is_convergent() is S.true + + +def test_issue_14871(): + assert Sum((Rational(1, 10))**n*rf(0, n)/factorial(n), (n, 0, oo)).rewrite(factorial).doit() == 1 + + +def test_issue_17165(): + n = symbols("n", integer=True) + x = symbols('x') + s = (x*Sum(x**n, (n, -1, oo))) + ssimp = s.doit().simplify() + + assert ssimp == Piecewise((-1/(x - 1), (x > -1) & (x < 1)), + (x*Sum(x**n, (n, -1, oo)), True)), ssimp + assert ssimp.simplify() == ssimp + + +def test_issue_19379(): + assert Sum(factorial(n)/factorial(n + 2), (n, 1, oo)).is_convergent() is S.true + + +def test_issue_20777(): + assert Sum(exp(x*sin(n/m)), (n, 1, m)).doit() == Sum(exp(x*sin(n/m)), (n, 1, m)) + + +def test__dummy_with_inherited_properties_concrete(): + x = Symbol('x') + + from sympy.core.containers import Tuple + d = _dummy_with_inherited_properties_concrete(Tuple(x, 0, 5)) + assert d.is_real + assert d.is_integer + assert d.is_nonnegative + assert d.is_extended_nonnegative + + d = _dummy_with_inherited_properties_concrete(Tuple(x, 1, 9)) + assert d.is_real + assert d.is_integer + assert d.is_positive + assert d.is_odd is None + + d = _dummy_with_inherited_properties_concrete(Tuple(x, -5, 5)) + assert d.is_real + assert d.is_integer + assert d.is_positive is None + assert d.is_extended_nonnegative is None + assert d.is_odd is None + + d = _dummy_with_inherited_properties_concrete(Tuple(x, -1.5, 1.5)) + assert d.is_real + assert d.is_integer is None + assert d.is_positive is None + assert d.is_extended_nonnegative is None + + N = Symbol('N', integer=True, positive=True) + d = _dummy_with_inherited_properties_concrete(Tuple(x, 2, N)) + assert d.is_real + assert d.is_positive + assert d.is_integer + + # Return None if no assumptions are added + N = Symbol('N', integer=True, positive=True) + d = _dummy_with_inherited_properties_concrete(Tuple(N, 2, 4)) + assert d is None + + x = Symbol('x', negative=True) + raises(InconsistentAssumptions, + lambda: _dummy_with_inherited_properties_concrete(Tuple(x, 1, 5))) + + +def test_matrixsymbol_summation_numerical_limits(): + A = MatrixSymbol('A', 3, 3) + n = Symbol('n', integer=True) + + assert Sum(A**n, (n, 0, 2)).doit() == Identity(3) + A + A**2 + assert Sum(A, (n, 0, 2)).doit() == 3*A + assert Sum(n*A, (n, 0, 2)).doit() == 3*A + + B = Matrix([[0, n, 0], [-1, 0, 0], [0, 0, 2]]) + ans = Matrix([[0, 6, 0], [-4, 0, 0], [0, 0, 8]]) + 4*A + assert Sum(A+B, (n, 0, 3)).doit() == ans + ans = A*Matrix([[0, 6, 0], [-4, 0, 0], [0, 0, 8]]) + assert Sum(A*B, (n, 0, 3)).doit() == ans + + ans = (A**2*Matrix([[-2, 0, 0], [0,-2, 0], [0, 0, 4]]) + + A**3*Matrix([[0, -9, 0], [3, 0, 0], [0, 0, 8]]) + + A*Matrix([[0, 1, 0], [-1, 0, 0], [0, 0, 2]])) + assert Sum(A**n*B**n, (n, 1, 3)).doit() == ans + + +def test_issue_21651(): + i = Symbol('i') + a = Sum(floor(2*2**(-i)), (i, S.One, 2)) + assert a.doit() == S.One + + +@XFAIL +def test_matrixsymbol_summation_symbolic_limits(): + N = Symbol('N', integer=True, positive=True) + + A = MatrixSymbol('A', 3, 3) + n = Symbol('n', integer=True) + assert Sum(A, (n, 0, N)).doit() == (N+1)*A + assert Sum(n*A, (n, 0, N)).doit() == (N**2/2+N/2)*A + + +def test_summation_by_residues(): + x = Symbol('x') + + # Examples from Nakhle H. Asmar, Loukas Grafakos, + # Complex Analysis with Applications + assert eval_sum_residue(1 / (x**2 + 1), (x, -oo, oo)) == pi/tanh(pi) + assert eval_sum_residue(1 / x**6, (x, S(1), oo)) == pi**6/945 + assert eval_sum_residue(1 / (x**2 + 9), (x, -oo, oo)) == pi/(3*tanh(3*pi)) + assert eval_sum_residue(1 / (x**2 + 1)**2, (x, -oo, oo)).cancel() == \ + (-pi**2*tanh(pi)**2 + pi*tanh(pi) + pi**2)/(2*tanh(pi)**2) + assert eval_sum_residue(x**2 / (x**2 + 1)**2, (x, -oo, oo)).cancel() == \ + (-pi**2 + pi*tanh(pi) + pi**2*tanh(pi)**2)/(2*tanh(pi)**2) + assert eval_sum_residue(1 / (4*x**2 - 1), (x, -oo, oo)) == 0 + assert eval_sum_residue(x**2 / (x**2 - S(1)/4)**2, (x, -oo, oo)) == pi**2/2 + assert eval_sum_residue(1 / (4*x**2 - 1)**2, (x, -oo, oo)) == pi**2/8 + assert eval_sum_residue(1 / ((x - S(1)/2)**2 + 1), (x, -oo, oo)) == pi*tanh(pi) + assert eval_sum_residue(1 / x**2, (x, S(1), oo)) == pi**2/6 + assert eval_sum_residue(1 / x**4, (x, S(1), oo)) == pi**4/90 + assert eval_sum_residue(1 / x**2 / (x**2 + 4), (x, S(1), oo)) == \ + -pi*(-pi/12 - 1/(16*pi) + 1/(8*tanh(2*pi)))/2 + + # Some examples made from 1 / (x**2 + 1) + assert eval_sum_residue(1 / (x**2 + 1), (x, S(0), oo)) == \ + S(1)/2 + pi/(2*tanh(pi)) + assert eval_sum_residue(1 / (x**2 + 1), (x, S(1), oo)) == \ + -S(1)/2 + pi/(2*tanh(pi)) + assert eval_sum_residue(1 / (x**2 + 1), (x, S(-1), oo)) == \ + 1 + pi/(2*tanh(pi)) + assert eval_sum_residue((-1)**x / (x**2 + 1), (x, -oo, oo)) == \ + pi/sinh(pi) + assert eval_sum_residue((-1)**x / (x**2 + 1), (x, S(0), oo)) == \ + pi/(2*sinh(pi)) + S(1)/2 + assert eval_sum_residue((-1)**x / (x**2 + 1), (x, S(1), oo)) == \ + -S(1)/2 + pi/(2*sinh(pi)) + assert eval_sum_residue((-1)**x / (x**2 + 1), (x, S(-1), oo)) == \ + pi/(2*sinh(pi)) + + # Some examples made from shifting of 1 / (x**2 + 1) + assert eval_sum_residue(1 / (x**2 + 2*x + 2), (x, S(-1), oo)) == S(1)/2 + pi/(2*tanh(pi)) + assert eval_sum_residue(1 / (x**2 + 4*x + 5), (x, S(-2), oo)) == S(1)/2 + pi/(2*tanh(pi)) + assert eval_sum_residue(1 / (x**2 - 2*x + 2), (x, S(1), oo)) == S(1)/2 + pi/(2*tanh(pi)) + assert eval_sum_residue(1 / (x**2 - 4*x + 5), (x, S(2), oo)) == S(1)/2 + pi/(2*tanh(pi)) + assert eval_sum_residue((-1)**x * -1 / (x**2 + 2*x + 2), (x, S(-1), oo)) == S(1)/2 + pi/(2*sinh(pi)) + assert eval_sum_residue((-1)**x * -1 / (x**2 -2*x + 2), (x, S(1), oo)) == S(1)/2 + pi/(2*sinh(pi)) + + # Some examples made from 1 / x**2 + assert eval_sum_residue(1 / x**2, (x, S(2), oo)) == -1 + pi**2/6 + assert eval_sum_residue(1 / x**2, (x, S(3), oo)) == -S(5)/4 + pi**2/6 + assert eval_sum_residue((-1)**x / x**2, (x, S(1), oo)) == -pi**2/12 + assert eval_sum_residue((-1)**x / x**2, (x, S(2), oo)) == 1 - pi**2/12 + + +@slow +def test_summation_by_residues_failing(): + x = Symbol('x') + + # Failing because of the bug in residue computation + assert eval_sum_residue(x**2 / (x**4 + 1), (x, S(1), oo)) + assert eval_sum_residue(1 / ((x - 1)*(x - 2) + 1), (x, -oo, oo)) != 0 + + +def test_process_limits(): + from sympy.concrete.expr_with_limits import _process_limits + + # these should be (x, Range(3)) not Range(3) + raises(ValueError, lambda: _process_limits( + Range(3), discrete=True)) + raises(ValueError, lambda: _process_limits( + Range(3), discrete=False)) + # these should be (x, union) not union + # (but then we would get a TypeError because we don't + # handle non-contiguous sets: see below use of `union`) + union = Or(x < 1, x > 3).as_set() + raises(ValueError, lambda: _process_limits( + union, discrete=True)) + raises(ValueError, lambda: _process_limits( + union, discrete=False)) + + # error not triggered if not needed + assert _process_limits((x, 1, 2)) == ([(x, 1, 2)], 1) + + # this equivalence is used to detect Reals in _process_limits + assert isinstance(S.Reals, Interval) + + C = Integral # continuous limits + assert C(x, x >= 5) == C(x, (x, 5, oo)) + assert C(x, x < 3) == C(x, (x, -oo, 3)) + ans = C(x, (x, 0, 3)) + assert C(x, And(x >= 0, x < 3)) == ans + assert C(x, (x, Interval.Ropen(0, 3))) == ans + raises(TypeError, lambda: C(x, (x, Range(3)))) + + # discrete limits + for D in (Sum, Product): + r, ans = Range(3, 10, 2), D(2*x + 3, (x, 0, 3)) + assert D(x, (x, r)) == ans + assert D(x, (x, r.reversed)) == ans + r, ans = Range(3, oo, 2), D(2*x + 3, (x, 0, oo)) + assert D(x, (x, r)) == ans + assert D(x, (x, r.reversed)) == ans + r, ans = Range(-oo, 5, 2), D(3 - 2*x, (x, 0, oo)) + assert D(x, (x, r)) == ans + assert D(x, (x, r.reversed)) == ans + raises(TypeError, lambda: D(x, x > 0)) + raises(ValueError, lambda: D(x, Interval(1, 3))) + raises(NotImplementedError, lambda: D(x, (x, union))) + + +def test_pr_22677(): + b = Symbol('b', integer=True, positive=True) + assert Sum(1/x**2,(x, 0, b)).doit() == Sum(x**(-2), (x, 0, b)) + assert Sum(1/(x - b)**2,(x, 0, b-1)).doit() == Sum( + (-b + x)**(-2), (x, 0, b - 1)) + + +def test_issue_23952(): + p, q = symbols("p q", real=True, nonnegative=True) + k1, k2 = symbols("k1 k2", integer=True, nonnegative=True) + n = Symbol("n", integer=True, positive=True) + expr = Sum(abs(k1 - k2)*p**k1 *(1 - q)**(n - k2), + (k1, 0, n), (k2, 0, n)) + assert expr.subs(p,0).subs(q,1).subs(n, 3).doit() == 3 diff --git a/.venv/lib/python3.13/site-packages/sympy/holonomic/tests/__init__.py b/.venv/lib/python3.13/site-packages/sympy/holonomic/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.13/site-packages/sympy/holonomic/tests/test_holonomic.py b/.venv/lib/python3.13/site-packages/sympy/holonomic/tests/test_holonomic.py new file mode 100644 index 0000000000000000000000000000000000000000..49956419e917b3bc81a163d29862c539f33f6284 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/holonomic/tests/test_holonomic.py @@ -0,0 +1,851 @@ +from sympy.holonomic import (DifferentialOperator, HolonomicFunction, + DifferentialOperators, from_hyper, + from_meijerg, expr_to_holonomic) +from sympy.holonomic.recurrence import RecurrenceOperators, HolonomicSequence +from sympy.core import EulerGamma +from sympy.core.numbers import (I, Rational, pi) +from sympy.core.singleton import S +from sympy.core.symbol import (Symbol, symbols) +from sympy.functions.elementary.exponential import (exp, log) +from sympy.functions.elementary.hyperbolic import (asinh, cosh) +from sympy.functions.elementary.miscellaneous import sqrt +from sympy.functions.elementary.trigonometric import (cos, sin) +from sympy.functions.special.bessel import besselj +from sympy.functions.special.beta_functions import beta +from sympy.functions.special.error_functions import (Ci, Si, erf, erfc) +from sympy.functions.special.gamma_functions import gamma +from sympy.functions.special.hyper import (hyper, meijerg) +from sympy.printing.str import sstr +from sympy.series.order import O +from sympy.simplify.hyperexpand import hyperexpand +from sympy.polys.domains.integerring import ZZ +from sympy.polys.domains.rationalfield import QQ +from sympy.polys.domains.realfield import RR + + +def test_DifferentialOperator(): + x = symbols('x') + R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx') + assert Dx == R.derivative_operator + assert Dx == DifferentialOperator([R.base.zero, R.base.one], R) + assert x * Dx + x**2 * Dx**2 == DifferentialOperator([0, x, x**2], R) + assert (x**2 + 1) + Dx + x * \ + Dx**5 == DifferentialOperator([x**2 + 1, 1, 0, 0, 0, x], R) + assert (x * Dx + x**2 + 1 - Dx * (x**3 + x))**3 == (-48 * x**6) + \ + (-57 * x**7) * Dx + (-15 * x**8) * Dx**2 + (-x**9) * Dx**3 + p = (x * Dx**2 + (x**2 + 3) * Dx**5) * (Dx + x**2) + q = (2 * x) + (4 * x**2) * Dx + (x**3) * Dx**2 + \ + (20 * x**2 + x + 60) * Dx**3 + (10 * x**3 + 30 * x) * Dx**4 + \ + (x**4 + 3 * x**2) * Dx**5 + (x**2 + 3) * Dx**6 + assert p == q + + +def test_HolonomicFunction_addition(): + x = symbols('x') + R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx') + p = HolonomicFunction(Dx**2 * x, x) + q = HolonomicFunction((2) * Dx + (x) * Dx**2, x) + assert p == q + p = HolonomicFunction(x * Dx + 1, x) + q = HolonomicFunction(Dx + 1, x) + r = HolonomicFunction((x - 2) + (x**2 - 2) * Dx + (x**2 - x) * Dx**2, x) + assert p + q == r + p = HolonomicFunction(x * Dx + Dx**2 * (x**2 + 2), x) + q = HolonomicFunction(Dx - 3, x) + r = HolonomicFunction((-54 * x**2 - 126 * x - 150) + (-135 * x**3 - 252 * x**2 - 270 * x + 140) * Dx +\ + (-27 * x**4 - 24 * x**2 + 14 * x - 150) * Dx**2 + \ + (9 * x**4 + 15 * x**3 + 38 * x**2 + 30 * x +40) * Dx**3, x) + assert p + q == r + p = HolonomicFunction(Dx**5 - 1, x) + q = HolonomicFunction(x**3 + Dx, x) + r = HolonomicFunction((-x**18 + 45*x**14 - 525*x**10 + 1575*x**6 - x**3 - 630*x**2) + \ + (-x**15 + 30*x**11 - 195*x**7 + 210*x**3 - 1)*Dx + (x**18 - 45*x**14 + 525*x**10 - \ + 1575*x**6 + x**3 + 630*x**2)*Dx**5 + (x**15 - 30*x**11 + 195*x**7 - 210*x**3 + \ + 1)*Dx**6, x) + assert p+q == r + + p = x**2 + 3*x + 8 + q = x**3 - 7*x + 5 + p = p*Dx - p.diff() + q = q*Dx - q.diff() + r = HolonomicFunction(p, x) + HolonomicFunction(q, x) + s = HolonomicFunction((6*x**2 + 18*x + 14) + (-4*x**3 - 18*x**2 - 62*x + 10)*Dx +\ + (x**4 + 6*x**3 + 31*x**2 - 10*x - 71)*Dx**2, x) + assert r == s + + +def test_HolonomicFunction_multiplication(): + x = symbols('x') + R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx') + p = HolonomicFunction(Dx+x+x*Dx**2, x) + q = HolonomicFunction(x*Dx+Dx*x+Dx**2, x) + r = HolonomicFunction((8*x**6 + 4*x**4 + 6*x**2 + 3) + (24*x**5 - 4*x**3 + 24*x)*Dx + \ + (8*x**6 + 20*x**4 + 12*x**2 + 2)*Dx**2 + (8*x**5 + 4*x**3 + 4*x)*Dx**3 + \ + (2*x**4 + x**2)*Dx**4, x) + assert p*q == r + p = HolonomicFunction(Dx**2+1, x) + q = HolonomicFunction(Dx-1, x) + r = HolonomicFunction((2) + (-2)*Dx + (1)*Dx**2, x) + assert p*q == r + p = HolonomicFunction(Dx**2+1+x+Dx, x) + q = HolonomicFunction((Dx*x-1)**2, x) + r = HolonomicFunction((4*x**7 + 11*x**6 + 16*x**5 + 4*x**4 - 6*x**3 - 7*x**2 - 8*x - 2) + \ + (8*x**6 + 26*x**5 + 24*x**4 - 3*x**3 - 11*x**2 - 6*x - 2)*Dx + \ + (8*x**6 + 18*x**5 + 15*x**4 - 3*x**3 - 6*x**2 - 6*x - 2)*Dx**2 + (8*x**5 + \ + 10*x**4 + 6*x**3 - 2*x**2 - 4*x)*Dx**3 + (4*x**5 + 3*x**4 - x**2)*Dx**4, x) + assert p*q == r + p = HolonomicFunction(x*Dx**2-1, x) + q = HolonomicFunction(Dx*x-x, x) + r = HolonomicFunction((x - 3) + (-2*x + 2)*Dx + (x)*Dx**2, x) + assert p*q == r + + +def test_HolonomicFunction_power(): + x = symbols('x') + R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx') + p = HolonomicFunction(Dx+x+x*Dx**2, x) + a = HolonomicFunction(Dx, x) + for n in range(10): + assert a == p**n + a *= p + + +def test_addition_initial_condition(): + x = symbols('x') + R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx') + p = HolonomicFunction(Dx-1, x, 0, [3]) + q = HolonomicFunction(Dx**2+1, x, 0, [1, 0]) + r = HolonomicFunction(-1 + Dx - Dx**2 + Dx**3, x, 0, [4, 3, 2]) + assert p + q == r + p = HolonomicFunction(Dx - x + Dx**2, x, 0, [1, 2]) + q = HolonomicFunction(Dx**2 + x, x, 0, [1, 0]) + r = HolonomicFunction((-x**4 - x**3/4 - x**2 + Rational(1, 4)) + (x**3 + x**2/4 + x*Rational(3, 4) + 1)*Dx + \ + (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]) + assert p + q == r + p = HolonomicFunction(Dx**2 + 4*x*Dx + x**2, x, 0, [3, 4]) + q = HolonomicFunction(Dx**2 + 1, x, 0, [1, 1]) + r = HolonomicFunction((x**6 + 2*x**4 - 5*x**2 - 6) + (4*x**5 + 36*x**3 - 32*x)*Dx + \ + (x**6 + 3*x**4 + 5*x**2 - 9)*Dx**2 + (4*x**5 + 36*x**3 - 32*x)*Dx**3 + (x**4 + \ + 10*x**2 - 3)*Dx**4, x, 0, [4, 5, -1, -17]) + assert p + q == r + q = HolonomicFunction(Dx**3 + x, x, 2, [3, 0, 1]) + p = HolonomicFunction(Dx - 1, x, 2, [1]) + r = HolonomicFunction((-x**2 - x + 1) + (x**2 + x)*Dx + (-x - 2)*Dx**3 + \ + (x + 1)*Dx**4, x, 2, [4, 1, 2, -5 ]) + assert p + q == r + p = expr_to_holonomic(sin(x)) + q = expr_to_holonomic(1/x, x0=1) + r = HolonomicFunction((x**2 + 6) + (x**3 + 2*x)*Dx + (x**2 + 6)*Dx**2 + (x**3 + 2*x)*Dx**3, \ + x, 1, [sin(1) + 1, -1 + cos(1), -sin(1) + 2]) + assert p + q == r + C_1 = symbols('C_1') + p = expr_to_holonomic(sqrt(x)) + q = expr_to_holonomic(sqrt(x**2-x)) + r = (p + q).to_expr().subs(C_1, -I/2).expand() + assert r == I*sqrt(x)*sqrt(-x + 1) + sqrt(x) + + +def test_multiplication_initial_condition(): + x = symbols('x') + R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx') + p = HolonomicFunction(Dx**2 + x*Dx - 1, x, 0, [3, 1]) + q = HolonomicFunction(Dx**2 + 1, x, 0, [1, 1]) + r = HolonomicFunction((x**4 + 14*x**2 + 60) + 4*x*Dx + (x**4 + 9*x**2 + 20)*Dx**2 + \ + (2*x**3 + 18*x)*Dx**3 + (x**2 + 10)*Dx**4, x, 0, [3, 4, 2, 3]) + assert p * q == r + p = HolonomicFunction(Dx**2 + x, x, 0, [1, 0]) + q = HolonomicFunction(Dx**3 - x**2, x, 0, [3, 3, 3]) + r = HolonomicFunction((x**8 - 37*x**7/27 - 10*x**6/27 - 164*x**5/9 - 184*x**4/9 + \ + 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 + \ + 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 + \ + 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 + \ + (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 + \ + 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]) + assert p * q == r + p = HolonomicFunction(Dx - 1, x, 0, [2]) + q = HolonomicFunction(Dx**2 + 1, x, 0, [0, 1]) + r = HolonomicFunction(2 -2*Dx + Dx**2, x, 0, [0, 2]) + assert p * q == r + q = HolonomicFunction(x*Dx**2 + 1 + 2*Dx, x, 0,[0, 1]) + r = HolonomicFunction((x - 1) + (-2*x + 2)*Dx + x*Dx**2, x, 0, [0, 2]) + assert p * q == r + p = HolonomicFunction(Dx**2 - 1, x, 0, [1, 3]) + q = HolonomicFunction(Dx**3 + 1, x, 0, [1, 2, 1]) + r = HolonomicFunction(6*Dx + 3*Dx**2 + 2*Dx**3 - 3*Dx**4 + Dx**6, x, 0, [1, 5, 14, 17, 17, 2]) + assert p * q == r + p = expr_to_holonomic(sin(x)) + q = expr_to_holonomic(1/x, x0=1) + r = HolonomicFunction(x + 2*Dx + x*Dx**2, x, 1, [sin(1), -sin(1) + cos(1)]) + assert p * q == r + p = expr_to_holonomic(sqrt(x)) + q = expr_to_holonomic(sqrt(x**2-x)) + r = (p * q).to_expr() + assert r == I*x*sqrt(-x + 1) + + +def test_HolonomicFunction_composition(): + x = symbols('x') + R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx') + p = HolonomicFunction(Dx-1, x).composition(x**2+x) + r = HolonomicFunction((-2*x - 1) + Dx, x) + assert p == r + p = HolonomicFunction(Dx**2+1, x).composition(x**5+x**2+1) + r = HolonomicFunction((125*x**12 + 150*x**9 + 60*x**6 + 8*x**3) + (-20*x**3 - 2)*Dx + \ + (5*x**4 + 2*x)*Dx**2, x) + assert p == r + p = HolonomicFunction(Dx**2*x+x, x).composition(2*x**3+x**2+1) + r = HolonomicFunction((216*x**9 + 324*x**8 + 180*x**7 + 152*x**6 + 112*x**5 + \ + 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 + \ + x**3 + 3*x**2 + x)*Dx**2, x) + assert p == r + p = HolonomicFunction(Dx**2+1, x).composition(1-x**2) + r = HolonomicFunction((4*x**3) - Dx + x*Dx**2, x) + assert p == r + p = HolonomicFunction(Dx**2+1, x).composition(x - 2/(x**2 + 1)) + r = HolonomicFunction((x**12 + 6*x**10 + 12*x**9 + 15*x**8 + 48*x**7 + 68*x**6 + \ + 72*x**5 + 111*x**4 + 112*x**3 + 54*x**2 + 12*x + 1) + (12*x**8 + 32*x**6 + \ + 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+ \ + 15*x**4 + 16*x**3 + 6*x**2 + 4*x + 1)*Dx**2, x) + assert p == r + + +def test_from_hyper(): + x = symbols('x') + R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx') + p = hyper([1, 1], [Rational(3, 2)], x**2/4) + 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)]) + r = from_hyper(p) + assert r == q + p = from_hyper(hyper([1], [Rational(3, 2)], x**2/4)) + q = HolonomicFunction(-x + (-x**2/2 + 2)*Dx + x*Dx**2, x) + # x0 = 1 + y0 = '[sqrt(pi)*exp(1/4)*erf(1/2), -sqrt(pi)*exp(1/4)*erf(1/2)/2 + 1]' + assert sstr(p.y0) == y0 + assert q.annihilator == p.annihilator + + +def test_from_meijerg(): + x = symbols('x') + R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx') + p = from_meijerg(meijerg(([], [Rational(3, 2)]), ([S.Half], [S.Half, 1]), x)) + q = HolonomicFunction(x/2 - Rational(1, 4) + (-x**2 + x/4)*Dx + x**2*Dx**2 + x**3*Dx**3, x, 1, \ + [1/sqrt(pi), 1/(2*sqrt(pi)), -1/(4*sqrt(pi))]) + assert p == q + p = from_meijerg(meijerg(([], []), ([0], []), x)) + q = HolonomicFunction(1 + Dx, x, 0, [1]) + assert p == q + p = from_meijerg(meijerg(([1], []), ([S.Half], [0]), x)) + q = HolonomicFunction((x + S.Half)*Dx + x*Dx**2, x, 1, [sqrt(pi)*erf(1), exp(-1)]) + assert p == q + p = from_meijerg(meijerg(([0], [1]), ([0], []), 2*x**2)) + q = HolonomicFunction((3*x**2 - 1)*Dx + x**3*Dx**2, x, 1, [-exp(Rational(-1, 2)) + 1, -exp(Rational(-1, 2))]) + assert p == q + + +def test_to_Sequence(): + x = symbols('x') + R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx') + n = symbols('n', integer=True) + _, Sn = RecurrenceOperators(ZZ.old_poly_ring(n), 'Sn') + p = HolonomicFunction(x**2*Dx**4 + x + Dx, x).to_sequence() + q = [(HolonomicSequence(1 + (n + 2)*Sn**2 + (n**4 + 6*n**3 + 11*n**2 + 6*n)*Sn**3), 0, 1)] + assert p == q + p = HolonomicFunction(x**2*Dx**4 + x**3 + Dx**2, x).to_sequence() + q = [(HolonomicSequence(1 + (n**4 + 14*n**3 + 72*n**2 + 163*n + 140)*Sn**5), 0, 0)] + assert p == q + p = HolonomicFunction(x**3*Dx**4 + 1 + Dx**2, x).to_sequence() + q = [(HolonomicSequence(1 + (n**4 - 2*n**3 - n**2 + 2*n)*Sn + (n**2 + 3*n + 2)*Sn**2), 0, 0)] + assert p == q + p = HolonomicFunction(3*x**3*Dx**4 + 2*x*Dx + x*Dx**3, x).to_sequence() + 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)] + assert p == q + + +def test_to_Sequence_Initial_Coniditons(): + x = symbols('x') + R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx') + n = symbols('n', integer=True) + _, Sn = RecurrenceOperators(QQ.old_poly_ring(n), 'Sn') + p = HolonomicFunction(Dx - 1, x, 0, [1]).to_sequence() + q = [(HolonomicSequence(-1 + (n + 1)*Sn, 1), 0)] + assert p == q + p = HolonomicFunction(Dx**2 + 1, x, 0, [0, 1]).to_sequence() + q = [(HolonomicSequence(1 + (n**2 + 3*n + 2)*Sn**2, [0, 1]), 0)] + assert p == q + p = HolonomicFunction(Dx**2 + 1 + x**3*Dx, x, 0, [2, 3]).to_sequence() + q = [(HolonomicSequence(n + Sn**2 + (n**2 + 7*n + 12)*Sn**4, [2, 3, -1, Rational(-1, 2), Rational(1, 12)]), 1)] + assert p == q + p = HolonomicFunction(x**3*Dx**5 + 1 + Dx, x).to_sequence() + q = [(HolonomicSequence(1 + (n + 1)*Sn + (n**5 - 5*n**3 + 4*n)*Sn**2), 0, 3)] + assert p == q + C_0, C_1, C_2, C_3 = symbols('C_0, C_1, C_2, C_3') + p = expr_to_holonomic(log(1+x**2)) + q = [(HolonomicSequence(n**2 + (n**2 + 2*n)*Sn**2, [0, 0, C_2]), 0, 1)] + assert p.to_sequence() == q + p = p.diff() + q = [(HolonomicSequence((n + 2) + (n + 2)*Sn**2, [C_0, 0]), 1, 0)] + assert p.to_sequence() == q + p = expr_to_holonomic(erf(x) + x).to_sequence() + 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)] + assert p == q + +def test_series(): + x = symbols('x') + R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx') + p = HolonomicFunction(Dx**2 + 2*x*Dx, x, 0, [0, 1]).series(n=10) + q = x - x**3/3 + x**5/10 - x**7/42 + x**9/216 + O(x**10) + assert p == q + p = HolonomicFunction(Dx - 1, x).composition(x**2, 0, [1]) # e^(x**2) + q = HolonomicFunction(Dx**2 + 1, x, 0, [1, 0]) # cos(x) + r = (p * q).series(n=10) # expansion of cos(x) * exp(x**2) + s = 1 + x**2/2 + x**4/24 - 31*x**6/720 - 179*x**8/8064 + O(x**10) + assert r == s + t = HolonomicFunction((1 + x)*Dx**2 + Dx, x, 0, [0, 1]) # log(1 + x) + r = (p * t + q).series(n=10) + s = 1 + x - x**2 + 4*x**3/3 - 17*x**4/24 + 31*x**5/30 - 481*x**6/720 +\ + 71*x**7/105 - 20159*x**8/40320 + 379*x**9/840 + O(x**10) + assert r == s + p = HolonomicFunction((6+6*x-3*x**2) - (10*x-3*x**2-3*x**3)*Dx + \ + (4-6*x**3+2*x**4)*Dx**2, x, 0, [0, 1]).series(n=7) + q = x + x**3/6 - 3*x**4/16 + x**5/20 - 23*x**6/960 + O(x**7) + assert p == q + p = HolonomicFunction((6+6*x-3*x**2) - (10*x-3*x**2-3*x**3)*Dx + \ + (4-6*x**3+2*x**4)*Dx**2, x, 0, [1, 0]).series(n=7) + q = 1 - 3*x**2/4 - x**3/4 - 5*x**4/32 - 3*x**5/40 - 17*x**6/384 + O(x**7) + assert p == q + p = expr_to_holonomic(erf(x) + x).series(n=10) + C_3 = symbols('C_3') + q = (erf(x) + x).series(n=10) + assert p.subs(C_3, -2/(3*sqrt(pi))) == q + assert expr_to_holonomic(sqrt(x**3 + x)).series(n=10) == sqrt(x**3 + x).series(n=10) + assert expr_to_holonomic((2*x - 3*x**2)**Rational(1, 3)).series() == ((2*x - 3*x**2)**Rational(1, 3)).series() + assert expr_to_holonomic(sqrt(x**2-x)).series() == (sqrt(x**2-x)).series() + 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) + 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() + assert expr_to_holonomic(cos(x-1)**2/(x-1)**2, x0=1, y0={-2: [1, 0, -1]}).series(n=10) \ + == (cos(x-1)**2/(x-1)**2).series(x0=1, n=10) + +def test_evalf_euler(): + x = symbols('x') + R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx') + + # log(1+x) + p = HolonomicFunction((1 + x)*Dx**2 + Dx, x, 0, [0, 1]) + + # path taken is a straight line from 0 to 1, on the real axis + r = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1] + s = '0.699525841805253' # approx. equal to log(2) i.e. 0.693147180559945 + assert sstr(p.evalf(r, method='Euler')[-1]) == s + + # path taken is a triangle 0-->1+i-->2 + r = [0.1 + 0.1*I] + for i in range(9): + r.append(r[-1]+0.1+0.1*I) + for i in range(10): + r.append(r[-1]+0.1-0.1*I) + + # close to the exact solution 1.09861228866811 + # imaginary part also close to zero + s = '1.07530466271334 - 0.0251200594793912*I' + assert sstr(p.evalf(r, method='Euler')[-1]) == s + + # sin(x) + p = HolonomicFunction(Dx**2 + 1, x, 0, [0, 1]) + s = '0.905546532085401 - 6.93889390390723e-18*I' + assert sstr(p.evalf(r, method='Euler')[-1]) == s + + # computing sin(pi/2) using this method + # using a linear path from 0 to pi/2 + r = [0.1] + for i in range(14): + r.append(r[-1] + 0.1) + r.append(pi/2) + s = '1.08016557252834' # close to 1.0 (exact solution) + assert sstr(p.evalf(r, method='Euler')[-1]) == s + + # trying different path, a rectangle (0-->i-->pi/2 + i-->pi/2) + # computing the same value sin(pi/2) using different path + r = [0.1*I] + for i in range(9): + r.append(r[-1]+0.1*I) + for i in range(15): + r.append(r[-1]+0.1) + r.append(pi/2+I) + for i in range(10): + r.append(r[-1]-0.1*I) + + # close to 1.0 + s = '0.976882381836257 - 1.65557671738537e-16*I' + assert sstr(p.evalf(r, method='Euler')[-1]) == s + + # cos(x) + p = HolonomicFunction(Dx**2 + 1, x, 0, [1, 0]) + # compute cos(pi) along 0-->pi + r = [0.05] + for i in range(61): + r.append(r[-1]+0.05) + r.append(pi) + # close to -1 (exact answer) + s = '-1.08140824719196' + assert sstr(p.evalf(r, method='Euler')[-1]) == s + + # a rectangular path (0 -> i -> 2+i -> 2) + r = [0.1*I] + for i in range(9): + r.append(r[-1]+0.1*I) + for i in range(20): + r.append(r[-1]+0.1) + for i in range(10): + r.append(r[-1]-0.1*I) + + p = HolonomicFunction(Dx**2 + 1, x, 0, [1,1]).evalf(r, method='Euler') + s = '0.501421652861245 - 3.88578058618805e-16*I' + assert sstr(p[-1]) == s + +def test_evalf_rk4(): + x = symbols('x') + R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx') + + # log(1+x) + p = HolonomicFunction((1 + x)*Dx**2 + Dx, x, 0, [0, 1]) + + # path taken is a straight line from 0 to 1, on the real axis + r = [0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1] + s = '0.693146363174626' # approx. equal to log(2) i.e. 0.693147180559945 + assert sstr(p.evalf(r)[-1]) == s + + # path taken is a triangle 0-->1+i-->2 + r = [0.1 + 0.1*I] + for i in range(9): + r.append(r[-1]+0.1+0.1*I) + for i in range(10): + r.append(r[-1]+0.1-0.1*I) + + # close to the exact solution 1.09861228866811 + # imaginary part also close to zero + s = '1.098616 + 1.36083e-7*I' + assert sstr(p.evalf(r)[-1].n(7)) == s + + # sin(x) + p = HolonomicFunction(Dx**2 + 1, x, 0, [0, 1]) + s = '0.90929463522785 + 1.52655665885959e-16*I' + assert sstr(p.evalf(r)[-1]) == s + + # computing sin(pi/2) using this method + # using a linear path from 0 to pi/2 + r = [0.1] + for i in range(14): + r.append(r[-1] + 0.1) + r.append(pi/2) + s = '0.999999895088917' # close to 1.0 (exact solution) + assert sstr(p.evalf(r)[-1]) == s + + # trying different path, a rectangle (0-->i-->pi/2 + i-->pi/2) + # computing the same value sin(pi/2) using different path + r = [0.1*I] + for i in range(9): + r.append(r[-1]+0.1*I) + for i in range(15): + r.append(r[-1]+0.1) + r.append(pi/2+I) + for i in range(10): + r.append(r[-1]-0.1*I) + + # close to 1.0 + s = '1.00000003415141 + 6.11940487991086e-16*I' + assert sstr(p.evalf(r)[-1]) == s + + # cos(x) + p = HolonomicFunction(Dx**2 + 1, x, 0, [1, 0]) + # compute cos(pi) along 0-->pi + r = [0.05] + for i in range(61): + r.append(r[-1]+0.05) + r.append(pi) + # close to -1 (exact answer) + s = '-0.999999993238714' + assert sstr(p.evalf(r)[-1]) == s + + # a rectangular path (0 -> i -> 2+i -> 2) + r = [0.1*I] + for i in range(9): + r.append(r[-1]+0.1*I) + for i in range(20): + r.append(r[-1]+0.1) + for i in range(10): + r.append(r[-1]-0.1*I) + + p = HolonomicFunction(Dx**2 + 1, x, 0, [1,1]).evalf(r) + s = '0.493152791638442 - 1.41553435639707e-15*I' + assert sstr(p[-1]) == s + + +def test_expr_to_holonomic(): + x = symbols('x') + R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx') + p = expr_to_holonomic((sin(x)/x)**2) + q = HolonomicFunction(8*x + (4*x**2 + 6)*Dx + 6*x*Dx**2 + x**2*Dx**3, x, 0, \ + [1, 0, Rational(-2, 3)]) + assert p == q + p = expr_to_holonomic(1/(1+x**2)**2) + q = HolonomicFunction(4*x + (x**2 + 1)*Dx, x, 0, [1]) + assert p == q + p = expr_to_holonomic(exp(x)*sin(x)+x*log(1+x)) + q = HolonomicFunction((2*x**3 + 10*x**2 + 20*x + 18) + (-2*x**4 - 10*x**3 - 20*x**2 \ + - 18*x)*Dx + (2*x**5 + 6*x**4 + 7*x**3 + 8*x**2 + 10*x - 4)*Dx**2 + \ + (-2*x**5 - 5*x**4 - 2*x**3 + 2*x**2 - x + 4)*Dx**3 + (x**5 + 2*x**4 - x**3 - \ + 7*x**2/2 + x + Rational(5, 2))*Dx**4, x, 0, [0, 1, 4, -1]) + assert p == q + p = expr_to_holonomic(x*exp(x)+cos(x)+1) + q = HolonomicFunction((-x - 3)*Dx + (x + 2)*Dx**2 + (-x - 3)*Dx**3 + (x + 2)*Dx**4, x, \ + 0, [2, 1, 1, 3]) + assert p == q + assert (x*exp(x)+cos(x)+1).series(n=10) == p.series(n=10) + p = expr_to_holonomic(log(1 + x)**2 + 1) + q = HolonomicFunction(Dx + (3*x + 3)*Dx**2 + (x**2 + 2*x + 1)*Dx**3, x, 0, [1, 0, 2]) + assert p == q + p = expr_to_holonomic(erf(x)**2 + x) + q = HolonomicFunction((8*x**4 - 2*x**2 + 2)*Dx**2 + (6*x**3 - x/2)*Dx**3 + \ + (x**2+ Rational(1, 4))*Dx**4, x, 0, [0, 1, 8/pi, 0]) + assert p == q + p = expr_to_holonomic(cosh(x)*x) + q = HolonomicFunction((-x**2 + 2) -2*x*Dx + x**2*Dx**2, x, 0, [0, 1]) + assert p == q + p = expr_to_holonomic(besselj(2, x)) + q = HolonomicFunction((x**2 - 4) + x*Dx + x**2*Dx**2, x, 0, [0, 0]) + assert p == q + p = expr_to_holonomic(besselj(0, x) + exp(x)) + q = HolonomicFunction((-x**2 - x/2 + S.Half) + (x**2 - x/2 - Rational(3, 2))*Dx + (-x**2 + x/2 + 1)*Dx**2 +\ + (x**2 + x/2)*Dx**3, x, 0, [2, 1, S.Half]) + assert p == q + p = expr_to_holonomic(sin(x)**2/x) + q = HolonomicFunction(4 + 4*x*Dx + 3*Dx**2 + x*Dx**3, x, 0, [0, 1, 0]) + assert p == q + p = expr_to_holonomic(sin(x)**2/x, x0=2) + q = HolonomicFunction((4) + (4*x)*Dx + (3)*Dx**2 + (x)*Dx**3, x, 2, [sin(2)**2/2, + sin(2)*cos(2) - sin(2)**2/4, -3*sin(2)**2/4 + cos(2)**2 - sin(2)*cos(2)]) + assert p == q + p = expr_to_holonomic(log(x)/2 - Ci(2*x)/2 + Ci(2)/2) + q = HolonomicFunction(4*Dx + 4*x*Dx**2 + 3*Dx**3 + x*Dx**4, x, 0, \ + [-log(2)/2 - EulerGamma/2 + Ci(2)/2, 0, 1, 0]) + assert p == q + p = p.to_expr() + q = log(x)/2 - Ci(2*x)/2 + Ci(2)/2 + assert p == q + p = expr_to_holonomic(x**S.Half, x0=1) + q = HolonomicFunction(x*Dx - S.Half, x, 1, [1]) + assert p == q + p = expr_to_holonomic(sqrt(1 + x**2)) + q = HolonomicFunction((-x) + (x**2 + 1)*Dx, x, 0, [1]) + assert p == q + assert (expr_to_holonomic(sqrt(x) + sqrt(2*x)).to_expr()-\ + (sqrt(x) + sqrt(2*x))).simplify() == 0 + assert expr_to_holonomic(3*x+2*sqrt(x)).to_expr() == 3*x+2*sqrt(x) + p = expr_to_holonomic((x**4+x**3+5*x**2+3*x+2)/x**2, lenics=3) + q = HolonomicFunction((-2*x**4 - x**3 + 3*x + 4) + (x**5 + x**4 + 5*x**3 + 3*x**2 + \ + 2*x)*Dx, x, 0, {-2: [2, 3, 5]}) + assert p == q + p = expr_to_holonomic(1/(x-1)**2, lenics=3, x0=1) + q = HolonomicFunction((2) + (x - 1)*Dx, x, 1, {-2: [1, 0, 0]}) + assert p == q + a = symbols("a") + p = expr_to_holonomic(sqrt(a*x), x=x) + assert p.to_expr() == sqrt(a)*sqrt(x) + +def test_to_hyper(): + x = symbols('x') + R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx') + p = HolonomicFunction(Dx - 2, x, 0, [3]).to_hyper() + q = 3 * hyper([], [], 2*x) + assert p == q + p = hyperexpand(HolonomicFunction((1 + x) * Dx - 3, x, 0, [2]).to_hyper()).expand() + q = 2*x**3 + 6*x**2 + 6*x + 2 + assert p == q + p = HolonomicFunction((1 + x)*Dx**2 + Dx, x, 0, [0, 1]).to_hyper() + q = -x**2*hyper((2, 2, 1), (3, 2), -x)/2 + x + assert p == q + p = HolonomicFunction(2*x*Dx + Dx**2, x, 0, [0, 2/sqrt(pi)]).to_hyper() + q = 2*x*hyper((S.Half,), (Rational(3, 2),), -x**2)/sqrt(pi) + assert p == q + p = hyperexpand(HolonomicFunction(2*x*Dx + Dx**2, x, 0, [1, -2/sqrt(pi)]).to_hyper()) + q = erfc(x) + assert p.rewrite(erfc) == q + p = hyperexpand(HolonomicFunction((x**2 - 1) + x*Dx + x**2*Dx**2, + x, 0, [0, S.Half]).to_hyper()) + q = besselj(1, x) + assert p == q + p = hyperexpand(HolonomicFunction(x*Dx**2 + Dx + x, x, 0, [1, 0]).to_hyper()) + q = besselj(0, x) + assert p == q + +def test_to_expr(): + x = symbols('x') + R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx') + p = HolonomicFunction(Dx - 1, x, 0, [1]).to_expr() + q = exp(x) + assert p == q + p = HolonomicFunction(Dx**2 + 1, x, 0, [1, 0]).to_expr() + q = cos(x) + assert p == q + p = HolonomicFunction(Dx**2 - 1, x, 0, [1, 0]).to_expr() + q = cosh(x) + assert p == q + p = HolonomicFunction(2 + (4*x - 1)*Dx + \ + (x**2 - x)*Dx**2, x, 0, [1, 2]).to_expr().expand() + q = 1/(x**2 - 2*x + 1) + assert p == q + p = expr_to_holonomic(sin(x)**2/x).integrate((x, 0, x)).to_expr() + q = (sin(x)**2/x).integrate((x, 0, x)) + assert p == q + C_0, C_1, C_2, C_3 = symbols('C_0, C_1, C_2, C_3') + p = expr_to_holonomic(log(1+x**2)).to_expr() + q = C_2*log(x**2 + 1) + assert p == q + p = expr_to_holonomic(log(1+x**2)).diff().to_expr() + q = C_0*x/(x**2 + 1) + assert p == q + p = expr_to_holonomic(erf(x) + x).to_expr() + q = 3*C_3*x - 3*sqrt(pi)*C_3*erf(x)/2 + x + 2*x/sqrt(pi) + assert p == q + p = expr_to_holonomic(sqrt(x), x0=1).to_expr() + assert p == sqrt(x) + assert expr_to_holonomic(sqrt(x)).to_expr() == sqrt(x) + p = expr_to_holonomic(sqrt(1 + x**2)).to_expr() + assert p == sqrt(1+x**2) + p = expr_to_holonomic((2*x**2 + 1)**Rational(2, 3)).to_expr() + assert p == (2*x**2 + 1)**Rational(2, 3) + p = expr_to_holonomic(sqrt(-x**2+2*x)).to_expr() + assert p == sqrt(x)*sqrt(-x + 2) + p = expr_to_holonomic((-2*x**3+7*x)**Rational(2, 3)).to_expr() + q = x**Rational(2, 3)*(-2*x**2 + 7)**Rational(2, 3) + assert p == q + p = from_hyper(hyper((-2, -3), (S.Half, ), x)) + s = hyperexpand(hyper((-2, -3), (S.Half, ), x)) + D_0 = Symbol('D_0') + C_0 = Symbol('C_0') + assert (p.to_expr().subs({C_0:1, D_0:0}) - s).simplify() == 0 + p.y0 = {0: [1], S.Half: [0]} + assert p.to_expr() == s + assert expr_to_holonomic(x**5).to_expr() == x**5 + assert expr_to_holonomic(2*x**3-3*x**2).to_expr().expand() == \ + 2*x**3-3*x**2 + a = symbols("a") + p = (expr_to_holonomic(1.4*x)*expr_to_holonomic(a*x, x)).to_expr() + q = 1.4*a*x**2 + assert p == q + p = (expr_to_holonomic(1.4*x)+expr_to_holonomic(a*x, x)).to_expr() + q = x*(a + 1.4) + assert p == q + p = (expr_to_holonomic(1.4*x)+expr_to_holonomic(x)).to_expr() + assert p == 2.4*x + + +def test_integrate(): + x = symbols('x') + R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx') + p = expr_to_holonomic(sin(x)**2/x, x0=1).integrate((x, 2, 3)) + q = '0.166270406994788' + assert sstr(p) == q + p = expr_to_holonomic(sin(x)).integrate((x, 0, x)).to_expr() + q = 1 - cos(x) + assert p == q + p = expr_to_holonomic(sin(x)).integrate((x, 0, 3)) + q = 1 - cos(3) + assert p == q + p = expr_to_holonomic(sin(x)/x, x0=1).integrate((x, 1, 2)) + q = '0.659329913368450' + assert sstr(p) == q + p = expr_to_holonomic(sin(x)**2/x, x0=1).integrate((x, 1, 0)) + q = '-0.423690480850035' + assert sstr(p) == q + p = expr_to_holonomic(sin(x)/x) + assert p.integrate(x).to_expr() == Si(x) + assert p.integrate((x, 0, 2)) == Si(2) + p = expr_to_holonomic(sin(x)**2/x) + q = p.to_expr() + assert p.integrate(x).to_expr() == q.integrate((x, 0, x)) + assert p.integrate((x, 0, 1)) == q.integrate((x, 0, 1)) + assert expr_to_holonomic(1/x, x0=1).integrate(x).to_expr() == log(x) + p = expr_to_holonomic((x + 1)**3*exp(-x), x0=-1).integrate(x).to_expr() + q = (-x**3 - 6*x**2 - 15*x + 6*exp(x + 1) - 16)*exp(-x) + assert p == q + p = expr_to_holonomic(cos(x)**2/x**2, y0={-2: [1, 0, -1]}).integrate(x).to_expr() + q = -Si(2*x) - cos(x)**2/x + assert p == q + p = expr_to_holonomic(sqrt(x**2+x)).integrate(x).to_expr() + q = (x**Rational(3, 2)*(2*x**2 + 3*x + 1) - x*sqrt(x + 1)*asinh(sqrt(x)))/(4*x*sqrt(x + 1)) + assert p == q + p = expr_to_holonomic(sqrt(x**2+1)).integrate(x).to_expr() + q = (sqrt(x**2+1)).integrate(x) + assert (p-q).simplify() == 0 + p = expr_to_holonomic(1/x**2, y0={-2:[1, 0, 0]}) + r = expr_to_holonomic(1/x**2, lenics=3) + assert p == r + q = expr_to_holonomic(cos(x)**2) + assert (r*q).integrate(x).to_expr() == -Si(2*x) - cos(x)**2/x + + +def test_diff(): + x, y = symbols('x, y') + R, Dx = DifferentialOperators(ZZ.old_poly_ring(x), 'Dx') + p = HolonomicFunction(x*Dx**2 + 1, x, 0, [0, 1]) + assert p.diff().to_expr() == p.to_expr().diff().simplify() + p = HolonomicFunction(Dx**2 - 1, x, 0, [1, 0]) + assert p.diff(x, 2).to_expr() == p.to_expr() + p = expr_to_holonomic(Si(x)) + assert p.diff().to_expr() == sin(x)/x + assert p.diff(y) == 0 + C_0, C_1, C_2, C_3 = symbols('C_0, C_1, C_2, C_3') + q = Si(x) + assert p.diff(x).to_expr() == q.diff() + assert p.diff(x, 2).to_expr().subs(C_0, Rational(-1, 3)).cancel() == q.diff(x, 2).cancel() + assert p.diff(x, 3).series().subs({C_3: Rational(-1, 3), C_0: 0}) == q.diff(x, 3).series() + + +def test_extended_domain_in_expr_to_holonomic(): + x = symbols('x') + p = expr_to_holonomic(1.2*cos(3.1*x)) + assert p.to_expr() == 1.2*cos(3.1*x) + assert sstr(p.integrate(x).to_expr()) == '0.387096774193548*sin(3.1*x)' + _, Dx = DifferentialOperators(RR.old_poly_ring(x), 'Dx') + p = expr_to_holonomic(1.1329138213*x) + q = HolonomicFunction((-1.1329138213) + (1.1329138213*x)*Dx, x, 0, {1: [1.1329138213]}) + assert p == q + assert p.to_expr() == 1.1329138213*x + assert sstr(p.integrate((x, 1, 2))) == sstr((1.1329138213*x).integrate((x, 1, 2))) + y, z = symbols('y, z') + p = expr_to_holonomic(sin(x*y*z), x=x) + assert p.to_expr() == sin(x*y*z) + assert p.integrate(x).to_expr() == (-cos(x*y*z) + 1)/(y*z) + p = expr_to_holonomic(sin(x*y + z), x=x).integrate(x).to_expr() + q = (cos(z) - cos(x*y + z))/y + assert p == q + a = symbols('a') + p = expr_to_holonomic(a*x, x) + assert p.to_expr() == a*x + assert p.integrate(x).to_expr() == a*x**2/2 + D_2, C_1 = symbols("D_2, C_1") + p = expr_to_holonomic(x) + expr_to_holonomic(1.2*cos(x)) + p = p.to_expr().subs(D_2, 0) + assert p - x - 1.2*cos(1.0*x) == 0 + p = expr_to_holonomic(x) * expr_to_holonomic(1.2*cos(x)) + p = p.to_expr().subs(C_1, 0) + assert p - 1.2*x*cos(1.0*x) == 0 + + +def test_to_meijerg(): + x = symbols('x') + assert hyperexpand(expr_to_holonomic(sin(x)).to_meijerg()) == sin(x) + assert hyperexpand(expr_to_holonomic(cos(x)).to_meijerg()) == cos(x) + assert hyperexpand(expr_to_holonomic(exp(x)).to_meijerg()) == exp(x) + assert hyperexpand(expr_to_holonomic(log(x)).to_meijerg()).simplify() == log(x) + assert expr_to_holonomic(4*x**2/3 + 7).to_meijerg() == 4*x**2/3 + 7 + assert hyperexpand(expr_to_holonomic(besselj(2, x), lenics=3).to_meijerg()) == besselj(2, x) + p = hyper((Rational(-1, 2), -3), (), x) + assert from_hyper(p).to_meijerg() == hyperexpand(p) + p = hyper((S.One, S(3)), (S(2), ), x) + assert (hyperexpand(from_hyper(p).to_meijerg()) - hyperexpand(p)).expand() == 0 + p = from_hyper(hyper((-2, -3), (S.Half, ), x)) + s = hyperexpand(hyper((-2, -3), (S.Half, ), x)) + C_0 = Symbol('C_0') + C_1 = Symbol('C_1') + D_0 = Symbol('D_0') + assert (hyperexpand(p.to_meijerg()).subs({C_0:1, D_0:0}) - s).simplify() == 0 + p.y0 = {0: [1], S.Half: [0]} + assert (hyperexpand(p.to_meijerg()) - s).simplify() == 0 + p = expr_to_holonomic(besselj(S.Half, x), initcond=False) + assert (p.to_expr() - (D_0*sin(x) + C_0*cos(x) + C_1*sin(x))/sqrt(x)).simplify() == 0 + p = expr_to_holonomic(besselj(S.Half, x), y0={Rational(-1, 2): [sqrt(2)/sqrt(pi), sqrt(2)/sqrt(pi)]}) + assert (p.to_expr() - besselj(S.Half, x) - besselj(Rational(-1, 2), x)).simplify() == 0 + + +def test_gaussian(): + mu, x = symbols("mu x") + sd = symbols("sd", positive=True) + Q = QQ[mu, sd].get_field() + e = sqrt(2)*exp(-(-mu + x)**2/(2*sd**2))/(2*sqrt(pi)*sd) + h1 = expr_to_holonomic(e, x, domain=Q) + + _, Dx = DifferentialOperators(Q.old_poly_ring(x), 'Dx') + h2 = HolonomicFunction((-mu/sd**2 + x/sd**2) + (1)*Dx, x) + + assert h1 == h2 + + +def test_beta(): + a, b, x = symbols("a b x", positive=True) + e = x**(a - 1)*(-x + 1)**(b - 1)/beta(a, b) + Q = QQ[a, b].get_field() + h1 = expr_to_holonomic(e, x, domain=Q) + + _, Dx = DifferentialOperators(Q.old_poly_ring(x), 'Dx') + h2 = HolonomicFunction((a + x*(-a - b + 2) - 1) + (x**2 - x)*Dx, x) + + assert h1 == h2 + + +def test_gamma(): + a, b, x = symbols("a b x", positive=True) + e = b**(-a)*x**(a - 1)*exp(-x/b)/gamma(a) + Q = QQ[a, b].get_field() + h1 = expr_to_holonomic(e, x, domain=Q) + + _, Dx = DifferentialOperators(Q.old_poly_ring(x), 'Dx') + h2 = HolonomicFunction((-a + 1 + x/b) + (x)*Dx, x) + + assert h1 == h2 + + +def test_symbolic_power(): + x, n = symbols("x n") + Q = QQ[n].get_field() + _, Dx = DifferentialOperators(Q.old_poly_ring(x), 'Dx') + h1 = HolonomicFunction((-1) + (x)*Dx, x) ** -n + h2 = HolonomicFunction((n) + (x)*Dx, x) + + assert h1 == h2 + + +def test_negative_power(): + x = symbols("x") + _, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx') + h1 = HolonomicFunction((-1) + (x)*Dx, x) ** -2 + h2 = HolonomicFunction((2) + (x)*Dx, x) + + assert h1 == h2 + + +def test_expr_in_power(): + x, n = symbols("x n") + Q = QQ[n].get_field() + _, Dx = DifferentialOperators(Q.old_poly_ring(x), 'Dx') + h1 = HolonomicFunction((-1) + (x)*Dx, x) ** (n - 3) + h2 = HolonomicFunction((-n + 3) + (x)*Dx, x) + + assert h1 == h2 + + +def test_DifferentialOperatorEqPoly(): + x = symbols('x', integer=True) + R, Dx = DifferentialOperators(QQ.old_poly_ring(x), 'Dx') + do = DifferentialOperator([x**2, R.base.zero, R.base.zero], R) + do2 = DifferentialOperator([x**2, 1, x], R) + assert not do == do2 + + # polynomial comparison issue, see https://github.com/sympy/sympy/pull/15799 + # should work once that is solved + # p = do.listofpoly[0] + # assert do == p + + p2 = do2.listofpoly[0] + assert not do2 == p2 + + +def test_DifferentialOperatorPow(): + x = symbols('x', integer=True) + R, _ = DifferentialOperators(QQ.old_poly_ring(x), 'Dx') + do = DifferentialOperator([x**2, R.base.zero, R.base.zero], R) + a = DifferentialOperator([R.base.one], R) + for n in range(10): + assert a == do**n + a *= do diff --git a/.venv/lib/python3.13/site-packages/sympy/holonomic/tests/test_recurrence.py b/.venv/lib/python3.13/site-packages/sympy/holonomic/tests/test_recurrence.py new file mode 100644 index 0000000000000000000000000000000000000000..526595e91c5fc507877275e3e53e78c6f3716095 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/holonomic/tests/test_recurrence.py @@ -0,0 +1,41 @@ +from sympy.holonomic.recurrence import RecurrenceOperators, RecurrenceOperator +from sympy.core.symbol import symbols +from sympy.polys.domains.rationalfield import QQ + + +def test_RecurrenceOperator(): + n = symbols('n', integer=True) + R, Sn = RecurrenceOperators(QQ.old_poly_ring(n), 'Sn') + assert Sn*n == (n + 1)*Sn + assert Sn*n**2 == (n**2+1+2*n)*Sn + assert Sn**2*n**2 == (n**2 + 4*n + 4)*Sn**2 + p = (Sn**3*n**2 + Sn*n)**2 + q = (n**2 + 3*n + 2)*Sn**2 + (2*n**3 + 19*n**2 + 57*n + 52)*Sn**4 + (n**4 + 18*n**3 + \ + 117*n**2 + 324*n + 324)*Sn**6 + assert p == q + + +def test_RecurrenceOperatorEqPoly(): + n = symbols('n', integer=True) + R, Sn = RecurrenceOperators(QQ.old_poly_ring(n), 'Sn') + rr = RecurrenceOperator([n**2, 0, 0], R) + rr2 = RecurrenceOperator([n**2, 1, n], R) + assert not rr == rr2 + + # polynomial comparison issue, see https://github.com/sympy/sympy/pull/15799 + # should work once that is solved + # d = rr.listofpoly[0] + # assert rr == d + + d2 = rr2.listofpoly[0] + assert not rr2 == d2 + + +def test_RecurrenceOperatorPow(): + n = symbols('n', integer=True) + R, _ = RecurrenceOperators(QQ.old_poly_ring(n), 'Sn') + rr = RecurrenceOperator([n**2, 0, 0], R) + a = RecurrenceOperator([R.base.one], R) + for m in range(10): + assert a == rr**m + a *= rr diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/__init__.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..c6839b4494afd0ee0c0ecd9ddee65d1afbdc6b53 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/__init__.py @@ -0,0 +1,57 @@ +"""Implementation of mathematical domains. """ + +__all__ = [ + 'Domain', 'FiniteField', 'IntegerRing', 'RationalField', 'RealField', + 'ComplexField', 'AlgebraicField', 'PolynomialRing', 'FractionField', + 'ExpressionDomain', 'PythonRational', + + 'GF', 'FF', 'ZZ', 'QQ', 'ZZ_I', 'QQ_I', 'RR', 'CC', 'EX', 'EXRAW', +] + +from .domain import Domain +from .finitefield import FiniteField, FF, GF +from .integerring import IntegerRing, ZZ +from .rationalfield import RationalField, QQ +from .algebraicfield import AlgebraicField +from .gaussiandomains import ZZ_I, QQ_I +from .realfield import RealField, RR +from .complexfield import ComplexField, CC +from .polynomialring import PolynomialRing +from .fractionfield import FractionField +from .expressiondomain import ExpressionDomain, EX +from .expressionrawdomain import EXRAW +from .pythonrational import PythonRational + + +# This is imported purely for backwards compatibility because some parts of +# the codebase used to import this from here and it's possible that downstream +# does as well: +from sympy.external.gmpy import GROUND_TYPES # noqa: F401 + +# +# The rest of these are obsolete and provided only for backwards +# compatibility: +# + +from .pythonfinitefield import PythonFiniteField +from .gmpyfinitefield import GMPYFiniteField +from .pythonintegerring import PythonIntegerRing +from .gmpyintegerring import GMPYIntegerRing +from .pythonrationalfield import PythonRationalField +from .gmpyrationalfield import GMPYRationalField + +FF_python = PythonFiniteField +FF_gmpy = GMPYFiniteField + +ZZ_python = PythonIntegerRing +ZZ_gmpy = GMPYIntegerRing + +QQ_python = PythonRationalField +QQ_gmpy = GMPYRationalField + +__all__.extend(( + 'PythonFiniteField', 'GMPYFiniteField', 'PythonIntegerRing', + 'GMPYIntegerRing', 'PythonRational', 'GMPYRationalField', + + 'FF_python', 'FF_gmpy', 'ZZ_python', 'ZZ_gmpy', 'QQ_python', 'QQ_gmpy', +)) diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/algebraicfield.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/algebraicfield.py new file mode 100644 index 0000000000000000000000000000000000000000..3ee3f10d90fc4a3331471ea9a24589d65654d1cd --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/algebraicfield.py @@ -0,0 +1,638 @@ +"""Implementation of :class:`AlgebraicField` class. """ + + +from sympy.core.add import Add +from sympy.core.mul import Mul +from sympy.core.singleton import S +from sympy.core.symbol import Dummy, symbols +from sympy.polys.domains.characteristiczero import CharacteristicZero +from sympy.polys.domains.field import Field +from sympy.polys.domains.simpledomain import SimpleDomain +from sympy.polys.polyclasses import ANP +from sympy.polys.polyerrors import CoercionFailed, DomainError, NotAlgebraic, IsomorphismFailed +from sympy.utilities import public + +@public +class AlgebraicField(Field, CharacteristicZero, SimpleDomain): + r"""Algebraic number field :ref:`QQ(a)` + + A :ref:`QQ(a)` domain represents an `algebraic number field`_ + `\mathbb{Q}(a)` as a :py:class:`~.Domain` in the domain system (see + :ref:`polys-domainsintro`). + + A :py:class:`~.Poly` created from an expression involving `algebraic + numbers`_ will treat the algebraic numbers as generators if the generators + argument is not specified. + + >>> from sympy import Poly, Symbol, sqrt + >>> x = Symbol('x') + >>> Poly(x**2 + sqrt(2)) + Poly(x**2 + (sqrt(2)), x, sqrt(2), domain='ZZ') + + That is a multivariate polynomial with ``sqrt(2)`` treated as one of the + generators (variables). If the generators are explicitly specified then + ``sqrt(2)`` will be considered to be a coefficient but by default the + :ref:`EX` domain is used. To make a :py:class:`~.Poly` with a :ref:`QQ(a)` + domain the argument ``extension=True`` can be given. + + >>> Poly(x**2 + sqrt(2), x) + Poly(x**2 + sqrt(2), x, domain='EX') + >>> Poly(x**2 + sqrt(2), x, extension=True) + Poly(x**2 + sqrt(2), x, domain='QQ') + + A generator of the algebraic field extension can also be specified + explicitly which is particularly useful if the coefficients are all + rational but an extension field is needed (e.g. to factor the + polynomial). + + >>> Poly(x**2 + 1) + Poly(x**2 + 1, x, domain='ZZ') + >>> Poly(x**2 + 1, extension=sqrt(2)) + Poly(x**2 + 1, x, domain='QQ') + + It is possible to factorise a polynomial over a :ref:`QQ(a)` domain using + the ``extension`` argument to :py:func:`~.factor` or by specifying the domain + explicitly. + + >>> from sympy import factor, QQ + >>> factor(x**2 - 2) + x**2 - 2 + >>> factor(x**2 - 2, extension=sqrt(2)) + (x - sqrt(2))*(x + sqrt(2)) + >>> factor(x**2 - 2, domain='QQ') + (x - sqrt(2))*(x + sqrt(2)) + >>> factor(x**2 - 2, domain=QQ.algebraic_field(sqrt(2))) + (x - sqrt(2))*(x + sqrt(2)) + + The ``extension=True`` argument can be used but will only create an + extension that contains the coefficients which is usually not enough to + factorise the polynomial. + + >>> p = x**3 + sqrt(2)*x**2 - 2*x - 2*sqrt(2) + >>> factor(p) # treats sqrt(2) as a symbol + (x + sqrt(2))*(x**2 - 2) + >>> factor(p, extension=True) + (x - sqrt(2))*(x + sqrt(2))**2 + >>> factor(x**2 - 2, extension=True) # all rational coefficients + x**2 - 2 + + It is also possible to use :ref:`QQ(a)` with the :py:func:`~.cancel` + and :py:func:`~.gcd` functions. + + >>> from sympy import cancel, gcd + >>> cancel((x**2 - 2)/(x - sqrt(2))) + (x**2 - 2)/(x - sqrt(2)) + >>> cancel((x**2 - 2)/(x - sqrt(2)), extension=sqrt(2)) + x + sqrt(2) + >>> gcd(x**2 - 2, x - sqrt(2)) + 1 + >>> gcd(x**2 - 2, x - sqrt(2), extension=sqrt(2)) + x - sqrt(2) + + When using the domain directly :ref:`QQ(a)` can be used as a constructor + to create instances which then support the operations ``+,-,*,**,/``. The + :py:meth:`~.Domain.algebraic_field` method is used to construct a + particular :ref:`QQ(a)` domain. The :py:meth:`~.Domain.from_sympy` method + can be used to create domain elements from normal SymPy expressions. + + >>> K = QQ.algebraic_field(sqrt(2)) + >>> K + QQ + >>> xk = K.from_sympy(3 + 4*sqrt(2)) + >>> xk # doctest: +SKIP + ANP([4, 3], [1, 0, -2], QQ) + + Elements of :ref:`QQ(a)` are instances of :py:class:`~.ANP` which have + limited printing support. The raw display shows the internal + representation of the element as the list ``[4, 3]`` representing the + coefficients of ``1`` and ``sqrt(2)`` for this element in the form + ``a * sqrt(2) + b * 1`` where ``a`` and ``b`` are elements of :ref:`QQ`. + The minimal polynomial for the generator ``(x**2 - 2)`` is also shown in + the :ref:`dup-representation` as the list ``[1, 0, -2]``. We can use + :py:meth:`~.Domain.to_sympy` to get a better printed form for the + elements and to see the results of operations. + + >>> xk = K.from_sympy(3 + 4*sqrt(2)) + >>> yk = K.from_sympy(2 + 3*sqrt(2)) + >>> xk * yk # doctest: +SKIP + ANP([17, 30], [1, 0, -2], QQ) + >>> K.to_sympy(xk * yk) + 17*sqrt(2) + 30 + >>> K.to_sympy(xk + yk) + 5 + 7*sqrt(2) + >>> K.to_sympy(xk ** 2) + 24*sqrt(2) + 41 + >>> K.to_sympy(xk / yk) + sqrt(2)/14 + 9/7 + + Any expression representing an algebraic number can be used to generate + a :ref:`QQ(a)` domain provided its `minimal polynomial`_ can be computed. + The function :py:func:`~.minpoly` function is used for this. + + >>> from sympy import exp, I, pi, minpoly + >>> g = exp(2*I*pi/3) + >>> g + exp(2*I*pi/3) + >>> g.is_algebraic + True + >>> minpoly(g, x) + x**2 + x + 1 + >>> factor(x**3 - 1, extension=g) + (x - 1)*(x - exp(2*I*pi/3))*(x + 1 + exp(2*I*pi/3)) + + It is also possible to make an algebraic field from multiple extension + elements. + + >>> K = QQ.algebraic_field(sqrt(2), sqrt(3)) + >>> K + QQ + >>> p = x**4 - 5*x**2 + 6 + >>> factor(p) + (x**2 - 3)*(x**2 - 2) + >>> factor(p, domain=K) + (x - sqrt(2))*(x + sqrt(2))*(x - sqrt(3))*(x + sqrt(3)) + >>> factor(p, extension=[sqrt(2), sqrt(3)]) + (x - sqrt(2))*(x + sqrt(2))*(x - sqrt(3))*(x + sqrt(3)) + + Multiple extension elements are always combined together to make a single + `primitive element`_. In the case of ``[sqrt(2), sqrt(3)]`` the primitive + element chosen is ``sqrt(2) + sqrt(3)`` which is why the domain displays + as ``QQ``. The minimal polynomial for the primitive + element is computed using the :py:func:`~.primitive_element` function. + + >>> from sympy import primitive_element + >>> primitive_element([sqrt(2), sqrt(3)], x) + (x**4 - 10*x**2 + 1, [1, 1]) + >>> minpoly(sqrt(2) + sqrt(3), x) + x**4 - 10*x**2 + 1 + + The extension elements that generate the domain can be accessed from the + domain using the :py:attr:`~.ext` and :py:attr:`~.orig_ext` attributes as + instances of :py:class:`~.AlgebraicNumber`. The minimal polynomial for + the primitive element as a :py:class:`~.DMP` instance is available as + :py:attr:`~.mod`. + + >>> K = QQ.algebraic_field(sqrt(2), sqrt(3)) + >>> K + QQ + >>> K.ext + sqrt(2) + sqrt(3) + >>> K.orig_ext + (sqrt(2), sqrt(3)) + >>> K.mod # doctest: +SKIP + DMP_Python([1, 0, -10, 0, 1], QQ) + + The `discriminant`_ of the field can be obtained from the + :py:meth:`~.discriminant` method, and an `integral basis`_ from the + :py:meth:`~.integral_basis` method. The latter returns a list of + :py:class:`~.ANP` instances by default, but can be made to return instances + of :py:class:`~.Expr` or :py:class:`~.AlgebraicNumber` by passing a ``fmt`` + argument. The maximal order, or ring of integers, of the field can also be + obtained from the :py:meth:`~.maximal_order` method, as a + :py:class:`~sympy.polys.numberfields.modules.Submodule`. + + >>> zeta5 = exp(2*I*pi/5) + >>> K = QQ.algebraic_field(zeta5) + >>> K + QQ + >>> K.discriminant() + 125 + >>> K = QQ.algebraic_field(sqrt(5)) + >>> K + QQ + >>> K.integral_basis(fmt='sympy') + [1, 1/2 + sqrt(5)/2] + >>> K.maximal_order() + Submodule[[2, 0], [1, 1]]/2 + + The factorization of a rational prime into prime ideals of the field is + computed by the :py:meth:`~.primes_above` method, which returns a list + of :py:class:`~sympy.polys.numberfields.primes.PrimeIdeal` instances. + + >>> zeta7 = exp(2*I*pi/7) + >>> K = QQ.algebraic_field(zeta7) + >>> K + QQ + >>> K.primes_above(11) + [(11, _x**3 + 5*_x**2 + 4*_x - 1), (11, _x**3 - 4*_x**2 - 5*_x - 1)] + + The Galois group of the Galois closure of the field can be computed (when + the minimal polynomial of the field is of sufficiently small degree). + + >>> K.galois_group(by_name=True)[0] + S6TransitiveSubgroups.C6 + + Notes + ===== + + It is not currently possible to generate an algebraic extension over any + domain other than :ref:`QQ`. Ideally it would be possible to generate + extensions like ``QQ(x)(sqrt(x**2 - 2))``. This is equivalent to the + quotient ring ``QQ(x)[y]/(y**2 - x**2 + 2)`` and there are two + implementations of this kind of quotient ring/extension in the + :py:class:`~.QuotientRing` and :py:class:`~.MonogenicFiniteExtension` + classes. Each of those implementations needs some work to make them fully + usable though. + + .. _algebraic number field: https://en.wikipedia.org/wiki/Algebraic_number_field + .. _algebraic numbers: https://en.wikipedia.org/wiki/Algebraic_number + .. _discriminant: https://en.wikipedia.org/wiki/Discriminant_of_an_algebraic_number_field + .. _integral basis: https://en.wikipedia.org/wiki/Algebraic_number_field#Integral_basis + .. _minimal polynomial: https://en.wikipedia.org/wiki/Minimal_polynomial_(field_theory) + .. _primitive element: https://en.wikipedia.org/wiki/Primitive_element_theorem + """ + + dtype = ANP + + is_AlgebraicField = is_Algebraic = True + is_Numerical = True + + has_assoc_Ring = False + has_assoc_Field = True + + def __init__(self, dom, *ext, alias=None): + r""" + Parameters + ========== + + dom : :py:class:`~.Domain` + The base field over which this is an extension field. + Currently only :ref:`QQ` is accepted. + + *ext : One or more :py:class:`~.Expr` + Generators of the extension. These should be expressions that are + algebraic over `\mathbb{Q}`. + + alias : str, :py:class:`~.Symbol`, None, optional (default=None) + If provided, this will be used as the alias symbol for the + primitive element of the :py:class:`~.AlgebraicField`. + If ``None``, while ``ext`` consists of exactly one + :py:class:`~.AlgebraicNumber`, its alias (if any) will be used. + """ + if not dom.is_QQ: + raise DomainError("ground domain must be a rational field") + + from sympy.polys.numberfields import to_number_field + if len(ext) == 1 and isinstance(ext[0], tuple): + orig_ext = ext[0][1:] + else: + orig_ext = ext + + if alias is None and len(ext) == 1: + alias = getattr(ext[0], 'alias', None) + + self.orig_ext = orig_ext + """ + Original elements given to generate the extension. + + >>> from sympy import QQ, sqrt + >>> K = QQ.algebraic_field(sqrt(2), sqrt(3)) + >>> K.orig_ext + (sqrt(2), sqrt(3)) + """ + + self.ext = to_number_field(ext, alias=alias) + """ + Primitive element used for the extension. + + >>> from sympy import QQ, sqrt + >>> K = QQ.algebraic_field(sqrt(2), sqrt(3)) + >>> K.ext + sqrt(2) + sqrt(3) + """ + + self.mod = self.ext.minpoly.rep + """ + Minimal polynomial for the primitive element of the extension. + + >>> from sympy import QQ, sqrt + >>> K = QQ.algebraic_field(sqrt(2)) + >>> K.mod + DMP([1, 0, -2], QQ) + """ + + self.domain = self.dom = dom + + self.ngens = 1 + self.symbols = self.gens = (self.ext,) + self.unit = self([dom(1), dom(0)]) + + self.zero = self.dtype.zero(self.mod.to_list(), dom) + self.one = self.dtype.one(self.mod.to_list(), dom) + + self._maximal_order = None + self._discriminant = None + self._nilradicals_mod_p = {} + + def new(self, element): + return self.dtype(element, self.mod.to_list(), self.dom) + + def __str__(self): + return str(self.dom) + '<' + str(self.ext) + '>' + + def __hash__(self): + return hash((self.__class__.__name__, self.dtype, self.dom, self.ext)) + + def __eq__(self, other): + """Returns ``True`` if two domains are equivalent. """ + if isinstance(other, AlgebraicField): + return self.dtype == other.dtype and self.ext == other.ext + else: + return NotImplemented + + def algebraic_field(self, *extension, alias=None): + r"""Returns an algebraic field, i.e. `\mathbb{Q}(\alpha, \ldots)`. """ + return AlgebraicField(self.dom, *((self.ext,) + extension), alias=alias) + + def to_alg_num(self, a): + """Convert ``a`` of ``dtype`` to an :py:class:`~.AlgebraicNumber`. """ + return self.ext.field_element(a) + + def to_sympy(self, a): + """Convert ``a`` of ``dtype`` to a SymPy object. """ + # Precompute a converter to be reused: + if not hasattr(self, '_converter'): + self._converter = _make_converter(self) + + return self._converter(a) + + def from_sympy(self, a): + """Convert SymPy's expression to ``dtype``. """ + try: + return self([self.dom.from_sympy(a)]) + except CoercionFailed: + pass + + from sympy.polys.numberfields import to_number_field + + try: + return self(to_number_field(a, self.ext).native_coeffs()) + except (NotAlgebraic, IsomorphismFailed): + raise CoercionFailed( + "%s is not a valid algebraic number in %s" % (a, self)) + + def from_ZZ(K1, a, K0): + """Convert a Python ``int`` object to ``dtype``. """ + return K1(K1.dom.convert(a, K0)) + + def from_ZZ_python(K1, a, K0): + """Convert a Python ``int`` object to ``dtype``. """ + return K1(K1.dom.convert(a, K0)) + + def from_QQ(K1, a, K0): + """Convert a Python ``Fraction`` object to ``dtype``. """ + return K1(K1.dom.convert(a, K0)) + + def from_QQ_python(K1, a, K0): + """Convert a Python ``Fraction`` object to ``dtype``. """ + return K1(K1.dom.convert(a, K0)) + + def from_ZZ_gmpy(K1, a, K0): + """Convert a GMPY ``mpz`` object to ``dtype``. """ + return K1(K1.dom.convert(a, K0)) + + def from_QQ_gmpy(K1, a, K0): + """Convert a GMPY ``mpq`` object to ``dtype``. """ + return K1(K1.dom.convert(a, K0)) + + def from_RealField(K1, a, K0): + """Convert a mpmath ``mpf`` object to ``dtype``. """ + return K1(K1.dom.convert(a, K0)) + + def get_ring(self): + """Returns a ring associated with ``self``. """ + raise DomainError('there is no ring associated with %s' % self) + + def is_positive(self, a): + """Returns True if ``a`` is positive. """ + return self.dom.is_positive(a.LC()) + + def is_negative(self, a): + """Returns True if ``a`` is negative. """ + return self.dom.is_negative(a.LC()) + + def is_nonpositive(self, a): + """Returns True if ``a`` is non-positive. """ + return self.dom.is_nonpositive(a.LC()) + + def is_nonnegative(self, a): + """Returns True if ``a`` is non-negative. """ + return self.dom.is_nonnegative(a.LC()) + + def numer(self, a): + """Returns numerator of ``a``. """ + return a + + def denom(self, a): + """Returns denominator of ``a``. """ + return self.one + + def from_AlgebraicField(K1, a, K0): + """Convert AlgebraicField element 'a' to another AlgebraicField """ + return K1.from_sympy(K0.to_sympy(a)) + + def from_GaussianIntegerRing(K1, a, K0): + """Convert a GaussianInteger element 'a' to ``dtype``. """ + return K1.from_sympy(K0.to_sympy(a)) + + def from_GaussianRationalField(K1, a, K0): + """Convert a GaussianRational element 'a' to ``dtype``. """ + return K1.from_sympy(K0.to_sympy(a)) + + def _do_round_two(self): + from sympy.polys.numberfields.basis import round_two + ZK, dK = round_two(self, radicals=self._nilradicals_mod_p) + self._maximal_order = ZK + self._discriminant = dK + + def maximal_order(self): + """ + Compute the maximal order, or ring of integers, of the field. + + Returns + ======= + + :py:class:`~sympy.polys.numberfields.modules.Submodule`. + + See Also + ======== + + integral_basis + + """ + if self._maximal_order is None: + self._do_round_two() + return self._maximal_order + + def integral_basis(self, fmt=None): + r""" + Get an integral basis for the field. + + Parameters + ========== + + fmt : str, None, optional (default=None) + If ``None``, return a list of :py:class:`~.ANP` instances. + If ``"sympy"``, convert each element of the list to an + :py:class:`~.Expr`, using ``self.to_sympy()``. + If ``"alg"``, convert each element of the list to an + :py:class:`~.AlgebraicNumber`, using ``self.to_alg_num()``. + + Examples + ======== + + >>> from sympy import QQ, AlgebraicNumber, sqrt + >>> alpha = AlgebraicNumber(sqrt(5), alias='alpha') + >>> k = QQ.algebraic_field(alpha) + >>> B0 = k.integral_basis() + >>> B1 = k.integral_basis(fmt='sympy') + >>> B2 = k.integral_basis(fmt='alg') + >>> print(B0[1]) # doctest: +SKIP + ANP([mpq(1,2), mpq(1,2)], [mpq(1,1), mpq(0,1), mpq(-5,1)], QQ) + >>> print(B1[1]) + 1/2 + alpha/2 + >>> print(B2[1]) + alpha/2 + 1/2 + + In the last two cases we get legible expressions, which print somewhat + differently because of the different types involved: + + >>> print(type(B1[1])) + + >>> print(type(B2[1])) + + + See Also + ======== + + to_sympy + to_alg_num + maximal_order + """ + ZK = self.maximal_order() + M = ZK.QQ_matrix + n = M.shape[1] + B = [self.new(list(reversed(M[:, j].flat()))) for j in range(n)] + if fmt == 'sympy': + return [self.to_sympy(b) for b in B] + elif fmt == 'alg': + return [self.to_alg_num(b) for b in B] + return B + + def discriminant(self): + """Get the discriminant of the field.""" + if self._discriminant is None: + self._do_round_two() + return self._discriminant + + def primes_above(self, p): + """Compute the prime ideals lying above a given rational prime *p*.""" + from sympy.polys.numberfields.primes import prime_decomp + ZK = self.maximal_order() + dK = self.discriminant() + rad = self._nilradicals_mod_p.get(p) + return prime_decomp(p, ZK=ZK, dK=dK, radical=rad) + + def galois_group(self, by_name=False, max_tries=30, randomize=False): + """ + Compute the Galois group of the Galois closure of this field. + + Examples + ======== + + If the field is Galois, the order of the group will equal the degree + of the field: + + >>> from sympy import QQ + >>> from sympy.abc import x + >>> k = QQ.alg_field_from_poly(x**4 + 1) + >>> G, _ = k.galois_group() + >>> G.order() + 4 + + If the field is not Galois, then its Galois closure is a proper + extension, and the order of the Galois group will be greater than the + degree of the field: + + >>> k = QQ.alg_field_from_poly(x**4 - 2) + >>> G, _ = k.galois_group() + >>> G.order() + 8 + + See Also + ======== + + sympy.polys.numberfields.galoisgroups.galois_group + + """ + return self.ext.minpoly_of_element().galois_group( + by_name=by_name, max_tries=max_tries, randomize=randomize) + + +def _make_converter(K): + """Construct the converter to convert back to Expr""" + # Precompute the effect of converting to SymPy and expanding expressions + # like (sqrt(2) + sqrt(3))**2. Asking Expr to do the expansion on every + # conversion from K to Expr is slow. Here we compute the expansions for + # each power of the generator and collect together the resulting algebraic + # terms and the rational coefficients into a matrix. + + ext = K.ext.as_expr() + todom = K.dom.from_sympy + toexpr = K.dom.to_sympy + + if not ext.is_Add: + powers = [ext**n for n in range(K.mod.degree())] + else: + # primitive_element generates a QQ-linear combination of lower degree + # algebraic numbers to generate the higher degree extension e.g. + # QQ That means that we end up having high powers of low + # degree algebraic numbers that can be reduced. Here we will use the + # minimal polynomials of the algebraic numbers to reduce those powers + # before converting to Expr. + from sympy.polys.numberfields.minpoly import minpoly + + # Decompose ext as a linear combination of gens and make a symbol for + # each gen. + gens, coeffs = zip(*ext.as_coefficients_dict().items()) + syms = symbols(f'a:{len(gens)}', cls=Dummy) + sym2gen = dict(zip(syms, gens)) + + # Make a polynomial ring that can express ext and minpolys of all gens + # in terms of syms. + R = K.dom[syms] + monoms = [R.ring.monomial_basis(i) for i in range(R.ngens)] + ext_dict = {m: todom(c) for m, c in zip(monoms, coeffs)} + ext_poly = R.ring.from_dict(ext_dict) + minpolys = [R.from_sympy(minpoly(g, s)) for s, g in sym2gen.items()] + + # Compute all powers of ext_poly reduced modulo minpolys + powers = [R.one, ext_poly] + for n in range(2, K.mod.degree()): + ext_poly_n = (powers[-1] * ext_poly).rem(minpolys) + powers.append(ext_poly_n) + + # Convert the powers back to Expr. This will recombine some things like + # sqrt(2)*sqrt(3) -> sqrt(6). + powers = [p.as_expr().xreplace(sym2gen) for p in powers] + + # This also expands some rational powers + powers = [p.expand() for p in powers] + + # Collect the rational coefficients and algebraic Expr that can + # map the ANP coefficients into an expanded SymPy expression + terms = [dict(t.as_coeff_Mul()[::-1] for t in Add.make_args(p)) for p in powers] + algebraics = set().union(*terms) + matrix = [[todom(t.get(a, S.Zero)) for t in terms] for a in algebraics] + + # Create a function to do the conversion efficiently: + + def converter(a): + """Convert a to Expr using converter""" + ai = a.to_list()[::-1] + coeffs_dom = [sum(mij*aj for mij, aj in zip(mi, ai)) for mi in matrix] + coeffs_sympy = [toexpr(c) for c in coeffs_dom] + res = Add(*(Mul(c, a) for c, a in zip(coeffs_sympy, algebraics))) + return res + + return converter diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/characteristiczero.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/characteristiczero.py new file mode 100644 index 0000000000000000000000000000000000000000..755a354bea9594b9e8f73256c448b3debae037b2 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/characteristiczero.py @@ -0,0 +1,15 @@ +"""Implementation of :class:`CharacteristicZero` class. """ + + +from sympy.polys.domains.domain import Domain +from sympy.utilities import public + +@public +class CharacteristicZero(Domain): + """Domain that has infinite number of elements. """ + + has_CharacteristicZero = True + + def characteristic(self): + """Return the characteristic of this domain. """ + return 0 diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/complexfield.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/complexfield.py new file mode 100644 index 0000000000000000000000000000000000000000..69f0bff2c1b311a150add88d5a1f146ea7b1726a --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/complexfield.py @@ -0,0 +1,198 @@ +"""Implementation of :class:`ComplexField` class. """ + + +from sympy.external.gmpy import SYMPY_INTS +from sympy.core.numbers import Float, I +from sympy.polys.domains.characteristiczero import CharacteristicZero +from sympy.polys.domains.field import Field +from sympy.polys.domains.gaussiandomains import QQ_I +from sympy.polys.domains.simpledomain import SimpleDomain +from sympy.polys.polyerrors import DomainError, CoercionFailed +from sympy.utilities import public + +from mpmath import MPContext + + +@public +class ComplexField(Field, CharacteristicZero, SimpleDomain): + """Complex numbers up to the given precision. """ + + rep = 'CC' + + is_ComplexField = is_CC = True + + is_Exact = False + is_Numerical = True + + has_assoc_Ring = False + has_assoc_Field = True + + _default_precision = 53 + + @property + def has_default_precision(self): + return self.precision == self._default_precision + + @property + def precision(self): + return self._context.prec + + @property + def dps(self): + return self._context.dps + + @property + def tolerance(self): + return self._tolerance + + def __init__(self, prec=None, dps=None, tol=None): + # XXX: The tolerance parameter is ignored but is kept for backward + # compatibility for now. + + context = MPContext() + + if prec is None and dps is None: + context.prec = self._default_precision + elif dps is None: + context.prec = prec + elif prec is None: + context.dps = dps + else: + raise TypeError("Cannot set both prec and dps") + + self._context = context + + self._dtype = context.mpc + self.zero = self.dtype(0) + self.one = self.dtype(1) + + # XXX: Neither of these is actually used anywhere. + self._max_denom = max(2**context.prec // 200, 99) + self._tolerance = self.one / self._max_denom + + @property + def tp(self): + # XXX: Domain treats tp as an alias of dtype. Here we need two separate + # things: dtype is a callable to make/convert instances. We use tp with + # isinstance to check if an object is an instance of the domain + # already. + return self._dtype + + def dtype(self, x, y=0): + # XXX: This is needed because mpmath does not recognise fmpz. + # It might be better to add conversion routines to mpmath and if that + # happens then this can be removed. + if isinstance(x, SYMPY_INTS): + x = int(x) + if isinstance(y, SYMPY_INTS): + y = int(y) + return self._dtype(x, y) + + def __eq__(self, other): + return isinstance(other, ComplexField) and self.precision == other.precision + + def __hash__(self): + return hash((self.__class__.__name__, self._dtype, self.precision)) + + def to_sympy(self, element): + """Convert ``element`` to SymPy number. """ + return Float(element.real, self.dps) + I*Float(element.imag, self.dps) + + def from_sympy(self, expr): + """Convert SymPy's number to ``dtype``. """ + number = expr.evalf(n=self.dps) + real, imag = number.as_real_imag() + + if real.is_Number and imag.is_Number: + return self.dtype(real, imag) + else: + raise CoercionFailed("expected complex number, got %s" % expr) + + def from_ZZ(self, element, base): + return self.dtype(element) + + def from_ZZ_gmpy(self, element, base): + return self.dtype(int(element)) + + def from_ZZ_python(self, element, base): + return self.dtype(element) + + def from_QQ(self, element, base): + return self.dtype(int(element.numerator)) / int(element.denominator) + + def from_QQ_python(self, element, base): + return self.dtype(element.numerator) / element.denominator + + def from_QQ_gmpy(self, element, base): + return self.dtype(int(element.numerator)) / int(element.denominator) + + def from_GaussianIntegerRing(self, element, base): + return self.dtype(int(element.x), int(element.y)) + + def from_GaussianRationalField(self, element, base): + x = element.x + y = element.y + return (self.dtype(int(x.numerator)) / int(x.denominator) + + self.dtype(0, int(y.numerator)) / int(y.denominator)) + + def from_AlgebraicField(self, element, base): + return self.from_sympy(base.to_sympy(element).evalf(self.dps)) + + def from_RealField(self, element, base): + return self.dtype(element) + + def from_ComplexField(self, element, base): + return self.dtype(element) + + def get_ring(self): + """Returns a ring associated with ``self``. """ + raise DomainError("there is no ring associated with %s" % self) + + def get_exact(self): + """Returns an exact domain associated with ``self``. """ + return QQ_I + + def is_negative(self, element): + """Returns ``False`` for any ``ComplexElement``. """ + return False + + def is_positive(self, element): + """Returns ``False`` for any ``ComplexElement``. """ + return False + + def is_nonnegative(self, element): + """Returns ``False`` for any ``ComplexElement``. """ + return False + + def is_nonpositive(self, element): + """Returns ``False`` for any ``ComplexElement``. """ + return False + + def gcd(self, a, b): + """Returns GCD of ``a`` and ``b``. """ + return self.one + + def lcm(self, a, b): + """Returns LCM of ``a`` and ``b``. """ + return a*b + + def almosteq(self, a, b, tolerance=None): + """Check if ``a`` and ``b`` are almost equal. """ + return self._context.almosteq(a, b, tolerance) + + def is_square(self, a): + """Returns ``True``. Every complex number has a complex square root.""" + return True + + def exsqrt(self, a): + r"""Returns the principal complex square root of ``a``. + + Explanation + =========== + The argument of the principal square root is always within + $(-\frac{\pi}{2}, \frac{\pi}{2}]$. The square root may be + slightly inaccurate due to floating point rounding error. + """ + return a ** 0.5 + +CC = ComplexField() diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/compositedomain.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/compositedomain.py new file mode 100644 index 0000000000000000000000000000000000000000..a8f63ba7bb86b1d69493b77bfa8c7f33652adbbf --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/compositedomain.py @@ -0,0 +1,52 @@ +"""Implementation of :class:`CompositeDomain` class. """ + + +from sympy.polys.domains.domain import Domain +from sympy.polys.polyerrors import GeneratorsError + +from sympy.utilities import public + +@public +class CompositeDomain(Domain): + """Base class for composite domains, e.g. ZZ[x], ZZ(X). """ + + is_Composite = True + + gens, ngens, symbols, domain = [None]*4 + + def inject(self, *symbols): + """Inject generators into this domain. """ + if not (set(self.symbols) & set(symbols)): + return self.__class__(self.domain, self.symbols + symbols, self.order) + else: + raise GeneratorsError("common generators in %s and %s" % (self.symbols, symbols)) + + def drop(self, *symbols): + """Drop generators from this domain. """ + symset = set(symbols) + newsyms = tuple(s for s in self.symbols if s not in symset) + domain = self.domain.drop(*symbols) + if not newsyms: + return domain + else: + return self.__class__(domain, newsyms, self.order) + + def set_domain(self, domain): + """Set the ground domain of this domain. """ + return self.__class__(domain, self.symbols, self.order) + + @property + def is_Exact(self): + """Returns ``True`` if this domain is exact. """ + return self.domain.is_Exact + + def get_exact(self): + """Returns an exact version of this domain. """ + return self.set_domain(self.domain.get_exact()) + + @property + def has_CharacteristicZero(self): + return self.domain.has_CharacteristicZero + + def characteristic(self): + return self.domain.characteristic() diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/domain.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/domain.py new file mode 100644 index 0000000000000000000000000000000000000000..1d7fc1eac6184601c199fb6724a11e92346789f1 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/domain.py @@ -0,0 +1,1382 @@ +"""Implementation of :class:`Domain` class. """ + +from __future__ import annotations +from typing import Any + +from sympy.core.numbers import AlgebraicNumber +from sympy.core import Basic, sympify +from sympy.core.sorting import ordered +from sympy.external.gmpy import GROUND_TYPES +from sympy.polys.domains.domainelement import DomainElement +from sympy.polys.orderings import lex +from sympy.polys.polyerrors import UnificationFailed, CoercionFailed, DomainError +from sympy.polys.polyutils import _unify_gens, _not_a_coeff +from sympy.utilities import public +from sympy.utilities.iterables import is_sequence + + +@public +class Domain: + """Superclass for all domains in the polys domains system. + + See :ref:`polys-domainsintro` for an introductory explanation of the + domains system. + + The :py:class:`~.Domain` class is an abstract base class for all of the + concrete domain types. There are many different :py:class:`~.Domain` + subclasses each of which has an associated ``dtype`` which is a class + representing the elements of the domain. The coefficients of a + :py:class:`~.Poly` are elements of a domain which must be a subclass of + :py:class:`~.Domain`. + + Examples + ======== + + The most common example domains are the integers :ref:`ZZ` and the + rationals :ref:`QQ`. + + >>> from sympy import Poly, symbols, Domain + >>> x, y = symbols('x, y') + >>> p = Poly(x**2 + y) + >>> p + Poly(x**2 + y, x, y, domain='ZZ') + >>> p.domain + ZZ + >>> isinstance(p.domain, Domain) + True + >>> Poly(x**2 + y/2) + Poly(x**2 + 1/2*y, x, y, domain='QQ') + + The domains can be used directly in which case the domain object e.g. + (:ref:`ZZ` or :ref:`QQ`) can be used as a constructor for elements of + ``dtype``. + + >>> from sympy import ZZ, QQ + >>> ZZ(2) + 2 + >>> ZZ.dtype # doctest: +SKIP + + >>> type(ZZ(2)) # doctest: +SKIP + + >>> QQ(1, 2) + 1/2 + >>> type(QQ(1, 2)) # doctest: +SKIP + + + The corresponding domain elements can be used with the arithmetic + operations ``+,-,*,**`` and depending on the domain some combination of + ``/,//,%`` might be usable. For example in :ref:`ZZ` both ``//`` (floor + division) and ``%`` (modulo division) can be used but ``/`` (true + division) cannot. Since :ref:`QQ` is a :py:class:`~.Field` its elements + can be used with ``/`` but ``//`` and ``%`` should not be used. Some + domains have a :py:meth:`~.Domain.gcd` method. + + >>> ZZ(2) + ZZ(3) + 5 + >>> ZZ(5) // ZZ(2) + 2 + >>> ZZ(5) % ZZ(2) + 1 + >>> QQ(1, 2) / QQ(2, 3) + 3/4 + >>> ZZ.gcd(ZZ(4), ZZ(2)) + 2 + >>> QQ.gcd(QQ(2,7), QQ(5,3)) + 1/21 + >>> ZZ.is_Field + False + >>> QQ.is_Field + True + + There are also many other domains including: + + 1. :ref:`GF(p)` for finite fields of prime order. + 2. :ref:`RR` for real (floating point) numbers. + 3. :ref:`CC` for complex (floating point) numbers. + 4. :ref:`QQ(a)` for algebraic number fields. + 5. :ref:`K[x]` for polynomial rings. + 6. :ref:`K(x)` for rational function fields. + 7. :ref:`EX` for arbitrary expressions. + + Each domain is represented by a domain object and also an implementation + class (``dtype``) for the elements of the domain. For example the + :ref:`K[x]` domains are represented by a domain object which is an + instance of :py:class:`~.PolynomialRing` and the elements are always + instances of :py:class:`~.PolyElement`. The implementation class + represents particular types of mathematical expressions in a way that is + more efficient than a normal SymPy expression which is of type + :py:class:`~.Expr`. The domain methods :py:meth:`~.Domain.from_sympy` and + :py:meth:`~.Domain.to_sympy` are used to convert from :py:class:`~.Expr` + to a domain element and vice versa. + + >>> from sympy import Symbol, ZZ, Expr + >>> x = Symbol('x') + >>> K = ZZ[x] # polynomial ring domain + >>> K + ZZ[x] + >>> type(K) # class of the domain + + >>> K.dtype # doctest: +SKIP + + >>> p_expr = x**2 + 1 # Expr + >>> p_expr + x**2 + 1 + >>> type(p_expr) + + >>> isinstance(p_expr, Expr) + True + >>> p_domain = K.from_sympy(p_expr) + >>> p_domain # domain element + x**2 + 1 + >>> type(p_domain) + + >>> K.to_sympy(p_domain) == p_expr + True + + The :py:meth:`~.Domain.convert_from` method is used to convert domain + elements from one domain to another. + + >>> from sympy import ZZ, QQ + >>> ez = ZZ(2) + >>> eq = QQ.convert_from(ez, ZZ) + >>> type(ez) # doctest: +SKIP + + >>> type(eq) # doctest: +SKIP + + + Elements from different domains should not be mixed in arithmetic or other + operations: they should be converted to a common domain first. The domain + method :py:meth:`~.Domain.unify` is used to find a domain that can + represent all the elements of two given domains. + + >>> from sympy import ZZ, QQ, symbols + >>> x, y = symbols('x, y') + >>> ZZ.unify(QQ) + QQ + >>> ZZ[x].unify(QQ) + QQ[x] + >>> ZZ[x].unify(QQ[y]) + QQ[x,y] + + If a domain is a :py:class:`~.Ring` then is might have an associated + :py:class:`~.Field` and vice versa. The :py:meth:`~.Domain.get_field` and + :py:meth:`~.Domain.get_ring` methods will find or create the associated + domain. + + >>> from sympy import ZZ, QQ, Symbol + >>> x = Symbol('x') + >>> ZZ.has_assoc_Field + True + >>> ZZ.get_field() + QQ + >>> QQ.has_assoc_Ring + True + >>> QQ.get_ring() + ZZ + >>> K = QQ[x] + >>> K + QQ[x] + >>> K.get_field() + QQ(x) + + See also + ======== + + DomainElement: abstract base class for domain elements + construct_domain: construct a minimal domain for some expressions + + """ + + dtype: type | None = None + """The type (class) of the elements of this :py:class:`~.Domain`: + + >>> from sympy import ZZ, QQ, Symbol + >>> ZZ.dtype + + >>> z = ZZ(2) + >>> z + 2 + >>> type(z) + + >>> type(z) == ZZ.dtype + True + + Every domain has an associated **dtype** ("datatype") which is the + class of the associated domain elements. + + See also + ======== + + of_type + """ + + zero: Any = None + """The zero element of the :py:class:`~.Domain`: + + >>> from sympy import QQ + >>> QQ.zero + 0 + >>> QQ.of_type(QQ.zero) + True + + See also + ======== + + of_type + one + """ + + one: Any = None + """The one element of the :py:class:`~.Domain`: + + >>> from sympy import QQ + >>> QQ.one + 1 + >>> QQ.of_type(QQ.one) + True + + See also + ======== + + of_type + zero + """ + + is_Ring = False + """Boolean flag indicating if the domain is a :py:class:`~.Ring`. + + >>> from sympy import ZZ + >>> ZZ.is_Ring + True + + Basically every :py:class:`~.Domain` represents a ring so this flag is + not that useful. + + See also + ======== + + is_PID + is_Field + get_ring + has_assoc_Ring + """ + + is_Field = False + """Boolean flag indicating if the domain is a :py:class:`~.Field`. + + >>> from sympy import ZZ, QQ + >>> ZZ.is_Field + False + >>> QQ.is_Field + True + + See also + ======== + + is_PID + is_Ring + get_field + has_assoc_Field + """ + + has_assoc_Ring = False + """Boolean flag indicating if the domain has an associated + :py:class:`~.Ring`. + + >>> from sympy import QQ + >>> QQ.has_assoc_Ring + True + >>> QQ.get_ring() + ZZ + + See also + ======== + + is_Field + get_ring + """ + + has_assoc_Field = False + """Boolean flag indicating if the domain has an associated + :py:class:`~.Field`. + + >>> from sympy import ZZ + >>> ZZ.has_assoc_Field + True + >>> ZZ.get_field() + QQ + + See also + ======== + + is_Field + get_field + """ + + is_FiniteField = is_FF = False + is_IntegerRing = is_ZZ = False + is_RationalField = is_QQ = False + is_GaussianRing = is_ZZ_I = False + is_GaussianField = is_QQ_I = False + is_RealField = is_RR = False + is_ComplexField = is_CC = False + is_AlgebraicField = is_Algebraic = False + is_PolynomialRing = is_Poly = False + is_FractionField = is_Frac = False + is_SymbolicDomain = is_EX = False + is_SymbolicRawDomain = is_EXRAW = False + is_FiniteExtension = False + + is_Exact = True + is_Numerical = False + + is_Simple = False + is_Composite = False + + is_PID = False + """Boolean flag indicating if the domain is a `principal ideal domain`_. + + >>> from sympy import ZZ + >>> ZZ.has_assoc_Field + True + >>> ZZ.get_field() + QQ + + .. _principal ideal domain: https://en.wikipedia.org/wiki/Principal_ideal_domain + + See also + ======== + + is_Field + get_field + """ + + has_CharacteristicZero = False + + rep: str | None = None + alias: str | None = None + + def __init__(self): + raise NotImplementedError + + def __str__(self): + return self.rep + + def __repr__(self): + return str(self) + + def __hash__(self): + return hash((self.__class__.__name__, self.dtype)) + + def new(self, *args): + return self.dtype(*args) + + @property + def tp(self): + """Alias for :py:attr:`~.Domain.dtype`""" + return self.dtype + + def __call__(self, *args): + """Construct an element of ``self`` domain from ``args``. """ + return self.new(*args) + + def normal(self, *args): + return self.dtype(*args) + + def convert_from(self, element, base): + """Convert ``element`` to ``self.dtype`` given the base domain. """ + if base.alias is not None: + method = "from_" + base.alias + else: + method = "from_" + base.__class__.__name__ + + _convert = getattr(self, method) + + if _convert is not None: + result = _convert(element, base) + + if result is not None: + return result + + raise CoercionFailed("Cannot convert %s of type %s from %s to %s" % (element, type(element), base, self)) + + def convert(self, element, base=None): + """Convert ``element`` to ``self.dtype``. """ + + if base is not None: + if _not_a_coeff(element): + raise CoercionFailed('%s is not in any domain' % element) + return self.convert_from(element, base) + + if self.of_type(element): + return element + + if _not_a_coeff(element): + raise CoercionFailed('%s is not in any domain' % element) + + from sympy.polys.domains import ZZ, QQ, RealField, ComplexField + + if ZZ.of_type(element): + return self.convert_from(element, ZZ) + + if isinstance(element, int): + return self.convert_from(ZZ(element), ZZ) + + if GROUND_TYPES != 'python': + if isinstance(element, ZZ.tp): + return self.convert_from(element, ZZ) + if isinstance(element, QQ.tp): + return self.convert_from(element, QQ) + + if isinstance(element, float): + parent = RealField() + return self.convert_from(parent(element), parent) + + if isinstance(element, complex): + parent = ComplexField() + return self.convert_from(parent(element), parent) + + if type(element).__name__ == 'mpf': + parent = RealField() + return self.convert_from(parent(element), parent) + + if type(element).__name__ == 'mpc': + parent = ComplexField() + return self.convert_from(parent(element), parent) + + if isinstance(element, DomainElement): + return self.convert_from(element, element.parent()) + + # TODO: implement this in from_ methods + if self.is_Numerical and getattr(element, 'is_ground', False): + return self.convert(element.LC()) + + if isinstance(element, Basic): + try: + return self.from_sympy(element) + except (TypeError, ValueError): + pass + else: # TODO: remove this branch + if not is_sequence(element): + try: + element = sympify(element, strict=True) + if isinstance(element, Basic): + return self.from_sympy(element) + except (TypeError, ValueError): + pass + + raise CoercionFailed("Cannot convert %s of type %s to %s" % (element, type(element), self)) + + def of_type(self, element): + """Check if ``a`` is of type ``dtype``. """ + return isinstance(element, self.tp) + + def __contains__(self, a): + """Check if ``a`` belongs to this domain. """ + try: + if _not_a_coeff(a): + raise CoercionFailed + self.convert(a) # this might raise, too + except CoercionFailed: + return False + + return True + + def to_sympy(self, a): + """Convert domain element *a* to a SymPy expression (Expr). + + Explanation + =========== + + Convert a :py:class:`~.Domain` element *a* to :py:class:`~.Expr`. Most + public SymPy functions work with objects of type :py:class:`~.Expr`. + The elements of a :py:class:`~.Domain` have a different internal + representation. It is not possible to mix domain elements with + :py:class:`~.Expr` so each domain has :py:meth:`~.Domain.to_sympy` and + :py:meth:`~.Domain.from_sympy` methods to convert its domain elements + to and from :py:class:`~.Expr`. + + Parameters + ========== + + a: domain element + An element of this :py:class:`~.Domain`. + + Returns + ======= + + expr: Expr + A normal SymPy expression of type :py:class:`~.Expr`. + + Examples + ======== + + Construct an element of the :ref:`QQ` domain and then convert it to + :py:class:`~.Expr`. + + >>> from sympy import QQ, Expr + >>> q_domain = QQ(2) + >>> q_domain + 2 + >>> q_expr = QQ.to_sympy(q_domain) + >>> q_expr + 2 + + Although the printed forms look similar these objects are not of the + same type. + + >>> isinstance(q_domain, Expr) + False + >>> isinstance(q_expr, Expr) + True + + Construct an element of :ref:`K[x]` and convert to + :py:class:`~.Expr`. + + >>> from sympy import Symbol + >>> x = Symbol('x') + >>> K = QQ[x] + >>> x_domain = K.gens[0] # generator x as a domain element + >>> p_domain = x_domain**2/3 + 1 + >>> p_domain + 1/3*x**2 + 1 + >>> p_expr = K.to_sympy(p_domain) + >>> p_expr + x**2/3 + 1 + + The :py:meth:`~.Domain.from_sympy` method is used for the opposite + conversion from a normal SymPy expression to a domain element. + + >>> p_domain == p_expr + False + >>> K.from_sympy(p_expr) == p_domain + True + >>> K.to_sympy(p_domain) == p_expr + True + >>> K.from_sympy(K.to_sympy(p_domain)) == p_domain + True + >>> K.to_sympy(K.from_sympy(p_expr)) == p_expr + True + + The :py:meth:`~.Domain.from_sympy` method makes it easier to construct + domain elements interactively. + + >>> from sympy import Symbol + >>> x = Symbol('x') + >>> K = QQ[x] + >>> K.from_sympy(x**2/3 + 1) + 1/3*x**2 + 1 + + See also + ======== + + from_sympy + convert_from + """ + raise NotImplementedError + + def from_sympy(self, a): + """Convert a SymPy expression to an element of this domain. + + Explanation + =========== + + See :py:meth:`~.Domain.to_sympy` for explanation and examples. + + Parameters + ========== + + expr: Expr + A normal SymPy expression of type :py:class:`~.Expr`. + + Returns + ======= + + a: domain element + An element of this :py:class:`~.Domain`. + + See also + ======== + + to_sympy + convert_from + """ + raise NotImplementedError + + def sum(self, args): + return sum(args, start=self.zero) + + def from_FF(K1, a, K0): + """Convert ``ModularInteger(int)`` to ``dtype``. """ + return None + + def from_FF_python(K1, a, K0): + """Convert ``ModularInteger(int)`` to ``dtype``. """ + return None + + def from_ZZ_python(K1, a, K0): + """Convert a Python ``int`` object to ``dtype``. """ + return None + + def from_QQ_python(K1, a, K0): + """Convert a Python ``Fraction`` object to ``dtype``. """ + return None + + def from_FF_gmpy(K1, a, K0): + """Convert ``ModularInteger(mpz)`` to ``dtype``. """ + return None + + def from_ZZ_gmpy(K1, a, K0): + """Convert a GMPY ``mpz`` object to ``dtype``. """ + return None + + def from_QQ_gmpy(K1, a, K0): + """Convert a GMPY ``mpq`` object to ``dtype``. """ + return None + + def from_RealField(K1, a, K0): + """Convert a real element object to ``dtype``. """ + return None + + def from_ComplexField(K1, a, K0): + """Convert a complex element to ``dtype``. """ + return None + + def from_AlgebraicField(K1, a, K0): + """Convert an algebraic number to ``dtype``. """ + return None + + def from_PolynomialRing(K1, a, K0): + """Convert a polynomial to ``dtype``. """ + if a.is_ground: + return K1.convert(a.LC, K0.dom) + + def from_FractionField(K1, a, K0): + """Convert a rational function to ``dtype``. """ + return None + + def from_MonogenicFiniteExtension(K1, a, K0): + """Convert an ``ExtensionElement`` to ``dtype``. """ + return K1.convert_from(a.rep, K0.ring) + + def from_ExpressionDomain(K1, a, K0): + """Convert a ``EX`` object to ``dtype``. """ + return K1.from_sympy(a.ex) + + def from_ExpressionRawDomain(K1, a, K0): + """Convert a ``EX`` object to ``dtype``. """ + return K1.from_sympy(a) + + def from_GlobalPolynomialRing(K1, a, K0): + """Convert a polynomial to ``dtype``. """ + if a.degree() <= 0: + return K1.convert(a.LC(), K0.dom) + + def from_GeneralizedPolynomialRing(K1, a, K0): + return K1.from_FractionField(a, K0) + + def unify_with_symbols(K0, K1, symbols): + if (K0.is_Composite and (set(K0.symbols) & set(symbols))) or (K1.is_Composite and (set(K1.symbols) & set(symbols))): + raise UnificationFailed("Cannot unify %s with %s, given %s generators" % (K0, K1, tuple(symbols))) + + return K0.unify(K1) + + def unify_composite(K0, K1): + """Unify two domains where at least one is composite.""" + K0_ground = K0.dom if K0.is_Composite else K0 + K1_ground = K1.dom if K1.is_Composite else K1 + + K0_symbols = K0.symbols if K0.is_Composite else () + K1_symbols = K1.symbols if K1.is_Composite else () + + domain = K0_ground.unify(K1_ground) + symbols = _unify_gens(K0_symbols, K1_symbols) + order = K0.order if K0.is_Composite else K1.order + + # E.g. ZZ[x].unify(QQ.frac_field(x)) -> ZZ.frac_field(x) + if ((K0.is_FractionField and K1.is_PolynomialRing or + K1.is_FractionField and K0.is_PolynomialRing) and + (not K0_ground.is_Field or not K1_ground.is_Field) and domain.is_Field + and domain.has_assoc_Ring): + domain = domain.get_ring() + + if K0.is_Composite and (not K1.is_Composite or K0.is_FractionField or K1.is_PolynomialRing): + cls = K0.__class__ + else: + cls = K1.__class__ + + # Here cls might be PolynomialRing, FractionField, GlobalPolynomialRing + # (dense/old Polynomialring) or dense/old FractionField. + + from sympy.polys.domains.old_polynomialring import GlobalPolynomialRing + if cls == GlobalPolynomialRing: + return cls(domain, symbols) + + return cls(domain, symbols, order) + + def unify(K0, K1, symbols=None): + """ + Construct a minimal domain that contains elements of ``K0`` and ``K1``. + + Known domains (from smallest to largest): + + - ``GF(p)`` + - ``ZZ`` + - ``QQ`` + - ``RR(prec, tol)`` + - ``CC(prec, tol)`` + - ``ALG(a, b, c)`` + - ``K[x, y, z]`` + - ``K(x, y, z)`` + - ``EX`` + + """ + if symbols is not None: + return K0.unify_with_symbols(K1, symbols) + + if K0 == K1: + return K0 + + if not (K0.has_CharacteristicZero and K1.has_CharacteristicZero): + # Reject unification of domains with different characteristics. + if K0.characteristic() != K1.characteristic(): + raise UnificationFailed("Cannot unify %s with %s" % (K0, K1)) + + # We do not get here if K0 == K1. The two domains have the same + # characteristic but are unequal so at least one is composite and + # we are unifying something like GF(3).unify(GF(3)[x]). + return K0.unify_composite(K1) + + # From here we know both domains have characteristic zero and it can be + # acceptable to fall back on EX. + + if K0.is_EXRAW: + return K0 + if K1.is_EXRAW: + return K1 + + if K0.is_EX: + return K0 + if K1.is_EX: + return K1 + + if K0.is_FiniteExtension or K1.is_FiniteExtension: + if K1.is_FiniteExtension: + K0, K1 = K1, K0 + if K1.is_FiniteExtension: + # Unifying two extensions. + # Try to ensure that K0.unify(K1) == K1.unify(K0) + if list(ordered([K0.modulus, K1.modulus]))[1] == K0.modulus: + K0, K1 = K1, K0 + return K1.set_domain(K0) + else: + # Drop the generator from other and unify with the base domain + K1 = K1.drop(K0.symbol) + K1 = K0.domain.unify(K1) + return K0.set_domain(K1) + + if K0.is_Composite or K1.is_Composite: + return K0.unify_composite(K1) + + if K1.is_ComplexField: + K0, K1 = K1, K0 + if K0.is_ComplexField: + if K1.is_ComplexField or K1.is_RealField: + if K0.precision >= K1.precision: + return K0 + else: + from sympy.polys.domains.complexfield import ComplexField + return ComplexField(prec=K1.precision) + else: + return K0 + + if K1.is_RealField: + K0, K1 = K1, K0 + if K0.is_RealField: + if K1.is_RealField: + if K0.precision >= K1.precision: + return K0 + else: + return K1 + elif K1.is_GaussianRing or K1.is_GaussianField: + from sympy.polys.domains.complexfield import ComplexField + return ComplexField(prec=K0.precision) + else: + return K0 + + if K1.is_AlgebraicField: + K0, K1 = K1, K0 + if K0.is_AlgebraicField: + if K1.is_GaussianRing: + K1 = K1.get_field() + if K1.is_GaussianField: + K1 = K1.as_AlgebraicField() + if K1.is_AlgebraicField: + return K0.__class__(K0.dom.unify(K1.dom), *_unify_gens(K0.orig_ext, K1.orig_ext)) + else: + return K0 + + if K0.is_GaussianField: + return K0 + if K1.is_GaussianField: + return K1 + + if K0.is_GaussianRing: + if K1.is_RationalField: + K0 = K0.get_field() + return K0 + if K1.is_GaussianRing: + if K0.is_RationalField: + K1 = K1.get_field() + return K1 + + if K0.is_RationalField: + return K0 + if K1.is_RationalField: + return K1 + + if K0.is_IntegerRing: + return K0 + if K1.is_IntegerRing: + return K1 + + from sympy.polys.domains import EX + return EX + + def __eq__(self, other): + """Returns ``True`` if two domains are equivalent. """ + # XXX: Remove this. + return isinstance(other, Domain) and self.dtype == other.dtype + + def __ne__(self, other): + """Returns ``False`` if two domains are equivalent. """ + return not self == other + + def map(self, seq): + """Rersively apply ``self`` to all elements of ``seq``. """ + result = [] + + for elt in seq: + if isinstance(elt, list): + result.append(self.map(elt)) + else: + result.append(self(elt)) + + return result + + def get_ring(self): + """Returns a ring associated with ``self``. """ + raise DomainError('there is no ring associated with %s' % self) + + def get_field(self): + """Returns a field associated with ``self``. """ + raise DomainError('there is no field associated with %s' % self) + + def get_exact(self): + """Returns an exact domain associated with ``self``. """ + return self + + def __getitem__(self, symbols): + """The mathematical way to make a polynomial ring. """ + if hasattr(symbols, '__iter__'): + return self.poly_ring(*symbols) + else: + return self.poly_ring(symbols) + + def poly_ring(self, *symbols, order=lex): + """Returns a polynomial ring, i.e. `K[X]`. """ + from sympy.polys.domains.polynomialring import PolynomialRing + return PolynomialRing(self, symbols, order) + + def frac_field(self, *symbols, order=lex): + """Returns a fraction field, i.e. `K(X)`. """ + from sympy.polys.domains.fractionfield import FractionField + return FractionField(self, symbols, order) + + def old_poly_ring(self, *symbols, **kwargs): + """Returns a polynomial ring, i.e. `K[X]`. """ + from sympy.polys.domains.old_polynomialring import PolynomialRing + return PolynomialRing(self, *symbols, **kwargs) + + def old_frac_field(self, *symbols, **kwargs): + """Returns a fraction field, i.e. `K(X)`. """ + from sympy.polys.domains.old_fractionfield import FractionField + return FractionField(self, *symbols, **kwargs) + + def algebraic_field(self, *extension, alias=None): + r"""Returns an algebraic field, i.e. `K(\alpha, \ldots)`. """ + raise DomainError("Cannot create algebraic field over %s" % self) + + def alg_field_from_poly(self, poly, alias=None, root_index=-1): + r""" + Convenience method to construct an algebraic extension on a root of a + polynomial, chosen by root index. + + Parameters + ========== + + poly : :py:class:`~.Poly` + The polynomial whose root generates the extension. + alias : str, optional (default=None) + Symbol name for the generator of the extension. + E.g. "alpha" or "theta". + root_index : int, optional (default=-1) + Specifies which root of the polynomial is desired. The ordering is + as defined by the :py:class:`~.ComplexRootOf` class. The default of + ``-1`` selects the most natural choice in the common cases of + quadratic and cyclotomic fields (the square root on the positive + real or imaginary axis, resp. $\mathrm{e}^{2\pi i/n}$). + + Examples + ======== + + >>> from sympy import QQ, Poly + >>> from sympy.abc import x + >>> f = Poly(x**2 - 2) + >>> K = QQ.alg_field_from_poly(f) + >>> K.ext.minpoly == f + True + >>> g = Poly(8*x**3 - 6*x - 1) + >>> L = QQ.alg_field_from_poly(g, "alpha") + >>> L.ext.minpoly == g + True + >>> L.to_sympy(L([1, 1, 1])) + alpha**2 + alpha + 1 + + """ + from sympy.polys.rootoftools import CRootOf + root = CRootOf(poly, root_index) + alpha = AlgebraicNumber(root, alias=alias) + return self.algebraic_field(alpha, alias=alias) + + def cyclotomic_field(self, n, ss=False, alias="zeta", gen=None, root_index=-1): + r""" + Convenience method to construct a cyclotomic field. + + Parameters + ========== + + n : int + Construct the nth cyclotomic field. + ss : boolean, optional (default=False) + If True, append *n* as a subscript on the alias string. + alias : str, optional (default="zeta") + Symbol name for the generator. + gen : :py:class:`~.Symbol`, optional (default=None) + Desired variable for the cyclotomic polynomial that defines the + field. If ``None``, a dummy variable will be used. + root_index : int, optional (default=-1) + Specifies which root of the polynomial is desired. The ordering is + as defined by the :py:class:`~.ComplexRootOf` class. The default of + ``-1`` selects the root $\mathrm{e}^{2\pi i/n}$. + + Examples + ======== + + >>> from sympy import QQ, latex + >>> K = QQ.cyclotomic_field(5) + >>> K.to_sympy(K([-1, 1])) + 1 - zeta + >>> L = QQ.cyclotomic_field(7, True) + >>> a = L.to_sympy(L([-1, 1])) + >>> print(a) + 1 - zeta7 + >>> print(latex(a)) + 1 - \zeta_{7} + + """ + from sympy.polys.specialpolys import cyclotomic_poly + if ss: + alias += str(n) + return self.alg_field_from_poly(cyclotomic_poly(n, gen), alias=alias, + root_index=root_index) + + def inject(self, *symbols): + """Inject generators into this domain. """ + raise NotImplementedError + + def drop(self, *symbols): + """Drop generators from this domain. """ + if self.is_Simple: + return self + raise NotImplementedError # pragma: no cover + + def is_zero(self, a): + """Returns True if ``a`` is zero. """ + return not a + + def is_one(self, a): + """Returns True if ``a`` is one. """ + return a == self.one + + def is_positive(self, a): + """Returns True if ``a`` is positive. """ + return a > 0 + + def is_negative(self, a): + """Returns True if ``a`` is negative. """ + return a < 0 + + def is_nonpositive(self, a): + """Returns True if ``a`` is non-positive. """ + return a <= 0 + + def is_nonnegative(self, a): + """Returns True if ``a`` is non-negative. """ + return a >= 0 + + def canonical_unit(self, a): + if self.is_negative(a): + return -self.one + else: + return self.one + + def abs(self, a): + """Absolute value of ``a``, implies ``__abs__``. """ + return abs(a) + + def neg(self, a): + """Returns ``a`` negated, implies ``__neg__``. """ + return -a + + def pos(self, a): + """Returns ``a`` positive, implies ``__pos__``. """ + return +a + + def add(self, a, b): + """Sum of ``a`` and ``b``, implies ``__add__``. """ + return a + b + + def sub(self, a, b): + """Difference of ``a`` and ``b``, implies ``__sub__``. """ + return a - b + + def mul(self, a, b): + """Product of ``a`` and ``b``, implies ``__mul__``. """ + return a * b + + def pow(self, a, b): + """Raise ``a`` to power ``b``, implies ``__pow__``. """ + return a ** b + + def exquo(self, a, b): + """Exact quotient of *a* and *b*. Analogue of ``a / b``. + + Explanation + =========== + + This is essentially the same as ``a / b`` except that an error will be + raised if the division is inexact (if there is any remainder) and the + result will always be a domain element. When working in a + :py:class:`~.Domain` that is not a :py:class:`~.Field` (e.g. :ref:`ZZ` + or :ref:`K[x]`) ``exquo`` should be used instead of ``/``. + + The key invariant is that if ``q = K.exquo(a, b)`` (and ``exquo`` does + not raise an exception) then ``a == b*q``. + + Examples + ======== + + We can use ``K.exquo`` instead of ``/`` for exact division. + + >>> from sympy import ZZ + >>> ZZ.exquo(ZZ(4), ZZ(2)) + 2 + >>> ZZ.exquo(ZZ(5), ZZ(2)) + Traceback (most recent call last): + ... + ExactQuotientFailed: 2 does not divide 5 in ZZ + + Over a :py:class:`~.Field` such as :ref:`QQ`, division (with nonzero + divisor) is always exact so in that case ``/`` can be used instead of + :py:meth:`~.Domain.exquo`. + + >>> from sympy import QQ + >>> QQ.exquo(QQ(5), QQ(2)) + 5/2 + >>> QQ(5) / QQ(2) + 5/2 + + Parameters + ========== + + a: domain element + The dividend + b: domain element + The divisor + + Returns + ======= + + q: domain element + The exact quotient + + Raises + ====== + + ExactQuotientFailed: if exact division is not possible. + ZeroDivisionError: when the divisor is zero. + + See also + ======== + + quo: Analogue of ``a // b`` + rem: Analogue of ``a % b`` + div: Analogue of ``divmod(a, b)`` + + Notes + ===== + + Since the default :py:attr:`~.Domain.dtype` for :ref:`ZZ` is ``int`` + (or ``mpz``) division as ``a / b`` should not be used as it would give + a ``float`` which is not a domain element. + + >>> ZZ(4) / ZZ(2) # doctest: +SKIP + 2.0 + >>> ZZ(5) / ZZ(2) # doctest: +SKIP + 2.5 + + On the other hand with `SYMPY_GROUND_TYPES=flint` elements of :ref:`ZZ` + are ``flint.fmpz`` and division would raise an exception: + + >>> ZZ(4) / ZZ(2) # doctest: +SKIP + Traceback (most recent call last): + ... + TypeError: unsupported operand type(s) for /: 'fmpz' and 'fmpz' + + Using ``/`` with :ref:`ZZ` will lead to incorrect results so + :py:meth:`~.Domain.exquo` should be used instead. + + """ + raise NotImplementedError + + def quo(self, a, b): + """Quotient of *a* and *b*. Analogue of ``a // b``. + + ``K.quo(a, b)`` is equivalent to ``K.div(a, b)[0]``. See + :py:meth:`~.Domain.div` for more explanation. + + See also + ======== + + rem: Analogue of ``a % b`` + div: Analogue of ``divmod(a, b)`` + exquo: Analogue of ``a / b`` + """ + raise NotImplementedError + + def rem(self, a, b): + """Modulo division of *a* and *b*. Analogue of ``a % b``. + + ``K.rem(a, b)`` is equivalent to ``K.div(a, b)[1]``. See + :py:meth:`~.Domain.div` for more explanation. + + See also + ======== + + quo: Analogue of ``a // b`` + div: Analogue of ``divmod(a, b)`` + exquo: Analogue of ``a / b`` + """ + raise NotImplementedError + + def div(self, a, b): + """Quotient and remainder for *a* and *b*. Analogue of ``divmod(a, b)`` + + Explanation + =========== + + This is essentially the same as ``divmod(a, b)`` except that is more + consistent when working over some :py:class:`~.Field` domains such as + :ref:`QQ`. When working over an arbitrary :py:class:`~.Domain` the + :py:meth:`~.Domain.div` method should be used instead of ``divmod``. + + The key invariant is that if ``q, r = K.div(a, b)`` then + ``a == b*q + r``. + + The result of ``K.div(a, b)`` is the same as the tuple + ``(K.quo(a, b), K.rem(a, b))`` except that if both quotient and + remainder are needed then it is more efficient to use + :py:meth:`~.Domain.div`. + + Examples + ======== + + We can use ``K.div`` instead of ``divmod`` for floor division and + remainder. + + >>> from sympy import ZZ, QQ + >>> ZZ.div(ZZ(5), ZZ(2)) + (2, 1) + + If ``K`` is a :py:class:`~.Field` then the division is always exact + with a remainder of :py:attr:`~.Domain.zero`. + + >>> QQ.div(QQ(5), QQ(2)) + (5/2, 0) + + Parameters + ========== + + a: domain element + The dividend + b: domain element + The divisor + + Returns + ======= + + (q, r): tuple of domain elements + The quotient and remainder + + Raises + ====== + + ZeroDivisionError: when the divisor is zero. + + See also + ======== + + quo: Analogue of ``a // b`` + rem: Analogue of ``a % b`` + exquo: Analogue of ``a / b`` + + Notes + ===== + + If ``gmpy`` is installed then the ``gmpy.mpq`` type will be used as + the :py:attr:`~.Domain.dtype` for :ref:`QQ`. The ``gmpy.mpq`` type + defines ``divmod`` in a way that is undesirable so + :py:meth:`~.Domain.div` should be used instead of ``divmod``. + + >>> a = QQ(1) + >>> b = QQ(3, 2) + >>> a # doctest: +SKIP + mpq(1,1) + >>> b # doctest: +SKIP + mpq(3,2) + >>> divmod(a, b) # doctest: +SKIP + (mpz(0), mpq(1,1)) + >>> QQ.div(a, b) # doctest: +SKIP + (mpq(2,3), mpq(0,1)) + + Using ``//`` or ``%`` with :ref:`QQ` will lead to incorrect results so + :py:meth:`~.Domain.div` should be used instead. + + """ + raise NotImplementedError + + def invert(self, a, b): + """Returns inversion of ``a mod b``, implies something. """ + raise NotImplementedError + + def revert(self, a): + """Returns ``a**(-1)`` if possible. """ + raise NotImplementedError + + def numer(self, a): + """Returns numerator of ``a``. """ + raise NotImplementedError + + def denom(self, a): + """Returns denominator of ``a``. """ + raise NotImplementedError + + def half_gcdex(self, a, b): + """Half extended GCD of ``a`` and ``b``. """ + s, t, h = self.gcdex(a, b) + return s, h + + def gcdex(self, a, b): + """Extended GCD of ``a`` and ``b``. """ + raise NotImplementedError + + def cofactors(self, a, b): + """Returns GCD and cofactors of ``a`` and ``b``. """ + gcd = self.gcd(a, b) + cfa = self.quo(a, gcd) + cfb = self.quo(b, gcd) + return gcd, cfa, cfb + + def gcd(self, a, b): + """Returns GCD of ``a`` and ``b``. """ + raise NotImplementedError + + def lcm(self, a, b): + """Returns LCM of ``a`` and ``b``. """ + raise NotImplementedError + + def log(self, a, b): + """Returns b-base logarithm of ``a``. """ + raise NotImplementedError + + def sqrt(self, a): + """Returns a (possibly inexact) square root of ``a``. + + Explanation + =========== + There is no universal definition of "inexact square root" for all + domains. It is not recommended to implement this method for domains + other then :ref:`ZZ`. + + See also + ======== + exsqrt + """ + raise NotImplementedError + + def is_square(self, a): + """Returns whether ``a`` is a square in the domain. + + Explanation + =========== + Returns ``True`` if there is an element ``b`` in the domain such that + ``b * b == a``, otherwise returns ``False``. For inexact domains like + :ref:`RR` and :ref:`CC`, a tiny difference in this equality can be + tolerated. + + See also + ======== + exsqrt + """ + raise NotImplementedError + + def exsqrt(self, a): + """Principal square root of a within the domain if ``a`` is square. + + Explanation + =========== + The implementation of this method should return an element ``b`` in the + domain such that ``b * b == a``, or ``None`` if there is no such ``b``. + For inexact domains like :ref:`RR` and :ref:`CC`, a tiny difference in + this equality can be tolerated. The choice of a "principal" square root + should follow a consistent rule whenever possible. + + See also + ======== + sqrt, is_square + """ + raise NotImplementedError + + def evalf(self, a, prec=None, **options): + """Returns numerical approximation of ``a``. """ + return self.to_sympy(a).evalf(prec, **options) + + n = evalf + + def real(self, a): + return a + + def imag(self, a): + return self.zero + + def almosteq(self, a, b, tolerance=None): + """Check if ``a`` and ``b`` are almost equal. """ + return a == b + + def characteristic(self): + """Return the characteristic of this domain. """ + raise NotImplementedError('characteristic()') + + +__all__ = ['Domain'] diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/domainelement.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/domainelement.py new file mode 100644 index 0000000000000000000000000000000000000000..b1033e86a7edcbffa633efd65ca7ced48f3b1f1a --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/domainelement.py @@ -0,0 +1,38 @@ +"""Trait for implementing domain elements. """ + + +from sympy.utilities import public + +@public +class DomainElement: + """ + Represents an element of a domain. + + Mix in this trait into a class whose instances should be recognized as + elements of a domain. Method ``parent()`` gives that domain. + """ + + __slots__ = () + + def parent(self): + """Get the domain associated with ``self`` + + Examples + ======== + + >>> from sympy import ZZ, symbols + >>> x, y = symbols('x, y') + >>> K = ZZ[x,y] + >>> p = K(x)**2 + K(y)**2 + >>> p + x**2 + y**2 + >>> p.parent() + ZZ[x,y] + + Notes + ===== + + This is used by :py:meth:`~.Domain.convert` to identify the domain + associated with a domain element. + """ + raise NotImplementedError("abstract method") diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/expressiondomain.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/expressiondomain.py new file mode 100644 index 0000000000000000000000000000000000000000..26cd5aa5bf34985f885093be227df6aa9b35d36c --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/expressiondomain.py @@ -0,0 +1,278 @@ +"""Implementation of :class:`ExpressionDomain` class. """ + + +from sympy.core import sympify, SympifyError +from sympy.polys.domains.domainelement import DomainElement +from sympy.polys.domains.characteristiczero import CharacteristicZero +from sympy.polys.domains.field import Field +from sympy.polys.domains.simpledomain import SimpleDomain +from sympy.polys.polyutils import PicklableWithSlots +from sympy.utilities import public + +eflags = {"deep": False, "mul": True, "power_exp": False, "power_base": False, + "basic": False, "multinomial": False, "log": False} + +@public +class ExpressionDomain(Field, CharacteristicZero, SimpleDomain): + """A class for arbitrary expressions. """ + + is_SymbolicDomain = is_EX = True + + class Expression(DomainElement, PicklableWithSlots): + """An arbitrary expression. """ + + __slots__ = ('ex',) + + def __init__(self, ex): + if not isinstance(ex, self.__class__): + self.ex = sympify(ex) + else: + self.ex = ex.ex + + def __repr__(f): + return 'EX(%s)' % repr(f.ex) + + def __str__(f): + return 'EX(%s)' % str(f.ex) + + def __hash__(self): + return hash((self.__class__.__name__, self.ex)) + + def parent(self): + return EX + + def as_expr(f): + return f.ex + + def numer(f): + return f.__class__(f.ex.as_numer_denom()[0]) + + def denom(f): + return f.__class__(f.ex.as_numer_denom()[1]) + + def simplify(f, ex): + return f.__class__(ex.cancel().expand(**eflags)) + + def __abs__(f): + return f.__class__(abs(f.ex)) + + def __neg__(f): + return f.__class__(-f.ex) + + def _to_ex(f, g): + try: + return f.__class__(g) + except SympifyError: + return None + + def __lt__(f, g): + return f.ex.sort_key() < g.ex.sort_key() + + def __add__(f, g): + g = f._to_ex(g) + + if g is None: + return NotImplemented + elif g == EX.zero: + return f + elif f == EX.zero: + return g + else: + return f.simplify(f.ex + g.ex) + + def __radd__(f, g): + return f.simplify(f.__class__(g).ex + f.ex) + + def __sub__(f, g): + g = f._to_ex(g) + + if g is None: + return NotImplemented + elif g == EX.zero: + return f + elif f == EX.zero: + return -g + else: + return f.simplify(f.ex - g.ex) + + def __rsub__(f, g): + return f.simplify(f.__class__(g).ex - f.ex) + + def __mul__(f, g): + g = f._to_ex(g) + + if g is None: + return NotImplemented + + if EX.zero in (f, g): + return EX.zero + elif f.ex.is_Number and g.ex.is_Number: + return f.__class__(f.ex*g.ex) + + return f.simplify(f.ex*g.ex) + + def __rmul__(f, g): + return f.simplify(f.__class__(g).ex*f.ex) + + def __pow__(f, n): + n = f._to_ex(n) + + if n is not None: + return f.simplify(f.ex**n.ex) + else: + return NotImplemented + + def __truediv__(f, g): + g = f._to_ex(g) + + if g is not None: + return f.simplify(f.ex/g.ex) + else: + return NotImplemented + + def __rtruediv__(f, g): + return f.simplify(f.__class__(g).ex/f.ex) + + def __eq__(f, g): + return f.ex == f.__class__(g).ex + + def __ne__(f, g): + return not f == g + + def __bool__(f): + return not f.ex.is_zero + + def gcd(f, g): + from sympy.polys import gcd + return f.__class__(gcd(f.ex, f.__class__(g).ex)) + + def lcm(f, g): + from sympy.polys import lcm + return f.__class__(lcm(f.ex, f.__class__(g).ex)) + + dtype = Expression + + zero = Expression(0) + one = Expression(1) + + rep = 'EX' + + has_assoc_Ring = False + has_assoc_Field = True + + def __init__(self): + pass + + def __eq__(self, other): + if isinstance(other, ExpressionDomain): + return True + else: + return NotImplemented + + def __hash__(self): + return hash("EX") + + def to_sympy(self, a): + """Convert ``a`` to a SymPy object. """ + return a.as_expr() + + def from_sympy(self, a): + """Convert SymPy's expression to ``dtype``. """ + return self.dtype(a) + + def from_ZZ(K1, a, K0): + """Convert a Python ``int`` object to ``dtype``. """ + return K1(K0.to_sympy(a)) + + def from_ZZ_python(K1, a, K0): + """Convert a Python ``int`` object to ``dtype``. """ + return K1(K0.to_sympy(a)) + + def from_QQ(K1, a, K0): + """Convert a Python ``Fraction`` object to ``dtype``. """ + return K1(K0.to_sympy(a)) + + def from_QQ_python(K1, a, K0): + """Convert a Python ``Fraction`` object to ``dtype``. """ + return K1(K0.to_sympy(a)) + + def from_ZZ_gmpy(K1, a, K0): + """Convert a GMPY ``mpz`` object to ``dtype``. """ + return K1(K0.to_sympy(a)) + + def from_QQ_gmpy(K1, a, K0): + """Convert a GMPY ``mpq`` object to ``dtype``. """ + return K1(K0.to_sympy(a)) + + def from_GaussianIntegerRing(K1, a, K0): + """Convert a ``GaussianRational`` object to ``dtype``. """ + return K1(K0.to_sympy(a)) + + def from_GaussianRationalField(K1, a, K0): + """Convert a ``GaussianRational`` object to ``dtype``. """ + return K1(K0.to_sympy(a)) + + def from_AlgebraicField(K1, a, K0): + """Convert an ``ANP`` object to ``dtype``. """ + return K1(K0.to_sympy(a)) + + def from_RealField(K1, a, K0): + """Convert a mpmath ``mpf`` object to ``dtype``. """ + return K1(K0.to_sympy(a)) + + def from_ComplexField(K1, a, K0): + """Convert a mpmath ``mpc`` object to ``dtype``. """ + return K1(K0.to_sympy(a)) + + def from_PolynomialRing(K1, a, K0): + """Convert a ``DMP`` object to ``dtype``. """ + return K1(K0.to_sympy(a)) + + def from_FractionField(K1, a, K0): + """Convert a ``DMF`` object to ``dtype``. """ + return K1(K0.to_sympy(a)) + + def from_ExpressionDomain(K1, a, K0): + """Convert a ``EX`` object to ``dtype``. """ + return a + + def get_ring(self): + """Returns a ring associated with ``self``. """ + return self # XXX: EX is not a ring but we don't have much choice here. + + def get_field(self): + """Returns a field associated with ``self``. """ + return self + + def is_positive(self, a): + """Returns True if ``a`` is positive. """ + return a.ex.as_coeff_mul()[0].is_positive + + def is_negative(self, a): + """Returns True if ``a`` is negative. """ + return a.ex.could_extract_minus_sign() + + def is_nonpositive(self, a): + """Returns True if ``a`` is non-positive. """ + return a.ex.as_coeff_mul()[0].is_nonpositive + + def is_nonnegative(self, a): + """Returns True if ``a`` is non-negative. """ + return a.ex.as_coeff_mul()[0].is_nonnegative + + def numer(self, a): + """Returns numerator of ``a``. """ + return a.numer() + + def denom(self, a): + """Returns denominator of ``a``. """ + return a.denom() + + def gcd(self, a, b): + return self(1) + + def lcm(self, a, b): + return a.lcm(b) + + +EX = ExpressionDomain() diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/expressionrawdomain.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/expressionrawdomain.py new file mode 100644 index 0000000000000000000000000000000000000000..9811ca26c965197a13f56ab8266ad744e4571560 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/expressionrawdomain.py @@ -0,0 +1,57 @@ +"""Implementation of :class:`ExpressionRawDomain` class. """ + + +from sympy.core import Expr, S, sympify, Add +from sympy.polys.domains.characteristiczero import CharacteristicZero +from sympy.polys.domains.field import Field +from sympy.polys.domains.simpledomain import SimpleDomain +from sympy.polys.polyerrors import CoercionFailed +from sympy.utilities import public + + +@public +class ExpressionRawDomain(Field, CharacteristicZero, SimpleDomain): + """A class for arbitrary expressions but without automatic simplification. """ + + is_SymbolicRawDomain = is_EXRAW = True + + dtype = Expr + + zero = S.Zero + one = S.One + + rep = 'EXRAW' + + has_assoc_Ring = False + has_assoc_Field = True + + def __init__(self): + pass + + @classmethod + def new(self, a): + return sympify(a) + + def to_sympy(self, a): + """Convert ``a`` to a SymPy object. """ + return a + + def from_sympy(self, a): + """Convert SymPy's expression to ``dtype``. """ + if not isinstance(a, Expr): + raise CoercionFailed(f"Expecting an Expr instance but found: {type(a).__name__}") + return a + + def convert_from(self, a, K): + """Convert a domain element from another domain to EXRAW""" + return K.to_sympy(a) + + def get_field(self): + """Returns a field associated with ``self``. """ + return self + + def sum(self, items): + return Add(*items) + + +EXRAW = ExpressionRawDomain() diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/field.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/field.py new file mode 100644 index 0000000000000000000000000000000000000000..a6370294365a38dee1b2eda9942a66aeef8fdae9 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/field.py @@ -0,0 +1,118 @@ +"""Implementation of :class:`Field` class. """ + + +from sympy.polys.domains.ring import Ring +from sympy.polys.polyerrors import NotReversible, DomainError +from sympy.utilities import public + +@public +class Field(Ring): + """Represents a field domain. """ + + is_Field = True + is_PID = True + + def get_ring(self): + """Returns a ring associated with ``self``. """ + raise DomainError('there is no ring associated with %s' % self) + + def get_field(self): + """Returns a field associated with ``self``. """ + return self + + def exquo(self, a, b): + """Exact quotient of ``a`` and ``b``, implies ``__truediv__``. """ + return a / b + + def quo(self, a, b): + """Quotient of ``a`` and ``b``, implies ``__truediv__``. """ + return a / b + + def rem(self, a, b): + """Remainder of ``a`` and ``b``, implies nothing. """ + return self.zero + + def div(self, a, b): + """Division of ``a`` and ``b``, implies ``__truediv__``. """ + return a / b, self.zero + + def gcd(self, a, b): + """ + Returns GCD of ``a`` and ``b``. + + This definition of GCD over fields allows to clear denominators + in `primitive()`. + + Examples + ======== + + >>> from sympy.polys.domains import QQ + >>> from sympy import S, gcd, primitive + >>> from sympy.abc import x + + >>> QQ.gcd(QQ(2, 3), QQ(4, 9)) + 2/9 + >>> gcd(S(2)/3, S(4)/9) + 2/9 + >>> primitive(2*x/3 + S(4)/9) + (2/9, 3*x + 2) + + """ + try: + ring = self.get_ring() + except DomainError: + return self.one + + p = ring.gcd(self.numer(a), self.numer(b)) + q = ring.lcm(self.denom(a), self.denom(b)) + + return self.convert(p, ring)/q + + def gcdex(self, a, b): + """ + Returns x, y, g such that a * x + b * y == g == gcd(a, b) + """ + d = self.gcd(a, b) + + if a == self.zero: + if b == self.zero: + return self.zero, self.one, self.zero + else: + return self.zero, d/b, d + else: + return d/a, self.zero, d + + def lcm(self, a, b): + """ + Returns LCM of ``a`` and ``b``. + + >>> from sympy.polys.domains import QQ + >>> from sympy import S, lcm + + >>> QQ.lcm(QQ(2, 3), QQ(4, 9)) + 4/3 + >>> lcm(S(2)/3, S(4)/9) + 4/3 + + """ + + try: + ring = self.get_ring() + except DomainError: + return a*b + + p = ring.lcm(self.numer(a), self.numer(b)) + q = ring.gcd(self.denom(a), self.denom(b)) + + return self.convert(p, ring)/q + + def revert(self, a): + """Returns ``a**(-1)`` if possible. """ + if a: + return 1/a + else: + raise NotReversible('zero is not reversible') + + def is_unit(self, a): + """Return true if ``a`` is a invertible""" + return bool(a) diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/finitefield.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/finitefield.py new file mode 100644 index 0000000000000000000000000000000000000000..d3c48ac07f63aefb9a58c83bb95c5261e67e6a9e --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/finitefield.py @@ -0,0 +1,368 @@ +"""Implementation of :class:`FiniteField` class. """ + +import operator + +from sympy.external.gmpy import GROUND_TYPES +from sympy.utilities.decorator import doctest_depends_on + +from sympy.core.numbers import int_valued +from sympy.polys.domains.field import Field + +from sympy.polys.domains.modularinteger import ModularIntegerFactory +from sympy.polys.domains.simpledomain import SimpleDomain +from sympy.polys.galoistools import gf_zassenhaus, gf_irred_p_rabin +from sympy.polys.polyerrors import CoercionFailed +from sympy.utilities import public +from sympy.polys.domains.groundtypes import SymPyInteger + + +if GROUND_TYPES == 'flint': + __doctest_skip__ = ['FiniteField'] + + +if GROUND_TYPES == 'flint': + import flint + # Don't use python-flint < 0.5.0 because nmod was missing some features in + # previous versions of python-flint and fmpz_mod was not yet added. + _major, _minor, *_ = flint.__version__.split('.') + if (int(_major), int(_minor)) < (0, 5): + flint = None +else: + flint = None + + +def _modular_int_factory_nmod(mod): + # nmod only recognises int + index = operator.index + mod = index(mod) + nmod = flint.nmod + nmod_poly = flint.nmod_poly + + # flint's nmod is only for moduli up to 2^64-1 (on a 64-bit machine) + try: + nmod(0, mod) + except OverflowError: + return None, None + + def ctx(x): + try: + return nmod(x, mod) + except TypeError: + return nmod(index(x), mod) + + def poly_ctx(cs): + return nmod_poly(cs, mod) + + return ctx, poly_ctx + + +def _modular_int_factory_fmpz_mod(mod): + index = operator.index + fctx = flint.fmpz_mod_ctx(mod) + fctx_poly = flint.fmpz_mod_poly_ctx(mod) + fmpz_mod_poly = flint.fmpz_mod_poly + + def ctx(x): + try: + return fctx(x) + except TypeError: + # x might be Integer + return fctx(index(x)) + + def poly_ctx(cs): + return fmpz_mod_poly(cs, fctx_poly) + + return ctx, poly_ctx + + +def _modular_int_factory(mod, dom, symmetric, self): + # Convert the modulus to ZZ + try: + mod = dom.convert(mod) + except CoercionFailed: + raise ValueError('modulus must be an integer, got %s' % mod) + + ctx, poly_ctx, is_flint = None, None, False + + # Don't use flint if the modulus is not prime as it often crashes. + if flint is not None and mod.is_prime(): + + is_flint = True + + # Try to use flint's nmod first + ctx, poly_ctx = _modular_int_factory_nmod(mod) + + if ctx is None: + # Use fmpz_mod for larger moduli + ctx, poly_ctx = _modular_int_factory_fmpz_mod(mod) + + if ctx is None: + # Use the Python implementation if flint is not available or the + # modulus is not prime. + ctx = ModularIntegerFactory(mod, dom, symmetric, self) + poly_ctx = None # not used + + return ctx, poly_ctx, is_flint + + +@public +@doctest_depends_on(modules=['python', 'gmpy']) +class FiniteField(Field, SimpleDomain): + r"""Finite field of prime order :ref:`GF(p)` + + A :ref:`GF(p)` domain represents a `finite field`_ `\mathbb{F}_p` of prime + order as :py:class:`~.Domain` in the domain system (see + :ref:`polys-domainsintro`). + + A :py:class:`~.Poly` created from an expression with integer + coefficients will have the domain :ref:`ZZ`. However, if the ``modulus=p`` + option is given then the domain will be a finite field instead. + + >>> from sympy import Poly, Symbol + >>> x = Symbol('x') + >>> p = Poly(x**2 + 1) + >>> p + Poly(x**2 + 1, x, domain='ZZ') + >>> p.domain + ZZ + >>> p2 = Poly(x**2 + 1, modulus=2) + >>> p2 + Poly(x**2 + 1, x, modulus=2) + >>> p2.domain + GF(2) + + It is possible to factorise a polynomial over :ref:`GF(p)` using the + modulus argument to :py:func:`~.factor` or by specifying the domain + explicitly. The domain can also be given as a string. + + >>> from sympy import factor, GF + >>> factor(x**2 + 1) + x**2 + 1 + >>> factor(x**2 + 1, modulus=2) + (x + 1)**2 + >>> factor(x**2 + 1, domain=GF(2)) + (x + 1)**2 + >>> factor(x**2 + 1, domain='GF(2)') + (x + 1)**2 + + It is also possible to use :ref:`GF(p)` with the :py:func:`~.cancel` + and :py:func:`~.gcd` functions. + + >>> from sympy import cancel, gcd + >>> cancel((x**2 + 1)/(x + 1)) + (x**2 + 1)/(x + 1) + >>> cancel((x**2 + 1)/(x + 1), domain=GF(2)) + x + 1 + >>> gcd(x**2 + 1, x + 1) + 1 + >>> gcd(x**2 + 1, x + 1, domain=GF(2)) + x + 1 + + When using the domain directly :ref:`GF(p)` can be used as a constructor + to create instances which then support the operations ``+,-,*,**,/`` + + >>> from sympy import GF + >>> K = GF(5) + >>> K + GF(5) + >>> x = K(3) + >>> y = K(2) + >>> x + 3 mod 5 + >>> y + 2 mod 5 + >>> x * y + 1 mod 5 + >>> x / y + 4 mod 5 + + Notes + ===== + + It is also possible to create a :ref:`GF(p)` domain of **non-prime** + order but the resulting ring is **not** a field: it is just the ring of + the integers modulo ``n``. + + >>> K = GF(9) + >>> z = K(3) + >>> z + 3 mod 9 + >>> z**2 + 0 mod 9 + + It would be good to have a proper implementation of prime power fields + (``GF(p**n)``) but these are not yet implemented in SymPY. + + .. _finite field: https://en.wikipedia.org/wiki/Finite_field + """ + + rep = 'FF' + alias = 'FF' + + is_FiniteField = is_FF = True + is_Numerical = True + + has_assoc_Ring = False + has_assoc_Field = True + + dom = None + mod = None + + def __init__(self, mod, symmetric=True): + from sympy.polys.domains import ZZ + dom = ZZ + + if mod <= 0: + raise ValueError('modulus must be a positive integer, got %s' % mod) + + ctx, poly_ctx, is_flint = _modular_int_factory(mod, dom, symmetric, self) + + self.dtype = ctx + self._poly_ctx = poly_ctx + self._is_flint = is_flint + + self.zero = self.dtype(0) + self.one = self.dtype(1) + self.dom = dom + self.mod = mod + self.sym = symmetric + self._tp = type(self.zero) + + @property + def tp(self): + return self._tp + + @property + def is_Field(self): + is_field = getattr(self, '_is_field', None) + if is_field is None: + from sympy.ntheory.primetest import isprime + self._is_field = is_field = isprime(self.mod) + return is_field + + def __str__(self): + return 'GF(%s)' % self.mod + + def __hash__(self): + return hash((self.__class__.__name__, self.dtype, self.mod, self.dom)) + + def __eq__(self, other): + """Returns ``True`` if two domains are equivalent. """ + return isinstance(other, FiniteField) and \ + self.mod == other.mod and self.dom == other.dom + + def characteristic(self): + """Return the characteristic of this domain. """ + return self.mod + + def get_field(self): + """Returns a field associated with ``self``. """ + return self + + def to_sympy(self, a): + """Convert ``a`` to a SymPy object. """ + return SymPyInteger(self.to_int(a)) + + def from_sympy(self, a): + """Convert SymPy's Integer to SymPy's ``Integer``. """ + if a.is_Integer: + return self.dtype(self.dom.dtype(int(a))) + elif int_valued(a): + return self.dtype(self.dom.dtype(int(a))) + else: + raise CoercionFailed("expected an integer, got %s" % a) + + def to_int(self, a): + """Convert ``val`` to a Python ``int`` object. """ + aval = int(a) + if self.sym and aval > self.mod // 2: + aval -= self.mod + return aval + + def is_positive(self, a): + """Returns True if ``a`` is positive. """ + return bool(a) + + def is_nonnegative(self, a): + """Returns True if ``a`` is non-negative. """ + return True + + def is_negative(self, a): + """Returns True if ``a`` is negative. """ + return False + + def is_nonpositive(self, a): + """Returns True if ``a`` is non-positive. """ + return not a + + def from_FF(K1, a, K0=None): + """Convert ``ModularInteger(int)`` to ``dtype``. """ + return K1.dtype(K1.dom.from_ZZ(int(a), K0.dom)) + + def from_FF_python(K1, a, K0=None): + """Convert ``ModularInteger(int)`` to ``dtype``. """ + return K1.dtype(K1.dom.from_ZZ_python(int(a), K0.dom)) + + def from_ZZ(K1, a, K0=None): + """Convert Python's ``int`` to ``dtype``. """ + return K1.dtype(K1.dom.from_ZZ_python(a, K0)) + + def from_ZZ_python(K1, a, K0=None): + """Convert Python's ``int`` to ``dtype``. """ + return K1.dtype(K1.dom.from_ZZ_python(a, K0)) + + def from_QQ(K1, a, K0=None): + """Convert Python's ``Fraction`` to ``dtype``. """ + if a.denominator == 1: + return K1.from_ZZ_python(a.numerator) + + def from_QQ_python(K1, a, K0=None): + """Convert Python's ``Fraction`` to ``dtype``. """ + if a.denominator == 1: + return K1.from_ZZ_python(a.numerator) + + def from_FF_gmpy(K1, a, K0=None): + """Convert ``ModularInteger(mpz)`` to ``dtype``. """ + return K1.dtype(K1.dom.from_ZZ_gmpy(a.val, K0.dom)) + + def from_ZZ_gmpy(K1, a, K0=None): + """Convert GMPY's ``mpz`` to ``dtype``. """ + return K1.dtype(K1.dom.from_ZZ_gmpy(a, K0)) + + def from_QQ_gmpy(K1, a, K0=None): + """Convert GMPY's ``mpq`` to ``dtype``. """ + if a.denominator == 1: + return K1.from_ZZ_gmpy(a.numerator) + + def from_RealField(K1, a, K0): + """Convert mpmath's ``mpf`` to ``dtype``. """ + p, q = K0.to_rational(a) + + if q == 1: + return K1.dtype(K1.dom.dtype(p)) + + def is_square(self, a): + """Returns True if ``a`` is a quadratic residue modulo p. """ + # a is not a square <=> x**2-a is irreducible + poly = [int(x) for x in [self.one, self.zero, -a]] + return not gf_irred_p_rabin(poly, self.mod, self.dom) + + def exsqrt(self, a): + """Square root modulo p of ``a`` if it is a quadratic residue. + + Explanation + =========== + Always returns the square root that is no larger than ``p // 2``. + """ + # x**2-a is not square-free if a=0 or the field is characteristic 2 + if self.mod == 2 or a == 0: + return a + # Otherwise, use square-free factorization routine to factorize x**2-a + poly = [int(x) for x in [self.one, self.zero, -a]] + for factor in gf_zassenhaus(poly, self.mod, self.dom): + if len(factor) == 2 and factor[1] <= self.mod // 2: + return self.dtype(factor[1]) + return None + + +FF = GF = FiniteField diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/fractionfield.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/fractionfield.py new file mode 100644 index 0000000000000000000000000000000000000000..78f5054ddd5480fe6f77442f7a25f22603a4d90d --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/fractionfield.py @@ -0,0 +1,181 @@ +"""Implementation of :class:`FractionField` class. """ + + +from sympy.polys.domains.compositedomain import CompositeDomain +from sympy.polys.domains.field import Field +from sympy.polys.polyerrors import CoercionFailed, GeneratorsError +from sympy.utilities import public + +@public +class FractionField(Field, CompositeDomain): + """A class for representing multivariate rational function fields. """ + + is_FractionField = is_Frac = True + + has_assoc_Ring = True + has_assoc_Field = True + + def __init__(self, domain_or_field, symbols=None, order=None): + from sympy.polys.fields import FracField + + if isinstance(domain_or_field, FracField) and symbols is None and order is None: + field = domain_or_field + else: + field = FracField(symbols, domain_or_field, order) + + self.field = field + self.dtype = field.dtype + + self.gens = field.gens + self.ngens = field.ngens + self.symbols = field.symbols + self.domain = field.domain + + # TODO: remove this + self.dom = self.domain + + def new(self, element): + return self.field.field_new(element) + + def of_type(self, element): + """Check if ``a`` is of type ``dtype``. """ + return self.field.is_element(element) + + @property + def zero(self): + return self.field.zero + + @property + def one(self): + return self.field.one + + @property + def order(self): + return self.field.order + + def __str__(self): + return str(self.domain) + '(' + ','.join(map(str, self.symbols)) + ')' + + def __hash__(self): + return hash((self.__class__.__name__, self.field, self.domain, self.symbols)) + + def __eq__(self, other): + """Returns ``True`` if two domains are equivalent. """ + if not isinstance(other, FractionField): + return NotImplemented + return self.field == other.field + + def to_sympy(self, a): + """Convert ``a`` to a SymPy object. """ + return a.as_expr() + + def from_sympy(self, a): + """Convert SymPy's expression to ``dtype``. """ + return self.field.from_expr(a) + + def from_ZZ(K1, a, K0): + """Convert a Python ``int`` object to ``dtype``. """ + return K1(K1.domain.convert(a, K0)) + + def from_ZZ_python(K1, a, K0): + """Convert a Python ``int`` object to ``dtype``. """ + return K1(K1.domain.convert(a, K0)) + + def from_QQ(K1, a, K0): + """Convert a Python ``Fraction`` object to ``dtype``. """ + dom = K1.domain + conv = dom.convert_from + if dom.is_ZZ: + return K1(conv(K0.numer(a), K0)) / K1(conv(K0.denom(a), K0)) + else: + return K1(conv(a, K0)) + + def from_QQ_python(K1, a, K0): + """Convert a Python ``Fraction`` object to ``dtype``. """ + return K1(K1.domain.convert(a, K0)) + + def from_ZZ_gmpy(K1, a, K0): + """Convert a GMPY ``mpz`` object to ``dtype``. """ + return K1(K1.domain.convert(a, K0)) + + def from_QQ_gmpy(K1, a, K0): + """Convert a GMPY ``mpq`` object to ``dtype``. """ + return K1(K1.domain.convert(a, K0)) + + def from_GaussianRationalField(K1, a, K0): + """Convert a ``GaussianRational`` object to ``dtype``. """ + return K1(K1.domain.convert(a, K0)) + + def from_GaussianIntegerRing(K1, a, K0): + """Convert a ``GaussianInteger`` object to ``dtype``. """ + return K1(K1.domain.convert(a, K0)) + + def from_RealField(K1, a, K0): + """Convert a mpmath ``mpf`` object to ``dtype``. """ + return K1(K1.domain.convert(a, K0)) + + def from_ComplexField(K1, a, K0): + """Convert a mpmath ``mpf`` object to ``dtype``. """ + return K1(K1.domain.convert(a, K0)) + + def from_AlgebraicField(K1, a, K0): + """Convert an algebraic number to ``dtype``. """ + if K1.domain != K0: + a = K1.domain.convert_from(a, K0) + if a is not None: + return K1.new(a) + + def from_PolynomialRing(K1, a, K0): + """Convert a polynomial to ``dtype``. """ + if a.is_ground: + return K1.convert_from(a.coeff(1), K0.domain) + try: + return K1.new(a.set_ring(K1.field.ring)) + except (CoercionFailed, GeneratorsError): + # XXX: We get here if K1=ZZ(x,y) and K0=QQ[x,y] + # and the poly a in K0 has non-integer coefficients. + # It seems that K1.new can handle this but K1.new doesn't work + # when K0.domain is an algebraic field... + try: + return K1.new(a) + except (CoercionFailed, GeneratorsError): + return None + + def from_FractionField(K1, a, K0): + """Convert a rational function to ``dtype``. """ + try: + return a.set_field(K1.field) + except (CoercionFailed, GeneratorsError): + return None + + def get_ring(self): + """Returns a field associated with ``self``. """ + return self.field.to_ring().to_domain() + + def is_positive(self, a): + """Returns True if ``LC(a)`` is positive. """ + return self.domain.is_positive(a.numer.LC) + + def is_negative(self, a): + """Returns True if ``LC(a)`` is negative. """ + return self.domain.is_negative(a.numer.LC) + + def is_nonpositive(self, a): + """Returns True if ``LC(a)`` is non-positive. """ + return self.domain.is_nonpositive(a.numer.LC) + + def is_nonnegative(self, a): + """Returns True if ``LC(a)`` is non-negative. """ + return self.domain.is_nonnegative(a.numer.LC) + + def numer(self, a): + """Returns numerator of ``a``. """ + return a.numer + + def denom(self, a): + """Returns denominator of ``a``. """ + return a.denom + + def factorial(self, a): + """Returns factorial of ``a``. """ + return self.dtype(self.domain.factorial(a)) diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/gaussiandomains.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/gaussiandomains.py new file mode 100644 index 0000000000000000000000000000000000000000..a96bed78e29445c90c53605a85faa4df16bf807c --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/gaussiandomains.py @@ -0,0 +1,706 @@ +"""Domains of Gaussian type.""" + +from __future__ import annotations +from sympy.core.numbers import I +from sympy.polys.polyclasses import DMP +from sympy.polys.polyerrors import CoercionFailed +from sympy.polys.domains.integerring import ZZ +from sympy.polys.domains.rationalfield import QQ +from sympy.polys.domains.algebraicfield import AlgebraicField +from sympy.polys.domains.domain import Domain +from sympy.polys.domains.domainelement import DomainElement +from sympy.polys.domains.field import Field +from sympy.polys.domains.ring import Ring + + +class GaussianElement(DomainElement): + """Base class for elements of Gaussian type domains.""" + base: Domain + _parent: Domain + + __slots__ = ('x', 'y') + + def __new__(cls, x, y=0): + conv = cls.base.convert + return cls.new(conv(x), conv(y)) + + @classmethod + def new(cls, x, y): + """Create a new GaussianElement of the same domain.""" + obj = super().__new__(cls) + obj.x = x + obj.y = y + return obj + + def parent(self): + """The domain that this is an element of (ZZ_I or QQ_I)""" + return self._parent + + def __hash__(self): + return hash((self.x, self.y)) + + def __eq__(self, other): + if isinstance(other, self.__class__): + return self.x == other.x and self.y == other.y + else: + return NotImplemented + + def __lt__(self, other): + if not isinstance(other, GaussianElement): + return NotImplemented + return [self.y, self.x] < [other.y, other.x] + + def __pos__(self): + return self + + def __neg__(self): + return self.new(-self.x, -self.y) + + def __repr__(self): + return "%s(%s, %s)" % (self._parent.rep, self.x, self.y) + + def __str__(self): + return str(self._parent.to_sympy(self)) + + @classmethod + def _get_xy(cls, other): + if not isinstance(other, cls): + try: + other = cls._parent.convert(other) + except CoercionFailed: + return None, None + return other.x, other.y + + def __add__(self, other): + x, y = self._get_xy(other) + if x is not None: + return self.new(self.x + x, self.y + y) + else: + return NotImplemented + + __radd__ = __add__ + + def __sub__(self, other): + x, y = self._get_xy(other) + if x is not None: + return self.new(self.x - x, self.y - y) + else: + return NotImplemented + + def __rsub__(self, other): + x, y = self._get_xy(other) + if x is not None: + return self.new(x - self.x, y - self.y) + else: + return NotImplemented + + def __mul__(self, other): + x, y = self._get_xy(other) + if x is not None: + return self.new(self.x*x - self.y*y, self.x*y + self.y*x) + else: + return NotImplemented + + __rmul__ = __mul__ + + def __pow__(self, exp): + if exp == 0: + return self.new(1, 0) + if exp < 0: + self, exp = 1/self, -exp + if exp == 1: + return self + pow2 = self + prod = self if exp % 2 else self._parent.one + exp //= 2 + while exp: + pow2 *= pow2 + if exp % 2: + prod *= pow2 + exp //= 2 + return prod + + def __bool__(self): + return bool(self.x) or bool(self.y) + + def quadrant(self): + """Return quadrant index 0-3. + + 0 is included in quadrant 0. + """ + if self.y > 0: + return 0 if self.x > 0 else 1 + elif self.y < 0: + return 2 if self.x < 0 else 3 + else: + return 0 if self.x >= 0 else 2 + + def __rdivmod__(self, other): + try: + other = self._parent.convert(other) + except CoercionFailed: + return NotImplemented + else: + return other.__divmod__(self) + + def __rtruediv__(self, other): + try: + other = QQ_I.convert(other) + except CoercionFailed: + return NotImplemented + else: + return other.__truediv__(self) + + def __floordiv__(self, other): + qr = self.__divmod__(other) + return qr if qr is NotImplemented else qr[0] + + def __rfloordiv__(self, other): + qr = self.__rdivmod__(other) + return qr if qr is NotImplemented else qr[0] + + def __mod__(self, other): + qr = self.__divmod__(other) + return qr if qr is NotImplemented else qr[1] + + def __rmod__(self, other): + qr = self.__rdivmod__(other) + return qr if qr is NotImplemented else qr[1] + + +class GaussianInteger(GaussianElement): + """Gaussian integer: domain element for :ref:`ZZ_I` + + >>> from sympy import ZZ_I + >>> z = ZZ_I(2, 3) + >>> z + (2 + 3*I) + >>> type(z) + + """ + base = ZZ + + def __truediv__(self, other): + """Return a Gaussian rational.""" + return QQ_I.convert(self)/other + + def __divmod__(self, other): + if not other: + raise ZeroDivisionError('divmod({}, 0)'.format(self)) + x, y = self._get_xy(other) + if x is None: + return NotImplemented + + # multiply self and other by x - I*y + # self/other == (a + I*b)/c + a, b = self.x*x + self.y*y, -self.x*y + self.y*x + c = x*x + y*y + + # find integers qx and qy such that + # |a - qx*c| <= c/2 and |b - qy*c| <= c/2 + qx = (2*a + c) // (2*c) # -c <= 2*a - qx*2*c < c + qy = (2*b + c) // (2*c) + + q = GaussianInteger(qx, qy) + # |self/other - q| < 1 since + # |a/c - qx|**2 + |b/c - qy|**2 <= 1/4 + 1/4 < 1 + + return q, self - q*other # |r| < |other| + + +class GaussianRational(GaussianElement): + """Gaussian rational: domain element for :ref:`QQ_I` + + >>> from sympy import QQ_I, QQ + >>> z = QQ_I(QQ(2, 3), QQ(4, 5)) + >>> z + (2/3 + 4/5*I) + >>> type(z) + + """ + base = QQ + + def __truediv__(self, other): + """Return a Gaussian rational.""" + if not other: + raise ZeroDivisionError('{} / 0'.format(self)) + x, y = self._get_xy(other) + if x is None: + return NotImplemented + c = x*x + y*y + + return GaussianRational((self.x*x + self.y*y)/c, + (-self.x*y + self.y*x)/c) + + def __divmod__(self, other): + try: + other = self._parent.convert(other) + except CoercionFailed: + return NotImplemented + if not other: + raise ZeroDivisionError('{} % 0'.format(self)) + else: + return self/other, QQ_I.zero + + +class GaussianDomain(): + """Base class for Gaussian domains.""" + dom: Domain + + is_Numerical = True + is_Exact = True + + has_assoc_Ring = True + has_assoc_Field = True + + def to_sympy(self, a): + """Convert ``a`` to a SymPy object. """ + conv = self.dom.to_sympy + return conv(a.x) + I*conv(a.y) + + def from_sympy(self, a): + """Convert a SymPy object to ``self.dtype``.""" + r, b = a.as_coeff_Add() + x = self.dom.from_sympy(r) # may raise CoercionFailed + if not b: + return self.new(x, 0) + r, b = b.as_coeff_Mul() + y = self.dom.from_sympy(r) + if b is I: + return self.new(x, y) + else: + raise CoercionFailed("{} is not Gaussian".format(a)) + + def inject(self, *gens): + """Inject generators into this domain. """ + return self.poly_ring(*gens) + + def canonical_unit(self, d): + unit = self.units[-d.quadrant()] # - for inverse power + return unit + + def is_negative(self, element): + """Returns ``False`` for any ``GaussianElement``. """ + return False + + def is_positive(self, element): + """Returns ``False`` for any ``GaussianElement``. """ + return False + + def is_nonnegative(self, element): + """Returns ``False`` for any ``GaussianElement``. """ + return False + + def is_nonpositive(self, element): + """Returns ``False`` for any ``GaussianElement``. """ + return False + + def from_ZZ_gmpy(K1, a, K0): + """Convert a GMPY mpz to ``self.dtype``.""" + return K1(a) + + def from_ZZ(K1, a, K0): + """Convert a ZZ_python element to ``self.dtype``.""" + return K1(a) + + def from_ZZ_python(K1, a, K0): + """Convert a ZZ_python element to ``self.dtype``.""" + return K1(a) + + def from_QQ(K1, a, K0): + """Convert a GMPY mpq to ``self.dtype``.""" + return K1(a) + + def from_QQ_gmpy(K1, a, K0): + """Convert a GMPY mpq to ``self.dtype``.""" + return K1(a) + + def from_QQ_python(K1, a, K0): + """Convert a QQ_python element to ``self.dtype``.""" + return K1(a) + + def from_AlgebraicField(K1, a, K0): + """Convert an element from ZZ or QQ to ``self.dtype``.""" + if K0.ext.args[0] == I: + return K1.from_sympy(K0.to_sympy(a)) + + +class GaussianIntegerRing(GaussianDomain, Ring): + r"""Ring of Gaussian integers ``ZZ_I`` + + The :ref:`ZZ_I` domain represents the `Gaussian integers`_ `\mathbb{Z}[i]` + as a :py:class:`~.Domain` in the domain system (see + :ref:`polys-domainsintro`). + + By default a :py:class:`~.Poly` created from an expression with + coefficients that are combinations of integers and ``I`` (`\sqrt{-1}`) + will have the domain :ref:`ZZ_I`. + + >>> from sympy import Poly, Symbol, I + >>> x = Symbol('x') + >>> p = Poly(x**2 + I) + >>> p + Poly(x**2 + I, x, domain='ZZ_I') + >>> p.domain + ZZ_I + + The :ref:`ZZ_I` domain can be used to factorise polynomials that are + reducible over the Gaussian integers. + + >>> from sympy import factor + >>> factor(x**2 + 1) + x**2 + 1 + >>> factor(x**2 + 1, domain='ZZ_I') + (x - I)*(x + I) + + The corresponding `field of fractions`_ is the domain of the Gaussian + rationals :ref:`QQ_I`. Conversely :ref:`ZZ_I` is the `ring of integers`_ + of :ref:`QQ_I`. + + >>> from sympy import ZZ_I, QQ_I + >>> ZZ_I.get_field() + QQ_I + >>> QQ_I.get_ring() + ZZ_I + + When using the domain directly :ref:`ZZ_I` can be used as a constructor. + + >>> ZZ_I(3, 4) + (3 + 4*I) + >>> ZZ_I(5) + (5 + 0*I) + + The domain elements of :ref:`ZZ_I` are instances of + :py:class:`~.GaussianInteger` which support the rings operations + ``+,-,*,**``. + + >>> z1 = ZZ_I(5, 1) + >>> z2 = ZZ_I(2, 3) + >>> z1 + (5 + 1*I) + >>> z2 + (2 + 3*I) + >>> z1 + z2 + (7 + 4*I) + >>> z1 * z2 + (7 + 17*I) + >>> z1 ** 2 + (24 + 10*I) + + Both floor (``//``) and modulo (``%``) division work with + :py:class:`~.GaussianInteger` (see the :py:meth:`~.Domain.div` method). + + >>> z3, z4 = ZZ_I(5), ZZ_I(1, 3) + >>> z3 // z4 # floor division + (1 + -1*I) + >>> z3 % z4 # modulo division (remainder) + (1 + -2*I) + >>> (z3//z4)*z4 + z3%z4 == z3 + True + + True division (``/``) in :ref:`ZZ_I` gives an element of :ref:`QQ_I`. The + :py:meth:`~.Domain.exquo` method can be used to divide in :ref:`ZZ_I` when + exact division is possible. + + >>> z1 / z2 + (1 + -1*I) + >>> ZZ_I.exquo(z1, z2) + (1 + -1*I) + >>> z3 / z4 + (1/2 + -3/2*I) + >>> ZZ_I.exquo(z3, z4) + Traceback (most recent call last): + ... + ExactQuotientFailed: (1 + 3*I) does not divide (5 + 0*I) in ZZ_I + + The :py:meth:`~.Domain.gcd` method can be used to compute the `gcd`_ of any + two elements. + + >>> ZZ_I.gcd(ZZ_I(10), ZZ_I(2)) + (2 + 0*I) + >>> ZZ_I.gcd(ZZ_I(5), ZZ_I(2, 1)) + (2 + 1*I) + + .. _Gaussian integers: https://en.wikipedia.org/wiki/Gaussian_integer + .. _gcd: https://en.wikipedia.org/wiki/Greatest_common_divisor + + """ + dom = ZZ + mod = DMP([ZZ.one, ZZ.zero, ZZ.one], ZZ) + dtype = GaussianInteger + zero = dtype(ZZ(0), ZZ(0)) + one = dtype(ZZ(1), ZZ(0)) + imag_unit = dtype(ZZ(0), ZZ(1)) + units = (one, imag_unit, -one, -imag_unit) # powers of i + + rep = 'ZZ_I' + + is_GaussianRing = True + is_ZZ_I = True + is_PID = True + + def __init__(self): # override Domain.__init__ + """For constructing ZZ_I.""" + + def __eq__(self, other): + """Returns ``True`` if two domains are equivalent. """ + if isinstance(other, GaussianIntegerRing): + return True + else: + return NotImplemented + + def __hash__(self): + """Compute hash code of ``self``. """ + return hash('ZZ_I') + + @property + def has_CharacteristicZero(self): + return True + + def characteristic(self): + return 0 + + def get_ring(self): + """Returns a ring associated with ``self``. """ + return self + + def get_field(self): + """Returns a field associated with ``self``. """ + return QQ_I + + def normalize(self, d, *args): + """Return first quadrant element associated with ``d``. + + Also multiply the other arguments by the same power of i. + """ + unit = self.canonical_unit(d) + d *= unit + args = tuple(a*unit for a in args) + return (d,) + args if args else d + + def gcd(self, a, b): + """Greatest common divisor of a and b over ZZ_I.""" + while b: + a, b = b, a % b + return self.normalize(a) + + def gcdex(self, a, b): + """Return x, y, g such that x * a + y * b = g = gcd(a, b)""" + x_a = self.one + x_b = self.zero + y_a = self.zero + y_b = self.one + while b: + q = a // b + a, b = b, a - q * b + x_a, x_b = x_b, x_a - q * x_b + y_a, y_b = y_b, y_a - q * y_b + + a, x_a, y_a = self.normalize(a, x_a, y_a) + return x_a, y_a, a + + def lcm(self, a, b): + """Least common multiple of a and b over ZZ_I.""" + return (a * b) // self.gcd(a, b) + + def from_GaussianIntegerRing(K1, a, K0): + """Convert a ZZ_I element to ZZ_I.""" + return a + + def from_GaussianRationalField(K1, a, K0): + """Convert a QQ_I element to ZZ_I.""" + return K1.new(ZZ.convert(a.x), ZZ.convert(a.y)) + +ZZ_I = GaussianInteger._parent = GaussianIntegerRing() + + +class GaussianRationalField(GaussianDomain, Field): + r"""Field of Gaussian rationals ``QQ_I`` + + The :ref:`QQ_I` domain represents the `Gaussian rationals`_ `\mathbb{Q}(i)` + as a :py:class:`~.Domain` in the domain system (see + :ref:`polys-domainsintro`). + + By default a :py:class:`~.Poly` created from an expression with + coefficients that are combinations of rationals and ``I`` (`\sqrt{-1}`) + will have the domain :ref:`QQ_I`. + + >>> from sympy import Poly, Symbol, I + >>> x = Symbol('x') + >>> p = Poly(x**2 + I/2) + >>> p + Poly(x**2 + I/2, x, domain='QQ_I') + >>> p.domain + QQ_I + + The polys option ``gaussian=True`` can be used to specify that the domain + should be :ref:`QQ_I` even if the coefficients do not contain ``I`` or are + all integers. + + >>> Poly(x**2) + Poly(x**2, x, domain='ZZ') + >>> Poly(x**2 + I) + Poly(x**2 + I, x, domain='ZZ_I') + >>> Poly(x**2/2) + Poly(1/2*x**2, x, domain='QQ') + >>> Poly(x**2, gaussian=True) + Poly(x**2, x, domain='QQ_I') + >>> Poly(x**2 + I, gaussian=True) + Poly(x**2 + I, x, domain='QQ_I') + >>> Poly(x**2/2, gaussian=True) + Poly(1/2*x**2, x, domain='QQ_I') + + The :ref:`QQ_I` domain can be used to factorise polynomials that are + reducible over the Gaussian rationals. + + >>> from sympy import factor, QQ_I + >>> factor(x**2/4 + 1) + (x**2 + 4)/4 + >>> factor(x**2/4 + 1, domain='QQ_I') + (x - 2*I)*(x + 2*I)/4 + >>> factor(x**2/4 + 1, domain=QQ_I) + (x - 2*I)*(x + 2*I)/4 + + It is also possible to specify the :ref:`QQ_I` domain explicitly with + polys functions like :py:func:`~.apart`. + + >>> from sympy import apart + >>> apart(1/(1 + x**2)) + 1/(x**2 + 1) + >>> apart(1/(1 + x**2), domain=QQ_I) + I/(2*(x + I)) - I/(2*(x - I)) + + The corresponding `ring of integers`_ is the domain of the Gaussian + integers :ref:`ZZ_I`. Conversely :ref:`QQ_I` is the `field of fractions`_ + of :ref:`ZZ_I`. + + >>> from sympy import ZZ_I, QQ_I, QQ + >>> ZZ_I.get_field() + QQ_I + >>> QQ_I.get_ring() + ZZ_I + + When using the domain directly :ref:`QQ_I` can be used as a constructor. + + >>> QQ_I(3, 4) + (3 + 4*I) + >>> QQ_I(5) + (5 + 0*I) + >>> QQ_I(QQ(2, 3), QQ(4, 5)) + (2/3 + 4/5*I) + + The domain elements of :ref:`QQ_I` are instances of + :py:class:`~.GaussianRational` which support the field operations + ``+,-,*,**,/``. + + >>> z1 = QQ_I(5, 1) + >>> z2 = QQ_I(2, QQ(1, 2)) + >>> z1 + (5 + 1*I) + >>> z2 + (2 + 1/2*I) + >>> z1 + z2 + (7 + 3/2*I) + >>> z1 * z2 + (19/2 + 9/2*I) + >>> z2 ** 2 + (15/4 + 2*I) + + True division (``/``) in :ref:`QQ_I` gives an element of :ref:`QQ_I` and + is always exact. + + >>> z1 / z2 + (42/17 + -2/17*I) + >>> QQ_I.exquo(z1, z2) + (42/17 + -2/17*I) + >>> z1 == (z1/z2)*z2 + True + + Both floor (``//``) and modulo (``%``) division can be used with + :py:class:`~.GaussianRational` (see :py:meth:`~.Domain.div`) + but division is always exact so there is no remainder. + + >>> z1 // z2 + (42/17 + -2/17*I) + >>> z1 % z2 + (0 + 0*I) + >>> QQ_I.div(z1, z2) + ((42/17 + -2/17*I), (0 + 0*I)) + >>> (z1//z2)*z2 + z1%z2 == z1 + True + + .. _Gaussian rationals: https://en.wikipedia.org/wiki/Gaussian_rational + """ + dom = QQ + mod = DMP([QQ.one, QQ.zero, QQ.one], QQ) + dtype = GaussianRational + zero = dtype(QQ(0), QQ(0)) + one = dtype(QQ(1), QQ(0)) + imag_unit = dtype(QQ(0), QQ(1)) + units = (one, imag_unit, -one, -imag_unit) # powers of i + + rep = 'QQ_I' + + is_GaussianField = True + is_QQ_I = True + + def __init__(self): # override Domain.__init__ + """For constructing QQ_I.""" + + def __eq__(self, other): + """Returns ``True`` if two domains are equivalent. """ + if isinstance(other, GaussianRationalField): + return True + else: + return NotImplemented + + def __hash__(self): + """Compute hash code of ``self``. """ + return hash('QQ_I') + + @property + def has_CharacteristicZero(self): + return True + + def characteristic(self): + return 0 + + def get_ring(self): + """Returns a ring associated with ``self``. """ + return ZZ_I + + def get_field(self): + """Returns a field associated with ``self``. """ + return self + + def as_AlgebraicField(self): + """Get equivalent domain as an ``AlgebraicField``. """ + return AlgebraicField(self.dom, I) + + def numer(self, a): + """Get the numerator of ``a``.""" + ZZ_I = self.get_ring() + return ZZ_I.convert(a * self.denom(a)) + + def denom(self, a): + """Get the denominator of ``a``.""" + ZZ = self.dom.get_ring() + QQ = self.dom + ZZ_I = self.get_ring() + denom_ZZ = ZZ.lcm(QQ.denom(a.x), QQ.denom(a.y)) + return ZZ_I(denom_ZZ, ZZ.zero) + + def from_GaussianIntegerRing(K1, a, K0): + """Convert a ZZ_I element to QQ_I.""" + return K1.new(a.x, a.y) + + def from_GaussianRationalField(K1, a, K0): + """Convert a QQ_I element to QQ_I.""" + return a + + def from_ComplexField(K1, a, K0): + """Convert a ComplexField element to QQ_I.""" + return K1.new(QQ.convert(a.real), QQ.convert(a.imag)) + + +QQ_I = GaussianRational._parent = GaussianRationalField() diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/gmpyfinitefield.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/gmpyfinitefield.py new file mode 100644 index 0000000000000000000000000000000000000000..2e8315a29eca8160102d66b83d953caf998b0fd7 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/gmpyfinitefield.py @@ -0,0 +1,16 @@ +"""Implementation of :class:`GMPYFiniteField` class. """ + + +from sympy.polys.domains.finitefield import FiniteField +from sympy.polys.domains.gmpyintegerring import GMPYIntegerRing + +from sympy.utilities import public + +@public +class GMPYFiniteField(FiniteField): + """Finite field based on GMPY integers. """ + + alias = 'FF_gmpy' + + def __init__(self, mod, symmetric=True): + super().__init__(mod, GMPYIntegerRing(), symmetric) diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/gmpyintegerring.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/gmpyintegerring.py new file mode 100644 index 0000000000000000000000000000000000000000..f132bbe5aff7a4164a09b9b90f00ae5f140cbd03 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/gmpyintegerring.py @@ -0,0 +1,105 @@ +"""Implementation of :class:`GMPYIntegerRing` class. """ + + +from sympy.polys.domains.groundtypes import ( + GMPYInteger, SymPyInteger, + factorial as gmpy_factorial, + gmpy_gcdex, gmpy_gcd, gmpy_lcm, sqrt as gmpy_sqrt, +) +from sympy.core.numbers import int_valued +from sympy.polys.domains.integerring import IntegerRing +from sympy.polys.polyerrors import CoercionFailed +from sympy.utilities import public + +@public +class GMPYIntegerRing(IntegerRing): + """Integer ring based on GMPY's ``mpz`` type. + + This will be the implementation of :ref:`ZZ` if ``gmpy`` or ``gmpy2`` is + installed. Elements will be of type ``gmpy.mpz``. + """ + + dtype = GMPYInteger + zero = dtype(0) + one = dtype(1) + tp = type(one) + alias = 'ZZ_gmpy' + + def __init__(self): + """Allow instantiation of this domain. """ + + def to_sympy(self, a): + """Convert ``a`` to a SymPy object. """ + return SymPyInteger(int(a)) + + def from_sympy(self, a): + """Convert SymPy's Integer to ``dtype``. """ + if a.is_Integer: + return GMPYInteger(a.p) + elif int_valued(a): + return GMPYInteger(int(a)) + else: + raise CoercionFailed("expected an integer, got %s" % a) + + def from_FF_python(K1, a, K0): + """Convert ``ModularInteger(int)`` to GMPY's ``mpz``. """ + return K0.to_int(a) + + def from_ZZ_python(K1, a, K0): + """Convert Python's ``int`` to GMPY's ``mpz``. """ + return GMPYInteger(a) + + def from_QQ(K1, a, K0): + """Convert Python's ``Fraction`` to GMPY's ``mpz``. """ + if a.denominator == 1: + return GMPYInteger(a.numerator) + + def from_QQ_python(K1, a, K0): + """Convert Python's ``Fraction`` to GMPY's ``mpz``. """ + if a.denominator == 1: + return GMPYInteger(a.numerator) + + def from_FF_gmpy(K1, a, K0): + """Convert ``ModularInteger(mpz)`` to GMPY's ``mpz``. """ + return K0.to_int(a) + + def from_ZZ_gmpy(K1, a, K0): + """Convert GMPY's ``mpz`` to GMPY's ``mpz``. """ + return a + + def from_QQ_gmpy(K1, a, K0): + """Convert GMPY ``mpq`` to GMPY's ``mpz``. """ + if a.denominator == 1: + return a.numerator + + def from_RealField(K1, a, K0): + """Convert mpmath's ``mpf`` to GMPY's ``mpz``. """ + p, q = K0.to_rational(a) + + if q == 1: + return GMPYInteger(p) + + def from_GaussianIntegerRing(K1, a, K0): + if a.y == 0: + return a.x + + def gcdex(self, a, b): + """Compute extended GCD of ``a`` and ``b``. """ + h, s, t = gmpy_gcdex(a, b) + return s, t, h + + def gcd(self, a, b): + """Compute GCD of ``a`` and ``b``. """ + return gmpy_gcd(a, b) + + def lcm(self, a, b): + """Compute LCM of ``a`` and ``b``. """ + return gmpy_lcm(a, b) + + def sqrt(self, a): + """Compute square root of ``a``. """ + return gmpy_sqrt(a) + + def factorial(self, a): + """Compute factorial of ``a``. """ + return gmpy_factorial(a) diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/gmpyrationalfield.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/gmpyrationalfield.py new file mode 100644 index 0000000000000000000000000000000000000000..10bae5b2b7b476f96ba06f637c549ee4afff4c6d --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/gmpyrationalfield.py @@ -0,0 +1,100 @@ +"""Implementation of :class:`GMPYRationalField` class. """ + + +from sympy.polys.domains.groundtypes import ( + GMPYRational, SymPyRational, + gmpy_numer, gmpy_denom, factorial as gmpy_factorial, +) +from sympy.polys.domains.rationalfield import RationalField +from sympy.polys.polyerrors import CoercionFailed +from sympy.utilities import public + +@public +class GMPYRationalField(RationalField): + """Rational field based on GMPY's ``mpq`` type. + + This will be the implementation of :ref:`QQ` if ``gmpy`` or ``gmpy2`` is + installed. Elements will be of type ``gmpy.mpq``. + """ + + dtype = GMPYRational + zero = dtype(0) + one = dtype(1) + tp = type(one) + alias = 'QQ_gmpy' + + def __init__(self): + pass + + def get_ring(self): + """Returns ring associated with ``self``. """ + from sympy.polys.domains import GMPYIntegerRing + return GMPYIntegerRing() + + def to_sympy(self, a): + """Convert ``a`` to a SymPy object. """ + return SymPyRational(int(gmpy_numer(a)), + int(gmpy_denom(a))) + + def from_sympy(self, a): + """Convert SymPy's Integer to ``dtype``. """ + if a.is_Rational: + return GMPYRational(a.p, a.q) + elif a.is_Float: + from sympy.polys.domains import RR + return GMPYRational(*map(int, RR.to_rational(a))) + else: + raise CoercionFailed("expected ``Rational`` object, got %s" % a) + + def from_ZZ_python(K1, a, K0): + """Convert a Python ``int`` object to ``dtype``. """ + return GMPYRational(a) + + def from_QQ_python(K1, a, K0): + """Convert a Python ``Fraction`` object to ``dtype``. """ + return GMPYRational(a.numerator, a.denominator) + + def from_ZZ_gmpy(K1, a, K0): + """Convert a GMPY ``mpz`` object to ``dtype``. """ + return GMPYRational(a) + + def from_QQ_gmpy(K1, a, K0): + """Convert a GMPY ``mpq`` object to ``dtype``. """ + return a + + def from_GaussianRationalField(K1, a, K0): + """Convert a ``GaussianElement`` object to ``dtype``. """ + if a.y == 0: + return GMPYRational(a.x) + + def from_RealField(K1, a, K0): + """Convert a mpmath ``mpf`` object to ``dtype``. """ + return GMPYRational(*map(int, K0.to_rational(a))) + + def exquo(self, a, b): + """Exact quotient of ``a`` and ``b``, implies ``__truediv__``. """ + return GMPYRational(a) / GMPYRational(b) + + def quo(self, a, b): + """Quotient of ``a`` and ``b``, implies ``__truediv__``. """ + return GMPYRational(a) / GMPYRational(b) + + def rem(self, a, b): + """Remainder of ``a`` and ``b``, implies nothing. """ + return self.zero + + def div(self, a, b): + """Division of ``a`` and ``b``, implies ``__truediv__``. """ + return GMPYRational(a) / GMPYRational(b), self.zero + + def numer(self, a): + """Returns numerator of ``a``. """ + return a.numerator + + def denom(self, a): + """Returns denominator of ``a``. """ + return a.denominator + + def factorial(self, a): + """Returns factorial of ``a``. """ + return GMPYRational(gmpy_factorial(int(a))) diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/groundtypes.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/groundtypes.py new file mode 100644 index 0000000000000000000000000000000000000000..1d50cf912a998767c4a52c5a2f3aab825e072aec --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/groundtypes.py @@ -0,0 +1,99 @@ +"""Ground types for various mathematical domains in SymPy. """ + +import builtins +from sympy.external.gmpy import GROUND_TYPES, factorial, sqrt, is_square, sqrtrem + +PythonInteger = builtins.int +PythonReal = builtins.float +PythonComplex = builtins.complex + +from .pythonrational import PythonRational + +from sympy.core.intfunc import ( + igcdex as python_gcdex, + igcd2 as python_gcd, + ilcm as python_lcm, +) + +from sympy.core.numbers import (Float as SymPyReal, Integer as SymPyInteger, Rational as SymPyRational) + + +class _GMPYInteger: + def __init__(self, obj): + pass + +class _GMPYRational: + def __init__(self, obj): + pass + + +if GROUND_TYPES == 'gmpy': + + from gmpy2 import ( + mpz as GMPYInteger, + mpq as GMPYRational, + numer as gmpy_numer, + denom as gmpy_denom, + gcdext as gmpy_gcdex, + gcd as gmpy_gcd, + lcm as gmpy_lcm, + qdiv as gmpy_qdiv, + ) + gcdex = gmpy_gcdex + gcd = gmpy_gcd + lcm = gmpy_lcm + +elif GROUND_TYPES == 'flint': + + from flint import fmpz as _fmpz + + GMPYInteger = _GMPYInteger + GMPYRational = _GMPYRational + gmpy_numer = None + gmpy_denom = None + gmpy_gcdex = None + gmpy_gcd = None + gmpy_lcm = None + gmpy_qdiv = None + + def gcd(a, b): + return a.gcd(b) + + def gcdex(a, b): + x, y, g = python_gcdex(a, b) + return _fmpz(x), _fmpz(y), _fmpz(g) + + def lcm(a, b): + return a.lcm(b) + +else: + GMPYInteger = _GMPYInteger + GMPYRational = _GMPYRational + gmpy_numer = None + gmpy_denom = None + gmpy_gcdex = None + gmpy_gcd = None + gmpy_lcm = None + gmpy_qdiv = None + gcdex = python_gcdex + gcd = python_gcd + lcm = python_lcm + + +__all__ = [ + 'PythonInteger', 'PythonReal', 'PythonComplex', + + 'PythonRational', + + 'python_gcdex', 'python_gcd', 'python_lcm', + + 'SymPyReal', 'SymPyInteger', 'SymPyRational', + + 'GMPYInteger', 'GMPYRational', 'gmpy_numer', + 'gmpy_denom', 'gmpy_gcdex', 'gmpy_gcd', 'gmpy_lcm', + 'gmpy_qdiv', + + 'factorial', 'sqrt', 'is_square', 'sqrtrem', + + 'GMPYInteger', 'GMPYRational', +] diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/integerring.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/integerring.py new file mode 100644 index 0000000000000000000000000000000000000000..65eaa9631cfdf138997a4ebdb362c4233fb098fb --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/integerring.py @@ -0,0 +1,276 @@ +"""Implementation of :class:`IntegerRing` class. """ + +from sympy.external.gmpy import MPZ, GROUND_TYPES + +from sympy.core.numbers import int_valued +from sympy.polys.domains.groundtypes import ( + SymPyInteger, + factorial, + gcdex, gcd, lcm, sqrt, is_square, sqrtrem, +) + +from sympy.polys.domains.characteristiczero import CharacteristicZero +from sympy.polys.domains.ring import Ring +from sympy.polys.domains.simpledomain import SimpleDomain +from sympy.polys.polyerrors import CoercionFailed +from sympy.utilities import public + +import math + +@public +class IntegerRing(Ring, CharacteristicZero, SimpleDomain): + r"""The domain ``ZZ`` representing the integers `\mathbb{Z}`. + + The :py:class:`IntegerRing` class represents the ring of integers as a + :py:class:`~.Domain` in the domain system. :py:class:`IntegerRing` is a + super class of :py:class:`PythonIntegerRing` and + :py:class:`GMPYIntegerRing` one of which will be the implementation for + :ref:`ZZ` depending on whether or not ``gmpy`` or ``gmpy2`` is installed. + + See also + ======== + + Domain + """ + + rep = 'ZZ' + alias = 'ZZ' + dtype = MPZ + zero = dtype(0) + one = dtype(1) + tp = type(one) + + + is_IntegerRing = is_ZZ = True + is_Numerical = True + is_PID = True + + has_assoc_Ring = True + has_assoc_Field = True + + def __init__(self): + """Allow instantiation of this domain. """ + + def __eq__(self, other): + """Returns ``True`` if two domains are equivalent. """ + if isinstance(other, IntegerRing): + return True + else: + return NotImplemented + + def __hash__(self): + """Compute a hash value for this domain. """ + return hash('ZZ') + + def to_sympy(self, a): + """Convert ``a`` to a SymPy object. """ + return SymPyInteger(int(a)) + + def from_sympy(self, a): + """Convert SymPy's Integer to ``dtype``. """ + if a.is_Integer: + return MPZ(a.p) + elif int_valued(a): + return MPZ(int(a)) + else: + raise CoercionFailed("expected an integer, got %s" % a) + + def get_field(self): + r"""Return the associated field of fractions :ref:`QQ` + + Returns + ======= + + :ref:`QQ`: + The associated field of fractions :ref:`QQ`, a + :py:class:`~.Domain` representing the rational numbers + `\mathbb{Q}`. + + Examples + ======== + + >>> from sympy import ZZ + >>> ZZ.get_field() + QQ + """ + from sympy.polys.domains import QQ + return QQ + + def algebraic_field(self, *extension, alias=None): + r"""Returns an algebraic field, i.e. `\mathbb{Q}(\alpha, \ldots)`. + + Parameters + ========== + + *extension : One or more :py:class:`~.Expr`. + Generators of the extension. These should be expressions that are + algebraic over `\mathbb{Q}`. + + alias : str, :py:class:`~.Symbol`, None, optional (default=None) + If provided, this will be used as the alias symbol for the + primitive element of the returned :py:class:`~.AlgebraicField`. + + Returns + ======= + + :py:class:`~.AlgebraicField` + A :py:class:`~.Domain` representing the algebraic field extension. + + Examples + ======== + + >>> from sympy import ZZ, sqrt + >>> ZZ.algebraic_field(sqrt(2)) + QQ + """ + return self.get_field().algebraic_field(*extension, alias=alias) + + def from_AlgebraicField(K1, a, K0): + """Convert a :py:class:`~.ANP` object to :ref:`ZZ`. + + See :py:meth:`~.Domain.convert`. + """ + if a.is_ground: + return K1.convert(a.LC(), K0.dom) + + def log(self, a, b): + r"""Logarithm of *a* to the base *b*. + + Parameters + ========== + + a: number + b: number + + Returns + ======= + + $\\lfloor\log(a, b)\\rfloor$: + Floor of the logarithm of *a* to the base *b* + + Examples + ======== + + >>> from sympy import ZZ + >>> ZZ.log(ZZ(8), ZZ(2)) + 3 + >>> ZZ.log(ZZ(9), ZZ(2)) + 3 + + Notes + ===== + + This function uses ``math.log`` which is based on ``float`` so it will + fail for large integer arguments. + """ + return self.dtype(int(math.log(int(a), b))) + + def from_FF(K1, a, K0): + """Convert ``ModularInteger(int)`` to GMPY's ``mpz``. """ + return MPZ(K0.to_int(a)) + + def from_FF_python(K1, a, K0): + """Convert ``ModularInteger(int)`` to GMPY's ``mpz``. """ + return MPZ(K0.to_int(a)) + + def from_ZZ(K1, a, K0): + """Convert Python's ``int`` to GMPY's ``mpz``. """ + return MPZ(a) + + def from_ZZ_python(K1, a, K0): + """Convert Python's ``int`` to GMPY's ``mpz``. """ + return MPZ(a) + + def from_QQ(K1, a, K0): + """Convert Python's ``Fraction`` to GMPY's ``mpz``. """ + if a.denominator == 1: + return MPZ(a.numerator) + + def from_QQ_python(K1, a, K0): + """Convert Python's ``Fraction`` to GMPY's ``mpz``. """ + if a.denominator == 1: + return MPZ(a.numerator) + + def from_FF_gmpy(K1, a, K0): + """Convert ``ModularInteger(mpz)`` to GMPY's ``mpz``. """ + return MPZ(K0.to_int(a)) + + def from_ZZ_gmpy(K1, a, K0): + """Convert GMPY's ``mpz`` to GMPY's ``mpz``. """ + return a + + def from_QQ_gmpy(K1, a, K0): + """Convert GMPY ``mpq`` to GMPY's ``mpz``. """ + if a.denominator == 1: + return a.numerator + + def from_RealField(K1, a, K0): + """Convert mpmath's ``mpf`` to GMPY's ``mpz``. """ + p, q = K0.to_rational(a) + + if q == 1: + # XXX: If MPZ is flint.fmpz and p is a gmpy2.mpz, then we need + # to convert via int because fmpz and mpz do not know about each + # other. + return MPZ(int(p)) + + def from_GaussianIntegerRing(K1, a, K0): + if a.y == 0: + return a.x + + def from_EX(K1, a, K0): + """Convert ``Expression`` to GMPY's ``mpz``. """ + if a.is_Integer: + return K1.from_sympy(a) + + def gcdex(self, a, b): + """Compute extended GCD of ``a`` and ``b``. """ + h, s, t = gcdex(a, b) + # XXX: This conditional logic should be handled somewhere else. + if GROUND_TYPES == 'gmpy': + return s, t, h + else: + return h, s, t + + def gcd(self, a, b): + """Compute GCD of ``a`` and ``b``. """ + return gcd(a, b) + + def lcm(self, a, b): + """Compute LCM of ``a`` and ``b``. """ + return lcm(a, b) + + def sqrt(self, a): + """Compute square root of ``a``. """ + return sqrt(a) + + def is_square(self, a): + """Return ``True`` if ``a`` is a square. + + Explanation + =========== + An integer is a square if and only if there exists an integer + ``b`` such that ``b * b == a``. + """ + return is_square(a) + + def exsqrt(self, a): + """Non-negative square root of ``a`` if ``a`` is a square. + + See also + ======== + is_square + """ + if a < 0: + return None + root, rem = sqrtrem(a) + if rem != 0: + return None + return root + + def factorial(self, a): + """Compute factorial of ``a``. """ + return factorial(a) + + +ZZ = IntegerRing() diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/modularinteger.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/modularinteger.py new file mode 100644 index 0000000000000000000000000000000000000000..39a0237563c69a77e4736466d1ebcaa7ca39485f --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/modularinteger.py @@ -0,0 +1,237 @@ +"""Implementation of :class:`ModularInteger` class. """ + +from __future__ import annotations +from typing import Any + +import operator + +from sympy.polys.polyutils import PicklableWithSlots +from sympy.polys.polyerrors import CoercionFailed +from sympy.polys.domains.domainelement import DomainElement + +from sympy.utilities import public +from sympy.utilities.exceptions import sympy_deprecation_warning + +@public +class ModularInteger(PicklableWithSlots, DomainElement): + """A class representing a modular integer. """ + + mod, dom, sym, _parent = None, None, None, None + + __slots__ = ('val',) + + def parent(self): + return self._parent + + def __init__(self, val): + if isinstance(val, self.__class__): + self.val = val.val % self.mod + else: + self.val = self.dom.convert(val) % self.mod + + def modulus(self): + return self.mod + + def __hash__(self): + return hash((self.val, self.mod)) + + def __repr__(self): + return "%s(%s)" % (self.__class__.__name__, self.val) + + def __str__(self): + return "%s mod %s" % (self.val, self.mod) + + def __int__(self): + return int(self.val) + + def to_int(self): + + sympy_deprecation_warning( + """ModularInteger.to_int() is deprecated. + + Use int(a) or K = GF(p) and K.to_int(a) instead of a.to_int(). + """, + deprecated_since_version="1.13", + active_deprecations_target="modularinteger-to-int", + ) + + if self.sym: + if self.val <= self.mod // 2: + return self.val + else: + return self.val - self.mod + else: + return self.val + + def __pos__(self): + return self + + def __neg__(self): + return self.__class__(-self.val) + + @classmethod + def _get_val(cls, other): + if isinstance(other, cls): + return other.val + else: + try: + return cls.dom.convert(other) + except CoercionFailed: + return None + + def __add__(self, other): + val = self._get_val(other) + + if val is not None: + return self.__class__(self.val + val) + else: + return NotImplemented + + def __radd__(self, other): + return self.__add__(other) + + def __sub__(self, other): + val = self._get_val(other) + + if val is not None: + return self.__class__(self.val - val) + else: + return NotImplemented + + def __rsub__(self, other): + return (-self).__add__(other) + + def __mul__(self, other): + val = self._get_val(other) + + if val is not None: + return self.__class__(self.val * val) + else: + return NotImplemented + + def __rmul__(self, other): + return self.__mul__(other) + + def __truediv__(self, other): + val = self._get_val(other) + + if val is not None: + return self.__class__(self.val * self._invert(val)) + else: + return NotImplemented + + def __rtruediv__(self, other): + return self.invert().__mul__(other) + + def __mod__(self, other): + val = self._get_val(other) + + if val is not None: + return self.__class__(self.val % val) + else: + return NotImplemented + + def __rmod__(self, other): + val = self._get_val(other) + + if val is not None: + return self.__class__(val % self.val) + else: + return NotImplemented + + def __pow__(self, exp): + if not exp: + return self.__class__(self.dom.one) + + if exp < 0: + val, exp = self.invert().val, -exp + else: + val = self.val + + return self.__class__(pow(val, int(exp), self.mod)) + + def _compare(self, other, op): + val = self._get_val(other) + + if val is None: + return NotImplemented + + return op(self.val, val % self.mod) + + def _compare_deprecated(self, other, op): + val = self._get_val(other) + + if val is None: + return NotImplemented + + sympy_deprecation_warning( + """Ordered comparisons with modular integers are deprecated. + + Use e.g. int(a) < int(b) instead of a < b. + """, + deprecated_since_version="1.13", + active_deprecations_target="modularinteger-compare", + stacklevel=4, + ) + + return op(self.val, val % self.mod) + + def __eq__(self, other): + return self._compare(other, operator.eq) + + def __ne__(self, other): + return self._compare(other, operator.ne) + + def __lt__(self, other): + return self._compare_deprecated(other, operator.lt) + + def __le__(self, other): + return self._compare_deprecated(other, operator.le) + + def __gt__(self, other): + return self._compare_deprecated(other, operator.gt) + + def __ge__(self, other): + return self._compare_deprecated(other, operator.ge) + + def __bool__(self): + return bool(self.val) + + @classmethod + def _invert(cls, value): + return cls.dom.invert(value, cls.mod) + + def invert(self): + return self.__class__(self._invert(self.val)) + +_modular_integer_cache: dict[tuple[Any, Any, Any], type[ModularInteger]] = {} + +def ModularIntegerFactory(_mod, _dom, _sym, parent): + """Create custom class for specific integer modulus.""" + try: + _mod = _dom.convert(_mod) + except CoercionFailed: + ok = False + else: + ok = True + + if not ok or _mod < 1: + raise ValueError("modulus must be a positive integer, got %s" % _mod) + + key = _mod, _dom, _sym + + try: + cls = _modular_integer_cache[key] + except KeyError: + class cls(ModularInteger): + mod, dom, sym = _mod, _dom, _sym + _parent = parent + + if _sym: + cls.__name__ = "SymmetricModularIntegerMod%s" % _mod + else: + cls.__name__ = "ModularIntegerMod%s" % _mod + + _modular_integer_cache[key] = cls + + return cls diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/mpelements.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/mpelements.py new file mode 100644 index 0000000000000000000000000000000000000000..04ae8eaddcbb7fd8fae684374d9d2c05e79f6c7a --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/mpelements.py @@ -0,0 +1,181 @@ +# +# This module is deprecated and should not be used any more. The actual +# implementation of RR and CC now uses mpmath's mpf and mpc types directly. +# +"""Real and complex elements. """ + + +from sympy.external.gmpy import MPQ +from sympy.polys.domains.domainelement import DomainElement +from sympy.utilities import public + +from mpmath.ctx_mp_python import PythonMPContext, _mpf, _mpc, _constant +from mpmath.libmp import (MPZ_ONE, fzero, fone, finf, fninf, fnan, + round_nearest, mpf_mul, repr_dps, int_types, + from_int, from_float, from_str, to_rational) + + +@public +class RealElement(_mpf, DomainElement): + """An element of a real domain. """ + + __slots__ = ('__mpf__',) + + def _set_mpf(self, val): + self.__mpf__ = val + + _mpf_ = property(lambda self: self.__mpf__, _set_mpf) + + def parent(self): + return self.context._parent + +@public +class ComplexElement(_mpc, DomainElement): + """An element of a complex domain. """ + + __slots__ = ('__mpc__',) + + def _set_mpc(self, val): + self.__mpc__ = val + + _mpc_ = property(lambda self: self.__mpc__, _set_mpc) + + def parent(self): + return self.context._parent + +new = object.__new__ + +@public +class MPContext(PythonMPContext): + + def __init__(ctx, prec=53, dps=None, tol=None, real=False): + ctx._prec_rounding = [prec, round_nearest] + + if dps is None: + ctx._set_prec(prec) + else: + ctx._set_dps(dps) + + ctx.mpf = RealElement + ctx.mpc = ComplexElement + ctx.mpf._ctxdata = [ctx.mpf, new, ctx._prec_rounding] + ctx.mpc._ctxdata = [ctx.mpc, new, ctx._prec_rounding] + + if real: + ctx.mpf.context = ctx + else: + ctx.mpc.context = ctx + + ctx.constant = _constant + ctx.constant._ctxdata = [ctx.mpf, new, ctx._prec_rounding] + ctx.constant.context = ctx + + ctx.types = [ctx.mpf, ctx.mpc, ctx.constant] + ctx.trap_complex = True + ctx.pretty = True + + if tol is None: + ctx.tol = ctx._make_tol() + elif tol is False: + ctx.tol = fzero + else: + ctx.tol = ctx._convert_tol(tol) + + ctx.tolerance = ctx.make_mpf(ctx.tol) + + if not ctx.tolerance: + ctx.max_denom = 1000000 + else: + ctx.max_denom = int(1/ctx.tolerance) + + ctx.zero = ctx.make_mpf(fzero) + ctx.one = ctx.make_mpf(fone) + ctx.j = ctx.make_mpc((fzero, fone)) + ctx.inf = ctx.make_mpf(finf) + ctx.ninf = ctx.make_mpf(fninf) + ctx.nan = ctx.make_mpf(fnan) + + def _make_tol(ctx): + hundred = (0, 25, 2, 5) + eps = (0, MPZ_ONE, 1-ctx.prec, 1) + return mpf_mul(hundred, eps) + + def make_tol(ctx): + return ctx.make_mpf(ctx._make_tol()) + + def _convert_tol(ctx, tol): + if isinstance(tol, int_types): + return from_int(tol) + if isinstance(tol, float): + return from_float(tol) + if hasattr(tol, "_mpf_"): + return tol._mpf_ + prec, rounding = ctx._prec_rounding + if isinstance(tol, str): + return from_str(tol, prec, rounding) + raise ValueError("expected a real number, got %s" % tol) + + def _convert_fallback(ctx, x, strings): + raise TypeError("cannot create mpf from " + repr(x)) + + @property + def _repr_digits(ctx): + return repr_dps(ctx._prec) + + @property + def _str_digits(ctx): + return ctx._dps + + def to_rational(ctx, s, limit=True): + p, q = to_rational(s._mpf_) + + # Needed for GROUND_TYPES=flint if gmpy2 is installed because mpmath's + # to_rational() function returns a gmpy2.mpz instance and if MPQ is + # flint.fmpq then MPQ(p, q) will fail. + p = int(p) + + if not limit or q <= ctx.max_denom: + return p, q + + p0, q0, p1, q1 = 0, 1, 1, 0 + n, d = p, q + + while True: + a = n//d + q2 = q0 + a*q1 + if q2 > ctx.max_denom: + break + p0, q0, p1, q1 = p1, q1, p0 + a*p1, q2 + n, d = d, n - a*d + + k = (ctx.max_denom - q0)//q1 + + number = MPQ(p, q) + bound1 = MPQ(p0 + k*p1, q0 + k*q1) + bound2 = MPQ(p1, q1) + + if not bound2 or not bound1: + return p, q + elif abs(bound2 - number) <= abs(bound1 - number): + return bound2.numerator, bound2.denominator + else: + return bound1.numerator, bound1.denominator + + def almosteq(ctx, s, t, rel_eps=None, abs_eps=None): + t = ctx.convert(t) + if abs_eps is None and rel_eps is None: + rel_eps = abs_eps = ctx.tolerance or ctx.make_tol() + if abs_eps is None: + abs_eps = ctx.convert(rel_eps) + elif rel_eps is None: + rel_eps = ctx.convert(abs_eps) + diff = abs(s-t) + if diff <= abs_eps: + return True + abss = abs(s) + abst = abs(t) + if abss < abst: + err = diff/abst + else: + err = diff/abss + return err <= rel_eps diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/old_fractionfield.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/old_fractionfield.py new file mode 100644 index 0000000000000000000000000000000000000000..25d849c39e45259728479ab0305d4956053ae743 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/old_fractionfield.py @@ -0,0 +1,188 @@ +"""Implementation of :class:`FractionField` class. """ + + +from sympy.polys.domains.field import Field +from sympy.polys.domains.compositedomain import CompositeDomain +from sympy.polys.polyclasses import DMF +from sympy.polys.polyerrors import GeneratorsNeeded +from sympy.polys.polyutils import dict_from_basic, basic_from_dict, _dict_reorder +from sympy.utilities import public + +@public +class FractionField(Field, CompositeDomain): + """A class for representing rational function fields. """ + + dtype = DMF + is_FractionField = is_Frac = True + + has_assoc_Ring = True + has_assoc_Field = True + + def __init__(self, dom, *gens): + if not gens: + raise GeneratorsNeeded("generators not specified") + + lev = len(gens) - 1 + self.ngens = len(gens) + + self.zero = self.dtype.zero(lev, dom) + self.one = self.dtype.one(lev, dom) + + self.domain = self.dom = dom + self.symbols = self.gens = gens + + def set_domain(self, dom): + """Make a new fraction field with given domain. """ + return self.__class__(dom, *self.gens) + + def new(self, element): + return self.dtype(element, self.dom, len(self.gens) - 1) + + def __str__(self): + return str(self.dom) + '(' + ','.join(map(str, self.gens)) + ')' + + def __hash__(self): + return hash((self.__class__.__name__, self.dtype, self.dom, self.gens)) + + def __eq__(self, other): + """Returns ``True`` if two domains are equivalent. """ + return isinstance(other, FractionField) and \ + self.dtype == other.dtype and self.dom == other.dom and self.gens == other.gens + + def to_sympy(self, a): + """Convert ``a`` to a SymPy object. """ + return (basic_from_dict(a.numer().to_sympy_dict(), *self.gens) / + basic_from_dict(a.denom().to_sympy_dict(), *self.gens)) + + def from_sympy(self, a): + """Convert SymPy's expression to ``dtype``. """ + p, q = a.as_numer_denom() + + num, _ = dict_from_basic(p, gens=self.gens) + den, _ = dict_from_basic(q, gens=self.gens) + + for k, v in num.items(): + num[k] = self.dom.from_sympy(v) + + for k, v in den.items(): + den[k] = self.dom.from_sympy(v) + + return self((num, den)).cancel() + + def from_ZZ(K1, a, K0): + """Convert a Python ``int`` object to ``dtype``. """ + return K1(K1.dom.convert(a, K0)) + + def from_ZZ_python(K1, a, K0): + """Convert a Python ``int`` object to ``dtype``. """ + return K1(K1.dom.convert(a, K0)) + + def from_QQ_python(K1, a, K0): + """Convert a Python ``Fraction`` object to ``dtype``. """ + return K1(K1.dom.convert(a, K0)) + + def from_ZZ_gmpy(K1, a, K0): + """Convert a GMPY ``mpz`` object to ``dtype``. """ + return K1(K1.dom.convert(a, K0)) + + def from_QQ_gmpy(K1, a, K0): + """Convert a GMPY ``mpq`` object to ``dtype``. """ + return K1(K1.dom.convert(a, K0)) + + def from_RealField(K1, a, K0): + """Convert a mpmath ``mpf`` object to ``dtype``. """ + return K1(K1.dom.convert(a, K0)) + + def from_GlobalPolynomialRing(K1, a, K0): + """Convert a ``DMF`` object to ``dtype``. """ + if K1.gens == K0.gens: + if K1.dom == K0.dom: + return K1(a.to_list()) + else: + return K1(a.convert(K1.dom).to_list()) + else: + monoms, coeffs = _dict_reorder(a.to_dict(), K0.gens, K1.gens) + + if K1.dom != K0.dom: + coeffs = [ K1.dom.convert(c, K0.dom) for c in coeffs ] + + return K1(dict(zip(monoms, coeffs))) + + def from_FractionField(K1, a, K0): + """ + Convert a fraction field element to another fraction field. + + Examples + ======== + + >>> from sympy.polys.polyclasses import DMF + >>> from sympy.polys.domains import ZZ, QQ + >>> from sympy.abc import x + + >>> f = DMF(([ZZ(1), ZZ(2)], [ZZ(1), ZZ(1)]), ZZ) + + >>> QQx = QQ.old_frac_field(x) + >>> ZZx = ZZ.old_frac_field(x) + + >>> QQx.from_FractionField(f, ZZx) + DMF([1, 2], [1, 1], QQ) + + """ + if K1.gens == K0.gens: + if K1.dom == K0.dom: + return a + else: + return K1((a.numer().convert(K1.dom).to_list(), + a.denom().convert(K1.dom).to_list())) + elif set(K0.gens).issubset(K1.gens): + nmonoms, ncoeffs = _dict_reorder( + a.numer().to_dict(), K0.gens, K1.gens) + dmonoms, dcoeffs = _dict_reorder( + a.denom().to_dict(), K0.gens, K1.gens) + + if K1.dom != K0.dom: + ncoeffs = [ K1.dom.convert(c, K0.dom) for c in ncoeffs ] + dcoeffs = [ K1.dom.convert(c, K0.dom) for c in dcoeffs ] + + return K1((dict(zip(nmonoms, ncoeffs)), dict(zip(dmonoms, dcoeffs)))) + + def get_ring(self): + """Returns a ring associated with ``self``. """ + from sympy.polys.domains import PolynomialRing + return PolynomialRing(self.dom, *self.gens) + + def poly_ring(self, *gens): + """Returns a polynomial ring, i.e. `K[X]`. """ + raise NotImplementedError('nested domains not allowed') + + def frac_field(self, *gens): + """Returns a fraction field, i.e. `K(X)`. """ + raise NotImplementedError('nested domains not allowed') + + def is_positive(self, a): + """Returns True if ``a`` is positive. """ + return self.dom.is_positive(a.numer().LC()) + + def is_negative(self, a): + """Returns True if ``a`` is negative. """ + return self.dom.is_negative(a.numer().LC()) + + def is_nonpositive(self, a): + """Returns True if ``a`` is non-positive. """ + return self.dom.is_nonpositive(a.numer().LC()) + + def is_nonnegative(self, a): + """Returns True if ``a`` is non-negative. """ + return self.dom.is_nonnegative(a.numer().LC()) + + def numer(self, a): + """Returns numerator of ``a``. """ + return a.numer() + + def denom(self, a): + """Returns denominator of ``a``. """ + return a.denom() + + def factorial(self, a): + """Returns factorial of ``a``. """ + return self.dtype(self.dom.factorial(a)) diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/old_polynomialring.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/old_polynomialring.py new file mode 100644 index 0000000000000000000000000000000000000000..c29a4529aac3c64b29d8c670ac45b6c100294ced --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/old_polynomialring.py @@ -0,0 +1,490 @@ +"""Implementation of :class:`PolynomialRing` class. """ + + +from sympy.polys.agca.modules import FreeModulePolyRing +from sympy.polys.domains.compositedomain import CompositeDomain +from sympy.polys.domains.old_fractionfield import FractionField +from sympy.polys.domains.ring import Ring +from sympy.polys.orderings import monomial_key, build_product_order +from sympy.polys.polyclasses import DMP, DMF +from sympy.polys.polyerrors import (GeneratorsNeeded, PolynomialError, + CoercionFailed, ExactQuotientFailed, NotReversible) +from sympy.polys.polyutils import dict_from_basic, basic_from_dict, _dict_reorder +from sympy.utilities import public +from sympy.utilities.iterables import iterable + + +@public +class PolynomialRingBase(Ring, CompositeDomain): + """ + Base class for generalized polynomial rings. + + This base class should be used for uniform access to generalized polynomial + rings. Subclasses only supply information about the element storage etc. + + Do not instantiate. + """ + + has_assoc_Ring = True + has_assoc_Field = True + + default_order = "grevlex" + + def __init__(self, dom, *gens, **opts): + if not gens: + raise GeneratorsNeeded("generators not specified") + + lev = len(gens) - 1 + self.ngens = len(gens) + + self.zero = self.dtype.zero(lev, dom) + self.one = self.dtype.one(lev, dom) + + self.domain = self.dom = dom + self.symbols = self.gens = gens + # NOTE 'order' may not be set if inject was called through CompositeDomain + self.order = opts.get('order', monomial_key(self.default_order)) + + def set_domain(self, dom): + """Return a new polynomial ring with given domain. """ + return self.__class__(dom, *self.gens, order=self.order) + + def new(self, element): + return self.dtype(element, self.dom, len(self.gens) - 1) + + def _ground_new(self, element): + return self.one.ground_new(element) + + def _from_dict(self, element): + return DMP.from_dict(element, len(self.gens) - 1, self.dom) + + def __str__(self): + s_order = str(self.order) + orderstr = ( + " order=" + s_order) if s_order != self.default_order else "" + return str(self.dom) + '[' + ','.join(map(str, self.gens)) + orderstr + ']' + + def __hash__(self): + return hash((self.__class__.__name__, self.dtype, self.dom, + self.gens, self.order)) + + def __eq__(self, other): + """Returns ``True`` if two domains are equivalent. """ + return isinstance(other, PolynomialRingBase) and \ + self.dtype == other.dtype and self.dom == other.dom and \ + self.gens == other.gens and self.order == other.order + + def from_ZZ(K1, a, K0): + """Convert a Python ``int`` object to ``dtype``. """ + return K1._ground_new(K1.dom.convert(a, K0)) + + def from_ZZ_python(K1, a, K0): + """Convert a Python ``int`` object to ``dtype``. """ + return K1._ground_new(K1.dom.convert(a, K0)) + + def from_QQ(K1, a, K0): + """Convert a Python ``Fraction`` object to ``dtype``. """ + return K1._ground_new(K1.dom.convert(a, K0)) + + def from_QQ_python(K1, a, K0): + """Convert a Python ``Fraction`` object to ``dtype``. """ + return K1._ground_new(K1.dom.convert(a, K0)) + + def from_ZZ_gmpy(K1, a, K0): + """Convert a GMPY ``mpz`` object to ``dtype``. """ + return K1._ground_new(K1.dom.convert(a, K0)) + + def from_QQ_gmpy(K1, a, K0): + """Convert a GMPY ``mpq`` object to ``dtype``. """ + return K1._ground_new(K1.dom.convert(a, K0)) + + def from_RealField(K1, a, K0): + """Convert a mpmath ``mpf`` object to ``dtype``. """ + return K1._ground_new(K1.dom.convert(a, K0)) + + def from_AlgebraicField(K1, a, K0): + """Convert a ``ANP`` object to ``dtype``. """ + if K1.dom == K0: + return K1._ground_new(a) + + def from_PolynomialRing(K1, a, K0): + """Convert a ``PolyElement`` object to ``dtype``. """ + if K1.gens == K0.symbols: + if K1.dom == K0.dom: + return K1(dict(a)) # set the correct ring + else: + convert_dom = lambda c: K1.dom.convert_from(c, K0.dom) + return K1._from_dict({m: convert_dom(c) for m, c in a.items()}) + else: + monoms, coeffs = _dict_reorder(a.to_dict(), K0.symbols, K1.gens) + + if K1.dom != K0.dom: + coeffs = [ K1.dom.convert(c, K0.dom) for c in coeffs ] + + return K1._from_dict(dict(zip(monoms, coeffs))) + + def from_GlobalPolynomialRing(K1, a, K0): + """Convert a ``DMP`` object to ``dtype``. """ + if K1.gens == K0.gens: + if K1.dom != K0.dom: + a = a.convert(K1.dom) + return K1(a.to_list()) + else: + monoms, coeffs = _dict_reorder(a.to_dict(), K0.gens, K1.gens) + + if K1.dom != K0.dom: + coeffs = [ K1.dom.convert(c, K0.dom) for c in coeffs ] + + return K1(dict(zip(monoms, coeffs))) + + def get_field(self): + """Returns a field associated with ``self``. """ + return FractionField(self.dom, *self.gens) + + def poly_ring(self, *gens): + """Returns a polynomial ring, i.e. ``K[X]``. """ + raise NotImplementedError('nested domains not allowed') + + def frac_field(self, *gens): + """Returns a fraction field, i.e. ``K(X)``. """ + raise NotImplementedError('nested domains not allowed') + + def revert(self, a): + try: + return self.exquo(self.one, a) + except (ExactQuotientFailed, ZeroDivisionError): + raise NotReversible('%s is not a unit' % a) + + def gcdex(self, a, b): + """Extended GCD of ``a`` and ``b``. """ + return a.gcdex(b) + + def gcd(self, a, b): + """Returns GCD of ``a`` and ``b``. """ + return a.gcd(b) + + def lcm(self, a, b): + """Returns LCM of ``a`` and ``b``. """ + return a.lcm(b) + + def factorial(self, a): + """Returns factorial of ``a``. """ + return self.dtype(self.dom.factorial(a)) + + def _vector_to_sdm(self, v, order): + """ + For internal use by the modules class. + + Convert an iterable of elements of this ring into a sparse distributed + module element. + """ + raise NotImplementedError + + def _sdm_to_dics(self, s, n): + """Helper for _sdm_to_vector.""" + from sympy.polys.distributedmodules import sdm_to_dict + dic = sdm_to_dict(s) + res = [{} for _ in range(n)] + for k, v in dic.items(): + res[k[0]][k[1:]] = v + return res + + def _sdm_to_vector(self, s, n): + """ + For internal use by the modules class. + + Convert a sparse distributed module into a list of length ``n``. + + Examples + ======== + + >>> from sympy import QQ, ilex + >>> from sympy.abc import x, y + >>> R = QQ.old_poly_ring(x, y, order=ilex) + >>> L = [((1, 1, 1), QQ(1)), ((0, 1, 0), QQ(1)), ((0, 0, 1), QQ(2))] + >>> R._sdm_to_vector(L, 2) + [DMF([[1], [2, 0]], [[1]], QQ), DMF([[1, 0], []], [[1]], QQ)] + """ + dics = self._sdm_to_dics(s, n) + # NOTE this works for global and local rings! + return [self(x) for x in dics] + + def free_module(self, rank): + """ + Generate a free module of rank ``rank`` over ``self``. + + Examples + ======== + + >>> from sympy.abc import x + >>> from sympy import QQ + >>> QQ.old_poly_ring(x).free_module(2) + QQ[x]**2 + """ + return FreeModulePolyRing(self, rank) + + +def _vector_to_sdm_helper(v, order): + """Helper method for common code in Global and Local poly rings.""" + from sympy.polys.distributedmodules import sdm_from_dict + d = {} + for i, e in enumerate(v): + for key, value in e.to_dict().items(): + d[(i,) + key] = value + return sdm_from_dict(d, order) + + +@public +class GlobalPolynomialRing(PolynomialRingBase): + """A true polynomial ring, with objects DMP. """ + + is_PolynomialRing = is_Poly = True + dtype = DMP + + def new(self, element): + if isinstance(element, dict): + return DMP.from_dict(element, len(self.gens) - 1, self.dom) + elif element in self.dom: + return self._ground_new(self.dom.convert(element)) + else: + return self.dtype(element, self.dom, len(self.gens) - 1) + + def from_FractionField(K1, a, K0): + """ + Convert a ``DMF`` object to ``DMP``. + + Examples + ======== + + >>> from sympy.polys.polyclasses import DMP, DMF + >>> from sympy.polys.domains import ZZ + >>> from sympy.abc import x + + >>> f = DMF(([ZZ(1), ZZ(1)], [ZZ(1)]), ZZ) + >>> K = ZZ.old_frac_field(x) + + >>> F = ZZ.old_poly_ring(x).from_FractionField(f, K) + + >>> F == DMP([ZZ(1), ZZ(1)], ZZ) + True + >>> type(F) # doctest: +SKIP + + + """ + if a.denom().is_one: + return K1.from_GlobalPolynomialRing(a.numer(), K0) + + def to_sympy(self, a): + """Convert ``a`` to a SymPy object. """ + return basic_from_dict(a.to_sympy_dict(), *self.gens) + + def from_sympy(self, a): + """Convert SymPy's expression to ``dtype``. """ + try: + rep, _ = dict_from_basic(a, gens=self.gens) + except PolynomialError: + raise CoercionFailed("Cannot convert %s to type %s" % (a, self)) + + for k, v in rep.items(): + rep[k] = self.dom.from_sympy(v) + + return DMP.from_dict(rep, self.ngens - 1, self.dom) + + def is_positive(self, a): + """Returns True if ``LC(a)`` is positive. """ + return self.dom.is_positive(a.LC()) + + def is_negative(self, a): + """Returns True if ``LC(a)`` is negative. """ + return self.dom.is_negative(a.LC()) + + def is_nonpositive(self, a): + """Returns True if ``LC(a)`` is non-positive. """ + return self.dom.is_nonpositive(a.LC()) + + def is_nonnegative(self, a): + """Returns True if ``LC(a)`` is non-negative. """ + return self.dom.is_nonnegative(a.LC()) + + def _vector_to_sdm(self, v, order): + """ + Examples + ======== + + >>> from sympy import lex, QQ + >>> from sympy.abc import x, y + >>> R = QQ.old_poly_ring(x, y) + >>> f = R.convert(x + 2*y) + >>> g = R.convert(x * y) + >>> R._vector_to_sdm([f, g], lex) + [((1, 1, 1), 1), ((0, 1, 0), 1), ((0, 0, 1), 2)] + """ + return _vector_to_sdm_helper(v, order) + + +class GeneralizedPolynomialRing(PolynomialRingBase): + """A generalized polynomial ring, with objects DMF. """ + + dtype = DMF + + def new(self, a): + """Construct an element of ``self`` domain from ``a``. """ + res = self.dtype(a, self.dom, len(self.gens) - 1) + + # make sure res is actually in our ring + if res.denom().terms(order=self.order)[0][0] != (0,)*len(self.gens): + from sympy.printing.str import sstr + raise CoercionFailed("denominator %s not allowed in %s" + % (sstr(res), self)) + return res + + def __contains__(self, a): + try: + a = self.convert(a) + except CoercionFailed: + return False + return a.denom().terms(order=self.order)[0][0] == (0,)*len(self.gens) + + def to_sympy(self, a): + """Convert ``a`` to a SymPy object. """ + return (basic_from_dict(a.numer().to_sympy_dict(), *self.gens) / + basic_from_dict(a.denom().to_sympy_dict(), *self.gens)) + + def from_sympy(self, a): + """Convert SymPy's expression to ``dtype``. """ + p, q = a.as_numer_denom() + + num, _ = dict_from_basic(p, gens=self.gens) + den, _ = dict_from_basic(q, gens=self.gens) + + for k, v in num.items(): + num[k] = self.dom.from_sympy(v) + + for k, v in den.items(): + den[k] = self.dom.from_sympy(v) + + return self((num, den)).cancel() + + def exquo(self, a, b): + """Exact quotient of ``a`` and ``b``. """ + # Elements are DMF that will always divide (except 0). The result is + # not guaranteed to be in this ring, so we have to check that. + r = a / b + + try: + r = self.new((r.num, r.den)) + except CoercionFailed: + raise ExactQuotientFailed(a, b, self) + + return r + + def from_FractionField(K1, a, K0): + dmf = K1.get_field().from_FractionField(a, K0) + return K1((dmf.num, dmf.den)) + + def _vector_to_sdm(self, v, order): + """ + Turn an iterable into a sparse distributed module. + + Note that the vector is multiplied by a unit first to make all entries + polynomials. + + Examples + ======== + + >>> from sympy import ilex, QQ + >>> from sympy.abc import x, y + >>> R = QQ.old_poly_ring(x, y, order=ilex) + >>> f = R.convert((x + 2*y) / (1 + x)) + >>> g = R.convert(x * y) + >>> R._vector_to_sdm([f, g], ilex) + [((0, 0, 1), 2), ((0, 1, 0), 1), ((1, 1, 1), 1), ((1, + 2, 1), 1)] + """ + # NOTE this is quite inefficient... + u = self.one.numer() + for x in v: + u *= x.denom() + return _vector_to_sdm_helper([x.numer()*u/x.denom() for x in v], order) + + +@public +def PolynomialRing(dom, *gens, **opts): + r""" + Create a generalized multivariate polynomial ring. + + A generalized polynomial ring is defined by a ground field `K`, a set + of generators (typically `x_1, \ldots, x_n`) and a monomial order `<`. + The monomial order can be global, local or mixed. In any case it induces + a total ordering on the monomials, and there exists for every (non-zero) + polynomial `f \in K[x_1, \ldots, x_n]` a well-defined "leading monomial" + `LM(f) = LM(f, >)`. One can then define a multiplicative subset + `S = S_> = \{f \in K[x_1, \ldots, x_n] | LM(f) = 1\}`. The generalized + polynomial ring corresponding to the monomial order is + `R = S^{-1}K[x_1, \ldots, x_n]`. + + If `>` is a so-called global order, that is `1` is the smallest monomial, + then we just have `S = K` and `R = K[x_1, \ldots, x_n]`. + + Examples + ======== + + A few examples may make this clearer. + + >>> from sympy.abc import x, y + >>> from sympy import QQ + + Our first ring uses global lexicographic order. + + >>> R1 = QQ.old_poly_ring(x, y, order=(("lex", x, y),)) + + The second ring uses local lexicographic order. Note that when using a + single (non-product) order, you can just specify the name and omit the + variables: + + >>> R2 = QQ.old_poly_ring(x, y, order="ilex") + + The third and fourth rings use a mixed orders: + + >>> o1 = (("ilex", x), ("lex", y)) + >>> o2 = (("lex", x), ("ilex", y)) + >>> R3 = QQ.old_poly_ring(x, y, order=o1) + >>> R4 = QQ.old_poly_ring(x, y, order=o2) + + We will investigate what elements of `K(x, y)` are contained in the various + rings. + + >>> L = [x, 1/x, y/(1 + x), 1/(1 + y), 1/(1 + x*y)] + >>> test = lambda R: [f in R for f in L] + + The first ring is just `K[x, y]`: + + >>> test(R1) + [True, False, False, False, False] + + The second ring is R1 localised at the maximal ideal (x, y): + + >>> test(R2) + [True, False, True, True, True] + + The third ring is R1 localised at the prime ideal (x): + + >>> test(R3) + [True, False, True, False, True] + + Finally the fourth ring is R1 localised at `S = K[x, y] \setminus yK[y]`: + + >>> test(R4) + [True, False, False, True, False] + """ + + order = opts.get("order", GeneralizedPolynomialRing.default_order) + if iterable(order): + order = build_product_order(order, gens) + order = monomial_key(order) + opts['order'] = order + + if order.is_global: + return GlobalPolynomialRing(dom, *gens, **opts) + else: + return GeneralizedPolynomialRing(dom, *gens, **opts) diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/polynomialring.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/polynomialring.py new file mode 100644 index 0000000000000000000000000000000000000000..daccdcdede4d409e995a79540b0c3f9e8017d2d9 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/polynomialring.py @@ -0,0 +1,203 @@ +"""Implementation of :class:`PolynomialRing` class. """ + + +from sympy.polys.domains.ring import Ring +from sympy.polys.domains.compositedomain import CompositeDomain + +from sympy.polys.polyerrors import CoercionFailed, GeneratorsError +from sympy.utilities import public + +@public +class PolynomialRing(Ring, CompositeDomain): + """A class for representing multivariate polynomial rings. """ + + is_PolynomialRing = is_Poly = True + + has_assoc_Ring = True + has_assoc_Field = True + + def __init__(self, domain_or_ring, symbols=None, order=None): + from sympy.polys.rings import PolyRing + + if isinstance(domain_or_ring, PolyRing) and symbols is None and order is None: + ring = domain_or_ring + else: + ring = PolyRing(symbols, domain_or_ring, order) + + self.ring = ring + self.dtype = ring.dtype + + self.gens = ring.gens + self.ngens = ring.ngens + self.symbols = ring.symbols + self.domain = ring.domain + + + if symbols: + if ring.domain.is_Field and ring.domain.is_Exact and len(symbols)==1: + self.is_PID = True + + # TODO: remove this + self.dom = self.domain + + def new(self, element): + return self.ring.ring_new(element) + + def of_type(self, element): + """Check if ``a`` is of type ``dtype``. """ + return self.ring.is_element(element) + + @property + def zero(self): + return self.ring.zero + + @property + def one(self): + return self.ring.one + + @property + def order(self): + return self.ring.order + + def __str__(self): + return str(self.domain) + '[' + ','.join(map(str, self.symbols)) + ']' + + def __hash__(self): + return hash((self.__class__.__name__, self.ring, self.domain, self.symbols)) + + def __eq__(self, other): + """Returns `True` if two domains are equivalent. """ + if not isinstance(other, PolynomialRing): + return NotImplemented + return self.ring == other.ring + + def is_unit(self, a): + """Returns ``True`` if ``a`` is a unit of ``self``""" + if not a.is_ground: + return False + K = self.domain + return K.is_unit(K.convert_from(a, self)) + + def canonical_unit(self, a): + u = self.domain.canonical_unit(a.LC) + return self.ring.ground_new(u) + + def to_sympy(self, a): + """Convert `a` to a SymPy object. """ + return a.as_expr() + + def from_sympy(self, a): + """Convert SymPy's expression to `dtype`. """ + return self.ring.from_expr(a) + + def from_ZZ(K1, a, K0): + """Convert a Python `int` object to `dtype`. """ + return K1(K1.domain.convert(a, K0)) + + def from_ZZ_python(K1, a, K0): + """Convert a Python `int` object to `dtype`. """ + return K1(K1.domain.convert(a, K0)) + + def from_QQ(K1, a, K0): + """Convert a Python `Fraction` object to `dtype`. """ + return K1(K1.domain.convert(a, K0)) + + def from_QQ_python(K1, a, K0): + """Convert a Python `Fraction` object to `dtype`. """ + return K1(K1.domain.convert(a, K0)) + + def from_ZZ_gmpy(K1, a, K0): + """Convert a GMPY `mpz` object to `dtype`. """ + return K1(K1.domain.convert(a, K0)) + + def from_QQ_gmpy(K1, a, K0): + """Convert a GMPY `mpq` object to `dtype`. """ + return K1(K1.domain.convert(a, K0)) + + def from_GaussianIntegerRing(K1, a, K0): + """Convert a `GaussianInteger` object to `dtype`. """ + return K1(K1.domain.convert(a, K0)) + + def from_GaussianRationalField(K1, a, K0): + """Convert a `GaussianRational` object to `dtype`. """ + return K1(K1.domain.convert(a, K0)) + + def from_RealField(K1, a, K0): + """Convert a mpmath `mpf` object to `dtype`. """ + return K1(K1.domain.convert(a, K0)) + + def from_ComplexField(K1, a, K0): + """Convert a mpmath `mpf` object to `dtype`. """ + return K1(K1.domain.convert(a, K0)) + + def from_AlgebraicField(K1, a, K0): + """Convert an algebraic number to ``dtype``. """ + if K1.domain != K0: + a = K1.domain.convert_from(a, K0) + if a is not None: + return K1.new(a) + + def from_PolynomialRing(K1, a, K0): + """Convert a polynomial to ``dtype``. """ + try: + return a.set_ring(K1.ring) + except (CoercionFailed, GeneratorsError): + return None + + def from_FractionField(K1, a, K0): + """Convert a rational function to ``dtype``. """ + if K1.domain == K0: + return K1.ring.from_list([a]) + + q, r = K0.numer(a).div(K0.denom(a)) + + if r.is_zero: + return K1.from_PolynomialRing(q, K0.field.ring.to_domain()) + else: + return None + + def from_GlobalPolynomialRing(K1, a, K0): + """Convert from old poly ring to ``dtype``. """ + if K1.symbols == K0.gens: + ad = a.to_dict() + if K1.domain != K0.domain: + ad = {m: K1.domain.convert(c) for m, c in ad.items()} + return K1(ad) + elif a.is_ground and K0.domain == K1: + return K1.convert_from(a.to_list()[0], K0.domain) + + def get_field(self): + """Returns a field associated with `self`. """ + return self.ring.to_field().to_domain() + + def is_positive(self, a): + """Returns True if `LC(a)` is positive. """ + return self.domain.is_positive(a.LC) + + def is_negative(self, a): + """Returns True if `LC(a)` is negative. """ + return self.domain.is_negative(a.LC) + + def is_nonpositive(self, a): + """Returns True if `LC(a)` is non-positive. """ + return self.domain.is_nonpositive(a.LC) + + def is_nonnegative(self, a): + """Returns True if `LC(a)` is non-negative. """ + return self.domain.is_nonnegative(a.LC) + + def gcdex(self, a, b): + """Extended GCD of `a` and `b`. """ + return a.gcdex(b) + + def gcd(self, a, b): + """Returns GCD of `a` and `b`. """ + return a.gcd(b) + + def lcm(self, a, b): + """Returns LCM of `a` and `b`. """ + return a.lcm(b) + + def factorial(self, a): + """Returns factorial of `a`. """ + return self.dtype(self.domain.factorial(a)) diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/pythonfinitefield.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/pythonfinitefield.py new file mode 100644 index 0000000000000000000000000000000000000000..44baa4f6d1b43317283041206eaa43e06a5cc8db --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/pythonfinitefield.py @@ -0,0 +1,16 @@ +"""Implementation of :class:`PythonFiniteField` class. """ + + +from sympy.polys.domains.finitefield import FiniteField +from sympy.polys.domains.pythonintegerring import PythonIntegerRing + +from sympy.utilities import public + +@public +class PythonFiniteField(FiniteField): + """Finite field based on Python's integers. """ + + alias = 'FF_python' + + def __init__(self, mod, symmetric=True): + super().__init__(mod, PythonIntegerRing(), symmetric) diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/pythonintegerring.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/pythonintegerring.py new file mode 100644 index 0000000000000000000000000000000000000000..81ee9637a4ebcfaf3c5f11d12c18265305984c25 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/pythonintegerring.py @@ -0,0 +1,98 @@ +"""Implementation of :class:`PythonIntegerRing` class. """ + + +from sympy.core.numbers import int_valued +from sympy.polys.domains.groundtypes import ( + PythonInteger, SymPyInteger, sqrt as python_sqrt, + factorial as python_factorial, python_gcdex, python_gcd, python_lcm, +) +from sympy.polys.domains.integerring import IntegerRing +from sympy.polys.polyerrors import CoercionFailed +from sympy.utilities import public + +@public +class PythonIntegerRing(IntegerRing): + """Integer ring based on Python's ``int`` type. + + This will be used as :ref:`ZZ` if ``gmpy`` and ``gmpy2`` are not + installed. Elements are instances of the standard Python ``int`` type. + """ + + dtype = PythonInteger + zero = dtype(0) + one = dtype(1) + alias = 'ZZ_python' + + def __init__(self): + """Allow instantiation of this domain. """ + + def to_sympy(self, a): + """Convert ``a`` to a SymPy object. """ + return SymPyInteger(a) + + def from_sympy(self, a): + """Convert SymPy's Integer to ``dtype``. """ + if a.is_Integer: + return PythonInteger(a.p) + elif int_valued(a): + return PythonInteger(int(a)) + else: + raise CoercionFailed("expected an integer, got %s" % a) + + def from_FF_python(K1, a, K0): + """Convert ``ModularInteger(int)`` to Python's ``int``. """ + return K0.to_int(a) + + def from_ZZ_python(K1, a, K0): + """Convert Python's ``int`` to Python's ``int``. """ + return a + + def from_QQ(K1, a, K0): + """Convert Python's ``Fraction`` to Python's ``int``. """ + if a.denominator == 1: + return a.numerator + + def from_QQ_python(K1, a, K0): + """Convert Python's ``Fraction`` to Python's ``int``. """ + if a.denominator == 1: + return a.numerator + + def from_FF_gmpy(K1, a, K0): + """Convert ``ModularInteger(mpz)`` to Python's ``int``. """ + return PythonInteger(K0.to_int(a)) + + def from_ZZ_gmpy(K1, a, K0): + """Convert GMPY's ``mpz`` to Python's ``int``. """ + return PythonInteger(a) + + def from_QQ_gmpy(K1, a, K0): + """Convert GMPY's ``mpq`` to Python's ``int``. """ + if a.denom() == 1: + return PythonInteger(a.numer()) + + def from_RealField(K1, a, K0): + """Convert mpmath's ``mpf`` to Python's ``int``. """ + p, q = K0.to_rational(a) + + if q == 1: + return PythonInteger(p) + + def gcdex(self, a, b): + """Compute extended GCD of ``a`` and ``b``. """ + return python_gcdex(a, b) + + def gcd(self, a, b): + """Compute GCD of ``a`` and ``b``. """ + return python_gcd(a, b) + + def lcm(self, a, b): + """Compute LCM of ``a`` and ``b``. """ + return python_lcm(a, b) + + def sqrt(self, a): + """Compute square root of ``a``. """ + return python_sqrt(a) + + def factorial(self, a): + """Compute factorial of ``a``. """ + return python_factorial(a) diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/pythonrational.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/pythonrational.py new file mode 100644 index 0000000000000000000000000000000000000000..87b56d6c929c3ce3ce153dce7b3c210821d706a0 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/pythonrational.py @@ -0,0 +1,22 @@ +""" +Rational number type based on Python integers. + +The PythonRational class from here has been moved to +sympy.external.pythonmpq + +This module is just left here for backwards compatibility. +""" + + +from sympy.core.numbers import Rational +from sympy.core.sympify import _sympy_converter +from sympy.utilities import public +from sympy.external.pythonmpq import PythonMPQ + + +PythonRational = public(PythonMPQ) + + +def sympify_pythonrational(arg): + return Rational(arg.numerator, arg.denominator) +_sympy_converter[PythonRational] = sympify_pythonrational diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/pythonrationalfield.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/pythonrationalfield.py new file mode 100644 index 0000000000000000000000000000000000000000..51afaef636f000855d51a69fb93eb416ae1e5347 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/pythonrationalfield.py @@ -0,0 +1,73 @@ +"""Implementation of :class:`PythonRationalField` class. """ + + +from sympy.polys.domains.groundtypes import PythonInteger, PythonRational, SymPyRational +from sympy.polys.domains.rationalfield import RationalField +from sympy.polys.polyerrors import CoercionFailed +from sympy.utilities import public + +@public +class PythonRationalField(RationalField): + """Rational field based on :ref:`MPQ`. + + This will be used as :ref:`QQ` if ``gmpy`` and ``gmpy2`` are not + installed. Elements are instances of :ref:`MPQ`. + """ + + dtype = PythonRational + zero = dtype(0) + one = dtype(1) + alias = 'QQ_python' + + def __init__(self): + pass + + def get_ring(self): + """Returns ring associated with ``self``. """ + from sympy.polys.domains import PythonIntegerRing + return PythonIntegerRing() + + def to_sympy(self, a): + """Convert `a` to a SymPy object. """ + return SymPyRational(a.numerator, a.denominator) + + def from_sympy(self, a): + """Convert SymPy's Rational to `dtype`. """ + if a.is_Rational: + return PythonRational(a.p, a.q) + elif a.is_Float: + from sympy.polys.domains import RR + p, q = RR.to_rational(a) + return PythonRational(int(p), int(q)) + else: + raise CoercionFailed("expected `Rational` object, got %s" % a) + + def from_ZZ_python(K1, a, K0): + """Convert a Python `int` object to `dtype`. """ + return PythonRational(a) + + def from_QQ_python(K1, a, K0): + """Convert a Python `Fraction` object to `dtype`. """ + return a + + def from_ZZ_gmpy(K1, a, K0): + """Convert a GMPY `mpz` object to `dtype`. """ + return PythonRational(PythonInteger(a)) + + def from_QQ_gmpy(K1, a, K0): + """Convert a GMPY `mpq` object to `dtype`. """ + return PythonRational(PythonInteger(a.numer()), + PythonInteger(a.denom())) + + def from_RealField(K1, a, K0): + """Convert a mpmath `mpf` object to `dtype`. """ + p, q = K0.to_rational(a) + return PythonRational(int(p), int(q)) + + def numer(self, a): + """Returns numerator of `a`. """ + return a.numerator + + def denom(self, a): + """Returns denominator of `a`. """ + return a.denominator diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/quotientring.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/quotientring.py new file mode 100644 index 0000000000000000000000000000000000000000..7e8abf6b210a5627c9c139e41248637c9b88931f --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/quotientring.py @@ -0,0 +1,202 @@ +"""Implementation of :class:`QuotientRing` class.""" + + +from sympy.polys.agca.modules import FreeModuleQuotientRing +from sympy.polys.domains.ring import Ring +from sympy.polys.polyerrors import NotReversible, CoercionFailed +from sympy.utilities import public + +# TODO +# - successive quotients (when quotient ideals are implemented) +# - poly rings over quotients? +# - division by non-units in integral domains? + +@public +class QuotientRingElement: + """ + Class representing elements of (commutative) quotient rings. + + Attributes: + + - ring - containing ring + - data - element of ring.ring (i.e. base ring) representing self + """ + + def __init__(self, ring, data): + self.ring = ring + self.data = data + + def __str__(self): + from sympy.printing.str import sstr + data = self.ring.ring.to_sympy(self.data) + return sstr(data) + " + " + str(self.ring.base_ideal) + + __repr__ = __str__ + + def __bool__(self): + return not self.ring.is_zero(self) + + def __add__(self, om): + if not isinstance(om, self.__class__) or om.ring != self.ring: + try: + om = self.ring.convert(om) + except (NotImplementedError, CoercionFailed): + return NotImplemented + return self.ring(self.data + om.data) + + __radd__ = __add__ + + def __neg__(self): + return self.ring(self.data*self.ring.ring.convert(-1)) + + def __sub__(self, om): + return self.__add__(-om) + + def __rsub__(self, om): + return (-self).__add__(om) + + def __mul__(self, o): + if not isinstance(o, self.__class__): + try: + o = self.ring.convert(o) + except (NotImplementedError, CoercionFailed): + return NotImplemented + return self.ring(self.data*o.data) + + __rmul__ = __mul__ + + def __rtruediv__(self, o): + return self.ring.revert(self)*o + + def __truediv__(self, o): + if not isinstance(o, self.__class__): + try: + o = self.ring.convert(o) + except (NotImplementedError, CoercionFailed): + return NotImplemented + return self.ring.revert(o)*self + + def __pow__(self, oth): + if oth < 0: + return self.ring.revert(self) ** -oth + return self.ring(self.data ** oth) + + def __eq__(self, om): + if not isinstance(om, self.__class__) or om.ring != self.ring: + return False + return self.ring.is_zero(self - om) + + def __ne__(self, om): + return not self == om + + +class QuotientRing(Ring): + """ + Class representing (commutative) quotient rings. + + You should not usually instantiate this by hand, instead use the constructor + from the base ring in the construction. + + >>> from sympy.abc import x + >>> from sympy import QQ + >>> I = QQ.old_poly_ring(x).ideal(x**3 + 1) + >>> QQ.old_poly_ring(x).quotient_ring(I) + QQ[x]/ + + Shorter versions are possible: + + >>> QQ.old_poly_ring(x)/I + QQ[x]/ + + >>> QQ.old_poly_ring(x)/[x**3 + 1] + QQ[x]/ + + Attributes: + + - ring - the base ring + - base_ideal - the ideal used to form the quotient + """ + + has_assoc_Ring = True + has_assoc_Field = False + dtype = QuotientRingElement + + def __init__(self, ring, ideal): + if not ideal.ring == ring: + raise ValueError('Ideal must belong to %s, got %s' % (ring, ideal)) + self.ring = ring + self.base_ideal = ideal + self.zero = self(self.ring.zero) + self.one = self(self.ring.one) + + def __str__(self): + return str(self.ring) + "/" + str(self.base_ideal) + + def __hash__(self): + return hash((self.__class__.__name__, self.dtype, self.ring, self.base_ideal)) + + def new(self, a): + """Construct an element of ``self`` domain from ``a``. """ + if not isinstance(a, self.ring.dtype): + a = self.ring(a) + # TODO optionally disable reduction? + return self.dtype(self, self.base_ideal.reduce_element(a)) + + def __eq__(self, other): + """Returns ``True`` if two domains are equivalent. """ + return isinstance(other, QuotientRing) and \ + self.ring == other.ring and self.base_ideal == other.base_ideal + + def from_ZZ(K1, a, K0): + """Convert a Python ``int`` object to ``dtype``. """ + return K1(K1.ring.convert(a, K0)) + + from_ZZ_python = from_ZZ + from_QQ_python = from_ZZ_python + from_ZZ_gmpy = from_ZZ_python + from_QQ_gmpy = from_ZZ_python + from_RealField = from_ZZ_python + from_GlobalPolynomialRing = from_ZZ_python + from_FractionField = from_ZZ_python + + def from_sympy(self, a): + return self(self.ring.from_sympy(a)) + + def to_sympy(self, a): + return self.ring.to_sympy(a.data) + + def from_QuotientRing(self, a, K0): + if K0 == self: + return a + + def poly_ring(self, *gens): + """Returns a polynomial ring, i.e. ``K[X]``. """ + raise NotImplementedError('nested domains not allowed') + + def frac_field(self, *gens): + """Returns a fraction field, i.e. ``K(X)``. """ + raise NotImplementedError('nested domains not allowed') + + def revert(self, a): + """ + Compute a**(-1), if possible. + """ + I = self.ring.ideal(a.data) + self.base_ideal + try: + return self(I.in_terms_of_generators(1)[0]) + except ValueError: # 1 not in I + raise NotReversible('%s not a unit in %r' % (a, self)) + + def is_zero(self, a): + return self.base_ideal.contains(a.data) + + def free_module(self, rank): + """ + Generate a free module of rank ``rank`` over ``self``. + + >>> from sympy.abc import x + >>> from sympy import QQ + >>> (QQ.old_poly_ring(x)/[x**2 + 1]).free_module(2) + (QQ[x]/)**2 + """ + return FreeModuleQuotientRing(self, rank) diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/rationalfield.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/rationalfield.py new file mode 100644 index 0000000000000000000000000000000000000000..6da570332de8a6d39a21bb3d57447670c7a98441 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/rationalfield.py @@ -0,0 +1,200 @@ +"""Implementation of :class:`RationalField` class. """ + + +from sympy.external.gmpy import MPQ + +from sympy.polys.domains.groundtypes import SymPyRational, is_square, sqrtrem + +from sympy.polys.domains.characteristiczero import CharacteristicZero +from sympy.polys.domains.field import Field +from sympy.polys.domains.simpledomain import SimpleDomain +from sympy.polys.polyerrors import CoercionFailed +from sympy.utilities import public + +@public +class RationalField(Field, CharacteristicZero, SimpleDomain): + r"""Abstract base class for the domain :ref:`QQ`. + + The :py:class:`RationalField` class represents the field of rational + numbers $\mathbb{Q}$ as a :py:class:`~.Domain` in the domain system. + :py:class:`RationalField` is a superclass of + :py:class:`PythonRationalField` and :py:class:`GMPYRationalField` one of + which will be the implementation for :ref:`QQ` depending on whether either + of ``gmpy`` or ``gmpy2`` is installed or not. + + See also + ======== + + Domain + """ + + rep = 'QQ' + alias = 'QQ' + + is_RationalField = is_QQ = True + is_Numerical = True + + has_assoc_Ring = True + has_assoc_Field = True + + dtype = MPQ + zero = dtype(0) + one = dtype(1) + tp = type(one) + + def __init__(self): + pass + + def __eq__(self, other): + """Returns ``True`` if two domains are equivalent. """ + if isinstance(other, RationalField): + return True + else: + return NotImplemented + + def __hash__(self): + """Returns hash code of ``self``. """ + return hash('QQ') + + def get_ring(self): + """Returns ring associated with ``self``. """ + from sympy.polys.domains import ZZ + return ZZ + + def to_sympy(self, a): + """Convert ``a`` to a SymPy object. """ + return SymPyRational(int(a.numerator), int(a.denominator)) + + def from_sympy(self, a): + """Convert SymPy's Integer to ``dtype``. """ + if a.is_Rational: + return MPQ(a.p, a.q) + elif a.is_Float: + from sympy.polys.domains import RR + return MPQ(*map(int, RR.to_rational(a))) + else: + raise CoercionFailed("expected `Rational` object, got %s" % a) + + def algebraic_field(self, *extension, alias=None): + r"""Returns an algebraic field, i.e. `\mathbb{Q}(\alpha, \ldots)`. + + Parameters + ========== + + *extension : One or more :py:class:`~.Expr` + Generators of the extension. These should be expressions that are + algebraic over `\mathbb{Q}`. + + alias : str, :py:class:`~.Symbol`, None, optional (default=None) + If provided, this will be used as the alias symbol for the + primitive element of the returned :py:class:`~.AlgebraicField`. + + Returns + ======= + + :py:class:`~.AlgebraicField` + A :py:class:`~.Domain` representing the algebraic field extension. + + Examples + ======== + + >>> from sympy import QQ, sqrt + >>> QQ.algebraic_field(sqrt(2)) + QQ + """ + from sympy.polys.domains import AlgebraicField + return AlgebraicField(self, *extension, alias=alias) + + def from_AlgebraicField(K1, a, K0): + """Convert a :py:class:`~.ANP` object to :ref:`QQ`. + + See :py:meth:`~.Domain.convert` + """ + if a.is_ground: + return K1.convert(a.LC(), K0.dom) + + def from_ZZ(K1, a, K0): + """Convert a Python ``int`` object to ``dtype``. """ + return MPQ(a) + + def from_ZZ_python(K1, a, K0): + """Convert a Python ``int`` object to ``dtype``. """ + return MPQ(a) + + def from_QQ(K1, a, K0): + """Convert a Python ``Fraction`` object to ``dtype``. """ + return MPQ(a.numerator, a.denominator) + + def from_QQ_python(K1, a, K0): + """Convert a Python ``Fraction`` object to ``dtype``. """ + return MPQ(a.numerator, a.denominator) + + def from_ZZ_gmpy(K1, a, K0): + """Convert a GMPY ``mpz`` object to ``dtype``. """ + return MPQ(a) + + def from_QQ_gmpy(K1, a, K0): + """Convert a GMPY ``mpq`` object to ``dtype``. """ + return a + + def from_GaussianRationalField(K1, a, K0): + """Convert a ``GaussianElement`` object to ``dtype``. """ + if a.y == 0: + return MPQ(a.x) + + def from_RealField(K1, a, K0): + """Convert a mpmath ``mpf`` object to ``dtype``. """ + return MPQ(*map(int, K0.to_rational(a))) + + def exquo(self, a, b): + """Exact quotient of ``a`` and ``b``, implies ``__truediv__``. """ + return MPQ(a) / MPQ(b) + + def quo(self, a, b): + """Quotient of ``a`` and ``b``, implies ``__truediv__``. """ + return MPQ(a) / MPQ(b) + + def rem(self, a, b): + """Remainder of ``a`` and ``b``, implies nothing. """ + return self.zero + + def div(self, a, b): + """Division of ``a`` and ``b``, implies ``__truediv__``. """ + return MPQ(a) / MPQ(b), self.zero + + def numer(self, a): + """Returns numerator of ``a``. """ + return a.numerator + + def denom(self, a): + """Returns denominator of ``a``. """ + return a.denominator + + def is_square(self, a): + """Return ``True`` if ``a`` is a square. + + Explanation + =========== + A rational number is a square if and only if there exists + a rational number ``b`` such that ``b * b == a``. + """ + return is_square(a.numerator) and is_square(a.denominator) + + def exsqrt(self, a): + """Non-negative square root of ``a`` if ``a`` is a square. + + See also + ======== + is_square + """ + if a.numerator < 0: # denominator is always positive + return None + p_sqrt, p_rem = sqrtrem(a.numerator) + if p_rem != 0: + return None + q_sqrt, q_rem = sqrtrem(a.denominator) + if q_rem != 0: + return None + return MPQ(p_sqrt, q_sqrt) + +QQ = RationalField() diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/realfield.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/realfield.py new file mode 100644 index 0000000000000000000000000000000000000000..12f543b2619aa238969ecbe20215d6fd59792904 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/realfield.py @@ -0,0 +1,220 @@ +"""Implementation of :class:`RealField` class. """ + + +from sympy.external.gmpy import SYMPY_INTS, MPQ +from sympy.core.numbers import Float +from sympy.polys.domains.field import Field +from sympy.polys.domains.simpledomain import SimpleDomain +from sympy.polys.domains.characteristiczero import CharacteristicZero +from sympy.polys.polyerrors import CoercionFailed +from sympy.utilities import public + +from mpmath import MPContext +from mpmath.libmp import to_rational as _mpmath_to_rational + + +def to_rational(s, max_denom, limit=True): + + p, q = _mpmath_to_rational(s._mpf_) + + # Needed for GROUND_TYPES=flint if gmpy2 is installed because mpmath's + # to_rational() function returns a gmpy2.mpz instance and if MPQ is + # flint.fmpq then MPQ(p, q) will fail. + p = int(p) + q = int(q) + + if not limit or q <= max_denom: + return p, q + + p0, q0, p1, q1 = 0, 1, 1, 0 + n, d = p, q + + while True: + a = n//d + q2 = q0 + a*q1 + if q2 > max_denom: + break + p0, q0, p1, q1 = p1, q1, p0 + a*p1, q2 + n, d = d, n - a*d + + k = (max_denom - q0)//q1 + + number = MPQ(p, q) + bound1 = MPQ(p0 + k*p1, q0 + k*q1) + bound2 = MPQ(p1, q1) + + if not bound2 or not bound1: + return p, q + elif abs(bound2 - number) <= abs(bound1 - number): + return bound2.numerator, bound2.denominator + else: + return bound1.numerator, bound1.denominator + + +@public +class RealField(Field, CharacteristicZero, SimpleDomain): + """Real numbers up to the given precision. """ + + rep = 'RR' + + is_RealField = is_RR = True + + is_Exact = False + is_Numerical = True + is_PID = False + + has_assoc_Ring = False + has_assoc_Field = True + + _default_precision = 53 + + @property + def has_default_precision(self): + return self.precision == self._default_precision + + @property + def precision(self): + return self._context.prec + + @property + def dps(self): + return self._context.dps + + @property + def tolerance(self): + return self._tolerance + + def __init__(self, prec=None, dps=None, tol=None): + # XXX: The tol parameter is ignored but is kept for now for backwards + # compatibility. + + context = MPContext() + + if prec is None and dps is None: + context.prec = self._default_precision + elif dps is None: + context.prec = prec + elif prec is None: + context.dps = dps + else: + raise TypeError("Cannot set both prec and dps") + + self._context = context + + self._dtype = context.mpf + self.zero = self.dtype(0) + self.one = self.dtype(1) + + # Only max_denom here is used for anything and is only used for + # to_rational. + self._max_denom = max(2**context.prec // 200, 99) + self._tolerance = self.one / self._max_denom + + @property + def tp(self): + # XXX: Domain treats tp as an alias of dtype. Here we need to two + # separate things: dtype is a callable to make/convert instances. + # We use tp with isinstance to check if an object is an instance + # of the domain already. + return self._dtype + + def dtype(self, arg): + # XXX: This is needed because mpmath does not recognise fmpz. + # It might be better to add conversion routines to mpmath and if that + # happens then this can be removed. + if isinstance(arg, SYMPY_INTS): + arg = int(arg) + return self._dtype(arg) + + def __eq__(self, other): + return isinstance(other, RealField) and self.precision == other.precision + + def __hash__(self): + return hash((self.__class__.__name__, self._dtype, self.precision)) + + def to_sympy(self, element): + """Convert ``element`` to SymPy number. """ + return Float(element, self.dps) + + def from_sympy(self, expr): + """Convert SymPy's number to ``dtype``. """ + number = expr.evalf(n=self.dps) + + if number.is_Number: + return self.dtype(number) + else: + raise CoercionFailed("expected real number, got %s" % expr) + + def from_ZZ(self, element, base): + return self.dtype(element) + + def from_ZZ_python(self, element, base): + return self.dtype(element) + + def from_ZZ_gmpy(self, element, base): + return self.dtype(int(element)) + + # XXX: We need to convert the denominators to int here because mpmath does + # not recognise mpz. Ideally mpmath would handle this and if it changed to + # do so then the calls to int here could be removed. + + def from_QQ(self, element, base): + return self.dtype(element.numerator) / int(element.denominator) + + def from_QQ_python(self, element, base): + return self.dtype(element.numerator) / int(element.denominator) + + def from_QQ_gmpy(self, element, base): + return self.dtype(int(element.numerator)) / int(element.denominator) + + def from_AlgebraicField(self, element, base): + return self.from_sympy(base.to_sympy(element).evalf(self.dps)) + + def from_RealField(self, element, base): + return self.dtype(element) + + def from_ComplexField(self, element, base): + if not element.imag: + return self.dtype(element.real) + + def to_rational(self, element, limit=True): + """Convert a real number to rational number. """ + return to_rational(element, self._max_denom, limit=limit) + + def get_ring(self): + """Returns a ring associated with ``self``. """ + return self + + def get_exact(self): + """Returns an exact domain associated with ``self``. """ + from sympy.polys.domains import QQ + return QQ + + def gcd(self, a, b): + """Returns GCD of ``a`` and ``b``. """ + return self.one + + def lcm(self, a, b): + """Returns LCM of ``a`` and ``b``. """ + return a*b + + def almosteq(self, a, b, tolerance=None): + """Check if ``a`` and ``b`` are almost equal. """ + return self._context.almosteq(a, b, tolerance) + + def is_square(self, a): + """Returns ``True`` if ``a >= 0`` and ``False`` otherwise. """ + return a >= 0 + + def exsqrt(self, a): + """Non-negative square root for ``a >= 0`` and ``None`` otherwise. + + Explanation + =========== + The square root may be slightly inaccurate due to floating point + rounding error. + """ + return a ** 0.5 if a >= 0 else None + + +RR = RealField() diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/ring.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/ring.py new file mode 100644 index 0000000000000000000000000000000000000000..c69e6944d8f51e4b319609368a476e6e847ae126 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/ring.py @@ -0,0 +1,118 @@ +"""Implementation of :class:`Ring` class. """ + + +from sympy.polys.domains.domain import Domain +from sympy.polys.polyerrors import ExactQuotientFailed, NotInvertible, NotReversible + +from sympy.utilities import public + +@public +class Ring(Domain): + """Represents a ring domain. """ + + is_Ring = True + + def get_ring(self): + """Returns a ring associated with ``self``. """ + return self + + def exquo(self, a, b): + """Exact quotient of ``a`` and ``b``, implies ``__floordiv__``. """ + if a % b: + raise ExactQuotientFailed(a, b, self) + else: + return a // b + + def quo(self, a, b): + """Quotient of ``a`` and ``b``, implies ``__floordiv__``. """ + return a // b + + def rem(self, a, b): + """Remainder of ``a`` and ``b``, implies ``__mod__``. """ + return a % b + + def div(self, a, b): + """Division of ``a`` and ``b``, implies ``__divmod__``. """ + return divmod(a, b) + + def invert(self, a, b): + """Returns inversion of ``a mod b``. """ + s, t, h = self.gcdex(a, b) + + if self.is_one(h): + return s % b + else: + raise NotInvertible("zero divisor") + + def revert(self, a): + """Returns ``a**(-1)`` if possible. """ + if self.is_one(a) or self.is_one(-a): + return a + else: + raise NotReversible('only units are reversible in a ring') + + def is_unit(self, a): + try: + self.revert(a) + return True + except NotReversible: + return False + + def numer(self, a): + """Returns numerator of ``a``. """ + return a + + def denom(self, a): + """Returns denominator of `a`. """ + return self.one + + def free_module(self, rank): + """ + Generate a free module of rank ``rank`` over self. + + >>> from sympy.abc import x + >>> from sympy import QQ + >>> QQ.old_poly_ring(x).free_module(2) + QQ[x]**2 + """ + raise NotImplementedError + + def ideal(self, *gens): + """ + Generate an ideal of ``self``. + + >>> from sympy.abc import x + >>> from sympy import QQ + >>> QQ.old_poly_ring(x).ideal(x**2) + + """ + from sympy.polys.agca.ideals import ModuleImplementedIdeal + return ModuleImplementedIdeal(self, self.free_module(1).submodule( + *[[x] for x in gens])) + + def quotient_ring(self, e): + """ + Form a quotient ring of ``self``. + + Here ``e`` can be an ideal or an iterable. + + >>> from sympy.abc import x + >>> from sympy import QQ + >>> QQ.old_poly_ring(x).quotient_ring(QQ.old_poly_ring(x).ideal(x**2)) + QQ[x]/ + >>> QQ.old_poly_ring(x).quotient_ring([x**2]) + QQ[x]/ + + The division operator has been overloaded for this: + + >>> QQ.old_poly_ring(x)/[x**2] + QQ[x]/ + """ + from sympy.polys.agca.ideals import Ideal + from sympy.polys.domains.quotientring import QuotientRing + if not isinstance(e, Ideal): + e = self.ideal(*e) + return QuotientRing(self, e) + + def __truediv__(self, e): + return self.quotient_ring(e) diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/domains/simpledomain.py b/.venv/lib/python3.13/site-packages/sympy/polys/domains/simpledomain.py new file mode 100644 index 0000000000000000000000000000000000000000..88cf634555d8bd9229d7fc511af3cf96fececbb8 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/domains/simpledomain.py @@ -0,0 +1,15 @@ +"""Implementation of :class:`SimpleDomain` class. """ + + +from sympy.polys.domains.domain import Domain +from sympy.utilities import public + +@public +class SimpleDomain(Domain): + """Base class for simple domains, e.g. ZZ, QQ. """ + + is_Simple = True + + def inject(self, *gens): + """Inject generators into this domain. """ + return self.poly_ring(*gens) diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/__init__.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_appellseqs.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_appellseqs.py new file mode 100644 index 0000000000000000000000000000000000000000..f4718a2da272ac6f36a968572dc246ebc699e5c4 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_appellseqs.py @@ -0,0 +1,91 @@ +"""Tests for efficient functions for generating Appell sequences.""" +from sympy.core.numbers import Rational as Q +from sympy.polys.polytools import Poly +from sympy.testing.pytest import raises +from sympy.polys.appellseqs import (bernoulli_poly, bernoulli_c_poly, + euler_poly, genocchi_poly, andre_poly) +from sympy.abc import x + +def test_bernoulli_poly(): + raises(ValueError, lambda: bernoulli_poly(-1, x)) + assert bernoulli_poly(1, x, polys=True) == Poly(x - Q(1,2)) + + assert bernoulli_poly(0, x) == 1 + assert bernoulli_poly(1, x) == x - Q(1,2) + assert bernoulli_poly(2, x) == x**2 - x + Q(1,6) + assert bernoulli_poly(3, x) == x**3 - Q(3,2)*x**2 + Q(1,2)*x + assert bernoulli_poly(4, x) == x**4 - 2*x**3 + x**2 - Q(1,30) + assert bernoulli_poly(5, x) == x**5 - Q(5,2)*x**4 + Q(5,3)*x**3 - Q(1,6)*x + assert bernoulli_poly(6, x) == x**6 - 3*x**5 + Q(5,2)*x**4 - Q(1,2)*x**2 + Q(1,42) + + assert bernoulli_poly(1).dummy_eq(x - Q(1,2)) + assert bernoulli_poly(1, polys=True) == Poly(x - Q(1,2)) + +def test_bernoulli_c_poly(): + raises(ValueError, lambda: bernoulli_c_poly(-1, x)) + assert bernoulli_c_poly(1, x, polys=True) == Poly(x, domain='QQ') + + assert bernoulli_c_poly(0, x) == 1 + assert bernoulli_c_poly(1, x) == x + assert bernoulli_c_poly(2, x) == x**2 - Q(1,3) + assert bernoulli_c_poly(3, x) == x**3 - x + assert bernoulli_c_poly(4, x) == x**4 - 2*x**2 + Q(7,15) + assert bernoulli_c_poly(5, x) == x**5 - Q(10,3)*x**3 + Q(7,3)*x + assert bernoulli_c_poly(6, x) == x**6 - 5*x**4 + 7*x**2 - Q(31,21) + + assert bernoulli_c_poly(1).dummy_eq(x) + assert bernoulli_c_poly(1, polys=True) == Poly(x, domain='QQ') + + assert 2**8 * bernoulli_poly(8, (x+1)/2).expand() == bernoulli_c_poly(8, x) + assert 2**9 * bernoulli_poly(9, (x+1)/2).expand() == bernoulli_c_poly(9, x) + +def test_genocchi_poly(): + raises(ValueError, lambda: genocchi_poly(-1, x)) + assert genocchi_poly(2, x, polys=True) == Poly(-2*x + 1) + + assert genocchi_poly(0, x) == 0 + assert genocchi_poly(1, x) == -1 + assert genocchi_poly(2, x) == 1 - 2*x + assert genocchi_poly(3, x) == 3*x - 3*x**2 + assert genocchi_poly(4, x) == -1 + 6*x**2 - 4*x**3 + assert genocchi_poly(5, x) == -5*x + 10*x**3 - 5*x**4 + assert genocchi_poly(6, x) == 3 - 15*x**2 + 15*x**4 - 6*x**5 + + assert genocchi_poly(2).dummy_eq(-2*x + 1) + assert genocchi_poly(2, polys=True) == Poly(-2*x + 1) + + assert 2 * (bernoulli_poly(8, x) - bernoulli_c_poly(8, x)) == genocchi_poly(8, x) + assert 2 * (bernoulli_poly(9, x) - bernoulli_c_poly(9, x)) == genocchi_poly(9, x) + +def test_euler_poly(): + raises(ValueError, lambda: euler_poly(-1, x)) + assert euler_poly(1, x, polys=True) == Poly(x - Q(1,2)) + + assert euler_poly(0, x) == 1 + assert euler_poly(1, x) == x - Q(1,2) + assert euler_poly(2, x) == x**2 - x + assert euler_poly(3, x) == x**3 - Q(3,2)*x**2 + Q(1,4) + assert euler_poly(4, x) == x**4 - 2*x**3 + x + assert euler_poly(5, x) == x**5 - Q(5,2)*x**4 + Q(5,2)*x**2 - Q(1,2) + assert euler_poly(6, x) == x**6 - 3*x**5 + 5*x**3 - 3*x + + assert euler_poly(1).dummy_eq(x - Q(1,2)) + assert euler_poly(1, polys=True) == Poly(x - Q(1,2)) + + assert genocchi_poly(9, x) == euler_poly(8, x) * -9 + assert genocchi_poly(10, x) == euler_poly(9, x) * -10 + +def test_andre_poly(): + raises(ValueError, lambda: andre_poly(-1, x)) + assert andre_poly(1, x, polys=True) == Poly(x) + + assert andre_poly(0, x) == 1 + assert andre_poly(1, x) == x + assert andre_poly(2, x) == x**2 - 1 + assert andre_poly(3, x) == x**3 - 3*x + assert andre_poly(4, x) == x**4 - 6*x**2 + 5 + assert andre_poly(5, x) == x**5 - 10*x**3 + 25*x + assert andre_poly(6, x) == x**6 - 15*x**4 + 75*x**2 - 61 + + assert andre_poly(1).dummy_eq(x) + assert andre_poly(1, polys=True) == Poly(x) diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_constructor.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_constructor.py new file mode 100644 index 0000000000000000000000000000000000000000..b02d8a4b360dd09b993bbed80cdec307d09908fc --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_constructor.py @@ -0,0 +1,236 @@ +"""Tests for tools for constructing domains for expressions. """ + +from sympy.testing.pytest import tooslow + +from sympy.polys.constructor import construct_domain +from sympy.polys.domains import ZZ, QQ, ZZ_I, QQ_I, RR, CC, EX +from sympy.polys.domains.realfield import RealField +from sympy.polys.domains.complexfield import ComplexField + +from sympy.core import (Catalan, GoldenRatio) +from sympy.core.numbers import (E, Float, I, Rational, pi) +from sympy.core.singleton import S +from sympy.functions.elementary.exponential import exp +from sympy.functions.elementary.miscellaneous import sqrt +from sympy.functions.elementary.trigonometric import sin +from sympy import rootof + +from sympy.abc import x, y + + +def test_construct_domain(): + + assert construct_domain([1, 2, 3]) == (ZZ, [ZZ(1), ZZ(2), ZZ(3)]) + assert construct_domain([1, 2, 3], field=True) == (QQ, [QQ(1), QQ(2), QQ(3)]) + + assert construct_domain([S.One, S(2), S(3)]) == (ZZ, [ZZ(1), ZZ(2), ZZ(3)]) + assert construct_domain([S.One, S(2), S(3)], field=True) == (QQ, [QQ(1), QQ(2), QQ(3)]) + + assert construct_domain([S.Half, S(2)]) == (QQ, [QQ(1, 2), QQ(2)]) + result = construct_domain([3.14, 1, S.Half]) + assert isinstance(result[0], RealField) + assert result[1] == [RR(3.14), RR(1.0), RR(0.5)] + + result = construct_domain([3.14, I, S.Half]) + assert isinstance(result[0], ComplexField) + assert result[1] == [CC(3.14), CC(1.0j), CC(0.5)] + + assert construct_domain([1.0+I]) == (CC, [CC(1.0, 1.0)]) + assert construct_domain([2.0+3.0*I]) == (CC, [CC(2.0, 3.0)]) + + assert construct_domain([1, I]) == (ZZ_I, [ZZ_I(1, 0), ZZ_I(0, 1)]) + assert construct_domain([1, I/2]) == (QQ_I, [QQ_I(1, 0), QQ_I(0, S.Half)]) + + assert construct_domain([3.14, sqrt(2)], extension=None) == (EX, [EX(3.14), EX(sqrt(2))]) + assert construct_domain([3.14, sqrt(2)], extension=True) == (EX, [EX(3.14), EX(sqrt(2))]) + + assert construct_domain([1, sqrt(2)], extension=None) == (EX, [EX(1), EX(sqrt(2))]) + + assert construct_domain([x, sqrt(x)]) == (EX, [EX(x), EX(sqrt(x))]) + assert construct_domain([x, sqrt(x), sqrt(y)]) == (EX, [EX(x), EX(sqrt(x)), EX(sqrt(y))]) + + alg = QQ.algebraic_field(sqrt(2)) + + assert construct_domain([7, S.Half, sqrt(2)], extension=True) == \ + (alg, [alg.convert(7), alg.convert(S.Half), alg.convert(sqrt(2))]) + + alg = QQ.algebraic_field(sqrt(2) + sqrt(3)) + + assert construct_domain([7, sqrt(2), sqrt(3)], extension=True) == \ + (alg, [alg.convert(7), alg.convert(sqrt(2)), alg.convert(sqrt(3))]) + + dom = ZZ[x] + + assert construct_domain([2*x, 3]) == \ + (dom, [dom.convert(2*x), dom.convert(3)]) + + dom = ZZ[x, y] + + assert construct_domain([2*x, 3*y]) == \ + (dom, [dom.convert(2*x), dom.convert(3*y)]) + + dom = QQ[x] + + assert construct_domain([x/2, 3]) == \ + (dom, [dom.convert(x/2), dom.convert(3)]) + + dom = QQ[x, y] + + assert construct_domain([x/2, 3*y]) == \ + (dom, [dom.convert(x/2), dom.convert(3*y)]) + + dom = ZZ_I[x] + + assert construct_domain([2*x, I]) == \ + (dom, [dom.convert(2*x), dom.convert(I)]) + + dom = ZZ_I[x, y] + + assert construct_domain([2*x, I*y]) == \ + (dom, [dom.convert(2*x), dom.convert(I*y)]) + + dom = QQ_I[x] + + assert construct_domain([x/2, I]) == \ + (dom, [dom.convert(x/2), dom.convert(I)]) + + dom = QQ_I[x, y] + + assert construct_domain([x/2, I*y]) == \ + (dom, [dom.convert(x/2), dom.convert(I*y)]) + + dom = RR[x] + + assert construct_domain([x/2, 3.5]) == \ + (dom, [dom.convert(x/2), dom.convert(3.5)]) + + dom = RR[x, y] + + assert construct_domain([x/2, 3.5*y]) == \ + (dom, [dom.convert(x/2), dom.convert(3.5*y)]) + + dom = CC[x] + + assert construct_domain([I*x/2, 3.5]) == \ + (dom, [dom.convert(I*x/2), dom.convert(3.5)]) + + dom = CC[x, y] + + assert construct_domain([I*x/2, 3.5*y]) == \ + (dom, [dom.convert(I*x/2), dom.convert(3.5*y)]) + + dom = CC[x] + + assert construct_domain([x/2, I*3.5]) == \ + (dom, [dom.convert(x/2), dom.convert(I*3.5)]) + + dom = CC[x, y] + + assert construct_domain([x/2, I*3.5*y]) == \ + (dom, [dom.convert(x/2), dom.convert(I*3.5*y)]) + + dom = ZZ.frac_field(x) + + assert construct_domain([2/x, 3]) == \ + (dom, [dom.convert(2/x), dom.convert(3)]) + + dom = ZZ.frac_field(x, y) + + assert construct_domain([2/x, 3*y]) == \ + (dom, [dom.convert(2/x), dom.convert(3*y)]) + + dom = RR.frac_field(x) + + assert construct_domain([2/x, 3.5]) == \ + (dom, [dom.convert(2/x), dom.convert(3.5)]) + + dom = RR.frac_field(x, y) + + assert construct_domain([2/x, 3.5*y]) == \ + (dom, [dom.convert(2/x), dom.convert(3.5*y)]) + + dom = RealField(prec=336)[x] + + assert construct_domain([pi.evalf(100)*x]) == \ + (dom, [dom.convert(pi.evalf(100)*x)]) + + assert construct_domain(2) == (ZZ, ZZ(2)) + assert construct_domain(S(2)/3) == (QQ, QQ(2, 3)) + assert construct_domain(Rational(2, 3)) == (QQ, QQ(2, 3)) + + assert construct_domain({}) == (ZZ, {}) + + +def test_complex_exponential(): + w = exp(-I*2*pi/3, evaluate=False) + alg = QQ.algebraic_field(w) + assert construct_domain([w**2, w, 1], extension=True) == ( + alg, + [alg.convert(w**2), + alg.convert(w), + alg.convert(1)] + ) + + +def test_rootof(): + r1 = rootof(x**3 + x + 1, 0) + r2 = rootof(x**3 + x + 1, 1) + K1 = QQ.algebraic_field(r1) + K2 = QQ.algebraic_field(r2) + assert construct_domain([r1]) == (EX, [EX(r1)]) + assert construct_domain([r2]) == (EX, [EX(r2)]) + assert construct_domain([r1, r2]) == (EX, [EX(r1), EX(r2)]) + + assert construct_domain([r1], extension=True) == ( + K1, [K1.from_sympy(r1)]) + assert construct_domain([r2], extension=True) == ( + K2, [K2.from_sympy(r2)]) + + +@tooslow +def test_rootof_primitive_element(): + r1 = rootof(x**3 + x + 1, 0) + r2 = rootof(x**3 + x + 1, 1) + K12 = QQ.algebraic_field(r1 + r2) + assert construct_domain([r1, r2], extension=True) == ( + K12, [K12.from_sympy(r1), K12.from_sympy(r2)]) + + +def test_composite_option(): + assert construct_domain({(1,): sin(y)}, composite=False) == \ + (EX, {(1,): EX(sin(y))}) + + assert construct_domain({(1,): y}, composite=False) == \ + (EX, {(1,): EX(y)}) + + assert construct_domain({(1, 1): 1}, composite=False) == \ + (ZZ, {(1, 1): 1}) + + assert construct_domain({(1, 0): y}, composite=False) == \ + (EX, {(1, 0): EX(y)}) + + +def test_precision(): + f1 = Float("1.01") + f2 = Float("1.0000000000000000000001") + for u in [1, 1e-2, 1e-6, 1e-13, 1e-14, 1e-16, 1e-20, 1e-100, 1e-300, + f1, f2]: + result = construct_domain([u]) + v = float(result[1][0]) + assert abs(u - v) / u < 1e-14 # Test relative accuracy + + result = construct_domain([f1]) + y = result[1][0] + assert y-1 > 1e-50 + + result = construct_domain([f2]) + y = result[1][0] + assert y-1 > 1e-50 + + +def test_issue_11538(): + for n in [E, pi, Catalan]: + assert construct_domain(n)[0] == ZZ[n] + assert construct_domain(x + n)[0] == ZZ[x, n] + assert construct_domain(GoldenRatio)[0] == EX + assert construct_domain(x + GoldenRatio)[0] == EX diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_densearith.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_densearith.py new file mode 100644 index 0000000000000000000000000000000000000000..ebb29d50867ad578274ed11c766e0515d8e4da35 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_densearith.py @@ -0,0 +1,1007 @@ +"""Tests for dense recursive polynomials' arithmetics. """ + +from sympy.external.gmpy import GROUND_TYPES + +from sympy.polys.densebasic import ( + dup_normal, dmp_normal, +) + +from sympy.polys.densearith import ( + dup_add_term, dmp_add_term, + dup_sub_term, dmp_sub_term, + dup_mul_term, dmp_mul_term, + dup_add_ground, dmp_add_ground, + dup_sub_ground, dmp_sub_ground, + dup_mul_ground, dmp_mul_ground, + dup_quo_ground, dmp_quo_ground, + dup_exquo_ground, dmp_exquo_ground, + dup_lshift, dup_rshift, + dup_abs, dmp_abs, + dup_neg, dmp_neg, + dup_add, dmp_add, + dup_sub, dmp_sub, + dup_mul, dmp_mul, + dup_sqr, dmp_sqr, + dup_pow, dmp_pow, + dup_add_mul, dmp_add_mul, + dup_sub_mul, dmp_sub_mul, + dup_pdiv, dup_prem, dup_pquo, dup_pexquo, + dmp_pdiv, dmp_prem, dmp_pquo, dmp_pexquo, + dup_rr_div, dmp_rr_div, + dup_ff_div, dmp_ff_div, + dup_div, dup_rem, dup_quo, dup_exquo, + dmp_div, dmp_rem, dmp_quo, dmp_exquo, + dup_max_norm, dmp_max_norm, + dup_l1_norm, dmp_l1_norm, + dup_l2_norm_squared, dmp_l2_norm_squared, + dup_expand, dmp_expand, +) + +from sympy.polys.polyerrors import ( + ExactQuotientFailed, +) + +from sympy.polys.specialpolys import f_polys, Symbol, Poly +from sympy.polys.domains import FF, ZZ, QQ, CC + +from sympy.testing.pytest import raises + +x = Symbol('x') + +f_0, f_1, f_2, f_3, f_4, f_5, f_6 = [ f.to_dense() for f in f_polys() ] +F_0 = dmp_mul_ground(dmp_normal(f_0, 2, QQ), QQ(1, 7), 2, QQ) + +def test_dup_add_term(): + f = dup_normal([], ZZ) + + assert dup_add_term(f, ZZ(0), 0, ZZ) == dup_normal([], ZZ) + + assert dup_add_term(f, ZZ(1), 0, ZZ) == dup_normal([1], ZZ) + assert dup_add_term(f, ZZ(1), 1, ZZ) == dup_normal([1, 0], ZZ) + assert dup_add_term(f, ZZ(1), 2, ZZ) == dup_normal([1, 0, 0], ZZ) + + f = dup_normal([1, 1, 1], ZZ) + + assert dup_add_term(f, ZZ(1), 0, ZZ) == dup_normal([1, 1, 2], ZZ) + assert dup_add_term(f, ZZ(1), 1, ZZ) == dup_normal([1, 2, 1], ZZ) + assert dup_add_term(f, ZZ(1), 2, ZZ) == dup_normal([2, 1, 1], ZZ) + + assert dup_add_term(f, ZZ(1), 3, ZZ) == dup_normal([1, 1, 1, 1], ZZ) + assert dup_add_term(f, ZZ(1), 4, ZZ) == dup_normal([1, 0, 1, 1, 1], ZZ) + assert dup_add_term(f, ZZ(1), 5, ZZ) == dup_normal([1, 0, 0, 1, 1, 1], ZZ) + assert dup_add_term( + f, ZZ(1), 6, ZZ) == dup_normal([1, 0, 0, 0, 1, 1, 1], ZZ) + + assert dup_add_term(f, ZZ(-1), 2, ZZ) == dup_normal([1, 1], ZZ) + + +def test_dmp_add_term(): + assert dmp_add_term([ZZ(1), ZZ(1), ZZ(1)], ZZ(1), 2, 0, ZZ) == \ + dup_add_term([ZZ(1), ZZ(1), ZZ(1)], ZZ(1), 2, ZZ) + assert dmp_add_term(f_0, [[]], 3, 2, ZZ) == f_0 + assert dmp_add_term(F_0, [[]], 3, 2, QQ) == F_0 + + +def test_dup_sub_term(): + f = dup_normal([], ZZ) + + assert dup_sub_term(f, ZZ(0), 0, ZZ) == dup_normal([], ZZ) + + assert dup_sub_term(f, ZZ(1), 0, ZZ) == dup_normal([-1], ZZ) + assert dup_sub_term(f, ZZ(1), 1, ZZ) == dup_normal([-1, 0], ZZ) + assert dup_sub_term(f, ZZ(1), 2, ZZ) == dup_normal([-1, 0, 0], ZZ) + + f = dup_normal([1, 1, 1], ZZ) + + assert dup_sub_term(f, ZZ(2), 0, ZZ) == dup_normal([ 1, 1, -1], ZZ) + assert dup_sub_term(f, ZZ(2), 1, ZZ) == dup_normal([ 1, -1, 1], ZZ) + assert dup_sub_term(f, ZZ(2), 2, ZZ) == dup_normal([-1, 1, 1], ZZ) + + assert dup_sub_term(f, ZZ(1), 3, ZZ) == dup_normal([-1, 1, 1, 1], ZZ) + assert dup_sub_term(f, ZZ(1), 4, ZZ) == dup_normal([-1, 0, 1, 1, 1], ZZ) + assert dup_sub_term(f, ZZ(1), 5, ZZ) == dup_normal([-1, 0, 0, 1, 1, 1], ZZ) + assert dup_sub_term( + f, ZZ(1), 6, ZZ) == dup_normal([-1, 0, 0, 0, 1, 1, 1], ZZ) + + assert dup_sub_term(f, ZZ(1), 2, ZZ) == dup_normal([1, 1], ZZ) + + +def test_dmp_sub_term(): + assert dmp_sub_term([ZZ(1), ZZ(1), ZZ(1)], ZZ(1), 2, 0, ZZ) == \ + dup_sub_term([ZZ(1), ZZ(1), ZZ(1)], ZZ(1), 2, ZZ) + assert dmp_sub_term(f_0, [[]], 3, 2, ZZ) == f_0 + assert dmp_sub_term(F_0, [[]], 3, 2, QQ) == F_0 + + +def test_dup_mul_term(): + f = dup_normal([], ZZ) + + assert dup_mul_term(f, ZZ(2), 3, ZZ) == dup_normal([], ZZ) + + f = dup_normal([1, 1], ZZ) + + assert dup_mul_term(f, ZZ(0), 3, ZZ) == dup_normal([], ZZ) + + f = dup_normal([1, 2, 3], ZZ) + + assert dup_mul_term(f, ZZ(2), 0, ZZ) == dup_normal([2, 4, 6], ZZ) + assert dup_mul_term(f, ZZ(2), 1, ZZ) == dup_normal([2, 4, 6, 0], ZZ) + assert dup_mul_term(f, ZZ(2), 2, ZZ) == dup_normal([2, 4, 6, 0, 0], ZZ) + assert dup_mul_term(f, ZZ(2), 3, ZZ) == dup_normal([2, 4, 6, 0, 0, 0], ZZ) + + +def test_dmp_mul_term(): + assert dmp_mul_term([ZZ(1), ZZ(2), ZZ(3)], ZZ(2), 1, 0, ZZ) == \ + dup_mul_term([ZZ(1), ZZ(2), ZZ(3)], ZZ(2), 1, ZZ) + + assert dmp_mul_term([[]], [ZZ(2)], 3, 1, ZZ) == [[]] + assert dmp_mul_term([[ZZ(1)]], [], 3, 1, ZZ) == [[]] + + assert dmp_mul_term([[ZZ(1), ZZ(2)], [ZZ(3)]], [ZZ(2)], 2, 1, ZZ) == \ + [[ZZ(2), ZZ(4)], [ZZ(6)], [], []] + + assert dmp_mul_term([[]], [QQ(2, 3)], 3, 1, QQ) == [[]] + assert dmp_mul_term([[QQ(1, 2)]], [], 3, 1, QQ) == [[]] + + assert dmp_mul_term([[QQ(1, 5), QQ(2, 5)], [QQ(3, 5)]], [QQ(2, 3)], 2, 1, QQ) == \ + [[QQ(2, 15), QQ(4, 15)], [QQ(6, 15)], [], []] + + +def test_dup_add_ground(): + f = ZZ.map([1, 2, 3, 4]) + g = ZZ.map([1, 2, 3, 8]) + + assert dup_add_ground(f, ZZ(4), ZZ) == g + + +def test_dmp_add_ground(): + f = ZZ.map([[1], [2], [3], [4]]) + g = ZZ.map([[1], [2], [3], [8]]) + + assert dmp_add_ground(f, ZZ(4), 1, ZZ) == g + + +def test_dup_sub_ground(): + f = ZZ.map([1, 2, 3, 4]) + g = ZZ.map([1, 2, 3, 0]) + + assert dup_sub_ground(f, ZZ(4), ZZ) == g + + +def test_dmp_sub_ground(): + f = ZZ.map([[1], [2], [3], [4]]) + g = ZZ.map([[1], [2], [3], []]) + + assert dmp_sub_ground(f, ZZ(4), 1, ZZ) == g + + +def test_dup_mul_ground(): + f = dup_normal([], ZZ) + + assert dup_mul_ground(f, ZZ(2), ZZ) == dup_normal([], ZZ) + + f = dup_normal([1, 2, 3], ZZ) + + assert dup_mul_ground(f, ZZ(0), ZZ) == dup_normal([], ZZ) + assert dup_mul_ground(f, ZZ(2), ZZ) == dup_normal([2, 4, 6], ZZ) + + +def test_dmp_mul_ground(): + assert dmp_mul_ground(f_0, ZZ(2), 2, ZZ) == [ + [[ZZ(2), ZZ(4), ZZ(6)], [ZZ(4)]], + [[ZZ(6)]], + [[ZZ(8), ZZ(10), ZZ(12)], [ZZ(2), ZZ(4), ZZ(2)], [ZZ(2)]] + ] + + assert dmp_mul_ground(F_0, QQ(1, 2), 2, QQ) == [ + [[QQ(1, 14), QQ(2, 14), QQ(3, 14)], [QQ(2, 14)]], + [[QQ(3, 14)]], + [[QQ(4, 14), QQ(5, 14), QQ(6, 14)], [QQ(1, 14), QQ(2, 14), + QQ(1, 14)], [QQ(1, 14)]] + ] + + +def test_dup_quo_ground(): + raises(ZeroDivisionError, lambda: dup_quo_ground(dup_normal([1, 2, + 3], ZZ), ZZ(0), ZZ)) + + f = dup_normal([], ZZ) + + assert dup_quo_ground(f, ZZ(3), ZZ) == dup_normal([], ZZ) + + f = dup_normal([6, 2, 8], ZZ) + + assert dup_quo_ground(f, ZZ(1), ZZ) == f + assert dup_quo_ground(f, ZZ(2), ZZ) == dup_normal([3, 1, 4], ZZ) + + assert dup_quo_ground(f, ZZ(3), ZZ) == dup_normal([2, 0, 2], ZZ) + + f = dup_normal([6, 2, 8], QQ) + + assert dup_quo_ground(f, QQ(1), QQ) == f + assert dup_quo_ground(f, QQ(2), QQ) == [QQ(3), QQ(1), QQ(4)] + assert dup_quo_ground(f, QQ(7), QQ) == [QQ(6, 7), QQ(2, 7), QQ(8, 7)] + + +def test_dup_exquo_ground(): + raises(ZeroDivisionError, lambda: dup_exquo_ground(dup_normal([1, + 2, 3], ZZ), ZZ(0), ZZ)) + raises(ExactQuotientFailed, lambda: dup_exquo_ground(dup_normal([1, + 2, 3], ZZ), ZZ(3), ZZ)) + + f = dup_normal([], ZZ) + + assert dup_exquo_ground(f, ZZ(3), ZZ) == dup_normal([], ZZ) + + f = dup_normal([6, 2, 8], ZZ) + + assert dup_exquo_ground(f, ZZ(1), ZZ) == f + assert dup_exquo_ground(f, ZZ(2), ZZ) == dup_normal([3, 1, 4], ZZ) + + f = dup_normal([6, 2, 8], QQ) + + assert dup_exquo_ground(f, QQ(1), QQ) == f + assert dup_exquo_ground(f, QQ(2), QQ) == [QQ(3), QQ(1), QQ(4)] + assert dup_exquo_ground(f, QQ(7), QQ) == [QQ(6, 7), QQ(2, 7), QQ(8, 7)] + + +def test_dmp_quo_ground(): + f = dmp_normal([[6], [2], [8]], 1, ZZ) + + assert dmp_quo_ground(f, ZZ(1), 1, ZZ) == f + assert dmp_quo_ground( + f, ZZ(2), 1, ZZ) == dmp_normal([[3], [1], [4]], 1, ZZ) + + assert dmp_normal(dmp_quo_ground( + f, ZZ(3), 1, ZZ), 1, ZZ) == dmp_normal([[2], [], [2]], 1, ZZ) + + +def test_dmp_exquo_ground(): + f = dmp_normal([[6], [2], [8]], 1, ZZ) + + assert dmp_exquo_ground(f, ZZ(1), 1, ZZ) == f + assert dmp_exquo_ground( + f, ZZ(2), 1, ZZ) == dmp_normal([[3], [1], [4]], 1, ZZ) + + +def test_dup_lshift(): + assert dup_lshift([], 3, ZZ) == [] + assert dup_lshift([1], 3, ZZ) == [1, 0, 0, 0] + + +def test_dup_rshift(): + assert dup_rshift([], 3, ZZ) == [] + assert dup_rshift([1, 0, 0, 0], 3, ZZ) == [1] + + +def test_dup_abs(): + assert dup_abs([], ZZ) == [] + assert dup_abs([ZZ( 1)], ZZ) == [ZZ(1)] + assert dup_abs([ZZ(-7)], ZZ) == [ZZ(7)] + assert dup_abs([ZZ(-1), ZZ(2), ZZ(3)], ZZ) == [ZZ(1), ZZ(2), ZZ(3)] + + assert dup_abs([], QQ) == [] + assert dup_abs([QQ( 1, 2)], QQ) == [QQ(1, 2)] + assert dup_abs([QQ(-7, 3)], QQ) == [QQ(7, 3)] + assert dup_abs( + [QQ(-1, 7), QQ(2, 7), QQ(3, 7)], QQ) == [QQ(1, 7), QQ(2, 7), QQ(3, 7)] + + +def test_dmp_abs(): + assert dmp_abs([ZZ(-1)], 0, ZZ) == [ZZ(1)] + assert dmp_abs([QQ(-1, 2)], 0, QQ) == [QQ(1, 2)] + + assert dmp_abs([[[]]], 2, ZZ) == [[[]]] + assert dmp_abs([[[ZZ(1)]]], 2, ZZ) == [[[ZZ(1)]]] + assert dmp_abs([[[ZZ(-7)]]], 2, ZZ) == [[[ZZ(7)]]] + + assert dmp_abs([[[]]], 2, QQ) == [[[]]] + assert dmp_abs([[[QQ(1, 2)]]], 2, QQ) == [[[QQ(1, 2)]]] + assert dmp_abs([[[QQ(-7, 9)]]], 2, QQ) == [[[QQ(7, 9)]]] + + +def test_dup_neg(): + assert dup_neg([], ZZ) == [] + assert dup_neg([ZZ(1)], ZZ) == [ZZ(-1)] + assert dup_neg([ZZ(-7)], ZZ) == [ZZ(7)] + assert dup_neg([ZZ(-1), ZZ(2), ZZ(3)], ZZ) == [ZZ(1), ZZ(-2), ZZ(-3)] + + assert dup_neg([], QQ) == [] + assert dup_neg([QQ(1, 2)], QQ) == [QQ(-1, 2)] + assert dup_neg([QQ(-7, 9)], QQ) == [QQ(7, 9)] + assert dup_neg([QQ( + -1, 7), QQ(2, 7), QQ(3, 7)], QQ) == [QQ(1, 7), QQ(-2, 7), QQ(-3, 7)] + + +def test_dmp_neg(): + assert dmp_neg([ZZ(-1)], 0, ZZ) == [ZZ(1)] + assert dmp_neg([QQ(-1, 2)], 0, QQ) == [QQ(1, 2)] + + assert dmp_neg([[[]]], 2, ZZ) == [[[]]] + assert dmp_neg([[[ZZ(1)]]], 2, ZZ) == [[[ZZ(-1)]]] + assert dmp_neg([[[ZZ(-7)]]], 2, ZZ) == [[[ZZ(7)]]] + + assert dmp_neg([[[]]], 2, QQ) == [[[]]] + assert dmp_neg([[[QQ(1, 9)]]], 2, QQ) == [[[QQ(-1, 9)]]] + assert dmp_neg([[[QQ(-7, 9)]]], 2, QQ) == [[[QQ(7, 9)]]] + + +def test_dup_add(): + assert dup_add([], [], ZZ) == [] + assert dup_add([ZZ(1)], [], ZZ) == [ZZ(1)] + assert dup_add([], [ZZ(1)], ZZ) == [ZZ(1)] + assert dup_add([ZZ(1)], [ZZ(1)], ZZ) == [ZZ(2)] + assert dup_add([ZZ(1)], [ZZ(2)], ZZ) == [ZZ(3)] + + assert dup_add([ZZ(1), ZZ(2)], [ZZ(1)], ZZ) == [ZZ(1), ZZ(3)] + assert dup_add([ZZ(1)], [ZZ(1), ZZ(2)], ZZ) == [ZZ(1), ZZ(3)] + + assert dup_add([ZZ(1), ZZ( + 2), ZZ(3)], [ZZ(8), ZZ(9), ZZ(10)], ZZ) == [ZZ(9), ZZ(11), ZZ(13)] + + assert dup_add([], [], QQ) == [] + assert dup_add([QQ(1, 2)], [], QQ) == [QQ(1, 2)] + assert dup_add([], [QQ(1, 2)], QQ) == [QQ(1, 2)] + assert dup_add([QQ(1, 4)], [QQ(1, 4)], QQ) == [QQ(1, 2)] + assert dup_add([QQ(1, 4)], [QQ(1, 2)], QQ) == [QQ(3, 4)] + + assert dup_add([QQ(1, 2), QQ(2, 3)], [QQ(1)], QQ) == [QQ(1, 2), QQ(5, 3)] + assert dup_add([QQ(1)], [QQ(1, 2), QQ(2, 3)], QQ) == [QQ(1, 2), QQ(5, 3)] + + assert dup_add([QQ(1, 7), QQ(2, 7), QQ(3, 7)], [QQ( + 8, 7), QQ(9, 7), QQ(10, 7)], QQ) == [QQ(9, 7), QQ(11, 7), QQ(13, 7)] + + +def test_dmp_add(): + assert dmp_add([ZZ(1), ZZ(2)], [ZZ(1)], 0, ZZ) == \ + dup_add([ZZ(1), ZZ(2)], [ZZ(1)], ZZ) + assert dmp_add([QQ(1, 2), QQ(2, 3)], [QQ(1)], 0, QQ) == \ + dup_add([QQ(1, 2), QQ(2, 3)], [QQ(1)], QQ) + + assert dmp_add([[[]]], [[[]]], 2, ZZ) == [[[]]] + assert dmp_add([[[ZZ(1)]]], [[[]]], 2, ZZ) == [[[ZZ(1)]]] + assert dmp_add([[[]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(1)]]] + assert dmp_add([[[ZZ(2)]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(3)]]] + assert dmp_add([[[ZZ(1)]]], [[[ZZ(2)]]], 2, ZZ) == [[[ZZ(3)]]] + + assert dmp_add([[[]]], [[[]]], 2, QQ) == [[[]]] + assert dmp_add([[[QQ(1, 2)]]], [[[]]], 2, QQ) == [[[QQ(1, 2)]]] + assert dmp_add([[[]]], [[[QQ(1, 2)]]], 2, QQ) == [[[QQ(1, 2)]]] + assert dmp_add([[[QQ(2, 7)]]], [[[QQ(1, 7)]]], 2, QQ) == [[[QQ(3, 7)]]] + assert dmp_add([[[QQ(1, 7)]]], [[[QQ(2, 7)]]], 2, QQ) == [[[QQ(3, 7)]]] + + +def test_dup_sub(): + assert dup_sub([], [], ZZ) == [] + assert dup_sub([ZZ(1)], [], ZZ) == [ZZ(1)] + assert dup_sub([], [ZZ(1)], ZZ) == [ZZ(-1)] + assert dup_sub([ZZ(1)], [ZZ(1)], ZZ) == [] + assert dup_sub([ZZ(1)], [ZZ(2)], ZZ) == [ZZ(-1)] + + assert dup_sub([ZZ(1), ZZ(2)], [ZZ(1)], ZZ) == [ZZ(1), ZZ(1)] + assert dup_sub([ZZ(1)], [ZZ(1), ZZ(2)], ZZ) == [ZZ(-1), ZZ(-1)] + + assert dup_sub([ZZ(3), ZZ( + 2), ZZ(1)], [ZZ(8), ZZ(9), ZZ(10)], ZZ) == [ZZ(-5), ZZ(-7), ZZ(-9)] + + assert dup_sub([], [], QQ) == [] + assert dup_sub([QQ(1, 2)], [], QQ) == [QQ(1, 2)] + assert dup_sub([], [QQ(1, 2)], QQ) == [QQ(-1, 2)] + assert dup_sub([QQ(1, 3)], [QQ(1, 3)], QQ) == [] + assert dup_sub([QQ(1, 3)], [QQ(2, 3)], QQ) == [QQ(-1, 3)] + + assert dup_sub([QQ(1, 7), QQ(2, 7)], [QQ(1)], QQ) == [QQ(1, 7), QQ(-5, 7)] + assert dup_sub([QQ(1)], [QQ(1, 7), QQ(2, 7)], QQ) == [QQ(-1, 7), QQ(5, 7)] + + assert dup_sub([QQ(3, 7), QQ(2, 7), QQ(1, 7)], [QQ( + 8, 7), QQ(9, 7), QQ(10, 7)], QQ) == [QQ(-5, 7), QQ(-7, 7), QQ(-9, 7)] + + +def test_dmp_sub(): + assert dmp_sub([ZZ(1), ZZ(2)], [ZZ(1)], 0, ZZ) == \ + dup_sub([ZZ(1), ZZ(2)], [ZZ(1)], ZZ) + assert dmp_sub([QQ(1, 2), QQ(2, 3)], [QQ(1)], 0, QQ) == \ + dup_sub([QQ(1, 2), QQ(2, 3)], [QQ(1)], QQ) + + assert dmp_sub([[[]]], [[[]]], 2, ZZ) == [[[]]] + assert dmp_sub([[[ZZ(1)]]], [[[]]], 2, ZZ) == [[[ZZ(1)]]] + assert dmp_sub([[[]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(-1)]]] + assert dmp_sub([[[ZZ(2)]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(1)]]] + assert dmp_sub([[[ZZ(1)]]], [[[ZZ(2)]]], 2, ZZ) == [[[ZZ(-1)]]] + + assert dmp_sub([[[]]], [[[]]], 2, QQ) == [[[]]] + assert dmp_sub([[[QQ(1, 2)]]], [[[]]], 2, QQ) == [[[QQ(1, 2)]]] + assert dmp_sub([[[]]], [[[QQ(1, 2)]]], 2, QQ) == [[[QQ(-1, 2)]]] + assert dmp_sub([[[QQ(2, 7)]]], [[[QQ(1, 7)]]], 2, QQ) == [[[QQ(1, 7)]]] + assert dmp_sub([[[QQ(1, 7)]]], [[[QQ(2, 7)]]], 2, QQ) == [[[QQ(-1, 7)]]] + + +def test_dup_add_mul(): + assert dup_add_mul([ZZ(1), ZZ(2), ZZ(3)], [ZZ(3), ZZ(2), ZZ(1)], + [ZZ(1), ZZ(2)], ZZ) == [ZZ(3), ZZ(9), ZZ(7), ZZ(5)] + assert dmp_add_mul([[ZZ(1), ZZ(2)], [ZZ(3)]], [[ZZ(3)], [ZZ(2), ZZ(1)]], + [[ZZ(1)], [ZZ(2)]], 1, ZZ) == [[ZZ(3)], [ZZ(3), ZZ(9)], [ZZ(4), ZZ(5)]] + + +def test_dup_sub_mul(): + assert dup_sub_mul([ZZ(1), ZZ(2), ZZ(3)], [ZZ(3), ZZ(2), ZZ(1)], + [ZZ(1), ZZ(2)], ZZ) == [ZZ(-3), ZZ(-7), ZZ(-3), ZZ(1)] + assert dmp_sub_mul([[ZZ(1), ZZ(2)], [ZZ(3)]], [[ZZ(3)], [ZZ(2), ZZ(1)]], + [[ZZ(1)], [ZZ(2)]], 1, ZZ) == [[ZZ(-3)], [ZZ(-1), ZZ(-5)], [ZZ(-4), ZZ(1)]] + + +def test_dup_mul(): + assert dup_mul([], [], ZZ) == [] + assert dup_mul([], [ZZ(1)], ZZ) == [] + assert dup_mul([ZZ(1)], [], ZZ) == [] + assert dup_mul([ZZ(1)], [ZZ(1)], ZZ) == [ZZ(1)] + assert dup_mul([ZZ(5)], [ZZ(7)], ZZ) == [ZZ(35)] + + assert dup_mul([], [], QQ) == [] + assert dup_mul([], [QQ(1, 2)], QQ) == [] + assert dup_mul([QQ(1, 2)], [], QQ) == [] + assert dup_mul([QQ(1, 2)], [QQ(4, 7)], QQ) == [QQ(2, 7)] + assert dup_mul([QQ(5, 7)], [QQ(3, 7)], QQ) == [QQ(15, 49)] + + f = dup_normal([3, 0, 0, 6, 1, 2], ZZ) + g = dup_normal([4, 0, 1, 0], ZZ) + h = dup_normal([12, 0, 3, 24, 4, 14, 1, 2, 0], ZZ) + + assert dup_mul(f, g, ZZ) == h + assert dup_mul(g, f, ZZ) == h + + f = dup_normal([2, 0, 0, 1, 7], ZZ) + h = dup_normal([4, 0, 0, 4, 28, 0, 1, 14, 49], ZZ) + + assert dup_mul(f, f, ZZ) == h + + K = FF(6) + + assert dup_mul([K(2), K(1)], [K(3), K(4)], K) == [K(5), K(4)] + + p1 = dup_normal([79, -1, 78, -94, -10, 11, 32, -19, 78, 2, -89, 30, 73, 42, + 85, 77, 83, -30, -34, -2, 95, -81, 37, -49, -46, -58, -16, 37, 35, -11, + -57, -15, -31, 67, -20, 27, 76, 2, 70, 67, -65, 65, -26, -93, -44, -12, + -92, 57, -90, -57, -11, -67, -98, -69, 97, -41, 89, 33, 89, -50, 81, + -31, 60, -27, 43, 29, -77, 44, 21, -91, 32, -57, 33, 3, 53, -51, -38, + -99, -84, 23, -50, 66, -100, 1, -75, -25, 27, -60, 98, -51, -87, 6, 8, + 78, -28, -95, -88, 12, -35, 26, -9, 16, -92, 55, -7, -86, 68, -39, -46, + 84, 94, 45, 60, 92, 68, -75, -74, -19, 8, 75, 78, 91, 57, 34, 14, -3, + -49, 65, 78, -18, 6, -29, -80, -98, 17, 13, 58, 21, 20, 9, 37, 7, -30, + -53, -20, 34, 67, -42, 89, -22, 73, 43, -6, 5, 51, -8, -15, -52, -22, + -58, -72, -3, 43, -92, 82, 83, -2, -13, -23, -60, 16, -94, -8, -28, + -95, -72, 63, -90, 76, 6, -43, -100, -59, 76, 3, 3, 46, -85, 75, 62, + -71, -76, 88, 97, -72, -1, 30, -64, 72, -48, 14, -78, 58, 63, -91, 24, + -87, -27, -80, -100, -44, 98, 70, 100, -29, -38, 11, 77, 100, 52, 86, + 65, -5, -42, -81, -38, -42, 43, -2, -70, -63, -52], ZZ) + p2 = dup_normal([65, -19, -47, 1, 90, 81, -15, -34, 25, -75, 9, -83, 50, -5, + -44, 31, 1, 70, -7, 78, 74, 80, 85, 65, 21, 41, 66, 19, -40, 63, -21, + -27, 32, 69, 83, 34, -35, 14, 81, 57, -75, 32, -67, -89, -100, -61, 46, + 84, -78, -29, -50, -94, -24, -32, -68, -16, 100, -7, -72, -89, 35, 82, + 58, 81, -92, 62, 5, -47, -39, -58, -72, -13, 84, 44, 55, -25, 48, -54, + -31, -56, -11, -50, -84, 10, 67, 17, 13, -14, 61, 76, -64, -44, -40, + -96, 11, -11, -94, 2, 6, 27, -6, 68, -54, 66, -74, -14, -1, -24, -73, + 96, 89, -11, -89, 56, -53, 72, -43, 96, 25, 63, -31, 29, 68, 83, 91, + -93, -19, -38, -40, 40, -12, -19, -79, 44, 100, -66, -29, -77, 62, 39, + -8, 11, -97, 14, 87, 64, 21, -18, 13, 15, -59, -75, -99, -88, 57, 54, + 56, -67, 6, -63, -59, -14, 28, 87, -20, -39, 84, -91, -2, 49, -75, 11, + -24, -95, 36, 66, 5, 25, -72, -40, 86, 90, 37, -33, 57, -35, 29, -18, + 4, -79, 64, -17, -27, 21, 29, -5, -44, -87, -24, 52, 78, 11, -23, -53, + 36, 42, 21, -68, 94, -91, -51, -21, 51, -76, 72, 31, 24, -48, -80, -9, + 37, -47, -6, -8, -63, -91, 79, -79, -100, 38, -20, 38, 100, 83, -90, + 87, 63, -36, 82, -19, 18, -98, -38, 26, 98, -70, 79, 92, 12, 12, 70, + 74, 36, 48, -13, 31, 31, -47, -71, -12, -64, 36, -42, 32, -86, 60, 83, + 70, 55, 0, 1, 29, -35, 8, -82, 8, -73, -46, -50, 43, 48, -5, -86, -72, + 44, -90, 19, 19, 5, -20, 97, -13, -66, -5, 5, -69, 64, -30, 41, 51, 36, + 13, -99, -61, 94, -12, 74, 98, 68, 24, 46, -97, -87, -6, -27, 82, 62, + -11, -77, 86, 66, -47, -49, -50, 13, 18, 89, -89, 46, -80, 13, 98, -35, + -36, -25, 12, 20, 26, -52, 79, 27, 79, 100, 8, 62, -58, -28, 37], ZZ) + res = dup_normal([5135, -1566, 1376, -7466, 4579, 11710, 8001, -7183, + -3737, -7439, 345, -10084, 24522, -1201, 1070, -10245, 9582, 9264, + 1903, 23312, 18953, 10037, -15268, -5450, 6442, -6243, -3777, 5110, + 10936, -16649, -6022, 16255, 31300, 24818, 31922, 32760, 7854, 27080, + 15766, 29596, 7139, 31945, -19810, 465, -38026, -3971, 9641, 465, + -19375, 5524, -30112, -11960, -12813, 13535, 30670, 5925, -43725, + -14089, 11503, -22782, 6371, 43881, 37465, -33529, -33590, -39798, + -37854, -18466, -7908, -35825, -26020, -36923, -11332, -5699, 25166, + -3147, 19885, 12962, -20659, -1642, 27723, -56331, -24580, -11010, + -20206, 20087, -23772, -16038, 38580, 20901, -50731, 32037, -4299, + 26508, 18038, -28357, 31846, -7405, -20172, -15894, 2096, 25110, + -45786, 45918, -55333, -31928, -49428, -29824, -58796, -24609, -15408, + 69, -35415, -18439, 10123, -20360, -65949, 33356, -20333, 26476, + -32073, 33621, 930, 28803, -42791, 44716, 38164, 12302, -1739, 11421, + 73385, -7613, 14297, 38155, -414, 77587, 24338, -21415, 29367, 42639, + 13901, -288, 51027, -11827, 91260, 43407, 88521, -15186, 70572, -12049, + 5090, -12208, -56374, 15520, -623, -7742, 50825, 11199, -14894, 40892, + 59591, -31356, -28696, -57842, -87751, -33744, -28436, -28945, -40287, + 37957, -35638, 33401, -61534, 14870, 40292, 70366, -10803, 102290, + -71719, -85251, 7902, -22409, 75009, 99927, 35298, -1175, -762, -34744, + -10587, -47574, -62629, -19581, -43659, -54369, -32250, -39545, 15225, + -24454, 11241, -67308, -30148, 39929, 37639, 14383, -73475, -77636, + -81048, -35992, 41601, -90143, 76937, -8112, 56588, 9124, -40094, + -32340, 13253, 10898, -51639, 36390, 12086, -1885, 100714, -28561, + -23784, -18735, 18916, 16286, 10742, -87360, -13697, 10689, -19477, + -29770, 5060, 20189, -8297, 112407, 47071, 47743, 45519, -4109, 17468, + -68831, 78325, -6481, -21641, -19459, 30919, 96115, 8607, 53341, 32105, + -16211, 23538, 57259, -76272, -40583, 62093, 38511, -34255, -40665, + -40604, -37606, -15274, 33156, -13885, 103636, 118678, -14101, -92682, + -100791, 2634, 63791, 98266, 19286, -34590, -21067, -71130, 25380, + -40839, -27614, -26060, 52358, -15537, 27138, -6749, 36269, -33306, + 13207, -91084, -5540, -57116, 69548, 44169, -57742, -41234, -103327, + -62904, -8566, 41149, -12866, 71188, 23980, 1838, 58230, 73950, 5594, + 43113, -8159, -15925, 6911, 85598, -75016, -16214, -62726, -39016, + 8618, -63882, -4299, 23182, 49959, 49342, -3238, -24913, -37138, 78361, + 32451, 6337, -11438, -36241, -37737, 8169, -3077, -24829, 57953, 53016, + -31511, -91168, 12599, -41849, 41576, 55275, -62539, 47814, -62319, + 12300, -32076, -55137, -84881, -27546, 4312, -3433, -54382, 113288, + -30157, 74469, 18219, 79880, -2124, 98911, 17655, -33499, -32861, + 47242, -37393, 99765, 14831, -44483, 10800, -31617, -52710, 37406, + 22105, 29704, -20050, 13778, 43683, 36628, 8494, 60964, -22644, 31550, + -17693, 33805, -124879, -12302, 19343, 20400, -30937, -21574, -34037, + -33380, 56539, -24993, -75513, -1527, 53563, 65407, -101, 53577, 37991, + 18717, -23795, -8090, -47987, -94717, 41967, 5170, -14815, -94311, + 17896, -17734, -57718, -774, -38410, 24830, 29682, 76480, 58802, + -46416, -20348, -61353, -68225, -68306, 23822, -31598, 42972, 36327, + 28968, -65638, -21638, 24354, -8356, 26777, 52982, -11783, -44051, + -26467, -44721, -28435, -53265, -25574, -2669, 44155, 22946, -18454, + -30718, -11252, 58420, 8711, 67447, 4425, 41749, 67543, 43162, 11793, + -41907, 20477, -13080, 6559, -6104, -13244, 42853, 42935, 29793, 36730, + -28087, 28657, 17946, 7503, 7204, 21491, -27450, -24241, -98156, + -18082, -42613, -24928, 10775, -14842, -44127, 55910, 14777, 31151, -2194, + 39206, -2100, -4211, 11827, -8918, -19471, 72567, 36447, -65590, -34861, + -17147, -45303, 9025, -7333, -35473, 11101, 11638, 3441, 6626, -41800, + 9416, 13679, 33508, 40502, -60542, 16358, 8392, -43242, -35864, -34127, + -48721, 35878, 30598, 28630, 20279, -19983, -14638, -24455, -1851, -11344, + 45150, 42051, 26034, -28889, -32382, -3527, -14532, 22564, -22346, 477, + 11706, 28338, -25972, -9185, -22867, -12522, 32120, -4424, 11339, -33913, + -7184, 5101, -23552, -17115, -31401, -6104, 21906, 25708, 8406, 6317, + -7525, 5014, 20750, 20179, 22724, 11692, 13297, 2493, -253, -16841, -17339, + -6753, -4808, 2976, -10881, -10228, -13816, -12686, 1385, 2316, 2190, -875, + -1924], ZZ) + + assert dup_mul(p1, p2, ZZ) == res + + p1 = dup_normal([83, -61, -86, -24, 12, 43, -88, -9, 42, 55, -66, 74, 95, + -25, -12, 68, -99, 4, 45, 6, -15, -19, 78, 65, -55, 47, -13, 17, 86, + 81, -58, -27, 50, -40, -24, 39, -41, -92, 75, 90, -1, 40, -15, -27, + -35, 68, 70, -64, -40, 78, -88, -58, -39, 69, 46, 12, 28, -94, -37, + -50, -80, -96, -61, 25, 1, 71, 4, 12, 48, 4, 34, -47, -75, 5, 48, 82, + 88, 23, 98, 35, 17, -10, 48, -61, -95, 47, 65, -19, -66, -57, -6, -51, + -42, -89, 66, -13, 18, 37, 90, -23, 72, 96, -53, 0, 40, -73, -52, -68, + 32, -25, -53, 79, -52, 18, 44, 73, -81, 31, -90, 70, 3, 36, 48, 76, + -24, -44, 23, 98, -4, 73, 69, 88, -70, 14, -68, 94, -78, -15, -64, -97, + -70, -35, 65, 88, 49, -53, -7, 12, -45, -7, 59, -94, 99, -2, 67, -60, + -71, 29, -62, -77, 1, 51, 17, 80, -20, -47, -19, 24, -9, 39, -23, 21, + -84, 10, 84, 56, -17, -21, -66, 85, 70, 46, -51, -22, -95, 78, -60, + -96, -97, -45, 72, 35, 30, -61, -92, -93, -60, -61, 4, -4, -81, -73, + 46, 53, -11, 26, 94, 45, 14, -78, 55, 84, -68, 98, 60, 23, 100, -63, + 68, 96, -16, 3, 56, 21, -58, 62, -67, 66, 85, 41, -79, -22, 97, -67, + 82, 82, -96, -20, -7, 48, -67, 48, -9, -39, 78], ZZ) + p2 = dup_normal([52, 88, 76, 66, 9, -64, 46, -20, -28, 69, 60, 96, -36, + -92, -30, -11, -35, 35, 55, 63, -92, -7, 25, -58, 74, 55, -6, 4, 47, + -92, -65, 67, -45, 74, -76, 59, -6, 69, 39, 24, -71, -7, 39, -45, 60, + -68, 98, 97, -79, 17, 4, 94, -64, 68, -100, -96, -2, 3, 22, 96, 54, + -77, -86, 67, 6, 57, 37, 40, 89, -78, 64, -94, -45, -92, 57, 87, -26, + 36, 19, 97, 25, 77, -87, 24, 43, -5, 35, 57, 83, 71, 35, 63, 61, 96, + -22, 8, -1, 96, 43, 45, 94, -93, 36, 71, -41, -99, 85, -48, 59, 52, + -17, 5, 87, -16, -68, -54, 76, -18, 100, 91, -42, -70, -66, -88, -12, + 1, 95, -82, 52, 43, -29, 3, 12, 72, -99, -43, -32, -93, -51, 16, -20, + -12, -11, 5, 33, -38, 93, -5, -74, 25, 74, -58, 93, 59, -63, -86, 63, + -20, -4, -74, -73, -95, 29, -28, 93, -91, -2, -38, -62, 77, -58, -85, + -28, 95, 38, 19, -69, 86, 94, 25, -2, -4, 47, 34, -59, 35, -48, 29, + -63, -53, 34, 29, 66, 73, 6, 92, -84, 89, 15, 81, 93, 97, 51, -72, -78, + 25, 60, 90, -45, 39, 67, -84, -62, 57, 26, -32, -56, -14, -83, 76, 5, + -2, 99, -100, 28, 46, 94, -7, 53, -25, 16, -23, -36, 89, -78, -63, 31, + 1, 84, -99, -52, 76, 48, 90, -76, 44, -19, 54, -36, -9, -73, -100, -69, + 31, 42, 25, -39, 76, -26, -8, -14, 51, 3, 37, 45, 2, -54, 13, -34, -92, + 17, -25, -65, 53, -63, 30, 4, -70, -67, 90, 52, 51, 18, -3, 31, -45, + -9, 59, 63, -87, 22, -32, 29, -38, 21, 36, -82, 27, -11], ZZ) + res = dup_normal([4316, 4132, -3532, -7974, -11303, -10069, 5484, -3330, + -5874, 7734, 4673, 11327, -9884, -8031, 17343, 21035, -10570, -9285, + 15893, 3780, -14083, 8819, 17592, 10159, 7174, -11587, 8598, -16479, + 3602, 25596, 9781, 12163, 150, 18749, -21782, -12307, 27578, -2757, + -12573, 12565, 6345, -18956, 19503, -15617, 1443, -16778, 36851, 23588, + -28474, 5749, 40695, -7521, -53669, -2497, -18530, 6770, 57038, 3926, + -6927, -15399, 1848, -64649, -27728, 3644, 49608, 15187, -8902, -9480, + -7398, -40425, 4824, 23767, -7594, -6905, 33089, 18786, 12192, 24670, + 31114, 35334, -4501, -14676, 7107, -59018, -21352, 20777, 19661, 20653, + 33754, -885, -43758, 6269, 51897, -28719, -97488, -9527, 13746, 11644, + 17644, -21720, 23782, -10481, 47867, 20752, 33810, -1875, 39918, -7710, + -40840, 19808, -47075, 23066, 46616, 25201, 9287, 35436, -1602, 9645, + -11978, 13273, 15544, 33465, 20063, 44539, 11687, 27314, -6538, -37467, + 14031, 32970, -27086, 41323, 29551, 65910, -39027, -37800, -22232, + 8212, 46316, -28981, -55282, 50417, -44929, -44062, 73879, 37573, + -2596, -10877, -21893, -133218, -33707, -25753, -9531, 17530, 61126, + 2748, -56235, 43874, -10872, -90459, -30387, 115267, -7264, -44452, + 122626, 14839, -599, 10337, 57166, -67467, -54957, 63669, 1202, 18488, + 52594, 7205, -97822, 612, 78069, -5403, -63562, 47236, 36873, -154827, + -26188, 82427, -39521, 5628, 7416, 5276, -53095, 47050, 26121, -42207, + 79021, -13035, 2499, -66943, 29040, -72355, -23480, 23416, -12885, + -44225, -42688, -4224, 19858, 55299, 15735, 11465, 101876, -39169, + 51786, 14723, 43280, -68697, 16410, 92295, 56767, 7183, 111850, 4550, + 115451, -38443, -19642, -35058, 10230, 93829, 8925, 63047, 3146, 29250, + 8530, 5255, -98117, -115517, -76817, -8724, 41044, 1312, -35974, 79333, + -28567, 7547, -10580, -24559, -16238, 10794, -3867, 24848, 57770, + -51536, -35040, 71033, 29853, 62029, -7125, -125585, -32169, -47907, + 156811, -65176, -58006, -15757, -57861, 11963, 30225, -41901, -41681, + 31310, 27982, 18613, 61760, 60746, -59096, 33499, 30097, -17997, 24032, + 56442, -83042, 23747, -20931, -21978, -158752, -9883, -73598, -7987, + -7333, -125403, -116329, 30585, 53281, 51018, -29193, 88575, 8264, + -40147, -16289, 113088, 12810, -6508, 101552, -13037, 34440, -41840, + 101643, 24263, 80532, 61748, 65574, 6423, -20672, 6591, -10834, -71716, + 86919, -92626, 39161, 28490, 81319, 46676, 106720, 43530, 26998, 57456, + -8862, 60989, 13982, 3119, -2224, 14743, 55415, -49093, -29303, 28999, + 1789, 55953, -84043, -7780, -65013, 57129, -47251, 61484, 61994, + -78361, -82778, 22487, -26894, 9756, -74637, -15519, -4360, 30115, + 42433, 35475, 15286, 69768, 21509, -20214, 78675, -21163, 13596, 11443, + -10698, -53621, -53867, -24155, 64500, -42784, -33077, -16500, 873, + -52788, 14546, -38011, 36974, -39849, -34029, -94311, 83068, -50437, + -26169, -46746, 59185, 42259, -101379, -12943, 30089, -59086, 36271, + 22723, -30253, -52472, -70826, -23289, 3331, -31687, 14183, -857, + -28627, 35246, -51284, 5636, -6933, 66539, 36654, 50927, 24783, 3457, + 33276, 45281, 45650, -4938, -9968, -22590, 47995, 69229, 5214, -58365, + -17907, -14651, 18668, 18009, 12649, -11851, -13387, 20339, 52472, + -1087, -21458, -68647, 52295, 15849, 40608, 15323, 25164, -29368, + 10352, -7055, 7159, 21695, -5373, -54849, 101103, -24963, -10511, + 33227, 7659, 41042, -69588, 26718, -20515, 6441, 38135, -63, 24088, + -35364, -12785, -18709, 47843, 48533, -48575, 17251, -19394, 32878, + -9010, -9050, 504, -12407, 28076, -3429, 25324, -4210, -26119, 752, + -29203, 28251, -11324, -32140, -3366, -25135, 18702, -31588, -7047, + -24267, 49987, -14975, -33169, 37744, -7720, -9035, 16964, -2807, -421, + 14114, -17097, -13662, 40628, -12139, -9427, 5369, 17551, -13232, -16211, + 9804, -7422, 2677, 28635, -8280, -4906, 2908, -22558, 5604, 12459, 8756, + -3980, -4745, -18525, 7913, 5970, -16457, 20230, -6247, -13812, 2505, + 11899, 1409, -15094, 22540, -18863, 137, 11123, -4516, 2290, -8594, 12150, + -10380, 3005, 5235, -7350, 2535, -858], ZZ) + + assert dup_mul(p1, p2, ZZ) == res + + +def test_dmp_mul(): + assert dmp_mul([ZZ(5)], [ZZ(7)], 0, ZZ) == \ + dup_mul([ZZ(5)], [ZZ(7)], ZZ) + assert dmp_mul([QQ(5, 7)], [QQ(3, 7)], 0, QQ) == \ + dup_mul([QQ(5, 7)], [QQ(3, 7)], QQ) + + assert dmp_mul([[[]]], [[[]]], 2, ZZ) == [[[]]] + assert dmp_mul([[[ZZ(1)]]], [[[]]], 2, ZZ) == [[[]]] + assert dmp_mul([[[]]], [[[ZZ(1)]]], 2, ZZ) == [[[]]] + assert dmp_mul([[[ZZ(2)]]], [[[ZZ(1)]]], 2, ZZ) == [[[ZZ(2)]]] + assert dmp_mul([[[ZZ(1)]]], [[[ZZ(2)]]], 2, ZZ) == [[[ZZ(2)]]] + + assert dmp_mul([[[]]], [[[]]], 2, QQ) == [[[]]] + assert dmp_mul([[[QQ(1, 2)]]], [[[]]], 2, QQ) == [[[]]] + assert dmp_mul([[[]]], [[[QQ(1, 2)]]], 2, QQ) == [[[]]] + assert dmp_mul([[[QQ(2, 7)]]], [[[QQ(1, 3)]]], 2, QQ) == [[[QQ(2, 21)]]] + assert dmp_mul([[[QQ(1, 7)]]], [[[QQ(2, 3)]]], 2, QQ) == [[[QQ(2, 21)]]] + + K = FF(6) + + assert dmp_mul( + [[K(2)], [K(1)]], [[K(3)], [K(4)]], 1, K) == [[K(5)], [K(4)]] + + +def test_dup_sqr(): + assert dup_sqr([], ZZ) == [] + assert dup_sqr([ZZ(2)], ZZ) == [ZZ(4)] + assert dup_sqr([ZZ(1), ZZ(2)], ZZ) == [ZZ(1), ZZ(4), ZZ(4)] + + assert dup_sqr([], QQ) == [] + assert dup_sqr([QQ(2, 3)], QQ) == [QQ(4, 9)] + assert dup_sqr([QQ(1, 3), QQ(2, 3)], QQ) == [QQ(1, 9), QQ(4, 9), QQ(4, 9)] + + f = dup_normal([2, 0, 0, 1, 7], ZZ) + + assert dup_sqr(f, ZZ) == dup_normal([4, 0, 0, 4, 28, 0, 1, 14, 49], ZZ) + + K = FF(9) + + assert dup_sqr([K(3), K(4)], K) == [K(6), K(7)] + + +def test_dmp_sqr(): + assert dmp_sqr([ZZ(1), ZZ(2)], 0, ZZ) == \ + dup_sqr([ZZ(1), ZZ(2)], ZZ) + + assert dmp_sqr([[[]]], 2, ZZ) == [[[]]] + assert dmp_sqr([[[ZZ(2)]]], 2, ZZ) == [[[ZZ(4)]]] + + assert dmp_sqr([[[]]], 2, QQ) == [[[]]] + assert dmp_sqr([[[QQ(2, 3)]]], 2, QQ) == [[[QQ(4, 9)]]] + + K = FF(9) + + assert dmp_sqr([[K(3)], [K(4)]], 1, K) == [[K(6)], [K(7)]] + + +def test_dup_pow(): + assert dup_pow([], 0, ZZ) == [ZZ(1)] + assert dup_pow([], 0, QQ) == [QQ(1)] + + assert dup_pow([], 1, ZZ) == [] + assert dup_pow([], 7, ZZ) == [] + + assert dup_pow([ZZ(1)], 0, ZZ) == [ZZ(1)] + assert dup_pow([ZZ(1)], 1, ZZ) == [ZZ(1)] + assert dup_pow([ZZ(1)], 7, ZZ) == [ZZ(1)] + + assert dup_pow([ZZ(3)], 0, ZZ) == [ZZ(1)] + assert dup_pow([ZZ(3)], 1, ZZ) == [ZZ(3)] + assert dup_pow([ZZ(3)], 7, ZZ) == [ZZ(2187)] + + assert dup_pow([QQ(1, 1)], 0, QQ) == [QQ(1, 1)] + assert dup_pow([QQ(1, 1)], 1, QQ) == [QQ(1, 1)] + assert dup_pow([QQ(1, 1)], 7, QQ) == [QQ(1, 1)] + + assert dup_pow([QQ(3, 7)], 0, QQ) == [QQ(1, 1)] + assert dup_pow([QQ(3, 7)], 1, QQ) == [QQ(3, 7)] + assert dup_pow([QQ(3, 7)], 7, QQ) == [QQ(2187, 823543)] + + f = dup_normal([2, 0, 0, 1, 7], ZZ) + + assert dup_pow(f, 0, ZZ) == dup_normal([1], ZZ) + assert dup_pow(f, 1, ZZ) == dup_normal([2, 0, 0, 1, 7], ZZ) + assert dup_pow(f, 2, ZZ) == dup_normal([4, 0, 0, 4, 28, 0, 1, 14, 49], ZZ) + assert dup_pow(f, 3, ZZ) == dup_normal( + [8, 0, 0, 12, 84, 0, 6, 84, 294, 1, 21, 147, 343], ZZ) + + +def test_dmp_pow(): + assert dmp_pow([[]], 0, 1, ZZ) == [[ZZ(1)]] + assert dmp_pow([[]], 0, 1, QQ) == [[QQ(1)]] + + assert dmp_pow([[]], 1, 1, ZZ) == [[]] + assert dmp_pow([[]], 7, 1, ZZ) == [[]] + + assert dmp_pow([[ZZ(1)]], 0, 1, ZZ) == [[ZZ(1)]] + assert dmp_pow([[ZZ(1)]], 1, 1, ZZ) == [[ZZ(1)]] + assert dmp_pow([[ZZ(1)]], 7, 1, ZZ) == [[ZZ(1)]] + + assert dmp_pow([[QQ(3, 7)]], 0, 1, QQ) == [[QQ(1, 1)]] + assert dmp_pow([[QQ(3, 7)]], 1, 1, QQ) == [[QQ(3, 7)]] + assert dmp_pow([[QQ(3, 7)]], 7, 1, QQ) == [[QQ(2187, 823543)]] + + f = dup_normal([2, 0, 0, 1, 7], ZZ) + + assert dmp_pow(f, 2, 0, ZZ) == dup_pow(f, 2, ZZ) + + +def test_dup_pdiv(): + f = dup_normal([3, 1, 1, 5], ZZ) + g = dup_normal([5, -3, 1], ZZ) + + q = dup_normal([15, 14], ZZ) + r = dup_normal([52, 111], ZZ) + + assert dup_pdiv(f, g, ZZ) == (q, r) + assert dup_pquo(f, g, ZZ) == q + assert dup_prem(f, g, ZZ) == r + + raises(ExactQuotientFailed, lambda: dup_pexquo(f, g, ZZ)) + + f = dup_normal([3, 1, 1, 5], QQ) + g = dup_normal([5, -3, 1], QQ) + + q = dup_normal([15, 14], QQ) + r = dup_normal([52, 111], QQ) + + assert dup_pdiv(f, g, QQ) == (q, r) + assert dup_pquo(f, g, QQ) == q + assert dup_prem(f, g, QQ) == r + + raises(ExactQuotientFailed, lambda: dup_pexquo(f, g, QQ)) + + +def test_dmp_pdiv(): + f = dmp_normal([[1], [], [1, 0, 0]], 1, ZZ) + g = dmp_normal([[1], [-1, 0]], 1, ZZ) + + q = dmp_normal([[1], [1, 0]], 1, ZZ) + r = dmp_normal([[2, 0, 0]], 1, ZZ) + + assert dmp_pdiv(f, g, 1, ZZ) == (q, r) + assert dmp_pquo(f, g, 1, ZZ) == q + assert dmp_prem(f, g, 1, ZZ) == r + + raises(ExactQuotientFailed, lambda: dmp_pexquo(f, g, 1, ZZ)) + + f = dmp_normal([[1], [], [1, 0, 0]], 1, ZZ) + g = dmp_normal([[2], [-2, 0]], 1, ZZ) + + q = dmp_normal([[2], [2, 0]], 1, ZZ) + r = dmp_normal([[8, 0, 0]], 1, ZZ) + + assert dmp_pdiv(f, g, 1, ZZ) == (q, r) + assert dmp_pquo(f, g, 1, ZZ) == q + assert dmp_prem(f, g, 1, ZZ) == r + + raises(ExactQuotientFailed, lambda: dmp_pexquo(f, g, 1, ZZ)) + + +def test_dup_rr_div(): + raises(ZeroDivisionError, lambda: dup_rr_div([1, 2, 3], [], ZZ)) + + f = dup_normal([3, 1, 1, 5], ZZ) + g = dup_normal([5, -3, 1], ZZ) + + q, r = [], f + + assert dup_rr_div(f, g, ZZ) == (q, r) + + +def test_dmp_rr_div(): + raises(ZeroDivisionError, lambda: dmp_rr_div([[1, 2], [3]], [[]], 1, ZZ)) + + f = dmp_normal([[1], [], [1, 0, 0]], 1, ZZ) + g = dmp_normal([[1], [-1, 0]], 1, ZZ) + + q = dmp_normal([[1], [1, 0]], 1, ZZ) + r = dmp_normal([[2, 0, 0]], 1, ZZ) + + assert dmp_rr_div(f, g, 1, ZZ) == (q, r) + + f = dmp_normal([[1], [], [1, 0, 0]], 1, ZZ) + g = dmp_normal([[-1], [1, 0]], 1, ZZ) + + q = dmp_normal([[-1], [-1, 0]], 1, ZZ) + r = dmp_normal([[2, 0, 0]], 1, ZZ) + + assert dmp_rr_div(f, g, 1, ZZ) == (q, r) + + f = dmp_normal([[1], [], [1, 0, 0]], 1, ZZ) + g = dmp_normal([[2], [-2, 0]], 1, ZZ) + + q, r = [[]], f + + assert dmp_rr_div(f, g, 1, ZZ) == (q, r) + + +def test_dup_ff_div(): + raises(ZeroDivisionError, lambda: dup_ff_div([1, 2, 3], [], QQ)) + + f = dup_normal([3, 1, 1, 5], QQ) + g = dup_normal([5, -3, 1], QQ) + + q = [QQ(3, 5), QQ(14, 25)] + r = [QQ(52, 25), QQ(111, 25)] + + assert dup_ff_div(f, g, QQ) == (q, r) + +def test_dup_ff_div_gmpy2(): + if GROUND_TYPES != 'gmpy2': + return + + from gmpy2 import mpq + from sympy.polys.domains import GMPYRationalField + K = GMPYRationalField() + + f = [mpq(1,3), mpq(3,2)] + g = [mpq(2,1)] + assert dmp_ff_div(f, g, 0, K) == ([mpq(1,6), mpq(3,4)], []) + + f = [mpq(1,2), mpq(1,3), mpq(1,4), mpq(1,5)] + g = [mpq(-1,1), mpq(1,1), mpq(-1,1)] + assert dmp_ff_div(f, g, 0, K) == ([mpq(-1,2), mpq(-5,6)], [mpq(7,12), mpq(-19,30)]) + +def test_dmp_ff_div(): + raises(ZeroDivisionError, lambda: dmp_ff_div([[1, 2], [3]], [[]], 1, QQ)) + + f = dmp_normal([[1], [], [1, 0, 0]], 1, QQ) + g = dmp_normal([[1], [-1, 0]], 1, QQ) + + q = [[QQ(1, 1)], [QQ(1, 1), QQ(0, 1)]] + r = [[QQ(2, 1), QQ(0, 1), QQ(0, 1)]] + + assert dmp_ff_div(f, g, 1, QQ) == (q, r) + + f = dmp_normal([[1], [], [1, 0, 0]], 1, QQ) + g = dmp_normal([[-1], [1, 0]], 1, QQ) + + q = [[QQ(-1, 1)], [QQ(-1, 1), QQ(0, 1)]] + r = [[QQ(2, 1), QQ(0, 1), QQ(0, 1)]] + + assert dmp_ff_div(f, g, 1, QQ) == (q, r) + + f = dmp_normal([[1], [], [1, 0, 0]], 1, QQ) + g = dmp_normal([[2], [-2, 0]], 1, QQ) + + q = [[QQ(1, 2)], [QQ(1, 2), QQ(0, 1)]] + r = [[QQ(2, 1), QQ(0, 1), QQ(0, 1)]] + + assert dmp_ff_div(f, g, 1, QQ) == (q, r) + + +def test_dup_div(): + f, g, q, r = [5, 4, 3, 2, 1], [1, 2, 3], [5, -6, 0], [20, 1] + + assert dup_div(f, g, ZZ) == (q, r) + assert dup_quo(f, g, ZZ) == q + assert dup_rem(f, g, ZZ) == r + + raises(ExactQuotientFailed, lambda: dup_exquo(f, g, ZZ)) + + f, g, q, r = [5, 4, 3, 2, 1, 0], [1, 2, 0, 0, 9], [5, -6], [15, 2, -44, 54] + + assert dup_div(f, g, ZZ) == (q, r) + assert dup_quo(f, g, ZZ) == q + assert dup_rem(f, g, ZZ) == r + + raises(ExactQuotientFailed, lambda: dup_exquo(f, g, ZZ)) + + +def test_dmp_div(): + f, g, q, r = [5, 4, 3, 2, 1], [1, 2, 3], [5, -6, 0], [20, 1] + + assert dmp_div(f, g, 0, ZZ) == (q, r) + assert dmp_quo(f, g, 0, ZZ) == q + assert dmp_rem(f, g, 0, ZZ) == r + + raises(ExactQuotientFailed, lambda: dmp_exquo(f, g, 0, ZZ)) + + f, g, q, r = [[[1]]], [[[2]], [1]], [[[]]], [[[1]]] + + assert dmp_div(f, g, 2, ZZ) == (q, r) + assert dmp_quo(f, g, 2, ZZ) == q + assert dmp_rem(f, g, 2, ZZ) == r + + raises(ExactQuotientFailed, lambda: dmp_exquo(f, g, 2, ZZ)) + + +def test_dup_max_norm(): + assert dup_max_norm([], ZZ) == 0 + assert dup_max_norm([1], ZZ) == 1 + + assert dup_max_norm([1, 4, 2, 3], ZZ) == 4 + + +def test_dmp_max_norm(): + assert dmp_max_norm([[[]]], 2, ZZ) == 0 + assert dmp_max_norm([[[1]]], 2, ZZ) == 1 + + assert dmp_max_norm(f_0, 2, ZZ) == 6 + + +def test_dup_l1_norm(): + assert dup_l1_norm([], ZZ) == 0 + assert dup_l1_norm([1], ZZ) == 1 + assert dup_l1_norm([1, 4, 2, 3], ZZ) == 10 + + +def test_dmp_l1_norm(): + assert dmp_l1_norm([[[]]], 2, ZZ) == 0 + assert dmp_l1_norm([[[1]]], 2, ZZ) == 1 + + assert dmp_l1_norm(f_0, 2, ZZ) == 31 + + +def test_dup_l2_norm_squared(): + assert dup_l2_norm_squared([], ZZ) == 0 + assert dup_l2_norm_squared([1], ZZ) == 1 + assert dup_l2_norm_squared([1, 4, 2, 3], ZZ) == 30 + + +def test_dmp_l2_norm_squared(): + assert dmp_l2_norm_squared([[[]]], 2, ZZ) == 0 + assert dmp_l2_norm_squared([[[1]]], 2, ZZ) == 1 + assert dmp_l2_norm_squared(f_0, 2, ZZ) == 111 + + +def test_dup_expand(): + assert dup_expand((), ZZ) == [1] + assert dup_expand(([1, 2, 3], [1, 2], [7, 5, 4, 3]), ZZ) == \ + dup_mul([1, 2, 3], dup_mul([1, 2], [7, 5, 4, 3], ZZ), ZZ) + + +def test_dmp_expand(): + assert dmp_expand((), 1, ZZ) == [[1]] + assert dmp_expand(([[1], [2], [3]], [[1], [2]], [[7], [5], [4], [3]]), 1, ZZ) == \ + dmp_mul([[1], [2], [3]], dmp_mul([[1], [2]], [[7], [5], [ + 4], [3]], 1, ZZ), 1, ZZ) + +def test_dup_mul_poly(): + p = Poly(18786186952704.0*x**165 + 9.31746684052255e+31*x**82, x, domain='RR') + px = Poly(18786186952704.0*x**166 + 9.31746684052255e+31*x**83, x, domain='RR') + + assert p * x == px + assert p.set_domain(QQ) * x == px.set_domain(QQ) + assert p.set_domain(CC) * x == px.set_domain(CC) diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_densebasic.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_densebasic.py new file mode 100644 index 0000000000000000000000000000000000000000..43386d86d0e6ec7b20d3962d8063aa6402165f9a --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_densebasic.py @@ -0,0 +1,730 @@ +"""Tests for dense recursive polynomials' basic tools. """ + +from sympy.polys.densebasic import ( + ninf, + dup_LC, dmp_LC, + dup_TC, dmp_TC, + dmp_ground_LC, dmp_ground_TC, + dmp_true_LT, + dup_degree, dmp_degree, + dmp_degree_in, dmp_degree_list, + dup_strip, dmp_strip, + dmp_validate, + dup_reverse, + dup_copy, dmp_copy, + dup_normal, dmp_normal, + dup_convert, dmp_convert, + dup_from_sympy, dmp_from_sympy, + dup_nth, dmp_nth, dmp_ground_nth, + dmp_zero_p, dmp_zero, + dmp_one_p, dmp_one, + dmp_ground_p, dmp_ground, + dmp_negative_p, dmp_positive_p, + dmp_zeros, dmp_grounds, + dup_from_dict, dup_from_raw_dict, + dup_to_dict, dup_to_raw_dict, + dmp_from_dict, dmp_to_dict, + dmp_swap, dmp_permute, + dmp_nest, dmp_raise, + dup_deflate, dmp_deflate, + dup_multi_deflate, dmp_multi_deflate, + dup_inflate, dmp_inflate, + dmp_exclude, dmp_include, + dmp_inject, dmp_eject, + dup_terms_gcd, dmp_terms_gcd, + dmp_list_terms, dmp_apply_pairs, + dup_slice, + dup_random, +) + +from sympy.polys.specialpolys import f_polys +from sympy.polys.domains import ZZ, QQ +from sympy.polys.rings import ring + +from sympy.core.singleton import S +from sympy.testing.pytest import raises + +from sympy.core.numbers import oo + +f_0, f_1, f_2, f_3, f_4, f_5, f_6 = [ f.to_dense() for f in f_polys() ] + +def test_dup_LC(): + assert dup_LC([], ZZ) == 0 + assert dup_LC([2, 3, 4, 5], ZZ) == 2 + + +def test_dup_TC(): + assert dup_TC([], ZZ) == 0 + assert dup_TC([2, 3, 4, 5], ZZ) == 5 + + +def test_dmp_LC(): + assert dmp_LC([[]], ZZ) == [] + assert dmp_LC([[2, 3, 4], [5]], ZZ) == [2, 3, 4] + assert dmp_LC([[[]]], ZZ) == [[]] + assert dmp_LC([[[2], [3, 4]], [[5]]], ZZ) == [[2], [3, 4]] + + +def test_dmp_TC(): + assert dmp_TC([[]], ZZ) == [] + assert dmp_TC([[2, 3, 4], [5]], ZZ) == [5] + assert dmp_TC([[[]]], ZZ) == [[]] + assert dmp_TC([[[2], [3, 4]], [[5]]], ZZ) == [[5]] + + +def test_dmp_ground_LC(): + assert dmp_ground_LC([[]], 1, ZZ) == 0 + assert dmp_ground_LC([[2, 3, 4], [5]], 1, ZZ) == 2 + assert dmp_ground_LC([[[]]], 2, ZZ) == 0 + assert dmp_ground_LC([[[2], [3, 4]], [[5]]], 2, ZZ) == 2 + + +def test_dmp_ground_TC(): + assert dmp_ground_TC([[]], 1, ZZ) == 0 + assert dmp_ground_TC([[2, 3, 4], [5]], 1, ZZ) == 5 + assert dmp_ground_TC([[[]]], 2, ZZ) == 0 + assert dmp_ground_TC([[[2], [3, 4]], [[5]]], 2, ZZ) == 5 + + +def test_dmp_true_LT(): + assert dmp_true_LT([[]], 1, ZZ) == ((0, 0), 0) + assert dmp_true_LT([[7]], 1, ZZ) == ((0, 0), 7) + + assert dmp_true_LT([[1, 0]], 1, ZZ) == ((0, 1), 1) + assert dmp_true_LT([[1], []], 1, ZZ) == ((1, 0), 1) + assert dmp_true_LT([[1, 0], []], 1, ZZ) == ((1, 1), 1) + + +def test_dup_degree(): + assert ninf == float('-inf') + assert dup_degree([]) is ninf + assert dup_degree([1]) == 0 + assert dup_degree([1, 0]) == 1 + assert dup_degree([1, 0, 0, 0, 1]) == 4 + + +def test_dmp_degree(): + assert dmp_degree([[]], 1) is ninf + assert dmp_degree([[[]]], 2) is ninf + + assert dmp_degree([[1]], 1) == 0 + assert dmp_degree([[2], [1]], 1) == 1 + + +def test_dmp_degree_in(): + assert dmp_degree_in([[[]]], 0, 2) is ninf + assert dmp_degree_in([[[]]], 1, 2) is ninf + assert dmp_degree_in([[[]]], 2, 2) is ninf + + assert dmp_degree_in([[[1]]], 0, 2) == 0 + assert dmp_degree_in([[[1]]], 1, 2) == 0 + assert dmp_degree_in([[[1]]], 2, 2) == 0 + + assert dmp_degree_in(f_4, 0, 2) == 9 + assert dmp_degree_in(f_4, 1, 2) == 12 + assert dmp_degree_in(f_4, 2, 2) == 8 + + assert dmp_degree_in(f_6, 0, 2) == 4 + assert dmp_degree_in(f_6, 1, 2) == 4 + assert dmp_degree_in(f_6, 2, 2) == 6 + assert dmp_degree_in(f_6, 3, 3) == 3 + + raises(IndexError, lambda: dmp_degree_in([[1]], -5, 1)) + + +def test_dmp_degree_list(): + assert dmp_degree_list([[[[ ]]]], 3) == (-oo, -oo, -oo, -oo) + assert dmp_degree_list([[[[1]]]], 3) == ( 0, 0, 0, 0) + + assert dmp_degree_list(f_0, 2) == (2, 2, 2) + assert dmp_degree_list(f_1, 2) == (3, 3, 3) + assert dmp_degree_list(f_2, 2) == (5, 3, 3) + assert dmp_degree_list(f_3, 2) == (5, 4, 7) + assert dmp_degree_list(f_4, 2) == (9, 12, 8) + assert dmp_degree_list(f_5, 2) == (3, 3, 3) + assert dmp_degree_list(f_6, 3) == (4, 4, 6, 3) + + +def test_dup_strip(): + assert dup_strip([]) == [] + assert dup_strip([0]) == [] + assert dup_strip([0, 0, 0]) == [] + + assert dup_strip([1]) == [1] + assert dup_strip([0, 1]) == [1] + assert dup_strip([0, 0, 0, 1]) == [1] + + assert dup_strip([1, 2, 0]) == [1, 2, 0] + assert dup_strip([0, 1, 2, 0]) == [1, 2, 0] + assert dup_strip([0, 0, 0, 1, 2, 0]) == [1, 2, 0] + + +def test_dmp_strip(): + assert dmp_strip([0, 1, 0], 0) == [1, 0] + + assert dmp_strip([[]], 1) == [[]] + assert dmp_strip([[], []], 1) == [[]] + assert dmp_strip([[], [], []], 1) == [[]] + + assert dmp_strip([[[]]], 2) == [[[]]] + assert dmp_strip([[[]], [[]]], 2) == [[[]]] + assert dmp_strip([[[]], [[]], [[]]], 2) == [[[]]] + + assert dmp_strip([[[1]]], 2) == [[[1]]] + assert dmp_strip([[[]], [[1]]], 2) == [[[1]]] + assert dmp_strip([[[]], [[1]], [[]]], 2) == [[[1]], [[]]] + + +def test_dmp_validate(): + assert dmp_validate([]) == ([], 0) + assert dmp_validate([0, 0, 0, 1, 0]) == ([1, 0], 0) + + assert dmp_validate([[[]]]) == ([[[]]], 2) + assert dmp_validate([[0], [], [0], [1], [0]]) == ([[1], []], 1) + + raises(ValueError, lambda: dmp_validate([[0], 0, [0], [1], [0]])) + + +def test_dup_reverse(): + assert dup_reverse([1, 2, 0, 3]) == [3, 0, 2, 1] + assert dup_reverse([1, 2, 3, 0]) == [3, 2, 1] + + +def test_dup_copy(): + f = [ZZ(1), ZZ(0), ZZ(2)] + g = dup_copy(f) + + g[0], g[2] = ZZ(7), ZZ(0) + + assert f != g + + +def test_dmp_copy(): + f = [[ZZ(1)], [ZZ(2), ZZ(0)]] + g = dmp_copy(f, 1) + + g[0][0], g[1][1] = ZZ(7), ZZ(1) + + assert f != g + + +def test_dup_normal(): + assert dup_normal([0, 0, 2, 1, 0, 11, 0], ZZ) == \ + [ZZ(2), ZZ(1), ZZ(0), ZZ(11), ZZ(0)] + + +def test_dmp_normal(): + assert dmp_normal([[0], [], [0, 2, 1], [0], [11], []], 1, ZZ) == \ + [[ZZ(2), ZZ(1)], [], [ZZ(11)], []] + + +def test_dup_convert(): + K0, K1 = ZZ['x'], ZZ + + f = [K0(1), K0(2), K0(0), K0(3)] + + assert dup_convert(f, K0, K1) == \ + [ZZ(1), ZZ(2), ZZ(0), ZZ(3)] + + +def test_dmp_convert(): + K0, K1 = ZZ['x'], ZZ + + f = [[K0(1)], [K0(2)], [], [K0(3)]] + + assert dmp_convert(f, 1, K0, K1) == \ + [[ZZ(1)], [ZZ(2)], [], [ZZ(3)]] + + +def test_dup_from_sympy(): + assert dup_from_sympy([S.One, S(2)], ZZ) == \ + [ZZ(1), ZZ(2)] + assert dup_from_sympy([S.Half, S(3)], QQ) == \ + [QQ(1, 2), QQ(3, 1)] + + +def test_dmp_from_sympy(): + assert dmp_from_sympy([[S.One, S(2)], [S.Zero]], 1, ZZ) == \ + [[ZZ(1), ZZ(2)], []] + assert dmp_from_sympy([[S.Half, S(2)]], 1, QQ) == \ + [[QQ(1, 2), QQ(2, 1)]] + + +def test_dup_nth(): + assert dup_nth([1, 2, 3], 0, ZZ) == 3 + assert dup_nth([1, 2, 3], 1, ZZ) == 2 + assert dup_nth([1, 2, 3], 2, ZZ) == 1 + + assert dup_nth([1, 2, 3], 9, ZZ) == 0 + + raises(IndexError, lambda: dup_nth([3, 4, 5], -1, ZZ)) + + +def test_dmp_nth(): + assert dmp_nth([[1], [2], [3]], 0, 1, ZZ) == [3] + assert dmp_nth([[1], [2], [3]], 1, 1, ZZ) == [2] + assert dmp_nth([[1], [2], [3]], 2, 1, ZZ) == [1] + + assert dmp_nth([[1], [2], [3]], 9, 1, ZZ) == [] + + raises(IndexError, lambda: dmp_nth([[3], [4], [5]], -1, 1, ZZ)) + + +def test_dmp_ground_nth(): + assert dmp_ground_nth([[]], (0, 0), 1, ZZ) == 0 + assert dmp_ground_nth([[1], [2], [3]], (0, 0), 1, ZZ) == 3 + assert dmp_ground_nth([[1], [2], [3]], (1, 0), 1, ZZ) == 2 + assert dmp_ground_nth([[1], [2], [3]], (2, 0), 1, ZZ) == 1 + + assert dmp_ground_nth([[1], [2], [3]], (2, 1), 1, ZZ) == 0 + assert dmp_ground_nth([[1], [2], [3]], (3, 0), 1, ZZ) == 0 + + raises(IndexError, lambda: dmp_ground_nth([[3], [4], [5]], (2, -1), 1, ZZ)) + + +def test_dmp_zero_p(): + assert dmp_zero_p([], 0) is True + assert dmp_zero_p([[]], 1) is True + + assert dmp_zero_p([[[]]], 2) is True + assert dmp_zero_p([[[1]]], 2) is False + + +def test_dmp_zero(): + assert dmp_zero(0) == [] + assert dmp_zero(2) == [[[]]] + + +def test_dmp_one_p(): + assert dmp_one_p([1], 0, ZZ) is True + assert dmp_one_p([[1]], 1, ZZ) is True + assert dmp_one_p([[[1]]], 2, ZZ) is True + assert dmp_one_p([[[12]]], 2, ZZ) is False + + +def test_dmp_one(): + assert dmp_one(0, ZZ) == [ZZ(1)] + assert dmp_one(2, ZZ) == [[[ZZ(1)]]] + + +def test_dmp_ground_p(): + assert dmp_ground_p([], 0, 0) is True + assert dmp_ground_p([[]], 0, 1) is True + assert dmp_ground_p([[]], 1, 1) is False + + assert dmp_ground_p([[ZZ(1)]], 1, 1) is True + assert dmp_ground_p([[[ZZ(2)]]], 2, 2) is True + + assert dmp_ground_p([[[ZZ(2)]]], 3, 2) is False + assert dmp_ground_p([[[ZZ(3)], []]], 3, 2) is False + + assert dmp_ground_p([], None, 0) is True + assert dmp_ground_p([[]], None, 1) is True + + assert dmp_ground_p([ZZ(1)], None, 0) is True + assert dmp_ground_p([[[ZZ(1)]]], None, 2) is True + + assert dmp_ground_p([[[ZZ(3)], []]], None, 2) is False + + +def test_dmp_ground(): + assert dmp_ground(ZZ(0), 2) == [[[]]] + + assert dmp_ground(ZZ(7), -1) == ZZ(7) + assert dmp_ground(ZZ(7), 0) == [ZZ(7)] + assert dmp_ground(ZZ(7), 2) == [[[ZZ(7)]]] + + +def test_dmp_zeros(): + assert dmp_zeros(4, 0, ZZ) == [[], [], [], []] + + assert dmp_zeros(0, 2, ZZ) == [] + assert dmp_zeros(1, 2, ZZ) == [[[[]]]] + assert dmp_zeros(2, 2, ZZ) == [[[[]]], [[[]]]] + assert dmp_zeros(3, 2, ZZ) == [[[[]]], [[[]]], [[[]]]] + + assert dmp_zeros(3, -1, ZZ) == [0, 0, 0] + + +def test_dmp_grounds(): + assert dmp_grounds(ZZ(7), 0, 2) == [] + + assert dmp_grounds(ZZ(7), 1, 2) == [[[[7]]]] + assert dmp_grounds(ZZ(7), 2, 2) == [[[[7]]], [[[7]]]] + assert dmp_grounds(ZZ(7), 3, 2) == [[[[7]]], [[[7]]], [[[7]]]] + + assert dmp_grounds(ZZ(7), 3, -1) == [7, 7, 7] + + +def test_dmp_negative_p(): + assert dmp_negative_p([[[]]], 2, ZZ) is False + assert dmp_negative_p([[[1], [2]]], 2, ZZ) is False + assert dmp_negative_p([[[-1], [2]]], 2, ZZ) is True + + +def test_dmp_positive_p(): + assert dmp_positive_p([[[]]], 2, ZZ) is False + assert dmp_positive_p([[[1], [2]]], 2, ZZ) is True + assert dmp_positive_p([[[-1], [2]]], 2, ZZ) is False + + +def test_dup_from_to_dict(): + assert dup_from_raw_dict({}, ZZ) == [] + assert dup_from_dict({}, ZZ) == [] + + assert dup_to_raw_dict([]) == {} + assert dup_to_dict([]) == {} + + assert dup_to_raw_dict([], ZZ, zero=True) == {0: ZZ(0)} + assert dup_to_dict([], ZZ, zero=True) == {(0,): ZZ(0)} + + f = [3, 0, 0, 2, 0, 0, 0, 0, 8] + g = {8: 3, 5: 2, 0: 8} + h = {(8,): 3, (5,): 2, (0,): 8} + + assert dup_from_raw_dict(g, ZZ) == f + assert dup_from_dict(h, ZZ) == f + + assert dup_to_raw_dict(f) == g + assert dup_to_dict(f) == h + + R, x,y = ring("x,y", ZZ) + K = R.to_domain() + + f = [R(3), R(0), R(2), R(0), R(0), R(8)] + g = {5: R(3), 3: R(2), 0: R(8)} + h = {(5,): R(3), (3,): R(2), (0,): R(8)} + + assert dup_from_raw_dict(g, K) == f + assert dup_from_dict(h, K) == f + + assert dup_to_raw_dict(f) == g + assert dup_to_dict(f) == h + + +def test_dmp_from_to_dict(): + assert dmp_from_dict({}, 1, ZZ) == [[]] + assert dmp_to_dict([[]], 1) == {} + + assert dmp_to_dict([], 0, ZZ, zero=True) == {(0,): ZZ(0)} + assert dmp_to_dict([[]], 1, ZZ, zero=True) == {(0, 0): ZZ(0)} + + f = [[3], [], [], [2], [], [], [], [], [8]] + g = {(8, 0): 3, (5, 0): 2, (0, 0): 8} + + assert dmp_from_dict(g, 1, ZZ) == f + assert dmp_to_dict(f, 1) == g + + +def test_dmp_swap(): + f = dmp_normal([[1, 0, 0], [], [1, 0], [], [1]], 1, ZZ) + g = dmp_normal([[1, 0, 0, 0, 0], [1, 0, 0], [1]], 1, ZZ) + + assert dmp_swap(f, 1, 1, 1, ZZ) == f + + assert dmp_swap(f, 0, 1, 1, ZZ) == g + assert dmp_swap(g, 0, 1, 1, ZZ) == f + + raises(IndexError, lambda: dmp_swap(f, -1, -7, 1, ZZ)) + + +def test_dmp_permute(): + f = dmp_normal([[1, 0, 0], [], [1, 0], [], [1]], 1, ZZ) + g = dmp_normal([[1, 0, 0, 0, 0], [1, 0, 0], [1]], 1, ZZ) + + assert dmp_permute(f, [0, 1], 1, ZZ) == f + assert dmp_permute(g, [0, 1], 1, ZZ) == g + + assert dmp_permute(f, [1, 0], 1, ZZ) == g + assert dmp_permute(g, [1, 0], 1, ZZ) == f + + +def test_dmp_nest(): + assert dmp_nest(ZZ(1), 2, ZZ) == [[[1]]] + + assert dmp_nest([[1]], 0, ZZ) == [[1]] + assert dmp_nest([[1]], 1, ZZ) == [[[1]]] + assert dmp_nest([[1]], 2, ZZ) == [[[[1]]]] + + +def test_dmp_raise(): + assert dmp_raise([], 2, 0, ZZ) == [[[]]] + assert dmp_raise([[1]], 0, 1, ZZ) == [[1]] + + assert dmp_raise([[1, 2, 3], [], [2, 3]], 2, 1, ZZ) == \ + [[[[1]], [[2]], [[3]]], [[[]]], [[[2]], [[3]]]] + + +def test_dup_deflate(): + assert dup_deflate([], ZZ) == (1, []) + assert dup_deflate([2], ZZ) == (1, [2]) + assert dup_deflate([1, 2, 3], ZZ) == (1, [1, 2, 3]) + assert dup_deflate([1, 0, 2, 0, 3], ZZ) == (2, [1, 2, 3]) + + assert dup_deflate(dup_from_raw_dict({7: 1, 1: 1}, ZZ), ZZ) == \ + (1, [1, 0, 0, 0, 0, 0, 1, 0]) + assert dup_deflate(dup_from_raw_dict({7: 1, 0: 1}, ZZ), ZZ) == \ + (7, [1, 1]) + assert dup_deflate(dup_from_raw_dict({7: 1, 3: 1}, ZZ), ZZ) == \ + (1, [1, 0, 0, 0, 1, 0, 0, 0]) + + assert dup_deflate(dup_from_raw_dict({7: 1, 4: 1}, ZZ), ZZ) == \ + (1, [1, 0, 0, 1, 0, 0, 0, 0]) + assert dup_deflate(dup_from_raw_dict({8: 1, 4: 1}, ZZ), ZZ) == \ + (4, [1, 1, 0]) + + assert dup_deflate(dup_from_raw_dict({8: 1}, ZZ), ZZ) == \ + (8, [1, 0]) + assert dup_deflate(dup_from_raw_dict({7: 1}, ZZ), ZZ) == \ + (7, [1, 0]) + assert dup_deflate(dup_from_raw_dict({1: 1}, ZZ), ZZ) == \ + (1, [1, 0]) + + +def test_dmp_deflate(): + assert dmp_deflate([[]], 1, ZZ) == ((1, 1), [[]]) + assert dmp_deflate([[2]], 1, ZZ) == ((1, 1), [[2]]) + + f = [[1, 0, 0], [], [1, 0], [], [1]] + + assert dmp_deflate(f, 1, ZZ) == ((2, 1), [[1, 0, 0], [1, 0], [1]]) + + +def test_dup_multi_deflate(): + assert dup_multi_deflate(([2],), ZZ) == (1, ([2],)) + assert dup_multi_deflate(([], []), ZZ) == (1, ([], [])) + + assert dup_multi_deflate(([1, 2, 3],), ZZ) == (1, ([1, 2, 3],)) + assert dup_multi_deflate(([1, 0, 2, 0, 3],), ZZ) == (2, ([1, 2, 3],)) + + assert dup_multi_deflate(([1, 0, 2, 0, 3], [2, 0, 0]), ZZ) == \ + (2, ([1, 2, 3], [2, 0])) + assert dup_multi_deflate(([1, 0, 2, 0, 3], [2, 1, 0]), ZZ) == \ + (1, ([1, 0, 2, 0, 3], [2, 1, 0])) + + +def test_dmp_multi_deflate(): + assert dmp_multi_deflate(([[]],), 1, ZZ) == \ + ((1, 1), ([[]],)) + assert dmp_multi_deflate(([[]], [[]]), 1, ZZ) == \ + ((1, 1), ([[]], [[]])) + + assert dmp_multi_deflate(([[1]], [[]]), 1, ZZ) == \ + ((1, 1), ([[1]], [[]])) + assert dmp_multi_deflate(([[1]], [[2]]), 1, ZZ) == \ + ((1, 1), ([[1]], [[2]])) + assert dmp_multi_deflate(([[1]], [[2, 0]]), 1, ZZ) == \ + ((1, 1), ([[1]], [[2, 0]])) + + assert dmp_multi_deflate(([[2, 0]], [[2, 0]]), 1, ZZ) == \ + ((1, 1), ([[2, 0]], [[2, 0]])) + + assert dmp_multi_deflate( + ([[2]], [[2, 0, 0]]), 1, ZZ) == ((1, 2), ([[2]], [[2, 0]])) + assert dmp_multi_deflate( + ([[2, 0, 0]], [[2, 0, 0]]), 1, ZZ) == ((1, 2), ([[2, 0]], [[2, 0]])) + + assert dmp_multi_deflate(([2, 0, 0], [1, 0, 4, 0, 1]), 0, ZZ) == \ + ((2,), ([2, 0], [1, 4, 1])) + + f = [[1, 0, 0], [], [1, 0], [], [1]] + g = [[1, 0, 1, 0], [], [1]] + + assert dmp_multi_deflate((f,), 1, ZZ) == \ + ((2, 1), ([[1, 0, 0], [1, 0], [1]],)) + + assert dmp_multi_deflate((f, g), 1, ZZ) == \ + ((2, 1), ([[1, 0, 0], [1, 0], [1]], + [[1, 0, 1, 0], [1]])) + + +def test_dup_inflate(): + assert dup_inflate([], 17, ZZ) == [] + + assert dup_inflate([1, 2, 3], 1, ZZ) == [1, 2, 3] + assert dup_inflate([1, 2, 3], 2, ZZ) == [1, 0, 2, 0, 3] + assert dup_inflate([1, 2, 3], 3, ZZ) == [1, 0, 0, 2, 0, 0, 3] + assert dup_inflate([1, 2, 3], 4, ZZ) == [1, 0, 0, 0, 2, 0, 0, 0, 3] + + raises(IndexError, lambda: dup_inflate([1, 2, 3], 0, ZZ)) + + +def test_dmp_inflate(): + assert dmp_inflate([1], (3,), 0, ZZ) == [1] + + assert dmp_inflate([[]], (3, 7), 1, ZZ) == [[]] + assert dmp_inflate([[2]], (1, 2), 1, ZZ) == [[2]] + + assert dmp_inflate([[2, 0]], (1, 1), 1, ZZ) == [[2, 0]] + assert dmp_inflate([[2, 0]], (1, 2), 1, ZZ) == [[2, 0, 0]] + assert dmp_inflate([[2, 0]], (1, 3), 1, ZZ) == [[2, 0, 0, 0]] + + assert dmp_inflate([[1, 0, 0], [1], [1, 0]], (2, 1), 1, ZZ) == \ + [[1, 0, 0], [], [1], [], [1, 0]] + + raises(IndexError, lambda: dmp_inflate([[]], (-3, 7), 1, ZZ)) + + +def test_dmp_exclude(): + assert dmp_exclude([[[]]], 2, ZZ) == ([], [[[]]], 2) + assert dmp_exclude([[[7]]], 2, ZZ) == ([], [[[7]]], 2) + + assert dmp_exclude([1, 2, 3], 0, ZZ) == ([], [1, 2, 3], 0) + assert dmp_exclude([[1], [2, 3]], 1, ZZ) == ([], [[1], [2, 3]], 1) + + assert dmp_exclude([[1, 2, 3]], 1, ZZ) == ([0], [1, 2, 3], 0) + assert dmp_exclude([[1], [2], [3]], 1, ZZ) == ([1], [1, 2, 3], 0) + + assert dmp_exclude([[[1, 2, 3]]], 2, ZZ) == ([0, 1], [1, 2, 3], 0) + assert dmp_exclude([[[1]], [[2]], [[3]]], 2, ZZ) == ([1, 2], [1, 2, 3], 0) + + +def test_dmp_include(): + assert dmp_include([1, 2, 3], [], 0, ZZ) == [1, 2, 3] + + assert dmp_include([1, 2, 3], [0], 0, ZZ) == [[1, 2, 3]] + assert dmp_include([1, 2, 3], [1], 0, ZZ) == [[1], [2], [3]] + + assert dmp_include([1, 2, 3], [0, 1], 0, ZZ) == [[[1, 2, 3]]] + assert dmp_include([1, 2, 3], [1, 2], 0, ZZ) == [[[1]], [[2]], [[3]]] + + +def test_dmp_inject(): + R, x,y = ring("x,y", ZZ) + K = R.to_domain() + + assert dmp_inject([], 0, K) == ([[[]]], 2) + assert dmp_inject([[]], 1, K) == ([[[[]]]], 3) + + assert dmp_inject([R(1)], 0, K) == ([[[1]]], 2) + assert dmp_inject([[R(1)]], 1, K) == ([[[[1]]]], 3) + + assert dmp_inject([R(1), 2*x + 3*y + 4], 0, K) == ([[[1]], [[2], [3, 4]]], 2) + + f = [3*x**2 + 7*x*y + 5*y**2, 2*x, R(0), x*y**2 + 11] + g = [[[3], [7, 0], [5, 0, 0]], [[2], []], [[]], [[1, 0, 0], [11]]] + + assert dmp_inject(f, 0, K) == (g, 2) + + +def test_dmp_eject(): + R, x,y = ring("x,y", ZZ) + K = R.to_domain() + + assert dmp_eject([[[]]], 2, K) == [] + assert dmp_eject([[[[]]]], 3, K) == [[]] + + assert dmp_eject([[[1]]], 2, K) == [R(1)] + assert dmp_eject([[[[1]]]], 3, K) == [[R(1)]] + + assert dmp_eject([[[1]], [[2], [3, 4]]], 2, K) == [R(1), 2*x + 3*y + 4] + + f = [3*x**2 + 7*x*y + 5*y**2, 2*x, R(0), x*y**2 + 11] + g = [[[3], [7, 0], [5, 0, 0]], [[2], []], [[]], [[1, 0, 0], [11]]] + + assert dmp_eject(g, 2, K) == f + + +def test_dup_terms_gcd(): + assert dup_terms_gcd([], ZZ) == (0, []) + assert dup_terms_gcd([1, 0, 1], ZZ) == (0, [1, 0, 1]) + assert dup_terms_gcd([1, 0, 1, 0], ZZ) == (1, [1, 0, 1]) + + +def test_dmp_terms_gcd(): + assert dmp_terms_gcd([[]], 1, ZZ) == ((0, 0), [[]]) + + assert dmp_terms_gcd([1, 0, 1, 0], 0, ZZ) == ((1,), [1, 0, 1]) + assert dmp_terms_gcd([[1], [], [1], []], 1, ZZ) == ((1, 0), [[1], [], [1]]) + + assert dmp_terms_gcd( + [[1, 0], [], [1]], 1, ZZ) == ((0, 0), [[1, 0], [], [1]]) + assert dmp_terms_gcd( + [[1, 0], [1, 0, 0], [], []], 1, ZZ) == ((2, 1), [[1], [1, 0]]) + + +def test_dmp_list_terms(): + assert dmp_list_terms([[[]]], 2, ZZ) == [((0, 0, 0), 0)] + assert dmp_list_terms([[[1]]], 2, ZZ) == [((0, 0, 0), 1)] + + assert dmp_list_terms([1, 2, 4, 3, 5], 0, ZZ) == \ + [((4,), 1), ((3,), 2), ((2,), 4), ((1,), 3), ((0,), 5)] + + assert dmp_list_terms([[1], [2, 4], [3, 5, 0]], 1, ZZ) == \ + [((2, 0), 1), ((1, 1), 2), ((1, 0), 4), ((0, 2), 3), ((0, 1), 5)] + + f = [[2, 0, 0, 0], [1, 0, 0], []] + + assert dmp_list_terms(f, 1, ZZ, order='lex') == [((2, 3), 2), ((1, 2), 1)] + assert dmp_list_terms( + f, 1, ZZ, order='grlex') == [((2, 3), 2), ((1, 2), 1)] + + f = [[2, 0, 0, 0], [1, 0, 0, 0, 0, 0], []] + + assert dmp_list_terms(f, 1, ZZ, order='lex') == [((2, 3), 2), ((1, 5), 1)] + assert dmp_list_terms( + f, 1, ZZ, order='grlex') == [((1, 5), 1), ((2, 3), 2)] + + +def test_dmp_apply_pairs(): + h = lambda a, b: a*b + + assert dmp_apply_pairs([1, 2, 3], [4, 5, 6], h, [], 0, ZZ) == [4, 10, 18] + + assert dmp_apply_pairs([2, 3], [4, 5, 6], h, [], 0, ZZ) == [10, 18] + assert dmp_apply_pairs([1, 2, 3], [5, 6], h, [], 0, ZZ) == [10, 18] + + assert dmp_apply_pairs( + [[1, 2], [3]], [[4, 5], [6]], h, [], 1, ZZ) == [[4, 10], [18]] + + assert dmp_apply_pairs( + [[1, 2], [3]], [[4], [5, 6]], h, [], 1, ZZ) == [[8], [18]] + assert dmp_apply_pairs( + [[1], [2, 3]], [[4, 5], [6]], h, [], 1, ZZ) == [[5], [18]] + + +def test_dup_slice(): + f = [1, 2, 3, 4] + + assert dup_slice(f, 0, 0, ZZ) == [] + assert dup_slice(f, 0, 1, ZZ) == [4] + assert dup_slice(f, 0, 2, ZZ) == [3, 4] + assert dup_slice(f, 0, 3, ZZ) == [2, 3, 4] + assert dup_slice(f, 0, 4, ZZ) == [1, 2, 3, 4] + + assert dup_slice(f, 0, 4, ZZ) == f + assert dup_slice(f, 0, 9, ZZ) == f + + assert dup_slice(f, 1, 0, ZZ) == [] + assert dup_slice(f, 1, 1, ZZ) == [] + assert dup_slice(f, 1, 2, ZZ) == [3, 0] + assert dup_slice(f, 1, 3, ZZ) == [2, 3, 0] + assert dup_slice(f, 1, 4, ZZ) == [1, 2, 3, 0] + + assert dup_slice([1, 2], 0, 3, ZZ) == [1, 2] + + g = [1, 0, 0, 2] + + assert dup_slice(g, 0, 3, ZZ) == [2] + + +def test_dup_random(): + f = dup_random(0, -10, 10, ZZ) + + assert dup_degree(f) == 0 + assert all(-10 <= c <= 10 for c in f) + + f = dup_random(1, -20, 20, ZZ) + + assert dup_degree(f) == 1 + assert all(-20 <= c <= 20 for c in f) + + f = dup_random(2, -30, 30, ZZ) + + assert dup_degree(f) == 2 + assert all(-30 <= c <= 30 for c in f) + + f = dup_random(3, -40, 40, ZZ) + + assert dup_degree(f) == 3 + assert all(-40 <= c <= 40 for c in f) diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_densetools.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_densetools.py new file mode 100644 index 0000000000000000000000000000000000000000..b4bebd2a6f061a13a7d34b7689c696456310f62e --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_densetools.py @@ -0,0 +1,714 @@ +"""Tests for dense recursive polynomials' tools. """ + +from sympy.polys.densebasic import ( + dup_normal, dmp_normal, + dup_from_raw_dict, + dmp_convert, dmp_swap, +) + +from sympy.polys.densearith import dmp_mul_ground + +from sympy.polys.densetools import ( + dup_clear_denoms, dmp_clear_denoms, + dup_integrate, dmp_integrate, dmp_integrate_in, + dup_diff, dmp_diff, dmp_diff_in, + dup_eval, dmp_eval, dmp_eval_in, + dmp_eval_tail, dmp_diff_eval_in, + dup_trunc, dmp_trunc, dmp_ground_trunc, + dup_monic, dmp_ground_monic, + dup_content, dmp_ground_content, + dup_primitive, dmp_ground_primitive, + dup_extract, dmp_ground_extract, + dup_real_imag, + dup_mirror, dup_scale, dup_shift, dmp_shift, + dup_transform, + dup_compose, dmp_compose, + dup_decompose, + dmp_lift, + dup_sign_variations, + dup_revert, dmp_revert, +) +from sympy.polys.polyclasses import ANP + +from sympy.polys.polyerrors import ( + MultivariatePolynomialError, + ExactQuotientFailed, + NotReversible, + DomainError, +) + +from sympy.polys.specialpolys import f_polys + +from sympy.polys.domains import FF, ZZ, QQ, ZZ_I, QQ_I, EX, RR +from sympy.polys.rings import ring + +from sympy.core.numbers import I +from sympy.core.singleton import S +from sympy.functions.elementary.trigonometric import sin + +from sympy.abc import x +from sympy.testing.pytest import raises + +f_0, f_1, f_2, f_3, f_4, f_5, f_6 = [ f.to_dense() for f in f_polys() ] + +def test_dup_integrate(): + assert dup_integrate([], 1, QQ) == [] + assert dup_integrate([], 2, QQ) == [] + + assert dup_integrate([QQ(1)], 1, QQ) == [QQ(1), QQ(0)] + assert dup_integrate([QQ(1)], 2, QQ) == [QQ(1, 2), QQ(0), QQ(0)] + + assert dup_integrate([QQ(1), QQ(2), QQ(3)], 0, QQ) == \ + [QQ(1), QQ(2), QQ(3)] + assert dup_integrate([QQ(1), QQ(2), QQ(3)], 1, QQ) == \ + [QQ(1, 3), QQ(1), QQ(3), QQ(0)] + assert dup_integrate([QQ(1), QQ(2), QQ(3)], 2, QQ) == \ + [QQ(1, 12), QQ(1, 3), QQ(3, 2), QQ(0), QQ(0)] + assert dup_integrate([QQ(1), QQ(2), QQ(3)], 3, QQ) == \ + [QQ(1, 60), QQ(1, 12), QQ(1, 2), QQ(0), QQ(0), QQ(0)] + + assert dup_integrate(dup_from_raw_dict({29: QQ(17)}, QQ), 3, QQ) == \ + dup_from_raw_dict({32: QQ(17, 29760)}, QQ) + + assert dup_integrate(dup_from_raw_dict({29: QQ(17), 5: QQ(1, 2)}, QQ), 3, QQ) == \ + dup_from_raw_dict({32: QQ(17, 29760), 8: QQ(1, 672)}, QQ) + + +def test_dmp_integrate(): + assert dmp_integrate([QQ(1)], 2, 0, QQ) == [QQ(1, 2), QQ(0), QQ(0)] + + assert dmp_integrate([[[]]], 1, 2, QQ) == [[[]]] + assert dmp_integrate([[[]]], 2, 2, QQ) == [[[]]] + + assert dmp_integrate([[[QQ(1)]]], 1, 2, QQ) == [[[QQ(1)]], [[]]] + assert dmp_integrate([[[QQ(1)]]], 2, 2, QQ) == [[[QQ(1, 2)]], [[]], [[]]] + + assert dmp_integrate([[QQ(1)], [QQ(2)], [QQ(3)]], 0, 1, QQ) == \ + [[QQ(1)], [QQ(2)], [QQ(3)]] + assert dmp_integrate([[QQ(1)], [QQ(2)], [QQ(3)]], 1, 1, QQ) == \ + [[QQ(1, 3)], [QQ(1)], [QQ(3)], []] + assert dmp_integrate([[QQ(1)], [QQ(2)], [QQ(3)]], 2, 1, QQ) == \ + [[QQ(1, 12)], [QQ(1, 3)], [QQ(3, 2)], [], []] + assert dmp_integrate([[QQ(1)], [QQ(2)], [QQ(3)]], 3, 1, QQ) == \ + [[QQ(1, 60)], [QQ(1, 12)], [QQ(1, 2)], [], [], []] + + +def test_dmp_integrate_in(): + f = dmp_convert(f_6, 3, ZZ, QQ) + + assert dmp_integrate_in(f, 2, 1, 3, QQ) == \ + dmp_swap( + dmp_integrate(dmp_swap(f, 0, 1, 3, QQ), 2, 3, QQ), 0, 1, 3, QQ) + assert dmp_integrate_in(f, 3, 1, 3, QQ) == \ + dmp_swap( + dmp_integrate(dmp_swap(f, 0, 1, 3, QQ), 3, 3, QQ), 0, 1, 3, QQ) + assert dmp_integrate_in(f, 2, 2, 3, QQ) == \ + dmp_swap( + dmp_integrate(dmp_swap(f, 0, 2, 3, QQ), 2, 3, QQ), 0, 2, 3, QQ) + assert dmp_integrate_in(f, 3, 2, 3, QQ) == \ + dmp_swap( + dmp_integrate(dmp_swap(f, 0, 2, 3, QQ), 3, 3, QQ), 0, 2, 3, QQ) + + raises(IndexError, lambda: dmp_integrate_in(f, 1, -1, 3, QQ)) + raises(IndexError, lambda: dmp_integrate_in(f, 1, 4, 3, QQ)) + + +def test_dup_diff(): + assert dup_diff([], 1, ZZ) == [] + assert dup_diff([7], 1, ZZ) == [] + assert dup_diff([2, 7], 1, ZZ) == [2] + assert dup_diff([1, 2, 1], 1, ZZ) == [2, 2] + assert dup_diff([1, 2, 3, 4], 1, ZZ) == [3, 4, 3] + assert dup_diff([1, -1, 0, 0, 2], 1, ZZ) == [4, -3, 0, 0] + + f = dup_normal([17, 34, 56, -345, 23, 76, 0, 0, 12, 3, 7], ZZ) + + assert dup_diff(f, 0, ZZ) == f + assert dup_diff(f, 1, ZZ) == [170, 306, 448, -2415, 138, 380, 0, 0, 24, 3] + assert dup_diff(f, 2, ZZ) == dup_diff(dup_diff(f, 1, ZZ), 1, ZZ) + assert dup_diff( + f, 3, ZZ) == dup_diff(dup_diff(dup_diff(f, 1, ZZ), 1, ZZ), 1, ZZ) + + K = FF(3) + f = dup_normal([17, 34, 56, -345, 23, 76, 0, 0, 12, 3, 7], K) + + assert dup_diff(f, 1, K) == dup_normal([2, 0, 1, 0, 0, 2, 0, 0, 0, 0], K) + assert dup_diff(f, 2, K) == dup_normal([1, 0, 0, 2, 0, 0, 0], K) + assert dup_diff(f, 3, K) == dup_normal([], K) + + assert dup_diff(f, 0, K) == f + assert dup_diff(f, 2, K) == dup_diff(dup_diff(f, 1, K), 1, K) + assert dup_diff( + f, 3, K) == dup_diff(dup_diff(dup_diff(f, 1, K), 1, K), 1, K) + + +def test_dmp_diff(): + assert dmp_diff([], 1, 0, ZZ) == [] + assert dmp_diff([[]], 1, 1, ZZ) == [[]] + assert dmp_diff([[[]]], 1, 2, ZZ) == [[[]]] + + assert dmp_diff([[[1], [2]]], 1, 2, ZZ) == [[[]]] + + assert dmp_diff([[[1]], [[]]], 1, 2, ZZ) == [[[1]]] + assert dmp_diff([[[3]], [[1]], [[]]], 1, 2, ZZ) == [[[6]], [[1]]] + + assert dmp_diff([1, -1, 0, 0, 2], 1, 0, ZZ) == \ + dup_diff([1, -1, 0, 0, 2], 1, ZZ) + + assert dmp_diff(f_6, 0, 3, ZZ) == f_6 + assert dmp_diff(f_6, 1, 3, ZZ) == [[[[8460]], [[]]], + [[[135, 0, 0], [], [], [-135, 0, 0]]], + [[[]]], + [[[-423]], [[-47]], [[]], [[141], [], [94, 0], []], [[]]]] + assert dmp_diff( + f_6, 2, 3, ZZ) == dmp_diff(dmp_diff(f_6, 1, 3, ZZ), 1, 3, ZZ) + assert dmp_diff(f_6, 3, 3, ZZ) == dmp_diff( + dmp_diff(dmp_diff(f_6, 1, 3, ZZ), 1, 3, ZZ), 1, 3, ZZ) + + K = FF(23) + F_6 = dmp_normal(f_6, 3, K) + + assert dmp_diff(F_6, 0, 3, K) == F_6 + assert dmp_diff(F_6, 1, 3, K) == dmp_diff(F_6, 1, 3, K) + assert dmp_diff(F_6, 2, 3, K) == dmp_diff(dmp_diff(F_6, 1, 3, K), 1, 3, K) + assert dmp_diff(F_6, 3, 3, K) == dmp_diff( + dmp_diff(dmp_diff(F_6, 1, 3, K), 1, 3, K), 1, 3, K) + + +def test_dmp_diff_in(): + assert dmp_diff_in(f_6, 2, 1, 3, ZZ) == \ + dmp_swap(dmp_diff(dmp_swap(f_6, 0, 1, 3, ZZ), 2, 3, ZZ), 0, 1, 3, ZZ) + assert dmp_diff_in(f_6, 3, 1, 3, ZZ) == \ + dmp_swap(dmp_diff(dmp_swap(f_6, 0, 1, 3, ZZ), 3, 3, ZZ), 0, 1, 3, ZZ) + assert dmp_diff_in(f_6, 2, 2, 3, ZZ) == \ + dmp_swap(dmp_diff(dmp_swap(f_6, 0, 2, 3, ZZ), 2, 3, ZZ), 0, 2, 3, ZZ) + assert dmp_diff_in(f_6, 3, 2, 3, ZZ) == \ + dmp_swap(dmp_diff(dmp_swap(f_6, 0, 2, 3, ZZ), 3, 3, ZZ), 0, 2, 3, ZZ) + + raises(IndexError, lambda: dmp_diff_in(f_6, 1, -1, 3, ZZ)) + raises(IndexError, lambda: dmp_diff_in(f_6, 1, 4, 3, ZZ)) + +def test_dup_eval(): + assert dup_eval([], 7, ZZ) == 0 + assert dup_eval([1, 2], 0, ZZ) == 2 + assert dup_eval([1, 2, 3], 7, ZZ) == 66 + + +def test_dmp_eval(): + assert dmp_eval([], 3, 0, ZZ) == 0 + + assert dmp_eval([[]], 3, 1, ZZ) == [] + assert dmp_eval([[[]]], 3, 2, ZZ) == [[]] + + assert dmp_eval([[1, 2]], 0, 1, ZZ) == [1, 2] + + assert dmp_eval([[[1]]], 3, 2, ZZ) == [[1]] + assert dmp_eval([[[1, 2]]], 3, 2, ZZ) == [[1, 2]] + + assert dmp_eval([[3, 2], [1, 2]], 3, 1, ZZ) == [10, 8] + assert dmp_eval([[[3, 2]], [[1, 2]]], 3, 2, ZZ) == [[10, 8]] + + +def test_dmp_eval_in(): + assert dmp_eval_in( + f_6, -2, 1, 3, ZZ) == dmp_eval(dmp_swap(f_6, 0, 1, 3, ZZ), -2, 3, ZZ) + assert dmp_eval_in( + f_6, 7, 1, 3, ZZ) == dmp_eval(dmp_swap(f_6, 0, 1, 3, ZZ), 7, 3, ZZ) + assert dmp_eval_in(f_6, -2, 2, 3, ZZ) == dmp_swap( + dmp_eval(dmp_swap(f_6, 0, 2, 3, ZZ), -2, 3, ZZ), 0, 1, 2, ZZ) + assert dmp_eval_in(f_6, 7, 2, 3, ZZ) == dmp_swap( + dmp_eval(dmp_swap(f_6, 0, 2, 3, ZZ), 7, 3, ZZ), 0, 1, 2, ZZ) + + f = [[[int(45)]], [[]], [[]], [[int(-9)], [-1], [], [int(3), int(0), int(10), int(0)]]] + + assert dmp_eval_in(f, -2, 2, 2, ZZ) == \ + [[45], [], [], [-9, -1, 0, -44]] + + raises(IndexError, lambda: dmp_eval_in(f_6, ZZ(1), -1, 3, ZZ)) + raises(IndexError, lambda: dmp_eval_in(f_6, ZZ(1), 4, 3, ZZ)) + + +def test_dmp_eval_tail(): + assert dmp_eval_tail([[]], [1], 1, ZZ) == [] + assert dmp_eval_tail([[[]]], [1], 2, ZZ) == [[]] + assert dmp_eval_tail([[[]]], [1, 2], 2, ZZ) == [] + + assert dmp_eval_tail(f_0, [], 2, ZZ) == f_0 + + assert dmp_eval_tail(f_0, [1, -17, 8], 2, ZZ) == 84496 + assert dmp_eval_tail(f_0, [-17, 8], 2, ZZ) == [-1409, 3, 85902] + assert dmp_eval_tail(f_0, [8], 2, ZZ) == [[83, 2], [3], [302, 81, 1]] + + assert dmp_eval_tail(f_1, [-17, 8], 2, ZZ) == [-136, 15699, 9166, -27144] + + assert dmp_eval_tail( + f_2, [-12, 3], 2, ZZ) == [-1377, 0, -702, -1224, 0, -624] + assert dmp_eval_tail( + f_3, [-12, 3], 2, ZZ) == [144, 82, -5181, -28872, -14868, -540] + + assert dmp_eval_tail( + f_4, [25, -1], 2, ZZ) == [152587890625, 9765625, -59605407714843750, + -3839159765625, -1562475, 9536712644531250, 610349546750, -4, 24414375000, 1562520] + assert dmp_eval_tail(f_5, [25, -1], 2, ZZ) == [-1, -78, -2028, -17576] + + assert dmp_eval_tail(f_6, [0, 2, 4], 3, ZZ) == [5040, 0, 0, 4480] + + +def test_dmp_diff_eval_in(): + assert dmp_diff_eval_in(f_6, 2, 7, 1, 3, ZZ) == \ + dmp_eval(dmp_diff(dmp_swap(f_6, 0, 1, 3, ZZ), 2, 3, ZZ), 7, 3, ZZ) + + assert dmp_diff_eval_in(f_6, 2, 7, 0, 3, ZZ) == \ + dmp_eval(dmp_diff(f_6, 2, 3, ZZ), 7, 3, ZZ) + + raises(IndexError, lambda: dmp_diff_eval_in(f_6, 1, ZZ(1), 4, 3, ZZ)) + + +def test_dup_revert(): + f = [-QQ(1, 720), QQ(0), QQ(1, 24), QQ(0), -QQ(1, 2), QQ(0), QQ(1)] + g = [QQ(61, 720), QQ(0), QQ(5, 24), QQ(0), QQ(1, 2), QQ(0), QQ(1)] + + assert dup_revert(f, 8, QQ) == g + + raises(NotReversible, lambda: dup_revert([QQ(1), QQ(0)], 3, QQ)) + + +def test_dmp_revert(): + f = [-QQ(1, 720), QQ(0), QQ(1, 24), QQ(0), -QQ(1, 2), QQ(0), QQ(1)] + g = [QQ(61, 720), QQ(0), QQ(5, 24), QQ(0), QQ(1, 2), QQ(0), QQ(1)] + + assert dmp_revert(f, 8, 0, QQ) == g + + raises(MultivariatePolynomialError, lambda: dmp_revert([[1]], 2, 1, QQ)) + + +def test_dup_trunc(): + assert dup_trunc([1, 2, 3, 4, 5, 6], ZZ(3), ZZ) == [1, -1, 0, 1, -1, 0] + assert dup_trunc([6, 5, 4, 3, 2, 1], ZZ(3), ZZ) == [-1, 1, 0, -1, 1] + + R = ZZ_I + assert dup_trunc([R(3), R(4), R(5)], R(3), R) == [R(1), R(-1)] + + K = FF(5) + assert dup_trunc([K(3), K(4), K(5)], K(3), K) == [K(1), K(0)] + + +def test_dmp_trunc(): + assert dmp_trunc([[]], [1, 2], 2, ZZ) == [[]] + assert dmp_trunc([[1, 2], [1, 4, 1], [1]], [1, 2], 1, ZZ) == [[-3], [1]] + + +def test_dmp_ground_trunc(): + assert dmp_ground_trunc(f_0, ZZ(3), 2, ZZ) == \ + dmp_normal( + [[[1, -1, 0], [-1]], [[]], [[1, -1, 0], [1, -1, 1], [1]]], 2, ZZ) + + +def test_dup_monic(): + assert dup_monic([3, 6, 9], ZZ) == [1, 2, 3] + + raises(ExactQuotientFailed, lambda: dup_monic([3, 4, 5], ZZ)) + + assert dup_monic([], QQ) == [] + assert dup_monic([QQ(1)], QQ) == [QQ(1)] + assert dup_monic([QQ(7), QQ(1), QQ(21)], QQ) == [QQ(1), QQ(1, 7), QQ(3)] + + +def test_dmp_ground_monic(): + assert dmp_ground_monic([3, 6, 9], 0, ZZ) == [1, 2, 3] + + assert dmp_ground_monic([[3], [6], [9]], 1, ZZ) == [[1], [2], [3]] + + raises( + ExactQuotientFailed, lambda: dmp_ground_monic([[3], [4], [5]], 1, ZZ)) + + assert dmp_ground_monic([[]], 1, QQ) == [[]] + assert dmp_ground_monic([[QQ(1)]], 1, QQ) == [[QQ(1)]] + assert dmp_ground_monic( + [[QQ(7)], [QQ(1)], [QQ(21)]], 1, QQ) == [[QQ(1)], [QQ(1, 7)], [QQ(3)]] + + +def test_dup_content(): + assert dup_content([], ZZ) == ZZ(0) + assert dup_content([1], ZZ) == ZZ(1) + assert dup_content([-1], ZZ) == ZZ(1) + assert dup_content([1, 1], ZZ) == ZZ(1) + assert dup_content([2, 2], ZZ) == ZZ(2) + assert dup_content([1, 2, 1], ZZ) == ZZ(1) + assert dup_content([2, 4, 2], ZZ) == ZZ(2) + + assert dup_content([QQ(2, 3), QQ(4, 9)], QQ) == QQ(2, 9) + assert dup_content([QQ(2, 3), QQ(4, 5)], QQ) == QQ(2, 15) + + +def test_dmp_ground_content(): + assert dmp_ground_content([[]], 1, ZZ) == ZZ(0) + assert dmp_ground_content([[]], 1, QQ) == QQ(0) + assert dmp_ground_content([[1]], 1, ZZ) == ZZ(1) + assert dmp_ground_content([[-1]], 1, ZZ) == ZZ(1) + assert dmp_ground_content([[1], [1]], 1, ZZ) == ZZ(1) + assert dmp_ground_content([[2], [2]], 1, ZZ) == ZZ(2) + assert dmp_ground_content([[1], [2], [1]], 1, ZZ) == ZZ(1) + assert dmp_ground_content([[2], [4], [2]], 1, ZZ) == ZZ(2) + + assert dmp_ground_content([[QQ(2, 3)], [QQ(4, 9)]], 1, QQ) == QQ(2, 9) + assert dmp_ground_content([[QQ(2, 3)], [QQ(4, 5)]], 1, QQ) == QQ(2, 15) + + assert dmp_ground_content(f_0, 2, ZZ) == ZZ(1) + assert dmp_ground_content( + dmp_mul_ground(f_0, ZZ(2), 2, ZZ), 2, ZZ) == ZZ(2) + + assert dmp_ground_content(f_1, 2, ZZ) == ZZ(1) + assert dmp_ground_content( + dmp_mul_ground(f_1, ZZ(3), 2, ZZ), 2, ZZ) == ZZ(3) + + assert dmp_ground_content(f_2, 2, ZZ) == ZZ(1) + assert dmp_ground_content( + dmp_mul_ground(f_2, ZZ(4), 2, ZZ), 2, ZZ) == ZZ(4) + + assert dmp_ground_content(f_3, 2, ZZ) == ZZ(1) + assert dmp_ground_content( + dmp_mul_ground(f_3, ZZ(5), 2, ZZ), 2, ZZ) == ZZ(5) + + assert dmp_ground_content(f_4, 2, ZZ) == ZZ(1) + assert dmp_ground_content( + dmp_mul_ground(f_4, ZZ(6), 2, ZZ), 2, ZZ) == ZZ(6) + + assert dmp_ground_content(f_5, 2, ZZ) == ZZ(1) + assert dmp_ground_content( + dmp_mul_ground(f_5, ZZ(7), 2, ZZ), 2, ZZ) == ZZ(7) + + assert dmp_ground_content(f_6, 3, ZZ) == ZZ(1) + assert dmp_ground_content( + dmp_mul_ground(f_6, ZZ(8), 3, ZZ), 3, ZZ) == ZZ(8) + + +def test_dup_primitive(): + assert dup_primitive([], ZZ) == (ZZ(0), []) + assert dup_primitive([ZZ(1)], ZZ) == (ZZ(1), [ZZ(1)]) + assert dup_primitive([ZZ(1), ZZ(1)], ZZ) == (ZZ(1), [ZZ(1), ZZ(1)]) + assert dup_primitive([ZZ(2), ZZ(2)], ZZ) == (ZZ(2), [ZZ(1), ZZ(1)]) + assert dup_primitive( + [ZZ(1), ZZ(2), ZZ(1)], ZZ) == (ZZ(1), [ZZ(1), ZZ(2), ZZ(1)]) + assert dup_primitive( + [ZZ(2), ZZ(4), ZZ(2)], ZZ) == (ZZ(2), [ZZ(1), ZZ(2), ZZ(1)]) + + assert dup_primitive([], QQ) == (QQ(0), []) + assert dup_primitive([QQ(1)], QQ) == (QQ(1), [QQ(1)]) + assert dup_primitive([QQ(1), QQ(1)], QQ) == (QQ(1), [QQ(1), QQ(1)]) + assert dup_primitive([QQ(2), QQ(2)], QQ) == (QQ(2), [QQ(1), QQ(1)]) + assert dup_primitive( + [QQ(1), QQ(2), QQ(1)], QQ) == (QQ(1), [QQ(1), QQ(2), QQ(1)]) + assert dup_primitive( + [QQ(2), QQ(4), QQ(2)], QQ) == (QQ(2), [QQ(1), QQ(2), QQ(1)]) + + assert dup_primitive( + [QQ(2, 3), QQ(4, 9)], QQ) == (QQ(2, 9), [QQ(3), QQ(2)]) + assert dup_primitive( + [QQ(2, 3), QQ(4, 5)], QQ) == (QQ(2, 15), [QQ(5), QQ(6)]) + + +def test_dmp_ground_primitive(): + assert dmp_ground_primitive([ZZ(1)], 0, ZZ) == (ZZ(1), [ZZ(1)]) + + assert dmp_ground_primitive([[]], 1, ZZ) == (ZZ(0), [[]]) + + assert dmp_ground_primitive(f_0, 2, ZZ) == (ZZ(1), f_0) + assert dmp_ground_primitive( + dmp_mul_ground(f_0, ZZ(2), 2, ZZ), 2, ZZ) == (ZZ(2), f_0) + + assert dmp_ground_primitive(f_1, 2, ZZ) == (ZZ(1), f_1) + assert dmp_ground_primitive( + dmp_mul_ground(f_1, ZZ(3), 2, ZZ), 2, ZZ) == (ZZ(3), f_1) + + assert dmp_ground_primitive(f_2, 2, ZZ) == (ZZ(1), f_2) + assert dmp_ground_primitive( + dmp_mul_ground(f_2, ZZ(4), 2, ZZ), 2, ZZ) == (ZZ(4), f_2) + + assert dmp_ground_primitive(f_3, 2, ZZ) == (ZZ(1), f_3) + assert dmp_ground_primitive( + dmp_mul_ground(f_3, ZZ(5), 2, ZZ), 2, ZZ) == (ZZ(5), f_3) + + assert dmp_ground_primitive(f_4, 2, ZZ) == (ZZ(1), f_4) + assert dmp_ground_primitive( + dmp_mul_ground(f_4, ZZ(6), 2, ZZ), 2, ZZ) == (ZZ(6), f_4) + + assert dmp_ground_primitive(f_5, 2, ZZ) == (ZZ(1), f_5) + assert dmp_ground_primitive( + dmp_mul_ground(f_5, ZZ(7), 2, ZZ), 2, ZZ) == (ZZ(7), f_5) + + assert dmp_ground_primitive(f_6, 3, ZZ) == (ZZ(1), f_6) + assert dmp_ground_primitive( + dmp_mul_ground(f_6, ZZ(8), 3, ZZ), 3, ZZ) == (ZZ(8), f_6) + + assert dmp_ground_primitive([[ZZ(2)]], 1, ZZ) == (ZZ(2), [[ZZ(1)]]) + assert dmp_ground_primitive([[QQ(2)]], 1, QQ) == (QQ(2), [[QQ(1)]]) + + assert dmp_ground_primitive( + [[QQ(2, 3)], [QQ(4, 9)]], 1, QQ) == (QQ(2, 9), [[QQ(3)], [QQ(2)]]) + assert dmp_ground_primitive( + [[QQ(2, 3)], [QQ(4, 5)]], 1, QQ) == (QQ(2, 15), [[QQ(5)], [QQ(6)]]) + + +def test_dup_extract(): + f = dup_normal([2930944, 0, 2198208, 0, 549552, 0, 45796], ZZ) + g = dup_normal([17585664, 0, 8792832, 0, 1099104, 0], ZZ) + + F = dup_normal([64, 0, 48, 0, 12, 0, 1], ZZ) + G = dup_normal([384, 0, 192, 0, 24, 0], ZZ) + + assert dup_extract(f, g, ZZ) == (45796, F, G) + + +def test_dmp_ground_extract(): + f = dmp_normal( + [[2930944], [], [2198208], [], [549552], [], [45796]], 1, ZZ) + g = dmp_normal([[17585664], [], [8792832], [], [1099104], []], 1, ZZ) + + F = dmp_normal([[64], [], [48], [], [12], [], [1]], 1, ZZ) + G = dmp_normal([[384], [], [192], [], [24], []], 1, ZZ) + + assert dmp_ground_extract(f, g, 1, ZZ) == (45796, F, G) + + +def test_dup_real_imag(): + assert dup_real_imag([], ZZ) == ([[]], [[]]) + assert dup_real_imag([1], ZZ) == ([[1]], [[]]) + + assert dup_real_imag([1, 1], ZZ) == ([[1], [1]], [[1, 0]]) + assert dup_real_imag([1, 2], ZZ) == ([[1], [2]], [[1, 0]]) + + assert dup_real_imag( + [1, 2, 3], ZZ) == ([[1], [2], [-1, 0, 3]], [[2, 0], [2, 0]]) + + assert dup_real_imag([ZZ(1), ZZ(0), ZZ(1), ZZ(3)], ZZ) == ( + [[ZZ(1)], [], [ZZ(-3), ZZ(0), ZZ(1)], [ZZ(3)]], + [[ZZ(3), ZZ(0)], [], [ZZ(-1), ZZ(0), ZZ(1), ZZ(0)]] + ) + + raises(DomainError, lambda: dup_real_imag([EX(1), EX(2)], EX)) + + + +def test_dup_mirror(): + assert dup_mirror([], ZZ) == [] + assert dup_mirror([1], ZZ) == [1] + + assert dup_mirror([1, 2, 3, 4, 5], ZZ) == [1, -2, 3, -4, 5] + assert dup_mirror([1, 2, 3, 4, 5, 6], ZZ) == [-1, 2, -3, 4, -5, 6] + + +def test_dup_scale(): + assert dup_scale([], -1, ZZ) == [] + assert dup_scale([1], -1, ZZ) == [1] + + assert dup_scale([1, 2, 3, 4, 5], -1, ZZ) == [1, -2, 3, -4, 5] + assert dup_scale([1, 2, 3, 4, 5], -7, ZZ) == [2401, -686, 147, -28, 5] + + +def test_dup_shift(): + assert dup_shift([], 1, ZZ) == [] + assert dup_shift([1], 1, ZZ) == [1] + + assert dup_shift([1, 2, 3, 4, 5], 1, ZZ) == [1, 6, 15, 20, 15] + assert dup_shift([1, 2, 3, 4, 5], 7, ZZ) == [1, 30, 339, 1712, 3267] + + +def test_dmp_shift(): + assert dmp_shift([ZZ(1), ZZ(2)], [ZZ(1)], 0, ZZ) == [ZZ(1), ZZ(3)] + + assert dmp_shift([[]], [ZZ(1), ZZ(2)], 1, ZZ) == [[]] + + xy = [[ZZ(1), ZZ(0)], []] # x*y + x1y2 = [[ZZ(1), ZZ(2)], [ZZ(1), ZZ(2)]] # (x+1)*(y+2) + assert dmp_shift(xy, [ZZ(1), ZZ(2)], 1, ZZ) == x1y2 + + +def test_dup_transform(): + assert dup_transform([], [], [1, 1], ZZ) == [] + assert dup_transform([], [1], [1, 1], ZZ) == [] + assert dup_transform([], [1, 2], [1, 1], ZZ) == [] + + assert dup_transform([6, -5, 4, -3, 17], [1, -3, 4], [2, -3], ZZ) == \ + [6, -82, 541, -2205, 6277, -12723, 17191, -13603, 4773] + + +def test_dup_compose(): + assert dup_compose([], [], ZZ) == [] + assert dup_compose([], [1], ZZ) == [] + assert dup_compose([], [1, 2], ZZ) == [] + + assert dup_compose([1], [], ZZ) == [1] + + assert dup_compose([1, 2, 0], [], ZZ) == [] + assert dup_compose([1, 2, 1], [], ZZ) == [1] + + assert dup_compose([1, 2, 1], [1], ZZ) == [4] + assert dup_compose([1, 2, 1], [7], ZZ) == [64] + + assert dup_compose([1, 2, 1], [1, -1], ZZ) == [1, 0, 0] + assert dup_compose([1, 2, 1], [1, 1], ZZ) == [1, 4, 4] + assert dup_compose([1, 2, 1], [1, 2, 1], ZZ) == [1, 4, 8, 8, 4] + + +def test_dmp_compose(): + assert dmp_compose([1, 2, 1], [1, 2, 1], 0, ZZ) == [1, 4, 8, 8, 4] + + assert dmp_compose([[[]]], [[[]]], 2, ZZ) == [[[]]] + assert dmp_compose([[[]]], [[[1]]], 2, ZZ) == [[[]]] + assert dmp_compose([[[]]], [[[1]], [[2]]], 2, ZZ) == [[[]]] + + assert dmp_compose([[[1]]], [], 2, ZZ) == [[[1]]] + + assert dmp_compose([[1], [2], [ ]], [[]], 1, ZZ) == [[]] + assert dmp_compose([[1], [2], [1]], [[]], 1, ZZ) == [[1]] + + assert dmp_compose([[1], [2], [1]], [[1]], 1, ZZ) == [[4]] + assert dmp_compose([[1], [2], [1]], [[7]], 1, ZZ) == [[64]] + + assert dmp_compose([[1], [2], [1]], [[1], [-1]], 1, ZZ) == [[1], [ ], [ ]] + assert dmp_compose([[1], [2], [1]], [[1], [ 1]], 1, ZZ) == [[1], [4], [4]] + + assert dmp_compose( + [[1], [2], [1]], [[1], [2], [1]], 1, ZZ) == [[1], [4], [8], [8], [4]] + + +def test_dup_decompose(): + assert dup_decompose([1], ZZ) == [[1]] + + assert dup_decompose([1, 0], ZZ) == [[1, 0]] + assert dup_decompose([1, 0, 0, 0], ZZ) == [[1, 0, 0, 0]] + + assert dup_decompose([1, 0, 0, 0, 0], ZZ) == [[1, 0, 0], [1, 0, 0]] + assert dup_decompose( + [1, 0, 0, 0, 0, 0, 0], ZZ) == [[1, 0, 0, 0], [1, 0, 0]] + + assert dup_decompose([7, 0, 0, 0, 1], ZZ) == [[7, 0, 1], [1, 0, 0]] + assert dup_decompose([4, 0, 3, 0, 2], ZZ) == [[4, 3, 2], [1, 0, 0]] + + f = [1, 0, 20, 0, 150, 0, 500, 0, 625, -2, 0, -10, 9] + + assert dup_decompose(f, ZZ) == [[1, 0, 0, -2, 9], [1, 0, 5, 0]] + + f = [2, 0, 40, 0, 300, 0, 1000, 0, 1250, -4, 0, -20, 18] + + assert dup_decompose(f, ZZ) == [[2, 0, 0, -4, 18], [1, 0, 5, 0]] + + f = [1, 0, 20, -8, 150, -120, 524, -600, 865, -1034, 600, -170, 29] + + assert dup_decompose(f, ZZ) == [[1, -8, 24, -34, 29], [1, 0, 5, 0]] + + R, t = ring("t", ZZ) + f = [6*t**2 - 42, + 48*t**2 + 96, + 144*t**2 + 648*t + 288, + 624*t**2 + 864*t + 384, + 108*t**3 + 312*t**2 + 432*t + 192] + + assert dup_decompose(f, R.to_domain()) == [f] + + +def test_dmp_lift(): + q = [QQ(1, 1), QQ(0, 1), QQ(1, 1)] + + f_a = [ANP([QQ(1, 1)], q, QQ), ANP([], q, QQ), ANP([], q, QQ), + ANP([QQ(1, 1), QQ(0, 1)], q, QQ), ANP([QQ(17, 1), QQ(0, 1)], q, QQ)] + + f_lift = QQ.map([1, 0, 0, 0, 0, 0, 1, 34, 289]) + + assert dmp_lift(f_a, 0, QQ.algebraic_field(I)) == f_lift + + f_g = [QQ_I(1), QQ_I(0), QQ_I(0), QQ_I(0, 1), QQ_I(0, 17)] + + assert dmp_lift(f_g, 0, QQ_I) == f_lift + + raises(DomainError, lambda: dmp_lift([EX(1), EX(2)], 0, EX)) + + +def test_dup_sign_variations(): + assert dup_sign_variations([], ZZ) == 0 + assert dup_sign_variations([1, 0], ZZ) == 0 + assert dup_sign_variations([1, 0, 2], ZZ) == 0 + assert dup_sign_variations([1, 0, 3, 0], ZZ) == 0 + assert dup_sign_variations([1, 0, 4, 0, 5], ZZ) == 0 + + assert dup_sign_variations([-1, 0, 2], ZZ) == 1 + assert dup_sign_variations([-1, 0, 3, 0], ZZ) == 1 + assert dup_sign_variations([-1, 0, 4, 0, 5], ZZ) == 1 + + assert dup_sign_variations([-1, -4, -5], ZZ) == 0 + assert dup_sign_variations([ 1, -4, -5], ZZ) == 1 + assert dup_sign_variations([ 1, 4, -5], ZZ) == 1 + assert dup_sign_variations([ 1, -4, 5], ZZ) == 2 + assert dup_sign_variations([-1, 4, -5], ZZ) == 2 + assert dup_sign_variations([-1, 4, 5], ZZ) == 1 + assert dup_sign_variations([-1, -4, 5], ZZ) == 1 + assert dup_sign_variations([ 1, 4, 5], ZZ) == 0 + + assert dup_sign_variations([-1, 0, -4, 0, -5], ZZ) == 0 + assert dup_sign_variations([ 1, 0, -4, 0, -5], ZZ) == 1 + assert dup_sign_variations([ 1, 0, 4, 0, -5], ZZ) == 1 + assert dup_sign_variations([ 1, 0, -4, 0, 5], ZZ) == 2 + assert dup_sign_variations([-1, 0, 4, 0, -5], ZZ) == 2 + assert dup_sign_variations([-1, 0, 4, 0, 5], ZZ) == 1 + assert dup_sign_variations([-1, 0, -4, 0, 5], ZZ) == 1 + assert dup_sign_variations([ 1, 0, 4, 0, 5], ZZ) == 0 + + +def test_dup_clear_denoms(): + assert dup_clear_denoms([], QQ, ZZ) == (ZZ(1), []) + + assert dup_clear_denoms([QQ(1)], QQ, ZZ) == (ZZ(1), [QQ(1)]) + assert dup_clear_denoms([QQ(7)], QQ, ZZ) == (ZZ(1), [QQ(7)]) + + assert dup_clear_denoms([QQ(7, 3)], QQ) == (ZZ(3), [QQ(7)]) + assert dup_clear_denoms([QQ(7, 3)], QQ, ZZ) == (ZZ(3), [QQ(7)]) + + assert dup_clear_denoms( + [QQ(3), QQ(1), QQ(0)], QQ, ZZ) == (ZZ(1), [QQ(3), QQ(1), QQ(0)]) + assert dup_clear_denoms( + [QQ(1), QQ(1, 2), QQ(0)], QQ, ZZ) == (ZZ(2), [QQ(2), QQ(1), QQ(0)]) + + assert dup_clear_denoms([QQ(3), QQ( + 1), QQ(0)], QQ, ZZ, convert=True) == (ZZ(1), [ZZ(3), ZZ(1), ZZ(0)]) + assert dup_clear_denoms([QQ(1), QQ( + 1, 2), QQ(0)], QQ, ZZ, convert=True) == (ZZ(2), [ZZ(2), ZZ(1), ZZ(0)]) + + assert dup_clear_denoms( + [EX(S(3)/2), EX(S(9)/4)], EX) == (EX(4), [EX(6), EX(9)]) + + assert dup_clear_denoms([EX(7)], EX) == (EX(1), [EX(7)]) + assert dup_clear_denoms([EX(sin(x)/x), EX(0)], EX) == (EX(x), [EX(sin(x)), EX(0)]) + + F = RR.frac_field(x) + result = dup_clear_denoms([F(8.48717/(8.0089*x + 2.83)), F(0.0)], F) + assert str(result) == "(x + 0.353356890459364, [1.05971731448763, 0.0])" + +def test_dmp_clear_denoms(): + assert dmp_clear_denoms([[]], 1, QQ, ZZ) == (ZZ(1), [[]]) + + assert dmp_clear_denoms([[QQ(1)]], 1, QQ, ZZ) == (ZZ(1), [[QQ(1)]]) + assert dmp_clear_denoms([[QQ(7)]], 1, QQ, ZZ) == (ZZ(1), [[QQ(7)]]) + + assert dmp_clear_denoms([[QQ(7, 3)]], 1, QQ) == (ZZ(3), [[QQ(7)]]) + assert dmp_clear_denoms([[QQ(7, 3)]], 1, QQ, ZZ) == (ZZ(3), [[QQ(7)]]) + + assert dmp_clear_denoms( + [[QQ(3)], [QQ(1)], []], 1, QQ, ZZ) == (ZZ(1), [[QQ(3)], [QQ(1)], []]) + assert dmp_clear_denoms([[QQ( + 1)], [QQ(1, 2)], []], 1, QQ, ZZ) == (ZZ(2), [[QQ(2)], [QQ(1)], []]) + + assert dmp_clear_denoms([QQ(3), QQ( + 1), QQ(0)], 0, QQ, ZZ, convert=True) == (ZZ(1), [ZZ(3), ZZ(1), ZZ(0)]) + assert dmp_clear_denoms([QQ(1), QQ(1, 2), QQ( + 0)], 0, QQ, ZZ, convert=True) == (ZZ(2), [ZZ(2), ZZ(1), ZZ(0)]) + + assert dmp_clear_denoms([[QQ(3)], [QQ( + 1)], []], 1, QQ, ZZ, convert=True) == (ZZ(1), [[QQ(3)], [QQ(1)], []]) + assert dmp_clear_denoms([[QQ(1)], [QQ(1, 2)], []], 1, QQ, ZZ, + convert=True) == (ZZ(2), [[QQ(2)], [QQ(1)], []]) + + assert dmp_clear_denoms( + [[EX(S(3)/2)], [EX(S(9)/4)]], 1, EX) == (EX(4), [[EX(6)], [EX(9)]]) + assert dmp_clear_denoms([[EX(7)]], 1, EX) == (EX(1), [[EX(7)]]) + assert dmp_clear_denoms([[EX(sin(x)/x), EX(0)]], 1, EX) == (EX(x), [[EX(sin(x)), EX(0)]]) diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_dispersion.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_dispersion.py new file mode 100644 index 0000000000000000000000000000000000000000..ad56b7bebd73c38e037085d36625a41729c0369a --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_dispersion.py @@ -0,0 +1,95 @@ +from sympy.core import Symbol, S, oo +from sympy.functions.elementary.miscellaneous import sqrt +from sympy.polys import poly +from sympy.polys.dispersion import dispersion, dispersionset + + +def test_dispersion(): + x = Symbol("x") + a = Symbol("a") + + fp = poly(S.Zero, x) + assert sorted(dispersionset(fp)) == [0] + + fp = poly(S(2), x) + assert sorted(dispersionset(fp)) == [0] + + fp = poly(x + 1, x) + assert sorted(dispersionset(fp)) == [0] + assert dispersion(fp) == 0 + + fp = poly((x + 1)*(x + 2), x) + assert sorted(dispersionset(fp)) == [0, 1] + assert dispersion(fp) == 1 + + fp = poly(x*(x + 3), x) + assert sorted(dispersionset(fp)) == [0, 3] + assert dispersion(fp) == 3 + + fp = poly((x - 3)*(x + 3), x) + assert sorted(dispersionset(fp)) == [0, 6] + assert dispersion(fp) == 6 + + fp = poly(x**4 - 3*x**2 + 1, x) + gp = fp.shift(-3) + assert sorted(dispersionset(fp, gp)) == [2, 3, 4] + assert dispersion(fp, gp) == 4 + assert sorted(dispersionset(gp, fp)) == [] + assert dispersion(gp, fp) is -oo + + fp = poly(x*(3*x**2+a)*(x-2536)*(x**3+a), x) + gp = fp.as_expr().subs(x, x-345).as_poly(x) + assert sorted(dispersionset(fp, gp)) == [345, 2881] + assert sorted(dispersionset(gp, fp)) == [2191] + + gp = poly((x-2)**2*(x-3)**3*(x-5)**3, x) + assert sorted(dispersionset(gp)) == [0, 1, 2, 3] + assert sorted(dispersionset(gp, (gp+4)**2)) == [1, 2] + + fp = poly(x*(x+2)*(x-1), x) + assert sorted(dispersionset(fp)) == [0, 1, 2, 3] + + fp = poly(x**2 + sqrt(5)*x - 1, x, domain='QQ') + gp = poly(x**2 + (2 + sqrt(5))*x + sqrt(5), x, domain='QQ') + assert sorted(dispersionset(fp, gp)) == [2] + assert sorted(dispersionset(gp, fp)) == [1, 4] + + # There are some difficulties if we compute over Z[a] + # and alpha happens to lie in Z[a] instead of simply Z. + # Hence we can not decide if alpha is indeed integral + # in general. + + fp = poly(4*x**4 + (4*a + 8)*x**3 + (a**2 + 6*a + 4)*x**2 + (a**2 + 2*a)*x, x) + assert sorted(dispersionset(fp)) == [0, 1] + + # For any specific value of a, the dispersion is 3*a + # but the algorithm can not find this in general. + # This is the point where the resultant based Ansatz + # is superior to the current one. + fp = poly(a**2*x**3 + (a**3 + a**2 + a + 1)*x, x) + gp = fp.as_expr().subs(x, x - 3*a).as_poly(x) + assert sorted(dispersionset(fp, gp)) == [] + + fpa = fp.as_expr().subs(a, 2).as_poly(x) + gpa = gp.as_expr().subs(a, 2).as_poly(x) + assert sorted(dispersionset(fpa, gpa)) == [6] + + # Work with Expr instead of Poly + f = (x + 1)*(x + 2) + assert sorted(dispersionset(f)) == [0, 1] + assert dispersion(f) == 1 + + f = x**4 - 3*x**2 + 1 + g = x**4 - 12*x**3 + 51*x**2 - 90*x + 55 + assert sorted(dispersionset(f, g)) == [2, 3, 4] + assert dispersion(f, g) == 4 + + # Work with Expr and specify a generator + f = (x + 1)*(x + 2) + assert sorted(dispersionset(f, None, x)) == [0, 1] + assert dispersion(f, None, x) == 1 + + f = x**4 - 3*x**2 + 1 + g = x**4 - 12*x**3 + 51*x**2 - 90*x + 55 + assert sorted(dispersionset(f, g, x)) == [2, 3, 4] + assert dispersion(f, g, x) == 4 diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_distributedmodules.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_distributedmodules.py new file mode 100644 index 0000000000000000000000000000000000000000..c95672f99f878f3def660aadec901afbde9adf8b --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_distributedmodules.py @@ -0,0 +1,208 @@ +"""Tests for sparse distributed modules. """ + +from sympy.polys.distributedmodules import ( + sdm_monomial_mul, sdm_monomial_deg, sdm_monomial_divides, + sdm_add, sdm_LM, sdm_LT, sdm_mul_term, sdm_zero, sdm_deg, + sdm_LC, sdm_from_dict, + sdm_spoly, sdm_ecart, sdm_nf_mora, sdm_groebner, + sdm_from_vector, sdm_to_vector, sdm_monomial_lcm +) + +from sympy.polys.orderings import lex, grlex, InverseOrder +from sympy.polys.domains import QQ + +from sympy.abc import x, y, z + + +def test_sdm_monomial_mul(): + assert sdm_monomial_mul((1, 1, 0), (1, 3)) == (1, 2, 3) + + +def test_sdm_monomial_deg(): + assert sdm_monomial_deg((5, 2, 1)) == 3 + + +def test_sdm_monomial_lcm(): + assert sdm_monomial_lcm((1, 2, 3), (1, 5, 0)) == (1, 5, 3) + + +def test_sdm_monomial_divides(): + assert sdm_monomial_divides((1, 0, 0), (1, 0, 0)) is True + assert sdm_monomial_divides((1, 0, 0), (1, 2, 1)) is True + assert sdm_monomial_divides((5, 1, 1), (5, 2, 1)) is True + + assert sdm_monomial_divides((1, 0, 0), (2, 0, 0)) is False + assert sdm_monomial_divides((1, 1, 0), (1, 0, 0)) is False + assert sdm_monomial_divides((5, 1, 2), (5, 0, 1)) is False + + +def test_sdm_LC(): + assert sdm_LC([((1, 2, 3), QQ(5))], QQ) == QQ(5) + + +def test_sdm_from_dict(): + dic = {(1, 2, 1, 1): QQ(1), (1, 1, 2, 1): QQ(1), (1, 0, 2, 1): QQ(1), + (1, 0, 0, 3): QQ(1), (1, 1, 1, 0): QQ(1)} + assert sdm_from_dict(dic, grlex) == \ + [((1, 2, 1, 1), QQ(1)), ((1, 1, 2, 1), QQ(1)), + ((1, 0, 2, 1), QQ(1)), ((1, 0, 0, 3), QQ(1)), ((1, 1, 1, 0), QQ(1))] + +# TODO test to_dict? + + +def test_sdm_add(): + assert sdm_add([((1, 1, 1), QQ(1))], [((2, 0, 0), QQ(1))], lex, QQ) == \ + [((2, 0, 0), QQ(1)), ((1, 1, 1), QQ(1))] + assert sdm_add([((1, 1, 1), QQ(1))], [((1, 1, 1), QQ(-1))], lex, QQ) == [] + assert sdm_add([((1, 0, 0), QQ(1))], [((1, 0, 0), QQ(2))], lex, QQ) == \ + [((1, 0, 0), QQ(3))] + assert sdm_add([((1, 0, 1), QQ(1))], [((1, 1, 0), QQ(1))], lex, QQ) == \ + [((1, 1, 0), QQ(1)), ((1, 0, 1), QQ(1))] + + +def test_sdm_LM(): + dic = {(1, 2, 3): QQ(1), (4, 0, 0): QQ(1), (4, 0, 1): QQ(1)} + assert sdm_LM(sdm_from_dict(dic, lex)) == (4, 0, 1) + + +def test_sdm_LT(): + dic = {(1, 2, 3): QQ(1), (4, 0, 0): QQ(2), (4, 0, 1): QQ(3)} + assert sdm_LT(sdm_from_dict(dic, lex)) == ((4, 0, 1), QQ(3)) + + +def test_sdm_mul_term(): + assert sdm_mul_term([((1, 0, 0), QQ(1))], ((0, 0), QQ(0)), lex, QQ) == [] + assert sdm_mul_term([], ((1, 0), QQ(1)), lex, QQ) == [] + assert sdm_mul_term([((1, 0, 0), QQ(1))], ((1, 0), QQ(1)), lex, QQ) == \ + [((1, 1, 0), QQ(1))] + f = [((2, 0, 1), QQ(4)), ((1, 1, 0), QQ(3))] + assert sdm_mul_term(f, ((1, 1), QQ(2)), lex, QQ) == \ + [((2, 1, 2), QQ(8)), ((1, 2, 1), QQ(6))] + + +def test_sdm_zero(): + assert sdm_zero() == [] + + +def test_sdm_deg(): + assert sdm_deg([((1, 2, 3), 1), ((10, 0, 1), 1), ((2, 3, 4), 4)]) == 7 + + +def test_sdm_spoly(): + f = [((2, 1, 1), QQ(1)), ((1, 0, 1), QQ(1))] + g = [((2, 3, 0), QQ(1))] + h = [((1, 2, 3), QQ(1))] + assert sdm_spoly(f, h, lex, QQ) == [] + assert sdm_spoly(f, g, lex, QQ) == [((1, 2, 1), QQ(1))] + + +def test_sdm_ecart(): + assert sdm_ecart([((1, 2, 3), 1), ((1, 0, 1), 1)]) == 0 + assert sdm_ecart([((2, 2, 1), 1), ((1, 5, 1), 1)]) == 3 + + +def test_sdm_nf_mora(): + f = sdm_from_dict({(1, 2, 1, 1): QQ(1), (1, 1, 2, 1): QQ(1), + (1, 0, 2, 1): QQ(1), (1, 0, 0, 3): QQ(1), (1, 1, 1, 0): QQ(1)}, + grlex) + f1 = sdm_from_dict({(1, 1, 1, 0): QQ(1), (1, 0, 2, 0): QQ(1), + (1, 0, 0, 0): QQ(-1)}, grlex) + f2 = sdm_from_dict({(1, 1, 1, 0): QQ(1)}, grlex) + (id0, id1, id2) = [sdm_from_dict({(i, 0, 0, 0): QQ(1)}, grlex) + for i in range(3)] + + assert sdm_nf_mora(f, [f1, f2], grlex, QQ, phantom=(id0, [id1, id2])) == \ + ([((1, 0, 2, 1), QQ(1)), ((1, 0, 0, 3), QQ(1)), ((1, 1, 1, 0), QQ(1)), + ((1, 1, 0, 1), QQ(1))], + [((1, 1, 0, 1), QQ(-1)), ((0, 0, 0, 0), QQ(1))]) + assert sdm_nf_mora(f, [f2, f1], grlex, QQ, phantom=(id0, [id2, id1])) == \ + ([((1, 0, 2, 1), QQ(1)), ((1, 0, 0, 3), QQ(1)), ((1, 1, 1, 0), QQ(1))], + [((2, 1, 0, 1), QQ(-1)), ((2, 0, 1, 1), QQ(-1)), ((0, 0, 0, 0), QQ(1))]) + + f = sdm_from_vector([x*z, y**2 + y*z - z, y], lex, QQ, gens=[x, y, z]) + f1 = sdm_from_vector([x, y, 1], lex, QQ, gens=[x, y, z]) + f2 = sdm_from_vector([x*y, z, z**2], lex, QQ, gens=[x, y, z]) + assert sdm_nf_mora(f, [f1, f2], lex, QQ) == \ + sdm_nf_mora(f, [f2, f1], lex, QQ) == \ + [((1, 0, 1, 1), QQ(1)), ((1, 0, 0, 1), QQ(-1)), ((0, 1, 1, 0), QQ(-1)), + ((0, 1, 0, 1), QQ(1))] + + +def test_conversion(): + f = [x**2 + y**2, 2*z] + g = [((1, 0, 0, 1), QQ(2)), ((0, 2, 0, 0), QQ(1)), ((0, 0, 2, 0), QQ(1))] + assert sdm_to_vector(g, [x, y, z], QQ) == f + assert sdm_from_vector(f, lex, QQ) == g + assert sdm_from_vector( + [x, 1], lex, QQ) == [((1, 0), QQ(1)), ((0, 1), QQ(1))] + assert sdm_to_vector([((1, 1, 0, 0), 1)], [x, y, z], QQ, n=3) == [0, x, 0] + assert sdm_from_vector([0, 0], lex, QQ, gens=[x, y]) == sdm_zero() + + +def test_nontrivial(): + gens = [x, y, z] + + def contains(I, f): + S = [sdm_from_vector([g], lex, QQ, gens=gens) for g in I] + G = sdm_groebner(S, sdm_nf_mora, lex, QQ) + return sdm_nf_mora(sdm_from_vector([f], lex, QQ, gens=gens), + G, lex, QQ) == sdm_zero() + + assert contains([x, y], x) + assert contains([x, y], x + y) + assert not contains([x, y], 1) + assert not contains([x, y], z) + assert contains([x**2 + y, x**2 + x], x - y) + assert not contains([x + y + z, x*y + x*z + y*z, x*y*z], x**2) + assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x**3) + assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x**4) + assert not contains([x + y + z, x*y + x*z + y*z, x*y*z], x*y**2) + assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x**4 + y**3 + 2*z*y*x) + assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x*y*z) + assert contains([x, 1 + x + y, 5 - 7*y], 1) + assert contains( + [x**3 + y**3, y**3 + z**3, z**3 + x**3, x**2*y + x**2*z + y**2*z], + x**3) + assert not contains( + [x**3 + y**3, y**3 + z**3, z**3 + x**3, x**2*y + x**2*z + y**2*z], + x**2 + y**2) + + # compare local order + assert not contains([x*(1 + x + y), y*(1 + z)], x) + assert not contains([x*(1 + x + y), y*(1 + z)], x + y) + + +def test_local(): + igrlex = InverseOrder(grlex) + gens = [x, y, z] + + def contains(I, f): + S = [sdm_from_vector([g], igrlex, QQ, gens=gens) for g in I] + G = sdm_groebner(S, sdm_nf_mora, igrlex, QQ) + return sdm_nf_mora(sdm_from_vector([f], lex, QQ, gens=gens), + G, lex, QQ) == sdm_zero() + assert contains([x, y], x) + assert contains([x, y], x + y) + assert not contains([x, y], 1) + assert not contains([x, y], z) + assert contains([x**2 + y, x**2 + x], x - y) + assert not contains([x + y + z, x*y + x*z + y*z, x*y*z], x**2) + assert contains([x*(1 + x + y), y*(1 + z)], x) + assert contains([x*(1 + x + y), y*(1 + z)], x + y) + + +def test_uncovered_line(): + gens = [x, y] + f1 = sdm_zero() + f2 = sdm_from_vector([x, 0], lex, QQ, gens=gens) + f3 = sdm_from_vector([0, y], lex, QQ, gens=gens) + + assert sdm_spoly(f1, f2, lex, QQ) == sdm_zero() + assert sdm_spoly(f3, f2, lex, QQ) == sdm_zero() + + +def test_chain_criterion(): + gens = [x] + f1 = sdm_from_vector([1, x], grlex, QQ, gens=gens) + f2 = sdm_from_vector([0, x - 2], grlex, QQ, gens=gens) + assert len(sdm_groebner([f1, f2], sdm_nf_mora, grlex, QQ)) == 2 diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_euclidtools.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_euclidtools.py new file mode 100644 index 0000000000000000000000000000000000000000..3061be73f987163951a5836ff50125d29abc60c7 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_euclidtools.py @@ -0,0 +1,712 @@ +"""Tests for Euclidean algorithms, GCDs, LCMs and polynomial remainder sequences. """ + +from sympy.polys.rings import ring +from sympy.polys.domains import ZZ, QQ, RR + +from sympy.polys.specialpolys import ( + f_polys, + dmp_fateman_poly_F_1, + dmp_fateman_poly_F_2, + dmp_fateman_poly_F_3) + +f_0, f_1, f_2, f_3, f_4, f_5, f_6 = f_polys() + +def test_dup_gcdex(): + R, x = ring("x", QQ) + + f = x**4 - 2*x**3 - 6*x**2 + 12*x + 15 + g = x**3 + x**2 - 4*x - 4 + + s = -QQ(1,5)*x + QQ(3,5) + t = QQ(1,5)*x**2 - QQ(6,5)*x + 2 + h = x + 1 + + assert R.dup_half_gcdex(f, g) == (s, h) + assert R.dup_gcdex(f, g) == (s, t, h) + + f = x**4 + 4*x**3 - x + 1 + g = x**3 - x + 1 + + s, t, h = R.dup_gcdex(f, g) + S, T, H = R.dup_gcdex(g, f) + + assert R.dup_add(R.dup_mul(s, f), + R.dup_mul(t, g)) == h + assert R.dup_add(R.dup_mul(S, g), + R.dup_mul(T, f)) == H + + f = 2*x + g = x**2 - 16 + + s = QQ(1,32)*x + t = -QQ(1,16) + h = 1 + + assert R.dup_half_gcdex(f, g) == (s, h) + assert R.dup_gcdex(f, g) == (s, t, h) + + +def test_dup_invert(): + R, x = ring("x", QQ) + assert R.dup_invert(2*x, x**2 - 16) == QQ(1,32)*x + + +def test_dup_euclidean_prs(): + R, x = ring("x", QQ) + + f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + + assert R.dup_euclidean_prs(f, g) == [ + f, + g, + -QQ(5,9)*x**4 + QQ(1,9)*x**2 - QQ(1,3), + -QQ(117,25)*x**2 - 9*x + QQ(441,25), + QQ(233150,19773)*x - QQ(102500,6591), + -QQ(1288744821,543589225)] + + +def test_dup_primitive_prs(): + R, x = ring("x", ZZ) + + f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + + assert R.dup_primitive_prs(f, g) == [ + f, + g, + -5*x**4 + x**2 - 3, + 13*x**2 + 25*x - 49, + 4663*x - 6150, + 1] + + +def test_dup_subresultants(): + R, x = ring("x", ZZ) + + assert R.dup_resultant(0, 0) == 0 + + assert R.dup_resultant(1, 0) == 0 + assert R.dup_resultant(0, 1) == 0 + + f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + + a = 15*x**4 - 3*x**2 + 9 + b = 65*x**2 + 125*x - 245 + c = 9326*x - 12300 + d = 260708 + + assert R.dup_subresultants(f, g) == [f, g, a, b, c, d] + assert R.dup_resultant(f, g) == R.dup_LC(d) + + f = x**2 - 2*x + 1 + g = x**2 - 1 + + a = 2*x - 2 + + assert R.dup_subresultants(f, g) == [f, g, a] + assert R.dup_resultant(f, g) == 0 + + f = x**2 + 1 + g = x**2 - 1 + + a = -2 + + assert R.dup_subresultants(f, g) == [f, g, a] + assert R.dup_resultant(f, g) == 4 + + f = x**2 - 1 + g = x**3 - x**2 + 2 + + assert R.dup_resultant(f, g) == 0 + + f = 3*x**3 - x + g = 5*x**2 + 1 + + assert R.dup_resultant(f, g) == 64 + + f = x**2 - 2*x + 7 + g = x**3 - x + 5 + + assert R.dup_resultant(f, g) == 265 + + f = x**3 - 6*x**2 + 11*x - 6 + g = x**3 - 15*x**2 + 74*x - 120 + + assert R.dup_resultant(f, g) == -8640 + + f = x**3 - 6*x**2 + 11*x - 6 + g = x**3 - 10*x**2 + 29*x - 20 + + assert R.dup_resultant(f, g) == 0 + + f = x**3 - 1 + g = x**3 + 2*x**2 + 2*x - 1 + + assert R.dup_resultant(f, g) == 16 + + f = x**8 - 2 + g = x - 1 + + assert R.dup_resultant(f, g) == -1 + + +def test_dmp_subresultants(): + R, x, y = ring("x,y", ZZ) + + assert R.dmp_resultant(0, 0) == 0 + assert R.dmp_prs_resultant(0, 0)[0] == 0 + assert R.dmp_zz_collins_resultant(0, 0) == 0 + assert R.dmp_qq_collins_resultant(0, 0) == 0 + + assert R.dmp_resultant(1, 0) == 0 + assert R.dmp_resultant(1, 0) == 0 + assert R.dmp_resultant(1, 0) == 0 + + assert R.dmp_resultant(0, 1) == 0 + assert R.dmp_prs_resultant(0, 1)[0] == 0 + assert R.dmp_zz_collins_resultant(0, 1) == 0 + assert R.dmp_qq_collins_resultant(0, 1) == 0 + + f = 3*x**2*y - y**3 - 4 + g = x**2 + x*y**3 - 9 + + a = 3*x*y**4 + y**3 - 27*y + 4 + b = -3*y**10 - 12*y**7 + y**6 - 54*y**4 + 8*y**3 + 729*y**2 - 216*y + 16 + + r = R.dmp_LC(b) + + assert R.dmp_subresultants(f, g) == [f, g, a, b] + + assert R.dmp_resultant(f, g) == r + assert R.dmp_prs_resultant(f, g)[0] == r + assert R.dmp_zz_collins_resultant(f, g) == r + assert R.dmp_qq_collins_resultant(f, g) == r + + f = -x**3 + 5 + g = 3*x**2*y + x**2 + + a = 45*y**2 + 30*y + 5 + b = 675*y**3 + 675*y**2 + 225*y + 25 + + r = R.dmp_LC(b) + + assert R.dmp_subresultants(f, g) == [f, g, a] + assert R.dmp_resultant(f, g) == r + assert R.dmp_prs_resultant(f, g)[0] == r + assert R.dmp_zz_collins_resultant(f, g) == r + assert R.dmp_qq_collins_resultant(f, g) == r + + R, x, y, z, u, v = ring("x,y,z,u,v", ZZ) + + f = 6*x**2 - 3*x*y - 2*x*z + y*z + g = x**2 - x*u - x*v + u*v + + 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 \ + - 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 \ + - 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 + + assert R.dmp_zz_collins_resultant(f, g) == r.drop(x) + + R, x, y, z, u, v = ring("x,y,z,u,v", QQ) + + f = x**2 - QQ(1,2)*x*y - QQ(1,3)*x*z + QQ(1,6)*y*z + g = x**2 - x*u - x*v + u*v + + 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 \ + - 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 \ + + 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 \ + - QQ(1,3)*z*u**2*v - QQ(1,3)*z*u*v**2 + u**2*v**2 + + assert R.dmp_qq_collins_resultant(f, g) == r.drop(x) + + Rt, t = ring("t", ZZ) + Rx, x = ring("x", Rt) + + f = x**6 - 5*x**4 + 5*x**2 + 4 + g = -6*t*x**5 + x**4 + 20*t*x**3 - 3*x**2 - 10*t*x + 6 + + assert Rx.dup_resultant(f, g) == 2930944*t**6 + 2198208*t**4 + 549552*t**2 + 45796 + + +def test_dup_discriminant(): + R, x = ring("x", ZZ) + + assert R.dup_discriminant(0) == 0 + assert R.dup_discriminant(x) == 1 + + assert R.dup_discriminant(x**3 + 3*x**2 + 9*x - 13) == -11664 + assert R.dup_discriminant(5*x**5 + x**3 + 2) == 31252160 + assert R.dup_discriminant(x**4 + 2*x**3 + 6*x**2 - 22*x + 13) == 0 + assert R.dup_discriminant(12*x**7 + 15*x**4 + 30*x**3 + x**2 + 1) == -220289699947514112 + + +def test_dmp_discriminant(): + R, x = ring("x", ZZ) + + assert R.dmp_discriminant(0) == 0 + + R, x, y = ring("x,y", ZZ) + + assert R.dmp_discriminant(0) == 0 + assert R.dmp_discriminant(y) == 0 + + assert R.dmp_discriminant(x**3 + 3*x**2 + 9*x - 13) == -11664 + assert R.dmp_discriminant(5*x**5 + x**3 + 2) == 31252160 + assert R.dmp_discriminant(x**4 + 2*x**3 + 6*x**2 - 22*x + 13) == 0 + assert R.dmp_discriminant(12*x**7 + 15*x**4 + 30*x**3 + x**2 + 1) == -220289699947514112 + + assert R.dmp_discriminant(x**2*y + 2*y) == (-8*y**2).drop(x) + assert R.dmp_discriminant(x*y**2 + 2*x) == 1 + + R, x, y, z = ring("x,y,z", ZZ) + assert R.dmp_discriminant(x*y + z) == 1 + + R, x, y, z, u = ring("x,y,z,u", ZZ) + assert R.dmp_discriminant(x**2*y + x*z + u) == (-4*y*u + z**2).drop(x) + + R, x, y, z, u, v = ring("x,y,z,u,v", ZZ) + assert R.dmp_discriminant(x**3*y + x**2*z + x*u + v) == \ + (-27*y**2*v**2 + 18*y*z*u*v - 4*y*u**3 - 4*z**3*v + z**2*u**2).drop(x) + + +def test_dup_gcd(): + R, x = ring("x", ZZ) + + f, g = 0, 0 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (0, 0, 0) + + f, g = 2, 0 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, 1, 0) + + f, g = -2, 0 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, -1, 0) + + f, g = 0, -2 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, 0, -1) + + f, g = 0, 2*x + 4 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2*x + 4, 0, 1) + + f, g = 2*x + 4, 0 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2*x + 4, 1, 0) + + f, g = 2, 2 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, 1, 1) + + f, g = -2, 2 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, -1, 1) + + f, g = 2, -2 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, 1, -1) + + f, g = -2, -2 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, -1, -1) + + f, g = x**2 + 2*x + 1, 1 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (1, x**2 + 2*x + 1, 1) + + f, g = x**2 + 2*x + 1, 2 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (1, x**2 + 2*x + 1, 2) + + f, g = 2*x**2 + 4*x + 2, 2 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, x**2 + 2*x + 1, 1) + + f, g = 2, 2*x**2 + 4*x + 2 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (2, 1, x**2 + 2*x + 1) + + f, g = 2*x**2 + 4*x + 2, x + 1 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (x + 1, 2*x + 2, 1) + + f, g = x + 1, 2*x**2 + 4*x + 2 + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (x + 1, 1, 2*x + 2) + + f, g = x - 31, x + assert R.dup_zz_heu_gcd(f, g) == R.dup_rr_prs_gcd(f, g) == (1, f, g) + + f = x**4 + 8*x**3 + 21*x**2 + 22*x + 8 + g = x**3 + 6*x**2 + 11*x + 6 + + h = x**2 + 3*x + 2 + + cff = x**2 + 5*x + 4 + cfg = x + 3 + + assert R.dup_zz_heu_gcd(f, g) == (h, cff, cfg) + assert R.dup_rr_prs_gcd(f, g) == (h, cff, cfg) + + f = x**4 - 4 + g = x**4 + 4*x**2 + 4 + + h = x**2 + 2 + + cff = x**2 - 2 + cfg = x**2 + 2 + + assert R.dup_zz_heu_gcd(f, g) == (h, cff, cfg) + assert R.dup_rr_prs_gcd(f, g) == (h, cff, cfg) + + f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + + h = 1 + + cff = f + cfg = g + + assert R.dup_zz_heu_gcd(f, g) == (h, cff, cfg) + assert R.dup_rr_prs_gcd(f, g) == (h, cff, cfg) + + R, x = ring("x", QQ) + + f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + + h = 1 + + cff = f + cfg = g + + assert R.dup_qq_heu_gcd(f, g) == (h, cff, cfg) + assert R.dup_ff_prs_gcd(f, g) == (h, cff, cfg) + + R, x = ring("x", ZZ) + + f = - 352518131239247345597970242177235495263669787845475025293906825864749649589178600387510272*x**49 \ + + 46818041807522713962450042363465092040687472354933295397472942006618953623327997952*x**42 \ + + 378182690892293941192071663536490788434899030680411695933646320291525827756032*x**35 \ + + 112806468807371824947796775491032386836656074179286744191026149539708928*x**28 \ + - 12278371209708240950316872681744825481125965781519138077173235712*x**21 \ + + 289127344604779611146960547954288113529690984687482920704*x**14 \ + + 19007977035740498977629742919480623972236450681*x**7 \ + + 311973482284542371301330321821976049 + + g = 365431878023781158602430064717380211405897160759702125019136*x**21 \ + + 197599133478719444145775798221171663643171734081650688*x**14 \ + - 9504116979659010018253915765478924103928886144*x**7 \ + - 311973482284542371301330321821976049 + + assert R.dup_zz_heu_gcd(f, R.dup_diff(f, 1))[0] == g + assert R.dup_rr_prs_gcd(f, R.dup_diff(f, 1))[0] == g + + R, x = ring("x", QQ) + + f = QQ(1,2)*x**2 + x + QQ(1,2) + g = QQ(1,2)*x + QQ(1,2) + + h = x + 1 + + assert R.dup_qq_heu_gcd(f, g) == (h, g, QQ(1,2)) + assert R.dup_ff_prs_gcd(f, g) == (h, g, QQ(1,2)) + + R, x = ring("x", ZZ) + + f = 1317378933230047068160*x + 2945748836994210856960 + g = 120352542776360960*x + 269116466014453760 + + h = 120352542776360960*x + 269116466014453760 + cff = 10946 + cfg = 1 + + assert R.dup_zz_heu_gcd(f, g) == (h, cff, cfg) + + +def test_dmp_gcd(): + R, x, y = ring("x,y", ZZ) + + f, g = 0, 0 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (0, 0, 0) + + f, g = 2, 0 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, 1, 0) + + f, g = -2, 0 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, -1, 0) + + f, g = 0, -2 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, 0, -1) + + f, g = 0, 2*x + 4 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2*x + 4, 0, 1) + + f, g = 2*x + 4, 0 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2*x + 4, 1, 0) + + f, g = 2, 2 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, 1, 1) + + f, g = -2, 2 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, -1, 1) + + f, g = 2, -2 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, 1, -1) + + f, g = -2, -2 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, -1, -1) + + f, g = x**2 + 2*x + 1, 1 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (1, x**2 + 2*x + 1, 1) + + f, g = x**2 + 2*x + 1, 2 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (1, x**2 + 2*x + 1, 2) + + f, g = 2*x**2 + 4*x + 2, 2 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, x**2 + 2*x + 1, 1) + + f, g = 2, 2*x**2 + 4*x + 2 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (2, 1, x**2 + 2*x + 1) + + f, g = 2*x**2 + 4*x + 2, x + 1 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (x + 1, 2*x + 2, 1) + + f, g = x + 1, 2*x**2 + 4*x + 2 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (x + 1, 1, 2*x + 2) + + R, x, y, z, u = ring("x,y,z,u", ZZ) + + f, g = u**2 + 2*u + 1, 2*u + 2 + assert R.dmp_zz_heu_gcd(f, g) == R.dmp_rr_prs_gcd(f, g) == (u + 1, u + 1, 2) + + f, g = z**2*u**2 + 2*z**2*u + z**2 + z*u + z, u**2 + 2*u + 1 + h, cff, cfg = u + 1, z**2*u + z**2 + z, u + 1 + + assert R.dmp_zz_heu_gcd(f, g) == (h, cff, cfg) + assert R.dmp_rr_prs_gcd(f, g) == (h, cff, cfg) + + assert R.dmp_zz_heu_gcd(g, f) == (h, cfg, cff) + assert R.dmp_rr_prs_gcd(g, f) == (h, cfg, cff) + + R, x, y, z = ring("x,y,z", ZZ) + + f, g, h = map(R.from_dense, dmp_fateman_poly_F_1(2, ZZ)) + H, cff, cfg = R.dmp_zz_heu_gcd(f, g) + + assert H == h and R.dmp_mul(H, cff) == f \ + and R.dmp_mul(H, cfg) == g + + H, cff, cfg = R.dmp_rr_prs_gcd(f, g) + + assert H == h and R.dmp_mul(H, cff) == f \ + and R.dmp_mul(H, cfg) == g + + R, x, y, z, u, v = ring("x,y,z,u,v", ZZ) + + f, g, h = map(R.from_dense, dmp_fateman_poly_F_1(4, ZZ)) + H, cff, cfg = R.dmp_zz_heu_gcd(f, g) + + assert H == h and R.dmp_mul(H, cff) == f \ + and R.dmp_mul(H, cfg) == g + + R, x, y, z, u, v, a, b = ring("x,y,z,u,v,a,b", ZZ) + + f, g, h = map(R.from_dense, dmp_fateman_poly_F_1(6, ZZ)) + H, cff, cfg = R.dmp_zz_heu_gcd(f, g) + + assert H == h and R.dmp_mul(H, cff) == f \ + and R.dmp_mul(H, cfg) == g + + R, x, y, z, u, v, a, b, c, d = ring("x,y,z,u,v,a,b,c,d", ZZ) + + f, g, h = map(R.from_dense, dmp_fateman_poly_F_1(8, ZZ)) + H, cff, cfg = R.dmp_zz_heu_gcd(f, g) + + assert H == h and R.dmp_mul(H, cff) == f \ + and R.dmp_mul(H, cfg) == g + + R, x, y, z = ring("x,y,z", ZZ) + + f, g, h = map(R.from_dense, dmp_fateman_poly_F_2(2, ZZ)) + H, cff, cfg = R.dmp_zz_heu_gcd(f, g) + + assert H == h and R.dmp_mul(H, cff) == f \ + and R.dmp_mul(H, cfg) == g + + H, cff, cfg = R.dmp_rr_prs_gcd(f, g) + + assert H == h and R.dmp_mul(H, cff) == f \ + and R.dmp_mul(H, cfg) == g + + f, g, h = map(R.from_dense, dmp_fateman_poly_F_3(2, ZZ)) + H, cff, cfg = R.dmp_zz_heu_gcd(f, g) + + assert H == h and R.dmp_mul(H, cff) == f \ + and R.dmp_mul(H, cfg) == g + + H, cff, cfg = R.dmp_rr_prs_gcd(f, g) + + assert H == h and R.dmp_mul(H, cff) == f \ + and R.dmp_mul(H, cfg) == g + + R, x, y, z, u, v = ring("x,y,z,u,v", ZZ) + + f, g, h = map(R.from_dense, dmp_fateman_poly_F_3(4, ZZ)) + H, cff, cfg = R.dmp_inner_gcd(f, g) + + assert H == h and R.dmp_mul(H, cff) == f \ + and R.dmp_mul(H, cfg) == g + + R, x, y = ring("x,y", QQ) + + f = QQ(1,2)*x**2 + x + QQ(1,2) + g = QQ(1,2)*x + QQ(1,2) + + h = x + 1 + + assert R.dmp_qq_heu_gcd(f, g) == (h, g, QQ(1,2)) + assert R.dmp_ff_prs_gcd(f, g) == (h, g, QQ(1,2)) + + R, x, y = ring("x,y", RR) + + f = 2.1*x*y**2 - 2.2*x*y + 2.1*x + g = 1.0*x**3 + + assert R.dmp_ff_prs_gcd(f, g) == \ + (1.0*x, 2.1*y**2 - 2.2*y + 2.1, 1.0*x**2) + + +def test_dup_lcm(): + R, x = ring("x", ZZ) + + assert R.dup_lcm(2, 6) == 6 + + assert R.dup_lcm(2*x**3, 6*x) == 6*x**3 + assert R.dup_lcm(2*x**3, 3*x) == 6*x**3 + + assert R.dup_lcm(x**2 + x, x) == x**2 + x + assert R.dup_lcm(x**2 + x, 2*x) == 2*x**2 + 2*x + assert R.dup_lcm(x**2 + 2*x, x) == x**2 + 2*x + assert R.dup_lcm(2*x**2 + x, x) == 2*x**2 + x + assert R.dup_lcm(2*x**2 + x, 2*x) == 4*x**2 + 2*x + + +def test_dmp_lcm(): + R, x, y = ring("x,y", ZZ) + + assert R.dmp_lcm(2, 6) == 6 + assert R.dmp_lcm(x, y) == x*y + + assert R.dmp_lcm(2*x**3, 6*x*y**2) == 6*x**3*y**2 + assert R.dmp_lcm(2*x**3, 3*x*y**2) == 6*x**3*y**2 + + assert R.dmp_lcm(x**2*y, x*y**2) == x**2*y**2 + + f = 2*x*y**5 - 3*x*y**4 - 2*x*y**3 + 3*x*y**2 + g = y**5 - 2*y**3 + y + 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 + + assert R.dmp_lcm(f, g) == h + + f = x**3 - 3*x**2*y - 9*x*y**2 - 5*y**3 + g = x**4 + 6*x**3*y + 12*x**2*y**2 + 10*x*y**3 + 3*y**4 + h = x**5 + x**4*y - 18*x**3*y**2 - 50*x**2*y**3 - 47*x*y**4 - 15*y**5 + + assert R.dmp_lcm(f, g) == h + + +def test_dmp_content(): + R, x,y = ring("x,y", ZZ) + + assert R.dmp_content(-2) == 2 + + f, g, F = 3*y**2 + 2*y + 1, 1, 0 + + for i in range(0, 5): + g *= f + F += x**i*g + + assert R.dmp_content(F) == f.drop(x) + + R, x,y,z = ring("x,y,z", ZZ) + + assert R.dmp_content(f_4) == 1 + assert R.dmp_content(f_5) == 1 + + R, x,y,z,t = ring("x,y,z,t", ZZ) + assert R.dmp_content(f_6) == 1 + + +def test_dmp_primitive(): + R, x,y = ring("x,y", ZZ) + + assert R.dmp_primitive(0) == (0, 0) + assert R.dmp_primitive(1) == (1, 1) + + f, g, F = 3*y**2 + 2*y + 1, 1, 0 + + for i in range(0, 5): + g *= f + F += x**i*g + + assert R.dmp_primitive(F) == (f.drop(x), F / f) + + R, x,y,z = ring("x,y,z", ZZ) + + cont, f = R.dmp_primitive(f_4) + assert cont == 1 and f == f_4 + cont, f = R.dmp_primitive(f_5) + assert cont == 1 and f == f_5 + + R, x,y,z,t = ring("x,y,z,t", ZZ) + + cont, f = R.dmp_primitive(f_6) + assert cont == 1 and f == f_6 + + +def test_dup_cancel(): + R, x = ring("x", ZZ) + + f = 2*x**2 - 2 + g = x**2 - 2*x + 1 + + p = 2*x + 2 + q = x - 1 + + assert R.dup_cancel(f, g) == (p, q) + assert R.dup_cancel(f, g, include=False) == (1, 1, p, q) + + f = -x - 2 + g = 3*x - 4 + + F = x + 2 + G = -3*x + 4 + + assert R.dup_cancel(f, g) == (f, g) + assert R.dup_cancel(F, G) == (f, g) + + assert R.dup_cancel(0, 0) == (0, 0) + assert R.dup_cancel(0, 0, include=False) == (1, 1, 0, 0) + + assert R.dup_cancel(x, 0) == (1, 0) + assert R.dup_cancel(x, 0, include=False) == (1, 1, 1, 0) + + assert R.dup_cancel(0, x) == (0, 1) + assert R.dup_cancel(0, x, include=False) == (1, 1, 0, 1) + + f = 0 + g = x + one = 1 + + assert R.dup_cancel(f, g, include=True) == (f, one) + + +def test_dmp_cancel(): + R, x, y = ring("x,y", ZZ) + + f = 2*x**2 - 2 + g = x**2 - 2*x + 1 + + p = 2*x + 2 + q = x - 1 + + assert R.dmp_cancel(f, g) == (p, q) + assert R.dmp_cancel(f, g, include=False) == (1, 1, p, q) + + assert R.dmp_cancel(0, 0) == (0, 0) + assert R.dmp_cancel(0, 0, include=False) == (1, 1, 0, 0) + + assert R.dmp_cancel(y, 0) == (1, 0) + assert R.dmp_cancel(y, 0, include=False) == (1, 1, 1, 0) + + assert R.dmp_cancel(0, y) == (0, 1) + assert R.dmp_cancel(0, y, include=False) == (1, 1, 0, 1) diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_factortools.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_factortools.py new file mode 100644 index 0000000000000000000000000000000000000000..7f99097c71e9cde761a800b01b149ec5c9896266 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_factortools.py @@ -0,0 +1,784 @@ +"""Tools for polynomial factorization routines in characteristic zero. """ + +from sympy.polys.rings import ring, xring +from sympy.polys.domains import FF, ZZ, QQ, ZZ_I, QQ_I, RR, EX + +from sympy.polys import polyconfig as config +from sympy.polys.polyerrors import DomainError +from sympy.polys.polyclasses import ANP +from sympy.polys.specialpolys import f_polys, w_polys + +from sympy.core.numbers import I +from sympy.functions.elementary.miscellaneous import sqrt +from sympy.functions.elementary.trigonometric import sin +from sympy.ntheory.generate import nextprime +from sympy.testing.pytest import raises, XFAIL + + +f_0, f_1, f_2, f_3, f_4, f_5, f_6 = f_polys() +w_1, w_2 = w_polys() + +def test_dup_trial_division(): + R, x = ring("x", ZZ) + assert R.dup_trial_division(x**5 + 8*x**4 + 25*x**3 + 38*x**2 + 28*x + 8, (x + 1, x + 2)) == [(x + 1, 2), (x + 2, 3)] + + +def test_dmp_trial_division(): + R, x, y = ring("x,y", ZZ) + assert R.dmp_trial_division(x**5 + 8*x**4 + 25*x**3 + 38*x**2 + 28*x + 8, (x + 1, x + 2)) == [(x + 1, 2), (x + 2, 3)] + + +def test_dup_zz_mignotte_bound(): + R, x = ring("x", ZZ) + assert R.dup_zz_mignotte_bound(2*x**2 + 3*x + 4) == 6 + assert R.dup_zz_mignotte_bound(x**3 + 14*x**2 + 56*x + 64) == 152 + + +def test_dmp_zz_mignotte_bound(): + R, x, y = ring("x,y", ZZ) + assert R.dmp_zz_mignotte_bound(2*x**2 + 3*x + 4) == 32 + + +def test_dup_zz_hensel_step(): + R, x = ring("x", ZZ) + + f = x**4 - 1 + g = x**3 + 2*x**2 - x - 2 + h = x - 2 + s = -2 + t = 2*x**2 - 2*x - 1 + + G, H, S, T = R.dup_zz_hensel_step(5, f, g, h, s, t) + + assert G == x**3 + 7*x**2 - x - 7 + assert H == x - 7 + assert S == 8 + assert T == -8*x**2 - 12*x - 1 + + +def test_dup_zz_hensel_lift(): + R, x = ring("x", ZZ) + + f = x**4 - 1 + F = [x - 1, x - 2, x + 2, x + 1] + + assert R.dup_zz_hensel_lift(ZZ(5), f, F, 4) == \ + [x - 1, x - 182, x + 182, x + 1] + + +def test_dup_zz_irreducible_p(): + R, x = ring("x", ZZ) + + assert R.dup_zz_irreducible_p(3*x**4 + 2*x**3 + 6*x**2 + 8*x + 7) is None + assert R.dup_zz_irreducible_p(3*x**4 + 2*x**3 + 6*x**2 + 8*x + 4) is None + + assert R.dup_zz_irreducible_p(3*x**4 + 2*x**3 + 6*x**2 + 8*x + 10) is True + assert R.dup_zz_irreducible_p(3*x**4 + 2*x**3 + 6*x**2 + 8*x + 14) is True + + +def test_dup_cyclotomic_p(): + R, x = ring("x", ZZ) + + assert R.dup_cyclotomic_p(x - 1) is True + assert R.dup_cyclotomic_p(x + 1) is True + assert R.dup_cyclotomic_p(x**2 + x + 1) is True + assert R.dup_cyclotomic_p(x**2 + 1) is True + assert R.dup_cyclotomic_p(x**4 + x**3 + x**2 + x + 1) is True + assert R.dup_cyclotomic_p(x**2 - x + 1) is True + assert R.dup_cyclotomic_p(x**6 + x**5 + x**4 + x**3 + x**2 + x + 1) is True + assert R.dup_cyclotomic_p(x**4 + 1) is True + assert R.dup_cyclotomic_p(x**6 + x**3 + 1) is True + + assert R.dup_cyclotomic_p(0) is False + assert R.dup_cyclotomic_p(1) is False + assert R.dup_cyclotomic_p(x) is False + assert R.dup_cyclotomic_p(x + 2) is False + assert R.dup_cyclotomic_p(3*x + 1) is False + assert R.dup_cyclotomic_p(x**2 - 1) is False + + f = x**16 + x**14 - x**10 + x**8 - x**6 + x**2 + 1 + assert R.dup_cyclotomic_p(f) is False + + g = x**16 + x**14 - x**10 - x**8 - x**6 + x**2 + 1 + assert R.dup_cyclotomic_p(g) is True + + R, x = ring("x", QQ) + assert R.dup_cyclotomic_p(x**2 + x + 1) is True + assert R.dup_cyclotomic_p(QQ(1,2)*x**2 + x + 1) is False + + R, x = ring("x", ZZ["y"]) + assert R.dup_cyclotomic_p(x**2 + x + 1) is False + + +def test_dup_zz_cyclotomic_poly(): + R, x = ring("x", ZZ) + + assert R.dup_zz_cyclotomic_poly(1) == x - 1 + assert R.dup_zz_cyclotomic_poly(2) == x + 1 + assert R.dup_zz_cyclotomic_poly(3) == x**2 + x + 1 + assert R.dup_zz_cyclotomic_poly(4) == x**2 + 1 + assert R.dup_zz_cyclotomic_poly(5) == x**4 + x**3 + x**2 + x + 1 + assert R.dup_zz_cyclotomic_poly(6) == x**2 - x + 1 + assert R.dup_zz_cyclotomic_poly(7) == x**6 + x**5 + x**4 + x**3 + x**2 + x + 1 + assert R.dup_zz_cyclotomic_poly(8) == x**4 + 1 + assert R.dup_zz_cyclotomic_poly(9) == x**6 + x**3 + 1 + + +def test_dup_zz_cyclotomic_factor(): + R, x = ring("x", ZZ) + + assert R.dup_zz_cyclotomic_factor(0) is None + assert R.dup_zz_cyclotomic_factor(1) is None + + assert R.dup_zz_cyclotomic_factor(2*x**10 - 1) is None + assert R.dup_zz_cyclotomic_factor(x**10 - 3) is None + assert R.dup_zz_cyclotomic_factor(x**10 + x**5 - 1) is None + + assert R.dup_zz_cyclotomic_factor(x + 1) == [x + 1] + assert R.dup_zz_cyclotomic_factor(x - 1) == [x - 1] + + assert R.dup_zz_cyclotomic_factor(x**2 + 1) == [x**2 + 1] + assert R.dup_zz_cyclotomic_factor(x**2 - 1) == [x - 1, x + 1] + + assert R.dup_zz_cyclotomic_factor(x**27 + 1) == \ + [x + 1, x**2 - x + 1, x**6 - x**3 + 1, x**18 - x**9 + 1] + assert R.dup_zz_cyclotomic_factor(x**27 - 1) == \ + [x - 1, x**2 + x + 1, x**6 + x**3 + 1, x**18 + x**9 + 1] + + +def test_dup_zz_factor(): + R, x = ring("x", ZZ) + + assert R.dup_zz_factor(0) == (0, []) + assert R.dup_zz_factor(7) == (7, []) + assert R.dup_zz_factor(-7) == (-7, []) + + assert R.dup_zz_factor_sqf(0) == (0, []) + assert R.dup_zz_factor_sqf(7) == (7, []) + assert R.dup_zz_factor_sqf(-7) == (-7, []) + + assert R.dup_zz_factor(2*x + 4) == (2, [(x + 2, 1)]) + assert R.dup_zz_factor_sqf(2*x + 4) == (2, [x + 2]) + + f = x**4 + x + 1 + + for i in range(0, 20): + assert R.dup_zz_factor(f) == (1, [(f, 1)]) + + assert R.dup_zz_factor(x**2 + 2*x + 2) == \ + (1, [(x**2 + 2*x + 2, 1)]) + + assert R.dup_zz_factor(18*x**2 + 12*x + 2) == \ + (2, [(3*x + 1, 2)]) + + assert R.dup_zz_factor(-9*x**2 + 1) == \ + (-1, [(3*x - 1, 1), + (3*x + 1, 1)]) + + assert R.dup_zz_factor_sqf(-9*x**2 + 1) == \ + (-1, [3*x - 1, + 3*x + 1]) + + # The order of the factors will be different when the ground types are + # flint. At the higher level dup_factor_list will sort the factors. + c, factors = R.dup_zz_factor(x**3 - 6*x**2 + 11*x - 6) + assert c == 1 + assert set(factors) == {(x - 3, 1), (x - 2, 1), (x - 1, 1)} + + assert R.dup_zz_factor_sqf(x**3 - 6*x**2 + 11*x - 6) == \ + (1, [x - 3, + x - 2, + x - 1]) + + assert R.dup_zz_factor(3*x**3 + 10*x**2 + 13*x + 10) == \ + (1, [(x + 2, 1), + (3*x**2 + 4*x + 5, 1)]) + + assert R.dup_zz_factor_sqf(3*x**3 + 10*x**2 + 13*x + 10) == \ + (1, [x + 2, + 3*x**2 + 4*x + 5]) + + c, factors = R.dup_zz_factor(-x**6 + x**2) + assert c == -1 + assert set(factors) == {(x, 2), (x - 1, 1), (x + 1, 1), (x**2 + 1, 1)} + + f = 1080*x**8 + 5184*x**7 + 2099*x**6 + 744*x**5 + 2736*x**4 - 648*x**3 + 129*x**2 - 324 + + assert R.dup_zz_factor(f) == \ + (1, [(5*x**4 + 24*x**3 + 9*x**2 + 12, 1), + (216*x**4 + 31*x**2 - 27, 1)]) + + f = -29802322387695312500000000000000000000*x**25 \ + + 2980232238769531250000000000000000*x**20 \ + + 1743435859680175781250000000000*x**15 \ + + 114142894744873046875000000*x**10 \ + - 210106372833251953125*x**5 \ + + 95367431640625 + + c, factors = R.dup_zz_factor(f) + assert c == -95367431640625 + assert set(factors) == { + (5*x - 1, 1), + (100*x**2 + 10*x - 1, 2), + (625*x**4 + 125*x**3 + 25*x**2 + 5*x + 1, 1), + (10000*x**4 - 3000*x**3 + 400*x**2 - 20*x + 1, 2), + (10000*x**4 + 2000*x**3 + 400*x**2 + 30*x + 1, 2), + } + + f = x**10 - 1 + + config.setup('USE_CYCLOTOMIC_FACTOR', True) + c0, F_0 = R.dup_zz_factor(f) + + config.setup('USE_CYCLOTOMIC_FACTOR', False) + c1, F_1 = R.dup_zz_factor(f) + + assert c0 == c1 == 1 + assert set(F_0) == set(F_1) == { + (x - 1, 1), + (x + 1, 1), + (x**4 - x**3 + x**2 - x + 1, 1), + (x**4 + x**3 + x**2 + x + 1, 1), + } + + config.setup('USE_CYCLOTOMIC_FACTOR') + + f = x**10 + 1 + + config.setup('USE_CYCLOTOMIC_FACTOR', True) + F_0 = R.dup_zz_factor(f) + + config.setup('USE_CYCLOTOMIC_FACTOR', False) + F_1 = R.dup_zz_factor(f) + + assert F_0 == F_1 == \ + (1, [(x**2 + 1, 1), + (x**8 - x**6 + x**4 - x**2 + 1, 1)]) + + config.setup('USE_CYCLOTOMIC_FACTOR') + +def test_dmp_zz_wang(): + R, x,y,z = ring("x,y,z", ZZ) + UV, _x = ring("x", ZZ) + + p = ZZ(nextprime(R.dmp_zz_mignotte_bound(w_1))) + assert p == 6291469 + + t_1, k_1, e_1 = y, 1, ZZ(-14) + t_2, k_2, e_2 = z, 2, ZZ(3) + t_3, k_3, e_3 = y + z, 2, ZZ(-11) + t_4, k_4, e_4 = y - z, 1, ZZ(-17) + + T = [t_1, t_2, t_3, t_4] + K = [k_1, k_2, k_3, k_4] + E = [e_1, e_2, e_3, e_4] + + T = zip([ t.drop(x) for t in T ], K) + + A = [ZZ(-14), ZZ(3)] + + S = R.dmp_eval_tail(w_1, A) + cs, s = UV.dup_primitive(S) + + assert cs == 1 and s == S == \ + 1036728*_x**6 + 915552*_x**5 + 55748*_x**4 + 105621*_x**3 - 17304*_x**2 - 26841*_x - 644 + + assert R.dmp_zz_wang_non_divisors(E, cs, ZZ(4)) == [7, 3, 11, 17] + assert UV.dup_sqf_p(s) and UV.dup_degree(s) == R.dmp_degree(w_1) + + _, H = UV.dup_zz_factor_sqf(s) + + h_1 = 44*_x**2 + 42*_x + 1 + h_2 = 126*_x**2 - 9*_x + 28 + h_3 = 187*_x**2 - 23 + + assert H == [h_1, h_2, h_3] + + LC = [ lc.drop(x) for lc in [-4*y - 4*z, -y*z**2, y**2 - z**2] ] + + assert R.dmp_zz_wang_lead_coeffs(w_1, T, cs, E, H, A) == (w_1, H, LC) + + factors = R.dmp_zz_wang_hensel_lifting(w_1, H, LC, A, p) + assert R.dmp_expand(factors) == w_1 + + +@XFAIL +def test_dmp_zz_wang_fail(): + R, x,y,z = ring("x,y,z", ZZ) + UV, _x = ring("x", ZZ) + + p = ZZ(nextprime(R.dmp_zz_mignotte_bound(w_1))) + assert p == 6291469 + + H_1 = [44*x**2 + 42*x + 1, 126*x**2 - 9*x + 28, 187*x**2 - 23] + H_2 = [-4*x**2*y - 12*x**2 - 3*x*y + 1, -9*x**2*y - 9*x - 2*y, x**2*y**2 - 9*x**2 + y - 9] + H_3 = [-4*x**2*y - 12*x**2 - 3*x*y + 1, -9*x**2*y - 9*x - 2*y, x**2*y**2 - 9*x**2 + y - 9] + + c_1 = -70686*x**5 - 5863*x**4 - 17826*x**3 + 2009*x**2 + 5031*x + 74 + c_2 = 9*x**5*y**4 + 12*x**5*y**3 - 45*x**5*y**2 - 108*x**5*y - 324*x**5 + 18*x**4*y**3 - 216*x**4*y**2 - 810*x**4*y + 2*x**3*y**4 + 9*x**3*y**3 - 252*x**3*y**2 - 288*x**3*y - 945*x**3 - 30*x**2*y**2 - 414*x**2*y + 2*x*y**3 - 54*x*y**2 - 3*x*y + 81*x + 12*y + c_3 = -36*x**4*y**2 - 108*x**4*y - 27*x**3*y**2 - 36*x**3*y - 108*x**3 - 8*x**2*y**2 - 42*x**2*y - 6*x*y**2 + 9*x + 2*y + + assert R.dmp_zz_diophantine(H_1, c_1, [], 5, p) == [-3*x, -2, 1] + assert R.dmp_zz_diophantine(H_2, c_2, [ZZ(-14)], 5, p) == [-x*y, -3*x, -6] + assert R.dmp_zz_diophantine(H_3, c_3, [ZZ(-14)], 5, p) == [0, 0, -1] + + +def test_issue_6355(): + # This tests a bug in the Wang algorithm that occurred only with a very + # specific set of random numbers. + random_sequence = [-1, -1, 0, 0, 0, 0, -1, -1, 0, -1, 3, -1, 3, 3, 3, 3, -1, 3] + + R, x, y, z = ring("x,y,z", ZZ) + f = 2*x**2 + y*z - y - z**2 + z + + assert R.dmp_zz_wang(f, seed=random_sequence) == [f] + + +def test_dmp_zz_factor(): + R, x = ring("x", ZZ) + assert R.dmp_zz_factor(0) == (0, []) + assert R.dmp_zz_factor(7) == (7, []) + assert R.dmp_zz_factor(-7) == (-7, []) + + assert R.dmp_zz_factor(x**2 - 9) == (1, [(x - 3, 1), (x + 3, 1)]) + + R, x, y = ring("x,y", ZZ) + assert R.dmp_zz_factor(0) == (0, []) + assert R.dmp_zz_factor(7) == (7, []) + assert R.dmp_zz_factor(-7) == (-7, []) + + assert R.dmp_zz_factor(x) == (1, [(x, 1)]) + assert R.dmp_zz_factor(4*x) == (4, [(x, 1)]) + assert R.dmp_zz_factor(4*x + 2) == (2, [(2*x + 1, 1)]) + assert R.dmp_zz_factor(x*y + 1) == (1, [(x*y + 1, 1)]) + assert R.dmp_zz_factor(y**2 + 1) == (1, [(y**2 + 1, 1)]) + assert R.dmp_zz_factor(y**2 - 1) == (1, [(y - 1, 1), (y + 1, 1)]) + + assert R.dmp_zz_factor(x**2*y**2 + 6*x**2*y + 9*x**2 - 1) == (1, [(x*y + 3*x - 1, 1), (x*y + 3*x + 1, 1)]) + assert R.dmp_zz_factor(x**2*y**2 - 9) == (1, [(x*y - 3, 1), (x*y + 3, 1)]) + + R, x, y, z = ring("x,y,z", ZZ) + assert R.dmp_zz_factor(x**2*y**2*z**2 - 9) == \ + (1, [(x*y*z - 3, 1), + (x*y*z + 3, 1)]) + + R, x, y, z, u = ring("x,y,z,u", ZZ) + assert R.dmp_zz_factor(x**2*y**2*z**2*u**2 - 9) == \ + (1, [(x*y*z*u - 3, 1), + (x*y*z*u + 3, 1)]) + + R, x, y, z = ring("x,y,z", ZZ) + assert R.dmp_zz_factor(f_1) == \ + (1, [(x + y*z + 20, 1), + (x*y + z + 10, 1), + (x*z + y + 30, 1)]) + + assert R.dmp_zz_factor(f_2) == \ + (1, [(x**2*y**2 + x**2*z**2 + y + 90, 1), + (x**3*y + x**3*z + z - 11, 1)]) + + assert R.dmp_zz_factor(f_3) == \ + (1, [(x**2*y**2 + x*z**4 + x + z, 1), + (x**3 + x*y*z + y**2 + y*z**3, 1)]) + + assert R.dmp_zz_factor(f_4) == \ + (-1, [(x*y**3 + z**2, 1), + (x**2*z + y**4*z**2 + 5, 1), + (x**3*y - z**2 - 3, 1), + (x**3*y**4 + z**2, 1)]) + + assert R.dmp_zz_factor(f_5) == \ + (-1, [(x + y - z, 3)]) + + R, x, y, z, t = ring("x,y,z,t", ZZ) + assert R.dmp_zz_factor(f_6) == \ + (1, [(47*x*y + z**3*t**2 - t**2, 1), + (45*x**3 - 9*y**3 - y**2 + 3*z**3 + 2*z*t, 1)]) + + R, x, y, z = ring("x,y,z", ZZ) + assert R.dmp_zz_factor(w_1) == \ + (1, [(x**2*y**2 - x**2*z**2 + y - z**2, 1), + (x**2*y*z**2 + 3*x*z + 2*y, 1), + (4*x**2*y + 4*x**2*z + x*y*z - 1, 1)]) + + R, x, y = ring("x,y", ZZ) + f = -12*x**16*y + 240*x**12*y**3 - 768*x**10*y**4 + 1080*x**8*y**5 - 768*x**6*y**6 + 240*x**4*y**7 - 12*y**9 + + assert R.dmp_zz_factor(f) == \ + (-12, [(y, 1), + (x**2 - y, 6), + (x**4 + 6*x**2*y + y**2, 1)]) + + +def test_dup_qq_i_factor(): + R, x = ring("x", QQ_I) + i = QQ_I(0, 1) + + assert R.dup_qq_i_factor(x**2 - 2) == (QQ_I(1, 0), [(x**2 - 2, 1)]) + + assert R.dup_qq_i_factor(x**2 - 1) == (QQ_I(1, 0), [(x - 1, 1), (x + 1, 1)]) + + assert R.dup_qq_i_factor(x**2 + 1) == (QQ_I(1, 0), [(x - i, 1), (x + i, 1)]) + + assert R.dup_qq_i_factor(x**2/4 + 1) == \ + (QQ_I(QQ(1, 4), 0), [(x - 2*i, 1), (x + 2*i, 1)]) + + assert R.dup_qq_i_factor(x**2 + 4) == \ + (QQ_I(1, 0), [(x - 2*i, 1), (x + 2*i, 1)]) + + assert R.dup_qq_i_factor(x**2 + 2*x + 1) == \ + (QQ_I(1, 0), [(x + 1, 2)]) + + assert R.dup_qq_i_factor(x**2 + 2*i*x - 1) == \ + (QQ_I(1, 0), [(x + i, 2)]) + + f = 8192*x**2 + x*(22656 + 175232*i) - 921416 + 242313*i + + assert R.dup_qq_i_factor(f) == \ + (QQ_I(8192, 0), [(x + QQ_I(QQ(177, 128), QQ(1369, 128)), 2)]) + + +def test_dmp_qq_i_factor(): + R, x, y = ring("x, y", QQ_I) + i = QQ_I(0, 1) + + assert R.dmp_qq_i_factor(x**2 + 2*y**2) == \ + (QQ_I(1, 0), [(x**2 + 2*y**2, 1)]) + + assert R.dmp_qq_i_factor(x**2 + y**2) == \ + (QQ_I(1, 0), [(x - i*y, 1), (x + i*y, 1)]) + + assert R.dmp_qq_i_factor(x**2 + y**2/4) == \ + (QQ_I(1, 0), [(x - i*y/2, 1), (x + i*y/2, 1)]) + + assert R.dmp_qq_i_factor(4*x**2 + y**2) == \ + (QQ_I(4, 0), [(x - i*y/2, 1), (x + i*y/2, 1)]) + + +def test_dup_zz_i_factor(): + R, x = ring("x", ZZ_I) + i = ZZ_I(0, 1) + + assert R.dup_zz_i_factor(x**2 - 2) == (ZZ_I(1, 0), [(x**2 - 2, 1)]) + + assert R.dup_zz_i_factor(x**2 - 1) == (ZZ_I(1, 0), [(x - 1, 1), (x + 1, 1)]) + + assert R.dup_zz_i_factor(x**2 + 1) == (ZZ_I(1, 0), [(x - i, 1), (x + i, 1)]) + + assert R.dup_zz_i_factor(x**2 + 4) == \ + (ZZ_I(1, 0), [(x - 2*i, 1), (x + 2*i, 1)]) + + assert R.dup_zz_i_factor(x**2 + 2*x + 1) == \ + (ZZ_I(1, 0), [(x + 1, 2)]) + + assert R.dup_zz_i_factor(x**2 + 2*i*x - 1) == \ + (ZZ_I(1, 0), [(x + i, 2)]) + + f = 8192*x**2 + x*(22656 + 175232*i) - 921416 + 242313*i + + assert R.dup_zz_i_factor(f) == \ + (ZZ_I(0, 1), [((64 - 64*i)*x + (773 + 596*i), 2)]) + + +def test_dmp_zz_i_factor(): + R, x, y = ring("x, y", ZZ_I) + i = ZZ_I(0, 1) + + assert R.dmp_zz_i_factor(x**2 + 2*y**2) == \ + (ZZ_I(1, 0), [(x**2 + 2*y**2, 1)]) + + assert R.dmp_zz_i_factor(x**2 + y**2) == \ + (ZZ_I(1, 0), [(x - i*y, 1), (x + i*y, 1)]) + + assert R.dmp_zz_i_factor(4*x**2 + y**2) == \ + (ZZ_I(1, 0), [(2*x - i*y, 1), (2*x + i*y, 1)]) + + +def test_dup_ext_factor(): + R, x = ring("x", QQ.algebraic_field(I)) + def anp(element): + return ANP(element, [QQ(1), QQ(0), QQ(1)], QQ) + + assert R.dup_ext_factor(0) == (anp([]), []) + + f = anp([QQ(1)])*x + anp([QQ(1)]) + + assert R.dup_ext_factor(f) == (anp([QQ(1)]), [(f, 1)]) + + g = anp([QQ(2)])*x + anp([QQ(2)]) + + assert R.dup_ext_factor(g) == (anp([QQ(2)]), [(f, 1)]) + + f = anp([QQ(7)])*x**4 + anp([QQ(1, 1)]) + g = anp([QQ(1)])*x**4 + anp([QQ(1, 7)]) + + assert R.dup_ext_factor(f) == (anp([QQ(7)]), [(g, 1)]) + + f = anp([QQ(1)])*x**4 + anp([QQ(1)]) + + assert R.dup_ext_factor(f) == \ + (anp([QQ(1, 1)]), [(anp([QQ(1)])*x**2 + anp([QQ(-1), QQ(0)]), 1), + (anp([QQ(1)])*x**2 + anp([QQ( 1), QQ(0)]), 1)]) + + f = anp([QQ(4, 1)])*x**2 + anp([QQ(9, 1)]) + + assert R.dup_ext_factor(f) == \ + (anp([QQ(4, 1)]), [(anp([QQ(1, 1)])*x + anp([-QQ(3, 2), QQ(0, 1)]), 1), + (anp([QQ(1, 1)])*x + anp([ QQ(3, 2), QQ(0, 1)]), 1)]) + + f = anp([QQ(4, 1)])*x**4 + anp([QQ(8, 1)])*x**3 + anp([QQ(77, 1)])*x**2 + anp([QQ(18, 1)])*x + anp([QQ(153, 1)]) + + assert R.dup_ext_factor(f) == \ + (anp([QQ(4, 1)]), [(anp([QQ(1, 1)])*x + anp([-QQ(4, 1), QQ(1, 1)]), 1), + (anp([QQ(1, 1)])*x + anp([-QQ(3, 2), QQ(0, 1)]), 1), + (anp([QQ(1, 1)])*x + anp([ QQ(3, 2), QQ(0, 1)]), 1), + (anp([QQ(1, 1)])*x + anp([ QQ(4, 1), QQ(1, 1)]), 1)]) + + R, x = ring("x", QQ.algebraic_field(sqrt(2))) + def anp(element): + return ANP(element, [QQ(1), QQ(0), QQ(-2)], QQ) + + f = anp([QQ(1)])*x**4 + anp([QQ(1, 1)]) + + assert R.dup_ext_factor(f) == \ + (anp([QQ(1)]), [(anp([QQ(1)])*x**2 + anp([QQ(-1), QQ(0)])*x + anp([QQ(1)]), 1), + (anp([QQ(1)])*x**2 + anp([QQ( 1), QQ(0)])*x + anp([QQ(1)]), 1)]) + + f = anp([QQ(1, 1)])*x**2 + anp([QQ(2), QQ(0)])*x + anp([QQ(2, 1)]) + + assert R.dup_ext_factor(f) == \ + (anp([QQ(1, 1)]), [(anp([1])*x + anp([1, 0]), 2)]) + + assert R.dup_ext_factor(f**3) == \ + (anp([QQ(1, 1)]), [(anp([1])*x + anp([1, 0]), 6)]) + + f *= anp([QQ(2, 1)]) + + assert R.dup_ext_factor(f) == \ + (anp([QQ(2, 1)]), [(anp([1])*x + anp([1, 0]), 2)]) + + assert R.dup_ext_factor(f**3) == \ + (anp([QQ(8, 1)]), [(anp([1])*x + anp([1, 0]), 6)]) + + +def test_dmp_ext_factor(): + K = QQ.algebraic_field(sqrt(2)) + R, x,y = ring("x,y", K) + sqrt2 = K.unit + + def anp(x): + return ANP(x, [QQ(1), QQ(0), QQ(-2)], QQ) + + assert R.dmp_ext_factor(0) == (anp([]), []) + + f = anp([QQ(1)])*x + anp([QQ(1)]) + + assert R.dmp_ext_factor(f) == (anp([QQ(1)]), [(f, 1)]) + + g = anp([QQ(2)])*x + anp([QQ(2)]) + + assert R.dmp_ext_factor(g) == (anp([QQ(2)]), [(f, 1)]) + + f = anp([QQ(1)])*x**2 + anp([QQ(-2)])*y**2 + + assert R.dmp_ext_factor(f) == \ + (anp([QQ(1)]), [(anp([QQ(1)])*x + anp([QQ(-1), QQ(0)])*y, 1), + (anp([QQ(1)])*x + anp([QQ( 1), QQ(0)])*y, 1)]) + + f = anp([QQ(2)])*x**2 + anp([QQ(-4)])*y**2 + + assert R.dmp_ext_factor(f) == \ + (anp([QQ(2)]), [(anp([QQ(1)])*x + anp([QQ(-1), QQ(0)])*y, 1), + (anp([QQ(1)])*x + anp([QQ( 1), QQ(0)])*y, 1)]) + + f1 = y + 1 + f2 = y + sqrt2 + f3 = x**2 + x + 2 + 3*sqrt2 + f = f1**2 * f2**2 * f3**2 + assert R.dmp_ext_factor(f) == (K.one, [(f1, 2), (f2, 2), (f3, 2)]) + + +def test_dup_factor_list(): + R, x = ring("x", ZZ) + assert R.dup_factor_list(0) == (0, []) + assert R.dup_factor_list(7) == (7, []) + + R, x = ring("x", QQ) + assert R.dup_factor_list(0) == (0, []) + assert R.dup_factor_list(QQ(1, 7)) == (QQ(1, 7), []) + + R, x = ring("x", ZZ['t']) + assert R.dup_factor_list(0) == (0, []) + assert R.dup_factor_list(7) == (7, []) + + R, x = ring("x", QQ['t']) + assert R.dup_factor_list(0) == (0, []) + assert R.dup_factor_list(QQ(1, 7)) == (QQ(1, 7), []) + + R, x = ring("x", ZZ) + assert R.dup_factor_list_include(0) == [(0, 1)] + assert R.dup_factor_list_include(7) == [(7, 1)] + + assert R.dup_factor_list(x**2 + 2*x + 1) == (1, [(x + 1, 2)]) + assert R.dup_factor_list_include(x**2 + 2*x + 1) == [(x + 1, 2)] + # issue 8037 + assert R.dup_factor_list(6*x**2 - 5*x - 6) == (1, [(2*x - 3, 1), (3*x + 2, 1)]) + + R, x = ring("x", QQ) + assert R.dup_factor_list(QQ(1,2)*x**2 + x + QQ(1,2)) == (QQ(1, 2), [(x + 1, 2)]) + + R, x = ring("x", FF(2)) + assert R.dup_factor_list(x**2 + 1) == (1, [(x + 1, 2)]) + + R, x = ring("x", RR) + assert R.dup_factor_list(1.0*x**2 + 2.0*x + 1.0) == (1.0, [(1.0*x + 1.0, 2)]) + assert R.dup_factor_list(2.0*x**2 + 4.0*x + 2.0) == (2.0, [(1.0*x + 1.0, 2)]) + + f = 6.7225336055071*x**2 - 10.6463972754741*x - 0.33469524022264 + coeff, factors = R.dup_factor_list(f) + assert coeff == RR(10.6463972754741) + assert len(factors) == 1 + assert factors[0][0].max_norm() == RR(1.0) + assert factors[0][1] == 1 + + Rt, t = ring("t", ZZ) + R, x = ring("x", Rt) + + f = 4*t*x**2 + 4*t**2*x + + assert R.dup_factor_list(f) == \ + (4*t, [(x, 1), + (x + t, 1)]) + + Rt, t = ring("t", QQ) + R, x = ring("x", Rt) + + f = QQ(1, 2)*t*x**2 + QQ(1, 2)*t**2*x + + assert R.dup_factor_list(f) == \ + (QQ(1, 2)*t, [(x, 1), + (x + t, 1)]) + + R, x = ring("x", QQ.algebraic_field(I)) + def anp(element): + return ANP(element, [QQ(1), QQ(0), QQ(1)], QQ) + + f = anp([QQ(1, 1)])*x**4 + anp([QQ(2, 1)])*x**2 + + assert R.dup_factor_list(f) == \ + (anp([QQ(1, 1)]), [(anp([QQ(1, 1)])*x, 2), + (anp([QQ(1, 1)])*x**2 + anp([])*x + anp([QQ(2, 1)]), 1)]) + + R, x = ring("x", EX) + raises(DomainError, lambda: R.dup_factor_list(EX(sin(1)))) + + +def test_dmp_factor_list(): + R, x, y = ring("x,y", ZZ) + assert R.dmp_factor_list(0) == (ZZ(0), []) + assert R.dmp_factor_list(7) == (7, []) + + R, x, y = ring("x,y", QQ) + assert R.dmp_factor_list(0) == (QQ(0), []) + assert R.dmp_factor_list(QQ(1, 7)) == (QQ(1, 7), []) + + Rt, t = ring("t", ZZ) + R, x, y = ring("x,y", Rt) + assert R.dmp_factor_list(0) == (0, []) + assert R.dmp_factor_list(7) == (ZZ(7), []) + + Rt, t = ring("t", QQ) + R, x, y = ring("x,y", Rt) + assert R.dmp_factor_list(0) == (0, []) + assert R.dmp_factor_list(QQ(1, 7)) == (QQ(1, 7), []) + + R, x, y = ring("x,y", ZZ) + assert R.dmp_factor_list_include(0) == [(0, 1)] + assert R.dmp_factor_list_include(7) == [(7, 1)] + + R, X = xring("x:200", ZZ) + + f, g = X[0]**2 + 2*X[0] + 1, X[0] + 1 + assert R.dmp_factor_list(f) == (1, [(g, 2)]) + + f, g = X[-1]**2 + 2*X[-1] + 1, X[-1] + 1 + assert R.dmp_factor_list(f) == (1, [(g, 2)]) + + R, x = ring("x", ZZ) + assert R.dmp_factor_list(x**2 + 2*x + 1) == (1, [(x + 1, 2)]) + R, x = ring("x", QQ) + assert R.dmp_factor_list(QQ(1,2)*x**2 + x + QQ(1,2)) == (QQ(1,2), [(x + 1, 2)]) + + R, x, y = ring("x,y", ZZ) + assert R.dmp_factor_list(x**2 + 2*x + 1) == (1, [(x + 1, 2)]) + R, x, y = ring("x,y", QQ) + assert R.dmp_factor_list(QQ(1,2)*x**2 + x + QQ(1,2)) == (QQ(1,2), [(x + 1, 2)]) + + R, x, y = ring("x,y", ZZ) + f = 4*x**2*y + 4*x*y**2 + + assert R.dmp_factor_list(f) == \ + (4, [(y, 1), + (x, 1), + (x + y, 1)]) + + assert R.dmp_factor_list_include(f) == \ + [(4*y, 1), + (x, 1), + (x + y, 1)] + + R, x, y = ring("x,y", QQ) + f = QQ(1,2)*x**2*y + QQ(1,2)*x*y**2 + + assert R.dmp_factor_list(f) == \ + (QQ(1,2), [(y, 1), + (x, 1), + (x + y, 1)]) + + R, x, y = ring("x,y", RR) + f = 2.0*x**2 - 8.0*y**2 + + assert R.dmp_factor_list(f) == \ + (RR(8.0), [(0.5*x - y, 1), + (0.5*x + y, 1)]) + + f = 6.7225336055071*x**2*y**2 - 10.6463972754741*x*y - 0.33469524022264 + coeff, factors = R.dmp_factor_list(f) + assert coeff == RR(10.6463972754741) + assert len(factors) == 1 + assert factors[0][0].max_norm() == RR(1.0) + assert factors[0][1] == 1 + + Rt, t = ring("t", ZZ) + R, x, y = ring("x,y", Rt) + f = 4*t*x**2 + 4*t**2*x + + assert R.dmp_factor_list(f) == \ + (4*t, [(x, 1), + (x + t, 1)]) + + Rt, t = ring("t", QQ) + R, x, y = ring("x,y", Rt) + f = QQ(1, 2)*t*x**2 + QQ(1, 2)*t**2*x + + assert R.dmp_factor_list(f) == \ + (QQ(1, 2)*t, [(x, 1), + (x + t, 1)]) + + R, x, y = ring("x,y", FF(2)) + raises(NotImplementedError, lambda: R.dmp_factor_list(x**2 + y**2)) + + R, x, y = ring("x,y", EX) + raises(DomainError, lambda: R.dmp_factor_list(EX(sin(1)))) + + +def test_dup_irreducible_p(): + R, x = ring("x", ZZ) + assert R.dup_irreducible_p(x**2 + x + 1) is True + assert R.dup_irreducible_p(x**2 + 2*x + 1) is False + + +def test_dmp_irreducible_p(): + R, x, y = ring("x,y", ZZ) + assert R.dmp_irreducible_p(x**2 + x + 1) is True + assert R.dmp_irreducible_p(x**2 + 2*x + 1) is False diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_galoistools.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_galoistools.py new file mode 100644 index 0000000000000000000000000000000000000000..e512bdd865c300bb138cb40b4ff78f393b323c22 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_galoistools.py @@ -0,0 +1,875 @@ +from sympy.polys.galoistools import ( + gf_crt, gf_crt1, gf_crt2, gf_int, + gf_degree, gf_strip, gf_trunc, gf_normal, + gf_from_dict, gf_to_dict, + gf_from_int_poly, gf_to_int_poly, + gf_neg, gf_add_ground, gf_sub_ground, gf_mul_ground, + gf_add, gf_sub, gf_add_mul, gf_sub_mul, gf_mul, gf_sqr, + gf_div, gf_rem, gf_quo, gf_exquo, + gf_lshift, gf_rshift, gf_expand, + gf_pow, gf_pow_mod, + gf_gcdex, gf_gcd, gf_lcm, gf_cofactors, + gf_LC, gf_TC, gf_monic, + gf_eval, gf_multi_eval, + gf_compose, gf_compose_mod, + gf_trace_map, + gf_diff, + gf_irreducible, gf_irreducible_p, + gf_irred_p_ben_or, gf_irred_p_rabin, + gf_sqf_list, gf_sqf_part, gf_sqf_p, + gf_Qmatrix, gf_Qbasis, + gf_ddf_zassenhaus, gf_ddf_shoup, + gf_edf_zassenhaus, gf_edf_shoup, + gf_berlekamp, + gf_factor_sqf, gf_factor, + gf_value, linear_congruence, _csolve_prime_las_vegas, + csolve_prime, gf_csolve, gf_frobenius_map, gf_frobenius_monomial_base +) + +from sympy.polys.polyerrors import ( + ExactQuotientFailed, +) + +from sympy.polys import polyconfig as config + +from sympy.polys.domains import ZZ +from sympy.core.numbers import pi +from sympy.ntheory.generate import nextprime +from sympy.testing.pytest import raises + + +def test_gf_crt(): + U = [49, 76, 65] + M = [99, 97, 95] + + p = 912285 + u = 639985 + + assert gf_crt(U, M, ZZ) == u + + E = [9215, 9405, 9603] + S = [62, 24, 12] + + assert gf_crt1(M, ZZ) == (p, E, S) + assert gf_crt2(U, M, p, E, S, ZZ) == u + + +def test_gf_int(): + assert gf_int(0, 5) == 0 + assert gf_int(1, 5) == 1 + assert gf_int(2, 5) == 2 + assert gf_int(3, 5) == -2 + assert gf_int(4, 5) == -1 + assert gf_int(5, 5) == 0 + + +def test_gf_degree(): + assert gf_degree([]) == -1 + assert gf_degree([1]) == 0 + assert gf_degree([1, 0]) == 1 + assert gf_degree([1, 0, 0, 0, 1]) == 4 + + +def test_gf_strip(): + assert gf_strip([]) == [] + assert gf_strip([0]) == [] + assert gf_strip([0, 0, 0]) == [] + + assert gf_strip([1]) == [1] + assert gf_strip([0, 1]) == [1] + assert gf_strip([0, 0, 0, 1]) == [1] + + assert gf_strip([1, 2, 0]) == [1, 2, 0] + assert gf_strip([0, 1, 2, 0]) == [1, 2, 0] + assert gf_strip([0, 0, 0, 1, 2, 0]) == [1, 2, 0] + + +def test_gf_trunc(): + assert gf_trunc([], 11) == [] + assert gf_trunc([1], 11) == [1] + assert gf_trunc([22], 11) == [] + assert gf_trunc([12], 11) == [1] + + assert gf_trunc([11, 22, 17, 1, 0], 11) == [6, 1, 0] + assert gf_trunc([12, 23, 17, 1, 0], 11) == [1, 1, 6, 1, 0] + + +def test_gf_normal(): + assert gf_normal([11, 22, 17, 1, 0], 11, ZZ) == [6, 1, 0] + + +def test_gf_from_to_dict(): + f = {11: 12, 6: 2, 0: 25} + F = {11: 1, 6: 2, 0: 3} + g = [1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 3] + + assert gf_from_dict(f, 11, ZZ) == g + assert gf_to_dict(g, 11) == F + + f = {11: -5, 4: 0, 3: 1, 0: 12} + F = {11: -5, 3: 1, 0: 1} + g = [6, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1] + + assert gf_from_dict(f, 11, ZZ) == g + assert gf_to_dict(g, 11) == F + + assert gf_to_dict([10], 11, symmetric=True) == {0: -1} + assert gf_to_dict([10], 11, symmetric=False) == {0: 10} + + +def test_gf_from_to_int_poly(): + assert gf_from_int_poly([1, 0, 7, 2, 20], 5) == [1, 0, 2, 2, 0] + assert gf_to_int_poly([1, 0, 4, 2, 3], 5) == [1, 0, -1, 2, -2] + + assert gf_to_int_poly([10], 11, symmetric=True) == [-1] + assert gf_to_int_poly([10], 11, symmetric=False) == [10] + + +def test_gf_LC(): + assert gf_LC([], ZZ) == 0 + assert gf_LC([1], ZZ) == 1 + assert gf_LC([1, 2], ZZ) == 1 + + +def test_gf_TC(): + assert gf_TC([], ZZ) == 0 + assert gf_TC([1], ZZ) == 1 + assert gf_TC([1, 2], ZZ) == 2 + + +def test_gf_monic(): + assert gf_monic(ZZ.map([]), 11, ZZ) == (0, []) + + assert gf_monic(ZZ.map([1]), 11, ZZ) == (1, [1]) + assert gf_monic(ZZ.map([2]), 11, ZZ) == (2, [1]) + + assert gf_monic(ZZ.map([1, 2, 3, 4]), 11, ZZ) == (1, [1, 2, 3, 4]) + assert gf_monic(ZZ.map([2, 3, 4, 5]), 11, ZZ) == (2, [1, 7, 2, 8]) + + +def test_gf_arith(): + assert gf_neg([], 11, ZZ) == [] + assert gf_neg([1], 11, ZZ) == [10] + assert gf_neg([1, 2, 3], 11, ZZ) == [10, 9, 8] + + assert gf_add_ground([], 0, 11, ZZ) == [] + assert gf_sub_ground([], 0, 11, ZZ) == [] + + assert gf_add_ground([], 3, 11, ZZ) == [3] + assert gf_sub_ground([], 3, 11, ZZ) == [8] + + assert gf_add_ground([1], 3, 11, ZZ) == [4] + assert gf_sub_ground([1], 3, 11, ZZ) == [9] + + assert gf_add_ground([8], 3, 11, ZZ) == [] + assert gf_sub_ground([3], 3, 11, ZZ) == [] + + assert gf_add_ground([1, 2, 3], 3, 11, ZZ) == [1, 2, 6] + assert gf_sub_ground([1, 2, 3], 3, 11, ZZ) == [1, 2, 0] + + assert gf_mul_ground([], 0, 11, ZZ) == [] + assert gf_mul_ground([], 1, 11, ZZ) == [] + + assert gf_mul_ground([1], 0, 11, ZZ) == [] + assert gf_mul_ground([1], 1, 11, ZZ) == [1] + + assert gf_mul_ground([1, 2, 3], 0, 11, ZZ) == [] + assert gf_mul_ground([1, 2, 3], 1, 11, ZZ) == [1, 2, 3] + assert gf_mul_ground([1, 2, 3], 7, 11, ZZ) == [7, 3, 10] + + assert gf_add([], [], 11, ZZ) == [] + assert gf_add([1], [], 11, ZZ) == [1] + assert gf_add([], [1], 11, ZZ) == [1] + assert gf_add([1], [1], 11, ZZ) == [2] + assert gf_add([1], [2], 11, ZZ) == [3] + + assert gf_add([1, 2], [1], 11, ZZ) == [1, 3] + assert gf_add([1], [1, 2], 11, ZZ) == [1, 3] + + assert gf_add([1, 2, 3], [8, 9, 10], 11, ZZ) == [9, 0, 2] + + assert gf_sub([], [], 11, ZZ) == [] + assert gf_sub([1], [], 11, ZZ) == [1] + assert gf_sub([], [1], 11, ZZ) == [10] + assert gf_sub([1], [1], 11, ZZ) == [] + assert gf_sub([1], [2], 11, ZZ) == [10] + + assert gf_sub([1, 2], [1], 11, ZZ) == [1, 1] + assert gf_sub([1], [1, 2], 11, ZZ) == [10, 10] + + assert gf_sub([3, 2, 1], [8, 9, 10], 11, ZZ) == [6, 4, 2] + + assert gf_add_mul( + [1, 5, 6], [7, 3], [8, 0, 6, 1], 11, ZZ) == [1, 2, 10, 8, 9] + assert gf_sub_mul( + [1, 5, 6], [7, 3], [8, 0, 6, 1], 11, ZZ) == [10, 9, 3, 2, 3] + + assert gf_mul([], [], 11, ZZ) == [] + assert gf_mul([], [1], 11, ZZ) == [] + assert gf_mul([1], [], 11, ZZ) == [] + assert gf_mul([1], [1], 11, ZZ) == [1] + assert gf_mul([5], [7], 11, ZZ) == [2] + + assert gf_mul([3, 0, 0, 6, 1, 2], [4, 0, 1, 0], 11, ZZ) == [1, 0, + 3, 2, 4, 3, 1, 2, 0] + assert gf_mul([4, 0, 1, 0], [3, 0, 0, 6, 1, 2], 11, ZZ) == [1, 0, + 3, 2, 4, 3, 1, 2, 0] + + assert gf_mul([2, 0, 0, 1, 7], [2, 0, 0, 1, 7], 11, ZZ) == [4, 0, + 0, 4, 6, 0, 1, 3, 5] + + assert gf_sqr([], 11, ZZ) == [] + assert gf_sqr([2], 11, ZZ) == [4] + assert gf_sqr([1, 2], 11, ZZ) == [1, 4, 4] + + assert gf_sqr([2, 0, 0, 1, 7], 11, ZZ) == [4, 0, 0, 4, 6, 0, 1, 3, 5] + + +def test_gf_division(): + raises(ZeroDivisionError, lambda: gf_div([1, 2, 3], [], 11, ZZ)) + raises(ZeroDivisionError, lambda: gf_rem([1, 2, 3], [], 11, ZZ)) + raises(ZeroDivisionError, lambda: gf_quo([1, 2, 3], [], 11, ZZ)) + raises(ZeroDivisionError, lambda: gf_quo([1, 2, 3], [], 11, ZZ)) + + assert gf_div([1], [1, 2, 3], 7, ZZ) == ([], [1]) + assert gf_rem([1], [1, 2, 3], 7, ZZ) == [1] + assert gf_quo([1], [1, 2, 3], 7, ZZ) == [] + + f = ZZ.map([5, 4, 3, 2, 1, 0]) + g = ZZ.map([1, 2, 3]) + q = [5, 1, 0, 6] + r = [3, 3] + + assert gf_div(f, g, 7, ZZ) == (q, r) + assert gf_rem(f, g, 7, ZZ) == r + assert gf_quo(f, g, 7, ZZ) == q + + raises(ExactQuotientFailed, lambda: gf_exquo(f, g, 7, ZZ)) + + f = ZZ.map([5, 4, 3, 2, 1, 0]) + g = ZZ.map([1, 2, 3, 0]) + q = [5, 1, 0] + r = [6, 1, 0] + + assert gf_div(f, g, 7, ZZ) == (q, r) + assert gf_rem(f, g, 7, ZZ) == r + assert gf_quo(f, g, 7, ZZ) == q + + raises(ExactQuotientFailed, lambda: gf_exquo(f, g, 7, ZZ)) + + assert gf_quo(ZZ.map([1, 2, 1]), ZZ.map([1, 1]), 11, ZZ) == [1, 1] + + +def test_gf_shift(): + f = [1, 2, 3, 4, 5] + + assert gf_lshift([], 5, ZZ) == [] + assert gf_rshift([], 5, ZZ) == ([], []) + + assert gf_lshift(f, 1, ZZ) == [1, 2, 3, 4, 5, 0] + assert gf_lshift(f, 2, ZZ) == [1, 2, 3, 4, 5, 0, 0] + + assert gf_rshift(f, 0, ZZ) == (f, []) + assert gf_rshift(f, 1, ZZ) == ([1, 2, 3, 4], [5]) + assert gf_rshift(f, 3, ZZ) == ([1, 2], [3, 4, 5]) + assert gf_rshift(f, 5, ZZ) == ([], f) + + +def test_gf_expand(): + F = [([1, 1], 2), ([1, 2], 3)] + + assert gf_expand(F, 11, ZZ) == [1, 8, 3, 5, 6, 8] + assert gf_expand((4, F), 11, ZZ) == [4, 10, 1, 9, 2, 10] + + +def test_gf_powering(): + assert gf_pow([1, 0, 0, 1, 8], 0, 11, ZZ) == [1] + assert gf_pow([1, 0, 0, 1, 8], 1, 11, ZZ) == [1, 0, 0, 1, 8] + assert gf_pow([1, 0, 0, 1, 8], 2, 11, ZZ) == [1, 0, 0, 2, 5, 0, 1, 5, 9] + + assert gf_pow([1, 0, 0, 1, 8], 5, 11, ZZ) == \ + [1, 0, 0, 5, 7, 0, 10, 6, 2, 10, 9, 6, 10, 6, 6, 0, 5, 2, 5, 9, 10] + + assert gf_pow([1, 0, 0, 1, 8], 8, 11, ZZ) == \ + [1, 0, 0, 8, 9, 0, 6, 8, 10, 1, 2, 5, 10, 7, 7, 9, 1, 2, 0, 0, 6, 2, + 5, 2, 5, 7, 7, 9, 10, 10, 7, 5, 5] + + assert gf_pow([1, 0, 0, 1, 8], 45, 11, ZZ) == \ + [ 1, 0, 0, 1, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 4, 10, 0, 0, 0, 0, 0, 0, + 10, 0, 0, 10, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 6, 0, 0, 6, 4, 0, 0, 0, 0, 0, 0, 8, 0, 0, 8, 9, 0, 0, 0, 0, 0, 0, + 10, 0, 0, 10, 3, 0, 0, 0, 0, 0, 0, 4, 0, 0, 4, 10, 0, 0, 0, 0, 0, 0, + 8, 0, 0, 8, 9, 0, 0, 0, 0, 0, 0, 9, 0, 0, 9, 6, 0, 0, 0, 0, 0, 0, + 3, 0, 0, 3, 2, 0, 0, 0, 0, 0, 0, 10, 0, 0, 10, 3, 0, 0, 0, 0, 0, 0, + 10, 0, 0, 10, 3, 0, 0, 0, 0, 0, 0, 2, 0, 0, 2, 5, 0, 0, 0, 0, 0, 0, + 4, 0, 0, 4, 10] + + assert gf_pow_mod(ZZ.map([1, 0, 0, 1, 8]), 0, ZZ.map([2, 0, 7]), 11, ZZ) == [1] + assert gf_pow_mod(ZZ.map([1, 0, 0, 1, 8]), 1, ZZ.map([2, 0, 7]), 11, ZZ) == [1, 1] + assert gf_pow_mod(ZZ.map([1, 0, 0, 1, 8]), 2, ZZ.map([2, 0, 7]), 11, ZZ) == [2, 3] + assert gf_pow_mod(ZZ.map([1, 0, 0, 1, 8]), 5, ZZ.map([2, 0, 7]), 11, ZZ) == [7, 8] + assert gf_pow_mod(ZZ.map([1, 0, 0, 1, 8]), 8, ZZ.map([2, 0, 7]), 11, ZZ) == [1, 5] + assert gf_pow_mod(ZZ.map([1, 0, 0, 1, 8]), 45, ZZ.map([2, 0, 7]), 11, ZZ) == [5, 4] + + +def test_gf_gcdex(): + assert gf_gcdex(ZZ.map([]), ZZ.map([]), 11, ZZ) == ([1], [], []) + assert gf_gcdex(ZZ.map([2]), ZZ.map([]), 11, ZZ) == ([6], [], [1]) + assert gf_gcdex(ZZ.map([]), ZZ.map([2]), 11, ZZ) == ([], [6], [1]) + assert gf_gcdex(ZZ.map([2]), ZZ.map([2]), 11, ZZ) == ([], [6], [1]) + + assert gf_gcdex(ZZ.map([]), ZZ.map([3, 0]), 11, ZZ) == ([], [4], [1, 0]) + assert gf_gcdex(ZZ.map([3, 0]), ZZ.map([]), 11, ZZ) == ([4], [], [1, 0]) + + assert gf_gcdex(ZZ.map([3, 0]), ZZ.map([3, 0]), 11, ZZ) == ([], [4], [1, 0]) + + assert gf_gcdex(ZZ.map([1, 8, 7]), ZZ.map([1, 7, 1, 7]), 11, ZZ) == ([5, 6], [6], [1, 7]) + + +def test_gf_gcd(): + assert gf_gcd(ZZ.map([]), ZZ.map([]), 11, ZZ) == [] + assert gf_gcd(ZZ.map([2]), ZZ.map([]), 11, ZZ) == [1] + assert gf_gcd(ZZ.map([]), ZZ.map([2]), 11, ZZ) == [1] + assert gf_gcd(ZZ.map([2]), ZZ.map([2]), 11, ZZ) == [1] + + assert gf_gcd(ZZ.map([]), ZZ.map([1, 0]), 11, ZZ) == [1, 0] + assert gf_gcd(ZZ.map([1, 0]), ZZ.map([]), 11, ZZ) == [1, 0] + + assert gf_gcd(ZZ.map([3, 0]), ZZ.map([3, 0]), 11, ZZ) == [1, 0] + assert gf_gcd(ZZ.map([1, 8, 7]), ZZ.map([1, 7, 1, 7]), 11, ZZ) == [1, 7] + + +def test_gf_lcm(): + assert gf_lcm(ZZ.map([]), ZZ.map([]), 11, ZZ) == [] + assert gf_lcm(ZZ.map([2]), ZZ.map([]), 11, ZZ) == [] + assert gf_lcm(ZZ.map([]), ZZ.map([2]), 11, ZZ) == [] + assert gf_lcm(ZZ.map([2]), ZZ.map([2]), 11, ZZ) == [1] + + assert gf_lcm(ZZ.map([]), ZZ.map([1, 0]), 11, ZZ) == [] + assert gf_lcm(ZZ.map([1, 0]), ZZ.map([]), 11, ZZ) == [] + + assert gf_lcm(ZZ.map([3, 0]), ZZ.map([3, 0]), 11, ZZ) == [1, 0] + assert gf_lcm(ZZ.map([1, 8, 7]), ZZ.map([1, 7, 1, 7]), 11, ZZ) == [1, 8, 8, 8, 7] + + +def test_gf_cofactors(): + assert gf_cofactors(ZZ.map([]), ZZ.map([]), 11, ZZ) == ([], [], []) + assert gf_cofactors(ZZ.map([2]), ZZ.map([]), 11, ZZ) == ([1], [2], []) + assert gf_cofactors(ZZ.map([]), ZZ.map([2]), 11, ZZ) == ([1], [], [2]) + assert gf_cofactors(ZZ.map([2]), ZZ.map([2]), 11, ZZ) == ([1], [2], [2]) + + assert gf_cofactors(ZZ.map([]), ZZ.map([1, 0]), 11, ZZ) == ([1, 0], [], [1]) + assert gf_cofactors(ZZ.map([1, 0]), ZZ.map([]), 11, ZZ) == ([1, 0], [1], []) + + assert gf_cofactors(ZZ.map([3, 0]), ZZ.map([3, 0]), 11, ZZ) == ( + [1, 0], [3], [3]) + assert gf_cofactors(ZZ.map([1, 8, 7]), ZZ.map([1, 7, 1, 7]), 11, ZZ) == ( + ([1, 7], [1, 1], [1, 0, 1])) + + +def test_gf_diff(): + assert gf_diff([], 11, ZZ) == [] + assert gf_diff([7], 11, ZZ) == [] + + assert gf_diff([7, 3], 11, ZZ) == [7] + assert gf_diff([7, 3, 1], 11, ZZ) == [3, 3] + + assert gf_diff([1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1], 11, ZZ) == [] + + +def test_gf_eval(): + assert gf_eval([], 4, 11, ZZ) == 0 + assert gf_eval([], 27, 11, ZZ) == 0 + assert gf_eval([7], 4, 11, ZZ) == 7 + assert gf_eval([7], 27, 11, ZZ) == 7 + + assert gf_eval([1, 0, 3, 2, 4, 3, 1, 2, 0], 0, 11, ZZ) == 0 + assert gf_eval([1, 0, 3, 2, 4, 3, 1, 2, 0], 4, 11, ZZ) == 9 + assert gf_eval([1, 0, 3, 2, 4, 3, 1, 2, 0], 27, 11, ZZ) == 5 + + assert gf_eval([4, 0, 0, 4, 6, 0, 1, 3, 5], 0, 11, ZZ) == 5 + assert gf_eval([4, 0, 0, 4, 6, 0, 1, 3, 5], 4, 11, ZZ) == 3 + assert gf_eval([4, 0, 0, 4, 6, 0, 1, 3, 5], 27, 11, ZZ) == 9 + + assert gf_multi_eval([3, 2, 1], [0, 1, 2, 3], 11, ZZ) == [1, 6, 6, 1] + + +def test_gf_compose(): + assert gf_compose([], [1, 0], 11, ZZ) == [] + assert gf_compose_mod([], [1, 0], [1, 0], 11, ZZ) == [] + + assert gf_compose([1], [], 11, ZZ) == [1] + assert gf_compose([1, 0], [], 11, ZZ) == [] + assert gf_compose([1, 0], [1, 0], 11, ZZ) == [1, 0] + + f = ZZ.map([1, 1, 4, 9, 1]) + g = ZZ.map([1, 1, 1]) + h = ZZ.map([1, 0, 0, 2]) + + assert gf_compose(g, h, 11, ZZ) == [1, 0, 0, 5, 0, 0, 7] + assert gf_compose_mod(g, h, f, 11, ZZ) == [3, 9, 6, 10] + + +def test_gf_trace_map(): + f = ZZ.map([1, 1, 4, 9, 1]) + a = [1, 1, 1] + c = ZZ.map([1, 0]) + b = gf_pow_mod(c, 11, f, 11, ZZ) + + assert gf_trace_map(a, b, c, 0, f, 11, ZZ) == \ + ([1, 1, 1], [1, 1, 1]) + assert gf_trace_map(a, b, c, 1, f, 11, ZZ) == \ + ([5, 2, 10, 3], [5, 3, 0, 4]) + assert gf_trace_map(a, b, c, 2, f, 11, ZZ) == \ + ([5, 9, 5, 3], [10, 1, 5, 7]) + assert gf_trace_map(a, b, c, 3, f, 11, ZZ) == \ + ([1, 10, 6, 0], [7]) + assert gf_trace_map(a, b, c, 4, f, 11, ZZ) == \ + ([1, 1, 1], [1, 1, 8]) + assert gf_trace_map(a, b, c, 5, f, 11, ZZ) == \ + ([5, 2, 10, 3], [5, 3, 0, 0]) + assert gf_trace_map(a, b, c, 11, f, 11, ZZ) == \ + ([1, 10, 6, 0], [10]) + + +def test_gf_irreducible(): + assert gf_irreducible_p(gf_irreducible(1, 11, ZZ), 11, ZZ) is True + assert gf_irreducible_p(gf_irreducible(2, 11, ZZ), 11, ZZ) is True + assert gf_irreducible_p(gf_irreducible(3, 11, ZZ), 11, ZZ) is True + assert gf_irreducible_p(gf_irreducible(4, 11, ZZ), 11, ZZ) is True + assert gf_irreducible_p(gf_irreducible(5, 11, ZZ), 11, ZZ) is True + assert gf_irreducible_p(gf_irreducible(6, 11, ZZ), 11, ZZ) is True + assert gf_irreducible_p(gf_irreducible(7, 11, ZZ), 11, ZZ) is True + + +def test_gf_irreducible_p(): + assert gf_irred_p_ben_or(ZZ.map([7]), 11, ZZ) is True + assert gf_irred_p_ben_or(ZZ.map([7, 3]), 11, ZZ) is True + assert gf_irred_p_ben_or(ZZ.map([7, 3, 1]), 11, ZZ) is False + + assert gf_irred_p_rabin(ZZ.map([7]), 11, ZZ) is True + assert gf_irred_p_rabin(ZZ.map([7, 3]), 11, ZZ) is True + assert gf_irred_p_rabin(ZZ.map([7, 3, 1]), 11, ZZ) is False + + config.setup('GF_IRRED_METHOD', 'ben-or') + + assert gf_irreducible_p(ZZ.map([7]), 11, ZZ) is True + assert gf_irreducible_p(ZZ.map([7, 3]), 11, ZZ) is True + assert gf_irreducible_p(ZZ.map([7, 3, 1]), 11, ZZ) is False + + config.setup('GF_IRRED_METHOD', 'rabin') + + assert gf_irreducible_p(ZZ.map([7]), 11, ZZ) is True + assert gf_irreducible_p(ZZ.map([7, 3]), 11, ZZ) is True + assert gf_irreducible_p(ZZ.map([7, 3, 1]), 11, ZZ) is False + + config.setup('GF_IRRED_METHOD', 'other') + raises(KeyError, lambda: gf_irreducible_p([7], 11, ZZ)) + config.setup('GF_IRRED_METHOD') + + f = ZZ.map([1, 9, 9, 13, 16, 15, 6, 7, 7, 7, 10]) + g = ZZ.map([1, 7, 16, 7, 15, 13, 13, 11, 16, 10, 9]) + + h = gf_mul(f, g, 17, ZZ) + + assert gf_irred_p_ben_or(f, 17, ZZ) is True + assert gf_irred_p_ben_or(g, 17, ZZ) is True + + assert gf_irred_p_ben_or(h, 17, ZZ) is False + + assert gf_irred_p_rabin(f, 17, ZZ) is True + assert gf_irred_p_rabin(g, 17, ZZ) is True + + assert gf_irred_p_rabin(h, 17, ZZ) is False + + +def test_gf_squarefree(): + assert gf_sqf_list([], 11, ZZ) == (0, []) + assert gf_sqf_list([1], 11, ZZ) == (1, []) + assert gf_sqf_list([1, 1], 11, ZZ) == (1, [([1, 1], 1)]) + + assert gf_sqf_p([], 11, ZZ) is True + assert gf_sqf_p([1], 11, ZZ) is True + assert gf_sqf_p([1, 1], 11, ZZ) is True + + f = gf_from_dict({11: 1, 0: 1}, 11, ZZ) + + assert gf_sqf_p(f, 11, ZZ) is False + + assert gf_sqf_list(f, 11, ZZ) == \ + (1, [([1, 1], 11)]) + + f = [1, 5, 8, 4] + + assert gf_sqf_p(f, 11, ZZ) is False + + assert gf_sqf_list(f, 11, ZZ) == \ + (1, [([1, 1], 1), + ([1, 2], 2)]) + + assert gf_sqf_part(f, 11, ZZ) == [1, 3, 2] + + f = [1, 0, 0, 2, 0, 0, 2, 0, 0, 1, 0] + + assert gf_sqf_list(f, 3, ZZ) == \ + (1, [([1, 0], 1), + ([1, 1], 3), + ([1, 2], 6)]) + +def test_gf_frobenius_map(): + f = ZZ.map([2, 0, 1, 0, 2, 2, 0, 2, 2, 2]) + g = ZZ.map([1,1,0,2,0,1,0,2,0,1]) + p = 3 + b = gf_frobenius_monomial_base(g, p, ZZ) + h = gf_frobenius_map(f, g, b, p, ZZ) + h1 = gf_pow_mod(f, p, g, p, ZZ) + assert h == h1 + + +def test_gf_berlekamp(): + f = gf_from_int_poly([1, -3, 1, -3, -1, -3, 1], 11) + + Q = [[1, 0, 0, 0, 0, 0], + [3, 5, 8, 8, 6, 5], + [3, 6, 6, 1, 10, 0], + [9, 4, 10, 3, 7, 9], + [7, 8, 10, 0, 0, 8], + [8, 10, 7, 8, 10, 8]] + + V = [[1, 0, 0, 0, 0, 0], + [0, 1, 1, 1, 1, 0], + [0, 0, 7, 9, 0, 1]] + + assert gf_Qmatrix(f, 11, ZZ) == Q + assert gf_Qbasis(Q, 11, ZZ) == V + + assert gf_berlekamp(f, 11, ZZ) == \ + [[1, 1], [1, 5, 3], [1, 2, 3, 4]] + + f = ZZ.map([1, 0, 1, 0, 10, 10, 8, 2, 8]) + + Q = ZZ.map([[1, 0, 0, 0, 0, 0, 0, 0], + [2, 1, 7, 11, 10, 12, 5, 11], + [3, 6, 4, 3, 0, 4, 7, 2], + [4, 3, 6, 5, 1, 6, 2, 3], + [2, 11, 8, 8, 3, 1, 3, 11], + [6, 11, 8, 6, 2, 7, 10, 9], + [5, 11, 7, 10, 0, 11, 7, 12], + [3, 3, 12, 5, 0, 11, 9, 12]]) + + V = [[1, 0, 0, 0, 0, 0, 0, 0], + [0, 5, 5, 0, 9, 5, 1, 0], + [0, 9, 11, 9, 10, 12, 0, 1]] + + assert gf_Qmatrix(f, 13, ZZ) == Q + assert gf_Qbasis(Q, 13, ZZ) == V + + assert gf_berlekamp(f, 13, ZZ) == \ + [[1, 3], [1, 8, 4, 12], [1, 2, 3, 4, 6]] + + +def test_gf_ddf(): + f = gf_from_dict({15: ZZ(1), 0: ZZ(-1)}, 11, ZZ) + g = [([1, 0, 0, 0, 0, 10], 1), + ([1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1], 2)] + + assert gf_ddf_zassenhaus(f, 11, ZZ) == g + assert gf_ddf_shoup(f, 11, ZZ) == g + + f = gf_from_dict({63: ZZ(1), 0: ZZ(1)}, 2, ZZ) + g = [([1, 1], 1), + ([1, 1, 1], 2), + ([1, 1, 1, 1, 1, 1, 1], 3), + ([1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, + 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1], 6)] + + assert gf_ddf_zassenhaus(f, 2, ZZ) == g + assert gf_ddf_shoup(f, 2, ZZ) == g + + f = gf_from_dict({6: ZZ(1), 5: ZZ(-1), 4: ZZ(1), 3: ZZ(1), 1: ZZ(-1)}, 3, ZZ) + g = [([1, 1, 0], 1), + ([1, 1, 0, 1, 2], 2)] + + assert gf_ddf_zassenhaus(f, 3, ZZ) == g + assert gf_ddf_shoup(f, 3, ZZ) == g + + f = ZZ.map([1, 2, 5, 26, 677, 436, 791, 325, 456, 24, 577]) + g = [([1, 701], 1), + ([1, 110, 559, 532, 694, 151, 110, 70, 735, 122], 9)] + + assert gf_ddf_zassenhaus(f, 809, ZZ) == g + assert gf_ddf_shoup(f, 809, ZZ) == g + + p = ZZ(nextprime(int((2**15 * pi).evalf()))) + f = gf_from_dict({15: 1, 1: 1, 0: 1}, p, ZZ) + g = [([1, 22730, 68144], 2), + ([1, 64876, 83977, 10787, 12561, 68608, 52650, 88001, 84356], 4), + ([1, 15347, 95022, 84569, 94508, 92335], 5)] + + assert gf_ddf_zassenhaus(f, p, ZZ) == g + assert gf_ddf_shoup(f, p, ZZ) == g + + +def test_gf_edf(): + f = ZZ.map([1, 1, 0, 1, 2]) + g = ZZ.map([[1, 0, 1], [1, 1, 2]]) + + assert gf_edf_zassenhaus(f, 2, 3, ZZ) == g + assert gf_edf_shoup(f, 2, 3, ZZ) == g + + +def test_issue_23174(): + f = ZZ.map([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]) + g = ZZ.map([[1, 0, 0, 1, 1, 1, 0, 0, 1], [1, 1, 1, 0, 1, 0, 1, 1, 1]]) + + assert gf_edf_zassenhaus(f, 8, 2, ZZ) == g + + +def test_gf_factor(): + assert gf_factor([], 11, ZZ) == (0, []) + assert gf_factor([1], 11, ZZ) == (1, []) + assert gf_factor([1, 1], 11, ZZ) == (1, [([1, 1], 1)]) + + assert gf_factor_sqf([], 11, ZZ) == (0, []) + assert gf_factor_sqf([1], 11, ZZ) == (1, []) + assert gf_factor_sqf([1, 1], 11, ZZ) == (1, [[1, 1]]) + + config.setup('GF_FACTOR_METHOD', 'berlekamp') + + assert gf_factor_sqf([], 11, ZZ) == (0, []) + assert gf_factor_sqf([1], 11, ZZ) == (1, []) + assert gf_factor_sqf([1, 1], 11, ZZ) == (1, [[1, 1]]) + + config.setup('GF_FACTOR_METHOD', 'zassenhaus') + + assert gf_factor_sqf([], 11, ZZ) == (0, []) + assert gf_factor_sqf([1], 11, ZZ) == (1, []) + assert gf_factor_sqf([1, 1], 11, ZZ) == (1, [[1, 1]]) + + config.setup('GF_FACTOR_METHOD', 'shoup') + + assert gf_factor_sqf(ZZ.map([]), 11, ZZ) == (0, []) + assert gf_factor_sqf(ZZ.map([1]), 11, ZZ) == (1, []) + assert gf_factor_sqf(ZZ.map([1, 1]), 11, ZZ) == (1, [[1, 1]]) + + f, p = ZZ.map([1, 0, 0, 1, 0]), 2 + + g = (1, [([1, 0], 1), + ([1, 1], 1), + ([1, 1, 1], 1)]) + + config.setup('GF_FACTOR_METHOD', 'berlekamp') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'zassenhaus') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'shoup') + assert gf_factor(f, p, ZZ) == g + + g = (1, [[1, 0], + [1, 1], + [1, 1, 1]]) + + config.setup('GF_FACTOR_METHOD', 'berlekamp') + assert gf_factor_sqf(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'zassenhaus') + assert gf_factor_sqf(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'shoup') + assert gf_factor_sqf(f, p, ZZ) == g + + f, p = gf_from_int_poly([1, -3, 1, -3, -1, -3, 1], 11), 11 + + g = (1, [([1, 1], 1), + ([1, 5, 3], 1), + ([1, 2, 3, 4], 1)]) + + config.setup('GF_FACTOR_METHOD', 'berlekamp') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'zassenhaus') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'shoup') + assert gf_factor(f, p, ZZ) == g + + f, p = [1, 5, 8, 4], 11 + + g = (1, [([1, 1], 1), ([1, 2], 2)]) + + config.setup('GF_FACTOR_METHOD', 'berlekamp') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'zassenhaus') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'shoup') + assert gf_factor(f, p, ZZ) == g + + f, p = [1, 1, 10, 1, 0, 10, 10, 10, 0, 0], 11 + + g = (1, [([1, 0], 2), ([1, 9, 5], 1), ([1, 3, 0, 8, 5, 2], 1)]) + + config.setup('GF_FACTOR_METHOD', 'berlekamp') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'zassenhaus') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'shoup') + assert gf_factor(f, p, ZZ) == g + + f, p = gf_from_dict({32: 1, 0: 1}, 11, ZZ), 11 + + g = (1, [([1, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 10], 1), + ([1, 0, 0, 0, 0, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 10], 1)]) + + config.setup('GF_FACTOR_METHOD', 'berlekamp') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'zassenhaus') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'shoup') + assert gf_factor(f, p, ZZ) == g + + f, p = gf_from_dict({32: ZZ(8), 0: ZZ(5)}, 11, ZZ), 11 + + g = (8, [([1, 3], 1), + ([1, 8], 1), + ([1, 0, 9], 1), + ([1, 2, 2], 1), + ([1, 9, 2], 1), + ([1, 0, 5, 0, 7], 1), + ([1, 0, 6, 0, 7], 1), + ([1, 0, 0, 0, 1, 0, 0, 0, 6], 1), + ([1, 0, 0, 0, 10, 0, 0, 0, 6], 1)]) + + config.setup('GF_FACTOR_METHOD', 'berlekamp') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'zassenhaus') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'shoup') + assert gf_factor(f, p, ZZ) == g + + f, p = gf_from_dict({63: ZZ(8), 0: ZZ(5)}, 11, ZZ), 11 + + g = (8, [([1, 7], 1), + ([1, 4, 5], 1), + ([1, 6, 8, 2], 1), + ([1, 9, 9, 2], 1), + ([1, 0, 0, 9, 0, 0, 4], 1), + ([1, 2, 0, 8, 4, 6, 4], 1), + ([1, 2, 3, 8, 0, 6, 4], 1), + ([1, 2, 6, 0, 8, 4, 4], 1), + ([1, 3, 3, 1, 6, 8, 4], 1), + ([1, 5, 6, 0, 8, 6, 4], 1), + ([1, 6, 2, 7, 9, 8, 4], 1), + ([1, 10, 4, 7, 10, 7, 4], 1), + ([1, 10, 10, 1, 4, 9, 4], 1)]) + + config.setup('GF_FACTOR_METHOD', 'berlekamp') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'zassenhaus') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'shoup') + assert gf_factor(f, p, ZZ) == g + + # Gathen polynomials: x**n + x + 1 (mod p > 2**n * pi) + + p = ZZ(nextprime(int((2**15 * pi).evalf()))) + f = gf_from_dict({15: 1, 1: 1, 0: 1}, p, ZZ) + + assert gf_sqf_p(f, p, ZZ) is True + + g = (1, [([1, 22730, 68144], 1), + ([1, 81553, 77449, 86810, 4724], 1), + ([1, 86276, 56779, 14859, 31575], 1), + ([1, 15347, 95022, 84569, 94508, 92335], 1)]) + + config.setup('GF_FACTOR_METHOD', 'zassenhaus') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'shoup') + assert gf_factor(f, p, ZZ) == g + + g = (1, [[1, 22730, 68144], + [1, 81553, 77449, 86810, 4724], + [1, 86276, 56779, 14859, 31575], + [1, 15347, 95022, 84569, 94508, 92335]]) + + config.setup('GF_FACTOR_METHOD', 'zassenhaus') + assert gf_factor_sqf(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'shoup') + assert gf_factor_sqf(f, p, ZZ) == g + + # Shoup polynomials: f = a_0 x**n + a_1 x**(n-1) + ... + a_n + # (mod p > 2**(n-2) * pi), where a_n = a_{n-1}**2 + 1, a_0 = 1 + + p = ZZ(nextprime(int((2**4 * pi).evalf()))) + f = ZZ.map([1, 2, 5, 26, 41, 39, 38]) + + assert gf_sqf_p(f, p, ZZ) is True + + g = (1, [([1, 44, 26], 1), + ([1, 11, 25, 18, 30], 1)]) + + config.setup('GF_FACTOR_METHOD', 'zassenhaus') + assert gf_factor(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'shoup') + assert gf_factor(f, p, ZZ) == g + + g = (1, [[1, 44, 26], + [1, 11, 25, 18, 30]]) + + config.setup('GF_FACTOR_METHOD', 'zassenhaus') + assert gf_factor_sqf(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'shoup') + assert gf_factor_sqf(f, p, ZZ) == g + + config.setup('GF_FACTOR_METHOD', 'other') + raises(KeyError, lambda: gf_factor([1, 1], 11, ZZ)) + config.setup('GF_FACTOR_METHOD') + + +def test_gf_csolve(): + assert gf_value([1, 7, 2, 4], 11) == 2204 + + assert linear_congruence(4, 3, 5) == [2] + assert linear_congruence(0, 3, 5) == [] + assert linear_congruence(6, 1, 4) == [] + assert linear_congruence(0, 5, 5) == [0, 1, 2, 3, 4] + assert linear_congruence(3, 12, 15) == [4, 9, 14] + assert linear_congruence(6, 0, 18) == [0, 3, 6, 9, 12, 15] + # _csolve_prime_las_vegas + assert _csolve_prime_las_vegas([2, 3, 1], 5) == [2, 4] + assert _csolve_prime_las_vegas([2, 0, 1], 5) == [] + from sympy.ntheory import primerange + for p in primerange(2, 100): + # f = x**(p-1) - 1 + f = gf_sub_ground(gf_pow([1, 0], p - 1, p, ZZ), 1, p, ZZ) + assert _csolve_prime_las_vegas(f, p) == list(range(1, p)) + # with power = 1 + assert csolve_prime([1, 3, 2, 17], 7) == [3] + assert csolve_prime([1, 3, 1, 5], 5) == [0, 1] + assert csolve_prime([3, 6, 9, 3], 3) == [0, 1, 2] + # with power > 1 + assert csolve_prime( + [1, 1, 223], 3, 4) == [4, 13, 22, 31, 40, 49, 58, 67, 76] + assert csolve_prime([3, 5, 2, 25], 5, 3) == [16, 50, 99] + assert csolve_prime([3, 2, 2, 49], 7, 3) == [147, 190, 234] + + assert gf_csolve([1, 1, 7], 189) == [13, 49, 76, 112, 139, 175] + assert gf_csolve([1, 3, 4, 1, 30], 60) == [10, 30] + assert gf_csolve([1, 1, 7], 15) == [] diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_heuristicgcd.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_heuristicgcd.py new file mode 100644 index 0000000000000000000000000000000000000000..7ff6bd6ea4effbd49c5e942ea8925cfcca4ba162 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_heuristicgcd.py @@ -0,0 +1,152 @@ +from sympy.polys.rings import ring +from sympy.polys.domains import ZZ +from sympy.polys.heuristicgcd import heugcd + + +def test_heugcd_univariate_integers(): + R, x = ring("x", ZZ) + + f = x**4 + 8*x**3 + 21*x**2 + 22*x + 8 + g = x**3 + 6*x**2 + 11*x + 6 + + h = x**2 + 3*x + 2 + + cff = x**2 + 5*x + 4 + cfg = x + 3 + + assert heugcd(f, g) == (h, cff, cfg) + + f = x**4 - 4 + g = x**4 + 4*x**2 + 4 + + h = x**2 + 2 + + cff = x**2 - 2 + cfg = x**2 + 2 + + assert heugcd(f, g) == (h, cff, cfg) + + f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + + h = 1 + + cff = f + cfg = g + + assert heugcd(f, g) == (h, cff, cfg) + + f = - 352518131239247345597970242177235495263669787845475025293906825864749649589178600387510272*x**49 \ + + 46818041807522713962450042363465092040687472354933295397472942006618953623327997952*x**42 \ + + 378182690892293941192071663536490788434899030680411695933646320291525827756032*x**35 \ + + 112806468807371824947796775491032386836656074179286744191026149539708928*x**28 \ + - 12278371209708240950316872681744825481125965781519138077173235712*x**21 \ + + 289127344604779611146960547954288113529690984687482920704*x**14 \ + + 19007977035740498977629742919480623972236450681*x**7 \ + + 311973482284542371301330321821976049 + + g = 365431878023781158602430064717380211405897160759702125019136*x**21 \ + + 197599133478719444145775798221171663643171734081650688*x**14 \ + - 9504116979659010018253915765478924103928886144*x**7 \ + - 311973482284542371301330321821976049 + + # TODO: assert heugcd(f, f.diff(x))[0] == g + + f = 1317378933230047068160*x + 2945748836994210856960 + g = 120352542776360960*x + 269116466014453760 + + h = 120352542776360960*x + 269116466014453760 + cff = 10946 + cfg = 1 + + assert heugcd(f, g) == (h, cff, cfg) + +def test_heugcd_multivariate_integers(): + R, x, y = ring("x,y", ZZ) + + f, g = 2*x**2 + 4*x + 2, x + 1 + assert heugcd(f, g) == (x + 1, 2*x + 2, 1) + + f, g = x + 1, 2*x**2 + 4*x + 2 + assert heugcd(f, g) == (x + 1, 1, 2*x + 2) + + R, x, y, z, u = ring("x,y,z,u", ZZ) + + f, g = u**2 + 2*u + 1, 2*u + 2 + assert heugcd(f, g) == (u + 1, u + 1, 2) + + f, g = z**2*u**2 + 2*z**2*u + z**2 + z*u + z, u**2 + 2*u + 1 + h, cff, cfg = u + 1, z**2*u + z**2 + z, u + 1 + + assert heugcd(f, g) == (h, cff, cfg) + assert heugcd(g, f) == (h, cfg, cff) + + R, x, y, z = ring("x,y,z", ZZ) + + f, g, h = R.fateman_poly_F_1() + H, cff, cfg = heugcd(f, g) + + assert H == h and H*cff == f and H*cfg == g + + R, x, y, z, u, v = ring("x,y,z,u,v", ZZ) + + f, g, h = R.fateman_poly_F_1() + H, cff, cfg = heugcd(f, g) + + assert H == h and H*cff == f and H*cfg == g + + R, x, y, z, u, v, a, b = ring("x,y,z,u,v,a,b", ZZ) + + f, g, h = R.fateman_poly_F_1() + H, cff, cfg = heugcd(f, g) + + assert H == h and H*cff == f and H*cfg == g + + R, x, y, z, u, v, a, b, c, d = ring("x,y,z,u,v,a,b,c,d", ZZ) + + f, g, h = R.fateman_poly_F_1() + H, cff, cfg = heugcd(f, g) + + assert H == h and H*cff == f and H*cfg == g + + R, x, y, z = ring("x,y,z", ZZ) + + f, g, h = R.fateman_poly_F_2() + H, cff, cfg = heugcd(f, g) + + assert H == h and H*cff == f and H*cfg == g + + f, g, h = R.fateman_poly_F_3() + H, cff, cfg = heugcd(f, g) + + assert H == h and H*cff == f and H*cfg == g + + R, x, y, z, t = ring("x,y,z,t", ZZ) + + f, g, h = R.fateman_poly_F_3() + H, cff, cfg = heugcd(f, g) + + assert H == h and H*cff == f and H*cfg == g + + +def test_issue_10996(): + R, x, y, z = ring("x,y,z", ZZ) + + f = 12*x**6*y**7*z**3 - 3*x**4*y**9*z**3 + 12*x**3*y**5*z**4 + g = -48*x**7*y**8*z**3 + 12*x**5*y**10*z**3 - 48*x**5*y**7*z**2 + \ + 36*x**4*y**7*z - 48*x**4*y**6*z**4 + 12*x**3*y**9*z**2 - 48*x**3*y**4 \ + - 9*x**2*y**9*z - 48*x**2*y**5*z**3 + 12*x*y**6 + 36*x*y**5*z**2 - 48*y**2*z + + H, cff, cfg = heugcd(f, g) + + assert H == 12*x**3*y**4 - 3*x*y**6 + 12*y**2*z + assert H*cff == f and H*cfg == g + + +def test_issue_25793(): + R, x = ring("x", ZZ) + f = x - 4851 # failure starts for values more than 4850 + g = f*(2*x + 1) + H, cff, cfg = R.dup_zz_heu_gcd(f, g) + assert H == f + # needs a test for dmp, too, that fails in master before this change diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_hypothesis.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_hypothesis.py new file mode 100644 index 0000000000000000000000000000000000000000..78c2369179c3f0ea4d34b8a7868417506177e3c5 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_hypothesis.py @@ -0,0 +1,36 @@ +from hypothesis import given +from hypothesis import strategies as st +from sympy.abc import x +from sympy.polys.polytools import Poly + + +def polys(*, nonzero=False, domain="ZZ"): + # This is a simple strategy, but sufficient the tests below + elems = {"ZZ": st.integers(), "QQ": st.fractions()} + coeff_st = st.lists(elems[domain]) + if nonzero: + coeff_st = coeff_st.filter(any) + return st.builds(Poly, coeff_st, st.just(x), domain=st.just(domain)) + + +@given(f=polys(), g=polys(), r=polys()) +def test_gcd_hypothesis(f, g, r): + gcd_1 = f.gcd(g) + gcd_2 = g.gcd(f) + assert gcd_1 == gcd_2 + + # multiply by r + gcd_3 = g.gcd(f + r * g) + assert gcd_1 == gcd_3 + + +@given(f_z=polys(), g_z=polys(nonzero=True)) +def test_poly_hypothesis_integers(f_z, g_z): + remainder_z = f_z.rem(g_z) + assert g_z.degree() >= remainder_z.degree() or remainder_z.degree() == 0 + + +@given(f_q=polys(domain="QQ"), g_q=polys(nonzero=True, domain="QQ")) +def test_poly_hypothesis_rationals(f_q, g_q): + remainder_q = f_q.rem(g_q) + assert g_q.degree() >= remainder_q.degree() or remainder_q.degree() == 0 diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_injections.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_injections.py new file mode 100644 index 0000000000000000000000000000000000000000..63a5537c94f00e52a3899c97f0d78bfadab78a67 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_injections.py @@ -0,0 +1,39 @@ +"""Tests for functions that inject symbols into the global namespace. """ + +from sympy.polys.rings import vring +from sympy.polys.fields import vfield +from sympy.polys.domains import QQ + +def test_vring(): + ns = {'vring':vring, 'QQ':QQ} + exec('R = vring("r", QQ)', ns) + exec('assert r == R.gens[0]', ns) + + exec('R = vring("rb rbb rcc rzz _rx", QQ)', ns) + exec('assert rb == R.gens[0]', ns) + exec('assert rbb == R.gens[1]', ns) + exec('assert rcc == R.gens[2]', ns) + exec('assert rzz == R.gens[3]', ns) + exec('assert _rx == R.gens[4]', ns) + + exec('R = vring(["rd", "re", "rfg"], QQ)', ns) + exec('assert rd == R.gens[0]', ns) + exec('assert re == R.gens[1]', ns) + exec('assert rfg == R.gens[2]', ns) + +def test_vfield(): + ns = {'vfield':vfield, 'QQ':QQ} + exec('F = vfield("f", QQ)', ns) + exec('assert f == F.gens[0]', ns) + + exec('F = vfield("fb fbb fcc fzz _fx", QQ)', ns) + exec('assert fb == F.gens[0]', ns) + exec('assert fbb == F.gens[1]', ns) + exec('assert fcc == F.gens[2]', ns) + exec('assert fzz == F.gens[3]', ns) + exec('assert _fx == F.gens[4]', ns) + + exec('F = vfield(["fd", "fe", "ffg"], QQ)', ns) + exec('assert fd == F.gens[0]', ns) + exec('assert fe == F.gens[1]', ns) + exec('assert ffg == F.gens[2]', ns) diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_modulargcd.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_modulargcd.py new file mode 100644 index 0000000000000000000000000000000000000000..20510f59186524ed4008ade943fab526a9ae7194 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_modulargcd.py @@ -0,0 +1,325 @@ +from sympy.polys.rings import ring +from sympy.polys.domains import ZZ, QQ, AlgebraicField +from sympy.polys.modulargcd import ( + modgcd_univariate, + modgcd_bivariate, + _chinese_remainder_reconstruction_multivariate, + modgcd_multivariate, + _to_ZZ_poly, + _to_ANP_poly, + func_field_modgcd, + _func_field_modgcd_m) +from sympy.functions.elementary.miscellaneous import sqrt + + +def test_modgcd_univariate_integers(): + R, x = ring("x", ZZ) + + f, g = R.zero, R.zero + assert modgcd_univariate(f, g) == (0, 0, 0) + + f, g = R.zero, x + assert modgcd_univariate(f, g) == (x, 0, 1) + assert modgcd_univariate(g, f) == (x, 1, 0) + + f, g = R.zero, -x + assert modgcd_univariate(f, g) == (x, 0, -1) + assert modgcd_univariate(g, f) == (x, -1, 0) + + f, g = 2*x, R(2) + assert modgcd_univariate(f, g) == (2, x, 1) + + f, g = 2*x + 2, 6*x**2 - 6 + assert modgcd_univariate(f, g) == (2*x + 2, 1, 3*x - 3) + + f = x**4 + 8*x**3 + 21*x**2 + 22*x + 8 + g = x**3 + 6*x**2 + 11*x + 6 + + h = x**2 + 3*x + 2 + + cff = x**2 + 5*x + 4 + cfg = x + 3 + + assert modgcd_univariate(f, g) == (h, cff, cfg) + + f = x**4 - 4 + g = x**4 + 4*x**2 + 4 + + h = x**2 + 2 + + cff = x**2 - 2 + cfg = x**2 + 2 + + assert modgcd_univariate(f, g) == (h, cff, cfg) + + f = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + g = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + + h = 1 + + cff = f + cfg = g + + assert modgcd_univariate(f, g) == (h, cff, cfg) + + f = - 352518131239247345597970242177235495263669787845475025293906825864749649589178600387510272*x**49 \ + + 46818041807522713962450042363465092040687472354933295397472942006618953623327997952*x**42 \ + + 378182690892293941192071663536490788434899030680411695933646320291525827756032*x**35 \ + + 112806468807371824947796775491032386836656074179286744191026149539708928*x**28 \ + - 12278371209708240950316872681744825481125965781519138077173235712*x**21 \ + + 289127344604779611146960547954288113529690984687482920704*x**14 \ + + 19007977035740498977629742919480623972236450681*x**7 \ + + 311973482284542371301330321821976049 + + g = 365431878023781158602430064717380211405897160759702125019136*x**21 \ + + 197599133478719444145775798221171663643171734081650688*x**14 \ + - 9504116979659010018253915765478924103928886144*x**7 \ + - 311973482284542371301330321821976049 + + assert modgcd_univariate(f, f.diff(x))[0] == g + + f = 1317378933230047068160*x + 2945748836994210856960 + g = 120352542776360960*x + 269116466014453760 + + h = 120352542776360960*x + 269116466014453760 + cff = 10946 + cfg = 1 + + assert modgcd_univariate(f, g) == (h, cff, cfg) + + +def test_modgcd_bivariate_integers(): + R, x, y = ring("x,y", ZZ) + + f, g = R.zero, R.zero + assert modgcd_bivariate(f, g) == (0, 0, 0) + + f, g = 2*x, R(2) + assert modgcd_bivariate(f, g) == (2, x, 1) + + f, g = x + 2*y, x + y + assert modgcd_bivariate(f, g) == (1, f, g) + + f, g = x**2 + 2*x*y + y**2, x**3 + y**3 + assert modgcd_bivariate(f, g) == (x + y, x + y, x**2 - x*y + y**2) + + f, g = x*y**2 + 2*x*y + x, x*y**3 + x + assert modgcd_bivariate(f, g) == (x*y + x, y + 1, y**2 - y + 1) + + f, g = x**2*y**2 + x**2*y + 1, x*y**2 + x*y + 1 + assert modgcd_bivariate(f, g) == (1, f, g) + + f = 2*x*y**2 + 4*x*y + 2*x + y**2 + 2*y + 1 + g = 2*x*y**3 + 2*x + y**3 + 1 + assert modgcd_bivariate(f, g) == (2*x*y + 2*x + y + 1, y + 1, y**2 - y + 1) + + f, g = 2*x**2 + 4*x + 2, x + 1 + assert modgcd_bivariate(f, g) == (x + 1, 2*x + 2, 1) + + f, g = x + 1, 2*x**2 + 4*x + 2 + assert modgcd_bivariate(f, g) == (x + 1, 1, 2*x + 2) + + f = 2*x**2 + 4*x*y - 2*x - 4*y + g = x**2 + x - 2 + assert modgcd_bivariate(f, g) == (x - 1, 2*x + 4*y, x + 2) + + f = 2*x**2 + 2*x*y - 3*x - 3*y + g = 4*x*y - 2*x + 4*y**2 - 2*y + assert modgcd_bivariate(f, g) == (x + y, 2*x - 3, 4*y - 2) + + +def test_chinese_remainder(): + R, x, y = ring("x, y", ZZ) + p, q = 3, 5 + + hp = x**3*y - x**2 - 1 + hq = -x**3*y - 2*x*y**2 + 2 + + hpq = _chinese_remainder_reconstruction_multivariate(hp, hq, p, q) + + assert hpq.trunc_ground(p) == hp + assert hpq.trunc_ground(q) == hq + + T, z = ring("z", R) + p, q = 3, 7 + + hp = (x*y + 1)*z**2 + x + hq = (x**2 - 3*y)*z + 2 + + hpq = _chinese_remainder_reconstruction_multivariate(hp, hq, p, q) + + assert hpq.trunc_ground(p) == hp + assert hpq.trunc_ground(q) == hq + + +def test_modgcd_multivariate_integers(): + R, x, y = ring("x,y", ZZ) + + f, g = R.zero, R.zero + assert modgcd_multivariate(f, g) == (0, 0, 0) + + f, g = 2*x**2 + 4*x + 2, x + 1 + assert modgcd_multivariate(f, g) == (x + 1, 2*x + 2, 1) + + f, g = x + 1, 2*x**2 + 4*x + 2 + assert modgcd_multivariate(f, g) == (x + 1, 1, 2*x + 2) + + f = 2*x**2 + 2*x*y - 3*x - 3*y + g = 4*x*y - 2*x + 4*y**2 - 2*y + assert modgcd_multivariate(f, g) == (x + y, 2*x - 3, 4*y - 2) + + f, g = x*y**2 + 2*x*y + x, x*y**3 + x + assert modgcd_multivariate(f, g) == (x*y + x, y + 1, y**2 - y + 1) + + f, g = x**2*y**2 + x**2*y + 1, x*y**2 + x*y + 1 + assert modgcd_multivariate(f, g) == (1, f, g) + + f = x**4 + 8*x**3 + 21*x**2 + 22*x + 8 + g = x**3 + 6*x**2 + 11*x + 6 + + h = x**2 + 3*x + 2 + + cff = x**2 + 5*x + 4 + cfg = x + 3 + + assert modgcd_multivariate(f, g) == (h, cff, cfg) + + R, x, y, z, u = ring("x,y,z,u", ZZ) + + f, g = x + y + z, -x - y - z - u + assert modgcd_multivariate(f, g) == (1, f, g) + + f, g = u**2 + 2*u + 1, 2*u + 2 + assert modgcd_multivariate(f, g) == (u + 1, u + 1, 2) + + f, g = z**2*u**2 + 2*z**2*u + z**2 + z*u + z, u**2 + 2*u + 1 + h, cff, cfg = u + 1, z**2*u + z**2 + z, u + 1 + + assert modgcd_multivariate(f, g) == (h, cff, cfg) + assert modgcd_multivariate(g, f) == (h, cfg, cff) + + R, x, y, z = ring("x,y,z", ZZ) + + f, g = x - y*z, x - y*z + assert modgcd_multivariate(f, g) == (x - y*z, 1, 1) + + f, g, h = R.fateman_poly_F_1() + H, cff, cfg = modgcd_multivariate(f, g) + + assert H == h and H*cff == f and H*cfg == g + + R, x, y, z, u, v = ring("x,y,z,u,v", ZZ) + + f, g, h = R.fateman_poly_F_1() + H, cff, cfg = modgcd_multivariate(f, g) + + assert H == h and H*cff == f and H*cfg == g + + R, x, y, z, u, v, a, b = ring("x,y,z,u,v,a,b", ZZ) + + f, g, h = R.fateman_poly_F_1() + H, cff, cfg = modgcd_multivariate(f, g) + + assert H == h and H*cff == f and H*cfg == g + + R, x, y, z, u, v, a, b, c, d = ring("x,y,z,u,v,a,b,c,d", ZZ) + + f, g, h = R.fateman_poly_F_1() + H, cff, cfg = modgcd_multivariate(f, g) + + assert H == h and H*cff == f and H*cfg == g + + R, x, y, z = ring("x,y,z", ZZ) + + f, g, h = R.fateman_poly_F_2() + H, cff, cfg = modgcd_multivariate(f, g) + + assert H == h and H*cff == f and H*cfg == g + + f, g, h = R.fateman_poly_F_3() + H, cff, cfg = modgcd_multivariate(f, g) + + assert H == h and H*cff == f and H*cfg == g + + R, x, y, z, t = ring("x,y,z,t", ZZ) + + f, g, h = R.fateman_poly_F_3() + H, cff, cfg = modgcd_multivariate(f, g) + + assert H == h and H*cff == f and H*cfg == g + + +def test_to_ZZ_ANP_poly(): + A = AlgebraicField(QQ, sqrt(2)) + R, x = ring("x", A) + f = x*(sqrt(2) + 1) + + T, x_, z_ = ring("x_, z_", ZZ) + f_ = x_*z_ + x_ + + assert _to_ZZ_poly(f, T) == f_ + assert _to_ANP_poly(f_, R) == f + + R, x, t, s = ring("x, t, s", A) + f = x*t**2 + x*s + sqrt(2) + + D, t_, s_ = ring("t_, s_", ZZ) + T, x_, z_ = ring("x_, z_", D) + f_ = (t_**2 + s_)*x_ + z_ + + assert _to_ZZ_poly(f, T) == f_ + assert _to_ANP_poly(f_, R) == f + + +def test_modgcd_algebraic_field(): + A = AlgebraicField(QQ, sqrt(2)) + R, x = ring("x", A) + one = A.one + + f, g = 2*x, R(2) + assert func_field_modgcd(f, g) == (one, f, g) + + f, g = 2*x, R(sqrt(2)) + assert func_field_modgcd(f, g) == (one, f, g) + + f, g = 2*x + 2, 6*x**2 - 6 + assert func_field_modgcd(f, g) == (x + 1, R(2), 6*x - 6) + + R, x, y = ring("x, y", A) + + f, g = x + sqrt(2)*y, x + y + assert func_field_modgcd(f, g) == (one, f, g) + + f, g = x*y + sqrt(2)*y**2, R(sqrt(2))*y + assert func_field_modgcd(f, g) == (y, x + sqrt(2)*y, R(sqrt(2))) + + f, g = x**2 + 2*sqrt(2)*x*y + 2*y**2, x + sqrt(2)*y + assert func_field_modgcd(f, g) == (g, g, one) + + A = AlgebraicField(QQ, sqrt(2), sqrt(3)) + R, x, y, z = ring("x, y, z", A) + + h = x**2*y**7 + sqrt(6)/21*z + f, g = h*(27*y**3 + 1), h*(y + x) + assert func_field_modgcd(f, g) == (h, 27*y**3+1, y+x) + + h = x**13*y**3 + 1/2*x**10 + 1/sqrt(2) + f, g = h*(x + 1), h*sqrt(2)/sqrt(3) + assert func_field_modgcd(f, g) == (h, x + 1, R(sqrt(2)/sqrt(3))) + + A = AlgebraicField(QQ, sqrt(2)**(-1)*sqrt(3)) + R, x = ring("x", A) + + f, g = x + 1, x - 1 + assert func_field_modgcd(f, g) == (A.one, f, g) + + +# when func_field_modgcd supports function fields, this test can be changed +def test_modgcd_func_field(): + D, t = ring("t", ZZ) + R, x, z = ring("x, z", D) + + minpoly = (z**2*t**2 + z**2*t - 1).drop(0) + f, g = x + 1, x - 1 + + assert _func_field_modgcd_m(f, g, minpoly) == R.one diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_monomials.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_monomials.py new file mode 100644 index 0000000000000000000000000000000000000000..c5ed28ba0e8e3f8e9f85c543a4fffcaef855fff8 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_monomials.py @@ -0,0 +1,269 @@ +"""Tests for tools and arithmetics for monomials of distributed polynomials. """ + +from sympy.polys.monomials import ( + itermonomials, monomial_count, + monomial_mul, monomial_div, + monomial_gcd, monomial_lcm, + monomial_max, monomial_min, + monomial_divides, monomial_pow, + Monomial, +) + +from sympy.polys.polyerrors import ExactQuotientFailed + +from sympy.abc import a, b, c, x, y, z +from sympy.core import S, symbols +from sympy.testing.pytest import raises + +def test_monomials(): + + # total_degree tests + assert set(itermonomials([], 0)) == {S.One} + assert set(itermonomials([], 1)) == {S.One} + assert set(itermonomials([], 2)) == {S.One} + + assert set(itermonomials([], 0, 0)) == {S.One} + assert set(itermonomials([], 1, 0)) == {S.One} + assert set(itermonomials([], 2, 0)) == {S.One} + + raises(StopIteration, lambda: next(itermonomials([], 0, 1))) + raises(StopIteration, lambda: next(itermonomials([], 0, 2))) + raises(StopIteration, lambda: next(itermonomials([], 0, 3))) + + assert set(itermonomials([], 0, 1)) == set() + assert set(itermonomials([], 0, 2)) == set() + assert set(itermonomials([], 0, 3)) == set() + + raises(ValueError, lambda: set(itermonomials([], -1))) + raises(ValueError, lambda: set(itermonomials([x], -1))) + raises(ValueError, lambda: set(itermonomials([x, y], -1))) + + assert set(itermonomials([x], 0)) == {S.One} + assert set(itermonomials([x], 1)) == {S.One, x} + assert set(itermonomials([x], 2)) == {S.One, x, x**2} + assert set(itermonomials([x], 3)) == {S.One, x, x**2, x**3} + + assert set(itermonomials([x, y], 0)) == {S.One} + assert set(itermonomials([x, y], 1)) == {S.One, x, y} + assert set(itermonomials([x, y], 2)) == {S.One, x, y, x**2, y**2, x*y} + assert set(itermonomials([x, y], 3)) == \ + {S.One, x, y, x**2, x**3, y**2, y**3, x*y, x*y**2, y*x**2} + + i, j, k = symbols('i j k', commutative=False) + assert set(itermonomials([i, j, k], 0)) == {S.One} + assert set(itermonomials([i, j, k], 1)) == {S.One, i, j, k} + assert set(itermonomials([i, j, k], 2)) == \ + {S.One, i, j, k, i**2, j**2, k**2, i*j, i*k, j*i, j*k, k*i, k*j} + + assert set(itermonomials([i, j, k], 3)) == \ + {S.One, i, j, k, i**2, j**2, k**2, i*j, i*k, j*i, j*k, k*i, k*j, + i**3, j**3, k**3, + i**2 * j, i**2 * k, j * i**2, k * i**2, + j**2 * i, j**2 * k, i * j**2, k * j**2, + k**2 * i, k**2 * j, i * k**2, j * k**2, + i*j*i, i*k*i, j*i*j, j*k*j, k*i*k, k*j*k, + i*j*k, i*k*j, j*i*k, j*k*i, k*i*j, k*j*i, + } + + assert set(itermonomials([x, i, j], 0)) == {S.One} + assert set(itermonomials([x, i, j], 1)) == {S.One, x, i, j} + assert set(itermonomials([x, i, j], 2)) == {S.One, x, i, j, x*i, x*j, i*j, j*i, x**2, i**2, j**2} + assert set(itermonomials([x, i, j], 3)) == \ + {S.One, x, i, j, x*i, x*j, i*j, j*i, x**2, i**2, j**2, + x**3, i**3, j**3, + x**2 * i, x**2 * j, + x * i**2, j * i**2, i**2 * j, i*j*i, + x * j**2, i * j**2, j**2 * i, j*i*j, + x * i * j, x * j * i + } + + # degree_list tests + assert set(itermonomials([], [])) == {S.One} + + raises(ValueError, lambda: set(itermonomials([], [0]))) + raises(ValueError, lambda: set(itermonomials([], [1]))) + raises(ValueError, lambda: set(itermonomials([], [2]))) + + raises(ValueError, lambda: set(itermonomials([x], [1], []))) + raises(ValueError, lambda: set(itermonomials([x], [1, 2], []))) + raises(ValueError, lambda: set(itermonomials([x], [1, 2, 3], []))) + + raises(ValueError, lambda: set(itermonomials([x], [], [1]))) + raises(ValueError, lambda: set(itermonomials([x], [], [1, 2]))) + raises(ValueError, lambda: set(itermonomials([x], [], [1, 2, 3]))) + + raises(ValueError, lambda: set(itermonomials([x, y], [1, 2], [1, 2, 3]))) + raises(ValueError, lambda: set(itermonomials([x, y, z], [1, 2, 3], [0, 1]))) + + raises(ValueError, lambda: set(itermonomials([x], [1], [-1]))) + raises(ValueError, lambda: set(itermonomials([x, y], [1, 2], [1, -1]))) + + raises(ValueError, lambda: set(itermonomials([], [], 1))) + raises(ValueError, lambda: set(itermonomials([], [], 2))) + raises(ValueError, lambda: set(itermonomials([], [], 3))) + + raises(ValueError, lambda: set(itermonomials([x, y], [0, 1], [1, 2]))) + raises(ValueError, lambda: set(itermonomials([x, y, z], [0, 0, 3], [0, 1, 2]))) + + assert set(itermonomials([x], [0])) == {S.One} + assert set(itermonomials([x], [1])) == {S.One, x} + assert set(itermonomials([x], [2])) == {S.One, x, x**2} + assert set(itermonomials([x], [3])) == {S.One, x, x**2, x**3} + + assert set(itermonomials([x], [3], [1])) == {x, x**3, x**2} + assert set(itermonomials([x], [3], [2])) == {x**3, x**2} + + assert set(itermonomials([x, y], 3, 3)) == {x**3, x**2*y, x*y**2, y**3} + assert set(itermonomials([x, y], 3, 2)) == {x**2, x*y, y**2, x**3, x**2*y, x*y**2, y**3} + + assert set(itermonomials([x, y], [0, 0])) == {S.One} + assert set(itermonomials([x, y], [0, 1])) == {S.One, y} + assert set(itermonomials([x, y], [0, 2])) == {S.One, y, y**2} + assert set(itermonomials([x, y], [0, 2], [0, 1])) == {y, y**2} + assert set(itermonomials([x, y], [0, 2], [0, 2])) == {y**2} + + assert set(itermonomials([x, y], [1, 0])) == {S.One, x} + assert set(itermonomials([x, y], [1, 1])) == {S.One, x, y, x*y} + assert set(itermonomials([x, y], [1, 2])) == {S.One, x, y, x*y, y**2, x*y**2} + assert set(itermonomials([x, y], [1, 2], [1, 1])) == {x*y, x*y**2} + assert set(itermonomials([x, y], [1, 2], [1, 2])) == {x*y**2} + + assert set(itermonomials([x, y], [2, 0])) == {S.One, x, x**2} + assert set(itermonomials([x, y], [2, 1])) == {S.One, x, y, x*y, x**2, x**2*y} + assert set(itermonomials([x, y], [2, 2])) == \ + {S.One, y**2, x*y**2, x, x*y, x**2, x**2*y**2, y, x**2*y} + + i, j, k = symbols('i j k', commutative=False) + assert set(itermonomials([i, j, k], 2, 2)) == \ + {k*i, i**2, i*j, j*k, j*i, k**2, j**2, k*j, i*k} + assert set(itermonomials([i, j, k], 3, 2)) == \ + {j*k**2, i*k**2, k*i*j, k*i**2, k**2, j*k*j, k*j**2, i*k*i, i*j, + j**2*k, i**2*j, j*i*k, j**3, i**3, k*j*i, j*k*i, j*i, + k**2*j, j*i**2, k*j, k*j*k, i*j*i, j*i*j, i*j**2, j**2, + k*i*k, i**2, j*k, i*k, i*k*j, k**3, i**2*k, j**2*i, k**2*i, + i*j*k, k*i + } + assert set(itermonomials([i, j, k], [0, 0, 0])) == {S.One} + assert set(itermonomials([i, j, k], [0, 0, 1])) == {1, k} + assert set(itermonomials([i, j, k], [0, 1, 0])) == {1, j} + assert set(itermonomials([i, j, k], [1, 0, 0])) == {i, 1} + assert set(itermonomials([i, j, k], [0, 0, 2])) == {k**2, 1, k} + assert set(itermonomials([i, j, k], [0, 2, 0])) == {1, j, j**2} + assert set(itermonomials([i, j, k], [2, 0, 0])) == {i, 1, i**2} + assert set(itermonomials([i, j, k], [1, 1, 1])) == {1, k, j, j*k, i*k, i, i*j, i*j*k} + assert set(itermonomials([i, j, k], [2, 2, 2])) == \ + {1, k, i**2*k**2, j*k, j**2, i, i*k, j*k**2, i*j**2*k**2, + i**2*j, i**2*j**2, k**2, j**2*k, i*j**2*k, + j**2*k**2, i*j, i**2*k, i**2*j**2*k, j, i**2*j*k, + i*j**2, i*k**2, i*j*k, i**2*j**2*k**2, i*j*k**2, i**2, i**2*j*k**2 + } + + assert set(itermonomials([x, j, k], [0, 0, 0])) == {S.One} + assert set(itermonomials([x, j, k], [0, 0, 1])) == {1, k} + assert set(itermonomials([x, j, k], [0, 1, 0])) == {1, j} + assert set(itermonomials([x, j, k], [1, 0, 0])) == {x, 1} + assert set(itermonomials([x, j, k], [0, 0, 2])) == {k**2, 1, k} + assert set(itermonomials([x, j, k], [0, 2, 0])) == {1, j, j**2} + assert set(itermonomials([x, j, k], [2, 0, 0])) == {x, 1, x**2} + assert set(itermonomials([x, j, k], [1, 1, 1])) == {1, k, j, j*k, x*k, x, x*j, x*j*k} + assert set(itermonomials([x, j, k], [2, 2, 2])) == \ + {1, k, x**2*k**2, j*k, j**2, x, x*k, j*k**2, x*j**2*k**2, + x**2*j, x**2*j**2, k**2, j**2*k, x*j**2*k, + j**2*k**2, x*j, x**2*k, x**2*j**2*k, j, x**2*j*k, + x*j**2, x*k**2, x*j*k, x**2*j**2*k**2, x*j*k**2, x**2, x**2*j*k**2 + } + +def test_monomial_count(): + assert monomial_count(2, 2) == 6 + assert monomial_count(2, 3) == 10 + +def test_monomial_mul(): + assert monomial_mul((3, 4, 1), (1, 2, 0)) == (4, 6, 1) + +def test_monomial_div(): + assert monomial_div((3, 4, 1), (1, 2, 0)) == (2, 2, 1) + +def test_monomial_gcd(): + assert monomial_gcd((3, 4, 1), (1, 2, 0)) == (1, 2, 0) + +def test_monomial_lcm(): + assert monomial_lcm((3, 4, 1), (1, 2, 0)) == (3, 4, 1) + +def test_monomial_max(): + assert monomial_max((3, 4, 5), (0, 5, 1), (6, 3, 9)) == (6, 5, 9) + +def test_monomial_pow(): + assert monomial_pow((1, 2, 3), 3) == (3, 6, 9) + +def test_monomial_min(): + assert monomial_min((3, 4, 5), (0, 5, 1), (6, 3, 9)) == (0, 3, 1) + +def test_monomial_divides(): + assert monomial_divides((1, 2, 3), (4, 5, 6)) is True + assert monomial_divides((1, 2, 3), (0, 5, 6)) is False + +def test_Monomial(): + m = Monomial((3, 4, 1), (x, y, z)) + n = Monomial((1, 2, 0), (x, y, z)) + + assert m.as_expr() == x**3*y**4*z + assert n.as_expr() == x**1*y**2 + + assert m.as_expr(a, b, c) == a**3*b**4*c + assert n.as_expr(a, b, c) == a**1*b**2 + + assert m.exponents == (3, 4, 1) + assert m.gens == (x, y, z) + + assert n.exponents == (1, 2, 0) + assert n.gens == (x, y, z) + + assert m == (3, 4, 1) + assert n != (3, 4, 1) + assert m != (1, 2, 0) + assert n == (1, 2, 0) + assert (m == 1) is False + + assert m[0] == m[-3] == 3 + assert m[1] == m[-2] == 4 + assert m[2] == m[-1] == 1 + + assert n[0] == n[-3] == 1 + assert n[1] == n[-2] == 2 + assert n[2] == n[-1] == 0 + + assert m[:2] == (3, 4) + assert n[:2] == (1, 2) + + assert m*n == Monomial((4, 6, 1)) + assert m/n == Monomial((2, 2, 1)) + + assert m*(1, 2, 0) == Monomial((4, 6, 1)) + assert m/(1, 2, 0) == Monomial((2, 2, 1)) + + assert m.gcd(n) == Monomial((1, 2, 0)) + assert m.lcm(n) == Monomial((3, 4, 1)) + + assert m.gcd((1, 2, 0)) == Monomial((1, 2, 0)) + assert m.lcm((1, 2, 0)) == Monomial((3, 4, 1)) + + assert m**0 == Monomial((0, 0, 0)) + assert m**1 == m + assert m**2 == Monomial((6, 8, 2)) + assert m**3 == Monomial((9, 12, 3)) + _a = Monomial((0, 0, 0)) + for n in range(10): + assert _a == m**n + _a *= m + + raises(ExactQuotientFailed, lambda: m/Monomial((5, 2, 0))) + + mm = Monomial((1, 2, 3)) + raises(ValueError, lambda: mm.as_expr()) + assert str(mm) == 'Monomial((1, 2, 3))' + assert str(m) == 'x**3*y**4*z**1' + raises(NotImplementedError, lambda: m*1) + raises(NotImplementedError, lambda: m/1) + raises(ValueError, lambda: m**-1) + raises(TypeError, lambda: m.gcd(3)) + raises(TypeError, lambda: m.lcm(3)) diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_multivariate_resultants.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_multivariate_resultants.py new file mode 100644 index 0000000000000000000000000000000000000000..0799feb41fc875cf038723916a3efd62ff31b1b4 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_multivariate_resultants.py @@ -0,0 +1,294 @@ +"""Tests for Dixon's and Macaulay's classes. """ + +from sympy.matrices.dense import Matrix +from sympy.polys.polytools import factor +from sympy.core import symbols +from sympy.tensor.indexed import IndexedBase + +from sympy.polys.multivariate_resultants import (DixonResultant, + MacaulayResultant) + +c, d = symbols("a, b") +x, y = symbols("x, y") + +p = c * x + y +q = x + d * y + +dixon = DixonResultant(polynomials=[p, q], variables=[x, y]) +macaulay = MacaulayResultant(polynomials=[p, q], variables=[x, y]) + +def test_dixon_resultant_init(): + """Test init method of DixonResultant.""" + a = IndexedBase("alpha") + + assert dixon.polynomials == [p, q] + assert dixon.variables == [x, y] + assert dixon.n == 2 + assert dixon.m == 2 + assert dixon.dummy_variables == [a[0], a[1]] + +def test_get_dixon_polynomial_numerical(): + """Test Dixon's polynomial for a numerical example.""" + a = IndexedBase("alpha") + + p = x + y + q = x ** 2 + y **3 + h = x ** 2 + y + + dixon = DixonResultant([p, q, h], [x, y]) + polynomial = -x * y ** 2 * a[0] - x * y ** 2 * a[1] - x * y * a[0] \ + * a[1] - x * y * a[1] ** 2 - x * a[0] * a[1] ** 2 + x * a[0] - \ + y ** 2 * a[0] * a[1] + y ** 2 * a[1] - y * a[0] * a[1] ** 2 + y * \ + a[1] ** 2 + + assert dixon.get_dixon_polynomial().as_expr().expand() == polynomial + +def test_get_max_degrees(): + """Tests max degrees function.""" + + p = x + y + q = x ** 2 + y **3 + h = x ** 2 + y + + dixon = DixonResultant(polynomials=[p, q, h], variables=[x, y]) + dixon_polynomial = dixon.get_dixon_polynomial() + + assert dixon.get_max_degrees(dixon_polynomial) == [1, 2] + +def test_get_dixon_matrix(): + """Test Dixon's resultant for a numerical example.""" + + x, y = symbols('x, y') + + p = x + y + q = x ** 2 + y ** 3 + h = x ** 2 + y + + dixon = DixonResultant([p, q, h], [x, y]) + polynomial = dixon.get_dixon_polynomial() + + assert dixon.get_dixon_matrix(polynomial).det() == 0 + +def test_get_dixon_matrix_example_two(): + """Test Dixon's matrix for example from [Palancz08]_.""" + x, y, z = symbols('x, y, z') + + f = x ** 2 + y ** 2 - 1 + z * 0 + g = x ** 2 + z ** 2 - 1 + y * 0 + h = y ** 2 + z ** 2 - 1 + + example_two = DixonResultant([f, g, h], [y, z]) + poly = example_two.get_dixon_polynomial() + matrix = example_two.get_dixon_matrix(poly) + + expr = 1 - 8 * x ** 2 + 24 * x ** 4 - 32 * x ** 6 + 16 * x ** 8 + assert (matrix.det() - expr).expand() == 0 + +def test_KSY_precondition(): + """Tests precondition for KSY Resultant.""" + A, B, C = symbols('A, B, C') + + m1 = Matrix([[1, 2, 3], + [4, 5, 12], + [6, 7, 18]]) + + m2 = Matrix([[0, C**2], + [-2 * C, -C ** 2]]) + + m3 = Matrix([[1, 0], + [0, 1]]) + + m4 = Matrix([[A**2, 0, 1], + [A, 1, 1 / A]]) + + m5 = Matrix([[5, 1], + [2, B], + [0, 1], + [0, 0]]) + + assert dixon.KSY_precondition(m1) == False + assert dixon.KSY_precondition(m2) == True + assert dixon.KSY_precondition(m3) == True + assert dixon.KSY_precondition(m4) == False + assert dixon.KSY_precondition(m5) == True + +def test_delete_zero_rows_and_columns(): + """Tests method for deleting rows and columns containing only zeros.""" + A, B, C = symbols('A, B, C') + + m1 = Matrix([[0, 0], + [0, 0], + [1, 2]]) + + m2 = Matrix([[0, 1, 2], + [0, 3, 4], + [0, 5, 6]]) + + m3 = Matrix([[0, 0, 0, 0], + [0, 1, 2, 0], + [0, 3, 4, 0], + [0, 0, 0, 0]]) + + m4 = Matrix([[1, 0, 2], + [0, 0, 0], + [3, 0, 4]]) + + m5 = Matrix([[0, 0, 0, 1], + [0, 0, 0, 2], + [0, 0, 0, 3], + [0, 0, 0, 4]]) + + m6 = Matrix([[0, 0, A], + [B, 0, 0], + [0, 0, C]]) + + assert dixon.delete_zero_rows_and_columns(m1) == Matrix([[1, 2]]) + + assert dixon.delete_zero_rows_and_columns(m2) == Matrix([[1, 2], + [3, 4], + [5, 6]]) + + assert dixon.delete_zero_rows_and_columns(m3) == Matrix([[1, 2], + [3, 4]]) + + assert dixon.delete_zero_rows_and_columns(m4) == Matrix([[1, 2], + [3, 4]]) + + assert dixon.delete_zero_rows_and_columns(m5) == Matrix([[1], + [2], + [3], + [4]]) + + assert dixon.delete_zero_rows_and_columns(m6) == Matrix([[0, A], + [B, 0], + [0, C]]) + +def test_product_leading_entries(): + """Tests product of leading entries method.""" + A, B = symbols('A, B') + + m1 = Matrix([[1, 2, 3], + [0, 4, 5], + [0, 0, 6]]) + + m2 = Matrix([[0, 0, 1], + [2, 0, 3]]) + + m3 = Matrix([[0, 0, 0], + [1, 2, 3], + [0, 0, 0]]) + + m4 = Matrix([[0, 0, A], + [1, 2, 3], + [B, 0, 0]]) + + assert dixon.product_leading_entries(m1) == 24 + assert dixon.product_leading_entries(m2) == 2 + assert dixon.product_leading_entries(m3) == 1 + assert dixon.product_leading_entries(m4) == A * B + +def test_get_KSY_Dixon_resultant_example_one(): + """Tests the KSY Dixon resultant for example one""" + x, y, z = symbols('x, y, z') + + p = x * y * z + q = x**2 - z**2 + h = x + y + z + dixon = DixonResultant([p, q, h], [x, y]) + dixon_poly = dixon.get_dixon_polynomial() + dixon_matrix = dixon.get_dixon_matrix(dixon_poly) + D = dixon.get_KSY_Dixon_resultant(dixon_matrix) + + assert D == -z**3 + +def test_get_KSY_Dixon_resultant_example_two(): + """Tests the KSY Dixon resultant for example two""" + x, y, A = symbols('x, y, A') + + p = x * y + x * A + x - A**2 - A + y**2 + y + q = x**2 + x * A - x + x * y + y * A - y + h = x**2 + x * y + 2 * x - x * A - y * A - 2 * A + + dixon = DixonResultant([p, q, h], [x, y]) + dixon_poly = dixon.get_dixon_polynomial() + dixon_matrix = dixon.get_dixon_matrix(dixon_poly) + D = factor(dixon.get_KSY_Dixon_resultant(dixon_matrix)) + + assert D == -8*A*(A - 1)*(A + 2)*(2*A - 1)**2 + +def test_macaulay_resultant_init(): + """Test init method of MacaulayResultant.""" + + assert macaulay.polynomials == [p, q] + assert macaulay.variables == [x, y] + assert macaulay.n == 2 + assert macaulay.degrees == [1, 1] + assert macaulay.degree_m == 1 + assert macaulay.monomials_size == 2 + +def test_get_degree_m(): + assert macaulay._get_degree_m() == 1 + +def test_get_size(): + assert macaulay.get_size() == 2 + +def test_macaulay_example_one(): + """Tests the Macaulay for example from [Bruce97]_""" + + x, y, z = symbols('x, y, z') + a_1_1, a_1_2, a_1_3 = symbols('a_1_1, a_1_2, a_1_3') + a_2_2, a_2_3, a_3_3 = symbols('a_2_2, a_2_3, a_3_3') + b_1_1, b_1_2, b_1_3 = symbols('b_1_1, b_1_2, b_1_3') + b_2_2, b_2_3, b_3_3 = symbols('b_2_2, b_2_3, b_3_3') + c_1, c_2, c_3 = symbols('c_1, c_2, c_3') + + f_1 = a_1_1 * x ** 2 + a_1_2 * x * y + a_1_3 * x * z + \ + a_2_2 * y ** 2 + a_2_3 * y * z + a_3_3 * z ** 2 + f_2 = b_1_1 * x ** 2 + b_1_2 * x * y + b_1_3 * x * z + \ + b_2_2 * y ** 2 + b_2_3 * y * z + b_3_3 * z ** 2 + f_3 = c_1 * x + c_2 * y + c_3 * z + + mac = MacaulayResultant([f_1, f_2, f_3], [x, y, z]) + + assert mac.degrees == [2, 2, 1] + assert mac.degree_m == 3 + + assert mac.monomial_set == [x ** 3, x ** 2 * y, x ** 2 * z, + x * y ** 2, + x * y * z, x * z ** 2, y ** 3, + y ** 2 *z, y * z ** 2, z ** 3] + assert mac.monomials_size == 10 + assert mac.get_row_coefficients() == [[x, y, z], [x, y, z], + [x * y, x * z, y * z, z ** 2]] + + matrix = mac.get_matrix() + assert matrix.shape == (mac.monomials_size, mac.monomials_size) + assert mac.get_submatrix(matrix) == Matrix([[a_1_1, a_2_2], + [b_1_1, b_2_2]]) + +def test_macaulay_example_two(): + """Tests the Macaulay formulation for example from [Stiller96]_.""" + + x, y, z = symbols('x, y, z') + a_0, a_1, a_2 = symbols('a_0, a_1, a_2') + b_0, b_1, b_2 = symbols('b_0, b_1, b_2') + c_0, c_1, c_2, c_3, c_4 = symbols('c_0, c_1, c_2, c_3, c_4') + + f = a_0 * y - a_1 * x + a_2 * z + g = b_1 * x ** 2 + b_0 * y ** 2 - b_2 * z ** 2 + h = c_0 * y - c_1 * x ** 3 + c_2 * x ** 2 * z - c_3 * x * z ** 2 + \ + c_4 * z ** 3 + + mac = MacaulayResultant([f, g, h], [x, y, z]) + + assert mac.degrees == [1, 2, 3] + assert mac.degree_m == 4 + assert mac.monomials_size == 15 + assert len(mac.get_row_coefficients()) == mac.n + + matrix = mac.get_matrix() + assert matrix.shape == (mac.monomials_size, mac.monomials_size) + assert mac.get_submatrix(matrix) == Matrix([[-a_1, a_0, a_2, 0], + [0, -a_1, 0, 0], + [0, 0, -a_1, 0], + [0, 0, 0, -a_1]]) diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_orderings.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_orderings.py new file mode 100644 index 0000000000000000000000000000000000000000..d61d4887754c9d9f49905c2e131d253a45cf2ffd --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_orderings.py @@ -0,0 +1,124 @@ +"""Tests of monomial orderings. """ + +from sympy.polys.orderings import ( + monomial_key, lex, grlex, grevlex, ilex, igrlex, + LexOrder, InverseOrder, ProductOrder, build_product_order, +) + +from sympy.abc import x, y, z, t +from sympy.core import S +from sympy.testing.pytest import raises + +def test_lex_order(): + assert lex((1, 2, 3)) == (1, 2, 3) + assert str(lex) == 'lex' + + assert lex((1, 2, 3)) == lex((1, 2, 3)) + + assert lex((2, 2, 3)) > lex((1, 2, 3)) + assert lex((1, 3, 3)) > lex((1, 2, 3)) + assert lex((1, 2, 4)) > lex((1, 2, 3)) + + assert lex((0, 2, 3)) < lex((1, 2, 3)) + assert lex((1, 1, 3)) < lex((1, 2, 3)) + assert lex((1, 2, 2)) < lex((1, 2, 3)) + + assert lex.is_global is True + assert lex == LexOrder() + assert lex != grlex + +def test_grlex_order(): + assert grlex((1, 2, 3)) == (6, (1, 2, 3)) + assert str(grlex) == 'grlex' + + assert grlex((1, 2, 3)) == grlex((1, 2, 3)) + + assert grlex((2, 2, 3)) > grlex((1, 2, 3)) + assert grlex((1, 3, 3)) > grlex((1, 2, 3)) + assert grlex((1, 2, 4)) > grlex((1, 2, 3)) + + assert grlex((0, 2, 3)) < grlex((1, 2, 3)) + assert grlex((1, 1, 3)) < grlex((1, 2, 3)) + assert grlex((1, 2, 2)) < grlex((1, 2, 3)) + + assert grlex((2, 2, 3)) > grlex((1, 2, 4)) + assert grlex((1, 3, 3)) > grlex((1, 2, 4)) + + assert grlex((0, 2, 3)) < grlex((1, 2, 2)) + assert grlex((1, 1, 3)) < grlex((1, 2, 2)) + + assert grlex((0, 1, 1)) > grlex((0, 0, 2)) + assert grlex((0, 3, 1)) < grlex((2, 2, 1)) + + assert grlex.is_global is True + +def test_grevlex_order(): + assert grevlex((1, 2, 3)) == (6, (-3, -2, -1)) + assert str(grevlex) == 'grevlex' + + assert grevlex((1, 2, 3)) == grevlex((1, 2, 3)) + + assert grevlex((2, 2, 3)) > grevlex((1, 2, 3)) + assert grevlex((1, 3, 3)) > grevlex((1, 2, 3)) + assert grevlex((1, 2, 4)) > grevlex((1, 2, 3)) + + assert grevlex((0, 2, 3)) < grevlex((1, 2, 3)) + assert grevlex((1, 1, 3)) < grevlex((1, 2, 3)) + assert grevlex((1, 2, 2)) < grevlex((1, 2, 3)) + + assert grevlex((2, 2, 3)) > grevlex((1, 2, 4)) + assert grevlex((1, 3, 3)) > grevlex((1, 2, 4)) + + assert grevlex((0, 2, 3)) < grevlex((1, 2, 2)) + assert grevlex((1, 1, 3)) < grevlex((1, 2, 2)) + + assert grevlex((0, 1, 1)) > grevlex((0, 0, 2)) + assert grevlex((0, 3, 1)) < grevlex((2, 2, 1)) + + assert grevlex.is_global is True + +def test_InverseOrder(): + ilex = InverseOrder(lex) + igrlex = InverseOrder(grlex) + + assert ilex((1, 2, 3)) > ilex((2, 0, 3)) + assert igrlex((1, 2, 3)) < igrlex((0, 2, 3)) + assert str(ilex) == "ilex" + assert str(igrlex) == "igrlex" + assert ilex.is_global is False + assert igrlex.is_global is False + assert ilex != igrlex + assert ilex == InverseOrder(LexOrder()) + +def test_ProductOrder(): + P = ProductOrder((grlex, lambda m: m[:2]), (grlex, lambda m: m[2:])) + assert P((1, 3, 3, 4, 5)) > P((2, 1, 5, 5, 5)) + assert str(P) == "ProductOrder(grlex, grlex)" + assert P.is_global is True + assert ProductOrder((grlex, None), (ilex, None)).is_global is None + assert ProductOrder((igrlex, None), (ilex, None)).is_global is False + +def test_monomial_key(): + assert monomial_key() == lex + + assert monomial_key('lex') == lex + assert monomial_key('grlex') == grlex + assert monomial_key('grevlex') == grevlex + + raises(ValueError, lambda: monomial_key('foo')) + raises(ValueError, lambda: monomial_key(1)) + + M = [x, x**2*z**2, x*y, x**2, S.One, y**2, x**3, y, z, x*y**2*z, x**2*y**2] + assert sorted(M, key=monomial_key('lex', [z, y, x])) == \ + [S.One, x, x**2, x**3, y, x*y, y**2, x**2*y**2, z, x*y**2*z, x**2*z**2] + assert sorted(M, key=monomial_key('grlex', [z, y, x])) == \ + [S.One, x, y, z, x**2, x*y, y**2, x**3, x**2*y**2, x*y**2*z, x**2*z**2] + assert sorted(M, key=monomial_key('grevlex', [z, y, x])) == \ + [S.One, x, y, z, x**2, x*y, y**2, x**3, x**2*y**2, x**2*z**2, x*y**2*z] + +def test_build_product_order(): + assert build_product_order((("grlex", x, y), ("grlex", z, t)), [x, y, z, t])((4, 5, 6, 7)) == \ + ((9, (4, 5)), (13, (6, 7))) + + assert build_product_order((("grlex", x, y), ("grlex", z, t)), [x, y, z, t]) == \ + build_product_order((("grlex", x, y), ("grlex", z, t)), [x, y, z, t]) diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_orthopolys.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_orthopolys.py new file mode 100644 index 0000000000000000000000000000000000000000..e81fbe75aa6285d229ba817026f44b23b76abd6a --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_orthopolys.py @@ -0,0 +1,175 @@ +"""Tests for efficient functions for generating orthogonal polynomials. """ + +from sympy.core.numbers import Rational as Q +from sympy.core.singleton import S +from sympy.core.symbol import symbols +from sympy.polys.polytools import Poly +from sympy.testing.pytest import raises + +from sympy.polys.orthopolys import ( + jacobi_poly, + gegenbauer_poly, + chebyshevt_poly, + chebyshevu_poly, + hermite_poly, + hermite_prob_poly, + legendre_poly, + laguerre_poly, + spherical_bessel_fn, +) + +from sympy.abc import x, a, b + + +def test_jacobi_poly(): + raises(ValueError, lambda: jacobi_poly(-1, a, b, x)) + + assert jacobi_poly(1, a, b, x, polys=True) == Poly( + (a/2 + b/2 + 1)*x + a/2 - b/2, x, domain='ZZ(a,b)') + + assert jacobi_poly(0, a, b, x) == 1 + assert jacobi_poly(1, a, b, x) == a/2 - b/2 + x*(a/2 + b/2 + 1) + assert jacobi_poly(2, a, b, x) == (a**2/8 - a*b/4 - a/8 + b**2/8 - b/8 + + x**2*(a**2/8 + a*b/4 + a*Q(7, 8) + b**2/8 + + b*Q(7, 8) + Q(3, 2)) + x*(a**2/4 + + a*Q(3, 4) - b**2/4 - b*Q(3, 4)) - S.Half) + + assert jacobi_poly(1, a, b, polys=True) == Poly( + (a/2 + b/2 + 1)*x + a/2 - b/2, x, domain='ZZ(a,b)') + + +def test_gegenbauer_poly(): + raises(ValueError, lambda: gegenbauer_poly(-1, a, x)) + + assert gegenbauer_poly( + 1, a, x, polys=True) == Poly(2*a*x, x, domain='ZZ(a)') + + assert gegenbauer_poly(0, a, x) == 1 + assert gegenbauer_poly(1, a, x) == 2*a*x + assert gegenbauer_poly(2, a, x) == -a + x**2*(2*a**2 + 2*a) + assert gegenbauer_poly( + 3, a, x) == x**3*(4*a**3/3 + 4*a**2 + a*Q(8, 3)) + x*(-2*a**2 - 2*a) + + assert gegenbauer_poly(1, S.Half).dummy_eq(x) + assert gegenbauer_poly(1, a, polys=True) == Poly(2*a*x, x, domain='ZZ(a)') + + +def test_chebyshevt_poly(): + raises(ValueError, lambda: chebyshevt_poly(-1, x)) + + assert chebyshevt_poly(1, x, polys=True) == Poly(x) + + assert chebyshevt_poly(0, x) == 1 + assert chebyshevt_poly(1, x) == x + assert chebyshevt_poly(2, x) == 2*x**2 - 1 + assert chebyshevt_poly(3, x) == 4*x**3 - 3*x + assert chebyshevt_poly(4, x) == 8*x**4 - 8*x**2 + 1 + assert chebyshevt_poly(5, x) == 16*x**5 - 20*x**3 + 5*x + assert chebyshevt_poly(6, x) == 32*x**6 - 48*x**4 + 18*x**2 - 1 + assert chebyshevt_poly(75, x) == (2*chebyshevt_poly(37, x)*chebyshevt_poly(38, x) - x).expand() + assert chebyshevt_poly(100, x) == (2*chebyshevt_poly(50, x)**2 - 1).expand() + + assert chebyshevt_poly(1).dummy_eq(x) + assert chebyshevt_poly(1, polys=True) == Poly(x) + + +def test_chebyshevu_poly(): + raises(ValueError, lambda: chebyshevu_poly(-1, x)) + + assert chebyshevu_poly(1, x, polys=True) == Poly(2*x) + + assert chebyshevu_poly(0, x) == 1 + assert chebyshevu_poly(1, x) == 2*x + assert chebyshevu_poly(2, x) == 4*x**2 - 1 + assert chebyshevu_poly(3, x) == 8*x**3 - 4*x + assert chebyshevu_poly(4, x) == 16*x**4 - 12*x**2 + 1 + assert chebyshevu_poly(5, x) == 32*x**5 - 32*x**3 + 6*x + assert chebyshevu_poly(6, x) == 64*x**6 - 80*x**4 + 24*x**2 - 1 + + assert chebyshevu_poly(1).dummy_eq(2*x) + assert chebyshevu_poly(1, polys=True) == Poly(2*x) + + +def test_hermite_poly(): + raises(ValueError, lambda: hermite_poly(-1, x)) + + assert hermite_poly(1, x, polys=True) == Poly(2*x) + + assert hermite_poly(0, x) == 1 + assert hermite_poly(1, x) == 2*x + assert hermite_poly(2, x) == 4*x**2 - 2 + assert hermite_poly(3, x) == 8*x**3 - 12*x + assert hermite_poly(4, x) == 16*x**4 - 48*x**2 + 12 + assert hermite_poly(5, x) == 32*x**5 - 160*x**3 + 120*x + assert hermite_poly(6, x) == 64*x**6 - 480*x**4 + 720*x**2 - 120 + + assert hermite_poly(1).dummy_eq(2*x) + assert hermite_poly(1, polys=True) == Poly(2*x) + + +def test_hermite_prob_poly(): + raises(ValueError, lambda: hermite_prob_poly(-1, x)) + + assert hermite_prob_poly(1, x, polys=True) == Poly(x) + + assert hermite_prob_poly(0, x) == 1 + assert hermite_prob_poly(1, x) == x + assert hermite_prob_poly(2, x) == x**2 - 1 + assert hermite_prob_poly(3, x) == x**3 - 3*x + assert hermite_prob_poly(4, x) == x**4 - 6*x**2 + 3 + assert hermite_prob_poly(5, x) == x**5 - 10*x**3 + 15*x + assert hermite_prob_poly(6, x) == x**6 - 15*x**4 + 45*x**2 - 15 + + assert hermite_prob_poly(1).dummy_eq(x) + assert hermite_prob_poly(1, polys=True) == Poly(x) + + +def test_legendre_poly(): + raises(ValueError, lambda: legendre_poly(-1, x)) + + assert legendre_poly(1, x, polys=True) == Poly(x, domain='QQ') + + assert legendre_poly(0, x) == 1 + assert legendre_poly(1, x) == x + assert legendre_poly(2, x) == Q(3, 2)*x**2 - Q(1, 2) + assert legendre_poly(3, x) == Q(5, 2)*x**3 - Q(3, 2)*x + assert legendre_poly(4, x) == Q(35, 8)*x**4 - Q(30, 8)*x**2 + Q(3, 8) + assert legendre_poly(5, x) == Q(63, 8)*x**5 - Q(70, 8)*x**3 + Q(15, 8)*x + assert legendre_poly(6, x) == Q( + 231, 16)*x**6 - Q(315, 16)*x**4 + Q(105, 16)*x**2 - Q(5, 16) + + assert legendre_poly(1).dummy_eq(x) + assert legendre_poly(1, polys=True) == Poly(x) + + +def test_laguerre_poly(): + raises(ValueError, lambda: laguerre_poly(-1, x)) + + assert laguerre_poly(1, x, polys=True) == Poly(-x + 1, domain='QQ') + + assert laguerre_poly(0, x) == 1 + assert laguerre_poly(1, x) == -x + 1 + assert laguerre_poly(2, x) == Q(1, 2)*x**2 - Q(4, 2)*x + 1 + assert laguerre_poly(3, x) == -Q(1, 6)*x**3 + Q(9, 6)*x**2 - Q(18, 6)*x + 1 + assert laguerre_poly(4, x) == Q( + 1, 24)*x**4 - Q(16, 24)*x**3 + Q(72, 24)*x**2 - Q(96, 24)*x + 1 + assert laguerre_poly(5, x) == -Q(1, 120)*x**5 + Q(25, 120)*x**4 - Q( + 200, 120)*x**3 + Q(600, 120)*x**2 - Q(600, 120)*x + 1 + assert laguerre_poly(6, x) == Q(1, 720)*x**6 - Q(36, 720)*x**5 + Q(450, 720)*x**4 - Q(2400, 720)*x**3 + Q(5400, 720)*x**2 - Q(4320, 720)*x + 1 + + assert laguerre_poly(0, x, a) == 1 + assert laguerre_poly(1, x, a) == -x + a + 1 + assert laguerre_poly(2, x, a) == x**2/2 + (-a - 2)*x + a**2/2 + a*Q(3, 2) + 1 + assert laguerre_poly(3, x, a) == -x**3/6 + (a/2 + Q( + 3)/2)*x**2 + (-a**2/2 - a*Q(5, 2) - 3)*x + a**3/6 + a**2 + a*Q(11, 6) + 1 + + assert laguerre_poly(1).dummy_eq(-x + 1) + assert laguerre_poly(1, polys=True) == Poly(-x + 1) + + +def test_spherical_bessel_fn(): + x, z = symbols("x z") + assert spherical_bessel_fn(1, z) == 1/z**2 + assert spherical_bessel_fn(2, z) == -1/z + 3/z**3 + assert spherical_bessel_fn(3, z) == -6/z**2 + 15/z**4 + assert spherical_bessel_fn(4, z) == 1/z - 45/z**3 + 105/z**5 diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_polyoptions.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_polyoptions.py new file mode 100644 index 0000000000000000000000000000000000000000..fa2e6054bad43aef5470949180ea5c2ffdc11f30 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_polyoptions.py @@ -0,0 +1,485 @@ +"""Tests for options manager for :class:`Poly` and public API functions. """ + +from sympy.polys.polyoptions import ( + Options, Expand, Gens, Wrt, Sort, Order, Field, Greedy, Domain, + Split, Gaussian, Extension, Modulus, Symmetric, Strict, Auto, + Frac, Formal, Polys, Include, All, Gen, Symbols, Method) + +from sympy.polys.orderings import lex +from sympy.polys.domains import FF, GF, ZZ, QQ, QQ_I, RR, CC, EX + +from sympy.polys.polyerrors import OptionError, GeneratorsError + +from sympy.core.numbers import (I, Integer) +from sympy.core.symbol import Symbol +from sympy.functions.elementary.miscellaneous import sqrt +from sympy.testing.pytest import raises +from sympy.abc import x, y, z + + +def test_Options_clone(): + opt = Options((x, y, z), {'domain': 'ZZ'}) + + assert opt.gens == (x, y, z) + assert opt.domain == ZZ + assert ('order' in opt) is False + + new_opt = opt.clone({'gens': (x, y), 'order': 'lex'}) + + assert opt.gens == (x, y, z) + assert opt.domain == ZZ + assert ('order' in opt) is False + + assert new_opt.gens == (x, y) + assert new_opt.domain == ZZ + assert ('order' in new_opt) is True + + +def test_Expand_preprocess(): + assert Expand.preprocess(False) is False + assert Expand.preprocess(True) is True + + assert Expand.preprocess(0) is False + assert Expand.preprocess(1) is True + + raises(OptionError, lambda: Expand.preprocess(x)) + + +def test_Expand_postprocess(): + opt = {'expand': True} + Expand.postprocess(opt) + + assert opt == {'expand': True} + + +def test_Gens_preprocess(): + assert Gens.preprocess((None,)) == () + assert Gens.preprocess((x, y, z)) == (x, y, z) + assert Gens.preprocess(((x, y, z),)) == (x, y, z) + + a = Symbol('a', commutative=False) + + raises(GeneratorsError, lambda: Gens.preprocess((x, x, y))) + raises(GeneratorsError, lambda: Gens.preprocess((x, y, a))) + + +def test_Gens_postprocess(): + opt = {'gens': (x, y)} + Gens.postprocess(opt) + + assert opt == {'gens': (x, y)} + + +def test_Wrt_preprocess(): + assert Wrt.preprocess(x) == ['x'] + assert Wrt.preprocess('') == [] + assert Wrt.preprocess(' ') == [] + assert Wrt.preprocess('x,y') == ['x', 'y'] + assert Wrt.preprocess('x y') == ['x', 'y'] + assert Wrt.preprocess('x, y') == ['x', 'y'] + assert Wrt.preprocess('x , y') == ['x', 'y'] + assert Wrt.preprocess(' x, y') == ['x', 'y'] + assert Wrt.preprocess(' x, y') == ['x', 'y'] + assert Wrt.preprocess([x, y]) == ['x', 'y'] + + raises(OptionError, lambda: Wrt.preprocess(',')) + raises(OptionError, lambda: Wrt.preprocess(0)) + + +def test_Wrt_postprocess(): + opt = {'wrt': ['x']} + Wrt.postprocess(opt) + + assert opt == {'wrt': ['x']} + + +def test_Sort_preprocess(): + assert Sort.preprocess([x, y, z]) == ['x', 'y', 'z'] + assert Sort.preprocess((x, y, z)) == ['x', 'y', 'z'] + + assert Sort.preprocess('x > y > z') == ['x', 'y', 'z'] + assert Sort.preprocess('x>y>z') == ['x', 'y', 'z'] + + raises(OptionError, lambda: Sort.preprocess(0)) + raises(OptionError, lambda: Sort.preprocess({x, y, z})) + + +def test_Sort_postprocess(): + opt = {'sort': 'x > y'} + Sort.postprocess(opt) + + assert opt == {'sort': 'x > y'} + + +def test_Order_preprocess(): + assert Order.preprocess('lex') == lex + + +def test_Order_postprocess(): + opt = {'order': True} + Order.postprocess(opt) + + assert opt == {'order': True} + + +def test_Field_preprocess(): + assert Field.preprocess(False) is False + assert Field.preprocess(True) is True + + assert Field.preprocess(0) is False + assert Field.preprocess(1) is True + + raises(OptionError, lambda: Field.preprocess(x)) + + +def test_Field_postprocess(): + opt = {'field': True} + Field.postprocess(opt) + + assert opt == {'field': True} + + +def test_Greedy_preprocess(): + assert Greedy.preprocess(False) is False + assert Greedy.preprocess(True) is True + + assert Greedy.preprocess(0) is False + assert Greedy.preprocess(1) is True + + raises(OptionError, lambda: Greedy.preprocess(x)) + + +def test_Greedy_postprocess(): + opt = {'greedy': True} + Greedy.postprocess(opt) + + assert opt == {'greedy': True} + + +def test_Domain_preprocess(): + assert Domain.preprocess(ZZ) == ZZ + assert Domain.preprocess(QQ) == QQ + assert Domain.preprocess(EX) == EX + assert Domain.preprocess(FF(2)) == FF(2) + assert Domain.preprocess(ZZ[x, y]) == ZZ[x, y] + + assert Domain.preprocess('Z') == ZZ + assert Domain.preprocess('Q') == QQ + + assert Domain.preprocess('ZZ') == ZZ + assert Domain.preprocess('QQ') == QQ + + assert Domain.preprocess('EX') == EX + + assert Domain.preprocess('FF(23)') == FF(23) + assert Domain.preprocess('GF(23)') == GF(23) + + raises(OptionError, lambda: Domain.preprocess('Z[]')) + + assert Domain.preprocess('Z[x]') == ZZ[x] + assert Domain.preprocess('Q[x]') == QQ[x] + assert Domain.preprocess('R[x]') == RR[x] + assert Domain.preprocess('C[x]') == CC[x] + + assert Domain.preprocess('ZZ[x]') == ZZ[x] + assert Domain.preprocess('QQ[x]') == QQ[x] + assert Domain.preprocess('RR[x]') == RR[x] + assert Domain.preprocess('CC[x]') == CC[x] + + assert Domain.preprocess('Z[x,y]') == ZZ[x, y] + assert Domain.preprocess('Q[x,y]') == QQ[x, y] + assert Domain.preprocess('R[x,y]') == RR[x, y] + assert Domain.preprocess('C[x,y]') == CC[x, y] + + assert Domain.preprocess('ZZ[x,y]') == ZZ[x, y] + assert Domain.preprocess('QQ[x,y]') == QQ[x, y] + assert Domain.preprocess('RR[x,y]') == RR[x, y] + assert Domain.preprocess('CC[x,y]') == CC[x, y] + + raises(OptionError, lambda: Domain.preprocess('Z()')) + + assert Domain.preprocess('Z(x)') == ZZ.frac_field(x) + assert Domain.preprocess('Q(x)') == QQ.frac_field(x) + + assert Domain.preprocess('ZZ(x)') == ZZ.frac_field(x) + assert Domain.preprocess('QQ(x)') == QQ.frac_field(x) + + assert Domain.preprocess('Z(x,y)') == ZZ.frac_field(x, y) + assert Domain.preprocess('Q(x,y)') == QQ.frac_field(x, y) + + assert Domain.preprocess('ZZ(x,y)') == ZZ.frac_field(x, y) + assert Domain.preprocess('QQ(x,y)') == QQ.frac_field(x, y) + + assert Domain.preprocess('Q') == QQ.algebraic_field(I) + assert Domain.preprocess('QQ') == QQ.algebraic_field(I) + + assert Domain.preprocess('Q') == QQ.algebraic_field(sqrt(2), I) + assert Domain.preprocess( + 'QQ') == QQ.algebraic_field(sqrt(2), I) + + raises(OptionError, lambda: Domain.preprocess('abc')) + + +def test_Domain_postprocess(): + raises(GeneratorsError, lambda: Domain.postprocess({'gens': (x, y), + 'domain': ZZ[y, z]})) + + raises(GeneratorsError, lambda: Domain.postprocess({'gens': (), + 'domain': EX})) + raises(GeneratorsError, lambda: Domain.postprocess({'domain': EX})) + + +def test_Split_preprocess(): + assert Split.preprocess(False) is False + assert Split.preprocess(True) is True + + assert Split.preprocess(0) is False + assert Split.preprocess(1) is True + + raises(OptionError, lambda: Split.preprocess(x)) + + +def test_Split_postprocess(): + raises(NotImplementedError, lambda: Split.postprocess({'split': True})) + + +def test_Gaussian_preprocess(): + assert Gaussian.preprocess(False) is False + assert Gaussian.preprocess(True) is True + + assert Gaussian.preprocess(0) is False + assert Gaussian.preprocess(1) is True + + raises(OptionError, lambda: Gaussian.preprocess(x)) + + +def test_Gaussian_postprocess(): + opt = {'gaussian': True} + Gaussian.postprocess(opt) + + assert opt == { + 'gaussian': True, + 'domain': QQ_I, + } + + +def test_Extension_preprocess(): + assert Extension.preprocess(True) is True + assert Extension.preprocess(1) is True + + assert Extension.preprocess([]) is None + + assert Extension.preprocess(sqrt(2)) == {sqrt(2)} + assert Extension.preprocess([sqrt(2)]) == {sqrt(2)} + + assert Extension.preprocess([sqrt(2), I]) == {sqrt(2), I} + + raises(OptionError, lambda: Extension.preprocess(False)) + raises(OptionError, lambda: Extension.preprocess(0)) + + +def test_Extension_postprocess(): + opt = {'extension': {sqrt(2)}} + Extension.postprocess(opt) + + assert opt == { + 'extension': {sqrt(2)}, + 'domain': QQ.algebraic_field(sqrt(2)), + } + + opt = {'extension': True} + Extension.postprocess(opt) + + assert opt == {'extension': True} + + +def test_Modulus_preprocess(): + assert Modulus.preprocess(23) == 23 + assert Modulus.preprocess(Integer(23)) == 23 + + raises(OptionError, lambda: Modulus.preprocess(0)) + raises(OptionError, lambda: Modulus.preprocess(x)) + + +def test_Modulus_postprocess(): + opt = {'modulus': 5} + Modulus.postprocess(opt) + + assert opt == { + 'modulus': 5, + 'domain': FF(5), + } + + opt = {'modulus': 5, 'symmetric': False} + Modulus.postprocess(opt) + + assert opt == { + 'modulus': 5, + 'domain': FF(5, False), + 'symmetric': False, + } + + +def test_Symmetric_preprocess(): + assert Symmetric.preprocess(False) is False + assert Symmetric.preprocess(True) is True + + assert Symmetric.preprocess(0) is False + assert Symmetric.preprocess(1) is True + + raises(OptionError, lambda: Symmetric.preprocess(x)) + + +def test_Symmetric_postprocess(): + opt = {'symmetric': True} + Symmetric.postprocess(opt) + + assert opt == {'symmetric': True} + + +def test_Strict_preprocess(): + assert Strict.preprocess(False) is False + assert Strict.preprocess(True) is True + + assert Strict.preprocess(0) is False + assert Strict.preprocess(1) is True + + raises(OptionError, lambda: Strict.preprocess(x)) + + +def test_Strict_postprocess(): + opt = {'strict': True} + Strict.postprocess(opt) + + assert opt == {'strict': True} + + +def test_Auto_preprocess(): + assert Auto.preprocess(False) is False + assert Auto.preprocess(True) is True + + assert Auto.preprocess(0) is False + assert Auto.preprocess(1) is True + + raises(OptionError, lambda: Auto.preprocess(x)) + + +def test_Auto_postprocess(): + opt = {'auto': True} + Auto.postprocess(opt) + + assert opt == {'auto': True} + + +def test_Frac_preprocess(): + assert Frac.preprocess(False) is False + assert Frac.preprocess(True) is True + + assert Frac.preprocess(0) is False + assert Frac.preprocess(1) is True + + raises(OptionError, lambda: Frac.preprocess(x)) + + +def test_Frac_postprocess(): + opt = {'frac': True} + Frac.postprocess(opt) + + assert opt == {'frac': True} + + +def test_Formal_preprocess(): + assert Formal.preprocess(False) is False + assert Formal.preprocess(True) is True + + assert Formal.preprocess(0) is False + assert Formal.preprocess(1) is True + + raises(OptionError, lambda: Formal.preprocess(x)) + + +def test_Formal_postprocess(): + opt = {'formal': True} + Formal.postprocess(opt) + + assert opt == {'formal': True} + + +def test_Polys_preprocess(): + assert Polys.preprocess(False) is False + assert Polys.preprocess(True) is True + + assert Polys.preprocess(0) is False + assert Polys.preprocess(1) is True + + raises(OptionError, lambda: Polys.preprocess(x)) + + +def test_Polys_postprocess(): + opt = {'polys': True} + Polys.postprocess(opt) + + assert opt == {'polys': True} + + +def test_Include_preprocess(): + assert Include.preprocess(False) is False + assert Include.preprocess(True) is True + + assert Include.preprocess(0) is False + assert Include.preprocess(1) is True + + raises(OptionError, lambda: Include.preprocess(x)) + + +def test_Include_postprocess(): + opt = {'include': True} + Include.postprocess(opt) + + assert opt == {'include': True} + + +def test_All_preprocess(): + assert All.preprocess(False) is False + assert All.preprocess(True) is True + + assert All.preprocess(0) is False + assert All.preprocess(1) is True + + raises(OptionError, lambda: All.preprocess(x)) + + +def test_All_postprocess(): + opt = {'all': True} + All.postprocess(opt) + + assert opt == {'all': True} + + +def test_Gen_postprocess(): + opt = {'gen': x} + Gen.postprocess(opt) + + assert opt == {'gen': x} + + +def test_Symbols_preprocess(): + raises(OptionError, lambda: Symbols.preprocess(x)) + + +def test_Symbols_postprocess(): + opt = {'symbols': [x, y, z]} + Symbols.postprocess(opt) + + assert opt == {'symbols': [x, y, z]} + + +def test_Method_preprocess(): + raises(OptionError, lambda: Method.preprocess(10)) + + +def test_Method_postprocess(): + opt = {'method': 'f5b'} + Method.postprocess(opt) + + assert opt == {'method': 'f5b'} diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_polyroots.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_polyroots.py new file mode 100644 index 0000000000000000000000000000000000000000..7f96b1930f6789ce3150ae2c920ba7d9faa68791 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_polyroots.py @@ -0,0 +1,758 @@ +"""Tests for algorithms for computing symbolic roots of polynomials. """ + +from sympy.core.numbers import (I, Rational, pi) +from sympy.core.singleton import S +from sympy.core.symbol import (Symbol, Wild, symbols) +from sympy.functions.elementary.complexes import (conjugate, im, re) +from sympy.functions.elementary.exponential import exp +from sympy.functions.elementary.miscellaneous import (root, sqrt) +from sympy.functions.elementary.piecewise import Piecewise +from sympy.functions.elementary.trigonometric import (acos, cos, sin) +from sympy.polys.domains.integerring import ZZ +from sympy.sets.sets import Interval +from sympy.simplify.powsimp import powsimp + +from sympy.polys import Poly, cyclotomic_poly, intervals, nroots, rootof + +from sympy.polys.polyroots import (root_factors, roots_linear, + roots_quadratic, roots_cubic, roots_quartic, roots_quintic, + roots_cyclotomic, roots_binomial, preprocess_roots, roots) + +from sympy.polys.orthopolys import legendre_poly +from sympy.polys.polyerrors import PolynomialError, \ + UnsolvableFactorError +from sympy.polys.polyutils import _nsort + +from sympy.testing.pytest import raises, slow +from sympy.core.random import verify_numerically +import mpmath +from itertools import product + + + +a, b, c, d, e, q, t, x, y, z = symbols('a,b,c,d,e,q,t,x,y,z') + + +def _check(roots): + # this is the desired invariant for roots returned + # by all_roots. It is trivially true for linear + # polynomials. + nreal = sum(1 if i.is_real else 0 for i in roots) + assert sorted(roots[:nreal]) == list(roots[:nreal]) + for ix in range(nreal, len(roots), 2): + if not ( + roots[ix + 1] == roots[ix] or + roots[ix + 1] == conjugate(roots[ix])): + return False + return True + + +def test_roots_linear(): + assert roots_linear(Poly(2*x + 1, x)) == [Rational(-1, 2)] + + +def test_roots_quadratic(): + assert roots_quadratic(Poly(2*x**2, x)) == [0, 0] + assert roots_quadratic(Poly(2*x**2 + 3*x, x)) == [Rational(-3, 2), 0] + assert roots_quadratic(Poly(2*x**2 + 3, x)) == [-I*sqrt(6)/2, I*sqrt(6)/2] + assert roots_quadratic(Poly(2*x**2 + 4*x + 3, x)) == [-1 - I*sqrt(2)/2, -1 + I*sqrt(2)/2] + _check(Poly(2*x**2 + 4*x + 3, x).all_roots()) + + f = x**2 + (2*a*e + 2*c*e)/(a - c)*x + (d - b + a*e**2 - c*e**2)/(a - c) + assert roots_quadratic(Poly(f, x)) == \ + [-e*(a + c)/(a - c) - sqrt(a*b + c*d - a*d - b*c + 4*a*c*e**2)/(a - c), + -e*(a + c)/(a - c) + sqrt(a*b + c*d - a*d - b*c + 4*a*c*e**2)/(a - c)] + + # check for simplification + f = Poly(y*x**2 - 2*x - 2*y, x) + assert roots_quadratic(f) == \ + [-sqrt(2*y**2 + 1)/y + 1/y, sqrt(2*y**2 + 1)/y + 1/y] + f = Poly(x**2 + (-y**2 - 2)*x + y**2 + 1, x) + assert roots_quadratic(f) == \ + [1,y**2 + 1] + + f = Poly(sqrt(2)*x**2 - 1, x) + r = roots_quadratic(f) + assert r == _nsort(r) + + # issue 8255 + f = Poly(-24*x**2 - 180*x + 264) + assert [w.n(2) for w in f.all_roots(radicals=True)] == \ + [w.n(2) for w in f.all_roots(radicals=False)] + for _a, _b, _c in product((-2, 2), (-2, 2), (0, -1)): + f = Poly(_a*x**2 + _b*x + _c) + roots = roots_quadratic(f) + assert roots == _nsort(roots) + + +def test_issue_7724(): + eq = Poly(x**4*I + x**2 + I, x) + assert roots(eq) == { + sqrt(I/2 + sqrt(5)*I/2): 1, + sqrt(-sqrt(5)*I/2 + I/2): 1, + -sqrt(I/2 + sqrt(5)*I/2): 1, + -sqrt(-sqrt(5)*I/2 + I/2): 1} + + +def test_issue_8438(): + p = Poly([1, y, -2, -3], x).as_expr() + roots = roots_cubic(Poly(p, x), x) + z = Rational(-3, 2) - I*7/2 # this will fail in code given in commit msg + post = [r.subs(y, z) for r in roots] + assert set(post) == \ + set(roots_cubic(Poly(p.subs(y, z), x))) + # /!\ if p is not made an expression, this is *very* slow + assert all(p.subs({y: z, x: i}).n(2, chop=True) == 0 for i in post) + + +def test_issue_8285(): + roots = (Poly(4*x**8 - 1, x)*Poly(x**2 + 1)).all_roots() + assert _check(roots) + f = Poly(x**4 + 5*x**2 + 6, x) + ro = [rootof(f, i) for i in range(4)] + roots = Poly(x**4 + 5*x**2 + 6, x).all_roots() + assert roots == ro + assert _check(roots) + # more than 2 complex roots from which to identify the + # imaginary ones + roots = Poly(2*x**8 - 1).all_roots() + assert _check(roots) + assert len(Poly(2*x**10 - 1).all_roots()) == 10 # doesn't fail + + +def test_issue_8289(): + roots = (Poly(x**2 + 2)*Poly(x**4 + 2)).all_roots() + assert _check(roots) + roots = Poly(x**6 + 3*x**3 + 2, x).all_roots() + assert _check(roots) + roots = Poly(x**6 - x + 1).all_roots() + assert _check(roots) + # all imaginary roots with multiplicity of 2 + roots = Poly(x**4 + 4*x**2 + 4, x).all_roots() + assert _check(roots) + + +def test_issue_14291(): + assert Poly(((x - 1)**2 + 1)*((x - 1)**2 + 2)*(x - 1) + ).all_roots() == [1, 1 - I, 1 + I, 1 - sqrt(2)*I, 1 + sqrt(2)*I] + p = x**4 + 10*x**2 + 1 + ans = [rootof(p, i) for i in range(4)] + assert Poly(p).all_roots() == ans + _check(ans) + + +def test_issue_13340(): + eq = Poly(y**3 + exp(x)*y + x, y, domain='EX') + roots_d = roots(eq) + assert len(roots_d) == 3 + + +def test_issue_14522(): + eq = Poly(x**4 + x**3*(16 + 32*I) + x**2*(-285 + 386*I) + x*(-2824 - 448*I) - 2058 - 6053*I, x) + roots_eq = roots(eq) + assert all(eq(r) == 0 for r in roots_eq) + + +def test_issue_15076(): + sol = roots_quartic(Poly(t**4 - 6*t**2 + t/x - 3, t)) + assert sol[0].has(x) + + +def test_issue_16589(): + eq = Poly(x**4 - 8*sqrt(2)*x**3 + 4*x**3 - 64*sqrt(2)*x**2 + 1024*x, x) + roots_eq = roots(eq) + assert 0 in roots_eq + + +def test_roots_cubic(): + assert roots_cubic(Poly(2*x**3, x)) == [0, 0, 0] + assert roots_cubic(Poly(x**3 - 3*x**2 + 3*x - 1, x)) == [1, 1, 1] + + # valid for arbitrary y (issue 21263) + r = root(y, 3) + assert roots_cubic(Poly(x**3 - y, x)) == [r, + r*(-S.Half + sqrt(3)*I/2), + r*(-S.Half - sqrt(3)*I/2)] + # simpler form when y is negative + assert roots_cubic(Poly(x**3 - -1, x)) == \ + [-1, S.Half - I*sqrt(3)/2, S.Half + I*sqrt(3)/2] + assert roots_cubic(Poly(2*x**3 - 3*x**2 - 3*x - 1, x))[0] == \ + S.Half + 3**Rational(1, 3)/2 + 3**Rational(2, 3)/2 + eq = -x**3 + 2*x**2 + 3*x - 2 + assert roots(eq, trig=True, multiple=True) == \ + roots_cubic(Poly(eq, x), trig=True) == [ + Rational(2, 3) + 2*sqrt(13)*cos(acos(8*sqrt(13)/169)/3)/3, + -2*sqrt(13)*sin(-acos(8*sqrt(13)/169)/3 + pi/6)/3 + Rational(2, 3), + -2*sqrt(13)*cos(-acos(8*sqrt(13)/169)/3 + pi/3)/3 + Rational(2, 3), + ] + + +def test_roots_quartic(): + assert roots_quartic(Poly(x**4, x)) == [0, 0, 0, 0] + assert roots_quartic(Poly(x**4 + x**3, x)) in [ + [-1, 0, 0, 0], + [0, -1, 0, 0], + [0, 0, -1, 0], + [0, 0, 0, -1] + ] + assert roots_quartic(Poly(x**4 - x**3, x)) in [ + [1, 0, 0, 0], + [0, 1, 0, 0], + [0, 0, 1, 0], + [0, 0, 0, 1] + ] + + lhs = roots_quartic(Poly(x**4 + x, x)) + rhs = [S.Half + I*sqrt(3)/2, S.Half - I*sqrt(3)/2, S.Zero, -S.One] + + assert sorted(lhs, key=hash) == sorted(rhs, key=hash) + + # test of all branches of roots quartic + for i, (a, b, c, d) in enumerate([(1, 2, 3, 0), + (3, -7, -9, 9), + (1, 2, 3, 4), + (1, 2, 3, 4), + (-7, -3, 3, -6), + (-3, 5, -6, -4), + (6, -5, -10, -3)]): + if i == 2: + c = -a*(a**2/S(8) - b/S(2)) + elif i == 3: + d = a*(a*(a**2*Rational(3, 256) - b/S(16)) + c/S(4)) + eq = x**4 + a*x**3 + b*x**2 + c*x + d + ans = roots_quartic(Poly(eq, x)) + assert all(eq.subs(x, ai).n(chop=True) == 0 for ai in ans) + + # not all symbolic quartics are unresolvable + eq = Poly(q*x + q/4 + x**4 + x**3 + 2*x**2 - Rational(1, 3), x) + sol = roots_quartic(eq) + assert all(verify_numerically(eq.subs(x, i), 0) for i in sol) + z = symbols('z', negative=True) + eq = x**4 + 2*x**3 + 3*x**2 + x*(z + 11) + 5 + zans = roots_quartic(Poly(eq, x)) + assert all(verify_numerically(eq.subs(((x, i), (z, -1))), 0) for i in zans) + # but some are (see also issue 4989) + # it's ok if the solution is not Piecewise, but the tests below should pass + eq = Poly(y*x**4 + x**3 - x + z, x) + ans = roots_quartic(eq) + assert all(type(i) == Piecewise for i in ans) + reps = ( + {"y": Rational(-1, 3), "z": Rational(-1, 4)}, # 4 real + {"y": Rational(-1, 3), "z": Rational(-1, 2)}, # 2 real + {"y": Rational(-1, 3), "z": -2}) # 0 real + for rep in reps: + sol = roots_quartic(Poly(eq.subs(rep), x)) + assert all(verify_numerically(w.subs(rep) - s, 0) for w, s in zip(ans, sol)) + + +def test_issue_21287(): + assert not any(isinstance(i, Piecewise) for i in roots_quartic( + Poly(x**4 - x**2*(3 + 5*I) + 2*x*(-1 + I) - 1 + 3*I, x))) + + +def test_roots_quintic(): + eqs = (x**5 - 2, + (x/2 + 1)**5 - 5*(x/2 + 1) + 12, + x**5 - 110*x**3 - 55*x**2 + 2310*x + 979) + for eq in eqs: + roots = roots_quintic(Poly(eq)) + assert len(roots) == 5 + assert all(eq.subs(x, r.n(10)).n(chop = 1e-5) == 0 for r in roots) + + +def test_roots_cyclotomic(): + assert roots_cyclotomic(cyclotomic_poly(1, x, polys=True)) == [1] + assert roots_cyclotomic(cyclotomic_poly(2, x, polys=True)) == [-1] + assert roots_cyclotomic(cyclotomic_poly( + 3, x, polys=True)) == [Rational(-1, 2) - I*sqrt(3)/2, Rational(-1, 2) + I*sqrt(3)/2] + assert roots_cyclotomic(cyclotomic_poly(4, x, polys=True)) == [-I, I] + assert roots_cyclotomic(cyclotomic_poly( + 6, x, polys=True)) == [S.Half - I*sqrt(3)/2, S.Half + I*sqrt(3)/2] + + assert roots_cyclotomic(cyclotomic_poly(7, x, polys=True)) == [ + -cos(pi/7) - I*sin(pi/7), + -cos(pi/7) + I*sin(pi/7), + -cos(pi*Rational(3, 7)) - I*sin(pi*Rational(3, 7)), + -cos(pi*Rational(3, 7)) + I*sin(pi*Rational(3, 7)), + cos(pi*Rational(2, 7)) - I*sin(pi*Rational(2, 7)), + cos(pi*Rational(2, 7)) + I*sin(pi*Rational(2, 7)), + ] + + assert roots_cyclotomic(cyclotomic_poly(8, x, polys=True)) == [ + -sqrt(2)/2 - I*sqrt(2)/2, + -sqrt(2)/2 + I*sqrt(2)/2, + sqrt(2)/2 - I*sqrt(2)/2, + sqrt(2)/2 + I*sqrt(2)/2, + ] + + assert roots_cyclotomic(cyclotomic_poly(12, x, polys=True)) == [ + -sqrt(3)/2 - I/2, + -sqrt(3)/2 + I/2, + sqrt(3)/2 - I/2, + sqrt(3)/2 + I/2, + ] + + assert roots_cyclotomic( + cyclotomic_poly(1, x, polys=True), factor=True) == [1] + assert roots_cyclotomic( + cyclotomic_poly(2, x, polys=True), factor=True) == [-1] + + assert roots_cyclotomic(cyclotomic_poly(3, x, polys=True), factor=True) == \ + [-root(-1, 3), -1 + root(-1, 3)] + assert roots_cyclotomic(cyclotomic_poly(4, x, polys=True), factor=True) == \ + [-I, I] + assert roots_cyclotomic(cyclotomic_poly(5, x, polys=True), factor=True) == \ + [-root(-1, 5), -root(-1, 5)**3, root(-1, 5)**2, -1 - root(-1, 5)**2 + root(-1, 5) + root(-1, 5)**3] + + assert roots_cyclotomic(cyclotomic_poly(6, x, polys=True), factor=True) == \ + [1 - root(-1, 3), root(-1, 3)] + + +def test_roots_binomial(): + assert roots_binomial(Poly(5*x, x)) == [0] + assert roots_binomial(Poly(5*x**4, x)) == [0, 0, 0, 0] + assert roots_binomial(Poly(5*x + 2, x)) == [Rational(-2, 5)] + + A = 10**Rational(3, 4)/10 + + assert roots_binomial(Poly(5*x**4 + 2, x)) == \ + [-A - A*I, -A + A*I, A - A*I, A + A*I] + _check(roots_binomial(Poly(x**8 - 2))) + + a1 = Symbol('a1', nonnegative=True) + b1 = Symbol('b1', nonnegative=True) + + r0 = roots_quadratic(Poly(a1*x**2 + b1, x)) + r1 = roots_binomial(Poly(a1*x**2 + b1, x)) + + assert powsimp(r0[0]) == powsimp(r1[0]) + assert powsimp(r0[1]) == powsimp(r1[1]) + for a, b, s, n in product((1, 2), (1, 2), (-1, 1), (2, 3, 4, 5)): + if a == b and a != 1: # a == b == 1 is sufficient + continue + p = Poly(a*x**n + s*b) + ans = roots_binomial(p) + assert ans == _nsort(ans) + + # issue 8813 + assert roots(Poly(2*x**3 - 16*y**3, x)) == { + 2*y*(Rational(-1, 2) - sqrt(3)*I/2): 1, + 2*y: 1, + 2*y*(Rational(-1, 2) + sqrt(3)*I/2): 1} + + +def test_roots_preprocessing(): + f = a*y*x**2 + y - b + + coeff, poly = preprocess_roots(Poly(f, x)) + + assert coeff == 1 + assert poly == Poly(a*y*x**2 + y - b, x) + + f = c**3*x**3 + c**2*x**2 + c*x + a + + coeff, poly = preprocess_roots(Poly(f, x)) + + assert coeff == 1/c + assert poly == Poly(x**3 + x**2 + x + a, x) + + f = c**3*x**3 + c**2*x**2 + a + + coeff, poly = preprocess_roots(Poly(f, x)) + + assert coeff == 1/c + assert poly == Poly(x**3 + x**2 + a, x) + + f = c**3*x**3 + c*x + a + + coeff, poly = preprocess_roots(Poly(f, x)) + + assert coeff == 1/c + assert poly == Poly(x**3 + x + a, x) + + f = c**3*x**3 + a + + coeff, poly = preprocess_roots(Poly(f, x)) + + assert coeff == 1/c + assert poly == Poly(x**3 + a, x) + + E, F, J, L = symbols("E,F,J,L") + + f = -21601054687500000000*E**8*J**8/L**16 + \ + 508232812500000000*F*x*E**7*J**7/L**14 - \ + 4269543750000000*E**6*F**2*J**6*x**2/L**12 + \ + 16194716250000*E**5*F**3*J**5*x**3/L**10 - \ + 27633173750*E**4*F**4*J**4*x**4/L**8 + \ + 14840215*E**3*F**5*J**3*x**5/L**6 + \ + 54794*E**2*F**6*J**2*x**6/(5*L**4) - \ + 1153*E*J*F**7*x**7/(80*L**2) + \ + 633*F**8*x**8/160000 + + coeff, poly = preprocess_roots(Poly(f, x)) + + assert coeff == 20*E*J/(F*L**2) + assert poly == 633*x**8 - 115300*x**7 + 4383520*x**6 + 296804300*x**5 - 27633173750*x**4 + \ + 809735812500*x**3 - 10673859375000*x**2 + 63529101562500*x - 135006591796875 + + f = Poly(-y**2 + x**2*exp(x), y, domain=ZZ[x, exp(x)]) + g = Poly(-y**2 + exp(x), y, domain=ZZ[exp(x)]) + + assert preprocess_roots(f) == (x, g) + + +def test_roots0(): + assert roots(1, x) == {} + assert roots(x, x) == {S.Zero: 1} + assert roots(x**9, x) == {S.Zero: 9} + assert roots(((x - 2)*(x + 3)*(x - 4)).expand(), x) == {-S(3): 1, S(2): 1, S(4): 1} + + assert roots(2*x + 1, x) == {Rational(-1, 2): 1} + assert roots((2*x + 1)**2, x) == {Rational(-1, 2): 2} + assert roots((2*x + 1)**5, x) == {Rational(-1, 2): 5} + assert roots((2*x + 1)**10, x) == {Rational(-1, 2): 10} + + assert roots(x**4 - 1, x) == {I: 1, S.One: 1, -S.One: 1, -I: 1} + assert roots((x**4 - 1)**2, x) == {I: 2, S.One: 2, -S.One: 2, -I: 2} + + assert roots(((2*x - 3)**2).expand(), x) == {Rational( 3, 2): 2} + assert roots(((2*x + 3)**2).expand(), x) == {Rational(-3, 2): 2} + + assert roots(((2*x - 3)**3).expand(), x) == {Rational( 3, 2): 3} + assert roots(((2*x + 3)**3).expand(), x) == {Rational(-3, 2): 3} + + assert roots(((2*x - 3)**5).expand(), x) == {Rational( 3, 2): 5} + assert roots(((2*x + 3)**5).expand(), x) == {Rational(-3, 2): 5} + + assert roots(((a*x - b)**5).expand(), x) == { b/a: 5} + assert roots(((a*x + b)**5).expand(), x) == {-b/a: 5} + + assert roots(x**2 + (-a - 1)*x + a, x) == {a: 1, S.One: 1} + + assert roots(x**4 - 2*x**2 + 1, x) == {S.One: 2, S.NegativeOne: 2} + + assert roots(x**6 - 4*x**4 + 4*x**3 - x**2, x) == \ + {S.One: 2, -1 - sqrt(2): 1, S.Zero: 2, -1 + sqrt(2): 1} + + assert roots(x**8 - 1, x) == { + sqrt(2)/2 + I*sqrt(2)/2: 1, + sqrt(2)/2 - I*sqrt(2)/2: 1, + -sqrt(2)/2 + I*sqrt(2)/2: 1, + -sqrt(2)/2 - I*sqrt(2)/2: 1, + S.One: 1, -S.One: 1, I: 1, -I: 1 + } + + f = -2016*x**2 - 5616*x**3 - 2056*x**4 + 3324*x**5 + 2176*x**6 - \ + 224*x**7 - 384*x**8 - 64*x**9 + + assert roots(f) == {S.Zero: 2, -S(2): 2, S(2): 1, Rational(-7, 2): 1, + Rational(-3, 2): 1, Rational(-1, 2): 1, Rational(3, 2): 1} + + assert roots((a + b + c)*x - (a + b + c + d), x) == {(a + b + c + d)/(a + b + c): 1} + + assert roots(x**3 + x**2 - x + 1, x, cubics=False) == {} + assert roots(((x - 2)*( + x + 3)*(x - 4)).expand(), x, cubics=False) == {-S(3): 1, S(2): 1, S(4): 1} + assert roots(((x - 2)*(x + 3)*(x - 4)*(x - 5)).expand(), x, cubics=False) == \ + {-S(3): 1, S(2): 1, S(4): 1, S(5): 1} + assert roots(x**3 + 2*x**2 + 4*x + 8, x) == {-S(2): 1, -2*I: 1, 2*I: 1} + assert roots(x**3 + 2*x**2 + 4*x + 8, x, cubics=True) == \ + {-2*I: 1, 2*I: 1, -S(2): 1} + assert roots((x**2 - x)*(x**3 + 2*x**2 + 4*x + 8), x ) == \ + {S.One: 1, S.Zero: 1, -S(2): 1, -2*I: 1, 2*I: 1} + + r1_2, r1_3 = S.Half, Rational(1, 3) + + x0 = (3*sqrt(33) + 19)**r1_3 + x1 = 4/x0/3 + x2 = x0/3 + x3 = sqrt(3)*I/2 + x4 = x3 - r1_2 + x5 = -x3 - r1_2 + assert roots(x**3 + x**2 - x + 1, x, cubics=True) == { + -x1 - x2 - r1_3: 1, + -x1/x4 - x2*x4 - r1_3: 1, + -x1/x5 - x2*x5 - r1_3: 1, + } + + f = (x**2 + 2*x + 3).subs(x, 2*x**2 + 3*x).subs(x, 5*x - 4) + + r13_20, r1_20 = [ Rational(*r) + for r in ((13, 20), (1, 20)) ] + + s2 = sqrt(2) + assert roots(f, x) == { + r13_20 + r1_20*sqrt(1 - 8*I*s2): 1, + r13_20 - r1_20*sqrt(1 - 8*I*s2): 1, + r13_20 + r1_20*sqrt(1 + 8*I*s2): 1, + r13_20 - r1_20*sqrt(1 + 8*I*s2): 1, + } + + f = x**4 + x**3 + x**2 + x + 1 + + r1_4, r1_8, r5_8 = [ Rational(*r) for r in ((1, 4), (1, 8), (5, 8)) ] + + assert roots(f, x) == { + -r1_4 + r1_4*5**r1_2 + I*(r5_8 + r1_8*5**r1_2)**r1_2: 1, + -r1_4 + r1_4*5**r1_2 - I*(r5_8 + r1_8*5**r1_2)**r1_2: 1, + -r1_4 - r1_4*5**r1_2 + I*(r5_8 - r1_8*5**r1_2)**r1_2: 1, + -r1_4 - r1_4*5**r1_2 - I*(r5_8 - r1_8*5**r1_2)**r1_2: 1, + } + + f = z**3 + (-2 - y)*z**2 + (1 + 2*y - 2*x**2)*z - y + 2*x**2 + + assert roots(f, z) == { + S.One: 1, + S.Half + S.Half*y + S.Half*sqrt(1 - 2*y + y**2 + 8*x**2): 1, + S.Half + S.Half*y - S.Half*sqrt(1 - 2*y + y**2 + 8*x**2): 1, + } + + assert roots(a*b*c*x**3 + 2*x**2 + 4*x + 8, x, cubics=False) == {} + assert roots(a*b*c*x**3 + 2*x**2 + 4*x + 8, x, cubics=True) != {} + + assert roots(x**4 - 1, x, filter='Z') == {S.One: 1, -S.One: 1} + assert roots(x**4 - 1, x, filter='I') == {I: 1, -I: 1} + + assert roots((x - 1)*(x + 1), x) == {S.One: 1, -S.One: 1} + assert roots( + (x - 1)*(x + 1), x, predicate=lambda r: r.is_positive) == {S.One: 1} + + assert roots(x**4 - 1, x, filter='Z', multiple=True) == [-S.One, S.One] + assert roots(x**4 - 1, x, filter='I', multiple=True) == [I, -I] + + ar, br = symbols('a, b', real=True) + p = x**2*(ar-br)**2 + 2*x*(br-ar) + 1 + assert roots(p, x, filter='R') == {1/(ar - br): 2} + + assert roots(x**3, x, multiple=True) == [S.Zero, S.Zero, S.Zero] + assert roots(1234, x, multiple=True) == [] + + f = x**6 - x**5 + x**4 - x**3 + x**2 - x + 1 + + assert roots(f) == { + -I*sin(pi/7) + cos(pi/7): 1, + -I*sin(pi*Rational(2, 7)) - cos(pi*Rational(2, 7)): 1, + -I*sin(pi*Rational(3, 7)) + cos(pi*Rational(3, 7)): 1, + I*sin(pi/7) + cos(pi/7): 1, + I*sin(pi*Rational(2, 7)) - cos(pi*Rational(2, 7)): 1, + I*sin(pi*Rational(3, 7)) + cos(pi*Rational(3, 7)): 1, + } + + g = ((x**2 + 1)*f**2).expand() + + assert roots(g) == { + -I*sin(pi/7) + cos(pi/7): 2, + -I*sin(pi*Rational(2, 7)) - cos(pi*Rational(2, 7)): 2, + -I*sin(pi*Rational(3, 7)) + cos(pi*Rational(3, 7)): 2, + I*sin(pi/7) + cos(pi/7): 2, + I*sin(pi*Rational(2, 7)) - cos(pi*Rational(2, 7)): 2, + I*sin(pi*Rational(3, 7)) + cos(pi*Rational(3, 7)): 2, + -I: 1, I: 1, + } + + r = roots(x**3 + 40*x + 64) + real_root = [rx for rx in r if rx.is_real][0] + cr = 108 + 6*sqrt(1074) + assert real_root == -2*root(cr, 3)/3 + 20/root(cr, 3) + + eq = Poly((7 + 5*sqrt(2))*x**3 + (-6 - 4*sqrt(2))*x**2 + (-sqrt(2) - 1)*x + 2, x, domain='EX') + assert roots(eq) == {-1 + sqrt(2): 1, -2 + 2*sqrt(2): 1, -sqrt(2) + 1: 1} + + eq = Poly(41*x**5 + 29*sqrt(2)*x**5 - 153*x**4 - 108*sqrt(2)*x**4 + + 175*x**3 + 125*sqrt(2)*x**3 - 45*x**2 - 30*sqrt(2)*x**2 - 26*sqrt(2)*x - + 26*x + 24, x, domain='EX') + assert roots(eq) == {-sqrt(2) + 1: 1, -2 + 2*sqrt(2): 1, -1 + sqrt(2): 1, + -4 + 4*sqrt(2): 1, -3 + 3*sqrt(2): 1} + + eq = Poly(x**3 - 2*x**2 + 6*sqrt(2)*x**2 - 8*sqrt(2)*x + 23*x - 14 + + 14*sqrt(2), x, domain='EX') + assert roots(eq) == {-2*sqrt(2) + 2: 1, -2*sqrt(2) + 1: 1, -2*sqrt(2) - 1: 1} + + assert roots(Poly((x + sqrt(2))**3 - 7, x, domain='EX')) == \ + {-sqrt(2) + root(7, 3)*(-S.Half - sqrt(3)*I/2): 1, + -sqrt(2) + root(7, 3)*(-S.Half + sqrt(3)*I/2): 1, + -sqrt(2) + root(7, 3): 1} + +def test_roots_slow(): + """Just test that calculating these roots does not hang. """ + a, b, c, d, x = symbols("a,b,c,d,x") + + f1 = x**2*c + (a/b) + x*c*d - a + f2 = x**2*(a + b*(c - d)*a) + x*a*b*c/(b*d - d) + (a*d - c/d) + + assert list(roots(f1, x).values()) == [1, 1] + assert list(roots(f2, x).values()) == [1, 1] + + (zz, yy, xx, zy, zx, yx, k) = symbols("zz,yy,xx,zy,zx,yx,k") + + e1 = (zz - k)*(yy - k)*(xx - k) + zy*yx*zx + zx - zy - yx + e2 = (zz - k)*yx*yx + zx*(yy - k)*zx + zy*zy*(xx - k) + + assert list(roots(e1 - e2, k).values()) == [1, 1, 1] + + f = x**3 + 2*x**2 + 8 + R = list(roots(f).keys()) + + assert not any(i for i in [f.subs(x, ri).n(chop=True) for ri in R]) + + +def test_roots_inexact(): + R1 = roots(x**2 + x + 1, x, multiple=True) + R2 = roots(x**2 + x + 1.0, x, multiple=True) + + for r1, r2 in zip(R1, R2): + assert abs(r1 - r2) < 1e-12 + + f = x**4 + 3.0*sqrt(2.0)*x**3 - (78.0 + 24.0*sqrt(3.0))*x**2 \ + + 144.0*(2*sqrt(3.0) + 9.0) + + R1 = roots(f, multiple=True) + R2 = (-12.7530479110482, -3.85012393732929, + 4.89897948556636, 7.46155167569183) + + for r1, r2 in zip(R1, R2): + assert abs(r1 - r2) < 1e-10 + + +def test_roots_preprocessed(): + E, F, J, L = symbols("E,F,J,L") + + f = -21601054687500000000*E**8*J**8/L**16 + \ + 508232812500000000*F*x*E**7*J**7/L**14 - \ + 4269543750000000*E**6*F**2*J**6*x**2/L**12 + \ + 16194716250000*E**5*F**3*J**5*x**3/L**10 - \ + 27633173750*E**4*F**4*J**4*x**4/L**8 + \ + 14840215*E**3*F**5*J**3*x**5/L**6 + \ + 54794*E**2*F**6*J**2*x**6/(5*L**4) - \ + 1153*E*J*F**7*x**7/(80*L**2) + \ + 633*F**8*x**8/160000 + + assert roots(f, x) == {} + + R1 = roots(f.evalf(), x, multiple=True) + R2 = [-1304.88375606366, 97.1168816800648, 186.946430171876, 245.526792947065, + 503.441004174773, 791.549343830097, 1273.16678129348, 1850.10650616851] + + w = Wild('w') + p = w*E*J/(F*L**2) + + assert len(R1) == len(R2) + + for r1, r2 in zip(R1, R2): + match = r1.match(p) + assert match is not None and abs(match[w] - r2) < 1e-10 + + +def test_roots_strict(): + assert roots(x**2 - 2*x + 1, strict=False) == {1: 2} + assert roots(x**2 - 2*x + 1, strict=True) == {1: 2} + + assert roots(x**6 - 2*x**5 - x**2 + 3*x - 2, strict=False) == {2: 1} + raises(UnsolvableFactorError, lambda: roots(x**6 - 2*x**5 - x**2 + 3*x - 2, strict=True)) + + +def test_roots_mixed(): + f = -1936 - 5056*x - 7592*x**2 + 2704*x**3 - 49*x**4 + + _re, _im = intervals(f, all=True) + _nroots = nroots(f) + _sroots = roots(f, multiple=True) + + _re = [ Interval(a, b) for (a, b), _ in _re ] + _im = [ Interval(re(a), re(b))*Interval(im(a), im(b)) for (a, b), + _ in _im ] + + _intervals = _re + _im + _sroots = [ r.evalf() for r in _sroots ] + + _nroots = sorted(_nroots, key=lambda x: x.sort_key()) + _sroots = sorted(_sroots, key=lambda x: x.sort_key()) + + for _roots in (_nroots, _sroots): + for i, r in zip(_intervals, _roots): + if r.is_real: + assert r in i + else: + assert (re(r), im(r)) in i + + +def test_root_factors(): + assert root_factors(Poly(1, x)) == [Poly(1, x)] + assert root_factors(Poly(x, x)) == [Poly(x, x)] + + assert root_factors(x**2 - 1, x) == [x + 1, x - 1] + assert root_factors(x**2 - y, x) == [x - sqrt(y), x + sqrt(y)] + + assert root_factors((x**4 - 1)**2) == \ + [x + 1, x + 1, x - 1, x - 1, x - I, x - I, x + I, x + I] + + assert root_factors(Poly(x**4 - 1, x), filter='Z') == \ + [Poly(x + 1, x), Poly(x - 1, x), Poly(x**2 + 1, x)] + assert root_factors(8*x**2 + 12*x**4 + 6*x**6 + x**8, x, filter='Q') == \ + [x, x, x**6 + 6*x**4 + 12*x**2 + 8] + + +@slow +def test_nroots1(): + n = 64 + p = legendre_poly(n, x, polys=True) + + raises(mpmath.mp.NoConvergence, lambda: p.nroots(n=3, maxsteps=5)) + + roots = p.nroots(n=3) + # The order of roots matters. They are ordered from smallest to the + # largest. + assert [str(r) for r in roots] == \ + ['-0.999', '-0.996', '-0.991', '-0.983', '-0.973', '-0.961', + '-0.946', '-0.930', '-0.911', '-0.889', '-0.866', '-0.841', + '-0.813', '-0.784', '-0.753', '-0.720', '-0.685', '-0.649', + '-0.611', '-0.572', '-0.531', '-0.489', '-0.446', '-0.402', + '-0.357', '-0.311', '-0.265', '-0.217', '-0.170', '-0.121', + '-0.0730', '-0.0243', '0.0243', '0.0730', '0.121', '0.170', + '0.217', '0.265', '0.311', '0.357', '0.402', '0.446', '0.489', + '0.531', '0.572', '0.611', '0.649', '0.685', '0.720', '0.753', + '0.784', '0.813', '0.841', '0.866', '0.889', '0.911', '0.930', + '0.946', '0.961', '0.973', '0.983', '0.991', '0.996', '0.999'] + +def test_nroots2(): + p = Poly(x**5 + 3*x + 1, x) + + roots = p.nroots(n=3) + # The order of roots matters. The roots are ordered by their real + # components (if they agree, then by their imaginary components), + # with real roots appearing first. + assert [str(r) for r in roots] == \ + ['-0.332', '-0.839 - 0.944*I', '-0.839 + 0.944*I', + '1.01 - 0.937*I', '1.01 + 0.937*I'] + + roots = p.nroots(n=5) + assert [str(r) for r in roots] == \ + ['-0.33199', '-0.83907 - 0.94385*I', '-0.83907 + 0.94385*I', + '1.0051 - 0.93726*I', '1.0051 + 0.93726*I'] + + +def test_roots_composite(): + assert len(roots(Poly(y**3 + y**2*sqrt(x) + y + x, y, composite=True))) == 3 + + +def test_issue_19113(): + eq = cos(x)**3 - cos(x) + 1 + raises(PolynomialError, lambda: roots(eq)) + + +def test_issue_17454(): + assert roots([1, -3*(-4 - 4*I)**2/8 + 12*I, 0], multiple=True) == [0, 0] + + +def test_issue_20913(): + assert Poly(x + 9671406556917067856609794, x).real_roots() == [-9671406556917067856609794] + assert Poly(x**3 + 4, x).real_roots() == [-2**(S(2)/3)] + + +def test_issue_22768(): + e = Rational(1, 3) + r = (-1/a)**e*(a + 1)**(5*e) + assert roots(Poly(a*x**3 + (a + 1)**5, x)) == { + r: 1, + -r*(1 + sqrt(3)*I)/2: 1, + r*(-1 + sqrt(3)*I)/2: 1} diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_polytools.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_polytools.py new file mode 100644 index 0000000000000000000000000000000000000000..a4096447cecea9db6e7559c305af6312b2a72725 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_polytools.py @@ -0,0 +1,3976 @@ +"""Tests for user-friendly public interface to polynomial functions. """ + +import pickle + +from sympy.polys.polytools import ( + Poly, PurePoly, poly, + parallel_poly_from_expr, + degree, degree_list, + total_degree, + LC, LM, LT, + pdiv, prem, pquo, pexquo, + div, rem, quo, exquo, + half_gcdex, gcdex, invert, + subresultants, + resultant, discriminant, + terms_gcd, cofactors, + gcd, gcd_list, + lcm, lcm_list, + trunc, + monic, content, primitive, + compose, decompose, + sturm, + gff_list, gff, + sqf_norm, sqf_part, sqf_list, sqf, + factor_list, factor, + intervals, refine_root, count_roots, + all_roots, real_roots, nroots, ground_roots, + nth_power_roots_poly, + cancel, reduced, groebner, + GroebnerBasis, is_zero_dimensional, + _torational_factor_list, + to_rational_coeffs) + +from sympy.polys.polyerrors import ( + MultivariatePolynomialError, + ExactQuotientFailed, + PolificationFailed, + ComputationFailed, + UnificationFailed, + RefinementFailed, + GeneratorsNeeded, + GeneratorsError, + PolynomialError, + CoercionFailed, + DomainError, + OptionError, + FlagError) + +from sympy.polys.polyclasses import DMP + +from sympy.polys.fields import field +from sympy.polys.domains import FF, ZZ, QQ, ZZ_I, QQ_I, RR, EX +from sympy.polys.domains.realfield import RealField +from sympy.polys.domains.complexfield import ComplexField +from sympy.polys.orderings import lex, grlex, grevlex + +from sympy.combinatorics.galois import S4TransitiveSubgroups +from sympy.core.add import Add +from sympy.core.basic import _aresame +from sympy.core.containers import Tuple +from sympy.core.expr import Expr +from sympy.core.function import (Derivative, diff, expand) +from sympy.core.mul import _keep_coeff, Mul +from sympy.core.numbers import (Float, I, Integer, Rational, oo, pi) +from sympy.core.power import Pow +from sympy.core.relational import Eq +from sympy.core.singleton import S +from sympy.core.symbol import Symbol +from sympy.functions.elementary.complexes import (im, re) +from sympy.functions.elementary.exponential import exp +from sympy.functions.elementary.hyperbolic import tanh +from sympy.functions.elementary.miscellaneous import sqrt +from sympy.functions.elementary.piecewise import Piecewise +from sympy.functions.elementary.trigonometric import sin +from sympy.matrices.dense import Matrix +from sympy.matrices.expressions.matexpr import MatrixSymbol +from sympy.polys.rootoftools import rootof +from sympy.simplify.simplify import signsimp +from sympy.utilities.iterables import iterable +from sympy.utilities.exceptions import SymPyDeprecationWarning + +from sympy.testing.pytest import ( + raises, warns_deprecated_sympy, warns, tooslow, XFAIL +) + +from sympy.abc import a, b, c, d, p, q, t, w, x, y, z + + +def _epsilon_eq(a, b): + for u, v in zip(a, b): + if abs(u - v) > 1e-10: + return False + return True + + +def _strict_eq(a, b): + if type(a) == type(b): + if iterable(a): + if len(a) == len(b): + return all(_strict_eq(c, d) for c, d in zip(a, b)) + else: + return False + else: + return isinstance(a, Poly) and a.eq(b, strict=True) + else: + return False + + +def test_Poly_mixed_operations(): + p = Poly(x, x) + with warns_deprecated_sympy(): + p * exp(x) + with warns_deprecated_sympy(): + p + exp(x) + with warns_deprecated_sympy(): + p - exp(x) + + +def test_Poly_from_dict(): + K = FF(3) + + assert Poly.from_dict( + {0: 1, 1: 2}, gens=x, domain=K).rep == DMP([K(2), K(1)], K) + assert Poly.from_dict( + {0: 1, 1: 5}, gens=x, domain=K).rep == DMP([K(2), K(1)], K) + + assert Poly.from_dict( + {(0,): 1, (1,): 2}, gens=x, domain=K).rep == DMP([K(2), K(1)], K) + assert Poly.from_dict( + {(0,): 1, (1,): 5}, gens=x, domain=K).rep == DMP([K(2), K(1)], K) + + assert Poly.from_dict({(0, 0): 1, (1, 1): 2}, gens=( + x, y), domain=K).rep == DMP([[K(2), K(0)], [K(1)]], K) + + assert Poly.from_dict({0: 1, 1: 2}, gens=x).rep == DMP([ZZ(2), ZZ(1)], ZZ) + assert Poly.from_dict( + {0: 1, 1: 2}, gens=x, field=True).rep == DMP([QQ(2), QQ(1)], QQ) + + assert Poly.from_dict( + {0: 1, 1: 2}, gens=x, domain=ZZ).rep == DMP([ZZ(2), ZZ(1)], ZZ) + assert Poly.from_dict( + {0: 1, 1: 2}, gens=x, domain=QQ).rep == DMP([QQ(2), QQ(1)], QQ) + + assert Poly.from_dict( + {(0,): 1, (1,): 2}, gens=x).rep == DMP([ZZ(2), ZZ(1)], ZZ) + assert Poly.from_dict( + {(0,): 1, (1,): 2}, gens=x, field=True).rep == DMP([QQ(2), QQ(1)], QQ) + + assert Poly.from_dict( + {(0,): 1, (1,): 2}, gens=x, domain=ZZ).rep == DMP([ZZ(2), ZZ(1)], ZZ) + assert Poly.from_dict( + {(0,): 1, (1,): 2}, gens=x, domain=QQ).rep == DMP([QQ(2), QQ(1)], QQ) + + assert Poly.from_dict({(1,): sin(y)}, gens=x, composite=False) == \ + Poly(sin(y)*x, x, domain='EX') + assert Poly.from_dict({(1,): y}, gens=x, composite=False) == \ + Poly(y*x, x, domain='EX') + assert Poly.from_dict({(1, 1): 1}, gens=(x, y), composite=False) == \ + Poly(x*y, x, y, domain='ZZ') + assert Poly.from_dict({(1, 0): y}, gens=(x, z), composite=False) == \ + Poly(y*x, x, z, domain='EX') + + +def test_Poly_from_list(): + K = FF(3) + + assert Poly.from_list([2, 1], gens=x, domain=K).rep == DMP([K(2), K(1)], K) + assert Poly.from_list([5, 1], gens=x, domain=K).rep == DMP([K(2), K(1)], K) + + assert Poly.from_list([2, 1], gens=x).rep == DMP([ZZ(2), ZZ(1)], ZZ) + assert Poly.from_list([2, 1], gens=x, field=True).rep == DMP([QQ(2), QQ(1)], QQ) + + assert Poly.from_list([2, 1], gens=x, domain=ZZ).rep == DMP([ZZ(2), ZZ(1)], ZZ) + assert Poly.from_list([2, 1], gens=x, domain=QQ).rep == DMP([QQ(2), QQ(1)], QQ) + + assert Poly.from_list([0, 1.0], gens=x).rep == DMP([RR(1.0)], RR) + assert Poly.from_list([1.0, 0], gens=x).rep == DMP([RR(1.0), RR(0.0)], RR) + + raises(MultivariatePolynomialError, lambda: Poly.from_list([[]], gens=(x, y))) + + +def test_Poly_from_poly(): + f = Poly(x + 7, x, domain=ZZ) + g = Poly(x + 2, x, modulus=3) + h = Poly(x + y, x, y, domain=ZZ) + + K = FF(3) + + assert Poly.from_poly(f) == f + assert Poly.from_poly(f, domain=K).rep == DMP([K(1), K(1)], K) + assert Poly.from_poly(f, domain=ZZ).rep == DMP([ZZ(1), ZZ(7)], ZZ) + assert Poly.from_poly(f, domain=QQ).rep == DMP([QQ(1), QQ(7)], QQ) + + assert Poly.from_poly(f, gens=x) == f + assert Poly.from_poly(f, gens=x, domain=K).rep == DMP([K(1), K(1)], K) + assert Poly.from_poly(f, gens=x, domain=ZZ).rep == DMP([ZZ(1), ZZ(7)], ZZ) + assert Poly.from_poly(f, gens=x, domain=QQ).rep == DMP([QQ(1), QQ(7)], QQ) + + assert Poly.from_poly(f, gens=y) == Poly(x + 7, y, domain='ZZ[x]') + raises(CoercionFailed, lambda: Poly.from_poly(f, gens=y, domain=K)) + raises(CoercionFailed, lambda: Poly.from_poly(f, gens=y, domain=ZZ)) + raises(CoercionFailed, lambda: Poly.from_poly(f, gens=y, domain=QQ)) + + assert Poly.from_poly(f, gens=(x, y)) == Poly(x + 7, x, y, domain='ZZ') + assert Poly.from_poly( + f, gens=(x, y), domain=ZZ) == Poly(x + 7, x, y, domain='ZZ') + assert Poly.from_poly( + f, gens=(x, y), domain=QQ) == Poly(x + 7, x, y, domain='QQ') + assert Poly.from_poly( + f, gens=(x, y), modulus=3) == Poly(x + 7, x, y, domain='FF(3)') + + K = FF(2) + + assert Poly.from_poly(g) == g + assert Poly.from_poly(g, domain=ZZ).rep == DMP([ZZ(1), ZZ(-1)], ZZ) + raises(CoercionFailed, lambda: Poly.from_poly(g, domain=QQ)) + assert Poly.from_poly(g, domain=K).rep == DMP([K(1), K(0)], K) + + assert Poly.from_poly(g, gens=x) == g + assert Poly.from_poly(g, gens=x, domain=ZZ).rep == DMP([ZZ(1), ZZ(-1)], ZZ) + raises(CoercionFailed, lambda: Poly.from_poly(g, gens=x, domain=QQ)) + assert Poly.from_poly(g, gens=x, domain=K).rep == DMP([K(1), K(0)], K) + + K = FF(3) + + assert Poly.from_poly(h) == h + assert Poly.from_poly( + h, domain=ZZ).rep == DMP([[ZZ(1)], [ZZ(1), ZZ(0)]], ZZ) + assert Poly.from_poly( + h, domain=QQ).rep == DMP([[QQ(1)], [QQ(1), QQ(0)]], QQ) + assert Poly.from_poly(h, domain=K).rep == DMP([[K(1)], [K(1), K(0)]], K) + + assert Poly.from_poly(h, gens=x) == Poly(x + y, x, domain=ZZ[y]) + raises(CoercionFailed, lambda: Poly.from_poly(h, gens=x, domain=ZZ)) + assert Poly.from_poly( + h, gens=x, domain=ZZ[y]) == Poly(x + y, x, domain=ZZ[y]) + raises(CoercionFailed, lambda: Poly.from_poly(h, gens=x, domain=QQ)) + assert Poly.from_poly( + h, gens=x, domain=QQ[y]) == Poly(x + y, x, domain=QQ[y]) + raises(CoercionFailed, lambda: Poly.from_poly(h, gens=x, modulus=3)) + + assert Poly.from_poly(h, gens=y) == Poly(x + y, y, domain=ZZ[x]) + raises(CoercionFailed, lambda: Poly.from_poly(h, gens=y, domain=ZZ)) + assert Poly.from_poly( + h, gens=y, domain=ZZ[x]) == Poly(x + y, y, domain=ZZ[x]) + raises(CoercionFailed, lambda: Poly.from_poly(h, gens=y, domain=QQ)) + assert Poly.from_poly( + h, gens=y, domain=QQ[x]) == Poly(x + y, y, domain=QQ[x]) + raises(CoercionFailed, lambda: Poly.from_poly(h, gens=y, modulus=3)) + + assert Poly.from_poly(h, gens=(x, y)) == h + assert Poly.from_poly( + h, gens=(x, y), domain=ZZ).rep == DMP([[ZZ(1)], [ZZ(1), ZZ(0)]], ZZ) + assert Poly.from_poly( + h, gens=(x, y), domain=QQ).rep == DMP([[QQ(1)], [QQ(1), QQ(0)]], QQ) + assert Poly.from_poly( + h, gens=(x, y), domain=K).rep == DMP([[K(1)], [K(1), K(0)]], K) + + assert Poly.from_poly( + h, gens=(y, x)).rep == DMP([[ZZ(1)], [ZZ(1), ZZ(0)]], ZZ) + assert Poly.from_poly( + h, gens=(y, x), domain=ZZ).rep == DMP([[ZZ(1)], [ZZ(1), ZZ(0)]], ZZ) + assert Poly.from_poly( + h, gens=(y, x), domain=QQ).rep == DMP([[QQ(1)], [QQ(1), QQ(0)]], QQ) + assert Poly.from_poly( + h, gens=(y, x), domain=K).rep == DMP([[K(1)], [K(1), K(0)]], K) + + assert Poly.from_poly( + h, gens=(x, y), field=True).rep == DMP([[QQ(1)], [QQ(1), QQ(0)]], QQ) + assert Poly.from_poly( + h, gens=(x, y), field=True).rep == DMP([[QQ(1)], [QQ(1), QQ(0)]], QQ) + + +def test_Poly_from_expr(): + raises(GeneratorsNeeded, lambda: Poly.from_expr(S.Zero)) + raises(GeneratorsNeeded, lambda: Poly.from_expr(S(7))) + + F3 = FF(3) + + assert Poly.from_expr(x + 5, domain=F3).rep == DMP([F3(1), F3(2)], F3) + assert Poly.from_expr(y + 5, domain=F3).rep == DMP([F3(1), F3(2)], F3) + + assert Poly.from_expr(x + 5, x, domain=F3).rep == DMP([F3(1), F3(2)], F3) + assert Poly.from_expr(y + 5, y, domain=F3).rep == DMP([F3(1), F3(2)], F3) + + assert Poly.from_expr(x + y, domain=F3).rep == DMP([[F3(1)], [F3(1), F3(0)]], F3) + assert Poly.from_expr(x + y, x, y, domain=F3).rep == DMP([[F3(1)], [F3(1), F3(0)]], F3) + + assert Poly.from_expr(x + 5).rep == DMP([ZZ(1), ZZ(5)], ZZ) + assert Poly.from_expr(y + 5).rep == DMP([ZZ(1), ZZ(5)], ZZ) + + assert Poly.from_expr(x + 5, x).rep == DMP([ZZ(1), ZZ(5)], ZZ) + assert Poly.from_expr(y + 5, y).rep == DMP([ZZ(1), ZZ(5)], ZZ) + + assert Poly.from_expr(x + 5, domain=ZZ).rep == DMP([ZZ(1), ZZ(5)], ZZ) + assert Poly.from_expr(y + 5, domain=ZZ).rep == DMP([ZZ(1), ZZ(5)], ZZ) + + assert Poly.from_expr(x + 5, x, domain=ZZ).rep == DMP([ZZ(1), ZZ(5)], ZZ) + assert Poly.from_expr(y + 5, y, domain=ZZ).rep == DMP([ZZ(1), ZZ(5)], ZZ) + + assert Poly.from_expr(x + 5, x, y, domain=ZZ).rep == DMP([[ZZ(1)], [ZZ(5)]], ZZ) + assert Poly.from_expr(y + 5, x, y, domain=ZZ).rep == DMP([[ZZ(1), ZZ(5)]], ZZ) + + +def test_Poly_rootof_extension(): + r1 = rootof(x**3 + x + 3, 0) + r2 = rootof(x**3 + x + 3, 1) + K1 = QQ.algebraic_field(r1) + K2 = QQ.algebraic_field(r2) + assert Poly(r1, y) == Poly(r1, y, domain=EX) + assert Poly(r2, y) == Poly(r2, y, domain=EX) + assert Poly(r1, y, extension=True) == Poly(r1, y, domain=K1) + assert Poly(r2, y, extension=True) == Poly(r2, y, domain=K2) + + +@tooslow +def test_Poly_rootof_extension_primitive_element(): + r1 = rootof(x**3 + x + 3, 0) + r2 = rootof(x**3 + x + 3, 1) + K12 = QQ.algebraic_field(r1 + r2) + assert Poly(r1*y + r2, y, extension=True) == Poly(r1*y + r2, y, domain=K12) + + +@XFAIL +def test_Poly_rootof_same_symbol_issue_26808(): + # XXX: This fails because r1 contains x. + r1 = rootof(x**3 + x + 3, 0) + K1 = QQ.algebraic_field(r1) + assert Poly(r1, x) == Poly(r1, x, domain=EX) + assert Poly(r1, x, extension=True) == Poly(r1, x, domain=K1) + + +def test_Poly_rootof_extension_to_sympy(): + # Verify that when primitive elements and RootOf are used, the expression + # is not exploded on the way back to sympy. + r1 = rootof(y**3 + y**2 - 1, 0) + r2 = rootof(z**5 + z**2 - 1, 0) + p = -x**5 + x**2 + x*r1 - r2 + 3*r1**2 + assert p.as_poly(x, extension=True).as_expr() == p + + +def test_poly_from_domain_element(): + dom = ZZ[x] + assert Poly(dom(x+1), y, domain=dom).rep == DMP([dom(x+1)], dom) + dom = dom.get_field() + assert Poly(dom(x+1), y, domain=dom).rep == DMP([dom(x+1)], dom) + + dom = QQ[x] + assert Poly(dom(x+1), y, domain=dom).rep == DMP([dom(x+1)], dom) + dom = dom.get_field() + assert Poly(dom(x+1), y, domain=dom).rep == DMP([dom(x+1)], dom) + + dom = ZZ.old_poly_ring(x) + assert Poly(dom([ZZ(1), ZZ(1)]), y, domain=dom).rep == DMP([dom([ZZ(1), ZZ(1)])], dom) + dom = dom.get_field() + assert Poly(dom([ZZ(1), ZZ(1)]), y, domain=dom).rep == DMP([dom([ZZ(1), ZZ(1)])], dom) + + dom = QQ.old_poly_ring(x) + assert Poly(dom([QQ(1), QQ(1)]), y, domain=dom).rep == DMP([dom([QQ(1), QQ(1)])], dom) + dom = dom.get_field() + assert Poly(dom([QQ(1), QQ(1)]), y, domain=dom).rep == DMP([dom([QQ(1), QQ(1)])], dom) + + dom = QQ.algebraic_field(I) + assert Poly(dom([1, 1]), x, domain=dom).rep == DMP([dom([1, 1])], dom) + + +def test_Poly__new__(): + raises(GeneratorsError, lambda: Poly(x + 1, x, x)) + + raises(GeneratorsError, lambda: Poly(x + y, x, y, domain=ZZ[x])) + raises(GeneratorsError, lambda: Poly(x + y, x, y, domain=ZZ[y])) + + raises(OptionError, lambda: Poly(x, x, symmetric=True)) + raises(OptionError, lambda: Poly(x + 2, x, modulus=3, domain=QQ)) + + raises(OptionError, lambda: Poly(x + 2, x, domain=ZZ, gaussian=True)) + raises(OptionError, lambda: Poly(x + 2, x, modulus=3, gaussian=True)) + + raises(OptionError, lambda: Poly(x + 2, x, domain=ZZ, extension=[sqrt(3)])) + raises(OptionError, lambda: Poly(x + 2, x, modulus=3, extension=[sqrt(3)])) + + raises(OptionError, lambda: Poly(x + 2, x, domain=ZZ, extension=True)) + raises(OptionError, lambda: Poly(x + 2, x, modulus=3, extension=True)) + + raises(OptionError, lambda: Poly(x + 2, x, domain=ZZ, greedy=True)) + raises(OptionError, lambda: Poly(x + 2, x, domain=QQ, field=True)) + + raises(OptionError, lambda: Poly(x + 2, x, domain=ZZ, greedy=False)) + raises(OptionError, lambda: Poly(x + 2, x, domain=QQ, field=False)) + + raises(NotImplementedError, lambda: Poly(x + 1, x, modulus=3, order='grlex')) + raises(NotImplementedError, lambda: Poly(x + 1, x, order='grlex')) + + raises(GeneratorsNeeded, lambda: Poly({1: 2, 0: 1})) + raises(GeneratorsNeeded, lambda: Poly([2, 1])) + raises(GeneratorsNeeded, lambda: Poly((2, 1))) + + raises(GeneratorsNeeded, lambda: Poly(1)) + + assert Poly('x-x') == Poly(0, x) + + f = a*x**2 + b*x + c + + assert Poly({2: a, 1: b, 0: c}, x) == f + assert Poly(iter([a, b, c]), x) == f + assert Poly([a, b, c], x) == f + assert Poly((a, b, c), x) == f + + f = Poly({}, x, y, z) + + assert f.gens == (x, y, z) and f.as_expr() == 0 + + assert Poly(Poly(a*x + b*y, x, y), x) == Poly(a*x + b*y, x) + + assert Poly(3*x**2 + 2*x + 1, domain='ZZ').all_coeffs() == [3, 2, 1] + assert Poly(3*x**2 + 2*x + 1, domain='QQ').all_coeffs() == [3, 2, 1] + assert Poly(3*x**2 + 2*x + 1, domain='RR').all_coeffs() == [3.0, 2.0, 1.0] + + raises(CoercionFailed, lambda: Poly(3*x**2/5 + x*Rational(2, 5) + 1, domain='ZZ')) + assert Poly( + 3*x**2/5 + x*Rational(2, 5) + 1, domain='QQ').all_coeffs() == [Rational(3, 5), Rational(2, 5), 1] + assert _epsilon_eq( + Poly(3*x**2/5 + x*Rational(2, 5) + 1, domain='RR').all_coeffs(), [0.6, 0.4, 1.0]) + + assert Poly(3.0*x**2 + 2.0*x + 1, domain='ZZ').all_coeffs() == [3, 2, 1] + assert Poly(3.0*x**2 + 2.0*x + 1, domain='QQ').all_coeffs() == [3, 2, 1] + assert Poly( + 3.0*x**2 + 2.0*x + 1, domain='RR').all_coeffs() == [3.0, 2.0, 1.0] + + raises(CoercionFailed, lambda: Poly(3.1*x**2 + 2.1*x + 1, domain='ZZ')) + assert Poly(3.1*x**2 + 2.1*x + 1, domain='QQ').all_coeffs() == [Rational(31, 10), Rational(21, 10), 1] + assert Poly(3.1*x**2 + 2.1*x + 1, domain='RR').all_coeffs() == [3.1, 2.1, 1.0] + + assert Poly({(2, 1): 1, (1, 2): 2, (1, 1): 3}, x, y) == \ + Poly(x**2*y + 2*x*y**2 + 3*x*y, x, y) + + assert Poly(x**2 + 1, extension=I).get_domain() == QQ.algebraic_field(I) + + f = 3*x**5 - x**4 + x**3 - x** 2 + 65538 + + assert Poly(f, x, modulus=65537, symmetric=True) == \ + Poly(3*x**5 - x**4 + x**3 - x** 2 + 1, x, modulus=65537, + symmetric=True) + assert Poly(f, x, modulus=65537, symmetric=False) == \ + Poly(3*x**5 + 65536*x**4 + x**3 + 65536*x** 2 + 1, x, + modulus=65537, symmetric=False) + + N = 10**100 + assert Poly(-1, x, modulus=N, symmetric=False).as_expr() == N - 1 + + assert isinstance(Poly(x**2 + x + 1.0).get_domain(), RealField) + assert isinstance(Poly(x**2 + x + I + 1.0).get_domain(), ComplexField) + + +def test_Poly__args(): + assert Poly(x**2 + 1).args == (x**2 + 1, x) + + +def test_Poly__gens(): + assert Poly((x - p)*(x - q), x).gens == (x,) + assert Poly((x - p)*(x - q), p).gens == (p,) + assert Poly((x - p)*(x - q), q).gens == (q,) + + assert Poly((x - p)*(x - q), x, p).gens == (x, p) + assert Poly((x - p)*(x - q), x, q).gens == (x, q) + + assert Poly((x - p)*(x - q), x, p, q).gens == (x, p, q) + assert Poly((x - p)*(x - q), p, x, q).gens == (p, x, q) + assert Poly((x - p)*(x - q), p, q, x).gens == (p, q, x) + + assert Poly((x - p)*(x - q)).gens == (x, p, q) + + assert Poly((x - p)*(x - q), sort='x > p > q').gens == (x, p, q) + assert Poly((x - p)*(x - q), sort='p > x > q').gens == (p, x, q) + assert Poly((x - p)*(x - q), sort='p > q > x').gens == (p, q, x) + + assert Poly((x - p)*(x - q), x, p, q, sort='p > q > x').gens == (x, p, q) + + assert Poly((x - p)*(x - q), wrt='x').gens == (x, p, q) + assert Poly((x - p)*(x - q), wrt='p').gens == (p, x, q) + assert Poly((x - p)*(x - q), wrt='q').gens == (q, x, p) + + assert Poly((x - p)*(x - q), wrt=x).gens == (x, p, q) + assert Poly((x - p)*(x - q), wrt=p).gens == (p, x, q) + assert Poly((x - p)*(x - q), wrt=q).gens == (q, x, p) + + assert Poly((x - p)*(x - q), x, p, q, wrt='p').gens == (x, p, q) + + assert Poly((x - p)*(x - q), wrt='p', sort='q > x').gens == (p, q, x) + assert Poly((x - p)*(x - q), wrt='q', sort='p > x').gens == (q, p, x) + + +def test_Poly_zero(): + assert Poly(x).zero == Poly(0, x, domain=ZZ) + assert Poly(x/2).zero == Poly(0, x, domain=QQ) + + +def test_Poly_one(): + assert Poly(x).one == Poly(1, x, domain=ZZ) + assert Poly(x/2).one == Poly(1, x, domain=QQ) + + +def test_Poly__unify(): + raises(UnificationFailed, lambda: Poly(x)._unify(y)) + + F3 = FF(3) + + assert Poly(x, x, modulus=3)._unify(Poly(y, y, modulus=3))[2:] == ( + DMP([[F3(1)], []], F3), DMP([[F3(1), F3(0)]], F3)) + raises(UnificationFailed, lambda: Poly(x, x, modulus=3)._unify(Poly(y, y, modulus=5))) + + raises(UnificationFailed, lambda: Poly(y, x, y)._unify(Poly(x, x, modulus=3))) + raises(UnificationFailed, lambda: Poly(x, x, modulus=3)._unify(Poly(y, x, y))) + + assert Poly(x + 1, x)._unify(Poly(x + 2, x))[2:] ==\ + (DMP([ZZ(1), ZZ(1)], ZZ), DMP([ZZ(1), ZZ(2)], ZZ)) + assert Poly(x + 1, x, domain='QQ')._unify(Poly(x + 2, x))[2:] ==\ + (DMP([QQ(1), QQ(1)], QQ), DMP([QQ(1), QQ(2)], QQ)) + assert Poly(x + 1, x)._unify(Poly(x + 2, x, domain='QQ'))[2:] ==\ + (DMP([QQ(1), QQ(1)], QQ), DMP([QQ(1), QQ(2)], QQ)) + + assert Poly(x + 1, x)._unify(Poly(x + 2, x, y))[2:] ==\ + (DMP([[ZZ(1)], [ZZ(1)]], ZZ), DMP([[ZZ(1)], [ZZ(2)]], ZZ)) + assert Poly(x + 1, x, domain='QQ')._unify(Poly(x + 2, x, y))[2:] ==\ + (DMP([[QQ(1)], [QQ(1)]], QQ), DMP([[QQ(1)], [QQ(2)]], QQ)) + assert Poly(x + 1, x)._unify(Poly(x + 2, x, y, domain='QQ'))[2:] ==\ + (DMP([[QQ(1)], [QQ(1)]], QQ), DMP([[QQ(1)], [QQ(2)]], QQ)) + + assert Poly(x + 1, x, y)._unify(Poly(x + 2, x))[2:] ==\ + (DMP([[ZZ(1)], [ZZ(1)]], ZZ), DMP([[ZZ(1)], [ZZ(2)]], ZZ)) + assert Poly(x + 1, x, y, domain='QQ')._unify(Poly(x + 2, x))[2:] ==\ + (DMP([[QQ(1)], [QQ(1)]], QQ), DMP([[QQ(1)], [QQ(2)]], QQ)) + assert Poly(x + 1, x, y)._unify(Poly(x + 2, x, domain='QQ'))[2:] ==\ + (DMP([[QQ(1)], [QQ(1)]], QQ), DMP([[QQ(1)], [QQ(2)]], QQ)) + + assert Poly(x + 1, x, y)._unify(Poly(x + 2, x, y))[2:] ==\ + (DMP([[ZZ(1)], [ZZ(1)]], ZZ), DMP([[ZZ(1)], [ZZ(2)]], ZZ)) + assert Poly(x + 1, x, y, domain='QQ')._unify(Poly(x + 2, x, y))[2:] ==\ + (DMP([[QQ(1)], [QQ(1)]], QQ), DMP([[QQ(1)], [QQ(2)]], QQ)) + assert Poly(x + 1, x, y)._unify(Poly(x + 2, x, y, domain='QQ'))[2:] ==\ + (DMP([[QQ(1)], [QQ(1)]], QQ), DMP([[QQ(1)], [QQ(2)]], QQ)) + + assert Poly(x + 1, x)._unify(Poly(x + 2, y, x))[2:] ==\ + (DMP([[ZZ(1), ZZ(1)]], ZZ), DMP([[ZZ(1), ZZ(2)]], ZZ)) + assert Poly(x + 1, x, domain='QQ')._unify(Poly(x + 2, y, x))[2:] ==\ + (DMP([[QQ(1), QQ(1)]], QQ), DMP([[QQ(1), QQ(2)]], QQ)) + assert Poly(x + 1, x)._unify(Poly(x + 2, y, x, domain='QQ'))[2:] ==\ + (DMP([[QQ(1), QQ(1)]], QQ), DMP([[QQ(1), QQ(2)]], QQ)) + + assert Poly(x + 1, y, x)._unify(Poly(x + 2, x))[2:] ==\ + (DMP([[ZZ(1), ZZ(1)]], ZZ), DMP([[ZZ(1), ZZ(2)]], ZZ)) + assert Poly(x + 1, y, x, domain='QQ')._unify(Poly(x + 2, x))[2:] ==\ + (DMP([[QQ(1), QQ(1)]], QQ), DMP([[QQ(1), QQ(2)]], QQ)) + assert Poly(x + 1, y, x)._unify(Poly(x + 2, x, domain='QQ'))[2:] ==\ + (DMP([[QQ(1), QQ(1)]], QQ), DMP([[QQ(1), QQ(2)]], QQ)) + + assert Poly(x + 1, x, y)._unify(Poly(x + 2, y, x))[2:] ==\ + (DMP([[ZZ(1)], [ZZ(1)]], ZZ), DMP([[ZZ(1)], [ZZ(2)]], ZZ)) + assert Poly(x + 1, x, y, domain='QQ')._unify(Poly(x + 2, y, x))[2:] ==\ + (DMP([[QQ(1)], [QQ(1)]], QQ), DMP([[QQ(1)], [QQ(2)]], QQ)) + assert Poly(x + 1, x, y)._unify(Poly(x + 2, y, x, domain='QQ'))[2:] ==\ + (DMP([[QQ(1)], [QQ(1)]], QQ), DMP([[QQ(1)], [QQ(2)]], QQ)) + + assert Poly(x + 1, y, x)._unify(Poly(x + 2, x, y))[2:] ==\ + (DMP([[ZZ(1), ZZ(1)]], ZZ), DMP([[ZZ(1), ZZ(2)]], ZZ)) + assert Poly(x + 1, y, x, domain='QQ')._unify(Poly(x + 2, x, y))[2:] ==\ + (DMP([[QQ(1), QQ(1)]], QQ), DMP([[QQ(1), QQ(2)]], QQ)) + assert Poly(x + 1, y, x)._unify(Poly(x + 2, x, y, domain='QQ'))[2:] ==\ + (DMP([[QQ(1), QQ(1)]], QQ), DMP([[QQ(1), QQ(2)]], QQ)) + + assert Poly(x**2 + I, x, domain=ZZ_I).unify(Poly(x**2 + sqrt(2), x, extension=True)) == \ + (Poly(x**2 + I, x, domain='QQ'), Poly(x**2 + sqrt(2), x, domain='QQ')) + + F, A, B = field("a,b", ZZ) + + assert Poly(a*x, x, domain='ZZ[a]')._unify(Poly(a*b*x, x, domain='ZZ(a,b)'))[2:] == \ + (DMP([A, F(0)], F.to_domain()), DMP([A*B, F(0)], F.to_domain())) + + assert Poly(a*x, x, domain='ZZ(a)')._unify(Poly(a*b*x, x, domain='ZZ(a,b)'))[2:] == \ + (DMP([A, F(0)], F.to_domain()), DMP([A*B, F(0)], F.to_domain())) + + raises(CoercionFailed, lambda: Poly(Poly(x**2 + x**2*z, y, field=True), domain='ZZ(x)')) + + f = Poly(t**2 + t/3 + x, t, domain='QQ(x)') + g = Poly(t**2 + t/3 + x, t, domain='QQ[x]') + + assert f._unify(g)[2:] == (f.rep, f.rep) + + +def test_Poly_free_symbols(): + assert Poly(x**2 + 1).free_symbols == {x} + assert Poly(x**2 + y*z).free_symbols == {x, y, z} + assert Poly(x**2 + y*z, x).free_symbols == {x, y, z} + assert Poly(x**2 + sin(y*z)).free_symbols == {x, y, z} + assert Poly(x**2 + sin(y*z), x).free_symbols == {x, y, z} + assert Poly(x**2 + sin(y*z), x, domain=EX).free_symbols == {x, y, z} + assert Poly(1 + x + x**2, x, y, z).free_symbols == {x} + assert Poly(x + sin(y), z).free_symbols == {x, y} + + +def test_PurePoly_free_symbols(): + assert PurePoly(x**2 + 1).free_symbols == set() + assert PurePoly(x**2 + y*z).free_symbols == set() + assert PurePoly(x**2 + y*z, x).free_symbols == {y, z} + assert PurePoly(x**2 + sin(y*z)).free_symbols == set() + assert PurePoly(x**2 + sin(y*z), x).free_symbols == {y, z} + assert PurePoly(x**2 + sin(y*z), x, domain=EX).free_symbols == {y, z} + + +def test_Poly__eq__(): + assert (Poly(x, x) == Poly(x, x)) is True + assert (Poly(x, x, domain=QQ) == Poly(x, x)) is False + assert (Poly(x, x) == Poly(x, x, domain=QQ)) is False + + assert (Poly(x, x, domain=ZZ[a]) == Poly(x, x)) is False + assert (Poly(x, x) == Poly(x, x, domain=ZZ[a])) is False + + assert (Poly(x*y, x, y) == Poly(x, x)) is False + + assert (Poly(x, x, y) == Poly(x, x)) is False + assert (Poly(x, x) == Poly(x, x, y)) is False + + assert (Poly(x**2 + 1, x) == Poly(y**2 + 1, y)) is False + assert (Poly(y**2 + 1, y) == Poly(x**2 + 1, x)) is False + + f = Poly(x, x, domain=ZZ) + g = Poly(x, x, domain=QQ) + + assert f.eq(g) is False + assert f.ne(g) is True + + assert f.eq(g, strict=True) is False + assert f.ne(g, strict=True) is True + + t0 = Symbol('t0') + + f = Poly((t0/2 + x**2)*t**2 - x**2*t, t, domain='QQ[x,t0]') + g = Poly((t0/2 + x**2)*t**2 - x**2*t, t, domain='ZZ(x,t0)') + + assert (f == g) is False + + +def test_PurePoly__eq__(): + assert (PurePoly(x, x) == PurePoly(x, x)) is True + assert (PurePoly(x, x, domain=QQ) == PurePoly(x, x)) is True + assert (PurePoly(x, x) == PurePoly(x, x, domain=QQ)) is True + + assert (PurePoly(x, x, domain=ZZ[a]) == PurePoly(x, x)) is True + assert (PurePoly(x, x) == PurePoly(x, x, domain=ZZ[a])) is True + + assert (PurePoly(x*y, x, y) == PurePoly(x, x)) is False + + assert (PurePoly(x, x, y) == PurePoly(x, x)) is False + assert (PurePoly(x, x) == PurePoly(x, x, y)) is False + + assert (PurePoly(x**2 + 1, x) == PurePoly(y**2 + 1, y)) is True + assert (PurePoly(y**2 + 1, y) == PurePoly(x**2 + 1, x)) is True + + f = PurePoly(x, x, domain=ZZ) + g = PurePoly(x, x, domain=QQ) + + assert f.eq(g) is True + assert f.ne(g) is False + + assert f.eq(g, strict=True) is False + assert f.ne(g, strict=True) is True + + f = PurePoly(x, x, domain=ZZ) + g = PurePoly(y, y, domain=QQ) + + assert f.eq(g) is True + assert f.ne(g) is False + + assert f.eq(g, strict=True) is False + assert f.ne(g, strict=True) is True + + +def test_PurePoly_Poly(): + assert isinstance(PurePoly(Poly(x**2 + 1)), PurePoly) is True + assert isinstance(Poly(PurePoly(x**2 + 1)), Poly) is True + + +def test_Poly_get_domain(): + assert Poly(2*x).get_domain() == ZZ + + assert Poly(2*x, domain='ZZ').get_domain() == ZZ + assert Poly(2*x, domain='QQ').get_domain() == QQ + + assert Poly(x/2).get_domain() == QQ + + raises(CoercionFailed, lambda: Poly(x/2, domain='ZZ')) + assert Poly(x/2, domain='QQ').get_domain() == QQ + + assert isinstance(Poly(0.2*x).get_domain(), RealField) + + +def test_Poly_set_domain(): + assert Poly(2*x + 1).set_domain(ZZ) == Poly(2*x + 1) + assert Poly(2*x + 1).set_domain('ZZ') == Poly(2*x + 1) + + assert Poly(2*x + 1).set_domain(QQ) == Poly(2*x + 1, domain='QQ') + assert Poly(2*x + 1).set_domain('QQ') == Poly(2*x + 1, domain='QQ') + + assert Poly(Rational(2, 10)*x + Rational(1, 10)).set_domain('RR') == Poly(0.2*x + 0.1) + assert Poly(0.2*x + 0.1).set_domain('QQ') == Poly(Rational(2, 10)*x + Rational(1, 10)) + + raises(CoercionFailed, lambda: Poly(x/2 + 1).set_domain(ZZ)) + raises(CoercionFailed, lambda: Poly(x + 1, modulus=2).set_domain(QQ)) + + raises(GeneratorsError, lambda: Poly(x*y, x, y).set_domain(ZZ[y])) + + +def test_Poly_get_modulus(): + assert Poly(x**2 + 1, modulus=2).get_modulus() == 2 + raises(PolynomialError, lambda: Poly(x**2 + 1).get_modulus()) + + +def test_Poly_set_modulus(): + assert Poly( + x**2 + 1, modulus=2).set_modulus(7) == Poly(x**2 + 1, modulus=7) + assert Poly( + x**2 + 5, modulus=7).set_modulus(2) == Poly(x**2 + 1, modulus=2) + + assert Poly(x**2 + 1).set_modulus(2) == Poly(x**2 + 1, modulus=2) + + raises(CoercionFailed, lambda: Poly(x/2 + 1).set_modulus(2)) + + +def test_Poly_add_ground(): + assert Poly(x + 1).add_ground(2) == Poly(x + 3) + + +def test_Poly_sub_ground(): + assert Poly(x + 1).sub_ground(2) == Poly(x - 1) + + +def test_Poly_mul_ground(): + assert Poly(x + 1).mul_ground(2) == Poly(2*x + 2) + + +def test_Poly_quo_ground(): + assert Poly(2*x + 4).quo_ground(2) == Poly(x + 2) + assert Poly(2*x + 3).quo_ground(2) == Poly(x + 1) + + +def test_Poly_exquo_ground(): + assert Poly(2*x + 4).exquo_ground(2) == Poly(x + 2) + raises(ExactQuotientFailed, lambda: Poly(2*x + 3).exquo_ground(2)) + + +def test_Poly_abs(): + assert Poly(-x + 1, x).abs() == abs(Poly(-x + 1, x)) == Poly(x + 1, x) + + +def test_Poly_neg(): + assert Poly(-x + 1, x).neg() == -Poly(-x + 1, x) == Poly(x - 1, x) + + +def test_Poly_add(): + assert Poly(0, x).add(Poly(0, x)) == Poly(0, x) + assert Poly(0, x) + Poly(0, x) == Poly(0, x) + + assert Poly(1, x).add(Poly(0, x)) == Poly(1, x) + assert Poly(1, x, y) + Poly(0, x) == Poly(1, x, y) + assert Poly(0, x).add(Poly(1, x, y)) == Poly(1, x, y) + assert Poly(0, x, y) + Poly(1, x, y) == Poly(1, x, y) + + assert Poly(1, x) + x == Poly(x + 1, x) + with warns_deprecated_sympy(): + Poly(1, x) + sin(x) + + assert Poly(x, x) + 1 == Poly(x + 1, x) + assert 1 + Poly(x, x) == Poly(x + 1, x) + + +def test_Poly_sub(): + assert Poly(0, x).sub(Poly(0, x)) == Poly(0, x) + assert Poly(0, x) - Poly(0, x) == Poly(0, x) + + assert Poly(1, x).sub(Poly(0, x)) == Poly(1, x) + assert Poly(1, x, y) - Poly(0, x) == Poly(1, x, y) + assert Poly(0, x).sub(Poly(1, x, y)) == Poly(-1, x, y) + assert Poly(0, x, y) - Poly(1, x, y) == Poly(-1, x, y) + + assert Poly(1, x) - x == Poly(1 - x, x) + with warns_deprecated_sympy(): + Poly(1, x) - sin(x) + + assert Poly(x, x) - 1 == Poly(x - 1, x) + assert 1 - Poly(x, x) == Poly(1 - x, x) + + +def test_Poly_mul(): + assert Poly(0, x).mul(Poly(0, x)) == Poly(0, x) + assert Poly(0, x) * Poly(0, x) == Poly(0, x) + + assert Poly(2, x).mul(Poly(4, x)) == Poly(8, x) + assert Poly(2, x, y) * Poly(4, x) == Poly(8, x, y) + assert Poly(4, x).mul(Poly(2, x, y)) == Poly(8, x, y) + assert Poly(4, x, y) * Poly(2, x, y) == Poly(8, x, y) + + assert Poly(1, x) * x == Poly(x, x) + with warns_deprecated_sympy(): + Poly(1, x) * sin(x) + + assert Poly(x, x) * 2 == Poly(2*x, x) + assert 2 * Poly(x, x) == Poly(2*x, x) + +def test_issue_13079(): + assert Poly(x)*x == Poly(x**2, x, domain='ZZ') + assert x*Poly(x) == Poly(x**2, x, domain='ZZ') + assert -2*Poly(x) == Poly(-2*x, x, domain='ZZ') + assert S(-2)*Poly(x) == Poly(-2*x, x, domain='ZZ') + assert Poly(x)*S(-2) == Poly(-2*x, x, domain='ZZ') + +def test_Poly_sqr(): + assert Poly(x*y, x, y).sqr() == Poly(x**2*y**2, x, y) + + +def test_Poly_pow(): + assert Poly(x, x).pow(10) == Poly(x**10, x) + assert Poly(x, x).pow(Integer(10)) == Poly(x**10, x) + + assert Poly(2*y, x, y).pow(4) == Poly(16*y**4, x, y) + assert Poly(2*y, x, y).pow(Integer(4)) == Poly(16*y**4, x, y) + + assert Poly(7*x*y, x, y)**3 == Poly(343*x**3*y**3, x, y) + + raises(TypeError, lambda: Poly(x*y + 1, x, y)**(-1)) + raises(TypeError, lambda: Poly(x*y + 1, x, y)**x) + + +def test_Poly_divmod(): + f, g = Poly(x**2), Poly(x) + q, r = g, Poly(0, x) + + assert divmod(f, g) == (q, r) + assert f // g == q + assert f % g == r + + assert divmod(f, x) == (q, r) + assert f // x == q + assert f % x == r + + q, r = Poly(0, x), Poly(2, x) + + assert divmod(2, g) == (q, r) + assert 2 // g == q + assert 2 % g == r + + assert Poly(x)/Poly(x) == 1 + assert Poly(x**2)/Poly(x) == x + assert Poly(x)/Poly(x**2) == 1/x + + +def test_Poly_eq_ne(): + assert (Poly(x + y, x, y) == Poly(x + y, x, y)) is True + assert (Poly(x + y, x) == Poly(x + y, x, y)) is False + assert (Poly(x + y, x, y) == Poly(x + y, x)) is False + assert (Poly(x + y, x) == Poly(x + y, x)) is True + assert (Poly(x + y, y) == Poly(x + y, y)) is True + + assert (Poly(x + y, x, y) == x + y) is True + assert (Poly(x + y, x) == x + y) is True + assert (Poly(x + y, x, y) == x + y) is True + assert (Poly(x + y, x) == x + y) is True + assert (Poly(x + y, y) == x + y) is True + + assert (Poly(x + y, x, y) != Poly(x + y, x, y)) is False + assert (Poly(x + y, x) != Poly(x + y, x, y)) is True + assert (Poly(x + y, x, y) != Poly(x + y, x)) is True + assert (Poly(x + y, x) != Poly(x + y, x)) is False + assert (Poly(x + y, y) != Poly(x + y, y)) is False + + assert (Poly(x + y, x, y) != x + y) is False + assert (Poly(x + y, x) != x + y) is False + assert (Poly(x + y, x, y) != x + y) is False + assert (Poly(x + y, x) != x + y) is False + assert (Poly(x + y, y) != x + y) is False + + assert (Poly(x, x) == sin(x)) is False + assert (Poly(x, x) != sin(x)) is True + + +def test_Poly_nonzero(): + assert not bool(Poly(0, x)) is True + assert not bool(Poly(1, x)) is False + + +def test_Poly_properties(): + assert Poly(0, x).is_zero is True + assert Poly(1, x).is_zero is False + + assert Poly(1, x).is_one is True + assert Poly(2, x).is_one is False + + assert Poly(x - 1, x).is_sqf is True + assert Poly((x - 1)**2, x).is_sqf is False + + assert Poly(x - 1, x).is_monic is True + assert Poly(2*x - 1, x).is_monic is False + + assert Poly(3*x + 2, x).is_primitive is True + assert Poly(4*x + 2, x).is_primitive is False + + assert Poly(1, x).is_ground is True + assert Poly(x, x).is_ground is False + + assert Poly(x + y + z + 1).is_linear is True + assert Poly(x*y*z + 1).is_linear is False + + assert Poly(x*y + z + 1).is_quadratic is True + assert Poly(x*y*z + 1).is_quadratic is False + + assert Poly(x*y).is_monomial is True + assert Poly(x*y + 1).is_monomial is False + + assert Poly(x**2 + x*y).is_homogeneous is True + assert Poly(x**3 + x*y).is_homogeneous is False + + assert Poly(x).is_univariate is True + assert Poly(x*y).is_univariate is False + + assert Poly(x*y).is_multivariate is True + assert Poly(x).is_multivariate is False + + assert Poly( + x**16 + x**14 - x**10 + x**8 - x**6 + x**2 + 1).is_cyclotomic is False + assert Poly( + x**16 + x**14 - x**10 - x**8 - x**6 + x**2 + 1).is_cyclotomic is True + + +def test_Poly_is_irreducible(): + assert Poly(x**2 + x + 1).is_irreducible is True + assert Poly(x**2 + 2*x + 1).is_irreducible is False + + assert Poly(7*x + 3, modulus=11).is_irreducible is True + assert Poly(7*x**2 + 3*x + 1, modulus=11).is_irreducible is False + + +def test_Poly_subs(): + assert Poly(x + 1).subs(x, 0) == 1 + + assert Poly(x + 1).subs(x, x) == Poly(x + 1) + assert Poly(x + 1).subs(x, y) == Poly(y + 1) + + assert Poly(x*y, x).subs(y, x) == x**2 + assert Poly(x*y, x).subs(x, y) == y**2 + + +def test_Poly_replace(): + assert Poly(x + 1).replace(x) == Poly(x + 1) + assert Poly(x + 1).replace(y) == Poly(y + 1) + + raises(PolynomialError, lambda: Poly(x + y).replace(z)) + + assert Poly(x + 1).replace(x, x) == Poly(x + 1) + assert Poly(x + 1).replace(x, y) == Poly(y + 1) + + assert Poly(x + y).replace(x, x) == Poly(x + y) + assert Poly(x + y).replace(x, z) == Poly(z + y, z, y) + + assert Poly(x + y).replace(y, y) == Poly(x + y) + assert Poly(x + y).replace(y, z) == Poly(x + z, x, z) + assert Poly(x + y).replace(z, t) == Poly(x + y) + + raises(PolynomialError, lambda: Poly(x + y).replace(x, y)) + + assert Poly(x + y, x).replace(x, z) == Poly(z + y, z) + assert Poly(x + y, y).replace(y, z) == Poly(x + z, z) + + raises(PolynomialError, lambda: Poly(x + y, x).replace(x, y)) + raises(PolynomialError, lambda: Poly(x + y, y).replace(y, x)) + + +def test_Poly_reorder(): + raises(PolynomialError, lambda: Poly(x + y).reorder(x, z)) + + assert Poly(x + y, x, y).reorder(x, y) == Poly(x + y, x, y) + assert Poly(x + y, x, y).reorder(y, x) == Poly(x + y, y, x) + + assert Poly(x + y, y, x).reorder(x, y) == Poly(x + y, x, y) + assert Poly(x + y, y, x).reorder(y, x) == Poly(x + y, y, x) + + assert Poly(x + y, x, y).reorder(wrt=x) == Poly(x + y, x, y) + assert Poly(x + y, x, y).reorder(wrt=y) == Poly(x + y, y, x) + + +def test_Poly_ltrim(): + f = Poly(y**2 + y*z**2, x, y, z).ltrim(y) + assert f.as_expr() == y**2 + y*z**2 and f.gens == (y, z) + assert Poly(x*y - x, z, x, y).ltrim(1) == Poly(x*y - x, x, y) + + raises(PolynomialError, lambda: Poly(x*y**2 + y**2, x, y).ltrim(y)) + raises(PolynomialError, lambda: Poly(x*y - x, x, y).ltrim(-1)) + +def test_Poly_has_only_gens(): + assert Poly(x*y + 1, x, y, z).has_only_gens(x, y) is True + assert Poly(x*y + z, x, y, z).has_only_gens(x, y) is False + + raises(GeneratorsError, lambda: Poly(x*y**2 + y**2, x, y).has_only_gens(t)) + + +def test_Poly_to_ring(): + assert Poly(2*x + 1, domain='ZZ').to_ring() == Poly(2*x + 1, domain='ZZ') + assert Poly(2*x + 1, domain='QQ').to_ring() == Poly(2*x + 1, domain='ZZ') + + raises(CoercionFailed, lambda: Poly(x/2 + 1).to_ring()) + raises(DomainError, lambda: Poly(2*x + 1, modulus=3).to_ring()) + + +def test_Poly_to_field(): + assert Poly(2*x + 1, domain='ZZ').to_field() == Poly(2*x + 1, domain='QQ') + assert Poly(2*x + 1, domain='QQ').to_field() == Poly(2*x + 1, domain='QQ') + + assert Poly(x/2 + 1, domain='QQ').to_field() == Poly(x/2 + 1, domain='QQ') + assert Poly(2*x + 1, modulus=3).to_field() == Poly(2*x + 1, modulus=3) + + assert Poly(2.0*x + 1.0).to_field() == Poly(2.0*x + 1.0) + + +def test_Poly_to_exact(): + assert Poly(2*x).to_exact() == Poly(2*x) + assert Poly(x/2).to_exact() == Poly(x/2) + + assert Poly(0.1*x).to_exact() == Poly(x/10) + + +def test_Poly_retract(): + f = Poly(x**2 + 1, x, domain=QQ[y]) + + assert f.retract() == Poly(x**2 + 1, x, domain='ZZ') + assert f.retract(field=True) == Poly(x**2 + 1, x, domain='QQ') + + assert Poly(0, x, y).retract() == Poly(0, x, y) + + +def test_Poly_slice(): + f = Poly(x**3 + 2*x**2 + 3*x + 4) + + assert f.slice(0, 0) == Poly(0, x) + assert f.slice(0, 1) == Poly(4, x) + assert f.slice(0, 2) == Poly(3*x + 4, x) + assert f.slice(0, 3) == Poly(2*x**2 + 3*x + 4, x) + assert f.slice(0, 4) == Poly(x**3 + 2*x**2 + 3*x + 4, x) + + assert f.slice(x, 0, 0) == Poly(0, x) + assert f.slice(x, 0, 1) == Poly(4, x) + assert f.slice(x, 0, 2) == Poly(3*x + 4, x) + assert f.slice(x, 0, 3) == Poly(2*x**2 + 3*x + 4, x) + assert f.slice(x, 0, 4) == Poly(x**3 + 2*x**2 + 3*x + 4, x) + + g = Poly(x**3 + 1) + + assert g.slice(0, 3) == Poly(1, x) + + +def test_Poly_coeffs(): + assert Poly(0, x).coeffs() == [0] + assert Poly(1, x).coeffs() == [1] + + assert Poly(2*x + 1, x).coeffs() == [2, 1] + + assert Poly(7*x**2 + 2*x + 1, x).coeffs() == [7, 2, 1] + assert Poly(7*x**4 + 2*x + 1, x).coeffs() == [7, 2, 1] + + assert Poly(x*y**7 + 2*x**2*y**3).coeffs('lex') == [2, 1] + assert Poly(x*y**7 + 2*x**2*y**3).coeffs('grlex') == [1, 2] + + +def test_Poly_monoms(): + assert Poly(0, x).monoms() == [(0,)] + assert Poly(1, x).monoms() == [(0,)] + + assert Poly(2*x + 1, x).monoms() == [(1,), (0,)] + + assert Poly(7*x**2 + 2*x + 1, x).monoms() == [(2,), (1,), (0,)] + assert Poly(7*x**4 + 2*x + 1, x).monoms() == [(4,), (1,), (0,)] + + assert Poly(x*y**7 + 2*x**2*y**3).monoms('lex') == [(2, 3), (1, 7)] + assert Poly(x*y**7 + 2*x**2*y**3).monoms('grlex') == [(1, 7), (2, 3)] + + +def test_Poly_terms(): + assert Poly(0, x).terms() == [((0,), 0)] + assert Poly(1, x).terms() == [((0,), 1)] + + assert Poly(2*x + 1, x).terms() == [((1,), 2), ((0,), 1)] + + assert Poly(7*x**2 + 2*x + 1, x).terms() == [((2,), 7), ((1,), 2), ((0,), 1)] + assert Poly(7*x**4 + 2*x + 1, x).terms() == [((4,), 7), ((1,), 2), ((0,), 1)] + + assert Poly( + x*y**7 + 2*x**2*y**3).terms('lex') == [((2, 3), 2), ((1, 7), 1)] + assert Poly( + x*y**7 + 2*x**2*y**3).terms('grlex') == [((1, 7), 1), ((2, 3), 2)] + + +def test_Poly_all_coeffs(): + assert Poly(0, x).all_coeffs() == [0] + assert Poly(1, x).all_coeffs() == [1] + + assert Poly(2*x + 1, x).all_coeffs() == [2, 1] + + assert Poly(7*x**2 + 2*x + 1, x).all_coeffs() == [7, 2, 1] + assert Poly(7*x**4 + 2*x + 1, x).all_coeffs() == [7, 0, 0, 2, 1] + + +def test_Poly_all_monoms(): + assert Poly(0, x).all_monoms() == [(0,)] + assert Poly(1, x).all_monoms() == [(0,)] + + assert Poly(2*x + 1, x).all_monoms() == [(1,), (0,)] + + assert Poly(7*x**2 + 2*x + 1, x).all_monoms() == [(2,), (1,), (0,)] + assert Poly(7*x**4 + 2*x + 1, x).all_monoms() == [(4,), (3,), (2,), (1,), (0,)] + + +def test_Poly_all_terms(): + assert Poly(0, x).all_terms() == [((0,), 0)] + assert Poly(1, x).all_terms() == [((0,), 1)] + + assert Poly(2*x + 1, x).all_terms() == [((1,), 2), ((0,), 1)] + + assert Poly(7*x**2 + 2*x + 1, x).all_terms() == \ + [((2,), 7), ((1,), 2), ((0,), 1)] + assert Poly(7*x**4 + 2*x + 1, x).all_terms() == \ + [((4,), 7), ((3,), 0), ((2,), 0), ((1,), 2), ((0,), 1)] + + +def test_Poly_termwise(): + f = Poly(x**2 + 20*x + 400) + g = Poly(x**2 + 2*x + 4) + + def func(monom, coeff): + (k,) = monom + return coeff//10**(2 - k) + + assert f.termwise(func) == g + + def func(monom, coeff): + (k,) = monom + return (k,), coeff//10**(2 - k) + + assert f.termwise(func) == g + + +def test_Poly_length(): + assert Poly(0, x).length() == 0 + assert Poly(1, x).length() == 1 + assert Poly(x, x).length() == 1 + + assert Poly(x + 1, x).length() == 2 + assert Poly(x**2 + 1, x).length() == 2 + assert Poly(x**2 + x + 1, x).length() == 3 + + +def test_Poly_as_dict(): + assert Poly(0, x).as_dict() == {} + assert Poly(0, x, y, z).as_dict() == {} + + assert Poly(1, x).as_dict() == {(0,): 1} + assert Poly(1, x, y, z).as_dict() == {(0, 0, 0): 1} + + assert Poly(x**2 + 3, x).as_dict() == {(2,): 1, (0,): 3} + assert Poly(x**2 + 3, x, y, z).as_dict() == {(2, 0, 0): 1, (0, 0, 0): 3} + + assert Poly(3*x**2*y*z**3 + 4*x*y + 5*x*z).as_dict() == {(2, 1, 3): 3, + (1, 1, 0): 4, (1, 0, 1): 5} + + +def test_Poly_as_expr(): + assert Poly(0, x).as_expr() == 0 + assert Poly(0, x, y, z).as_expr() == 0 + + assert Poly(1, x).as_expr() == 1 + assert Poly(1, x, y, z).as_expr() == 1 + + assert Poly(x**2 + 3, x).as_expr() == x**2 + 3 + assert Poly(x**2 + 3, x, y, z).as_expr() == x**2 + 3 + + assert Poly( + 3*x**2*y*z**3 + 4*x*y + 5*x*z).as_expr() == 3*x**2*y*z**3 + 4*x*y + 5*x*z + + f = Poly(x**2 + 2*x*y**2 - y, x, y) + + assert f.as_expr() == -y + x**2 + 2*x*y**2 + + assert f.as_expr({x: 5}) == 25 - y + 10*y**2 + assert f.as_expr({y: 6}) == -6 + 72*x + x**2 + + assert f.as_expr({x: 5, y: 6}) == 379 + assert f.as_expr(5, 6) == 379 + + raises(GeneratorsError, lambda: f.as_expr({z: 7})) + + +def test_Poly_lift(): + p = Poly(x**4 - I*x + 17*I, x, gaussian=True) + assert p.lift() == Poly(x**8 + x**2 - 34*x + 289, x, domain='QQ') + + +def test_Poly_lift_multiple(): + + r1 = rootof(y**3 + y**2 - 1, 0) + r2 = rootof(z**5 + z**2 - 1, 0) + p = Poly(r1*x + 3*r1**2 - r2 + x**2 - x**5, x, extension=True) + + assert p.lift() == Poly( + -x**75 + 15*x**72 - 5*x**71 + 15*x**70 - 105*x**69 + 70*x**68 - + 220*x**67 + 560*x**66 - 635*x**65 + 1495*x**64 - 2735*x**63 + + 4415*x**62 - 7410*x**61 + 12741*x**60 - 22090*x**59 + 32125*x**58 - + 56281*x**57 + 88157*x**56 - 126842*x**55 + 214223*x**54 - 311802*x**53 + + 462667*x**52 - 700883*x**51 + 1006278*x**50 - 1480950*x**49 + + 2078055*x**48 - 3004675*x**47 + 4140410*x**46 - 5664222*x**45 + + 8029445*x**44 - 10528785*x**43 + 14309614*x**42 - 19032988*x**41 + + 24570573*x**40 - 32530459*x**39 + 41239581*x**38 - 52968051*x**37 + + 65891606*x**36 - 81997276*x**35 + 102530732*x**34 - 122009994*x**33 + + 150227996*x**32 - 176452478*x**31 + 206393768*x**30 - 245291426*x**29 + + 276598718*x**28 - 320005297*x**27 + 353649032*x**26 + - 393246309*x**25 + 434566186*x**24 - 460608964*x**23 + 508052079*x**22 + - 513976618*x**21 + 539374498*x**20 - 557851717*x**19 + 540788016*x**18 + - 564949060*x**17 + 520866566*x**16 + - 507861375*x**15 + 474999819*x**14 - 423619160*x**13 + 414540540*x**12 + - 322522367*x**11 + 311586511*x**10 - 238812299*x**9 + 184482053*x**8 + - 189265274*x**7 + 93619528*x**6 - 106852385*x**5 + 57294385*x**4 - + 26486666*x**3 + 42614683*x**2 - 1511583*x + 15975845, x, domain='QQ' + ) + + +def test_Poly_deflate(): + assert Poly(0, x).deflate() == ((1,), Poly(0, x)) + assert Poly(1, x).deflate() == ((1,), Poly(1, x)) + assert Poly(x, x).deflate() == ((1,), Poly(x, x)) + + assert Poly(x**2, x).deflate() == ((2,), Poly(x, x)) + assert Poly(x**17, x).deflate() == ((17,), Poly(x, x)) + + assert Poly( + x**2*y*z**11 + x**4*z**11).deflate() == ((2, 1, 11), Poly(x*y*z + x**2*z)) + + +def test_Poly_inject(): + f = Poly(x**2*y + x*y**3 + x*y + 1, x) + + assert f.inject() == Poly(x**2*y + x*y**3 + x*y + 1, x, y) + assert f.inject(front=True) == Poly(y**3*x + y*x**2 + y*x + 1, y, x) + + +def test_Poly_eject(): + f = Poly(x**2*y + x*y**3 + x*y + 1, x, y) + + assert f.eject(x) == Poly(x*y**3 + (x**2 + x)*y + 1, y, domain='ZZ[x]') + assert f.eject(y) == Poly(y*x**2 + (y**3 + y)*x + 1, x, domain='ZZ[y]') + + ex = x + y + z + t + w + g = Poly(ex, x, y, z, t, w) + + assert g.eject(x) == Poly(ex, y, z, t, w, domain='ZZ[x]') + assert g.eject(x, y) == Poly(ex, z, t, w, domain='ZZ[x, y]') + assert g.eject(x, y, z) == Poly(ex, t, w, domain='ZZ[x, y, z]') + assert g.eject(w) == Poly(ex, x, y, z, t, domain='ZZ[w]') + assert g.eject(t, w) == Poly(ex, x, y, z, domain='ZZ[t, w]') + assert g.eject(z, t, w) == Poly(ex, x, y, domain='ZZ[z, t, w]') + + raises(DomainError, lambda: Poly(x*y, x, y, domain=ZZ[z]).eject(y)) + raises(NotImplementedError, lambda: Poly(x*y, x, y, z).eject(y)) + + +def test_Poly_exclude(): + assert Poly(x, x, y).exclude() == Poly(x, x) + assert Poly(x*y, x, y).exclude() == Poly(x*y, x, y) + assert Poly(1, x, y).exclude() == Poly(1, x, y) + + +def test_Poly__gen_to_level(): + assert Poly(1, x, y)._gen_to_level(-2) == 0 + assert Poly(1, x, y)._gen_to_level(-1) == 1 + assert Poly(1, x, y)._gen_to_level( 0) == 0 + assert Poly(1, x, y)._gen_to_level( 1) == 1 + + raises(PolynomialError, lambda: Poly(1, x, y)._gen_to_level(-3)) + raises(PolynomialError, lambda: Poly(1, x, y)._gen_to_level( 2)) + + assert Poly(1, x, y)._gen_to_level(x) == 0 + assert Poly(1, x, y)._gen_to_level(y) == 1 + + assert Poly(1, x, y)._gen_to_level('x') == 0 + assert Poly(1, x, y)._gen_to_level('y') == 1 + + raises(PolynomialError, lambda: Poly(1, x, y)._gen_to_level(z)) + raises(PolynomialError, lambda: Poly(1, x, y)._gen_to_level('z')) + + +def test_Poly_degree(): + assert Poly(0, x).degree() is -oo + assert Poly(1, x).degree() == 0 + assert Poly(x, x).degree() == 1 + + assert Poly(0, x).degree(gen=0) is -oo + assert Poly(1, x).degree(gen=0) == 0 + assert Poly(x, x).degree(gen=0) == 1 + + assert Poly(0, x).degree(gen=x) is -oo + assert Poly(1, x).degree(gen=x) == 0 + assert Poly(x, x).degree(gen=x) == 1 + + assert Poly(0, x).degree(gen='x') is -oo + assert Poly(1, x).degree(gen='x') == 0 + assert Poly(x, x).degree(gen='x') == 1 + + raises(PolynomialError, lambda: Poly(1, x).degree(gen=1)) + raises(PolynomialError, lambda: Poly(1, x).degree(gen=y)) + raises(PolynomialError, lambda: Poly(1, x).degree(gen='y')) + + assert Poly(1, x, y).degree() == 0 + assert Poly(2*y, x, y).degree() == 0 + assert Poly(x*y, x, y).degree() == 1 + + assert Poly(1, x, y).degree(gen=x) == 0 + assert Poly(2*y, x, y).degree(gen=x) == 0 + assert Poly(x*y, x, y).degree(gen=x) == 1 + + assert Poly(1, x, y).degree(gen=y) == 0 + assert Poly(2*y, x, y).degree(gen=y) == 1 + assert Poly(x*y, x, y).degree(gen=y) == 1 + + assert degree(0, x) is -oo + assert degree(1, x) == 0 + assert degree(x, x) == 1 + + assert degree(x*y**2, x) == 1 + assert degree(x*y**2, y) == 2 + assert degree(x*y**2, z) == 0 + + assert degree(pi) == 1 + + raises(TypeError, lambda: degree(y**2 + x**3)) + raises(TypeError, lambda: degree(y**2 + x**3, 1)) + raises(PolynomialError, lambda: degree(x, 1.1)) + raises(PolynomialError, lambda: degree(x**2/(x**3 + 1), x)) + + assert degree(Poly(0,x),z) is -oo + assert degree(Poly(1,x),z) == 0 + assert degree(Poly(x**2+y**3,y)) == 3 + assert degree(Poly(y**2 + x**3, y, x), 1) == 3 + assert degree(Poly(y**2 + x**3, x), z) == 0 + assert degree(Poly(y**2 + x**3 + z**4, x), z) == 4 + +def test_Poly_degree_list(): + assert Poly(0, x).degree_list() == (-oo,) + assert Poly(0, x, y).degree_list() == (-oo, -oo) + assert Poly(0, x, y, z).degree_list() == (-oo, -oo, -oo) + + assert Poly(1, x).degree_list() == (0,) + assert Poly(1, x, y).degree_list() == (0, 0) + assert Poly(1, x, y, z).degree_list() == (0, 0, 0) + + assert Poly(x**2*y + x**3*z**2 + 1).degree_list() == (3, 1, 2) + + assert degree_list(1, x) == (0,) + assert degree_list(x, x) == (1,) + + assert degree_list(x*y**2) == (1, 2) + + raises(ComputationFailed, lambda: degree_list(1)) + + +def test_Poly_total_degree(): + assert Poly(x**2*y + x**3*z**2 + 1).total_degree() == 5 + assert Poly(x**2 + z**3).total_degree() == 3 + assert Poly(x*y*z + z**4).total_degree() == 4 + assert Poly(x**3 + x + 1).total_degree() == 3 + + assert total_degree(x*y + z**3) == 3 + assert total_degree(x*y + z**3, x, y) == 2 + assert total_degree(1) == 0 + assert total_degree(Poly(y**2 + x**3 + z**4)) == 4 + assert total_degree(Poly(y**2 + x**3 + z**4, x)) == 3 + assert total_degree(Poly(y**2 + x**3 + z**4, x), z) == 4 + assert total_degree(Poly(x**9 + x*z*y + x**3*z**2 + z**7,x), z) == 7 + +def test_Poly_homogenize(): + assert Poly(x**2+y).homogenize(z) == Poly(x**2+y*z) + assert Poly(x+y).homogenize(z) == Poly(x+y, x, y, z) + assert Poly(x+y**2).homogenize(y) == Poly(x*y+y**2) + + +def test_Poly_homogeneous_order(): + assert Poly(0, x, y).homogeneous_order() is -oo + assert Poly(1, x, y).homogeneous_order() == 0 + assert Poly(x, x, y).homogeneous_order() == 1 + assert Poly(x*y, x, y).homogeneous_order() == 2 + + assert Poly(x + 1, x, y).homogeneous_order() is None + assert Poly(x*y + x, x, y).homogeneous_order() is None + + assert Poly(x**5 + 2*x**3*y**2 + 9*x*y**4).homogeneous_order() == 5 + assert Poly(x**5 + 2*x**3*y**3 + 9*x*y**4).homogeneous_order() is None + + +def test_Poly_LC(): + assert Poly(0, x).LC() == 0 + assert Poly(1, x).LC() == 1 + assert Poly(2*x**2 + x, x).LC() == 2 + + assert Poly(x*y**7 + 2*x**2*y**3).LC('lex') == 2 + assert Poly(x*y**7 + 2*x**2*y**3).LC('grlex') == 1 + + assert LC(x*y**7 + 2*x**2*y**3, order='lex') == 2 + assert LC(x*y**7 + 2*x**2*y**3, order='grlex') == 1 + + +def test_Poly_TC(): + assert Poly(0, x).TC() == 0 + assert Poly(1, x).TC() == 1 + assert Poly(2*x**2 + x, x).TC() == 0 + + +def test_Poly_EC(): + assert Poly(0, x).EC() == 0 + assert Poly(1, x).EC() == 1 + assert Poly(2*x**2 + x, x).EC() == 1 + + assert Poly(x*y**7 + 2*x**2*y**3).EC('lex') == 1 + assert Poly(x*y**7 + 2*x**2*y**3).EC('grlex') == 2 + + +def test_Poly_coeff(): + assert Poly(0, x).coeff_monomial(1) == 0 + assert Poly(0, x).coeff_monomial(x) == 0 + + assert Poly(1, x).coeff_monomial(1) == 1 + assert Poly(1, x).coeff_monomial(x) == 0 + + assert Poly(x**8, x).coeff_monomial(1) == 0 + assert Poly(x**8, x).coeff_monomial(x**7) == 0 + assert Poly(x**8, x).coeff_monomial(x**8) == 1 + assert Poly(x**8, x).coeff_monomial(x**9) == 0 + + assert Poly(3*x*y**2 + 1, x, y).coeff_monomial(1) == 1 + assert Poly(3*x*y**2 + 1, x, y).coeff_monomial(x*y**2) == 3 + + p = Poly(24*x*y*exp(8) + 23*x, x, y) + + assert p.coeff_monomial(x) == 23 + assert p.coeff_monomial(y) == 0 + assert p.coeff_monomial(x*y) == 24*exp(8) + + assert p.as_expr().coeff(x) == 24*y*exp(8) + 23 + raises(NotImplementedError, lambda: p.coeff(x)) + + raises(ValueError, lambda: Poly(x + 1).coeff_monomial(0)) + raises(ValueError, lambda: Poly(x + 1).coeff_monomial(3*x)) + raises(ValueError, lambda: Poly(x + 1).coeff_monomial(3*x*y)) + + +def test_Poly_nth(): + assert Poly(0, x).nth(0) == 0 + assert Poly(0, x).nth(1) == 0 + + assert Poly(1, x).nth(0) == 1 + assert Poly(1, x).nth(1) == 0 + + assert Poly(x**8, x).nth(0) == 0 + assert Poly(x**8, x).nth(7) == 0 + assert Poly(x**8, x).nth(8) == 1 + assert Poly(x**8, x).nth(9) == 0 + + assert Poly(3*x*y**2 + 1, x, y).nth(0, 0) == 1 + assert Poly(3*x*y**2 + 1, x, y).nth(1, 2) == 3 + + raises(ValueError, lambda: Poly(x*y + 1, x, y).nth(1)) + + +def test_Poly_LM(): + assert Poly(0, x).LM() == (0,) + assert Poly(1, x).LM() == (0,) + assert Poly(2*x**2 + x, x).LM() == (2,) + + assert Poly(x*y**7 + 2*x**2*y**3).LM('lex') == (2, 3) + assert Poly(x*y**7 + 2*x**2*y**3).LM('grlex') == (1, 7) + + assert LM(x*y**7 + 2*x**2*y**3, order='lex') == x**2*y**3 + assert LM(x*y**7 + 2*x**2*y**3, order='grlex') == x*y**7 + + +def test_Poly_LM_custom_order(): + f = Poly(x**2*y**3*z + x**2*y*z**3 + x*y*z + 1) + rev_lex = lambda monom: tuple(reversed(monom)) + + assert f.LM(order='lex') == (2, 3, 1) + assert f.LM(order=rev_lex) == (2, 1, 3) + + +def test_Poly_EM(): + assert Poly(0, x).EM() == (0,) + assert Poly(1, x).EM() == (0,) + assert Poly(2*x**2 + x, x).EM() == (1,) + + assert Poly(x*y**7 + 2*x**2*y**3).EM('lex') == (1, 7) + assert Poly(x*y**7 + 2*x**2*y**3).EM('grlex') == (2, 3) + + +def test_Poly_LT(): + assert Poly(0, x).LT() == ((0,), 0) + assert Poly(1, x).LT() == ((0,), 1) + assert Poly(2*x**2 + x, x).LT() == ((2,), 2) + + assert Poly(x*y**7 + 2*x**2*y**3).LT('lex') == ((2, 3), 2) + assert Poly(x*y**7 + 2*x**2*y**3).LT('grlex') == ((1, 7), 1) + + assert LT(x*y**7 + 2*x**2*y**3, order='lex') == 2*x**2*y**3 + assert LT(x*y**7 + 2*x**2*y**3, order='grlex') == x*y**7 + + +def test_Poly_ET(): + assert Poly(0, x).ET() == ((0,), 0) + assert Poly(1, x).ET() == ((0,), 1) + assert Poly(2*x**2 + x, x).ET() == ((1,), 1) + + assert Poly(x*y**7 + 2*x**2*y**3).ET('lex') == ((1, 7), 1) + assert Poly(x*y**7 + 2*x**2*y**3).ET('grlex') == ((2, 3), 2) + + +def test_Poly_max_norm(): + assert Poly(-1, x).max_norm() == 1 + assert Poly( 0, x).max_norm() == 0 + assert Poly( 1, x).max_norm() == 1 + + +def test_Poly_l1_norm(): + assert Poly(-1, x).l1_norm() == 1 + assert Poly( 0, x).l1_norm() == 0 + assert Poly( 1, x).l1_norm() == 1 + + +def test_Poly_clear_denoms(): + coeff, poly = Poly(x + 2, x).clear_denoms() + assert coeff == 1 and poly == Poly( + x + 2, x, domain='ZZ') and poly.get_domain() == ZZ + + coeff, poly = Poly(x/2 + 1, x).clear_denoms() + assert coeff == 2 and poly == Poly( + x + 2, x, domain='QQ') and poly.get_domain() == QQ + + coeff, poly = Poly(2*x**2 + 3, modulus=5).clear_denoms() + assert coeff == 1 and poly == Poly( + 2*x**2 + 3, x, modulus=5) and poly.get_domain() == FF(5) + + coeff, poly = Poly(x/2 + 1, x).clear_denoms(convert=True) + assert coeff == 2 and poly == Poly( + x + 2, x, domain='ZZ') and poly.get_domain() == ZZ + + coeff, poly = Poly(x/y + 1, x).clear_denoms(convert=True) + assert coeff == y and poly == Poly( + x + y, x, domain='ZZ[y]') and poly.get_domain() == ZZ[y] + + coeff, poly = Poly(x/3 + sqrt(2), x, domain='EX').clear_denoms() + assert coeff == 3 and poly == Poly( + x + 3*sqrt(2), x, domain='EX') and poly.get_domain() == EX + + coeff, poly = Poly( + x/3 + sqrt(2), x, domain='EX').clear_denoms(convert=True) + assert coeff == 3 and poly == Poly( + x + 3*sqrt(2), x, domain='EX') and poly.get_domain() == EX + + +def test_Poly_rat_clear_denoms(): + f = Poly(x**2/y + 1, x) + g = Poly(x**3 + y, x) + + assert f.rat_clear_denoms(g) == \ + (Poly(x**2 + y, x), Poly(y*x**3 + y**2, x)) + + f = f.set_domain(EX) + g = g.set_domain(EX) + + assert f.rat_clear_denoms(g) == (f, g) + + +def test_issue_20427(): + f = Poly(-117968192370600*18**(S(1)/3)/(217603955769048*(24201 + + 253*sqrt(9165))**(S(1)/3) + 2273005839412*sqrt(9165)*(24201 + + 253*sqrt(9165))**(S(1)/3)) - 15720318185*2**(S(2)/3)*3**(S(1)/3)*(24201 + + 253*sqrt(9165))**(S(2)/3)/(217603955769048*(24201 + 253*sqrt(9165))** + (S(1)/3) + 2273005839412*sqrt(9165)*(24201 + 253*sqrt(9165))**(S(1)/3)) + + 15720318185*12**(S(1)/3)*(24201 + 253*sqrt(9165))**(S(2)/3)/( + 217603955769048*(24201 + 253*sqrt(9165))**(S(1)/3) + 2273005839412* + sqrt(9165)*(24201 + 253*sqrt(9165))**(S(1)/3)) + 117968192370600*2**( + S(1)/3)*3**(S(2)/3)/(217603955769048*(24201 + 253*sqrt(9165))**(S(1)/3) + + 2273005839412*sqrt(9165)*(24201 + 253*sqrt(9165))**(S(1)/3)), x) + assert f == Poly(0, x, domain='EX') + + +def test_Poly_integrate(): + assert Poly(x + 1).integrate() == Poly(x**2/2 + x) + assert Poly(x + 1).integrate(x) == Poly(x**2/2 + x) + assert Poly(x + 1).integrate((x, 1)) == Poly(x**2/2 + x) + + assert Poly(x*y + 1).integrate(x) == Poly(x**2*y/2 + x) + assert Poly(x*y + 1).integrate(y) == Poly(x*y**2/2 + y) + + assert Poly(x*y + 1).integrate(x, x) == Poly(x**3*y/6 + x**2/2) + assert Poly(x*y + 1).integrate(y, y) == Poly(x*y**3/6 + y**2/2) + + assert Poly(x*y + 1).integrate((x, 2)) == Poly(x**3*y/6 + x**2/2) + assert Poly(x*y + 1).integrate((y, 2)) == Poly(x*y**3/6 + y**2/2) + + assert Poly(x*y + 1).integrate(x, y) == Poly(x**2*y**2/4 + x*y) + assert Poly(x*y + 1).integrate(y, x) == Poly(x**2*y**2/4 + x*y) + + +def test_Poly_diff(): + assert Poly(x**2 + x).diff() == Poly(2*x + 1) + assert Poly(x**2 + x).diff(x) == Poly(2*x + 1) + assert Poly(x**2 + x).diff((x, 1)) == Poly(2*x + 1) + + assert Poly(x**2*y**2 + x*y).diff(x) == Poly(2*x*y**2 + y) + assert Poly(x**2*y**2 + x*y).diff(y) == Poly(2*x**2*y + x) + + assert Poly(x**2*y**2 + x*y).diff(x, x) == Poly(2*y**2, x, y) + assert Poly(x**2*y**2 + x*y).diff(y, y) == Poly(2*x**2, x, y) + + assert Poly(x**2*y**2 + x*y).diff((x, 2)) == Poly(2*y**2, x, y) + assert Poly(x**2*y**2 + x*y).diff((y, 2)) == Poly(2*x**2, x, y) + + assert Poly(x**2*y**2 + x*y).diff(x, y) == Poly(4*x*y + 1) + assert Poly(x**2*y**2 + x*y).diff(y, x) == Poly(4*x*y + 1) + + +def test_issue_9585(): + assert diff(Poly(x**2 + x)) == Poly(2*x + 1) + assert diff(Poly(x**2 + x), x, evaluate=False) == \ + Derivative(Poly(x**2 + x), x) + assert Derivative(Poly(x**2 + x), x).doit() == Poly(2*x + 1) + + +def test_Poly_eval(): + assert Poly(0, x).eval(7) == 0 + assert Poly(1, x).eval(7) == 1 + assert Poly(x, x).eval(7) == 7 + + assert Poly(0, x).eval(0, 7) == 0 + assert Poly(1, x).eval(0, 7) == 1 + assert Poly(x, x).eval(0, 7) == 7 + + assert Poly(0, x).eval(x, 7) == 0 + assert Poly(1, x).eval(x, 7) == 1 + assert Poly(x, x).eval(x, 7) == 7 + + assert Poly(0, x).eval('x', 7) == 0 + assert Poly(1, x).eval('x', 7) == 1 + assert Poly(x, x).eval('x', 7) == 7 + + raises(PolynomialError, lambda: Poly(1, x).eval(1, 7)) + raises(PolynomialError, lambda: Poly(1, x).eval(y, 7)) + raises(PolynomialError, lambda: Poly(1, x).eval('y', 7)) + + assert Poly(123, x, y).eval(7) == Poly(123, y) + assert Poly(2*y, x, y).eval(7) == Poly(2*y, y) + assert Poly(x*y, x, y).eval(7) == Poly(7*y, y) + + assert Poly(123, x, y).eval(x, 7) == Poly(123, y) + assert Poly(2*y, x, y).eval(x, 7) == Poly(2*y, y) + assert Poly(x*y, x, y).eval(x, 7) == Poly(7*y, y) + + assert Poly(123, x, y).eval(y, 7) == Poly(123, x) + assert Poly(2*y, x, y).eval(y, 7) == Poly(14, x) + assert Poly(x*y, x, y).eval(y, 7) == Poly(7*x, x) + + assert Poly(x*y + y, x, y).eval({x: 7}) == Poly(8*y, y) + assert Poly(x*y + y, x, y).eval({y: 7}) == Poly(7*x + 7, x) + + assert Poly(x*y + y, x, y).eval({x: 6, y: 7}) == 49 + assert Poly(x*y + y, x, y).eval({x: 7, y: 6}) == 48 + + assert Poly(x*y + y, x, y).eval((6, 7)) == 49 + assert Poly(x*y + y, x, y).eval([6, 7]) == 49 + + assert Poly(x + 1, domain='ZZ').eval(S.Half) == Rational(3, 2) + assert Poly(x + 1, domain='ZZ').eval(sqrt(2)) == sqrt(2) + 1 + + raises(ValueError, lambda: Poly(x*y + y, x, y).eval((6, 7, 8))) + raises(DomainError, lambda: Poly(x + 1, domain='ZZ').eval(S.Half, auto=False)) + + # issue 6344 + alpha = Symbol('alpha') + result = (2*alpha*z - 2*alpha + z**2 + 3)/(z**2 - 2*z + 1) + + f = Poly(x**2 + (alpha - 1)*x - alpha + 1, x, domain='ZZ[alpha]') + assert f.eval((z + 1)/(z - 1)) == result + + g = Poly(x**2 + (alpha - 1)*x - alpha + 1, x, y, domain='ZZ[alpha]') + assert g.eval((z + 1)/(z - 1)) == Poly(result, y, domain='ZZ(alpha,z)') + +def test_Poly___call__(): + f = Poly(2*x*y + 3*x + y + 2*z) + + assert f(2) == Poly(5*y + 2*z + 6) + assert f(2, 5) == Poly(2*z + 31) + assert f(2, 5, 7) == 45 + + +def test_parallel_poly_from_expr(): + assert parallel_poly_from_expr( + [x - 1, x**2 - 1], x)[0] == [Poly(x - 1, x), Poly(x**2 - 1, x)] + assert parallel_poly_from_expr( + [Poly(x - 1, x), x**2 - 1], x)[0] == [Poly(x - 1, x), Poly(x**2 - 1, x)] + assert parallel_poly_from_expr( + [x - 1, Poly(x**2 - 1, x)], x)[0] == [Poly(x - 1, x), Poly(x**2 - 1, x)] + assert parallel_poly_from_expr([Poly( + x - 1, x), Poly(x**2 - 1, x)], x)[0] == [Poly(x - 1, x), Poly(x**2 - 1, x)] + + assert parallel_poly_from_expr( + [x - 1, x**2 - 1], x, y)[0] == [Poly(x - 1, x, y), Poly(x**2 - 1, x, y)] + assert parallel_poly_from_expr([Poly( + x - 1, x), x**2 - 1], x, y)[0] == [Poly(x - 1, x, y), Poly(x**2 - 1, x, y)] + assert parallel_poly_from_expr([x - 1, Poly( + x**2 - 1, x)], x, y)[0] == [Poly(x - 1, x, y), Poly(x**2 - 1, x, y)] + assert parallel_poly_from_expr([Poly(x - 1, x), Poly( + x**2 - 1, x)], x, y)[0] == [Poly(x - 1, x, y), Poly(x**2 - 1, x, y)] + + assert parallel_poly_from_expr( + [x - 1, x**2 - 1])[0] == [Poly(x - 1, x), Poly(x**2 - 1, x)] + assert parallel_poly_from_expr( + [Poly(x - 1, x), x**2 - 1])[0] == [Poly(x - 1, x), Poly(x**2 - 1, x)] + assert parallel_poly_from_expr( + [x - 1, Poly(x**2 - 1, x)])[0] == [Poly(x - 1, x), Poly(x**2 - 1, x)] + assert parallel_poly_from_expr( + [Poly(x - 1, x), Poly(x**2 - 1, x)])[0] == [Poly(x - 1, x), Poly(x**2 - 1, x)] + + assert parallel_poly_from_expr( + [1, x**2 - 1])[0] == [Poly(1, x), Poly(x**2 - 1, x)] + assert parallel_poly_from_expr( + [1, x**2 - 1])[0] == [Poly(1, x), Poly(x**2 - 1, x)] + assert parallel_poly_from_expr( + [1, Poly(x**2 - 1, x)])[0] == [Poly(1, x), Poly(x**2 - 1, x)] + assert parallel_poly_from_expr( + [1, Poly(x**2 - 1, x)])[0] == [Poly(1, x), Poly(x**2 - 1, x)] + + assert parallel_poly_from_expr( + [x**2 - 1, 1])[0] == [Poly(x**2 - 1, x), Poly(1, x)] + assert parallel_poly_from_expr( + [x**2 - 1, 1])[0] == [Poly(x**2 - 1, x), Poly(1, x)] + assert parallel_poly_from_expr( + [Poly(x**2 - 1, x), 1])[0] == [Poly(x**2 - 1, x), Poly(1, x)] + assert parallel_poly_from_expr( + [Poly(x**2 - 1, x), 1])[0] == [Poly(x**2 - 1, x), Poly(1, x)] + + assert parallel_poly_from_expr([Poly(x, x, y), Poly(y, x, y)], x, y, order='lex')[0] == \ + [Poly(x, x, y, domain='ZZ'), Poly(y, x, y, domain='ZZ')] + + raises(PolificationFailed, lambda: parallel_poly_from_expr([0, 1])) + + +def test_pdiv(): + f, g = x**2 - y**2, x - y + q, r = x + y, 0 + + F, G, Q, R = [ Poly(h, x, y) for h in (f, g, q, r) ] + + assert F.pdiv(G) == (Q, R) + assert F.prem(G) == R + assert F.pquo(G) == Q + assert F.pexquo(G) == Q + + assert pdiv(f, g) == (q, r) + assert prem(f, g) == r + assert pquo(f, g) == q + assert pexquo(f, g) == q + + assert pdiv(f, g, x, y) == (q, r) + assert prem(f, g, x, y) == r + assert pquo(f, g, x, y) == q + assert pexquo(f, g, x, y) == q + + assert pdiv(f, g, (x, y)) == (q, r) + assert prem(f, g, (x, y)) == r + assert pquo(f, g, (x, y)) == q + assert pexquo(f, g, (x, y)) == q + + assert pdiv(F, G) == (Q, R) + assert prem(F, G) == R + assert pquo(F, G) == Q + assert pexquo(F, G) == Q + + assert pdiv(f, g, polys=True) == (Q, R) + assert prem(f, g, polys=True) == R + assert pquo(f, g, polys=True) == Q + assert pexquo(f, g, polys=True) == Q + + assert pdiv(F, G, polys=False) == (q, r) + assert prem(F, G, polys=False) == r + assert pquo(F, G, polys=False) == q + assert pexquo(F, G, polys=False) == q + + raises(ComputationFailed, lambda: pdiv(4, 2)) + raises(ComputationFailed, lambda: prem(4, 2)) + raises(ComputationFailed, lambda: pquo(4, 2)) + raises(ComputationFailed, lambda: pexquo(4, 2)) + + +def test_div(): + f, g = x**2 - y**2, x - y + q, r = x + y, 0 + + F, G, Q, R = [ Poly(h, x, y) for h in (f, g, q, r) ] + + assert F.div(G) == (Q, R) + assert F.rem(G) == R + assert F.quo(G) == Q + assert F.exquo(G) == Q + + assert div(f, g) == (q, r) + assert rem(f, g) == r + assert quo(f, g) == q + assert exquo(f, g) == q + + assert div(f, g, x, y) == (q, r) + assert rem(f, g, x, y) == r + assert quo(f, g, x, y) == q + assert exquo(f, g, x, y) == q + + assert div(f, g, (x, y)) == (q, r) + assert rem(f, g, (x, y)) == r + assert quo(f, g, (x, y)) == q + assert exquo(f, g, (x, y)) == q + + assert div(F, G) == (Q, R) + assert rem(F, G) == R + assert quo(F, G) == Q + assert exquo(F, G) == Q + + assert div(f, g, polys=True) == (Q, R) + assert rem(f, g, polys=True) == R + assert quo(f, g, polys=True) == Q + assert exquo(f, g, polys=True) == Q + + assert div(F, G, polys=False) == (q, r) + assert rem(F, G, polys=False) == r + assert quo(F, G, polys=False) == q + assert exquo(F, G, polys=False) == q + + raises(ComputationFailed, lambda: div(4, 2)) + raises(ComputationFailed, lambda: rem(4, 2)) + raises(ComputationFailed, lambda: quo(4, 2)) + raises(ComputationFailed, lambda: exquo(4, 2)) + + f, g = x**2 + 1, 2*x - 4 + + qz, rz = 0, x**2 + 1 + qq, rq = x/2 + 1, 5 + + assert div(f, g) == (qq, rq) + assert div(f, g, auto=True) == (qq, rq) + assert div(f, g, auto=False) == (qz, rz) + assert div(f, g, domain=ZZ) == (qz, rz) + assert div(f, g, domain=QQ) == (qq, rq) + assert div(f, g, domain=ZZ, auto=True) == (qq, rq) + assert div(f, g, domain=ZZ, auto=False) == (qz, rz) + assert div(f, g, domain=QQ, auto=True) == (qq, rq) + assert div(f, g, domain=QQ, auto=False) == (qq, rq) + + assert rem(f, g) == rq + assert rem(f, g, auto=True) == rq + assert rem(f, g, auto=False) == rz + assert rem(f, g, domain=ZZ) == rz + assert rem(f, g, domain=QQ) == rq + assert rem(f, g, domain=ZZ, auto=True) == rq + assert rem(f, g, domain=ZZ, auto=False) == rz + assert rem(f, g, domain=QQ, auto=True) == rq + assert rem(f, g, domain=QQ, auto=False) == rq + + assert quo(f, g) == qq + assert quo(f, g, auto=True) == qq + assert quo(f, g, auto=False) == qz + assert quo(f, g, domain=ZZ) == qz + assert quo(f, g, domain=QQ) == qq + assert quo(f, g, domain=ZZ, auto=True) == qq + assert quo(f, g, domain=ZZ, auto=False) == qz + assert quo(f, g, domain=QQ, auto=True) == qq + assert quo(f, g, domain=QQ, auto=False) == qq + + f, g, q = x**2, 2*x, x/2 + + assert exquo(f, g) == q + assert exquo(f, g, auto=True) == q + raises(ExactQuotientFailed, lambda: exquo(f, g, auto=False)) + raises(ExactQuotientFailed, lambda: exquo(f, g, domain=ZZ)) + assert exquo(f, g, domain=QQ) == q + assert exquo(f, g, domain=ZZ, auto=True) == q + raises(ExactQuotientFailed, lambda: exquo(f, g, domain=ZZ, auto=False)) + assert exquo(f, g, domain=QQ, auto=True) == q + assert exquo(f, g, domain=QQ, auto=False) == q + + f, g = Poly(x**2), Poly(x) + + q, r = f.div(g) + assert q.get_domain().is_ZZ and r.get_domain().is_ZZ + r = f.rem(g) + assert r.get_domain().is_ZZ + q = f.quo(g) + assert q.get_domain().is_ZZ + q = f.exquo(g) + assert q.get_domain().is_ZZ + + f, g = Poly(x+y, x), Poly(2*x+y, x) + q, r = f.div(g) + assert q.get_domain().is_Frac and r.get_domain().is_Frac + + # https://github.com/sympy/sympy/issues/19579 + p = Poly(2+3*I, x, domain=ZZ_I) + q = Poly(1-I, x, domain=ZZ_I) + assert p.div(q, auto=False) == \ + (Poly(0, x, domain='ZZ_I'), Poly(2 + 3*I, x, domain='ZZ_I')) + assert p.div(q, auto=True) == \ + (Poly(-S(1)/2 + 5*I/2, x, domain='QQ_I'), Poly(0, x, domain='QQ_I')) + + f = 5*x**2 + 10*x + 3 + g = 2*x + 2 + assert div(f, g, domain=ZZ) == (0, f) + + +def test_issue_7864(): + q, r = div(a, .408248290463863*a) + assert abs(q - 2.44948974278318) < 1e-14 + assert r == 0 + + +def test_gcdex(): + f, g = 2*x, x**2 - 16 + s, t, h = x/32, Rational(-1, 16), 1 + + F, G, S, T, H = [ Poly(u, x, domain='QQ') for u in (f, g, s, t, h) ] + + assert F.half_gcdex(G) == (S, H) + assert F.gcdex(G) == (S, T, H) + assert F.invert(G) == S + + assert half_gcdex(f, g) == (s, h) + assert gcdex(f, g) == (s, t, h) + assert invert(f, g) == s + + assert half_gcdex(f, g, x) == (s, h) + assert gcdex(f, g, x) == (s, t, h) + assert invert(f, g, x) == s + + assert half_gcdex(f, g, (x,)) == (s, h) + assert gcdex(f, g, (x,)) == (s, t, h) + assert invert(f, g, (x,)) == s + + assert half_gcdex(F, G) == (S, H) + assert gcdex(F, G) == (S, T, H) + assert invert(F, G) == S + + assert half_gcdex(f, g, polys=True) == (S, H) + assert gcdex(f, g, polys=True) == (S, T, H) + assert invert(f, g, polys=True) == S + + assert half_gcdex(F, G, polys=False) == (s, h) + assert gcdex(F, G, polys=False) == (s, t, h) + assert invert(F, G, polys=False) == s + + assert half_gcdex(100, 2004) == (-20, 4) + assert gcdex(100, 2004) == (-20, 1, 4) + assert invert(3, 7) == 5 + + raises(DomainError, lambda: half_gcdex(x + 1, 2*x + 1, auto=False)) + raises(DomainError, lambda: gcdex(x + 1, 2*x + 1, auto=False)) + raises(DomainError, lambda: invert(x + 1, 2*x + 1, auto=False)) + + +def test_revert(): + f = Poly(1 - x**2/2 + x**4/24 - x**6/720) + g = Poly(61*x**6/720 + 5*x**4/24 + x**2/2 + 1) + + assert f.revert(8) == g + + +def test_subresultants(): + f, g, h = x**2 - 2*x + 1, x**2 - 1, 2*x - 2 + F, G, H = Poly(f), Poly(g), Poly(h) + + assert F.subresultants(G) == [F, G, H] + assert subresultants(f, g) == [f, g, h] + assert subresultants(f, g, x) == [f, g, h] + assert subresultants(f, g, (x,)) == [f, g, h] + assert subresultants(F, G) == [F, G, H] + assert subresultants(f, g, polys=True) == [F, G, H] + assert subresultants(F, G, polys=False) == [f, g, h] + + raises(ComputationFailed, lambda: subresultants(4, 2)) + + +def test_resultant(): + f, g, h = x**2 - 2*x + 1, x**2 - 1, 0 + F, G = Poly(f), Poly(g) + + assert F.resultant(G) == h + assert resultant(f, g) == h + assert resultant(f, g, x) == h + assert resultant(f, g, (x,)) == h + assert resultant(F, G) == h + assert resultant(f, g, polys=True) == h + assert resultant(F, G, polys=False) == h + assert resultant(f, g, includePRS=True) == (h, [f, g, 2*x - 2]) + + f, g, h = x - a, x - b, a - b + F, G, H = Poly(f), Poly(g), Poly(h) + + assert F.resultant(G) == H + assert resultant(f, g) == h + assert resultant(f, g, x) == h + assert resultant(f, g, (x,)) == h + assert resultant(F, G) == H + assert resultant(f, g, polys=True) == H + assert resultant(F, G, polys=False) == h + + raises(ComputationFailed, lambda: resultant(4, 2)) + + +def test_discriminant(): + f, g = x**3 + 3*x**2 + 9*x - 13, -11664 + F = Poly(f) + + assert F.discriminant() == g + assert discriminant(f) == g + assert discriminant(f, x) == g + assert discriminant(f, (x,)) == g + assert discriminant(F) == g + assert discriminant(f, polys=True) == g + assert discriminant(F, polys=False) == g + + f, g = a*x**2 + b*x + c, b**2 - 4*a*c + F, G = Poly(f), Poly(g) + + assert F.discriminant() == G + assert discriminant(f) == g + assert discriminant(f, x, a, b, c) == g + assert discriminant(f, (x, a, b, c)) == g + assert discriminant(F) == G + assert discriminant(f, polys=True) == G + assert discriminant(F, polys=False) == g + + raises(ComputationFailed, lambda: discriminant(4)) + + +def test_dispersion(): + # We test only the API here. For more mathematical + # tests see the dedicated test file. + fp = poly((x + 1)*(x + 2), x) + assert sorted(fp.dispersionset()) == [0, 1] + assert fp.dispersion() == 1 + + fp = poly(x**4 - 3*x**2 + 1, x) + gp = fp.shift(-3) + assert sorted(fp.dispersionset(gp)) == [2, 3, 4] + assert fp.dispersion(gp) == 4 + + +def test_gcd_list(): + F = [x**3 - 1, x**2 - 1, x**2 - 3*x + 2] + + assert gcd_list(F) == x - 1 + assert gcd_list(F, polys=True) == Poly(x - 1) + + assert gcd_list([]) == 0 + assert gcd_list([1, 2]) == 1 + assert gcd_list([4, 6, 8]) == 2 + + assert gcd_list([x*(y + 42) - x*y - x*42]) == 0 + + gcd = gcd_list([], x) + assert gcd.is_Number and gcd is S.Zero + + gcd = gcd_list([], x, polys=True) + assert gcd.is_Poly and gcd.is_zero + + a = sqrt(2) + assert gcd_list([a, -a]) == gcd_list([-a, a]) == a + + raises(ComputationFailed, lambda: gcd_list([], polys=True)) + + +def test_lcm_list(): + F = [x**3 - 1, x**2 - 1, x**2 - 3*x + 2] + + assert lcm_list(F) == x**5 - x**4 - 2*x**3 - x**2 + x + 2 + assert lcm_list(F, polys=True) == Poly(x**5 - x**4 - 2*x**3 - x**2 + x + 2) + + assert lcm_list([]) == 1 + assert lcm_list([1, 2]) == 2 + assert lcm_list([4, 6, 8]) == 24 + + assert lcm_list([x*(y + 42) - x*y - x*42]) == 0 + + lcm = lcm_list([], x) + assert lcm.is_Number and lcm is S.One + + lcm = lcm_list([], x, polys=True) + assert lcm.is_Poly and lcm.is_one + + raises(ComputationFailed, lambda: lcm_list([], polys=True)) + + +def test_gcd(): + f, g = x**3 - 1, x**2 - 1 + s, t = x**2 + x + 1, x + 1 + h, r = x - 1, x**4 + x**3 - x - 1 + + F, G, S, T, H, R = [ Poly(u) for u in (f, g, s, t, h, r) ] + + assert F.cofactors(G) == (H, S, T) + assert F.gcd(G) == H + assert F.lcm(G) == R + + assert cofactors(f, g) == (h, s, t) + assert gcd(f, g) == h + assert lcm(f, g) == r + + assert cofactors(f, g, x) == (h, s, t) + assert gcd(f, g, x) == h + assert lcm(f, g, x) == r + + assert cofactors(f, g, (x,)) == (h, s, t) + assert gcd(f, g, (x,)) == h + assert lcm(f, g, (x,)) == r + + assert cofactors(F, G) == (H, S, T) + assert gcd(F, G) == H + assert lcm(F, G) == R + + assert cofactors(f, g, polys=True) == (H, S, T) + assert gcd(f, g, polys=True) == H + assert lcm(f, g, polys=True) == R + + assert cofactors(F, G, polys=False) == (h, s, t) + assert gcd(F, G, polys=False) == h + assert lcm(F, G, polys=False) == r + + f, g = 1.0*x**2 - 1.0, 1.0*x - 1.0 + h, s, t = g, 1.0*x + 1.0, 1.0 + + assert cofactors(f, g) == (h, s, t) + assert gcd(f, g) == h + assert lcm(f, g) == f + + f, g = 1.0*x**2 - 1.0, 1.0*x - 1.0 + h, s, t = g, 1.0*x + 1.0, 1.0 + + assert cofactors(f, g) == (h, s, t) + assert gcd(f, g) == h + assert lcm(f, g) == f + + assert cofactors(8, 6) == (2, 4, 3) + assert gcd(8, 6) == 2 + assert lcm(8, 6) == 24 + + f, g = x**2 - 3*x - 4, x**3 - 4*x**2 + x - 4 + l = x**4 - 3*x**3 - 3*x**2 - 3*x - 4 + h, s, t = x - 4, x + 1, x**2 + 1 + + assert cofactors(f, g, modulus=11) == (h, s, t) + assert gcd(f, g, modulus=11) == h + assert lcm(f, g, modulus=11) == l + + f, g = x**2 + 8*x + 7, x**3 + 7*x**2 + x + 7 + l = x**4 + 8*x**3 + 8*x**2 + 8*x + 7 + h, s, t = x + 7, x + 1, x**2 + 1 + + assert cofactors(f, g, modulus=11, symmetric=False) == (h, s, t) + assert gcd(f, g, modulus=11, symmetric=False) == h + assert lcm(f, g, modulus=11, symmetric=False) == l + + a, b = sqrt(2), -sqrt(2) + assert gcd(a, b) == gcd(b, a) == sqrt(2) + + a, b = sqrt(-2), -sqrt(-2) + assert gcd(a, b) == gcd(b, a) == sqrt(2) + + assert gcd(Poly(x - 2, x), Poly(I*x, x)) == Poly(1, x, domain=ZZ_I) + + raises(TypeError, lambda: gcd(x)) + raises(TypeError, lambda: lcm(x)) + + f = Poly(-1, x) + g = Poly(1, x) + assert lcm(f, g) == Poly(1, x) + + f = Poly(0, x) + g = Poly([1, 1], x) + for i in (f, g): + assert lcm(i, 0) == 0 + assert lcm(0, i) == 0 + assert lcm(i, f) == 0 + assert lcm(f, i) == 0 + + f = 4*x**2 + x + 2 + pfz = Poly(f, domain=ZZ) + pfq = Poly(f, domain=QQ) + + assert pfz.gcd(pfz) == pfz + assert pfz.lcm(pfz) == pfz + assert pfq.gcd(pfq) == pfq.monic() + assert pfq.lcm(pfq) == pfq.monic() + assert gcd(f, f) == f + assert lcm(f, f) == f + assert gcd(f, f, domain=QQ) == monic(f) + assert lcm(f, f, domain=QQ) == monic(f) + + +def test_gcd_numbers_vs_polys(): + assert isinstance(gcd(3, 9), Integer) + assert isinstance(gcd(3*x, 9), Integer) + + assert gcd(3, 9) == 3 + assert gcd(3*x, 9) == 3 + + assert isinstance(gcd(Rational(3, 2), Rational(9, 4)), Rational) + assert isinstance(gcd(Rational(3, 2)*x, Rational(9, 4)), Rational) + + assert gcd(Rational(3, 2), Rational(9, 4)) == Rational(3, 4) + assert gcd(Rational(3, 2)*x, Rational(9, 4)) == 1 + + assert isinstance(gcd(3.0, 9.0), Float) + assert isinstance(gcd(3.0*x, 9.0), Float) + + assert gcd(3.0, 9.0) == 1.0 + assert gcd(3.0*x, 9.0) == 1.0 + + # partial fix of 20597 + assert gcd(Mul(2, 3, evaluate=False), 2) == 2 + + +def test_terms_gcd(): + assert terms_gcd(1) == 1 + assert terms_gcd(1, x) == 1 + + assert terms_gcd(x - 1) == x - 1 + assert terms_gcd(-x - 1) == -x - 1 + + assert terms_gcd(2*x + 3) == 2*x + 3 + assert terms_gcd(6*x + 4) == Mul(2, 3*x + 2, evaluate=False) + + assert terms_gcd(x**3*y + x*y**3) == x*y*(x**2 + y**2) + assert terms_gcd(2*x**3*y + 2*x*y**3) == 2*x*y*(x**2 + y**2) + assert terms_gcd(x**3*y/2 + x*y**3/2) == x*y/2*(x**2 + y**2) + + assert terms_gcd(x**3*y + 2*x*y**3) == x*y*(x**2 + 2*y**2) + assert terms_gcd(2*x**3*y + 4*x*y**3) == 2*x*y*(x**2 + 2*y**2) + assert terms_gcd(2*x**3*y/3 + 4*x*y**3/5) == x*y*Rational(2, 15)*(5*x**2 + 6*y**2) + + assert terms_gcd(2.0*x**3*y + 4.1*x*y**3) == x*y*(2.0*x**2 + 4.1*y**2) + assert _aresame(terms_gcd(2.0*x + 3), 2.0*x + 3) + + assert terms_gcd((3 + 3*x)*(x + x*y), expand=False) == \ + (3*x + 3)*(x*y + x) + assert terms_gcd((3 + 3*x)*(x + x*sin(3 + 3*y)), expand=False, deep=True) == \ + 3*x*(x + 1)*(sin(Mul(3, y + 1, evaluate=False)) + 1) + assert terms_gcd(sin(x + x*y), deep=True) == \ + sin(x*(y + 1)) + + eq = Eq(2*x, 2*y + 2*z*y) + assert terms_gcd(eq) == Eq(2*x, 2*y*(z + 1)) + assert terms_gcd(eq, deep=True) == Eq(2*x, 2*y*(z + 1)) + + raises(TypeError, lambda: terms_gcd(x < 2)) + + +def test_trunc(): + f, g = x**5 + 2*x**4 + 3*x**3 + 4*x**2 + 5*x + 6, x**5 - x**4 + x**2 - x + F, G = Poly(f), Poly(g) + + assert F.trunc(3) == G + assert trunc(f, 3) == g + assert trunc(f, 3, x) == g + assert trunc(f, 3, (x,)) == g + assert trunc(F, 3) == G + assert trunc(f, 3, polys=True) == G + assert trunc(F, 3, polys=False) == g + + f, g = 6*x**5 + 5*x**4 + 4*x**3 + 3*x**2 + 2*x + 1, -x**4 + x**3 - x + 1 + F, G = Poly(f), Poly(g) + + assert F.trunc(3) == G + assert trunc(f, 3) == g + assert trunc(f, 3, x) == g + assert trunc(f, 3, (x,)) == g + assert trunc(F, 3) == G + assert trunc(f, 3, polys=True) == G + assert trunc(F, 3, polys=False) == g + + f = Poly(x**2 + 2*x + 3, modulus=5) + + assert f.trunc(2) == Poly(x**2 + 1, modulus=5) + + +def test_monic(): + f, g = 2*x - 1, x - S.Half + F, G = Poly(f, domain='QQ'), Poly(g) + + assert F.monic() == G + assert monic(f) == g + assert monic(f, x) == g + assert monic(f, (x,)) == g + assert monic(F) == G + assert monic(f, polys=True) == G + assert monic(F, polys=False) == g + + raises(ComputationFailed, lambda: monic(4)) + + assert monic(2*x**2 + 6*x + 4, auto=False) == x**2 + 3*x + 2 + raises(ExactQuotientFailed, lambda: monic(2*x + 6*x + 1, auto=False)) + + assert monic(2.0*x**2 + 6.0*x + 4.0) == 1.0*x**2 + 3.0*x + 2.0 + assert monic(2*x**2 + 3*x + 4, modulus=5) == x**2 - x + 2 + + +def test_content(): + f, F = 4*x + 2, Poly(4*x + 2) + + assert F.content() == 2 + assert content(f) == 2 + + raises(ComputationFailed, lambda: content(4)) + + f = Poly(2*x, modulus=3) + + assert f.content() == 1 + + +def test_primitive(): + f, g = 4*x + 2, 2*x + 1 + F, G = Poly(f), Poly(g) + + assert F.primitive() == (2, G) + assert primitive(f) == (2, g) + assert primitive(f, x) == (2, g) + assert primitive(f, (x,)) == (2, g) + assert primitive(F) == (2, G) + assert primitive(f, polys=True) == (2, G) + assert primitive(F, polys=False) == (2, g) + + raises(ComputationFailed, lambda: primitive(4)) + + f = Poly(2*x, modulus=3) + g = Poly(2.0*x, domain=RR) + + assert f.primitive() == (1, f) + assert g.primitive() == (1.0, g) + + assert primitive(S('-3*x/4 + y + 11/8')) == \ + S('(1/8, -6*x + 8*y + 11)') + + +def test_compose(): + f = x**12 + 20*x**10 + 150*x**8 + 500*x**6 + 625*x**4 - 2*x**3 - 10*x + 9 + g = x**4 - 2*x + 9 + h = x**3 + 5*x + + F, G, H = map(Poly, (f, g, h)) + + assert G.compose(H) == F + assert compose(g, h) == f + assert compose(g, h, x) == f + assert compose(g, h, (x,)) == f + assert compose(G, H) == F + assert compose(g, h, polys=True) == F + assert compose(G, H, polys=False) == f + + assert F.decompose() == [G, H] + assert decompose(f) == [g, h] + assert decompose(f, x) == [g, h] + assert decompose(f, (x,)) == [g, h] + assert decompose(F) == [G, H] + assert decompose(f, polys=True) == [G, H] + assert decompose(F, polys=False) == [g, h] + + raises(ComputationFailed, lambda: compose(4, 2)) + raises(ComputationFailed, lambda: decompose(4)) + + assert compose(x**2 - y**2, x - y, x, y) == x**2 - 2*x*y + assert compose(x**2 - y**2, x - y, y, x) == -y**2 + 2*x*y + + +def test_shift(): + assert Poly(x**2 - 2*x + 1, x).shift(2) == Poly(x**2 + 2*x + 1, x) + + +def test_shift_list(): + assert Poly(x*y, [x,y]).shift_list([1,2]) == Poly((x+1)*(y+2), [x,y]) + + +def test_transform(): + # Also test that 3-way unification is done correctly + assert Poly(x**2 - 2*x + 1, x).transform(Poly(x + 1), Poly(x - 1)) == \ + Poly(4, x) == \ + cancel((x - 1)**2*(x**2 - 2*x + 1).subs(x, (x + 1)/(x - 1))) + + assert Poly(x**2 - x/2 + 1, x).transform(Poly(x + 1), Poly(x - 1)) == \ + Poly(3*x**2/2 + Rational(5, 2), x) == \ + cancel((x - 1)**2*(x**2 - x/2 + 1).subs(x, (x + 1)/(x - 1))) + + assert Poly(x**2 - 2*x + 1, x).transform(Poly(x + S.Half), Poly(x - 1)) == \ + Poly(Rational(9, 4), x) == \ + cancel((x - 1)**2*(x**2 - 2*x + 1).subs(x, (x + S.Half)/(x - 1))) + + assert Poly(x**2 - 2*x + 1, x).transform(Poly(x + 1), Poly(x - S.Half)) == \ + Poly(Rational(9, 4), x) == \ + cancel((x - S.Half)**2*(x**2 - 2*x + 1).subs(x, (x + 1)/(x - S.Half))) + + # Unify ZZ, QQ, and RR + assert Poly(x**2 - 2*x + 1, x).transform(Poly(x + 1.0), Poly(x - S.Half)) == \ + Poly(Rational(9, 4), x, domain='RR') == \ + cancel((x - S.Half)**2*(x**2 - 2*x + 1).subs(x, (x + 1.0)/(x - S.Half))) + + raises(ValueError, lambda: Poly(x*y).transform(Poly(x + 1), Poly(x - 1))) + raises(ValueError, lambda: Poly(x).transform(Poly(y + 1), Poly(x - 1))) + raises(ValueError, lambda: Poly(x).transform(Poly(x + 1), Poly(y - 1))) + raises(ValueError, lambda: Poly(x).transform(Poly(x*y + 1), Poly(x - 1))) + raises(ValueError, lambda: Poly(x).transform(Poly(x + 1), Poly(x*y - 1))) + + +def test_sturm(): + f, F = x, Poly(x, domain='QQ') + g, G = 1, Poly(1, x, domain='QQ') + + assert F.sturm() == [F, G] + assert sturm(f) == [f, g] + assert sturm(f, x) == [f, g] + assert sturm(f, (x,)) == [f, g] + assert sturm(F) == [F, G] + assert sturm(f, polys=True) == [F, G] + assert sturm(F, polys=False) == [f, g] + + raises(ComputationFailed, lambda: sturm(4)) + raises(DomainError, lambda: sturm(f, auto=False)) + + f = Poly(S(1024)/(15625*pi**8)*x**5 + - S(4096)/(625*pi**8)*x**4 + + S(32)/(15625*pi**4)*x**3 + - S(128)/(625*pi**4)*x**2 + + Rational(1, 62500)*x + - Rational(1, 625), x, domain='ZZ(pi)') + + assert sturm(f) == \ + [Poly(x**3 - 100*x**2 + pi**4/64*x - 25*pi**4/16, x, domain='ZZ(pi)'), + Poly(3*x**2 - 200*x + pi**4/64, x, domain='ZZ(pi)'), + Poly((Rational(20000, 9) - pi**4/96)*x + 25*pi**4/18, x, domain='ZZ(pi)'), + Poly((-3686400000000*pi**4 - 11520000*pi**8 - 9*pi**12)/(26214400000000 - 245760000*pi**4 + 576*pi**8), x, domain='ZZ(pi)')] + + +def test_gff(): + f = x**5 + 2*x**4 - x**3 - 2*x**2 + + assert Poly(f).gff_list() == [(Poly(x), 1), (Poly(x + 2), 4)] + assert gff_list(f) == [(x, 1), (x + 2, 4)] + + raises(NotImplementedError, lambda: gff(f)) + + f = x*(x - 1)**3*(x - 2)**2*(x - 4)**2*(x - 5) + + assert Poly(f).gff_list() == [( + Poly(x**2 - 5*x + 4), 1), (Poly(x**2 - 5*x + 4), 2), (Poly(x), 3)] + assert gff_list(f) == [(x**2 - 5*x + 4, 1), (x**2 - 5*x + 4, 2), (x, 3)] + + raises(NotImplementedError, lambda: gff(f)) + + +def test_norm(): + a, b = sqrt(2), sqrt(3) + f = Poly(a*x + b*y, x, y, extension=(a, b)) + assert f.norm() == Poly(4*x**4 - 12*x**2*y**2 + 9*y**4, x, y, domain='QQ') + + +def test_sqf_norm(): + assert sqf_norm(x**2 - 2, extension=sqrt(3)) == \ + ([1], x**2 - 2*sqrt(3)*x + 1, x**4 - 10*x**2 + 1) + assert sqf_norm(x**2 - 3, extension=sqrt(2)) == \ + ([1], x**2 - 2*sqrt(2)*x - 1, x**4 - 10*x**2 + 1) + + assert Poly(x**2 - 2, extension=sqrt(3)).sqf_norm() == \ + ([1], Poly(x**2 - 2*sqrt(3)*x + 1, x, extension=sqrt(3)), + Poly(x**4 - 10*x**2 + 1, x, domain='QQ')) + + assert Poly(x**2 - 3, extension=sqrt(2)).sqf_norm() == \ + ([1], Poly(x**2 - 2*sqrt(2)*x - 1, x, extension=sqrt(2)), + Poly(x**4 - 10*x**2 + 1, x, domain='QQ')) + + +def test_sqf(): + f = x**5 - x**3 - x**2 + 1 + g = x**3 + 2*x**2 + 2*x + 1 + h = x - 1 + + p = x**4 + x**3 - x - 1 + + F, G, H, P = map(Poly, (f, g, h, p)) + + assert F.sqf_part() == P + assert sqf_part(f) == p + assert sqf_part(f, x) == p + assert sqf_part(f, (x,)) == p + assert sqf_part(F) == P + assert sqf_part(f, polys=True) == P + assert sqf_part(F, polys=False) == p + + assert F.sqf_list() == (1, [(G, 1), (H, 2)]) + assert sqf_list(f) == (1, [(g, 1), (h, 2)]) + assert sqf_list(f, x) == (1, [(g, 1), (h, 2)]) + assert sqf_list(f, (x,)) == (1, [(g, 1), (h, 2)]) + assert sqf_list(F) == (1, [(G, 1), (H, 2)]) + assert sqf_list(f, polys=True) == (1, [(G, 1), (H, 2)]) + assert sqf_list(F, polys=False) == (1, [(g, 1), (h, 2)]) + + assert F.sqf_list_include() == [(G, 1), (H, 2)] + + raises(ComputationFailed, lambda: sqf_part(4)) + + assert sqf(1) == 1 + assert sqf_list(1) == (1, []) + + assert sqf((2*x**2 + 2)**7) == 128*(x**2 + 1)**7 + + assert sqf(f) == g*h**2 + assert sqf(f, x) == g*h**2 + assert sqf(f, (x,)) == g*h**2 + + d = x**2 + y**2 + + assert sqf(f/d) == (g*h**2)/d + assert sqf(f/d, x) == (g*h**2)/d + assert sqf(f/d, (x,)) == (g*h**2)/d + + assert sqf(x - 1) == x - 1 + assert sqf(-x - 1) == -x - 1 + + assert sqf(x - 1) == x - 1 + assert sqf(6*x - 10) == Mul(2, 3*x - 5, evaluate=False) + + assert sqf((6*x - 10)/(3*x - 6)) == Rational(2, 3)*((3*x - 5)/(x - 2)) + assert sqf(Poly(x**2 - 2*x + 1)) == (x - 1)**2 + + f = 3 + x - x*(1 + x) + x**2 + + assert sqf(f) == 3 + + f = (x**2 + 2*x + 1)**20000000000 + + assert sqf(f) == (x + 1)**40000000000 + assert sqf_list(f) == (1, [(x + 1, 40000000000)]) + + # https://github.com/sympy/sympy/issues/26497 + assert sqf(expand(((y - 2)**2 * (y + 2) * (x + 1)))) == \ + (y - 2)**2 * expand((y + 2) * (x + 1)) + assert sqf(expand(((y - 2)**2 * (y + 2) * (z + 1)))) == \ + (y - 2)**2 * expand((y + 2) * (z + 1)) + assert sqf(expand(((y - I)**2 * (y + I) * (x + 1)))) == \ + (y - I)**2 * expand((y + I) * (x + 1)) + assert sqf(expand(((y - I)**2 * (y + I) * (z + 1)))) == \ + (y - I)**2 * expand((y + I) * (z + 1)) + + # Check that factors are combined and sorted. + p = (x - 2)**2*(x - 1)*(x + y)**2*(y - 2)**2*(y - 1) + assert Poly(p).sqf_list() == (1, [ + (Poly(x*y - x - y + 1), 1), + (Poly(x**2*y - 2*x**2 + x*y**2 - 4*x*y + 4*x - 2*y**2 + 4*y), 2) + ]) + + +def test_factor(): + f = x**5 - x**3 - x**2 + 1 + + u = x + 1 + v = x - 1 + w = x**2 + x + 1 + + F, U, V, W = map(Poly, (f, u, v, w)) + + assert F.factor_list() == (1, [(U, 1), (V, 2), (W, 1)]) + assert factor_list(f) == (1, [(u, 1), (v, 2), (w, 1)]) + assert factor_list(f, x) == (1, [(u, 1), (v, 2), (w, 1)]) + assert factor_list(f, (x,)) == (1, [(u, 1), (v, 2), (w, 1)]) + assert factor_list(F) == (1, [(U, 1), (V, 2), (W, 1)]) + assert factor_list(f, polys=True) == (1, [(U, 1), (V, 2), (W, 1)]) + assert factor_list(F, polys=False) == (1, [(u, 1), (v, 2), (w, 1)]) + + assert F.factor_list_include() == [(U, 1), (V, 2), (W, 1)] + + assert factor_list(1) == (1, []) + assert factor_list(6) == (6, []) + assert factor_list(sqrt(3), x) == (sqrt(3), []) + assert factor_list((-1)**x, x) == (1, [(-1, x)]) + assert factor_list((2*x)**y, x) == (1, [(2, y), (x, y)]) + assert factor_list(sqrt(x*y), x) == (1, [(x*y, S.Half)]) + + assert factor(6) == 6 and factor(6).is_Integer + + assert factor_list(3*x) == (3, [(x, 1)]) + assert factor_list(3*x**2) == (3, [(x, 2)]) + + assert factor(3*x) == 3*x + assert factor(3*x**2) == 3*x**2 + + assert factor((2*x**2 + 2)**7) == 128*(x**2 + 1)**7 + + assert factor(f) == u*v**2*w + assert factor(f, x) == u*v**2*w + assert factor(f, (x,)) == u*v**2*w + + g, p, q, r = x**2 - y**2, x - y, x + y, x**2 + 1 + + assert factor(f/g) == (u*v**2*w)/(p*q) + assert factor(f/g, x) == (u*v**2*w)/(p*q) + assert factor(f/g, (x,)) == (u*v**2*w)/(p*q) + + p = Symbol('p', positive=True) + i = Symbol('i', integer=True) + r = Symbol('r', real=True) + + assert factor(sqrt(x*y)).is_Pow is True + + assert factor(sqrt(3*x**2 - 3)) == sqrt(3)*sqrt((x - 1)*(x + 1)) + assert factor(sqrt(3*x**2 + 3)) == sqrt(3)*sqrt(x**2 + 1) + + assert factor((y*x**2 - y)**i) == y**i*(x - 1)**i*(x + 1)**i + assert factor((y*x**2 + y)**i) == y**i*(x**2 + 1)**i + + assert factor((y*x**2 - y)**t) == (y*(x - 1)*(x + 1))**t + assert factor((y*x**2 + y)**t) == (y*(x**2 + 1))**t + + f = sqrt(expand((r**2 + 1)*(p + 1)*(p - 1)*(p - 2)**3)) + g = sqrt((p - 2)**3*(p - 1))*sqrt(p + 1)*sqrt(r**2 + 1) + + assert factor(f) == g + assert factor(g) == g + + g = (x - 1)**5*(r**2 + 1) + f = sqrt(expand(g)) + + assert factor(f) == sqrt(g) + + f = Poly(sin(1)*x + 1, x, domain=EX) + + assert f.factor_list() == (1, [(f, 1)]) + + f = x**4 + 1 + + assert factor(f) == f + assert factor(f, extension=I) == (x**2 - I)*(x**2 + I) + assert factor(f, gaussian=True) == (x**2 - I)*(x**2 + I) + assert factor( + f, extension=sqrt(2)) == (x**2 + sqrt(2)*x + 1)*(x**2 - sqrt(2)*x + 1) + + assert factor(x**2 + 4*I*x - 4) == (x + 2*I)**2 + + f = x**2 + 2*I*x - 4 + + assert factor(f) == f + + f = 8192*x**2 + x*(22656 + 175232*I) - 921416 + 242313*I + f_zzi = I*(x*(64 - 64*I) + 773 + 596*I)**2 + f_qqi = 8192*(x + S(177)/128 + 1369*I/128)**2 + + assert factor(f) == f_zzi + assert factor(f, domain=ZZ_I) == f_zzi + assert factor(f, domain=QQ_I) == f_qqi + + f = x**2 + 2*sqrt(2)*x + 2 + + assert factor(f, extension=sqrt(2)) == (x + sqrt(2))**2 + assert factor(f**3, extension=sqrt(2)) == (x + sqrt(2))**6 + + assert factor(x**2 - 2*y**2, extension=sqrt(2)) == \ + (x + sqrt(2)*y)*(x - sqrt(2)*y) + assert factor(2*x**2 - 4*y**2, extension=sqrt(2)) == \ + 2*((x + sqrt(2)*y)*(x - sqrt(2)*y)) + + assert factor(x - 1) == x - 1 + assert factor(-x - 1) == -x - 1 + + assert factor(x - 1) == x - 1 + + assert factor(6*x - 10) == Mul(2, 3*x - 5, evaluate=False) + + assert factor(x**11 + x + 1, modulus=65537, symmetric=True) == \ + (x**2 + x + 1)*(x**9 - x**8 + x**6 - x**5 + x**3 - x** 2 + 1) + assert factor(x**11 + x + 1, modulus=65537, symmetric=False) == \ + (x**2 + x + 1)*(x**9 + 65536*x**8 + x**6 + 65536*x**5 + + x**3 + 65536*x** 2 + 1) + + f = x/pi + x*sin(x)/pi + g = y/(pi**2 + 2*pi + 1) + y*sin(x)/(pi**2 + 2*pi + 1) + + assert factor(f) == x*(sin(x) + 1)/pi + assert factor(g) == y*(sin(x) + 1)/(pi + 1)**2 + + assert factor(Eq( + x**2 + 2*x + 1, x**3 + 1)) == Eq((x + 1)**2, (x + 1)*(x**2 - x + 1)) + + f = (x**2 - 1)/(x**2 + 4*x + 4) + + assert factor(f) == (x + 1)*(x - 1)/(x + 2)**2 + assert factor(f, x) == (x + 1)*(x - 1)/(x + 2)**2 + + f = 3 + x - x*(1 + x) + x**2 + + assert factor(f) == 3 + assert factor(f, x) == 3 + + assert factor(1/(x**2 + 2*x + 1/x) - 1) == -((1 - x + 2*x**2 + + x**3)/(1 + 2*x**2 + x**3)) + + assert factor(f, expand=False) == f + raises(PolynomialError, lambda: factor(f, x, expand=False)) + + raises(FlagError, lambda: factor(x**2 - 1, polys=True)) + + assert factor([x, Eq(x**2 - y**2, Tuple(x**2 - z**2, 1/x + 1/y))]) == \ + [x, Eq((x - y)*(x + y), Tuple((x - z)*(x + z), (x + y)/x/y))] + + assert not isinstance( + Poly(x**3 + x + 1).factor_list()[1][0][0], PurePoly) is True + assert isinstance( + PurePoly(x**3 + x + 1).factor_list()[1][0][0], PurePoly) is True + + assert factor(sqrt(-x)) == sqrt(-x) + + # issue 5917 + e = (-2*x*(-x + 1)*(x - 1)*(-x*(-x + 1)*(x - 1) - x*(x - 1)**2)*(x**2*(x - + 1) - x*(x - 1) - x) - (-2*x**2*(x - 1)**2 - x*(-x + 1)*(-x*(-x + 1) + + x*(x - 1)))*(x**2*(x - 1)**4 - x*(-x*(-x + 1)*(x - 1) - x*(x - 1)**2))) + assert factor(e) == 0 + + # deep option + assert factor(sin(x**2 + x) + x, deep=True) == sin(x*(x + 1)) + x + assert factor(sin(x**2 + x)*x, deep=True) == sin(x*(x + 1))*x + + assert factor(sqrt(x**2)) == sqrt(x**2) + + # issue 13149 + assert factor(expand((0.5*x+1)*(0.5*y+1))) == Mul(1.0, 0.5*x + 1.0, + 0.5*y + 1.0, evaluate = False) + assert factor(expand((0.5*x+0.5)**2)) == 0.25*(1.0*x + 1.0)**2 + + eq = x**2*y**2 + 11*x**2*y + 30*x**2 + 7*x*y**2 + 77*x*y + 210*x + 12*y**2 + 132*y + 360 + assert factor(eq, x) == (x + 3)*(x + 4)*(y**2 + 11*y + 30) + assert factor(eq, x, deep=True) == (x + 3)*(x + 4)*(y**2 + 11*y + 30) + assert factor(eq, y, deep=True) == (y + 5)*(y + 6)*(x**2 + 7*x + 12) + + # fraction option + f = 5*x + 3*exp(2 - 7*x) + assert factor(f, deep=True) == factor(f, deep=True, fraction=True) + assert factor(f, deep=True, fraction=False) == 5*x + 3*exp(2)*exp(-7*x) + + assert factor_list(x**3 - x*y**2, t, w, x) == ( + 1, [(x, 1), (x - y, 1), (x + y, 1)]) + assert factor_list((x+1)*(x**6-1)) == ( + 1, [(x - 1, 1), (x + 1, 2), (x**2 - x + 1, 1), (x**2 + x + 1, 1)]) + + # https://github.com/sympy/sympy/issues/24952 + s2, s2p, s2n = sqrt(2), 1 + sqrt(2), 1 - sqrt(2) + pip, pin = 1 + pi, 1 - pi + assert factor_list(s2p*s2n) == (-1, [(-s2n, 1), (s2p, 1)]) + assert factor_list(pip*pin) == (-1, [(-pin, 1), (pip, 1)]) + # Not sure about this one. Maybe coeff should be 1 or -1? + assert factor_list(s2*s2n) == (-s2, [(-s2n, 1)]) + assert factor_list(pi*pin) == (-1, [(-pin, 1), (pi, 1)]) + assert factor_list(s2p*s2n, x) == (s2p*s2n, []) + assert factor_list(pip*pin, x) == (pip*pin, []) + assert factor_list(s2*s2n, x) == (s2*s2n, []) + assert factor_list(pi*pin, x) == (pi*pin, []) + assert factor_list((x - sqrt(2)*pi)*(x + sqrt(2)*pi), x) == ( + 1, [(x - sqrt(2)*pi, 1), (x + sqrt(2)*pi, 1)]) + + # https://github.com/sympy/sympy/issues/26497 + p = ((y - I)**2 * (y + I) * (x + 1)) + assert factor(expand(p)) == p + + p = ((x - I)**2 * (x + I) * (y + 1)) + assert factor(expand(p)) == p + + p = (y + 1)**2*(y + sqrt(2))**2*(x**2 + x + 2 + 3*sqrt(2))**2 + assert factor(expand(p), extension=True) == p + + e = ( + -x**2*y**4/(y**2 + 1) + 2*I*x**2*y**3/(y**2 + 1) + 2*I*x**2*y/(y**2 + 1) + + x**2/(y**2 + 1) - 2*x*y**4/(y**2 + 1) + 4*I*x*y**3/(y**2 + 1) + + 4*I*x*y/(y**2 + 1) + 2*x/(y**2 + 1) - y**4 - y**4/(y**2 + 1) + 2*I*y**3 + + 2*I*y**3/(y**2 + 1) + 2*I*y + 2*I*y/(y**2 + 1) + 1 + 1/(y**2 + 1) + ) + assert factor(e) == -(y - I)**3*(y + I)*(x**2 + 2*x + y**2 + 2)/(y**2 + 1) + + # issue 27506 + e = (I*t*x*y - 3*I*t - I*x*y*z - 6*x*y + 3*I*z + 18) + assert factor(e) == -I*(x*y - 3)*(-t + z - 6*I) + + e = (8*x**2*z**2 - 32*x**2*z*t + 24*x**2*t**2 - 4*I*x*y*z**2 + 16*I*x*y*z*t - + 12*I*x*y*t**2 + z**4 - 8*z**3*t + 22*z**2*t**2 - 24*z*t**3 + 9*t**4) + assert factor(e) == (-3*t + z)*(-t + z)*(3*t**2 - 4*t*z + 8*x**2 - 4*I*x*y + z**2) + + +def test_factor_large(): + f = (x**2 + 4*x + 4)**10000000*(x**2 + 1)*(x**2 + 2*x + 1)**1234567 + g = ((x**2 + 2*x + 1)**3000*y**2 + (x**2 + 2*x + 1)**3000*2*y + ( + x**2 + 2*x + 1)**3000) + + assert factor(f) == (x + 2)**20000000*(x**2 + 1)*(x + 1)**2469134 + assert factor(g) == (x + 1)**6000*(y + 1)**2 + + assert factor_list( + f) == (1, [(x + 1, 2469134), (x + 2, 20000000), (x**2 + 1, 1)]) + assert factor_list(g) == (1, [(y + 1, 2), (x + 1, 6000)]) + + f = (x**2 - y**2)**200000*(x**7 + 1) + g = (x**2 + y**2)**200000*(x**7 + 1) + + assert factor(f) == \ + (x + 1)*(x - y)**200000*(x + y)**200000*(x**6 - x**5 + + x**4 - x**3 + x**2 - x + 1) + assert factor(g, gaussian=True) == \ + (x + 1)*(x - I*y)**200000*(x + I*y)**200000*(x**6 - x**5 + + x**4 - x**3 + x**2 - x + 1) + + assert factor_list(f) == \ + (1, [(x + 1, 1), (x - y, 200000), (x + y, 200000), (x**6 - + x**5 + x**4 - x**3 + x**2 - x + 1, 1)]) + assert factor_list(g, gaussian=True) == \ + (1, [(x + 1, 1), (x - I*y, 200000), (x + I*y, 200000), ( + x**6 - x**5 + x**4 - x**3 + x**2 - x + 1, 1)]) + + +def test_factor_noeval(): + assert factor(6*x - 10) == Mul(2, 3*x - 5, evaluate=False) + assert factor((6*x - 10)/(3*x - 6)) == Mul(Rational(2, 3), 3*x - 5, 1/(x - 2)) + + +def test_intervals(): + assert intervals(0) == [] + assert intervals(1) == [] + + assert intervals(x, sqf=True) == [(0, 0)] + assert intervals(x) == [((0, 0), 1)] + + assert intervals(x**128) == [((0, 0), 128)] + assert intervals([x**2, x**4]) == [((0, 0), {0: 2, 1: 4})] + + f = Poly((x*Rational(2, 5) - Rational(17, 3))*(4*x + Rational(1, 257))) + + assert f.intervals(sqf=True) == [(-1, 0), (14, 15)] + assert f.intervals() == [((-1, 0), 1), ((14, 15), 1)] + + assert f.intervals(fast=True, sqf=True) == [(-1, 0), (14, 15)] + assert f.intervals(fast=True) == [((-1, 0), 1), ((14, 15), 1)] + + assert f.intervals(eps=Rational(1, 10)) == f.intervals(eps=0.1) == \ + [((Rational(-1, 258), 0), 1), ((Rational(85, 6), Rational(85, 6)), 1)] + assert f.intervals(eps=Rational(1, 100)) == f.intervals(eps=0.01) == \ + [((Rational(-1, 258), 0), 1), ((Rational(85, 6), Rational(85, 6)), 1)] + assert f.intervals(eps=Rational(1, 1000)) == f.intervals(eps=0.001) == \ + [((Rational(-1, 1002), 0), 1), ((Rational(85, 6), Rational(85, 6)), 1)] + assert f.intervals(eps=Rational(1, 10000)) == f.intervals(eps=0.0001) == \ + [((Rational(-1, 1028), Rational(-1, 1028)), 1), ((Rational(85, 6), Rational(85, 6)), 1)] + + f = (x*Rational(2, 5) - Rational(17, 3))*(4*x + Rational(1, 257)) + + assert intervals(f, sqf=True) == [(-1, 0), (14, 15)] + assert intervals(f) == [((-1, 0), 1), ((14, 15), 1)] + + assert intervals(f, eps=Rational(1, 10)) == intervals(f, eps=0.1) == \ + [((Rational(-1, 258), 0), 1), ((Rational(85, 6), Rational(85, 6)), 1)] + assert intervals(f, eps=Rational(1, 100)) == intervals(f, eps=0.01) == \ + [((Rational(-1, 258), 0), 1), ((Rational(85, 6), Rational(85, 6)), 1)] + assert intervals(f, eps=Rational(1, 1000)) == intervals(f, eps=0.001) == \ + [((Rational(-1, 1002), 0), 1), ((Rational(85, 6), Rational(85, 6)), 1)] + assert intervals(f, eps=Rational(1, 10000)) == intervals(f, eps=0.0001) == \ + [((Rational(-1, 1028), Rational(-1, 1028)), 1), ((Rational(85, 6), Rational(85, 6)), 1)] + + f = Poly((x**2 - 2)*(x**2 - 3)**7*(x + 1)*(7*x + 3)**3) + + assert f.intervals() == \ + [((-2, Rational(-3, 2)), 7), ((Rational(-3, 2), -1), 1), + ((-1, -1), 1), ((-1, 0), 3), + ((1, Rational(3, 2)), 1), ((Rational(3, 2), 2), 7)] + + assert intervals([x**5 - 200, x**5 - 201]) == \ + [((Rational(75, 26), Rational(101, 35)), {0: 1}), ((Rational(309, 107), Rational(26, 9)), {1: 1})] + + assert intervals([x**5 - 200, x**5 - 201], fast=True) == \ + [((Rational(75, 26), Rational(101, 35)), {0: 1}), ((Rational(309, 107), Rational(26, 9)), {1: 1})] + + assert intervals([x**2 - 200, x**2 - 201]) == \ + [((Rational(-71, 5), Rational(-85, 6)), {1: 1}), ((Rational(-85, 6), -14), {0: 1}), + ((14, Rational(85, 6)), {0: 1}), ((Rational(85, 6), Rational(71, 5)), {1: 1})] + + assert intervals([x + 1, x + 2, x - 1, x + 1, 1, x - 1, x - 1, (x - 2)**2]) == \ + [((-2, -2), {1: 1}), ((-1, -1), {0: 1, 3: 1}), ((1, 1), {2: + 1, 5: 1, 6: 1}), ((2, 2), {7: 2})] + + f, g, h = x**2 - 2, x**4 - 4*x**2 + 4, x - 1 + + assert intervals(f, inf=Rational(7, 4), sqf=True) == [] + assert intervals(f, inf=Rational(7, 5), sqf=True) == [(Rational(7, 5), Rational(3, 2))] + assert intervals(f, sup=Rational(7, 4), sqf=True) == [(-2, -1), (1, Rational(3, 2))] + assert intervals(f, sup=Rational(7, 5), sqf=True) == [(-2, -1)] + + assert intervals(g, inf=Rational(7, 4)) == [] + assert intervals(g, inf=Rational(7, 5)) == [((Rational(7, 5), Rational(3, 2)), 2)] + assert intervals(g, sup=Rational(7, 4)) == [((-2, -1), 2), ((1, Rational(3, 2)), 2)] + assert intervals(g, sup=Rational(7, 5)) == [((-2, -1), 2)] + + assert intervals([g, h], inf=Rational(7, 4)) == [] + assert intervals([g, h], inf=Rational(7, 5)) == [((Rational(7, 5), Rational(3, 2)), {0: 2})] + assert intervals([g, h], sup=S( + 7)/4) == [((-2, -1), {0: 2}), ((1, 1), {1: 1}), ((1, Rational(3, 2)), {0: 2})] + assert intervals( + [g, h], sup=Rational(7, 5)) == [((-2, -1), {0: 2}), ((1, 1), {1: 1})] + + assert intervals([x + 2, x**2 - 2]) == \ + [((-2, -2), {0: 1}), ((-2, -1), {1: 1}), ((1, 2), {1: 1})] + assert intervals([x + 2, x**2 - 2], strict=True) == \ + [((-2, -2), {0: 1}), ((Rational(-3, 2), -1), {1: 1}), ((1, 2), {1: 1})] + + f = 7*z**4 - 19*z**3 + 20*z**2 + 17*z + 20 + + assert intervals(f) == [] + + real_part, complex_part = intervals(f, all=True, sqf=True) + + assert real_part == [] + assert all(re(a) < re(r) < re(b) and im( + a) < im(r) < im(b) for (a, b), r in zip(complex_part, nroots(f))) + + assert complex_part == [(Rational(-40, 7) - I*40/7, 0), + (Rational(-40, 7), I*40/7), + (I*Rational(-40, 7), Rational(40, 7)), + (0, Rational(40, 7) + I*40/7)] + + real_part, complex_part = intervals(f, all=True, sqf=True, eps=Rational(1, 10)) + + assert real_part == [] + assert all(re(a) < re(r) < re(b) and im( + a) < im(r) < im(b) for (a, b), r in zip(complex_part, nroots(f))) + + raises(ValueError, lambda: intervals(x**2 - 2, eps=10**-100000)) + raises(ValueError, lambda: Poly(x**2 - 2).intervals(eps=10**-100000)) + raises( + ValueError, lambda: intervals([x**2 - 2, x**2 - 3], eps=10**-100000)) + + +def test_refine_root(): + f = Poly(x**2 - 2) + + assert f.refine_root(1, 2, steps=0) == (1, 2) + assert f.refine_root(-2, -1, steps=0) == (-2, -1) + + assert f.refine_root(1, 2, steps=None) == (1, Rational(3, 2)) + assert f.refine_root(-2, -1, steps=None) == (Rational(-3, 2), -1) + + assert f.refine_root(1, 2, steps=1) == (1, Rational(3, 2)) + assert f.refine_root(-2, -1, steps=1) == (Rational(-3, 2), -1) + + assert f.refine_root(1, 2, steps=1, fast=True) == (1, Rational(3, 2)) + assert f.refine_root(-2, -1, steps=1, fast=True) == (Rational(-3, 2), -1) + + assert f.refine_root(1, 2, eps=Rational(1, 100)) == (Rational(24, 17), Rational(17, 12)) + assert f.refine_root(1, 2, eps=1e-2) == (Rational(24, 17), Rational(17, 12)) + + raises(PolynomialError, lambda: (f**2).refine_root(1, 2, check_sqf=True)) + + raises(RefinementFailed, lambda: (f**2).refine_root(1, 2)) + raises(RefinementFailed, lambda: (f**2).refine_root(2, 3)) + + f = x**2 - 2 + + assert refine_root(f, 1, 2, steps=1) == (1, Rational(3, 2)) + assert refine_root(f, -2, -1, steps=1) == (Rational(-3, 2), -1) + + assert refine_root(f, 1, 2, steps=1, fast=True) == (1, Rational(3, 2)) + assert refine_root(f, -2, -1, steps=1, fast=True) == (Rational(-3, 2), -1) + + assert refine_root(f, 1, 2, eps=Rational(1, 100)) == (Rational(24, 17), Rational(17, 12)) + assert refine_root(f, 1, 2, eps=1e-2) == (Rational(24, 17), Rational(17, 12)) + + raises(PolynomialError, lambda: refine_root(1, 7, 8, eps=Rational(1, 100))) + + raises(ValueError, lambda: Poly(f).refine_root(1, 2, eps=10**-100000)) + raises(ValueError, lambda: refine_root(f, 1, 2, eps=10**-100000)) + + +def test_count_roots(): + assert count_roots(x**2 - 2) == 2 + + assert count_roots(x**2 - 2, inf=-oo) == 2 + assert count_roots(x**2 - 2, sup=+oo) == 2 + assert count_roots(x**2 - 2, inf=-oo, sup=+oo) == 2 + + assert count_roots(x**2 - 2, inf=-2) == 2 + assert count_roots(x**2 - 2, inf=-1) == 1 + + assert count_roots(x**2 - 2, sup=1) == 1 + assert count_roots(x**2 - 2, sup=2) == 2 + + assert count_roots(x**2 - 2, inf=-1, sup=1) == 0 + assert count_roots(x**2 - 2, inf=-2, sup=2) == 2 + + assert count_roots(x**2 - 2, inf=-1, sup=1) == 0 + assert count_roots(x**2 - 2, inf=-2, sup=2) == 2 + + assert count_roots(x**2 + 2) == 0 + assert count_roots(x**2 + 2, inf=-2*I) == 2 + assert count_roots(x**2 + 2, sup=+2*I) == 2 + assert count_roots(x**2 + 2, inf=-2*I, sup=+2*I) == 2 + + assert count_roots(x**2 + 2, inf=0) == 0 + assert count_roots(x**2 + 2, sup=0) == 0 + + assert count_roots(x**2 + 2, inf=-I) == 1 + assert count_roots(x**2 + 2, sup=+I) == 1 + + assert count_roots(x**2 + 2, inf=+I/2, sup=+I) == 0 + assert count_roots(x**2 + 2, inf=-I, sup=-I/2) == 0 + + raises(PolynomialError, lambda: count_roots(1)) + + +def test_count_roots_extension(): + + p1 = Poly(sqrt(2)*x**2 - 2, x, extension=True) + assert p1.count_roots() == 2 + assert p1.count_roots(inf=0) == 1 + assert p1.count_roots(sup=0) == 1 + + p2 = Poly(x**2 + sqrt(2), x, extension=True) + assert p2.count_roots() == 0 + + p3 = Poly(x**2 + 2*sqrt(2)*x + 1, x, extension=True) + assert p3.count_roots() == 2 + assert p3.count_roots(inf=-10, sup=10) == 2 + assert p3.count_roots(inf=-10, sup=0) == 2 + assert p3.count_roots(inf=-10, sup=-3) == 0 + assert p3.count_roots(inf=-3, sup=-2) == 1 + assert p3.count_roots(inf=-1, sup=0) == 1 + + +def test_Poly_root(): + f = Poly(2*x**3 - 7*x**2 + 4*x + 4) + + assert f.root(0) == Rational(-1, 2) + assert f.root(1) == 2 + assert f.root(2) == 2 + raises(IndexError, lambda: f.root(3)) + + assert Poly(x**5 + x + 1).root(0) == rootof(x**3 - x**2 + 1, 0) + + +def test_real_roots(): + + assert real_roots(x) == [0] + assert real_roots(x, multiple=False) == [(0, 1)] + + assert real_roots(x**3) == [0, 0, 0] + assert real_roots(x**3, multiple=False) == [(0, 3)] + + assert real_roots(x*(x**3 + x + 3)) == [rootof(x**3 + x + 3, 0), 0] + assert real_roots(x*(x**3 + x + 3), multiple=False) == [(rootof( + x**3 + x + 3, 0), 1), (0, 1)] + + assert real_roots( + x**3*(x**3 + x + 3)) == [rootof(x**3 + x + 3, 0), 0, 0, 0] + assert real_roots(x**3*(x**3 + x + 3), multiple=False) == [(rootof( + x**3 + x + 3, 0), 1), (0, 3)] + + assert real_roots(x**2 - 2, radicals=False) == [ + rootof(x**2 - 2, 0, radicals=False), + rootof(x**2 - 2, 1, radicals=False), + ] + + f = 2*x**3 - 7*x**2 + 4*x + 4 + g = x**3 + x + 1 + + assert Poly(f).real_roots() == [Rational(-1, 2), 2, 2] + assert Poly(g).real_roots() == [rootof(g, 0)] + + # testing extension + f = x**2 - sqrt(2) + roots = [-2**(S(1)/4), 2**(S(1)/4)] + raises(NotImplementedError, lambda: real_roots(f)) + raises(NotImplementedError, lambda: real_roots(Poly(f, x))) + assert real_roots(f, extension=True) == roots + assert real_roots(Poly(f, extension=True)) == roots + assert real_roots(Poly(f), extension=True) == roots + + +def test_all_roots(): + + f = 2*x**3 - 7*x**2 + 4*x + 4 + froots = [Rational(-1, 2), 2, 2] + assert all_roots(f) == Poly(f).all_roots() == froots + + g = x**3 + x + 1 + groots = [rootof(g, 0), rootof(g, 1), rootof(g, 2)] + assert all_roots(g) == Poly(g).all_roots() == groots + + assert all_roots(x**2 - 2) == [-sqrt(2), sqrt(2)] + assert all_roots(x**2 - 2, multiple=False) == [(-sqrt(2), 1), (sqrt(2), 1)] + assert all_roots(x**2 - 2, radicals=False) == [ + rootof(x**2 - 2, 0, radicals=False), + rootof(x**2 - 2, 1, radicals=False), + ] + + p = x**5 - x - 1 + assert all_roots(p) == [ + rootof(p, 0), rootof(p, 1), rootof(p, 2), rootof(p, 3), rootof(p, 4) + ] + + # testing extension + f = x**2 + sqrt(2) + roots = [-2**(S(1)/4)*I, 2**(S(1)/4)*I] + raises(NotImplementedError, lambda: all_roots(f)) + raises(NotImplementedError, lambda : all_roots(Poly(f, x))) + assert all_roots(f, extension=True) == roots + assert all_roots(Poly(f, extension=True)) == roots + assert all_roots(Poly(f), extension=True) == roots + + +def test_nroots(): + assert Poly(0, x).nroots() == [] + assert Poly(1, x).nroots() == [] + + assert Poly(x**2 - 1, x).nroots() == [-1.0, 1.0] + assert Poly(x**2 + 1, x).nroots() == [-1.0*I, 1.0*I] + + roots = Poly(x**2 - 1, x).nroots() + assert roots == [-1.0, 1.0] + + roots = Poly(x**2 + 1, x).nroots() + assert roots == [-1.0*I, 1.0*I] + + roots = Poly(x**2/3 - Rational(1, 3), x).nroots() + assert roots == [-1.0, 1.0] + + roots = Poly(x**2/3 + Rational(1, 3), x).nroots() + assert roots == [-1.0*I, 1.0*I] + + assert Poly(x**2 + 2*I, x).nroots() == [-1.0 + 1.0*I, 1.0 - 1.0*I] + assert Poly( + x**2 + 2*I, x, extension=I).nroots() == [-1.0 + 1.0*I, 1.0 - 1.0*I] + + assert Poly(0.2*x + 0.1).nroots() == [-0.5] + + roots = nroots(x**5 + x + 1, n=5) + eps = Float("1e-5") + + assert re(roots[0]).epsilon_eq(-0.75487, eps) is S.true + assert im(roots[0]) == 0 + assert re(roots[1]) == Float(-0.5, 5) + assert im(roots[1]).epsilon_eq(-0.86602, eps) is S.true + assert re(roots[2]) == Float(-0.5, 5) + assert im(roots[2]).epsilon_eq(+0.86602, eps) is S.true + assert re(roots[3]).epsilon_eq(+0.87743, eps) is S.true + assert im(roots[3]).epsilon_eq(-0.74486, eps) is S.true + assert re(roots[4]).epsilon_eq(+0.87743, eps) is S.true + assert im(roots[4]).epsilon_eq(+0.74486, eps) is S.true + + eps = Float("1e-6") + + assert re(roots[0]).epsilon_eq(-0.75487, eps) is S.false + assert im(roots[0]) == 0 + assert re(roots[1]) == Float(-0.5, 5) + assert im(roots[1]).epsilon_eq(-0.86602, eps) is S.false + assert re(roots[2]) == Float(-0.5, 5) + assert im(roots[2]).epsilon_eq(+0.86602, eps) is S.false + assert re(roots[3]).epsilon_eq(+0.87743, eps) is S.false + assert im(roots[3]).epsilon_eq(-0.74486, eps) is S.false + assert re(roots[4]).epsilon_eq(+0.87743, eps) is S.false + assert im(roots[4]).epsilon_eq(+0.74486, eps) is S.false + + raises(DomainError, lambda: Poly(x + y, x).nroots()) + raises(MultivariatePolynomialError, lambda: Poly(x + y).nroots()) + + assert nroots(x**2 - 1) == [-1.0, 1.0] + + roots = nroots(x**2 - 1) + assert roots == [-1.0, 1.0] + + assert nroots(x + I) == [-1.0*I] + assert nroots(x + 2*I) == [-2.0*I] + + raises(PolynomialError, lambda: nroots(0)) + + # issue 8296 + f = Poly(x**4 - 1) + assert f.nroots(2) == [w.n(2) for w in f.all_roots()] + + assert str(Poly(x**16 + 32*x**14 + 508*x**12 + 5440*x**10 + + 39510*x**8 + 204320*x**6 + 755548*x**4 + 1434496*x**2 + + 877969).nroots(2)) == ('[-1.7 - 1.9*I, -1.7 + 1.9*I, -1.7 ' + '- 2.5*I, -1.7 + 2.5*I, -1.0*I, 1.0*I, -1.7*I, 1.7*I, -2.8*I, ' + '2.8*I, -3.4*I, 3.4*I, 1.7 - 1.9*I, 1.7 + 1.9*I, 1.7 - 2.5*I, ' + '1.7 + 2.5*I]') + assert str(Poly(1e-15*x**2 -1).nroots()) == ('[-31622776.6016838, 31622776.6016838]') + + # https://github.com/sympy/sympy/issues/23861 + + i = Float('3.000000000000000000000000000000000000000000000000001') + [r] = nroots(x + I*i, n=300) + assert abs(r + I*i) < 1e-300 + + +def test_ground_roots(): + f = x**6 - 4*x**4 + 4*x**3 - x**2 + + assert Poly(f).ground_roots() == {S.One: 2, S.Zero: 2} + assert ground_roots(f) == {S.One: 2, S.Zero: 2} + + +def test_nth_power_roots_poly(): + f = x**4 - x**2 + 1 + + f_2 = (x**2 - x + 1)**2 + f_3 = (x**2 + 1)**2 + f_4 = (x**2 + x + 1)**2 + f_12 = (x - 1)**4 + + assert nth_power_roots_poly(f, 1) == f + + raises(ValueError, lambda: nth_power_roots_poly(f, 0)) + raises(ValueError, lambda: nth_power_roots_poly(f, x)) + + assert factor(nth_power_roots_poly(f, 2)) == f_2 + assert factor(nth_power_roots_poly(f, 3)) == f_3 + assert factor(nth_power_roots_poly(f, 4)) == f_4 + assert factor(nth_power_roots_poly(f, 12)) == f_12 + + raises(MultivariatePolynomialError, lambda: nth_power_roots_poly( + x + y, 2, x, y)) + +def test_which_real_roots(): + f = Poly(x**4 - 1) + + assert f.which_real_roots([1, -1]) == [1, -1] + assert f.which_real_roots([1, -1, 2, 4]) == [1, -1] + assert f.which_real_roots([1, -1, -1, 1, 2, 5]) == [1, -1] + assert f.which_real_roots([10, 8, 7, -1, 1]) == [-1, 1] + + # no real roots + # (technically its still a superset) + f = Poly(x**2 + 1) + assert f.which_real_roots([5, 10]) == [] + + # not square free + f = Poly((x-1)**2) + assert f.which_real_roots([1, 1, -1, 2]) == [1] + + # candidates not superset + f = Poly(x**2 - 1) + assert f.which_real_roots([0, 2]) == [0, 2] + +def test_which_all_roots(): + f = Poly(x**4 - 1) + + assert f.which_all_roots([1, -1, I, -I]) == [1, -1, I, -I] + assert f.which_all_roots([I, I, -I, 1, -1]) == [I, -I, 1, -1] + + f = Poly(x**2 + 1) + assert f.which_all_roots([I, -I, I/2]) == [I, -I] + + # not square free + f = Poly((x-I)**2) + assert f.which_all_roots([I, I, 1, -1, 0]) == [I] + + # candidates not superset + f = Poly(x**2 + 1) + assert f.which_all_roots([I/2, -I/2]) == [I/2, -I/2] + +def test_same_root(): + f = Poly(x**4 + x**3 + x**2 + x + 1) + eq = f.same_root + r0 = exp(2 * I * pi / 5) + assert [i for i, r in enumerate(f.all_roots()) if eq(r, r0)] == [3] + + raises(PolynomialError, + lambda: Poly(x + 1, domain=QQ).same_root(0, 0)) + raises(DomainError, + lambda: Poly(x**2 + 1, domain=FF(7)).same_root(0, 0)) + raises(DomainError, + lambda: Poly(x ** 2 + 1, domain=ZZ_I).same_root(0, 0)) + raises(DomainError, + lambda: Poly(y * x**2 + 1, domain=ZZ[y]).same_root(0, 0)) + raises(MultivariatePolynomialError, + lambda: Poly(x * y + 1, domain=ZZ).same_root(0, 0)) + + +def test_torational_factor_list(): + p = expand(((x**2-1)*(x-2)).subs({x:x*(1 + sqrt(2))})) + assert _torational_factor_list(p, x) == (-2, [ + (-x*(1 + sqrt(2))/2 + 1, 1), + (-x*(1 + sqrt(2)) - 1, 1), + (-x*(1 + sqrt(2)) + 1, 1)]) + + + p = expand(((x**2-1)*(x-2)).subs({x:x*(1 + 2**Rational(1, 4))})) + assert _torational_factor_list(p, x) is None + + +def test_cancel(): + assert cancel(0) == 0 + assert cancel(7) == 7 + assert cancel(x) == x + + assert cancel(oo) is oo + + raises(ValueError, lambda: cancel((1, 2, 3))) + + # test first tuple returnr + assert (t:=cancel((2, 3))) == (1, 2, 3) + assert isinstance(t, tuple) + + # tests 2nd tuple return + assert (t:=cancel((1, 0), x)) == (1, 1, 0) + assert isinstance(t, tuple) + assert cancel((0, 1), x) == (1, 0, 1) + + f, g, p, q = 4*x**2 - 4, 2*x - 2, 2*x + 2, 1 + F, G, P, Q = [ Poly(u, x) for u in (f, g, p, q) ] + + assert F.cancel(G) == (1, P, Q) + assert cancel((f, g)) == (1, p, q) + assert cancel((f, g), x) == (1, p, q) + assert cancel((f, g), (x,)) == (1, p, q) + # tests 3rd tuple return + assert (t:=cancel((F, G))) == (1, P, Q) + assert isinstance(t, tuple) + assert cancel((f, g), polys=True) == (1, P, Q) + assert cancel((F, G), polys=False) == (1, p, q) + + f = (x**2 - 2)/(x + sqrt(2)) + + assert cancel(f) == f + assert cancel(f, greedy=False) == x - sqrt(2) + + f = (x**2 - 2)/(x - sqrt(2)) + + assert cancel(f) == f + assert cancel(f, greedy=False) == x + sqrt(2) + + assert cancel((x**2/4 - 1, x/2 - 1)) == (1, x + 2, 2) + # assert cancel((x**2/4 - 1, x/2 - 1)) == (S.Half, x + 2, 1) + + assert cancel((x**2 - y)/(x - y)) == 1/(x - y)*(x**2 - y) + + assert cancel((x**2 - y**2)/(x - y), x) == x + y + assert cancel((x**2 - y**2)/(x - y), y) == x + y + assert cancel((x**2 - y**2)/(x - y)) == x + y + + assert cancel((x**3 - 1)/(x**2 - 1)) == (x**2 + x + 1)/(x + 1) + assert cancel((x**3/2 - S.Half)/(x**2 - 1)) == (x**2 + x + 1)/(2*x + 2) + + assert cancel((exp(2*x) + 2*exp(x) + 1)/(exp(x) + 1)) == exp(x) + 1 + + f = Poly(x**2 - a**2, x) + g = Poly(x - a, x) + + F = Poly(x + a, x, domain='ZZ[a]') + G = Poly(1, x, domain='ZZ[a]') + + assert cancel((f, g)) == (1, F, G) + + f = x**3 + (sqrt(2) - 2)*x**2 - (2*sqrt(2) + 3)*x - 3*sqrt(2) + g = x**2 - 2 + + assert cancel((f, g), extension=True) == (1, x**2 - 2*x - 3, x - sqrt(2)) + + f = Poly(-2*x + 3, x) + g = Poly(-x**9 + x**8 + x**6 - x**5 + 2*x**2 - 3*x + 1, x) + + assert cancel((f, g)) == (1, -f, -g) + + f = Poly(x/3 + 1, x) + g = Poly(x/7 + 1, x) + + assert f.cancel(g) == (S(7)/3, + Poly(x + 3, x, domain=QQ), + Poly(x + 7, x, domain=QQ)) + assert f.cancel(g, include=True) == ( + Poly(7*x + 21, x, domain=QQ), + Poly(3*x + 21, x, domain=QQ)) + + pairs = [ + (1 + x, 1 + x, 1, 1, 1), + (1 + x, 1 - x, -1, -1-x, -1+x), + (1 - x, 1 + x, -1, 1-x, 1+x), + (1 - x, 1 - x, 1, 1, 1), + ] + for f, g, coeff, p, q in pairs: + assert cancel((f, g)) == (1, p, q) + pf = Poly(f, x) + pg = Poly(g, x) + pp = Poly(p, x) + pq = Poly(q, x) + assert pf.cancel(pg) == (coeff, coeff*pp, pq) + assert pf.rep.cancel(pg.rep) == (pp.rep, pq.rep) + assert pf.rep.cancel(pg.rep, include=True) == (pp.rep, pq.rep) + + f = Poly(y, y, domain='ZZ(x)') + g = Poly(1, y, domain='ZZ[x]') + + assert f.cancel( + g) == (1, Poly(y, y, domain='ZZ(x)'), Poly(1, y, domain='ZZ(x)')) + assert f.cancel(g, include=True) == ( + Poly(y, y, domain='ZZ(x)'), Poly(1, y, domain='ZZ(x)')) + + f = Poly(5*x*y + x, y, domain='ZZ(x)') + g = Poly(2*x**2*y, y, domain='ZZ(x)') + + assert f.cancel(g, include=True) == ( + Poly(5*y + 1, y, domain='ZZ(x)'), Poly(2*x*y, y, domain='ZZ(x)')) + + f = -(-2*x - 4*y + 0.005*(z - y)**2)/((z - y)*(-z + y + 2)) + assert cancel(f).is_Mul == True + + P = tanh(x - 3.0) + Q = tanh(x + 3.0) + f = ((-2*P**2 + 2)*(-P**2 + 1)*Q**2/2 + (-2*P**2 + 2)*(-2*Q**2 + 2)*P*Q - (-2*P**2 + 2)*P**2*Q**2 + (-2*Q**2 + 2)*(-Q**2 + 1)*P**2/2 - (-2*Q**2 + 2)*P**2*Q**2)/(2*sqrt(P**2*Q**2 + 0.0001)) \ + + (-(-2*P**2 + 2)*P*Q**2/2 - (-2*Q**2 + 2)*P**2*Q/2)*((-2*P**2 + 2)*P*Q**2/2 + (-2*Q**2 + 2)*P**2*Q/2)/(2*(P**2*Q**2 + 0.0001)**Rational(3, 2)) + assert cancel(f).is_Mul == True + + # issue 7022 + A = Symbol('A', commutative=False) + p1 = Piecewise((A*(x**2 - 1)/(x + 1), x > 1), ((x + 2)/(x**2 + 2*x), True)) + p2 = Piecewise((A*(x - 1), x > 1), (1/x, True)) + assert cancel(p1) == p2 + assert cancel(2*p1) == 2*p2 + assert cancel(1 + p1) == 1 + p2 + assert cancel((x**2 - 1)/(x + 1)*p1) == (x - 1)*p2 + assert cancel((x**2 - 1)/(x + 1) + p1) == (x - 1) + p2 + p3 = Piecewise(((x**2 - 1)/(x + 1), x > 1), ((x + 2)/(x**2 + 2*x), True)) + p4 = Piecewise(((x - 1), x > 1), (1/x, True)) + assert cancel(p3) == p4 + assert cancel(2*p3) == 2*p4 + assert cancel(1 + p3) == 1 + p4 + assert cancel((x**2 - 1)/(x + 1)*p3) == (x - 1)*p4 + assert cancel((x**2 - 1)/(x + 1) + p3) == (x - 1) + p4 + + # issue 4077 + q = S('''(2*1*(x - 1/x)/(x*(2*x - (-x + 1/x)/(x**2*(x - 1/x)**2) - 1/(x**2*(x - + 1/x)) - 2/x)) - 2*1*((x - 1/x)/((x*(x - 1/x)**2)) - 1/(x*(x - + 1/x)))*((-x + 1/x)*((x - 1/x)/((x*(x - 1/x)**2)) - 1/(x*(x - + 1/x)))/(2*x - (-x + 1/x)/(x**2*(x - 1/x)**2) - 1/(x**2*(x - 1/x)) - + 2/x) + 1)*((x - 1/x)/((x - 1/x)**2) - ((x - 1/x)/((x*(x - 1/x)**2)) - + 1/(x*(x - 1/x)))**2/(2*x - (-x + 1/x)/(x**2*(x - 1/x)**2) - 1/(x**2*(x + - 1/x)) - 2/x) - 1/(x - 1/x))*(2*x - (-x + 1/x)/(x**2*(x - 1/x)**2) - + 1/(x**2*(x - 1/x)) - 2/x)/x - 1/x)*(((-x + 1/x)/((x*(x - 1/x)**2)) + + 1/(x*(x - 1/x)))*((-(x - 1/x)/(x*(x - 1/x)) - 1/x)*((x - 1/x)/((x*(x - + 1/x)**2)) - 1/(x*(x - 1/x)))/(2*x - (-x + 1/x)/(x**2*(x - 1/x)**2) - + 1/(x**2*(x - 1/x)) - 2/x) - 1 + (x - 1/x)/(x - 1/x))/((x*((x - + 1/x)/((x - 1/x)**2) - ((x - 1/x)/((x*(x - 1/x)**2)) - 1/(x*(x - + 1/x)))**2/(2*x - (-x + 1/x)/(x**2*(x - 1/x)**2) - 1/(x**2*(x - 1/x)) - + 2/x) - 1/(x - 1/x))*(2*x - (-x + 1/x)/(x**2*(x - 1/x)**2) - 1/(x**2*(x + - 1/x)) - 2/x))) + ((x - 1/x)/((x*(x - 1/x))) + 1/x)/((x*(2*x - (-x + + 1/x)/(x**2*(x - 1/x)**2) - 1/(x**2*(x - 1/x)) - 2/x))) + 1/x)/(2*x + + 2*((x - 1/x)/((x*(x - 1/x)**2)) - 1/(x*(x - 1/x)))*((-(x - 1/x)/(x*(x + - 1/x)) - 1/x)*((x - 1/x)/((x*(x - 1/x)**2)) - 1/(x*(x - 1/x)))/(2*x - + (-x + 1/x)/(x**2*(x - 1/x)**2) - 1/(x**2*(x - 1/x)) - 2/x) - 1 + (x - + 1/x)/(x - 1/x))/((x*((x - 1/x)/((x - 1/x)**2) - ((x - 1/x)/((x*(x - + 1/x)**2)) - 1/(x*(x - 1/x)))**2/(2*x - (-x + 1/x)/(x**2*(x - 1/x)**2) + - 1/(x**2*(x - 1/x)) - 2/x) - 1/(x - 1/x))*(2*x - (-x + 1/x)/(x**2*(x + - 1/x)**2) - 1/(x**2*(x - 1/x)) - 2/x))) - 2*((x - 1/x)/((x*(x - + 1/x))) + 1/x)/(x*(2*x - (-x + 1/x)/(x**2*(x - 1/x)**2) - 1/(x**2*(x - + 1/x)) - 2/x)) - 2/x) - ((x - 1/x)/((x*(x - 1/x)**2)) - 1/(x*(x - + 1/x)))*((-x + 1/x)*((x - 1/x)/((x*(x - 1/x)**2)) - 1/(x*(x - + 1/x)))/(2*x - (-x + 1/x)/(x**2*(x - 1/x)**2) - 1/(x**2*(x - 1/x)) - + 2/x) + 1)/(x*((x - 1/x)/((x - 1/x)**2) - ((x - 1/x)/((x*(x - 1/x)**2)) + - 1/(x*(x - 1/x)))**2/(2*x - (-x + 1/x)/(x**2*(x - 1/x)**2) - + 1/(x**2*(x - 1/x)) - 2/x) - 1/(x - 1/x))*(2*x - (-x + 1/x)/(x**2*(x - + 1/x)**2) - 1/(x**2*(x - 1/x)) - 2/x)) + (x - 1/x)/((x*(2*x - (-x + + 1/x)/(x**2*(x - 1/x)**2) - 1/(x**2*(x - 1/x)) - 2/x))) - 1/x''', + evaluate=False) + assert cancel(q, _signsimp=False) is S.NaN + assert q.subs(x, 2) is S.NaN + assert signsimp(q) is S.NaN + + # issue 9363 + M = MatrixSymbol('M', 5, 5) + assert cancel(M[0,0] + 7) == M[0,0] + 7 + expr = sin(M[1, 4] + M[2, 1] * 5 * M[4, 0]) - 5 * M[1, 2] / z + assert cancel(expr) == (z*sin(M[1, 4] + M[2, 1] * 5 * M[4, 0]) - 5 * M[1, 2]) / z + + assert cancel((x**2 + 1)/(x - I)) == x + I + + +def test_cancel_modulus(): + assert cancel((x**2 - 1)/(x + 1), modulus=2) == x + 1 + assert Poly(x**2 - 1, modulus=2).cancel(Poly(x + 1, modulus=2)) ==\ + (1, Poly(x + 1, modulus=2), Poly(1, x, modulus=2)) + + +def test_make_monic_over_integers_by_scaling_roots(): + f = Poly(x**2 + 3*x + 4, x, domain='ZZ') + g, c = f.make_monic_over_integers_by_scaling_roots() + assert g == f + assert c == ZZ.one + + f = Poly(x**2 + 3*x + 4, x, domain='QQ') + g, c = f.make_monic_over_integers_by_scaling_roots() + assert g == f.to_ring() + assert c == ZZ.one + + f = Poly(x**2/2 + S(1)/4 * x + S(1)/8, x, domain='QQ') + g, c = f.make_monic_over_integers_by_scaling_roots() + assert g == Poly(x**2 + 2*x + 4, x, domain='ZZ') + assert c == 4 + + f = Poly(x**3/2 + S(1)/4 * x + S(1)/8, x, domain='QQ') + g, c = f.make_monic_over_integers_by_scaling_roots() + assert g == Poly(x**3 + 8*x + 16, x, domain='ZZ') + assert c == 4 + + f = Poly(x*y, x, y) + raises(ValueError, lambda: f.make_monic_over_integers_by_scaling_roots()) + + f = Poly(x, domain='RR') + raises(ValueError, lambda: f.make_monic_over_integers_by_scaling_roots()) + + +def test_galois_group(): + f = Poly(x ** 4 - 2) + G, alt = f.galois_group(by_name=True) + assert G == S4TransitiveSubgroups.D4 + assert alt is False + + +def test_reduced(): + f = 2*x**4 + y**2 - x**2 + y**3 + G = [x**3 - x, y**3 - y] + + Q = [2*x, 1] + r = x**2 + y**2 + y + + assert reduced(f, G) == (Q, r) + assert reduced(f, G, x, y) == (Q, r) + + H = groebner(G) + + assert H.reduce(f) == (Q, r) + + Q = [Poly(2*x, x, y), Poly(1, x, y)] + r = Poly(x**2 + y**2 + y, x, y) + + assert _strict_eq(reduced(f, G, polys=True), (Q, r)) + assert _strict_eq(reduced(f, G, x, y, polys=True), (Q, r)) + + H = groebner(G, polys=True) + + assert _strict_eq(H.reduce(f), (Q, r)) + + f = 2*x**3 + y**3 + 3*y + G = groebner([x**2 + y**2 - 1, x*y - 2]) + + Q = [x**2 - x*y**3/2 + x*y/2 + y**6/4 - y**4/2 + y**2/4, -y**5/4 + y**3/2 + y*Rational(3, 4)] + r = 0 + + assert reduced(f, G) == (Q, r) + assert G.reduce(f) == (Q, r) + + assert reduced(f, G, auto=False)[1] != 0 + assert G.reduce(f, auto=False)[1] != 0 + + assert G.contains(f) is True + assert G.contains(f + 1) is False + + assert reduced(1, [1], x) == ([1], 0) + raises(ComputationFailed, lambda: reduced(1, [1])) + + f_poly = Poly(2*x**3 + y**3 + 3*y) + G_poly = groebner([Poly(x**2 + y**2 - 1), Poly(x*y - 2)]) + + Q_poly = [Poly(x**2 - 1/2*x*y**3 + 1/2*x*y + 1/4*y**6 - 1/2*y**4 + 1/4*y**2, x, y, domain='QQ'), + Poly(-1/4*y**5 + 1/2*y**3 + 3/4*y, x, y, domain='QQ')] + r_poly = Poly(0, x, y, domain='QQ') + + assert G_poly.reduce(f_poly) == (Q_poly, r_poly) + + Q, r = G_poly.reduce(f) + assert all(isinstance(q, Poly) for q in Q) + assert isinstance(r, Poly) + + f_wrong_gens = Poly(2*x**3 + y**3 + 3*y, x, y, z) + raises(ValueError, lambda: G_poly.reduce(f_wrong_gens)) + + zero_poly = Poly(0, x, y) + Q, r = G_poly.reduce(zero_poly) + assert all(q.is_zero for q in Q) + assert r.is_zero + + const_poly = Poly(1, x, y) + Q, r = G_poly.reduce(const_poly) + assert isinstance(r, Poly) + assert r.as_expr() == 1 + assert all(q.is_zero for q in Q) + + +def test_groebner(): + assert groebner([], x, y, z) == [] + + assert groebner([x**2 + 1, y**4*x + x**3], x, y, order='lex') == [1 + x**2, -1 + y**4] + assert groebner([x**2 + 1, y**4*x + x**3, x*y*z**3], x, y, z, order='grevlex') == [-1 + y**4, z**3, 1 + x**2] + + assert groebner([x**2 + 1, y**4*x + x**3], x, y, order='lex', polys=True) == \ + [Poly(1 + x**2, x, y), Poly(-1 + y**4, x, y)] + assert groebner([x**2 + 1, y**4*x + x**3, x*y*z**3], x, y, z, order='grevlex', polys=True) == \ + [Poly(-1 + y**4, x, y, z), Poly(z**3, x, y, z), Poly(1 + x**2, x, y, z)] + + assert groebner([x**3 - 1, x**2 - 1]) == [x - 1] + assert groebner([Eq(x**3, 1), Eq(x**2, 1)]) == [x - 1] + + F = [3*x**2 + y*z - 5*x - 1, 2*x + 3*x*y + y**2, x - 3*y + x*z - 2*z**2] + f = z**9 - x**2*y**3 - 3*x*y**2*z + 11*y*z**2 + x**2*z**2 - 5 + + G = groebner(F, x, y, z, modulus=7, symmetric=False) + + assert G == [1 + x + y + 3*z + 2*z**2 + 2*z**3 + 6*z**4 + z**5, + 1 + 3*y + y**2 + 6*z**2 + 3*z**3 + 3*z**4 + 3*z**5 + 4*z**6, + 1 + 4*y + 4*z + y*z + 4*z**3 + z**4 + z**6, + 6 + 6*z + z**2 + 4*z**3 + 3*z**4 + 6*z**5 + 3*z**6 + z**7] + + Q, r = reduced(f, G, x, y, z, modulus=7, symmetric=False, polys=True) + + assert sum([ q*g for q, g in zip(Q, G.polys)], r) == Poly(f, modulus=7) + + F = [x*y - 2*y, 2*y**2 - x**2] + + assert groebner(F, x, y, order='grevlex') == \ + [y**3 - 2*y, x**2 - 2*y**2, x*y - 2*y] + assert groebner(F, y, x, order='grevlex') == \ + [x**3 - 2*x**2, -x**2 + 2*y**2, x*y - 2*y] + assert groebner(F, order='grevlex', field=True) == \ + [y**3 - 2*y, x**2 - 2*y**2, x*y - 2*y] + + assert groebner([1], x) == [1] + + assert groebner([x**2 + 2.0*y], x, y) == [1.0*x**2 + 2.0*y] + raises(ComputationFailed, lambda: groebner([1])) + + assert groebner([x**2 - 1, x**3 + 1], method='buchberger') == [x + 1] + assert groebner([x**2 - 1, x**3 + 1], method='f5b') == [x + 1] + + raises(ValueError, lambda: groebner([x, y], method='unknown')) + + +def test_fglm(): + F = [a + b + c + d, a*b + a*d + b*c + b*d, a*b*c + a*b*d + a*c*d + b*c*d, a*b*c*d - 1] + G = groebner(F, a, b, c, d, order=grlex) + + B = [ + 4*a + 3*d**9 - 4*d**5 - 3*d, + 4*b + 4*c - 3*d**9 + 4*d**5 + 7*d, + 4*c**2 + 3*d**10 - 4*d**6 - 3*d**2, + 4*c*d**4 + 4*c - d**9 + 4*d**5 + 5*d, + d**12 - d**8 - d**4 + 1, + ] + + assert groebner(F, a, b, c, d, order=lex) == B + assert G.fglm(lex) == B + + F = [9*x**8 + 36*x**7 - 32*x**6 - 252*x**5 - 78*x**4 + 468*x**3 + 288*x**2 - 108*x + 9, + -72*t*x**7 - 252*t*x**6 + 192*t*x**5 + 1260*t*x**4 + 312*t*x**3 - 404*t*x**2 - 576*t*x + \ + 108*t - 72*x**7 - 256*x**6 + 192*x**5 + 1280*x**4 + 312*x**3 - 576*x + 96] + G = groebner(F, t, x, order=grlex) + + B = [ + 203577793572507451707*t + 627982239411707112*x**7 - 666924143779443762*x**6 - \ + 10874593056632447619*x**5 + 5119998792707079562*x**4 + 72917161949456066376*x**3 + \ + 20362663855832380362*x**2 - 142079311455258371571*x + 183756699868981873194, + 9*x**8 + 36*x**7 - 32*x**6 - 252*x**5 - 78*x**4 + 468*x**3 + 288*x**2 - 108*x + 9, + ] + + assert groebner(F, t, x, order=lex) == B + assert G.fglm(lex) == B + + F = [x**2 - x - 3*y + 1, -2*x + y**2 + y - 1] + G = groebner(F, x, y, order=lex) + + B = [ + x**2 - x - 3*y + 1, + y**2 - 2*x + y - 1, + ] + + assert groebner(F, x, y, order=grlex) == B + assert G.fglm(grlex) == B + + +def test_is_zero_dimensional(): + assert is_zero_dimensional([x, y], x, y) is True + assert is_zero_dimensional([x**3 + y**2], x, y) is False + + assert is_zero_dimensional([x, y, z], x, y, z) is True + assert is_zero_dimensional([x, y, z], x, y, z, t) is False + + F = [x*y - z, y*z - x, x*y - y] + assert is_zero_dimensional(F, x, y, z) is True + + F = [x**2 - 2*x*z + 5, x*y**2 + y*z**3, 3*y**2 - 8*z**2] + assert is_zero_dimensional(F, x, y, z) is True + + +def test_GroebnerBasis(): + F = [x*y - 2*y, 2*y**2 - x**2] + + G = groebner(F, x, y, order='grevlex') + H = [y**3 - 2*y, x**2 - 2*y**2, x*y - 2*y] + P = [ Poly(h, x, y) for h in H ] + + assert groebner(F + [0], x, y, order='grevlex') == G + assert isinstance(G, GroebnerBasis) is True + + assert len(G) == 3 + + assert G[0] == H[0] and not G[0].is_Poly + assert G[1] == H[1] and not G[1].is_Poly + assert G[2] == H[2] and not G[2].is_Poly + + assert G[1:] == H[1:] and not any(g.is_Poly for g in G[1:]) + assert G[:2] == H[:2] and not any(g.is_Poly for g in G[1:]) + + assert G.exprs == H + assert G.polys == P + assert G.gens == (x, y) + assert G.domain == ZZ + assert G.order == grevlex + + assert G == H + assert G == tuple(H) + assert G == P + assert G == tuple(P) + + assert G != [] + + G = groebner(F, x, y, order='grevlex', polys=True) + + assert G[0] == P[0] and G[0].is_Poly + assert G[1] == P[1] and G[1].is_Poly + assert G[2] == P[2] and G[2].is_Poly + + assert G[1:] == P[1:] and all(g.is_Poly for g in G[1:]) + assert G[:2] == P[:2] and all(g.is_Poly for g in G[1:]) + + +def test_poly(): + assert poly(x) == Poly(x, x) + assert poly(y) == Poly(y, y) + + assert poly(x + y) == Poly(x + y, x, y) + assert poly(x + sin(x)) == Poly(x + sin(x), x, sin(x)) + + assert poly(x + y, wrt=y) == Poly(x + y, y, x) + assert poly(x + sin(x), wrt=sin(x)) == Poly(x + sin(x), sin(x), x) + + assert poly(x*y + 2*x*z**2 + 17) == Poly(x*y + 2*x*z**2 + 17, x, y, z) + + assert poly(2*(y + z)**2 - 1) == Poly(2*y**2 + 4*y*z + 2*z**2 - 1, y, z) + assert poly( + x*(y + z)**2 - 1) == Poly(x*y**2 + 2*x*y*z + x*z**2 - 1, x, y, z) + assert poly(2*x*( + y + z)**2 - 1) == Poly(2*x*y**2 + 4*x*y*z + 2*x*z**2 - 1, x, y, z) + + assert poly(2*( + y + z)**2 - x - 1) == Poly(2*y**2 + 4*y*z + 2*z**2 - x - 1, x, y, z) + assert poly(x*( + y + z)**2 - x - 1) == Poly(x*y**2 + 2*x*y*z + x*z**2 - x - 1, x, y, z) + assert poly(2*x*(y + z)**2 - x - 1) == Poly(2*x*y**2 + 4*x*y*z + 2* + x*z**2 - x - 1, x, y, z) + + assert poly(x*y + (x + y)**2 + (x + z)**2) == \ + Poly(2*x*z + 3*x*y + y**2 + z**2 + 2*x**2, x, y, z) + assert poly(x*y*(x + y)*(x + z)**2) == \ + Poly(x**3*y**2 + x*y**2*z**2 + y*x**2*z**2 + 2*z*x**2* + y**2 + 2*y*z*x**3 + y*x**4, x, y, z) + + assert poly(Poly(x + y + z, y, x, z)) == Poly(x + y + z, y, x, z) + + assert poly((x + y)**2, x) == Poly(x**2 + 2*x*y + y**2, x, domain=ZZ[y]) + assert poly((x + y)**2, y) == Poly(x**2 + 2*x*y + y**2, y, domain=ZZ[x]) + + assert poly(1, x) == Poly(1, x) + raises(GeneratorsNeeded, lambda: poly(1)) + + # issue 6184 + assert poly(x + y, x, y) == Poly(x + y, x, y) + assert poly(x + y, y, x) == Poly(x + y, y, x) + + # https://github.com/sympy/sympy/issues/19755 + expr1 = x + (2*x + 3)**2/5 + S(6)/5 + assert poly(expr1).as_expr() == expr1.expand() + expr2 = y*(y+1) + S(1)/3 + assert poly(expr2).as_expr() == expr2.expand() + + +def test_keep_coeff(): + u = Mul(2, x + 1, evaluate=False) + assert _keep_coeff(S.One, x) == x + assert _keep_coeff(S.NegativeOne, x) == -x + assert _keep_coeff(S(1.0), x) == 1.0*x + assert _keep_coeff(S(-1.0), x) == -1.0*x + assert _keep_coeff(S.One, 2*x) == 2*x + assert _keep_coeff(S(2), x/2) == x + assert _keep_coeff(S(2), sin(x)) == 2*sin(x) + assert _keep_coeff(S(2), x + 1) == u + assert _keep_coeff(x, 1/x) == 1 + assert _keep_coeff(x + 1, S(2)) == u + assert _keep_coeff(S.Half, S.One) == S.Half + p = Pow(2, 3, evaluate=False) + assert _keep_coeff(S(-1), p) == Mul(-1, p, evaluate=False) + a = Add(2, p, evaluate=False) + assert _keep_coeff(S.Half, a, clear=True + ) == Mul(S.Half, a, evaluate=False) + assert _keep_coeff(S.Half, a, clear=False + ) == Add(1, Mul(S.Half, p, evaluate=False), evaluate=False) + + +def test_poly_matching_consistency(): + # Test for this issue: + # https://github.com/sympy/sympy/issues/5514 + assert I * Poly(x, x) == Poly(I*x, x) + assert Poly(x, x) * I == Poly(I*x, x) + + +def test_issue_5786(): + assert expand(factor(expand( + (x - I*y)*(z - I*t)), extension=[I])) == -I*t*x - t*y + x*z - I*y*z + + +def test_noncommutative(): + class foo(Expr): + is_commutative=False + e = x/(x + x*y) + c = 1/( 1 + y) + assert cancel(foo(e)) == foo(c) + assert cancel(e + foo(e)) == c + foo(c) + assert cancel(e*foo(c)) == c*foo(c) + + +def test_to_rational_coeffs(): + assert to_rational_coeffs( + Poly(x**3 + y*x**2 + sqrt(y), x, domain='EX')) is None + # issue 21268 + assert to_rational_coeffs( + Poly(y**3 + sqrt(2)*y**2*sin(x) + 1, y)) is None + + assert to_rational_coeffs(Poly(x, y)) is None + assert to_rational_coeffs(Poly(sqrt(2)*y)) is None + + +def test_factor_terms(): + # issue 7067 + assert factor_list(x*(x + y)) == (1, [(x, 1), (x + y, 1)]) + assert sqf_list(x*(x + y)) == (1, [(x**2 + x*y, 1)]) + + +def test_as_list(): + # issue 14496 + assert Poly(x**3 + 2, x, domain='ZZ').as_list() == [1, 0, 0, 2] + assert Poly(x**2 + y + 1, x, y, domain='ZZ').as_list() == [[1], [], [1, 1]] + assert Poly(x**2 + y + 1, x, y, z, domain='ZZ').as_list() == \ + [[[1]], [[]], [[1], [1]]] + + +def test_issue_11198(): + assert factor_list(sqrt(2)*x) == (sqrt(2), [(x, 1)]) + assert factor_list(sqrt(2)*sin(x), sin(x)) == (sqrt(2), [(sin(x), 1)]) + + +def test_Poly_precision(): + # Make sure Poly doesn't lose precision + p = Poly(pi.evalf(100)*x) + assert p.as_expr() == pi.evalf(100)*x + + +def test_issue_12400(): + # Correction of check for negative exponents + assert poly(1/(1+sqrt(2)), x) == \ + Poly(1/(1+sqrt(2)), x, domain='EX') + +def test_issue_14364(): + assert gcd(S(6)*(1 + sqrt(3))/5, S(3)*(1 + sqrt(3))/10) == Rational(3, 10) * (1 + sqrt(3)) + assert gcd(sqrt(5)*Rational(4, 7), sqrt(5)*Rational(2, 3)) == sqrt(5)*Rational(2, 21) + + assert lcm(Rational(2, 3)*sqrt(3), Rational(5, 6)*sqrt(3)) == S(10)*sqrt(3)/3 + assert lcm(3*sqrt(3), 4/sqrt(3)) == 12*sqrt(3) + assert lcm(S(5)*(1 + 2**Rational(1, 3))/6, S(3)*(1 + 2**Rational(1, 3))/8) == Rational(15, 2) * (1 + 2**Rational(1, 3)) + + assert gcd(Rational(2, 3)*sqrt(3), Rational(5, 6)/sqrt(3)) == sqrt(3)/18 + assert gcd(S(4)*sqrt(13)/7, S(3)*sqrt(13)/14) == sqrt(13)/14 + + # gcd_list and lcm_list + assert gcd([S(2)*sqrt(47)/7, S(6)*sqrt(47)/5, S(8)*sqrt(47)/5]) == sqrt(47)*Rational(2, 35) + assert gcd([S(6)*(1 + sqrt(7))/5, S(2)*(1 + sqrt(7))/7, S(4)*(1 + sqrt(7))/13]) == (1 + sqrt(7))*Rational(2, 455) + assert lcm((Rational(7, 2)/sqrt(15), Rational(5, 6)/sqrt(15), Rational(5, 8)/sqrt(15))) == Rational(35, 2)/sqrt(15) + assert lcm([S(5)*(2 + 2**Rational(5, 7))/6, S(7)*(2 + 2**Rational(5, 7))/2, S(13)*(2 + 2**Rational(5, 7))/4]) == Rational(455, 2) * (2 + 2**Rational(5, 7)) + + +def test_issue_15669(): + x = Symbol("x", positive=True) + expr = (16*x**3/(-x**2 + sqrt(8*x**2 + (x**2 - 2)**2) + 2)**2 - + 2*2**Rational(4, 5)*x*(-x**2 + sqrt(8*x**2 + (x**2 - 2)**2) + 2)**Rational(3, 5) + 10*x) + assert factor(expr, deep=True) == x*(x**2 + 2) + + +def test_issue_17988(): + x = Symbol('x') + p = poly(x - 1) + with warns_deprecated_sympy(): + M = Matrix([[poly(x + 1), poly(x + 1)]]) + with warns(SymPyDeprecationWarning, test_stacklevel=False): + assert p * M == M * p == Matrix([[poly(x**2 - 1), poly(x**2 - 1)]]) + + +def test_issue_18205(): + assert cancel((2 + I)*(3 - I)) == 7 + I + assert cancel((2 + I)*(2 - I)) == 5 + + +def test_issue_8695(): + p = (x**2 + 1) * (x - 1)**2 * (x - 2)**3 * (x - 3)**3 + result = (1, [(x**2 + 1, 1), (x - 1, 2), (x**2 - 5*x + 6, 3)]) + assert sqf_list(p) == result + + +def test_issue_19113(): + eq = sin(x)**3 - sin(x) + 1 + raises(PolynomialError, lambda: refine_root(eq, 1, 2, 1e-2)) + raises(PolynomialError, lambda: count_roots(eq, -1, 1)) + raises(PolynomialError, lambda: real_roots(eq)) + raises(PolynomialError, lambda: nroots(eq)) + raises(PolynomialError, lambda: ground_roots(eq)) + raises(PolynomialError, lambda: nth_power_roots_poly(eq, 2)) + + +def test_issue_19360(): + f = 2*x**2 - 2*sqrt(2)*x*y + y**2 + assert factor(f, extension=sqrt(2)) == 2*(x - (sqrt(2)*y/2))**2 + + f = -I*t*x - t*y + x*z - I*y*z + assert factor(f, extension=I) == (x - I*y)*(-I*t + z) + + +def test_poly_copy_equals_original(): + poly = Poly(x + y, x, y, z) + copy = poly.copy() + assert poly == copy, ( + "Copied polynomial not equal to original.") + assert poly.gens == copy.gens, ( + "Copied polynomial has different generators than original.") + + +def test_deserialized_poly_equals_original(): + poly = Poly(x + y, x, y, z) + deserialized = pickle.loads(pickle.dumps(poly)) + assert poly == deserialized, ( + "Deserialized polynomial not equal to original.") + assert poly.gens == deserialized.gens, ( + "Deserialized polynomial has different generators than original.") + + +def test_issue_20389(): + result = degree(x * (x + 1) - x ** 2 - x, x) + assert result == -oo + + +def test_issue_20985(): + from sympy.core.symbol import symbols + w, R = symbols('w R') + poly = Poly(1.0 + I*w/R, w, 1/R) + assert poly.degree() == S(1) diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_puiseux.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_puiseux.py new file mode 100644 index 0000000000000000000000000000000000000000..031881e9d12c53053d8ec7136374bd8b3a385df0 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_puiseux.py @@ -0,0 +1,204 @@ +# +# Tests for PuiseuxRing and PuiseuxPoly +# + +from sympy.testing.pytest import raises + +from sympy import ZZ, QQ, ring +from sympy.polys.puiseux import PuiseuxRing, PuiseuxPoly, puiseux_ring + +from sympy.abc import x, y + + +def test_puiseux_ring(): + R, px = puiseux_ring('x', QQ) + R2, px2 = puiseux_ring([x], QQ) + assert isinstance(R, PuiseuxRing) + assert isinstance(px, PuiseuxPoly) + assert R == R2 + assert px == px2 + assert R == PuiseuxRing('x', QQ) + assert R == PuiseuxRing([x], QQ) + assert R != PuiseuxRing('y', QQ) + assert R != PuiseuxRing('x', ZZ) + assert R != PuiseuxRing('x, y', QQ) + assert R != QQ + assert str(R) == 'PuiseuxRing((x,), QQ)' + + +def test_puiseux_ring_attributes(): + R1, px1, py1 = ring('x, y', QQ) + R2, px2, py2 = puiseux_ring('x, y', QQ) + assert R2.domain == QQ + assert R2.symbols == (x, y) + assert R2.gens == (px2, py2) + assert R2.ngens == 2 + assert R2.poly_ring == R1 + assert R2.zero == PuiseuxPoly(R1.zero, R2) + assert R2.one == PuiseuxPoly(R1.one, R2) + assert R2.zero_monom == R1.zero_monom == (0, 0) # type: ignore + assert R2.monomial_mul((1, 2), (3, 4)) == (4, 6) + + +def test_puiseux_ring_methods(): + R1, px1, py1 = ring('x, y', QQ) + R2, px2, py2 = puiseux_ring('x, y', QQ) + assert R2({(1, 2): 3}) == 3*px2*py2**2 + assert R2(px1) == px2 + assert R2(1) == R2.one + assert R2(QQ(1,2)) == QQ(1,2)*R2.one + assert R2.from_poly(px1) == px2 + assert R2.from_poly(px1) != py2 + assert R2.from_dict({(1, 2): QQ(3)}) == 3*px2*py2**2 + assert R2.from_dict({(QQ(1,2), 2): QQ(3)}) == 3*px2**QQ(1,2)*py2**2 + assert R2.from_int(3) == 3*R2.one + assert R2.domain_new(3) == QQ(3) + assert QQ.of_type(R2.domain_new(3)) + assert R2.ground_new(3) == 3*R2.one + assert isinstance(R2.ground_new(3), PuiseuxPoly) + assert R2.index(px2) == 0 + assert R2.index(py2) == 1 + + +def test_puiseux_poly(): + R1, px1 = ring('x', QQ) + R2, px2 = puiseux_ring('x', QQ) + assert PuiseuxPoly(px1, R2) == px2 + assert px2.ring == R2 + assert px2.as_expr() == px1.as_expr() == x + assert px1 != px2 + assert R2.one == px2**0 == 1 + assert px2 == px1 + assert px2 != 2.0 + assert px2**QQ(1,2) != px1 + + +def test_puiseux_poly_normalization(): + R, x = puiseux_ring('x', QQ) + assert (x**2 + 1) / x == x + 1/x == R({(1,): 1, (-1,): 1}) + assert (x**QQ(1,6))**2 == x**QQ(1,3) == R({(QQ(1,3),): 1}) + assert (x**QQ(1,6))**(-2) == x**(-QQ(1,3)) == R({(-QQ(1,3),): 1}) + assert (x**QQ(1,6))**QQ(1,2) == x**QQ(1,12) == R({(QQ(1,12),): 1}) + assert (x**QQ(1,6))**6 == x == R({(1,): 1}) + assert x**QQ(1,6) * x**QQ(1,3) == x**QQ(1,2) == R({(QQ(1,2),): 1}) + assert 1/x * x**2 == x == R({(1,): 1}) + assert 1/x**QQ(1,3) * x**QQ(1,3) == 1 == R({(0,): 1}) + + +def test_puiseux_poly_monoms(): + R, x = puiseux_ring('x', QQ) + assert x.monoms() == [(1,)] + assert list(x) == [(1,)] + assert (x**2 + 1).monoms() == [(2,), (0,)] + assert R({(1,): 1, (-1,): 1}).monoms() == [(1,), (-1,)] + assert R({(QQ(1,3),): 1}).monoms() == [(QQ(1,3),)] + assert R({(-QQ(1,3),): 1}).monoms() == [(-QQ(1,3),)] + p = x**QQ(1,6) + assert p[(QQ(1,6),)] == 1 + raises(KeyError, lambda: p[(1,)]) + assert p.to_dict() == {(QQ(1,6),): 1} + assert R(p.to_dict()) == p + assert PuiseuxPoly.from_dict({(QQ(1,6),): 1}, R) == p + + +def test_puiseux_poly_repr(): + R, x = puiseux_ring('x', QQ) + assert repr(x) == 'x' + assert repr(x**QQ(1,2)) == 'x**(1/2)' + assert repr(1/x) == 'x**(-1)' + assert repr(2*x**2 + 1) == '1 + 2*x**2' + assert repr(R.one) == '1' + assert repr(2*R.one) == '2' + + +def test_puiseux_poly_unify(): + R, x = puiseux_ring('x', QQ) + assert 1/x + x == x + 1/x == R({(1,): 1, (-1,): 1}) + assert repr(1/x + x) == 'x**(-1) + x' + assert 1/x + 1/x == 2/x == R({(-1,): 2}) + assert repr(1/x + 1/x) == '2*x**(-1)' + assert x**QQ(1,2) + x**QQ(1,2) == 2*x**QQ(1,2) == R({(QQ(1,2),): 2}) + assert repr(x**QQ(1,2) + x**QQ(1,2)) == '2*x**(1/2)' + assert x**QQ(1,2) + x**QQ(1,3) == R({(QQ(1,2),): 1, (QQ(1,3),): 1}) + assert repr(x**QQ(1,2) + x**QQ(1,3)) == 'x**(1/3) + x**(1/2)' + assert x + x**QQ(1,2) == R({(1,): 1, (QQ(1,2),): 1}) + assert repr(x + x**QQ(1,2)) == 'x**(1/2) + x' + assert 1/x**QQ(1,2) + 1/x**QQ(1,3) == R({(-QQ(1,2),): 1, (-QQ(1,3),): 1}) + assert repr(1/x**QQ(1,2) + 1/x**QQ(1,3)) == 'x**(-1/2) + x**(-1/3)' + assert 1/x + x**QQ(1,2) == x**QQ(1,2) + 1/x == R({(-1,): 1, (QQ(1,2),): 1}) + assert repr(1/x + x**QQ(1,2)) == 'x**(-1) + x**(1/2)' + + +def test_puiseux_poly_arit(): + R, x = puiseux_ring('x', QQ) + R2, y = puiseux_ring('y', QQ) + p = x**2 + 1 + assert +p == p + assert -p == -1 - x**2 + assert p + p == 2*p == 2*x**2 + 2 + assert p + 1 == 1 + p == x**2 + 2 + assert p + QQ(1,2) == QQ(1,2) + p == x**2 + QQ(3,2) + assert p - p == 0 + assert p - 1 == -1 + p == x**2 + assert p - QQ(1,2) == -QQ(1,2) + p == x**2 + QQ(1,2) + assert 1 - p == -p + 1 == -x**2 + assert QQ(1,2) - p == -p + QQ(1,2) == -x**2 - QQ(1,2) + assert p * p == x**4 + 2*x**2 + 1 + assert p * 1 == 1 * p == p + assert 2 * p == p * 2 == 2*x**2 + 2 + assert p * QQ(1,2) == QQ(1,2) * p == QQ(1,2)*x**2 + QQ(1,2) + assert x**QQ(1,2) * x**QQ(1,2) == x + raises(ValueError, lambda: x + y) + raises(ValueError, lambda: x - y) + raises(ValueError, lambda: x * y) + raises(TypeError, lambda: x + None) + raises(TypeError, lambda: x - None) + raises(TypeError, lambda: x * None) + raises(TypeError, lambda: None + x) + raises(TypeError, lambda: None - x) + raises(TypeError, lambda: None * x) + + +def test_puiseux_poly_div(): + R, x = puiseux_ring('x', QQ) + R2, y = puiseux_ring('y', QQ) + p = x**2 - 1 + assert p / 1 == p + assert p / QQ(1,2) == 2*p == 2*x**2 - 2 + assert p / x == x - 1/x == R({(1,): 1, (-1,): -1}) + assert 2 / x == 2*x**-1 == R({(-1,): 2}) + assert QQ(1,2) / x == QQ(1,2)*x**-1 == 1/(2*x) == 1/x/2 == R({(-1,): QQ(1,2)}) + raises(ZeroDivisionError, lambda: p / 0) + raises(ValueError, lambda: (x + 1) / (x + 2)) + raises(ValueError, lambda: (x + 1) / (x + 1)) + raises(ValueError, lambda: x / y) + raises(TypeError, lambda: x / None) + raises(TypeError, lambda: None / x) + + +def test_puiseux_poly_pow(): + R, x = puiseux_ring('x', QQ) + Rz, xz = puiseux_ring('x', ZZ) + assert x**0 == 1 == R({(0,): 1}) + assert x**1 == x == R({(1,): 1}) + assert x**2 == x*x == R({(2,): 1}) + assert x**QQ(1,2) == R({(QQ(1,2),): 1}) + assert x**-1 == 1/x == R({(-1,): 1}) + assert x**-QQ(1,2) == 1/x**QQ(1,2) == R({(-QQ(1,2),): 1}) + assert (2*x)**-1 == 1/(2*x) == QQ(1,2)/x == QQ(1,2)*x**-1 == R({(-1,): QQ(1,2)}) + assert 2/x**2 == 2*x**-2 == R({(-2,): 2}) + assert 2/xz**2 == 2*xz**-2 == Rz({(-2,): 2}) + raises(TypeError, lambda: x**None) + raises(ValueError, lambda: (x + 1)**-1) + raises(ValueError, lambda: (x + 1)**QQ(1,2)) + raises(ValueError, lambda: (2*x)**QQ(1,2)) + raises(ValueError, lambda: (2*xz)**-1) + + +def test_puiseux_poly_diff(): + R, x, y = puiseux_ring('x, y', QQ) + assert (x**2 + 1).diff(x) == 2*x + assert (x**2 + 1).diff(y) == 0 + assert (x**2 + y**2).diff(x) == 2*x + assert (x**QQ(1,2) + y**QQ(1,2)).diff(x) == QQ(1,2)*x**-QQ(1,2) + assert ((x*y)**QQ(1,2)).diff(x) == QQ(1,2)*y**QQ(1,2)*x**-QQ(1,2) diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_pythonrational.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_pythonrational.py new file mode 100644 index 0000000000000000000000000000000000000000..547a5679626fd3a6165b151364bb506a574bb1db --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_pythonrational.py @@ -0,0 +1,139 @@ +"""Tests for PythonRational type. """ + +from sympy.polys.domains import PythonRational as QQ +from sympy.testing.pytest import raises + +def test_PythonRational__init__(): + assert QQ(0).numerator == 0 + assert QQ(0).denominator == 1 + assert QQ(0, 1).numerator == 0 + assert QQ(0, 1).denominator == 1 + assert QQ(0, -1).numerator == 0 + assert QQ(0, -1).denominator == 1 + + assert QQ(1).numerator == 1 + assert QQ(1).denominator == 1 + assert QQ(1, 1).numerator == 1 + assert QQ(1, 1).denominator == 1 + assert QQ(-1, -1).numerator == 1 + assert QQ(-1, -1).denominator == 1 + + assert QQ(-1).numerator == -1 + assert QQ(-1).denominator == 1 + assert QQ(-1, 1).numerator == -1 + assert QQ(-1, 1).denominator == 1 + assert QQ( 1, -1).numerator == -1 + assert QQ( 1, -1).denominator == 1 + + assert QQ(1, 2).numerator == 1 + assert QQ(1, 2).denominator == 2 + assert QQ(3, 4).numerator == 3 + assert QQ(3, 4).denominator == 4 + + assert QQ(2, 2).numerator == 1 + assert QQ(2, 2).denominator == 1 + assert QQ(2, 4).numerator == 1 + assert QQ(2, 4).denominator == 2 + +def test_PythonRational__hash__(): + assert hash(QQ(0)) == hash(0) + assert hash(QQ(1)) == hash(1) + assert hash(QQ(117)) == hash(117) + +def test_PythonRational__int__(): + assert int(QQ(-1, 4)) == 0 + assert int(QQ( 1, 4)) == 0 + assert int(QQ(-5, 4)) == -1 + assert int(QQ( 5, 4)) == 1 + +def test_PythonRational__float__(): + assert float(QQ(-1, 2)) == -0.5 + assert float(QQ( 1, 2)) == 0.5 + +def test_PythonRational__abs__(): + assert abs(QQ(-1, 2)) == QQ(1, 2) + assert abs(QQ( 1, 2)) == QQ(1, 2) + +def test_PythonRational__pos__(): + assert +QQ(-1, 2) == QQ(-1, 2) + assert +QQ( 1, 2) == QQ( 1, 2) + +def test_PythonRational__neg__(): + assert -QQ(-1, 2) == QQ( 1, 2) + assert -QQ( 1, 2) == QQ(-1, 2) + +def test_PythonRational__add__(): + assert QQ(-1, 2) + QQ( 1, 2) == QQ(0) + assert QQ( 1, 2) + QQ(-1, 2) == QQ(0) + + assert QQ(1, 2) + QQ(1, 2) == QQ(1) + assert QQ(1, 2) + QQ(3, 2) == QQ(2) + assert QQ(3, 2) + QQ(1, 2) == QQ(2) + assert QQ(3, 2) + QQ(3, 2) == QQ(3) + + assert 1 + QQ(1, 2) == QQ(3, 2) + assert QQ(1, 2) + 1 == QQ(3, 2) + +def test_PythonRational__sub__(): + assert QQ(-1, 2) - QQ( 1, 2) == QQ(-1) + assert QQ( 1, 2) - QQ(-1, 2) == QQ( 1) + + assert QQ(1, 2) - QQ(1, 2) == QQ( 0) + assert QQ(1, 2) - QQ(3, 2) == QQ(-1) + assert QQ(3, 2) - QQ(1, 2) == QQ( 1) + assert QQ(3, 2) - QQ(3, 2) == QQ( 0) + + assert 1 - QQ(1, 2) == QQ( 1, 2) + assert QQ(1, 2) - 1 == QQ(-1, 2) + +def test_PythonRational__mul__(): + assert QQ(-1, 2) * QQ( 1, 2) == QQ(-1, 4) + assert QQ( 1, 2) * QQ(-1, 2) == QQ(-1, 4) + + assert QQ(1, 2) * QQ(1, 2) == QQ(1, 4) + assert QQ(1, 2) * QQ(3, 2) == QQ(3, 4) + assert QQ(3, 2) * QQ(1, 2) == QQ(3, 4) + assert QQ(3, 2) * QQ(3, 2) == QQ(9, 4) + + assert 2 * QQ(1, 2) == QQ(1) + assert QQ(1, 2) * 2 == QQ(1) + +def test_PythonRational__truediv__(): + assert QQ(-1, 2) / QQ( 1, 2) == QQ(-1) + assert QQ( 1, 2) / QQ(-1, 2) == QQ(-1) + + assert QQ(1, 2) / QQ(1, 2) == QQ(1) + assert QQ(1, 2) / QQ(3, 2) == QQ(1, 3) + assert QQ(3, 2) / QQ(1, 2) == QQ(3) + assert QQ(3, 2) / QQ(3, 2) == QQ(1) + + assert 2 / QQ(1, 2) == QQ(4) + assert QQ(1, 2) / 2 == QQ(1, 4) + + raises(ZeroDivisionError, lambda: QQ(1, 2) / QQ(0)) + raises(ZeroDivisionError, lambda: QQ(1, 2) / 0) + +def test_PythonRational__pow__(): + assert QQ(1)**10 == QQ(1) + assert QQ(2)**10 == QQ(1024) + + assert QQ(1)**(-10) == QQ(1) + assert QQ(2)**(-10) == QQ(1, 1024) + +def test_PythonRational__eq__(): + assert (QQ(1, 2) == QQ(1, 2)) is True + assert (QQ(1, 2) != QQ(1, 2)) is False + + assert (QQ(1, 2) == QQ(1, 3)) is False + assert (QQ(1, 2) != QQ(1, 3)) is True + +def test_PythonRational__lt_le_gt_ge__(): + assert (QQ(1, 2) < QQ(1, 4)) is False + assert (QQ(1, 2) <= QQ(1, 4)) is False + assert (QQ(1, 2) > QQ(1, 4)) is True + assert (QQ(1, 2) >= QQ(1, 4)) is True + + assert (QQ(1, 4) < QQ(1, 2)) is True + assert (QQ(1, 4) <= QQ(1, 2)) is True + assert (QQ(1, 4) > QQ(1, 2)) is False + assert (QQ(1, 4) >= QQ(1, 2)) is False diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_ring_series.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_ring_series.py new file mode 100644 index 0000000000000000000000000000000000000000..d983fc99f8ffcf9361d8d069f1d381928ac0aada --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_ring_series.py @@ -0,0 +1,831 @@ +from sympy.polys.domains import ZZ, QQ, EX, RR +from sympy.polys.rings import ring +from sympy.polys.puiseux import puiseux_ring +from sympy.polys.ring_series import (_invert_monoms, rs_integrate, + rs_trunc, rs_mul, rs_square, rs_pow, _has_constant_term, rs_hadamard_exp, + rs_series_from_list, rs_exp, rs_log, rs_newton, rs_series_inversion, + rs_compose_add, rs_asin, _atan, rs_atan, _atanh, rs_atanh, rs_asinh, rs_tan, + rs_cot, rs_sin, rs_cos, rs_cos_sin, rs_sinh, rs_cosh, rs_cosh_sinh, rs_tanh, + _tan1, rs_fun, rs_nth_root, rs_LambertW, rs_series_reversion, rs_is_puiseux, + rs_series) +from sympy.testing.pytest import raises, slow +from sympy.core.symbol import symbols +from sympy.functions import (sin, cos, exp, tan, cot, sinh, cosh, atan, atanh, + asinh, tanh, log, sqrt) +from sympy.core.numbers import Rational, pi +from sympy.core import expand, S + +def is_close(a, b): + tol = 10**(-10) + assert abs(a - b) < tol + + +def test_ring_series1(): + R, x = ring('x', QQ) + p = x**4 + 2*x**3 + 3*x + 4 + assert _invert_monoms(p) == 4*x**4 + 3*x**3 + 2*x + 1 + assert rs_hadamard_exp(p) == x**4/24 + x**3/3 + 3*x + 4 + R, x = ring('x', QQ) + p = x**4 + 2*x**3 + 3*x + 4 + assert rs_integrate(p, x) == x**5/5 + x**4/2 + 3*x**2/2 + 4*x + R, x, y = ring('x, y', QQ) + p = x**2*y**2 + x + 1 + assert rs_integrate(p, x) == x**3*y**2/3 + x**2/2 + x + assert rs_integrate(p, y) == x**2*y**3/3 + x*y + y + + +def test_trunc(): + R, x, y, t = ring('x, y, t', QQ) + p = (y + t*x)**4 + p1 = rs_trunc(p, x, 3) + assert p1 == y**4 + 4*y**3*t*x + 6*y**2*t**2*x**2 + + +def test_mul_trunc(): + R, x, y, t = ring('x, y, t', QQ) + p = 1 + t*x + t*y + for i in range(2): + p = rs_mul(p, p, t, 3) + + assert p == 6*x**2*t**2 + 12*x*y*t**2 + 6*y**2*t**2 + 4*x*t + 4*y*t + 1 + p = 1 + t*x + t*y + t**2*x*y + p1 = rs_mul(p, p, t, 2) + assert p1 == 1 + 2*t*x + 2*t*y + R1, z = ring('z', QQ) + raises(ValueError, lambda: rs_mul(p, z, x, 2)) + + p1 = 2 + 2*x + 3*x**2 + p2 = 3 + x**2 + assert rs_mul(p1, p2, x, 4) == 2*x**3 + 11*x**2 + 6*x + 6 + + +def test_square_trunc(): + R, x, y, t = ring('x, y, t', QQ) + p = (1 + t*x + t*y)*2 + p1 = rs_mul(p, p, x, 3) + p2 = rs_square(p, x, 3) + assert p1 == p2 + p = 1 + x + x**2 + x**3 + assert rs_square(p, x, 4) == 4*x**3 + 3*x**2 + 2*x + 1 + + +def test_pow_trunc(): + R, x, y, z = ring('x, y, z', QQ) + p0 = y + x*z + p = p0**16 + for xx in (x, y, z): + p1 = rs_trunc(p, xx, 8) + p2 = rs_pow(p0, 16, xx, 8) + assert p1 == p2 + + p = 1 + x + p1 = rs_pow(p, 3, x, 2) + assert p1 == 1 + 3*x + assert rs_pow(p, 0, x, 2) == 1 + assert rs_pow(p, -2, x, 2) == 1 - 2*x + p = x + y + assert rs_pow(p, 3, y, 3) == x**3 + 3*x**2*y + 3*x*y**2 + assert rs_pow(1 + x, Rational(2, 3), x, 4) == 4*x**3/81 - x**2/9 + x*Rational(2, 3) + 1 + + +def test_has_constant_term(): + R, x, y, z = ring('x, y, z', QQ) + p = y + x*z + assert _has_constant_term(p, x) + p = x + x**4 + assert not _has_constant_term(p, x) + p = 1 + x + x**4 + assert _has_constant_term(p, x) + p = x + y + x*z + + +def test_inversion(): + R, x = ring('x', QQ) + p = 2 + x + 2*x**2 + n = 5 + p1 = rs_series_inversion(p, x, n) + assert rs_trunc(p*p1, x, n) == 1 + R, x, y = ring('x, y', QQ) + p = 2 + x + 2*x**2 + y*x + x**2*y + p1 = rs_series_inversion(p, x, n) + assert rs_trunc(p*p1, x, n) == 1 + + R, x, y = ring('x, y', QQ) + p = 1 + x + y + raises(NotImplementedError, lambda: rs_series_inversion(p, x, 4)) + p = R.zero + raises(ZeroDivisionError, lambda: rs_series_inversion(p, x, 3)) + + R, x = ring('x', ZZ) + p = 2 + x + raises(ValueError, lambda: rs_series_inversion(p, x, 3)) + + +def test_series_reversion(): + R, x, y = ring('x, y', QQ) + + p = rs_tan(x, x, 10) + assert rs_series_reversion(p, x, 8, y) == rs_atan(y, y, 8) + + p = rs_sin(x, x, 10) + assert rs_series_reversion(p, x, 8, y) == 5*y**7/112 + 3*y**5/40 + \ + y**3/6 + y + + +def test_series_from_list(): + R, x = ring('x', QQ) + p = 1 + 2*x + x**2 + 3*x**3 + c = [1, 2, 0, 4, 4] + r = rs_series_from_list(p, c, x, 5) + pc = R.from_list(list(reversed(c))) + r1 = rs_trunc(pc.compose(x, p), x, 5) + assert r == r1 + R, x, y = ring('x, y', QQ) + c = [1, 3, 5, 7] + p1 = rs_series_from_list(x + y, c, x, 3, concur=0) + p2 = rs_trunc((1 + 3*(x+y) + 5*(x+y)**2 + 7*(x+y)**3), x, 3) + assert p1 == p2 + + R, x = ring('x', QQ) + h = 25 + p = rs_exp(x, x, h) - 1 + p1 = rs_series_from_list(p, c, x, h) + p2 = 0 + for i, cx in enumerate(c): + p2 += cx*rs_pow(p, i, x, h) + assert p1 == p2 + + +def test_log(): + R, x = ring('x', QQ) + p = 1 + x + assert rs_log(p, x, 4) == x - x**2/2 + x**3/3 + p = 1 + x +2*x**2/3 + p1 = rs_log(p, x, 9) + assert p1 == -17*x**8/648 + 13*x**7/189 - 11*x**6/162 - x**5/45 + \ + 7*x**4/36 - x**3/3 + x**2/6 + x + p2 = rs_series_inversion(p, x, 9) + p3 = rs_log(p2, x, 9) + assert p3 == -p1 + + R, x, y = ring('x, y', QQ) + p = 1 + x + 2*y*x**2 + p1 = rs_log(p, x, 6) + assert p1 == (4*x**5*y**2 - 2*x**5*y - 2*x**4*y**2 + x**5/5 + 2*x**4*y - + x**4/4 - 2*x**3*y + x**3/3 + 2*x**2*y - x**2/2 + x) + + # Constant term in series + a = symbols('a') + R, x, y = ring('x, y', EX) + assert rs_log(x + a, x, 5) == -EX(1/(4*a**4))*x**4 + EX(1/(3*a**3))*x**3 \ + - EX(1/(2*a**2))*x**2 + EX(1/a)*x + EX(log(a)) + assert rs_log(x + x**2*y + a, x, 4) == -EX(a**(-2))*x**3*y + \ + EX(1/(3*a**3))*x**3 + EX(1/a)*x**2*y - EX(1/(2*a**2))*x**2 + \ + EX(1/a)*x + EX(log(a)) + + p = x + x**2 + 3 + assert rs_log(p, x, 10).compose(x, 5) == EX(log(3) + Rational(19281291595, 9920232)) + + +def test_exp(): + R, x = ring('x', QQ) + p = x + x**4 + for h in [10, 30]: + q = rs_series_inversion(1 + p, x, h) - 1 + p1 = rs_exp(q, x, h) + q1 = rs_log(p1, x, h) + assert q1 == q + p1 = rs_exp(p, x, 30) + assert p1.coeff(x**29) == QQ(74274246775059676726972369, 353670479749588078181744640000) + prec = 21 + p = rs_log(1 + x, x, prec) + p1 = rs_exp(p, x, prec) + assert p1 == x + 1 + + # Constant term in series + a = symbols('a') + R, x, y = ring('x, y', QQ[exp(a), a]) + assert rs_exp(x + a, x, 5) == exp(a)*x**4/24 + exp(a)*x**3/6 + \ + exp(a)*x**2/2 + exp(a)*x + exp(a) + assert rs_exp(x + x**2*y + a, x, 5) == exp(a)*x**4*y**2/2 + \ + exp(a)*x**4*y/2 + exp(a)*x**4/24 + exp(a)*x**3*y + \ + exp(a)*x**3/6 + exp(a)*x**2*y + exp(a)*x**2/2 + exp(a)*x + exp(a) + + R, x, y = ring('x, y', EX) + assert rs_exp(x + a, x, 5) == EX(exp(a)/24)*x**4 + EX(exp(a)/6)*x**3 + \ + EX(exp(a)/2)*x**2 + EX(exp(a))*x + EX(exp(a)) + assert rs_exp(x + x**2*y + a, x, 5) == EX(exp(a)/2)*x**4*y**2 + \ + EX(exp(a)/2)*x**4*y + EX(exp(a)/24)*x**4 + EX(exp(a))*x**3*y + \ + EX(exp(a)/6)*x**3 + EX(exp(a))*x**2*y + EX(exp(a)/2)*x**2 + \ + EX(exp(a))*x + EX(exp(a)) + + +def test_newton(): + R, x = ring('x', QQ) + p = x**2 - 2 + r = rs_newton(p, x, 4) + assert r == 8*x**4 + 4*x**2 + 2 + + +def test_compose_add(): + R, x = ring('x', QQ) + p1 = x**3 - 1 + p2 = x**2 - 2 + assert rs_compose_add(p1, p2) == x**6 - 6*x**4 - 2*x**3 + 12*x**2 - 12*x - 7 + + +def test_fun(): + R, x, y = ring('x, y', QQ) + p = x*y + x**2*y**3 + x**5*y + assert rs_fun(p, rs_tan, x, 10) == rs_tan(p, x, 10) + assert rs_fun(p, _tan1, x, 10) == _tan1(p, x, 10) + + +def test_nth_root(): + R, x, y = puiseux_ring('x, y', QQ) + assert rs_nth_root(1 + x**2*y, 4, x, 10) == -77*x**8*y**4/2048 + \ + 7*x**6*y**3/128 - 3*x**4*y**2/32 + x**2*y/4 + 1 + assert rs_nth_root(1 + x*y + x**2*y**3, 3, x, 5) == -x**4*y**6/9 + \ + 5*x**4*y**5/27 - 10*x**4*y**4/243 - 2*x**3*y**4/9 + 5*x**3*y**3/81 + \ + x**2*y**3/3 - x**2*y**2/9 + x*y/3 + 1 + assert rs_nth_root(8*x, 3, x, 3) == 2*x**QQ(1, 3) + assert rs_nth_root(8*x + x**2 + x**3, 3, x, 3) == x**QQ(4,3)/12 + 2*x**QQ(1,3) + r = rs_nth_root(8*x + x**2*y + x**3, 3, x, 4) + assert r == -x**QQ(7,3)*y**2/288 + x**QQ(7,3)/12 + x**QQ(4,3)*y/12 + 2*x**QQ(1,3) + + # Constant term in series + a = symbols('a') + R, x, y = puiseux_ring('x, y', EX) + assert rs_nth_root(x + EX(a), 3, x, 4) == EX(5/(81*a**QQ(8, 3)))*x**3 - \ + EX(1/(9*a**QQ(5, 3)))*x**2 + EX(1/(3*a**QQ(2, 3)))*x + EX(a**QQ(1, 3)) + assert rs_nth_root(x**QQ(2, 3) + x**2*y + 5, 2, x, 3) == -EX(sqrt(5)/100)*\ + x**QQ(8, 3)*y - EX(sqrt(5)/16000)*x**QQ(8, 3) + EX(sqrt(5)/10)*x**2*y + \ + EX(sqrt(5)/2000)*x**2 - EX(sqrt(5)/200)*x**QQ(4, 3) + \ + EX(sqrt(5)/10)*x**QQ(2, 3) + EX(sqrt(5)) + + +def test_atan(): + R, x, y = ring('x, y', QQ) + assert rs_atan(x, x, 9) == -x**7/7 + x**5/5 - x**3/3 + x + assert rs_atan(x*y + x**2*y**3, x, 9) == 2*x**8*y**11 - x**8*y**9 + \ + 2*x**7*y**9 - x**7*y**7/7 - x**6*y**9/3 + x**6*y**7 - x**5*y**7 + \ + x**5*y**5/5 - x**4*y**5 - x**3*y**3/3 + x**2*y**3 + x*y + + # Constant term in series + a = symbols('a') + R, x, y = ring('x, y', EX) + assert rs_atan(x + a, x, 5) == -EX((a**3 - a)/(a**8 + 4*a**6 + 6*a**4 + \ + 4*a**2 + 1))*x**4 + EX((3*a**2 - 1)/(3*a**6 + 9*a**4 + \ + 9*a**2 + 3))*x**3 - EX(a/(a**4 + 2*a**2 + 1))*x**2 + \ + EX(1/(a**2 + 1))*x + EX(atan(a)) + assert rs_atan(x + x**2*y + a, x, 4) == -EX(2*a/(a**4 + 2*a**2 + 1)) \ + *x**3*y + EX((3*a**2 - 1)/(3*a**6 + 9*a**4 + 9*a**2 + 3))*x**3 + \ + EX(1/(a**2 + 1))*x**2*y - EX(a/(a**4 + 2*a**2 + 1))*x**2 + EX(1/(a**2 \ + + 1))*x + EX(atan(a)) + + # Test for _atan faster for small and univariate series + R, x = ring('x', QQ) + p = x**2 + 2*x + assert _atan(p, x, 5) == rs_atan(p, x, 5) + + R, x = ring('x', EX) + p = x**2 + 2*x + assert _atan(p, x, 9) == rs_atan(p, x, 9) + + +def test_asin(): + R, x, y = ring('x, y', QQ) + assert rs_asin(x + x*y, x, 5) == x**3*y**3/6 + x**3*y**2/2 + x**3*y/2 + \ + x**3/6 + x*y + x + assert rs_asin(x*y + x**2*y**3, x, 6) == x**5*y**7/2 + 3*x**5*y**5/40 + \ + x**4*y**5/2 + x**3*y**3/6 + x**2*y**3 + x*y + + +def test_tan(): + R, x, y = ring('x, y', QQ) + assert rs_tan(x, x, 9) == x + x**3/3 + QQ(2,15)*x**5 + QQ(17,315)*x**7 + assert rs_tan(x*y + x**2*y**3, x, 9) == 4*x**8*y**11/3 + 17*x**8*y**9/45 + \ + 4*x**7*y**9/3 + 17*x**7*y**7/315 + x**6*y**9/3 + 2*x**6*y**7/3 + \ + x**5*y**7 + 2*x**5*y**5/15 + x**4*y**5 + x**3*y**3/3 + x**2*y**3 + x*y + + # Constant term in series + a = symbols('a') + R, x, y = ring('x, y', QQ[tan(a), a]) + assert rs_tan(x + a, x, 5) == (tan(a)**5 + 5*tan(a)**3/3 + + 2*tan(a)/3)*x**4 + (tan(a)**4 + 4*tan(a)**2/3 + Rational(1, 3))*x**3 + \ + (tan(a)**3 + tan(a))*x**2 + (tan(a)**2 + 1)*x + tan(a) + assert rs_tan(x + x**2*y + a, x, 4) == (2*tan(a)**3 + 2*tan(a))*x**3*y + \ + (tan(a)**4 + Rational(4, 3)*tan(a)**2 + Rational(1, 3))*x**3 + (tan(a)**2 + 1)*x**2*y + \ + (tan(a)**3 + tan(a))*x**2 + (tan(a)**2 + 1)*x + tan(a) + + R, x, y = ring('x, y', EX) + assert rs_tan(x + a, x, 5) == EX(tan(a)**5 + 5*tan(a)**3/3 + + 2*tan(a)/3)*x**4 + EX(tan(a)**4 + 4*tan(a)**2/3 + EX(1)/3)*x**3 + \ + EX(tan(a)**3 + tan(a))*x**2 + EX(tan(a)**2 + 1)*x + EX(tan(a)) + assert rs_tan(x + x**2*y + a, x, 4) == EX(2*tan(a)**3 + + 2*tan(a))*x**3*y + EX(tan(a)**4 + 4*tan(a)**2/3 + EX(1)/3)*x**3 + \ + EX(tan(a)**2 + 1)*x**2*y + EX(tan(a)**3 + tan(a))*x**2 + \ + EX(tan(a)**2 + 1)*x + EX(tan(a)) + + p = x + x**2 + 5 + assert rs_atan(p, x, 10).compose(x, 10) == EX(atan(5) + S(67701870330562640) / \ + 668083460499) + + +def test_cot(): + R, x, y = puiseux_ring('x, y', QQ) + assert rs_cot(x**6 + x**7, x, 8) == x**(-6) - x**(-5) + x**(-4) - \ + x**(-3) + x**(-2) - x**(-1) + 1 - x + x**2 - x**3 + x**4 - x**5 + \ + 2*x**6/3 - 4*x**7/3 + assert rs_cot(x + x**2*y, x, 5) == -x**4*y**5 - x**4*y/15 + x**3*y**4 - \ + x**3/45 - x**2*y**3 - x**2*y/3 + x*y**2 - x/3 - y + x**(-1) + + +def test_sin(): + R, x, y = ring('x, y', QQ) + assert rs_sin(x, x, 9) == x - x**3/6 + x**5/120 - x**7/5040 + assert rs_sin(x*y + x**2*y**3, x, 9) == x**8*y**11/12 - \ + x**8*y**9/720 + x**7*y**9/12 - x**7*y**7/5040 - x**6*y**9/6 + \ + x**6*y**7/24 - x**5*y**7/2 + x**5*y**5/120 - x**4*y**5/2 - \ + x**3*y**3/6 + x**2*y**3 + x*y + + # Constant term in series + a = symbols('a') + R, x, y = ring('x, y', QQ[sin(a), cos(a), a]) + assert rs_sin(x + a, x, 5) == sin(a)*x**4/24 - cos(a)*x**3/6 - \ + sin(a)*x**2/2 + cos(a)*x + sin(a) + assert rs_sin(x + x**2*y + a, x, 5) == -sin(a)*x**4*y**2/2 - \ + cos(a)*x**4*y/2 + sin(a)*x**4/24 - sin(a)*x**3*y - cos(a)*x**3/6 + \ + cos(a)*x**2*y - sin(a)*x**2/2 + cos(a)*x + sin(a) + + R, x, y = ring('x, y', EX) + assert rs_sin(x + a, x, 5) == EX(sin(a)/24)*x**4 - EX(cos(a)/6)*x**3 - \ + EX(sin(a)/2)*x**2 + EX(cos(a))*x + EX(sin(a)) + assert rs_sin(x + x**2*y + a, x, 5) == -EX(sin(a)/2)*x**4*y**2 - \ + EX(cos(a)/2)*x**4*y + EX(sin(a)/24)*x**4 - EX(sin(a))*x**3*y - \ + EX(cos(a)/6)*x**3 + EX(cos(a))*x**2*y - EX(sin(a)/2)*x**2 + \ + EX(cos(a))*x + EX(sin(a)) + + +def test_cos(): + R, x, y = ring('x, y', QQ) + assert rs_cos(x, x, 9) == 1 - x**2/2 + x**4/24 - x**6/720 + x**8/40320 + assert rs_cos(x*y + x**2*y**3, x, 9) == x**8*y**12/24 - \ + x**8*y**10/48 + x**8*y**8/40320 + x**7*y**10/6 - \ + x**7*y**8/120 + x**6*y**8/4 - x**6*y**6/720 + x**5*y**6/6 - \ + x**4*y**6/2 + x**4*y**4/24 - x**3*y**4 - x**2*y**2/2 + 1 + + # Constant term in series + a = symbols('a') + R, x, y = ring('x, y', QQ[sin(a), cos(a), a]) + assert rs_cos(x + a, x, 5) == cos(a)*x**4/24 + sin(a)*x**3/6 - \ + cos(a)*x**2/2 - sin(a)*x + cos(a) + assert rs_cos(x + x**2*y + a, x, 5) == -cos(a)*x**4*y**2/2 + \ + sin(a)*x**4*y/2 + cos(a)*x**4/24 - cos(a)*x**3*y + sin(a)*x**3/6 - \ + sin(a)*x**2*y - cos(a)*x**2/2 - sin(a)*x + cos(a) + + R, x, y = ring('x, y', EX) + assert rs_cos(x + a, x, 5) == EX(cos(a)/24)*x**4 + EX(sin(a)/6)*x**3 - \ + EX(cos(a)/2)*x**2 - EX(sin(a))*x + EX(cos(a)) + assert rs_cos(x + x**2*y + a, x, 5) == -EX(cos(a)/2)*x**4*y**2 + \ + EX(sin(a)/2)*x**4*y + EX(cos(a)/24)*x**4 - EX(cos(a))*x**3*y + \ + EX(sin(a)/6)*x**3 - EX(sin(a))*x**2*y - EX(cos(a)/2)*x**2 - \ + EX(sin(a))*x + EX(cos(a)) + + +def test_cos_sin(): + R, x, y = ring('x, y', QQ) + c, s = rs_cos_sin(x, x, 9) + assert c == rs_cos(x, x, 9) + assert s == rs_sin(x, x, 9) + c, s = rs_cos_sin(x + x*y, x, 5) + assert c == rs_cos(x + x*y, x, 5) + assert s == rs_sin(x + x*y, x, 5) + + # constant term in series + c, s = rs_cos_sin(1 + x + x**2, x, 5) + assert c == rs_cos(1 + x + x**2, x, 5) + assert s == rs_sin(1 + x + x**2, x, 5) + + a = symbols('a') + R, x, y = ring('x, y', QQ[sin(a), cos(a), a]) + c, s = rs_cos_sin(x + a, x, 5) + assert c == rs_cos(x + a, x, 5) + assert s == rs_sin(x + a, x, 5) + + R, x, y = ring('x, y', EX) + c, s = rs_cos_sin(x + a, x, 5) + assert c == rs_cos(x + a, x, 5) + assert s == rs_sin(x + a, x, 5) + + +def test_atanh(): + R, x, y = ring('x, y', QQ) + assert rs_atanh(x, x, 9) == x + x**3/3 + x**5/5 + x**7/7 + assert rs_atanh(x*y + x**2*y**3, x, 9) == 2*x**8*y**11 + x**8*y**9 + \ + 2*x**7*y**9 + x**7*y**7/7 + x**6*y**9/3 + x**6*y**7 + x**5*y**7 + \ + x**5*y**5/5 + x**4*y**5 + x**3*y**3/3 + x**2*y**3 + x*y + + # Constant term in series + a = symbols('a') + R, x, y = ring('x, y', EX) + assert rs_atanh(x + a, x, 5) == EX((a**3 + a)/(a**8 - 4*a**6 + 6*a**4 - \ + 4*a**2 + 1))*x**4 - EX((3*a**2 + 1)/(3*a**6 - 9*a**4 + \ + 9*a**2 - 3))*x**3 + EX(a/(a**4 - 2*a**2 + 1))*x**2 - EX(1/(a**2 - \ + 1))*x + EX(atanh(a)) + assert rs_atanh(x + x**2*y + a, x, 4) == EX(2*a/(a**4 - 2*a**2 + \ + 1))*x**3*y - EX((3*a**2 + 1)/(3*a**6 - 9*a**4 + 9*a**2 - 3))*x**3 - \ + EX(1/(a**2 - 1))*x**2*y + EX(a/(a**4 - 2*a**2 + 1))*x**2 - \ + EX(1/(a**2 - 1))*x + EX(atanh(a)) + + p = x + x**2 + 5 + assert rs_atanh(p, x, 10).compose(x, 10) == EX(Rational(-733442653682135, 5079158784) \ + + atanh(5)) + + # Test for _atanh faster for small and univariate series + R,x = ring('x', QQ) + p = x**2 + 2*x + assert _atanh(p, x, 5) == rs_atanh(p, x, 5) + + R,x = ring('x', EX) + p = x**2 + 2*x + assert _atanh(p, x, 9) == rs_atanh(p, x, 9) + + +def test_asinh(): + R, x, y = ring('x, y', QQ) + assert rs_asinh(x, x, 9) == -5/112*x**7 + 3/40*x**5 - 1/6*x**3 + x + assert rs_asinh(x*y + x**2*y**3, x, 9) == 3/4*x**8*y**11 - 5/16*x**8*y**9 + \ + 3/4*x**7*y**9 - 5/112*x**7*y**7 - 1/6*x**6*y**9 + 3/8*x**6*y**7 - 1/2*x \ + **5*y**7 + 3/40*x**5*y**5 - 1/2*x**4*y**5 - 1/6*x**3*y**3 + x**2*y**3 + x*y + + # Constant term in series + a = symbols('a') + R, x, y = ring('x, y', EX) + assert rs_asinh(x + a, x, 3) == -EX(a/(2*a**2*sqrt(a**2 + 1) + 2*sqrt(a**2 + 1))) \ + *x**2 + EX(1/sqrt(a**2 + 1))*x + EX(asinh(a)) + assert rs_asinh(x + x**2*y + a, x, 3) == EX(1/sqrt(a**2 + 1))*x**2*y - EX(a/(2*a**2 \ + *sqrt(a**2 + 1) + 2*sqrt(a**2 + 1)))*x**2 + EX(1/sqrt(a**2 + 1))*x + EX(asinh(a)) + + p = x + x ** 2 + 5 + assert rs_asinh(p, x, 10).compose(x, 10) == EX(asinh(5) + 4643789843094995*sqrt(26)/\ + 205564141692) + + +def test_sinh(): + R, x, y = ring('x, y', QQ) + assert rs_sinh(x, x, 9) == x + x**3/6 + x**5/120 + x**7/5040 + assert rs_sinh(x*y + x**2*y**3, x, 9) == x**8*y**11/12 + \ + x**8*y**9/720 + x**7*y**9/12 + x**7*y**7/5040 + x**6*y**9/6 + \ + x**6*y**7/24 + x**5*y**7/2 + x**5*y**5/120 + x**4*y**5/2 + \ + x**3*y**3/6 + x**2*y**3 + x*y + + # constant term in series + a = symbols('a') + R, x, y = ring('x, y', QQ[sinh(a), cosh(a), a]) + assert rs_sinh(x + a, x, 5) == 1/24*x**4*(sinh(a)) + 1/6*x**3*(cosh(a)) + 1/\ + 2*x**2*(sinh(a)) + x*(cosh(a)) + (sinh(a)) + assert rs_sinh(x + x**2*y + a, x, 5) == 1/2*(sinh(a))*x**4*y**2 + 1/2*(cosh(a))\ + *x**4*y + 1/24*(sinh(a))*x**4 + (sinh(a))*x**3*y + 1/6*(cosh(a))*x**3 + \ + (cosh(a))*x**2*y + 1/2*(sinh(a))*x**2 + (cosh(a))*x + (sinh(a)) + + R, x, y = ring('x, y', EX) + assert rs_sinh(x + a, x, 5) == EX(sinh(a)/24)*x**4 + EX(cosh(a)/6)*x**3 + \ + EX(sinh(a)/2)*x**2 + EX(cosh(a))*x + EX(sinh(a)) + assert rs_sinh(x + x**2*y + a, x, 5) == EX(sinh(a)/2)*x**4*y**2 + EX(cosh(a)/\ + 2)*x**4*y + EX(sinh(a)/24)*x**4 + EX(sinh(a))*x**3*y + EX(cosh(a)/6)*x**3 \ + + EX(cosh(a))*x**2*y + EX(sinh(a)/2)*x**2 + EX(cosh(a))*x + EX(sinh(a)) + + +def test_cosh(): + R, x, y = ring('x, y', QQ) + assert rs_cosh(x, x, 9) == 1 + x**2/2 + x**4/24 + x**6/720 + x**8/40320 + assert rs_cosh(x*y + x**2*y**3, x, 9) == x**8*y**12/24 + \ + x**8*y**10/48 + x**8*y**8/40320 + x**7*y**10/6 + \ + x**7*y**8/120 + x**6*y**8/4 + x**6*y**6/720 + x**5*y**6/6 + \ + x**4*y**6/2 + x**4*y**4/24 + x**3*y**4 + x**2*y**2/2 + 1 + + # constant term in series + a = symbols('a') + R, x, y = ring('x, y', QQ[sinh(a), cosh(a), a]) + assert rs_cosh(x + a, x, 5) == 1/24*(cosh(a))*x**4 + 1/6*(sinh(a))*x**3 + \ + 1/2*(cosh(a))*x**2 + (sinh(a))*x + (cosh(a)) + assert rs_cosh(x + x**2*y + a, x, 5) == 1/2*(cosh(a))*x**4*y**2 + 1/2*(sinh(a))\ + *x**4*y + 1/24*(cosh(a))*x**4 + (cosh(a))*x**3*y + 1/6*(sinh(a))*x**3 + \ + (sinh(a))*x**2*y + 1/2*(cosh(a))*x**2 + (sinh(a))*x + (cosh(a)) + R, x, y = ring('x, y', EX) + assert rs_cosh(x + a, x, 5) == EX(cosh(a)/24)*x**4 + EX(sinh(a)/6)*x**3 + \ + EX(cosh(a)/2)*x**2 + EX(sinh(a))*x + EX(cosh(a)) + assert rs_cosh(x + x**2*y + a, x, 5) == EX(cosh(a)/2)*x**4*y**2 + EX(sinh(a)/\ + 2)*x**4*y + EX(cosh(a)/24)*x**4 + EX(cosh(a))*x**3*y + EX(sinh(a)/6)*x**3 \ + + EX(sinh(a))*x**2*y + EX(cosh(a)/2)*x**2 + EX(sinh(a))*x + EX(cosh(a)) + + +def test_cosh_sinh(): + R, x, y = ring('x, y', QQ) + ch, sh = rs_cosh_sinh(x, x, 9) + assert ch == rs_cosh(x, x, 9) + assert sh == rs_sinh(x, x, 9) + ch, sh = rs_cosh_sinh(x + x*y, x, 5) + assert ch == rs_cosh(x + x*y, x, 5) + assert sh == rs_sinh(x + x*y, x, 5) + + # constant term in series + c, s = rs_cosh_sinh(1 + x + x**2, x, 5) + assert c == rs_cosh(1 + x + x**2, x, 5) + assert s == rs_sinh(1 + x + x**2, x, 5) + + a = symbols('a') + R, x, y = ring('x, y', QQ[sinh(a), cosh(a), a]) + ch, sh = rs_cosh_sinh(x + a, x, 5) + assert ch == rs_cosh(x + a, x, 5) + assert sh == rs_sinh(x + a, x, 5) + R, x, y = ring('x, y', EX) + ch, sh = rs_cosh_sinh(x + a, x, 5) + assert ch == rs_cosh(x + a, x, 5) + assert sh == rs_sinh(x + a, x, 5) + + +def test_tanh(): + R, x, y = ring('x, y', QQ) + assert rs_tanh(x, x, 9) == x - QQ(1,3)*x**3 + QQ(2,15)*x**5 - QQ(17,315)*x**7 + assert rs_tanh(x*y + x**2*y**3, x, 9) == 4*x**8*y**11/3 - \ + 17*x**8*y**9/45 + 4*x**7*y**9/3 - 17*x**7*y**7/315 - x**6*y**9/3 + \ + 2*x**6*y**7/3 - x**5*y**7 + 2*x**5*y**5/15 - x**4*y**5 - \ + x**3*y**3/3 + x**2*y**3 + x*y + + # Constant term in series + a = symbols('a') + R, x, y = ring('x, y', EX) + assert rs_tanh(x + a, x, 5) == EX(tanh(a)**5 - 5*tanh(a)**3/3 + + 2*tanh(a)/3)*x**4 + EX(-tanh(a)**4 + 4*tanh(a)**2/3 - QQ(1, 3))*x**3 + \ + EX(tanh(a)**3 - tanh(a))*x**2 + EX(-tanh(a)**2 + 1)*x + EX(tanh(a)) + + p = rs_tanh(x + x**2*y + a, x, 4) + assert (p.compose(x, 10)).compose(y, 5) == EX(-1000*tanh(a)**4 + \ + 10100*tanh(a)**3 + 2470*tanh(a)**2/3 - 10099*tanh(a) + QQ(530, 3)) + + +def test_RR(): + rs_funcs = [rs_sin, rs_cos, rs_tan, rs_cot, rs_atan, rs_tanh] + sympy_funcs = [sin, cos, tan, cot, atan, tanh] + R, x, y = ring('x, y', RR) + a = symbols('a') + for rs_func, sympy_func in zip(rs_funcs, sympy_funcs): + p = rs_func(2 + x, x, 5).compose(x, 5) + q = sympy_func(2 + a).series(a, 0, 5).removeO() + is_close(p.as_expr(), q.subs(a, 5).n()) + + p = rs_nth_root(2 + x, 5, x, 5).compose(x, 5) + q = ((2 + a)**QQ(1, 5)).series(a, 0, 5).removeO() + is_close(p.as_expr(), q.subs(a, 5).n()) + + +def test_is_regular(): + R, x, y = puiseux_ring('x, y', QQ) + p = 1 + 2*x + x**2 + 3*x**3 + assert not rs_is_puiseux(p, x) + + p = x + x**QQ(1,5)*y + assert rs_is_puiseux(p, x) + assert not rs_is_puiseux(p, y) + + p = x + x**2*y**QQ(1,5)*y + assert not rs_is_puiseux(p, x) + + +def test_puiseux(): + R, x, y = puiseux_ring('x, y', QQ) + p = x**QQ(2,5) + x**QQ(2,3) + x + + r = rs_series_inversion(p, x, 1) + r1 = -x**QQ(14,15) + x**QQ(4,5) - 3*x**QQ(11,15) + x**QQ(2,3) + \ + 2*x**QQ(7,15) - x**QQ(2,5) - x**QQ(1,5) + x**QQ(2,15) - x**QQ(-2,15) \ + + x**QQ(-2,5) + assert r == r1 + + r = rs_nth_root(1 + p, 3, x, 1) + assert r == -x**QQ(4,5)/9 + x**QQ(2,3)/3 + x**QQ(2,5)/3 + 1 + + r = rs_log(1 + p, x, 1) + assert r == -x**QQ(4,5)/2 + x**QQ(2,3) + x**QQ(2,5) + + r = rs_LambertW(p, x, 1) + assert r == -x**QQ(4,5) + x**QQ(2,3) + x**QQ(2,5) + + p1 = x + x**QQ(1,5)*y + r = rs_exp(p1, x, 1) + assert r == x**QQ(4,5)*y**4/24 + x**QQ(3,5)*y**3/6 + x**QQ(2,5)*y**2/2 + \ + x**QQ(1,5)*y + 1 + + r = rs_atan(p, x, 2) + assert r == -x**QQ(9,5) - x**QQ(26,15) - x**QQ(22,15) - x**QQ(6,5)/3 + \ + x + x**QQ(2,3) + x**QQ(2,5) + + r = rs_atan(p1, x, 2) + assert r == x**QQ(9,5)*y**9/9 + x**QQ(9,5)*y**4 - x**QQ(7,5)*y**7/7 - \ + x**QQ(7,5)*y**2 + x*y**5/5 + x - x**QQ(3,5)*y**3/3 + x**QQ(1,5)*y + + r = rs_tan(p, x, 2) + assert r == x**QQ(2,5) + x**QQ(2,3) + x + QQ(1,3)*x**QQ(6,5) + x**QQ(22,15)\ + + x**QQ(26,15) + x**QQ(9,5) + + r = rs_sin(p, x, 2) + assert r == x**QQ(2,5) + x**QQ(2,3) + x - QQ(1,6)*x**QQ(6,5) - QQ(1,2)*x**\ + QQ(22,15) - QQ(1,2)*x**QQ(26,15) - QQ(1,2)*x**QQ(9,5) + + r = rs_cos(p, x, 2) + assert r == 1 - QQ(1,2)*x**QQ(4,5) - x**QQ(16,15) - QQ(1,2)*x**QQ(4,3) - \ + x**QQ(7,5) + QQ(1,24)*x**QQ(8,5) - x**QQ(5,3) + QQ(1,6)*x**QQ(28,15) + + r = rs_asin(p, x, 2) + assert r == x**QQ(9,5)/2 + x**QQ(26,15)/2 + x**QQ(22,15)/2 + \ + x**QQ(6,5)/6 + x + x**QQ(2,3) + x**QQ(2,5) + + r = rs_cot(p, x, 1) + assert r == -x**QQ(14,15) + x**QQ(4,5) - 3*x**QQ(11,15) + \ + 2*x**QQ(2,3)/3 + 2*x**QQ(7,15) - 4*x**QQ(2,5)/3 - x**QQ(1,5) + \ + x**QQ(2,15) - x**QQ(-2,15) + x**QQ(-2,5) + + r = rs_cos_sin(p, x, 2) + assert r[0] == x**QQ(28,15)/6 - x**QQ(5,3) + x**QQ(8,5)/24 - x**QQ(7,5) - \ + x**QQ(4,3)/2 - x**QQ(16,15) - x**QQ(4,5)/2 + 1 + assert r[1] == -x**QQ(9,5)/2 - x**QQ(26,15)/2 - x**QQ(22,15)/2 - \ + x**QQ(6,5)/6 + x + x**QQ(2,3) + x**QQ(2,5) + + r = rs_atanh(p, x, 2) + assert r == x**QQ(9,5) + x**QQ(26,15) + x**QQ(22,15) + x**QQ(6,5)/3 + x + \ + x**QQ(2,3) + x**QQ(2,5) + + r = rs_asinh(p, x, 2) + assert r == x**QQ(2,5) + x**QQ(2,3) + x - QQ(1,6)*x**QQ(6,5) - QQ(1,2)*x**\ + QQ(22,15) - QQ(1,2)*x**QQ(26,15) - QQ(1,2)*x**QQ(9,5) + + r = rs_cosh(p, x, 2) + assert r == x**QQ(28,15)/6 + x**QQ(5,3) + x**QQ(8,5)/24 + x**QQ(7,5) + \ + x**QQ(4,3)/2 + x**QQ(16,15) + x**QQ(4,5)/2 + 1 + + r = rs_sinh(p, x, 2) + assert r == x**QQ(9,5)/2 + x**QQ(26,15)/2 + x**QQ(22,15)/2 + \ + x**QQ(6,5)/6 + x + x**QQ(2,3) + x**QQ(2,5) + + r = rs_cosh_sinh(p, x, 2) + assert r[0] == x**QQ(28,15)/6 + x**QQ(5,3) + x**QQ(8,5)/24 + x**QQ(7,5) + \ + x**QQ(4,3)/2 + x**QQ(16,15) + x**QQ(4,5)/2 + 1 + assert r[1] == x**QQ(9,5)/2 + x**QQ(26,15)/2 + x**QQ(22,15)/2 + \ + x**QQ(6,5)/6 + x + x**QQ(2,3) + x**QQ(2,5) + + r = rs_tanh(p, x, 2) + assert r == -x**QQ(9,5) - x**QQ(26,15) - x**QQ(22,15) - x**QQ(6,5)/3 + \ + x + x**QQ(2,3) + x**QQ(2,5) + + +def test_puiseux_algebraic(): # https://github.com/sympy/sympy/issues/24395 + + K = QQ.algebraic_field(sqrt(2)) + sqrt2 = K.from_sympy(sqrt(2)) + x, y = symbols('x, y') + R, xr, yr = puiseux_ring([x, y], K) + p = (1+sqrt2)*xr**QQ(1,2) + (1-sqrt2)*yr**QQ(2,3) + + assert p.to_dict() == {(QQ(1,2),QQ(0)):1+sqrt2, (QQ(0),QQ(2,3)):1-sqrt2} + assert p.as_expr() == (1 + sqrt(2))*x**(S(1)/2) + (1 - sqrt(2))*y**(S(2)/3) + + +def test1(): + R, x = puiseux_ring('x', QQ) + r = rs_sin(x, x, 15)*x**(-5) + assert r == x**8/6227020800 - x**6/39916800 + x**4/362880 - x**2/5040 + \ + QQ(1,120) - x**-2/6 + x**-4 + + p = rs_sin(x, x, 10) + r = rs_nth_root(p, 2, x, 10) + assert r == -67*x**QQ(17,2)/29030400 - x**QQ(13,2)/24192 + \ + x**QQ(9,2)/1440 - x**QQ(5,2)/12 + x**QQ(1,2) + + p = rs_sin(x, x, 10) + r = rs_nth_root(p, 7, x, 10) + r = rs_pow(r, 5, x, 10) + assert r == -97*x**QQ(61,7)/124467840 - x**QQ(47,7)/16464 + \ + 11*x**QQ(33,7)/3528 - 5*x**QQ(19,7)/42 + x**QQ(5,7) + + r = rs_exp(x**QQ(1,2), x, 10) + assert r == x**QQ(19,2)/121645100408832000 + x**9/6402373705728000 + \ + x**QQ(17,2)/355687428096000 + x**8/20922789888000 + \ + x**QQ(15,2)/1307674368000 + x**7/87178291200 + \ + x**QQ(13,2)/6227020800 + x**6/479001600 + x**QQ(11,2)/39916800 + \ + x**5/3628800 + x**QQ(9,2)/362880 + x**4/40320 + x**QQ(7,2)/5040 + \ + x**3/720 + x**QQ(5,2)/120 + x**2/24 + x**QQ(3,2)/6 + x/2 + \ + x**QQ(1,2) + 1 + + +def test_puiseux2(): + R, y = ring('y', QQ) + S, x = puiseux_ring('x', R.to_domain()) + + p = x + x**QQ(1,5)*y + r = rs_atan(p, x, 3) + assert r == (y**13/13 + y**8 + 2*y**3)*x**QQ(13,5) - (y**11/11 + y**6 + + y)*x**QQ(11,5) + (y**9/9 + y**4)*x**QQ(9,5) - (y**7/7 + + y**2)*x**QQ(7,5) + (y**5/5 + 1)*x - y**3*x**QQ(3,5)/3 + y*x**QQ(1,5) + + +@slow +def test_rs_series(): + x, a, b, c = symbols('x, a, b, c') + + assert rs_series(a, a, 5).as_expr() == a + assert rs_series(sin(a), a, 5).as_expr() == (sin(a).series(a, 0, + 5)).removeO() + assert rs_series(sin(a) + cos(a), a, 5).as_expr() == ((sin(a) + + cos(a)).series(a, 0, 5)).removeO() + assert rs_series(sin(a)*cos(a), a, 5).as_expr() == ((sin(a)* + cos(a)).series(a, 0, 5)).removeO() + + p = (sin(a) - a)*(cos(a**2) + a**4/2) + assert expand(rs_series(p, a, 10).as_expr()) == expand(p.series(a, 0, + 10).removeO()) + + p = sin(a**2/2 + a/3) + cos(a/5)*sin(a/2)**3 + assert expand(rs_series(p, a, 5).as_expr()) == expand(p.series(a, 0, + 5).removeO()) + + p = sin(x**2 + a)*(cos(x**3 - 1) - a - a**2) + assert expand(rs_series(p, a, 5).as_expr()) == expand(p.series(a, 0, + 5).removeO()) + + p = sin(a**2 - a/3 + 2)**5*exp(a**3 - a/2) + assert expand(rs_series(p, a, 10).as_expr()) == expand(p.series(a, 0, + 10).removeO()) + + p = sin(a + b + c) + assert expand(rs_series(p, a, 5).as_expr()) == expand(p.series(a, 0, + 5).removeO()) + + p = tan(sin(a**2 + 4) + b + c) + assert expand(rs_series(p, a, 6).as_expr()) == expand(p.series(a, 0, + 6).removeO()) + + p = a**QQ(2,5) + a**QQ(2,3) + a + + r = rs_series(tan(p), a, 2) + assert r.as_expr() == a**QQ(9,5) + a**QQ(26,15) + a**QQ(22,15) + a**QQ(6,5)/3 + \ + a + a**QQ(2,3) + a**QQ(2,5) + + r = rs_series(exp(p), a, 1) + assert r.as_expr() == a**QQ(4,5)/2 + a**QQ(2,3) + a**QQ(2,5) + 1 + + r = rs_series(sin(p), a, 2) + assert r.as_expr() == -a**QQ(9,5)/2 - a**QQ(26,15)/2 - a**QQ(22,15)/2 - \ + a**QQ(6,5)/6 + a + a**QQ(2,3) + a**QQ(2,5) + + r = rs_series(cos(p), a, 2) + assert r.as_expr() == a**QQ(28,15)/6 - a**QQ(5,3) + a**QQ(8,5)/24 - a**QQ(7,5) - \ + a**QQ(4,3)/2 - a**QQ(16,15) - a**QQ(4,5)/2 + 1 + + assert rs_series(sin(a)/7, a, 5).as_expr() == (sin(a)/7).series(a, 0, + 5).removeO() + + +def test_rs_series_ConstantInExpr(): + x, a = symbols('x a') + assert rs_series(log(1 + x), x, 5).as_expr() == -x**4/4 + x**3/3 - \ + x**2/2 + x + assert rs_series(log(1 + 4*x), x, 5).as_expr() == -64*x**4 + 64*x**3/3 - \ + 8*x**2 + 4*x + assert rs_series(log(1 + x + x**2), x, 10).as_expr() == -2*x**9/9 + \ + x**8/8 + x**7/7 - x**6/3 + x**5/5 + x**4/4 - 2*x**3/3 + x**2/2 + x + assert rs_series(log(1 + x*a**2), x, 7).as_expr() == -x**6*a**12/6 + \ + x**5*a**10/5 - x**4*a**8/4 + x**3*a**6/3 - x**2*a**4/2 + x*a**2 + + assert rs_series(atan(1 + x), x, 9).as_expr() == -x**7/112 + x**6/48 - x**5/40 \ + + x**3/12 - x**2/4 + x/2 + pi/4 + assert rs_series(atan(1 + x + x**2),x, 9).as_expr() == -15*x**7/112 - x**6/48 + \ + 9*x**5/40 - 5*x**3/12 + x**2/4 + x/2 + pi/4 + assert rs_series(atan(1 + x * a), x, 9).as_expr() == -a**7*x**7/112 + a**6*x**6/48 \ + - a**5*x**5/40 + a**3*x**3/12 - a**2*x**2/4 + a*x/2 + pi/4 + + assert rs_series(tanh(1 + x), x, 5).as_expr() == -5*x**4*tanh(1)**3/3 + x**4* \ + tanh(1)**5 + 2*x**4*tanh(1)/3 - x**3*tanh(1)**4 - x**3/3 + 4*x**3*tanh(1) \ + **2/3 - x**2*tanh(1) + x**2*tanh(1)**3 - x*tanh(1)**2 + x + tanh(1) + assert rs_series(tanh(1 + x * a), x, 3).as_expr() == -a**2*x**2*tanh(1) + a**2*x** \ + 2*tanh(1)**3 - a*x*tanh(1)**2 + a*x + tanh(1) + + assert rs_series(sinh(1 + x), x, 5).as_expr() == x**4*sinh(1)/24 + x**3*cosh(1)/6 + \ + x**2*sinh(1)/2 + x*cosh(1) + sinh(1) + assert rs_series(sinh(1 + x * a), x, 5).as_expr() == a**4*x**4*sinh(1)/24 + \ + a**3*x**3*cosh(1)/6 + a**2*x**2*sinh(1)/2 + a*x*cosh(1) + sinh(1) + + assert rs_series(cosh(1 + x), x, 5).as_expr() == x**4*cosh(1)/24 + x**3*sinh(1)/6 + \ + x**2*cosh(1)/2 + x*sinh(1) + cosh(1) + assert rs_series(cosh(1 + x * a), x, 5).as_expr() == a**4*x**4*cosh(1)/24 + \ + a**3*x**3*sinh(1)/6 + a**2*x**2*cosh(1)/2 + a*x*sinh(1) + cosh(1) + + +def test_issue(): + # https://github.com/sympy/sympy/issues/10191 + # https://github.com/sympy/sympy/issues/19543 + + a, b = symbols('a b') + assert rs_series(sin(a**QQ(3,7))*exp(a + b**QQ(6,7)), a,2).as_expr() == \ + a**QQ(10,7)*exp(b**QQ(6,7)) - a**QQ(9,7)*exp(b**QQ(6,7))/6 + a**QQ(3,7)*exp(b**QQ(6,7)) diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_rings.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_rings.py new file mode 100644 index 0000000000000000000000000000000000000000..455cc319908d0173737531b339e22def8e4a26fc --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_rings.py @@ -0,0 +1,1591 @@ +"""Test sparse polynomials. """ + +from functools import reduce +from operator import add, mul + +from sympy.polys.rings import ring, xring, sring, PolyRing, PolyElement +from sympy.polys.fields import field, FracField +from sympy.polys.densebasic import ninf +from sympy.polys.domains import ZZ, QQ, RR, FF, EX +from sympy.polys.orderings import lex, grlex +from sympy.polys.polyerrors import GeneratorsError, \ + ExactQuotientFailed, MultivariatePolynomialError, CoercionFailed + +from sympy.testing.pytest import raises +from sympy.core import Symbol, symbols +from sympy.core.singleton import S +from sympy.core.numbers import pi +from sympy.functions.elementary.exponential import exp +from sympy.functions.elementary.miscellaneous import sqrt + +def test_PolyRing___init__(): + x, y, z, t = map(Symbol, "xyzt") + + assert len(PolyRing("x,y,z", ZZ, lex).gens) == 3 + assert len(PolyRing(x, ZZ, lex).gens) == 1 + assert len(PolyRing(("x", "y", "z"), ZZ, lex).gens) == 3 + assert len(PolyRing((x, y, z), ZZ, lex).gens) == 3 + assert len(PolyRing("", ZZ, lex).gens) == 0 + assert len(PolyRing([], ZZ, lex).gens) == 0 + + raises(GeneratorsError, lambda: PolyRing(0, ZZ, lex)) + + assert PolyRing("x", ZZ[t], lex).domain == ZZ[t] + assert PolyRing("x", 'ZZ[t]', lex).domain == ZZ[t] + assert PolyRing("x", PolyRing("t", ZZ, lex), lex).domain == ZZ[t] + + raises(GeneratorsError, lambda: PolyRing("x", PolyRing("x", ZZ, lex), lex)) + + _lex = Symbol("lex") + assert PolyRing("x", ZZ, lex).order == lex + assert PolyRing("x", ZZ, _lex).order == lex + assert PolyRing("x", ZZ, 'lex').order == lex + + R1 = PolyRing("x,y", ZZ, lex) + R2 = PolyRing("x,y", ZZ, lex) + R3 = PolyRing("x,y,z", ZZ, lex) + + assert R1.x == R1.gens[0] + assert R1.y == R1.gens[1] + assert R1.x == R2.x + assert R1.y == R2.y + assert R1.x != R3.x + assert R1.y != R3.y + +def test_PolyRing___hash__(): + R, x, y, z = ring("x,y,z", QQ) + assert hash(R) + +def test_PolyRing___eq__(): + assert ring("x,y,z", QQ)[0] == ring("x,y,z", QQ)[0] + assert ring("x,y,z", QQ)[0] != ring("x,y,z", ZZ)[0] + assert ring("x,y,z", ZZ)[0] != ring("x,y,z", QQ)[0] + assert ring("x,y,z", QQ)[0] != ring("x,y", QQ)[0] + assert ring("x,y", QQ)[0] != ring("x,y,z", QQ)[0] + +def test_PolyRing_ring_new(): + R, x, y, z = ring("x,y,z", QQ) + + assert R.ring_new(7) == R(7) + assert R.ring_new(7*x*y*z) == 7*x*y*z + + f = x**2 + 2*x*y + 3*x + 4*z**2 + 5*z + 6 + + assert R.ring_new([[[1]], [[2], [3]], [[4, 5, 6]]]) == f + assert R.ring_new({(2, 0, 0): 1, (1, 1, 0): 2, (1, 0, 0): 3, (0, 0, 2): 4, (0, 0, 1): 5, (0, 0, 0): 6}) == f + assert R.ring_new([((2, 0, 0), 1), ((1, 1, 0), 2), ((1, 0, 0), 3), ((0, 0, 2), 4), ((0, 0, 1), 5), ((0, 0, 0), 6)]) == f + + R, = ring("", QQ) + assert R.ring_new([((), 7)]) == R(7) + +def test_PolyRing_drop(): + R, x,y,z = ring("x,y,z", ZZ) + + assert R.drop(x) == PolyRing("y,z", ZZ, lex) + assert R.drop(y) == PolyRing("x,z", ZZ, lex) + assert R.drop(z) == PolyRing("x,y", ZZ, lex) + + assert R.drop(0) == PolyRing("y,z", ZZ, lex) + assert R.drop(0).drop(0) == PolyRing("z", ZZ, lex) + assert R.drop(0).drop(0).drop(0) == ZZ + + assert R.drop(1) == PolyRing("x,z", ZZ, lex) + + assert R.drop(2) == PolyRing("x,y", ZZ, lex) + assert R.drop(2).drop(1) == PolyRing("x", ZZ, lex) + assert R.drop(2).drop(1).drop(0) == ZZ + + raises(ValueError, lambda: R.drop(3)) + raises(ValueError, lambda: R.drop(x).drop(y)) + +def test_PolyRing___getitem__(): + R, x,y,z = ring("x,y,z", ZZ) + + assert R[0:] == PolyRing("x,y,z", ZZ, lex) + assert R[1:] == PolyRing("y,z", ZZ, lex) + assert R[2:] == PolyRing("z", ZZ, lex) + assert R[3:] == ZZ + +def test_PolyRing_is_(): + R = PolyRing("x", QQ, lex) + + assert R.is_univariate is True + assert R.is_multivariate is False + + R = PolyRing("x,y,z", QQ, lex) + + assert R.is_univariate is False + assert R.is_multivariate is True + + R = PolyRing("", QQ, lex) + + assert R.is_univariate is False + assert R.is_multivariate is False + +def test_PolyRing_add(): + R, x = ring("x", ZZ) + F = [ x**2 + 2*i + 3 for i in range(4) ] + + assert R.add(F) == reduce(add, F) == 4*x**2 + 24 + + R, = ring("", ZZ) + + assert R.add([2, 5, 7]) == 14 + +def test_PolyRing_mul(): + R, x = ring("x", ZZ) + F = [ x**2 + 2*i + 3 for i in range(4) ] + + assert R.mul(F) == reduce(mul, F) == x**8 + 24*x**6 + 206*x**4 + 744*x**2 + 945 + + R, = ring("", ZZ) + + assert R.mul([2, 3, 5]) == 30 + +def test_PolyRing_symmetric_poly(): + R, x, y, z, t = ring("x,y,z,t", ZZ) + + raises(ValueError, lambda: R.symmetric_poly(-1)) + raises(ValueError, lambda: R.symmetric_poly(5)) + + assert R.symmetric_poly(0) == R.one + assert R.symmetric_poly(1) == x + y + z + t + assert R.symmetric_poly(2) == x*y + x*z + x*t + y*z + y*t + z*t + assert R.symmetric_poly(3) == x*y*z + x*y*t + x*z*t + y*z*t + assert R.symmetric_poly(4) == x*y*z*t + +def test_sring(): + x, y, z, t = symbols("x,y,z,t") + + R = PolyRing("x,y,z", ZZ, lex) + assert sring(x + 2*y + 3*z) == (R, R.x + 2*R.y + 3*R.z) + + R = PolyRing("x,y,z", QQ, lex) + assert sring(x + 2*y + z/3) == (R, R.x + 2*R.y + R.z/3) + assert sring([x, 2*y, z/3]) == (R, [R.x, 2*R.y, R.z/3]) + + Rt = PolyRing("t", ZZ, lex) + R = PolyRing("x,y,z", Rt, lex) + assert sring(x + 2*t*y + 3*t**2*z, x, y, z) == (R, R.x + 2*Rt.t*R.y + 3*Rt.t**2*R.z) + + Rt = PolyRing("t", QQ, lex) + R = PolyRing("x,y,z", Rt, lex) + assert sring(x + t*y/2 + t**2*z/3, x, y, z) == (R, R.x + Rt.t*R.y/2 + Rt.t**2*R.z/3) + + Rt = FracField("t", ZZ, lex) + R = PolyRing("x,y,z", Rt, lex) + assert sring(x + 2*y/t + t**2*z/3, x, y, z) == (R, R.x + 2*R.y/Rt.t + Rt.t**2*R.z/3) + + r = sqrt(2) - sqrt(3) + R, a = sring(r, extension=True) + assert R.domain == QQ.algebraic_field(sqrt(2) + sqrt(3)) + assert R.gens == () + assert a == R.domain.from_sympy(r) + +def test_PolyElement___hash__(): + R, x, y, z = ring("x,y,z", QQ) + assert hash(x*y*z) + +def test_PolyElement___eq__(): + R, x, y = ring("x,y", ZZ, lex) + + assert ((x*y + 5*x*y) == 6) == False + assert ((x*y + 5*x*y) == 6*x*y) == True + assert (6 == (x*y + 5*x*y)) == False + assert (6*x*y == (x*y + 5*x*y)) == True + + assert ((x*y - x*y) == 0) == True + assert (0 == (x*y - x*y)) == True + + assert ((x*y - x*y) == 1) == False + assert (1 == (x*y - x*y)) == False + + assert ((x*y - x*y) == 1) == False + assert (1 == (x*y - x*y)) == False + + assert ((x*y + 5*x*y) != 6) == True + assert ((x*y + 5*x*y) != 6*x*y) == False + assert (6 != (x*y + 5*x*y)) == True + assert (6*x*y != (x*y + 5*x*y)) == False + + assert ((x*y - x*y) != 0) == False + assert (0 != (x*y - x*y)) == False + + assert ((x*y - x*y) != 1) == True + assert (1 != (x*y - x*y)) == True + + assert R.one == QQ(1, 1) == R.one + assert R.one == 1 == R.one + + Rt, t = ring("t", ZZ) + R, x, y = ring("x,y", Rt) + + assert (t**3*x/x == t**3) == True + assert (t**3*x/x == t**4) == False + +def test_PolyElement__lt_le_gt_ge__(): + R, x, y = ring("x,y", ZZ) + + assert R(1) < x < x**2 < x**3 + assert R(1) <= x <= x**2 <= x**3 + + assert x**3 > x**2 > x > R(1) + assert x**3 >= x**2 >= x >= R(1) + +def test_PolyElement__str__(): + x, y = symbols('x, y') + + for dom in [ZZ, QQ, ZZ[x], ZZ[x,y], ZZ[x][y]]: + R, t = ring('t', dom) + assert str(2*t**2 + 1) == '2*t**2 + 1' + + for dom in [EX, EX[x]]: + R, t = ring('t', dom) + assert str(2*t**2 + 1) == 'EX(2)*t**2 + EX(1)' + +def test_PolyElement_copy(): + R, x, y, z = ring("x,y,z", ZZ) + + f = x*y + 3*z + g = f.copy() + + assert f == g + g[(1, 1, 1)] = 7 + assert f != g + +def test_PolyElement_as_expr(): + R, x, y, z = ring("x,y,z", ZZ) + f = 3*x**2*y - x*y*z + 7*z**3 + 1 + + X, Y, Z = R.symbols + g = 3*X**2*Y - X*Y*Z + 7*Z**3 + 1 + + assert f != g + assert f.as_expr() == g + + U, V, W = symbols("u,v,w") + g = 3*U**2*V - U*V*W + 7*W**3 + 1 + + assert f != g + assert f.as_expr(U, V, W) == g + + raises(ValueError, lambda: f.as_expr(X)) + + R, = ring("", ZZ) + assert R(3).as_expr() == 3 + +def test_PolyElement_from_expr(): + x, y, z = symbols("x,y,z") + R, X, Y, Z = ring((x, y, z), ZZ) + + f = R.from_expr(1) + assert f == 1 and R.is_element(f) + + f = R.from_expr(x) + assert f == X and R.is_element(f) + + f = R.from_expr(x*y*z) + assert f == X*Y*Z and R.is_element(f) + + f = R.from_expr(x*y*z + x*y + x) + assert f == X*Y*Z + X*Y + X and R.is_element(f) + + f = R.from_expr(x**3*y*z + x**2*y**7 + 1) + assert f == X**3*Y*Z + X**2*Y**7 + 1 and R.is_element(f) + + r, F = sring([exp(2)]) + f = r.from_expr(exp(2)) + assert f == F[0] and r.is_element(f) + + raises(ValueError, lambda: R.from_expr(1/x)) + raises(ValueError, lambda: R.from_expr(2**x)) + raises(ValueError, lambda: R.from_expr(7*x + sqrt(2))) + + R, = ring("", ZZ) + f = R.from_expr(1) + assert f == 1 and R.is_element(f) + +def test_PolyElement_degree(): + R, x,y,z = ring("x,y,z", ZZ) + + assert ninf == float('-inf') + + assert R(0).degree() is ninf + assert R(1).degree() == 0 + assert (x + 1).degree() == 1 + assert (2*y**3 + z).degree() == 0 + assert (x*y**3 + z).degree() == 1 + assert (x**5*y**3 + z).degree() == 5 + + assert R(0).degree(x) is ninf + assert R(1).degree(x) == 0 + assert (x + 1).degree(x) == 1 + assert (2*y**3 + z).degree(x) == 0 + assert (x*y**3 + z).degree(x) == 1 + assert (7*x**5*y**3 + z).degree(x) == 5 + + assert R(0).degree(y) is ninf + assert R(1).degree(y) == 0 + assert (x + 1).degree(y) == 0 + assert (2*y**3 + z).degree(y) == 3 + assert (x*y**3 + z).degree(y) == 3 + assert (7*x**5*y**3 + z).degree(y) == 3 + + assert R(0).degree(z) is ninf + assert R(1).degree(z) == 0 + assert (x + 1).degree(z) == 0 + assert (2*y**3 + z).degree(z) == 1 + assert (x*y**3 + z).degree(z) == 1 + assert (7*x**5*y**3 + z).degree(z) == 1 + + R, = ring("", ZZ) + assert R(0).degree() is ninf + assert R(1).degree() == 0 + +def test_PolyElement_tail_degree(): + R, x,y,z = ring("x,y,z", ZZ) + + assert R(0).tail_degree() is ninf + assert R(1).tail_degree() == 0 + assert (x + 1).tail_degree() == 0 + assert (2*y**3 + x**3*z).tail_degree() == 0 + assert (x*y**3 + x**3*z).tail_degree() == 1 + assert (x**5*y**3 + x**3*z).tail_degree() == 3 + + assert R(0).tail_degree(x) is ninf + assert R(1).tail_degree(x) == 0 + assert (x + 1).tail_degree(x) == 0 + assert (2*y**3 + x**3*z).tail_degree(x) == 0 + assert (x*y**3 + x**3*z).tail_degree(x) == 1 + assert (7*x**5*y**3 + x**3*z).tail_degree(x) == 3 + + assert R(0).tail_degree(y) is ninf + assert R(1).tail_degree(y) == 0 + assert (x + 1).tail_degree(y) == 0 + assert (2*y**3 + x**3*z).tail_degree(y) == 0 + assert (x*y**3 + x**3*z).tail_degree(y) == 0 + assert (7*x**5*y**3 + x**3*z).tail_degree(y) == 0 + + assert R(0).tail_degree(z) is ninf + assert R(1).tail_degree(z) == 0 + assert (x + 1).tail_degree(z) == 0 + assert (2*y**3 + x**3*z).tail_degree(z) == 0 + assert (x*y**3 + x**3*z).tail_degree(z) == 0 + assert (7*x**5*y**3 + x**3*z).tail_degree(z) == 0 + + R, = ring("", ZZ) + assert R(0).tail_degree() is ninf + assert R(1).tail_degree() == 0 + +def test_PolyElement_degrees(): + R, x,y,z = ring("x,y,z", ZZ) + + assert R(0).degrees() == (ninf, ninf, ninf) + assert R(1).degrees() == (0, 0, 0) + assert (x**2*y + x**3*z**2).degrees() == (3, 1, 2) + +def test_PolyElement_tail_degrees(): + R, x,y,z = ring("x,y,z", ZZ) + + assert R(0).tail_degrees() == (ninf, ninf, ninf) + assert R(1).tail_degrees() == (0, 0, 0) + assert (x**2*y + x**3*z**2).tail_degrees() == (2, 0, 0) + +def test_PolyElement_coeff(): + R, x, y, z = ring("x,y,z", ZZ, lex) + f = 3*x**2*y - x*y*z + 7*z**3 + 23 + + assert f.coeff(1) == 23 + raises(ValueError, lambda: f.coeff(3)) + + assert f.coeff(x) == 0 + assert f.coeff(y) == 0 + assert f.coeff(z) == 0 + + assert f.coeff(x**2*y) == 3 + assert f.coeff(x*y*z) == -1 + assert f.coeff(z**3) == 7 + + raises(ValueError, lambda: f.coeff(3*x**2*y)) + raises(ValueError, lambda: f.coeff(-x*y*z)) + raises(ValueError, lambda: f.coeff(7*z**3)) + + R, = ring("", ZZ) + assert R(3).coeff(1) == 3 + +def test_PolyElement_LC(): + R, x, y = ring("x,y", QQ, lex) + assert R(0).LC == QQ(0) + assert (QQ(1,2)*x).LC == QQ(1, 2) + assert (QQ(1,4)*x*y + QQ(1,2)*x).LC == QQ(1, 4) + +def test_PolyElement_LM(): + R, x, y = ring("x,y", QQ, lex) + assert R(0).LM == (0, 0) + assert (QQ(1,2)*x).LM == (1, 0) + assert (QQ(1,4)*x*y + QQ(1,2)*x).LM == (1, 1) + +def test_PolyElement_LT(): + R, x, y = ring("x,y", QQ, lex) + assert R(0).LT == ((0, 0), QQ(0)) + assert (QQ(1,2)*x).LT == ((1, 0), QQ(1, 2)) + assert (QQ(1,4)*x*y + QQ(1,2)*x).LT == ((1, 1), QQ(1, 4)) + + R, = ring("", ZZ) + assert R(0).LT == ((), 0) + assert R(1).LT == ((), 1) + +def test_PolyElement_leading_monom(): + R, x, y = ring("x,y", QQ, lex) + assert R(0).leading_monom() == 0 + assert (QQ(1,2)*x).leading_monom() == x + assert (QQ(1,4)*x*y + QQ(1,2)*x).leading_monom() == x*y + +def test_PolyElement_leading_term(): + R, x, y = ring("x,y", QQ, lex) + assert R(0).leading_term() == 0 + assert (QQ(1,2)*x).leading_term() == QQ(1,2)*x + assert (QQ(1,4)*x*y + QQ(1,2)*x).leading_term() == QQ(1,4)*x*y + +def test_PolyElement_terms(): + R, x,y,z = ring("x,y,z", QQ) + terms = (x**2/3 + y**3/4 + z**4/5).terms() + assert terms == [((2,0,0), QQ(1,3)), ((0,3,0), QQ(1,4)), ((0,0,4), QQ(1,5))] + + R, x,y = ring("x,y", ZZ, lex) + f = x*y**7 + 2*x**2*y**3 + + assert f.terms() == f.terms(lex) == f.terms('lex') == [((2, 3), 2), ((1, 7), 1)] + assert f.terms(grlex) == f.terms('grlex') == [((1, 7), 1), ((2, 3), 2)] + + R, x,y = ring("x,y", ZZ, grlex) + f = x*y**7 + 2*x**2*y**3 + + assert f.terms() == f.terms(grlex) == f.terms('grlex') == [((1, 7), 1), ((2, 3), 2)] + assert f.terms(lex) == f.terms('lex') == [((2, 3), 2), ((1, 7), 1)] + + R, = ring("", ZZ) + assert R(3).terms() == [((), 3)] + +def test_PolyElement_monoms(): + R, x,y,z = ring("x,y,z", QQ) + monoms = (x**2/3 + y**3/4 + z**4/5).monoms() + assert monoms == [(2,0,0), (0,3,0), (0,0,4)] + + R, x,y = ring("x,y", ZZ, lex) + f = x*y**7 + 2*x**2*y**3 + + assert f.monoms() == f.monoms(lex) == f.monoms('lex') == [(2, 3), (1, 7)] + assert f.monoms(grlex) == f.monoms('grlex') == [(1, 7), (2, 3)] + + R, x,y = ring("x,y", ZZ, grlex) + f = x*y**7 + 2*x**2*y**3 + + assert f.monoms() == f.monoms(grlex) == f.monoms('grlex') == [(1, 7), (2, 3)] + assert f.monoms(lex) == f.monoms('lex') == [(2, 3), (1, 7)] + +def test_PolyElement_coeffs(): + R, x,y,z = ring("x,y,z", QQ) + coeffs = (x**2/3 + y**3/4 + z**4/5).coeffs() + assert coeffs == [QQ(1,3), QQ(1,4), QQ(1,5)] + + R, x,y = ring("x,y", ZZ, lex) + f = x*y**7 + 2*x**2*y**3 + + assert f.coeffs() == f.coeffs(lex) == f.coeffs('lex') == [2, 1] + assert f.coeffs(grlex) == f.coeffs('grlex') == [1, 2] + + R, x,y = ring("x,y", ZZ, grlex) + f = x*y**7 + 2*x**2*y**3 + + assert f.coeffs() == f.coeffs(grlex) == f.coeffs('grlex') == [1, 2] + assert f.coeffs(lex) == f.coeffs('lex') == [2, 1] + +def test_PolyElement___add__(): + Rt, t = ring("t", ZZ) + Ruv, u,v = ring("u,v", ZZ) + Rxyz, x,y,z = ring("x,y,z", Ruv) + + assert dict(x + 3*y) == {(1, 0, 0): 1, (0, 1, 0): 3} + + assert dict(u + x) == dict(x + u) == {(1, 0, 0): 1, (0, 0, 0): u} + assert dict(u + x*y) == dict(x*y + u) == {(1, 1, 0): 1, (0, 0, 0): u} + assert dict(u + x*y + z) == dict(x*y + z + u) == {(1, 1, 0): 1, (0, 0, 1): 1, (0, 0, 0): u} + + assert dict(u*x + x) == dict(x + u*x) == {(1, 0, 0): u + 1} + assert dict(u*x + x*y) == dict(x*y + u*x) == {(1, 1, 0): 1, (1, 0, 0): u} + assert dict(u*x + x*y + z) == dict(x*y + z + u*x) == {(1, 1, 0): 1, (0, 0, 1): 1, (1, 0, 0): u} + + raises(TypeError, lambda: t + x) + raises(TypeError, lambda: x + t) + raises(TypeError, lambda: t + u) + raises(TypeError, lambda: u + t) + + Fuv, u,v = field("u,v", ZZ) + Rxyz, x,y,z = ring("x,y,z", Fuv) + + assert dict(u + x) == dict(x + u) == {(1, 0, 0): 1, (0, 0, 0): u} + + Rxyz, x,y,z = ring("x,y,z", EX) + + assert dict(EX(pi) + x*y*z) == dict(x*y*z + EX(pi)) == {(1, 1, 1): EX(1), (0, 0, 0): EX(pi)} + +def test_PolyElement___sub__(): + Rt, t = ring("t", ZZ) + Ruv, u,v = ring("u,v", ZZ) + Rxyz, x,y,z = ring("x,y,z", Ruv) + + assert dict(x - 3*y) == {(1, 0, 0): 1, (0, 1, 0): -3} + + assert dict(-u + x) == dict(x - u) == {(1, 0, 0): 1, (0, 0, 0): -u} + assert dict(-u + x*y) == dict(x*y - u) == {(1, 1, 0): 1, (0, 0, 0): -u} + assert dict(-u + x*y + z) == dict(x*y + z - u) == {(1, 1, 0): 1, (0, 0, 1): 1, (0, 0, 0): -u} + + assert dict(-u*x + x) == dict(x - u*x) == {(1, 0, 0): -u + 1} + assert dict(-u*x + x*y) == dict(x*y - u*x) == {(1, 1, 0): 1, (1, 0, 0): -u} + assert dict(-u*x + x*y + z) == dict(x*y + z - u*x) == {(1, 1, 0): 1, (0, 0, 1): 1, (1, 0, 0): -u} + + raises(TypeError, lambda: t - x) + raises(TypeError, lambda: x - t) + raises(TypeError, lambda: t - u) + raises(TypeError, lambda: u - t) + + Fuv, u,v = field("u,v", ZZ) + Rxyz, x,y,z = ring("x,y,z", Fuv) + + assert dict(-u + x) == dict(x - u) == {(1, 0, 0): 1, (0, 0, 0): -u} + + Rxyz, x,y,z = ring("x,y,z", EX) + + assert dict(-EX(pi) + x*y*z) == dict(x*y*z - EX(pi)) == {(1, 1, 1): EX(1), (0, 0, 0): -EX(pi)} + +def test_PolyElement___mul__(): + Rt, t = ring("t", ZZ) + Ruv, u,v = ring("u,v", ZZ) + Rxyz, x,y,z = ring("x,y,z", Ruv) + + assert dict(u*x) == dict(x*u) == {(1, 0, 0): u} + + assert dict(2*u*x + z) == dict(x*2*u + z) == {(1, 0, 0): 2*u, (0, 0, 1): 1} + assert dict(u*2*x + z) == dict(2*x*u + z) == {(1, 0, 0): 2*u, (0, 0, 1): 1} + assert dict(2*u*x + z) == dict(x*2*u + z) == {(1, 0, 0): 2*u, (0, 0, 1): 1} + assert dict(u*x*2 + z) == dict(x*u*2 + z) == {(1, 0, 0): 2*u, (0, 0, 1): 1} + + assert dict(2*u*x*y + z) == dict(x*y*2*u + z) == {(1, 1, 0): 2*u, (0, 0, 1): 1} + assert dict(u*2*x*y + z) == dict(2*x*y*u + z) == {(1, 1, 0): 2*u, (0, 0, 1): 1} + assert dict(2*u*x*y + z) == dict(x*y*2*u + z) == {(1, 1, 0): 2*u, (0, 0, 1): 1} + assert dict(u*x*y*2 + z) == dict(x*y*u*2 + z) == {(1, 1, 0): 2*u, (0, 0, 1): 1} + + assert dict(2*u*y*x + z) == dict(y*x*2*u + z) == {(1, 1, 0): 2*u, (0, 0, 1): 1} + assert dict(u*2*y*x + z) == dict(2*y*x*u + z) == {(1, 1, 0): 2*u, (0, 0, 1): 1} + assert dict(2*u*y*x + z) == dict(y*x*2*u + z) == {(1, 1, 0): 2*u, (0, 0, 1): 1} + assert dict(u*y*x*2 + z) == dict(y*x*u*2 + z) == {(1, 1, 0): 2*u, (0, 0, 1): 1} + + assert dict(3*u*(x + y) + z) == dict((x + y)*3*u + z) == {(1, 0, 0): 3*u, (0, 1, 0): 3*u, (0, 0, 1): 1} + + raises(TypeError, lambda: t*x + z) + raises(TypeError, lambda: x*t + z) + raises(TypeError, lambda: t*u + z) + raises(TypeError, lambda: u*t + z) + + Fuv, u,v = field("u,v", ZZ) + Rxyz, x,y,z = ring("x,y,z", Fuv) + + assert dict(u*x) == dict(x*u) == {(1, 0, 0): u} + + Rxyz, x,y,z = ring("x,y,z", EX) + + assert dict(EX(pi)*x*y*z) == dict(x*y*z*EX(pi)) == {(1, 1, 1): EX(pi)} + +def test_PolyElement___truediv__(): + R, x,y,z = ring("x,y,z", ZZ) + + assert (2*x**2 - 4)/2 == x**2 - 2 + assert (2*x**2 - 3)/2 == x**2 + + assert (x**2 - 1).quo(x) == x + assert (x**2 - x).quo(x) == x - 1 + + raises(ExactQuotientFailed, lambda: (x**2 - 1)/x) + assert (x**2 - x)/x == x - 1 + raises(ExactQuotientFailed, lambda: (x**2 - 1)/(2*x)) + + assert (x**2 - 1).quo(2*x) == 0 + assert (x**2 - x)/(x - 1) == (x**2 - x).quo(x - 1) == x + + + R, x,y,z = ring("x,y,z", ZZ) + assert len((x**2/3 + y**3/4 + z**4/5).terms()) == 0 + + R, x,y,z = ring("x,y,z", QQ) + assert len((x**2/3 + y**3/4 + z**4/5).terms()) == 3 + + Rt, t = ring("t", ZZ) + Ruv, u,v = ring("u,v", ZZ) + Rxyz, x,y,z = ring("x,y,z", Ruv) + + assert dict((u**2*x + u)/u) == {(1, 0, 0): u, (0, 0, 0): 1} + raises(ExactQuotientFailed, lambda: u/(u**2*x + u)) + + raises(TypeError, lambda: t/x) + raises(TypeError, lambda: x/t) + raises(TypeError, lambda: t/u) + raises(TypeError, lambda: u/t) + + R, x = ring("x", ZZ) + f, g = x**2 + 2*x + 3, R(0) + + raises(ZeroDivisionError, lambda: f.div(g)) + raises(ZeroDivisionError, lambda: divmod(f, g)) + raises(ZeroDivisionError, lambda: f.rem(g)) + raises(ZeroDivisionError, lambda: f % g) + raises(ZeroDivisionError, lambda: f.quo(g)) + raises(ZeroDivisionError, lambda: f / g) + raises(ZeroDivisionError, lambda: f.exquo(g)) + + R, x, y = ring("x,y", ZZ) + f, g = x*y + 2*x + 3, R(0) + + raises(ZeroDivisionError, lambda: f.div(g)) + raises(ZeroDivisionError, lambda: divmod(f, g)) + raises(ZeroDivisionError, lambda: f.rem(g)) + raises(ZeroDivisionError, lambda: f % g) + raises(ZeroDivisionError, lambda: f.quo(g)) + raises(ZeroDivisionError, lambda: f / g) + raises(ZeroDivisionError, lambda: f.exquo(g)) + + R, x = ring("x", ZZ) + + f, g = x**2 + 1, 2*x - 4 + q, r = R(0), x**2 + 1 + + assert f.div(g) == divmod(f, g) == (q, r) + assert f.rem(g) == f % g == r + assert f.quo(g) == q + raises(ExactQuotientFailed, lambda: f / g) + raises(ExactQuotientFailed, lambda: f.exquo(g)) + + f, g = 3*x**3 + x**2 + x + 5, 5*x**2 - 3*x + 1 + q, r = R(0), f + + assert f.div(g) == divmod(f, g) == (q, r) + assert f.rem(g) == f % g == r + assert f.quo(g) == q + raises(ExactQuotientFailed, lambda: f / g) + raises(ExactQuotientFailed, lambda: f.exquo(g)) + + f, g = 5*x**4 + 4*x**3 + 3*x**2 + 2*x + 1, x**2 + 2*x + 3 + q, r = 5*x**2 - 6*x, 20*x + 1 + + assert f.div(g) == divmod(f, g) == (q, r) + assert f.rem(g) == f % g == r + assert f.quo(g) == q + raises(ExactQuotientFailed, lambda: f / g) + raises(ExactQuotientFailed, lambda: f.exquo(g)) + + f, g = 5*x**5 + 4*x**4 + 3*x**3 + 2*x**2 + x, x**4 + 2*x**3 + 9 + q, r = 5*x - 6, 15*x**3 + 2*x**2 - 44*x + 54 + + assert f.div(g) == divmod(f, g) == (q, r) + assert f.rem(g) == f % g == r + assert f.quo(g) == q + raises(ExactQuotientFailed, lambda: f / g) + raises(ExactQuotientFailed, lambda: f.exquo(g)) + + R, x = ring("x", QQ) + + f, g = x**2 + 1, 2*x - 4 + q, r = x/2 + 1, R(5) + + assert f.div(g) == divmod(f, g) == (q, r) + assert f.rem(g) == f % g == r + assert f.quo(g) == q + raises(ExactQuotientFailed, lambda: f / g) + raises(ExactQuotientFailed, lambda: f.exquo(g)) + + f, g = 3*x**3 + x**2 + x + 5, 5*x**2 - 3*x + 1 + q, r = QQ(3, 5)*x + QQ(14, 25), QQ(52, 25)*x + QQ(111, 25) + + assert f.div(g) == divmod(f, g) == (q, r) + assert f.rem(g) == f % g == r + assert f.quo(g) == q + raises(ExactQuotientFailed, lambda: f / g) + raises(ExactQuotientFailed, lambda: f.exquo(g)) + + R, x,y = ring("x,y", ZZ) + + f, g = x**2 - y**2, x - y + q, r = x + y, R(0) + + assert f.div(g) == divmod(f, g) == (q, r) + assert f.rem(g) == f % g == r + assert f.quo(g) == q + assert f.exquo(g) == f / g == q + + f, g = x**2 + y**2, x - y + q, r = x + y, 2*y**2 + + assert f.div(g) == divmod(f, g) == (q, r) + assert f.rem(g) == f % g == r + assert f.quo(g) == q + raises(ExactQuotientFailed, lambda: f / g) + raises(ExactQuotientFailed, lambda: f.exquo(g)) + + f, g = x**2 + y**2, -x + y + q, r = -x - y, 2*y**2 + + assert f.div(g) == divmod(f, g) == (q, r) + assert f.rem(g) == f % g == r + assert f.quo(g) == q + raises(ExactQuotientFailed, lambda: f / g) + raises(ExactQuotientFailed, lambda: f.exquo(g)) + + f, g = x**2 + y**2, 2*x - 2*y + q, r = R(0), f + + assert f.div(g) == divmod(f, g) == (q, r) + assert f.rem(g) == f % g == r + assert f.quo(g) == q + raises(ExactQuotientFailed, lambda: f / g) + raises(ExactQuotientFailed, lambda: f.exquo(g)) + + R, x,y = ring("x,y", QQ) + + f, g = x**2 - y**2, x - y + q, r = x + y, R(0) + + assert f.div(g) == divmod(f, g) == (q, r) + assert f.rem(g) == f % g == r + assert f.quo(g) == q + assert f.exquo(g) == f / g == q + + f, g = x**2 + y**2, x - y + q, r = x + y, 2*y**2 + + assert f.div(g) == divmod(f, g) == (q, r) + assert f.rem(g) == f % g == r + assert f.quo(g) == q + raises(ExactQuotientFailed, lambda: f / g) + raises(ExactQuotientFailed, lambda: f.exquo(g)) + + f, g = x**2 + y**2, -x + y + q, r = -x - y, 2*y**2 + + assert f.div(g) == divmod(f, g) == (q, r) + assert f.rem(g) == f % g == r + assert f.quo(g) == q + raises(ExactQuotientFailed, lambda: f / g) + raises(ExactQuotientFailed, lambda: f.exquo(g)) + + f, g = x**2 + y**2, 2*x - 2*y + q, r = x/2 + y/2, 2*y**2 + + assert f.div(g) == divmod(f, g) == (q, r) + assert f.rem(g) == f % g == r + assert f.quo(g) == q + raises(ExactQuotientFailed, lambda: f / g) + raises(ExactQuotientFailed, lambda: f.exquo(g)) + +def test_PolyElement___pow__(): + R, x = ring("x", ZZ, grlex) + f = 2*x + 3 + + assert f**0 == 1 + assert f**1 == f + raises(ValueError, lambda: f**(-1)) + + assert f**2 == f._pow_generic(2) == f._pow_multinomial(2) == 4*x**2 + 12*x + 9 + assert f**3 == f._pow_generic(3) == f._pow_multinomial(3) == 8*x**3 + 36*x**2 + 54*x + 27 + assert f**4 == f._pow_generic(4) == f._pow_multinomial(4) == 16*x**4 + 96*x**3 + 216*x**2 + 216*x + 81 + assert f**5 == f._pow_generic(5) == f._pow_multinomial(5) == 32*x**5 + 240*x**4 + 720*x**3 + 1080*x**2 + 810*x + 243 + + R, x,y,z = ring("x,y,z", ZZ, grlex) + f = x**3*y - 2*x*y**2 - 3*z + 1 + g = x**6*y**2 - 4*x**4*y**3 - 6*x**3*y*z + 2*x**3*y + 4*x**2*y**4 + 12*x*y**2*z - 4*x*y**2 + 9*z**2 - 6*z + 1 + + assert f**2 == f._pow_generic(2) == f._pow_multinomial(2) == g + + R, t = ring("t", ZZ) + f = -11200*t**4 - 2604*t**2 + 49 + g = 15735193600000000*t**16 + 14633730048000000*t**14 + 4828147466240000*t**12 \ + + 598976863027200*t**10 + 3130812416256*t**8 - 2620523775744*t**6 \ + + 92413760096*t**4 - 1225431984*t**2 + 5764801 + + assert f**4 == f._pow_generic(4) == f._pow_multinomial(4) == g + +def test_PolyElement_div(): + R, x = ring("x", ZZ, grlex) + + f = x**3 - 12*x**2 - 42 + g = x - 3 + + q = x**2 - 9*x - 27 + r = -123 + + assert f.div([g]) == ([q], r) + + R, x = ring("x", ZZ, grlex) + f = x**2 + 2*x + 2 + assert f.div([R(1)]) == ([f], 0) + + R, x = ring("x", QQ, grlex) + f = x**2 + 2*x + 2 + assert f.div([R(2)]) == ([QQ(1,2)*x**2 + x + 1], 0) + + R, x,y = ring("x,y", ZZ, grlex) + f = 4*x**2*y - 2*x*y + 4*x - 2*y + 8 + + assert f.div([R(2)]) == ([2*x**2*y - x*y + 2*x - y + 4], 0) + assert f.div([2*y]) == ([2*x**2 - x - 1], 4*x + 8) + + f = x - 1 + g = y - 1 + + assert f.div([g]) == ([0], f) + + f = x*y**2 + 1 + G = [x*y + 1, y + 1] + + Q = [y, -1] + r = 2 + + assert f.div(G) == (Q, r) + + f = x**2*y + x*y**2 + y**2 + G = [x*y - 1, y**2 - 1] + + Q = [x + y, 1] + r = x + y + 1 + + assert f.div(G) == (Q, r) + + G = [y**2 - 1, x*y - 1] + + Q = [x + 1, x] + r = 2*x + 1 + + assert f.div(G) == (Q, r) + + R, = ring("", ZZ) + assert R(3).div(R(2)) == (0, 3) + + R, = ring("", QQ) + assert R(3).div(R(2)) == (QQ(3, 2), 0) + +def test_PolyElement_rem(): + R, x = ring("x", ZZ, grlex) + + f = x**3 - 12*x**2 - 42 + g = x - 3 + r = -123 + + assert f.rem([g]) == f.div([g])[1] == r + + R, x,y = ring("x,y", ZZ, grlex) + + f = 4*x**2*y - 2*x*y + 4*x - 2*y + 8 + + assert f.rem([R(2)]) == f.div([R(2)])[1] == 0 + assert f.rem([2*y]) == f.div([2*y])[1] == 4*x + 8 + + f = x - 1 + g = y - 1 + + assert f.rem([g]) == f.div([g])[1] == f + + f = x*y**2 + 1 + G = [x*y + 1, y + 1] + r = 2 + + assert f.rem(G) == f.div(G)[1] == r + + f = x**2*y + x*y**2 + y**2 + G = [x*y - 1, y**2 - 1] + r = x + y + 1 + + assert f.rem(G) == f.div(G)[1] == r + + G = [y**2 - 1, x*y - 1] + r = 2*x + 1 + + assert f.rem(G) == f.div(G)[1] == r + +def test_PolyElement_deflate(): + R, x = ring("x", ZZ) + + assert (2*x**2).deflate(x**4 + 4*x**2 + 1) == ((2,), [2*x, x**2 + 4*x + 1]) + + R, x,y = ring("x,y", ZZ) + + assert R(0).deflate(R(0)) == ((1, 1), [0, 0]) + assert R(1).deflate(R(0)) == ((1, 1), [1, 0]) + assert R(1).deflate(R(2)) == ((1, 1), [1, 2]) + assert R(1).deflate(2*y) == ((1, 1), [1, 2*y]) + assert (2*y).deflate(2*y) == ((1, 1), [2*y, 2*y]) + assert R(2).deflate(2*y**2) == ((1, 2), [2, 2*y]) + assert (2*y**2).deflate(2*y**2) == ((1, 2), [2*y, 2*y]) + + f = x**4*y**2 + x**2*y + 1 + g = x**2*y**3 + x**2*y + 1 + + assert f.deflate(g) == ((2, 1), [x**2*y**2 + x*y + 1, x*y**3 + x*y + 1]) + +def test_PolyElement_clear_denoms(): + R, x,y = ring("x,y", QQ) + + assert R(1).clear_denoms() == (ZZ(1), 1) + assert R(7).clear_denoms() == (ZZ(1), 7) + + assert R(QQ(7,3)).clear_denoms() == (3, 7) + assert R(QQ(7,3)).clear_denoms() == (3, 7) + + assert (3*x**2 + x).clear_denoms() == (1, 3*x**2 + x) + assert (x**2 + QQ(1,2)*x).clear_denoms() == (2, 2*x**2 + x) + + rQQ, x,t = ring("x,t", QQ, lex) + rZZ, X,T = ring("x,t", ZZ, lex) + + F = [x - QQ(17824537287975195925064602467992950991718052713078834557692023531499318507213727406844943097,413954288007559433755329699713866804710749652268151059918115348815925474842910720000)*t**7 + - QQ(4882321164854282623427463828745855894130208215961904469205260756604820743234704900167747753,12936071500236232304854053116058337647210926633379720622441104650497671088840960000)*t**6 + - QQ(36398103304520066098365558157422127347455927422509913596393052633155821154626830576085097433,25872143000472464609708106232116675294421853266759441244882209300995342177681920000)*t**5 + - QQ(168108082231614049052707339295479262031324376786405372698857619250210703675982492356828810819,58212321751063045371843239022262519412449169850208742800984970927239519899784320000)*t**4 + - QQ(5694176899498574510667890423110567593477487855183144378347226247962949388653159751849449037,1617008937529529038106756639507292205901365829172465077805138081312208886105120000)*t**3 + - QQ(154482622347268833757819824809033388503591365487934245386958884099214649755244381307907779,60637835157357338929003373981523457721301218593967440417692678049207833228942000)*t**2 + - QQ(2452813096069528207645703151222478123259511586701148682951852876484544822947007791153163,2425513406294293557160134959260938308852048743758697616707707121968313329157680)*t + - QQ(34305265428126440542854669008203683099323146152358231964773310260498715579162112959703,202126117191191129763344579938411525737670728646558134725642260164026110763140), + t**8 + QQ(693749860237914515552,67859264524169150569)*t**7 + + QQ(27761407182086143225024,610733380717522355121)*t**6 + + QQ(7785127652157884044288,67859264524169150569)*t**5 + + QQ(36567075214771261409792,203577793572507451707)*t**4 + + QQ(36336335165196147384320,203577793572507451707)*t**3 + + QQ(7452455676042754048000,67859264524169150569)*t**2 + + QQ(2593331082514399232000,67859264524169150569)*t + + QQ(390399197427343360000,67859264524169150569)] + + G = [3725588592068034903797967297424801242396746870413359539263038139343329273586196480000*X - + 160420835591776763325581422211936558925462474417709511019228211783493866564923546661604487873*T**7 - + 1406108495478033395547109582678806497509499966197028487131115097902188374051595011248311352864*T**6 - + 5241326875850889518164640374668786338033653548841427557880599579174438246266263602956254030352*T**5 - + 10758917262823299139373269714910672770004760114329943852726887632013485035262879510837043892416*T**4 - + 13119383576444715672578819534846747735372132018341964647712009275306635391456880068261130581248*T**3 - + 9491412317016197146080450036267011389660653495578680036574753839055748080962214787557853941760*T**2 - + 3767520915562795326943800040277726397326609797172964377014046018280260848046603967211258368000*T - + 632314652371226552085897259159210286886724229880266931574701654721512325555116066073245696000, + 610733380717522355121*T**8 + + 6243748742141230639968*T**7 + + 27761407182086143225024*T**6 + + 70066148869420956398592*T**5 + + 109701225644313784229376*T**4 + + 109009005495588442152960*T**3 + + 67072101084384786432000*T**2 + + 23339979742629593088000*T + + 3513592776846090240000] + + assert [ f.clear_denoms()[1].set_ring(rZZ) for f in F ] == G + +def test_PolyElement_cofactors(): + R, x, y = ring("x,y", ZZ) + + f, g = R(0), R(0) + assert f.cofactors(g) == (0, 0, 0) + + f, g = R(2), R(0) + assert f.cofactors(g) == (2, 1, 0) + + f, g = R(-2), R(0) + assert f.cofactors(g) == (2, -1, 0) + + f, g = R(0), R(-2) + assert f.cofactors(g) == (2, 0, -1) + + f, g = R(0), 2*x + 4 + assert f.cofactors(g) == (2*x + 4, 0, 1) + + f, g = 2*x + 4, R(0) + assert f.cofactors(g) == (2*x + 4, 1, 0) + + f, g = R(2), R(2) + assert f.cofactors(g) == (2, 1, 1) + + f, g = R(-2), R(2) + assert f.cofactors(g) == (2, -1, 1) + + f, g = R(2), R(-2) + assert f.cofactors(g) == (2, 1, -1) + + f, g = R(-2), R(-2) + assert f.cofactors(g) == (2, -1, -1) + + f, g = x**2 + 2*x + 1, R(1) + assert f.cofactors(g) == (1, x**2 + 2*x + 1, 1) + + f, g = x**2 + 2*x + 1, R(2) + assert f.cofactors(g) == (1, x**2 + 2*x + 1, 2) + + f, g = 2*x**2 + 4*x + 2, R(2) + assert f.cofactors(g) == (2, x**2 + 2*x + 1, 1) + + f, g = R(2), 2*x**2 + 4*x + 2 + assert f.cofactors(g) == (2, 1, x**2 + 2*x + 1) + + f, g = 2*x**2 + 4*x + 2, x + 1 + assert f.cofactors(g) == (x + 1, 2*x + 2, 1) + + f, g = x + 1, 2*x**2 + 4*x + 2 + assert f.cofactors(g) == (x + 1, 1, 2*x + 2) + + R, x, y, z, t = ring("x,y,z,t", ZZ) + + f, g = t**2 + 2*t + 1, 2*t + 2 + assert f.cofactors(g) == (t + 1, t + 1, 2) + + f, g = z**2*t**2 + 2*z**2*t + z**2 + z*t + z, t**2 + 2*t + 1 + h, cff, cfg = t + 1, z**2*t + z**2 + z, t + 1 + + assert f.cofactors(g) == (h, cff, cfg) + assert g.cofactors(f) == (h, cfg, cff) + + R, x, y = ring("x,y", QQ) + + f = QQ(1,2)*x**2 + x + QQ(1,2) + g = QQ(1,2)*x + QQ(1,2) + + h = x + 1 + + assert f.cofactors(g) == (h, g, QQ(1,2)) + assert g.cofactors(f) == (h, QQ(1,2), g) + + R, x, y = ring("x,y", RR) + + f = 2.1*x*y**2 - 2.1*x*y + 2.1*x + g = 2.1*x**3 + h = 1.0*x + + assert f.cofactors(g) == (h, f/h, g/h) + assert g.cofactors(f) == (h, g/h, f/h) + +def test_PolyElement_gcd(): + R, x, y = ring("x,y", QQ) + + f = QQ(1,2)*x**2 + x + QQ(1,2) + g = QQ(1,2)*x + QQ(1,2) + + assert f.gcd(g) == x + 1 + +def test_PolyElement_cancel(): + R, x, y = ring("x,y", ZZ) + + f = 2*x**3 + 4*x**2 + 2*x + g = 3*x**2 + 3*x + F = 2*x + 2 + G = 3 + + assert f.cancel(g) == (F, G) + + assert (-f).cancel(g) == (-F, G) + assert f.cancel(-g) == (-F, G) + + R, x, y = ring("x,y", QQ) + + f = QQ(1,2)*x**3 + x**2 + QQ(1,2)*x + g = QQ(1,3)*x**2 + QQ(1,3)*x + F = 3*x + 3 + G = 2 + + assert f.cancel(g) == (F, G) + + assert (-f).cancel(g) == (-F, G) + assert f.cancel(-g) == (-F, G) + + Fx, x = field("x", ZZ) + Rt, t = ring("t", Fx) + + f = (-x**2 - 4)/4*t + g = t**2 + (x**2 + 2)/2 + + assert f.cancel(g) == ((-x**2 - 4)*t, 4*t**2 + 2*x**2 + 4) + +def test_PolyElement_max_norm(): + R, x, y = ring("x,y", ZZ) + + assert R(0).max_norm() == 0 + assert R(1).max_norm() == 1 + + assert (x**3 + 4*x**2 + 2*x + 3).max_norm() == 4 + +def test_PolyElement_l1_norm(): + R, x, y = ring("x,y", ZZ) + + assert R(0).l1_norm() == 0 + assert R(1).l1_norm() == 1 + + assert (x**3 + 4*x**2 + 2*x + 3).l1_norm() == 10 + +def test_PolyElement_diff(): + R, X = xring("x:11", QQ) + + f = QQ(288,5)*X[0]**8*X[1]**6*X[4]**3*X[10]**2 + 8*X[0]**2*X[2]**3*X[4]**3 +2*X[0]**2 - 2*X[1]**2 + + assert f.diff(X[0]) == QQ(2304,5)*X[0]**7*X[1]**6*X[4]**3*X[10]**2 + 16*X[0]*X[2]**3*X[4]**3 + 4*X[0] + assert f.diff(X[4]) == QQ(864,5)*X[0]**8*X[1]**6*X[4]**2*X[10]**2 + 24*X[0]**2*X[2]**3*X[4]**2 + assert f.diff(X[10]) == QQ(576,5)*X[0]**8*X[1]**6*X[4]**3*X[10] + +def test_PolyElement___call__(): + R, x = ring("x", ZZ) + f = 3*x + 1 + + assert f(0) == 1 + assert f(1) == 4 + + raises(ValueError, lambda: f()) + raises(ValueError, lambda: f(0, 1)) + + raises(CoercionFailed, lambda: f(QQ(1,7))) + + R, x,y = ring("x,y", ZZ) + f = 3*x + y**2 + 1 + + assert f(0, 0) == 1 + assert f(1, 7) == 53 + + Ry = R.drop(x) + + assert f(0) == Ry.y**2 + 1 + assert f(1) == Ry.y**2 + 4 + + raises(ValueError, lambda: f()) + raises(ValueError, lambda: f(0, 1, 2)) + + raises(CoercionFailed, lambda: f(1, QQ(1,7))) + raises(CoercionFailed, lambda: f(QQ(1,7), 1)) + raises(CoercionFailed, lambda: f(QQ(1,7), QQ(1,7))) + +def test_PolyElement_evaluate(): + R, x = ring("x", ZZ) + f = x**3 + 4*x**2 + 2*x + 3 + + r = f.evaluate(x, 0) + assert r == 3 and not isinstance(r, PolyElement) + + raises(CoercionFailed, lambda: f.evaluate(x, QQ(1,7))) + + R, x, y, z = ring("x,y,z", ZZ) + f = (x*y)**3 + 4*(x*y)**2 + 2*x*y + 3 + + r = f.evaluate(x, 0) + assert r == 3 and R.drop(x).is_element(r) + r = f.evaluate([(x, 0), (y, 0)]) + assert r == 3 and R.drop(x, y).is_element(r) + r = f.evaluate(y, 0) + assert r == 3 and R.drop(y).is_element(r) + r = f.evaluate([(y, 0), (x, 0)]) + assert r == 3 and R.drop(y, x).is_element(r) + + r = f.evaluate([(x, 0), (y, 0), (z, 0)]) + assert r == 3 and not isinstance(r, PolyElement) + + raises(CoercionFailed, lambda: f.evaluate([(x, 1), (y, QQ(1,7))])) + raises(CoercionFailed, lambda: f.evaluate([(x, QQ(1,7)), (y, 1)])) + raises(CoercionFailed, lambda: f.evaluate([(x, QQ(1,7)), (y, QQ(1,7))])) + +def test_PolyElement_subs(): + R, x = ring("x", ZZ) + f = x**3 + 4*x**2 + 2*x + 3 + + r = f.subs(x, 0) + assert r == 3 and R.is_element(r) + + raises(CoercionFailed, lambda: f.subs(x, QQ(1,7))) + + R, x, y, z = ring("x,y,z", ZZ) + f = x**3 + 4*x**2 + 2*x + 3 + + r = f.subs(x, 0) + assert r == 3 and R.is_element(r) + r = f.subs([(x, 0), (y, 0)]) + assert r == 3 and R.is_element(r) + + raises(CoercionFailed, lambda: f.subs([(x, 1), (y, QQ(1,7))])) + raises(CoercionFailed, lambda: f.subs([(x, QQ(1,7)), (y, 1)])) + raises(CoercionFailed, lambda: f.subs([(x, QQ(1,7)), (y, QQ(1,7))])) + +def test_PolyElement_symmetrize(): + R, x, y = ring("x,y", ZZ) + + # Homogeneous, symmetric + f = x**2 + y**2 + sym, rem, m = f.symmetrize() + assert rem == 0 + assert sym.compose(m) + rem == f + + # Homogeneous, asymmetric + f = x**2 - y**2 + sym, rem, m = f.symmetrize() + assert rem != 0 + assert sym.compose(m) + rem == f + + # Inhomogeneous, symmetric + f = x*y + 7 + sym, rem, m = f.symmetrize() + assert rem == 0 + assert sym.compose(m) + rem == f + + # Inhomogeneous, asymmetric + f = y + 7 + sym, rem, m = f.symmetrize() + assert rem != 0 + assert sym.compose(m) + rem == f + + # Constant + f = R.from_expr(3) + sym, rem, m = f.symmetrize() + assert rem == 0 + assert sym.compose(m) + rem == f + + # Constant constructed from sring + R, f = sring(3) + sym, rem, m = f.symmetrize() + assert rem == 0 + assert sym.compose(m) + rem == f + +def test_PolyElement_compose(): + R, x = ring("x", ZZ) + f = x**3 + 4*x**2 + 2*x + 3 + + r = f.compose(x, 0) + assert r == 3 and R.is_element(r) + + assert f.compose(x, x) == f + assert f.compose(x, x**2) == x**6 + 4*x**4 + 2*x**2 + 3 + + raises(CoercionFailed, lambda: f.compose(x, QQ(1,7))) + + R, x, y, z = ring("x,y,z", ZZ) + f = x**3 + 4*x**2 + 2*x + 3 + + r = f.compose(x, 0) + assert r == 3 and R.is_element(r) + r = f.compose([(x, 0), (y, 0)]) + assert r == 3 and R.is_element(r) + + r = (x**3 + 4*x**2 + 2*x*y*z + 3).compose(x, y*z**2 - 1) + q = (y*z**2 - 1)**3 + 4*(y*z**2 - 1)**2 + 2*(y*z**2 - 1)*y*z + 3 + assert r == q and R.is_element(r) + +def test_PolyElement_is_(): + R, x,y,z = ring("x,y,z", QQ) + + assert (x - x).is_generator == False + assert (x - x).is_ground == True + assert (x - x).is_monomial == True + assert (x - x).is_term == True + + assert (x - x + 1).is_generator == False + assert (x - x + 1).is_ground == True + assert (x - x + 1).is_monomial == True + assert (x - x + 1).is_term == True + + assert x.is_generator == True + assert x.is_ground == False + assert x.is_monomial == True + assert x.is_term == True + + assert (x*y).is_generator == False + assert (x*y).is_ground == False + assert (x*y).is_monomial == True + assert (x*y).is_term == True + + assert (3*x).is_generator == False + assert (3*x).is_ground == False + assert (3*x).is_monomial == False + assert (3*x).is_term == True + + assert (3*x + 1).is_generator == False + assert (3*x + 1).is_ground == False + assert (3*x + 1).is_monomial == False + assert (3*x + 1).is_term == False + + assert R(0).is_zero is True + assert R(1).is_zero is False + + assert R(0).is_one is False + assert R(1).is_one is True + + assert (x - 1).is_monic is True + assert (2*x - 1).is_monic is False + + assert (3*x + 2).is_primitive is True + assert (4*x + 2).is_primitive is False + + assert (x + y + z + 1).is_linear is True + assert (x*y*z + 1).is_linear is False + + assert (x*y + z + 1).is_quadratic is True + assert (x*y*z + 1).is_quadratic is False + + assert (x - 1).is_squarefree is True + assert ((x - 1)**2).is_squarefree is False + + assert (x**2 + x + 1).is_irreducible is True + assert (x**2 + 2*x + 1).is_irreducible is False + + _, t = ring("t", FF(11)) + + assert (7*t + 3).is_irreducible is True + assert (7*t**2 + 3*t + 1).is_irreducible is False + + _, u = ring("u", ZZ) + f = u**16 + u**14 - u**10 - u**8 - u**6 + u**2 + + assert f.is_cyclotomic is False + assert (f + 1).is_cyclotomic is True + + raises(MultivariatePolynomialError, lambda: x.is_cyclotomic) + + R, = ring("", ZZ) + assert R(4).is_squarefree is True + assert R(6).is_irreducible is True + +def test_PolyElement_drop(): + R, x,y,z = ring("x,y,z", ZZ) + + assert R(1).drop(0).ring == PolyRing("y,z", ZZ, lex) + assert R(1).drop(0).drop(0).ring == PolyRing("z", ZZ, lex) + assert R.is_element(R(1).drop(0).drop(0).drop(0)) is False + + raises(ValueError, lambda: z.drop(0).drop(0).drop(0)) + raises(ValueError, lambda: x.drop(0)) + +def test_PolyElement_coeff_wrt(): + R, x, y, z = ring("x, y, z", ZZ) + + p = 4*x**3 + 5*y**2 + 6*y**2*z + 7 + assert p.coeff_wrt(1, 2) == 6*z + 5 # using generator index + assert p.coeff_wrt(x, 3) == 4 # using generator + + p = 2*x**4 + 3*x*y**2*z + 10*y**2 + 10*x*z**2 + assert p.coeff_wrt(x, 1) == 3*y**2*z + 10*z**2 + assert p.coeff_wrt(y, 2) == 3*x*z + 10 + + p = 4*x**2 + 2*x*y + 5 + assert p.coeff_wrt(z, 1) == R(0) + assert p.coeff_wrt(y, 2) == R(0) + +def test_PolyElement_prem(): + R, x, y = ring("x, y", ZZ) + + f, g = x**2 + x*y, 2*x + 2 + assert f.prem(g) == -4*y + 4 # first generator is chosen by default if it is not given + + f, g = x**2 + 1, 2*x - 4 + assert f.prem(g) == f.prem(g, x) == 20 + assert f.prem(g, 1) == R(0) + + f, g = x*y + 2*x + 1, x + y + assert f.prem(g) == -y**2 - 2*y + 1 + assert f.prem(g, 1) == f.prem(g, y) == -x**2 + 2*x + 1 + + raises(ZeroDivisionError, lambda: f.prem(R(0))) + +def test_PolyElement_pdiv(): + R, x, y = ring("x,y", ZZ) + + f, g = x**4 + 5*x**3 + 7*x**2, 2*x**2 + 3 + assert f.pdiv(g) == f.pdiv(g, x) == (4*x**2 + 20*x + 22, -60*x - 66) + + f, g = x**2 - y**2, x - y + assert f.pdiv(g) == f.pdiv(g, 0) == (x + y, 0) + + f, g = x*y + 2*x + 1, x + y + assert f.pdiv(g) == (y + 2, -y**2 - 2*y + 1) + assert f.pdiv(g, y) == f.pdiv(g, 1) == (x + 1, -x**2 + 2*x + 1) + + assert R(0).pdiv(g) == (0, 0) + raises(ZeroDivisionError, lambda: f.prem(R(0))) + +def test_PolyElement_pquo(): + R, x, y = ring("x, y", ZZ) + + f, g = x**4 - 4*x**2*y + 4*y**2, x**2 - 2*y + assert f.pquo(g) == f.pquo(g, x) == x**2 - 2*y + assert f.pquo(g, y) == 4*x**2 - 8*y + 4 + + f, g = x**4 - y**4, x**2 - y**2 + assert f.pquo(g) == f.pquo(g, 0) == x**2 + y**2 + +def test_PolyElement_pexquo(): + R, x, y = ring("x, y", ZZ) + + f, g = x**2 - y**2, x - y + assert f.pexquo(g) == f.pexquo(g, x) == x + y + assert f.pexquo(g, y) == f.pexquo(g, 1) == x + y + 1 + + f, g = x**2 + 3*x + 6, x + 2 + raises(ExactQuotientFailed, lambda: f.pexquo(g)) + +def test_PolyElement_gcdex(): + _, x = ring("x", QQ) + + f, g = 2*x, x**2 - 16 + s, t, h = x/32, -QQ(1, 16), 1 + + assert f.half_gcdex(g) == (s, h) + assert f.gcdex(g) == (s, t, h) + +def test_PolyElement_subresultants(): + R, x, y = ring("x, y", ZZ) + + f, g = x**2*y + x*y, x + y # degree(f, x) > degree(g, x) + h = y**3 - y**2 + assert f.subresultants(g) == [f, g, h] # first generator is chosen default + + # generator index or generator is given + assert f.subresultants(g, 0) == f.subresultants(g, x) == [f, g, h] + + assert f.subresultants(g, y) == [x**2*y + x*y, x + y, x**3 + x**2] + + f, g = 2*x - y, x**2 + 2*y + x # degree(f, x) < degree(g, x) + assert f.subresultants(g) == [x**2 + x + 2*y, 2*x - y, y**2 + 10*y] + + f, g = R(0), y**3 - y**2 # f = 0 + assert f.subresultants(g) == [y**3 - y**2, 1] + + f, g = x**2*y + x*y, R(0) # g = 0 + assert f.subresultants(g) == [x**2*y + x*y, 1] + + f, g = R(0), R(0) # f = 0 and g = 0 + assert f.subresultants(g) == [0, 0] + + f, g = x**2 + x, x**2 + x # f and g are same polynomial + assert f.subresultants(g) == [x**2 + x, x**2 + x] + +def test_PolyElement_resultant(): + _, x = ring("x", ZZ) + f, g, h = x**2 - 2*x + 1, x**2 - 1, 0 + + assert f.resultant(g) == h + +def test_PolyElement_discriminant(): + _, x = ring("x", ZZ) + f, g = x**3 + 3*x**2 + 9*x - 13, -11664 + + assert f.discriminant() == g + + F, a, b, c = ring("a,b,c", ZZ) + _, x = ring("x", F) + + f, g = a*x**2 + b*x + c, b**2 - 4*a*c + + assert f.discriminant() == g + +def test_PolyElement_decompose(): + _, x = ring("x", ZZ) + + f = x**12 + 20*x**10 + 150*x**8 + 500*x**6 + 625*x**4 - 2*x**3 - 10*x + 9 + g = x**4 - 2*x + 9 + h = x**3 + 5*x + + assert g.compose(x, h) == f + assert f.decompose() == [g, h] + +def test_PolyElement_shift(): + _, x = ring("x", ZZ) + assert (x**2 - 2*x + 1).shift(2) == x**2 + 2*x + 1 + assert (x**2 - 2*x + 1).shift_list([2]) == x**2 + 2*x + 1 + + R, x, y = ring("x, y", ZZ) + assert (x*y).shift_list([1, 2]) == (x+1)*(y+2) + + raises(MultivariatePolynomialError, lambda: (x*y).shift(1)) + +def test_PolyElement_sturm(): + F, t = field("t", ZZ) + _, x = ring("x", F) + + f = 1024/(15625*t**8)*x**5 - 4096/(625*t**8)*x**4 + 32/(15625*t**4)*x**3 - 128/(625*t**4)*x**2 + F(1)/62500*x - F(1)/625 + + assert f.sturm() == [ + x**3 - 100*x**2 + t**4/64*x - 25*t**4/16, + 3*x**2 - 200*x + t**4/64, + (-t**4/96 + F(20000)/9)*x + 25*t**4/18, + (-9*t**12 - 11520000*t**8 - 3686400000000*t**4)/(576*t**8 - 245760000*t**4 + 26214400000000), + ] + +def test_PolyElement_gff_list(): + _, x = ring("x", ZZ) + + f = x**5 + 2*x**4 - x**3 - 2*x**2 + assert f.gff_list() == [(x, 1), (x + 2, 4)] + + f = x*(x - 1)**3*(x - 2)**2*(x - 4)**2*(x - 5) + assert f.gff_list() == [(x**2 - 5*x + 4, 1), (x**2 - 5*x + 4, 2), (x, 3)] + +def test_PolyElement_norm(): + k = QQ + K = QQ.algebraic_field(sqrt(2)) + sqrt2 = K.unit + _, X, Y = ring("x,y", k) + _, x, y = ring("x,y", K) + + assert (x*y + sqrt2).norm() == X**2*Y**2 - 2 + +def test_PolyElement_sqf_norm(): + R, x = ring("x", QQ.algebraic_field(sqrt(3))) + X = R.to_ground().x + + assert (x**2 - 2).sqf_norm() == ([1], x**2 - 2*sqrt(3)*x + 1, X**4 - 10*X**2 + 1) + + R, x = ring("x", QQ.algebraic_field(sqrt(2))) + X = R.to_ground().x + + assert (x**2 - 3).sqf_norm() == ([1], x**2 - 2*sqrt(2)*x - 1, X**4 - 10*X**2 + 1) + +def test_PolyElement_sqf_list(): + _, x = ring("x", ZZ) + + f = x**5 - x**3 - x**2 + 1 + g = x**3 + 2*x**2 + 2*x + 1 + h = x - 1 + p = x**4 + x**3 - x - 1 + + assert f.sqf_part() == p + assert f.sqf_list() == (1, [(g, 1), (h, 2)]) + +def test_issue_18894(): + items = [S(3)/16 + sqrt(3*sqrt(3) + 10)/8, S(1)/8 + 3*sqrt(3)/16, S(1)/8 + 3*sqrt(3)/16, -S(3)/16 + sqrt(3*sqrt(3) + 10)/8] + R, a = sring(items, extension=True) + assert R.domain == QQ.algebraic_field(sqrt(3)+sqrt(3*sqrt(3)+10)) + assert R.gens == () + result = [] + for item in items: + result.append(R.domain.from_sympy(item)) + assert a == result + +def test_PolyElement_factor_list(): + _, x = ring("x", ZZ) + + f = x**5 - x**3 - x**2 + 1 + + u = x + 1 + v = x - 1 + w = x**2 + x + 1 + + assert f.factor_list() == (1, [(u, 1), (v, 2), (w, 1)]) + + +def test_issue_21410(): + R, x = ring('x', FF(2)) + p = x**6 + x**5 + x**4 + x**3 + 1 + assert p._pow_multinomial(4) == x**24 + x**20 + x**16 + x**12 + 1 + + +def test_zero_polynomial_primitive(): + + x = symbols('x') + + R = ZZ[x] + zero_poly = R(0) + cont, prim = zero_poly.primitive() + assert cont == 0 + assert prim == zero_poly + assert prim.is_primitive is False diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_rootoftools.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_rootoftools.py new file mode 100644 index 0000000000000000000000000000000000000000..de9dbcabd0a7e2bed0c5adb7127041b4be058379 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_rootoftools.py @@ -0,0 +1,697 @@ +"""Tests for the implementation of RootOf class and related tools. """ + +from sympy.polys.polytools import Poly +import sympy.polys.rootoftools as rootoftools +from sympy.polys.rootoftools import (rootof, RootOf, CRootOf, RootSum, + _pure_key_dict as D) + +from sympy.polys.polyerrors import ( + MultivariatePolynomialError, + GeneratorsNeeded, + PolynomialError, +) + +from sympy.core.function import (Function, Lambda) +from sympy.core.numbers import (Float, I, Rational) +from sympy.core.relational import Eq +from sympy.core.singleton import S +from sympy.functions.elementary.exponential import (exp, log) +from sympy.functions.elementary.miscellaneous import sqrt +from sympy.functions.elementary.trigonometric import tan +from sympy.integrals.integrals import Integral +from sympy.polys.orthopolys import legendre_poly +from sympy.solvers.solvers import solve + + +from sympy.testing.pytest import raises, slow +from sympy.core.expr import unchanged + +from sympy.abc import a, b, x, y, z, r + + +def test_CRootOf___new__(): + assert rootof(x, 0) == 0 + assert rootof(x, -1) == 0 + + assert rootof(x, S.Zero) == 0 + + assert rootof(x - 1, 0) == 1 + assert rootof(x - 1, -1) == 1 + + assert rootof(x + 1, 0) == -1 + assert rootof(x + 1, -1) == -1 + + assert rootof(x**2 + 2*x + 3, 0) == -1 - I*sqrt(2) + assert rootof(x**2 + 2*x + 3, 1) == -1 + I*sqrt(2) + assert rootof(x**2 + 2*x + 3, -1) == -1 + I*sqrt(2) + assert rootof(x**2 + 2*x + 3, -2) == -1 - I*sqrt(2) + + r = rootof(x**2 + 2*x + 3, 0, radicals=False) + assert isinstance(r, RootOf) is True + + r = rootof(x**2 + 2*x + 3, 1, radicals=False) + assert isinstance(r, RootOf) is True + + r = rootof(x**2 + 2*x + 3, -1, radicals=False) + assert isinstance(r, RootOf) is True + + r = rootof(x**2 + 2*x + 3, -2, radicals=False) + assert isinstance(r, RootOf) is True + + assert rootof((x - 1)*(x + 1), 0, radicals=False) == -1 + assert rootof((x - 1)*(x + 1), 1, radicals=False) == 1 + assert rootof((x - 1)*(x + 1), -1, radicals=False) == 1 + assert rootof((x - 1)*(x + 1), -2, radicals=False) == -1 + + assert rootof((x - 1)*(x + 1), 0, radicals=True) == -1 + assert rootof((x - 1)*(x + 1), 1, radicals=True) == 1 + assert rootof((x - 1)*(x + 1), -1, radicals=True) == 1 + assert rootof((x - 1)*(x + 1), -2, radicals=True) == -1 + + assert rootof((x - 1)*(x**3 + x + 3), 0) == rootof(x**3 + x + 3, 0) + assert rootof((x - 1)*(x**3 + x + 3), 1) == 1 + assert rootof((x - 1)*(x**3 + x + 3), 2) == rootof(x**3 + x + 3, 1) + assert rootof((x - 1)*(x**3 + x + 3), 3) == rootof(x**3 + x + 3, 2) + assert rootof((x - 1)*(x**3 + x + 3), -1) == rootof(x**3 + x + 3, 2) + assert rootof((x - 1)*(x**3 + x + 3), -2) == rootof(x**3 + x + 3, 1) + assert rootof((x - 1)*(x**3 + x + 3), -3) == 1 + assert rootof((x - 1)*(x**3 + x + 3), -4) == rootof(x**3 + x + 3, 0) + + assert rootof(x**4 + 3*x**3, 0) == -3 + assert rootof(x**4 + 3*x**3, 1) == 0 + assert rootof(x**4 + 3*x**3, 2) == 0 + assert rootof(x**4 + 3*x**3, 3) == 0 + + raises(GeneratorsNeeded, lambda: rootof(0, 0)) + raises(GeneratorsNeeded, lambda: rootof(1, 0)) + + raises(PolynomialError, lambda: rootof(Poly(0, x), 0)) + raises(PolynomialError, lambda: rootof(Poly(1, x), 0)) + raises(PolynomialError, lambda: rootof(x - y, 0)) + # issue 8617 + raises(PolynomialError, lambda: rootof(exp(x), 0)) + + raises(NotImplementedError, lambda: rootof(x**3 - x + sqrt(2), 0)) + raises(NotImplementedError, lambda: rootof(x**3 - x + I, 0)) + + raises(IndexError, lambda: rootof(x**2 - 1, -4)) + raises(IndexError, lambda: rootof(x**2 - 1, -3)) + raises(IndexError, lambda: rootof(x**2 - 1, 2)) + raises(IndexError, lambda: rootof(x**2 - 1, 3)) + raises(ValueError, lambda: rootof(x**2 - 1, x)) + + assert rootof(Poly(x - y, x), 0) == y + + assert rootof(Poly(x**2 - y, x), 0) == -sqrt(y) + assert rootof(Poly(x**2 - y, x), 1) == sqrt(y) + + assert rootof(Poly(x**3 - y, x), 0) == y**Rational(1, 3) + + assert rootof(y*x**3 + y*x + 2*y, x, 0) == -1 + raises(NotImplementedError, lambda: rootof(x**3 + x + 2*y, x, 0)) + + assert rootof(x**3 + x + 1, 0).is_commutative is True + + +def test_CRootOf_attributes(): + r = rootof(x**3 + x + 3, 0) + assert r.is_number + assert r.free_symbols == set() + # if the following assertion fails then multivariate polynomials + # are apparently supported and the RootOf.free_symbols routine + # should be changed to return whatever symbols would not be + # the PurePoly dummy symbol + raises(NotImplementedError, lambda: rootof(Poly(x**3 + y*x + 1, x), 0)) + + +def test_CRootOf___eq__(): + assert (rootof(x**3 + x + 3, 0) == rootof(x**3 + x + 3, 0)) is True + assert (rootof(x**3 + x + 3, 0) == rootof(x**3 + x + 3, 1)) is False + assert (rootof(x**3 + x + 3, 1) == rootof(x**3 + x + 3, 1)) is True + assert (rootof(x**3 + x + 3, 1) == rootof(x**3 + x + 3, 2)) is False + assert (rootof(x**3 + x + 3, 2) == rootof(x**3 + x + 3, 2)) is True + + assert (rootof(x**3 + x + 3, 0) == rootof(y**3 + y + 3, 0)) is True + assert (rootof(x**3 + x + 3, 0) == rootof(y**3 + y + 3, 1)) is False + assert (rootof(x**3 + x + 3, 1) == rootof(y**3 + y + 3, 1)) is True + assert (rootof(x**3 + x + 3, 1) == rootof(y**3 + y + 3, 2)) is False + assert (rootof(x**3 + x + 3, 2) == rootof(y**3 + y + 3, 2)) is True + + +def test_CRootOf___eval_Eq__(): + f = Function('f') + eq = x**3 + x + 3 + r = rootof(eq, 2) + r1 = rootof(eq, 1) + assert Eq(r, r1) is S.false + assert Eq(r, r) is S.true + assert unchanged(Eq, r, x) + assert Eq(r, 0) is S.false + assert Eq(r, S.Infinity) is S.false + assert Eq(r, I) is S.false + assert unchanged(Eq, r, f(0)) + sol = solve(eq) + for s in sol: + if s.is_real: + assert Eq(r, s) is S.false + r = rootof(eq, 0) + for s in sol: + if s.is_real: + assert Eq(r, s) is S.true + eq = x**3 + x + 1 + sol = solve(eq) + assert [Eq(rootof(eq, i), j) for i in range(3) for j in sol + ].count(True) == 3 + assert Eq(rootof(eq, 0), 1 + S.ImaginaryUnit) == False + + +def test_CRootOf_is_real(): + assert rootof(x**3 + x + 3, 0).is_real is True + assert rootof(x**3 + x + 3, 1).is_real is False + assert rootof(x**3 + x + 3, 2).is_real is False + + +def test_CRootOf_is_complex(): + assert rootof(x**3 + x + 3, 0).is_complex is True + + +def test_CRootOf_is_algebraic(): + assert rootof(x**3 + x + 3, 0).is_algebraic is True + assert rootof(x**3 + x + 3, 1).is_algebraic is True + assert rootof(x**3 + x + 3, 2).is_algebraic is True + + +def test_CRootOf_subs(): + assert rootof(x**3 + x + 1, 0).subs(x, y) == rootof(y**3 + y + 1, 0) + + +def test_CRootOf_diff(): + assert rootof(x**3 + x + 1, 0).diff(x) == 0 + assert rootof(x**3 + x + 1, 0).diff(y) == 0 + +@slow +def test_CRootOf_evalf(): + real = rootof(x**3 + x + 3, 0).evalf(n=20) + + assert real.epsilon_eq(Float("-1.2134116627622296341")) + + re, im = rootof(x**3 + x + 3, 1).evalf(n=20).as_real_imag() + + assert re.epsilon_eq( Float("0.60670583138111481707")) + assert im.epsilon_eq(-Float("1.45061224918844152650")) + + re, im = rootof(x**3 + x + 3, 2).evalf(n=20).as_real_imag() + + assert re.epsilon_eq(Float("0.60670583138111481707")) + assert im.epsilon_eq(Float("1.45061224918844152650")) + + p = legendre_poly(4, x, polys=True) + roots = [str(r.n(17)) for r in p.real_roots()] + # magnitudes are given by + # sqrt(3/S(7) - 2*sqrt(6/S(5))/7) + # and + # sqrt(3/S(7) + 2*sqrt(6/S(5))/7) + assert roots == [ + "-0.86113631159405258", + "-0.33998104358485626", + "0.33998104358485626", + "0.86113631159405258", + ] + + re = rootof(x**5 - 5*x + 12, 0).evalf(n=20) + assert re.epsilon_eq(Float("-1.84208596619025438271")) + + re, im = rootof(x**5 - 5*x + 12, 1).evalf(n=20).as_real_imag() + assert re.epsilon_eq(Float("-0.351854240827371999559")) + assert im.epsilon_eq(Float("-1.709561043370328882010")) + + re, im = rootof(x**5 - 5*x + 12, 2).evalf(n=20).as_real_imag() + assert re.epsilon_eq(Float("-0.351854240827371999559")) + assert im.epsilon_eq(Float("+1.709561043370328882010")) + + re, im = rootof(x**5 - 5*x + 12, 3).evalf(n=20).as_real_imag() + assert re.epsilon_eq(Float("+1.272897223922499190910")) + assert im.epsilon_eq(Float("-0.719798681483861386681")) + + re, im = rootof(x**5 - 5*x + 12, 4).evalf(n=20).as_real_imag() + assert re.epsilon_eq(Float("+1.272897223922499190910")) + assert im.epsilon_eq(Float("+0.719798681483861386681")) + + # issue 6393 + assert str(rootof(x**5 + 2*x**4 + x**3 - 68719476736, 0).n(3)) == '147.' + eq = (531441*x**11 + 3857868*x**10 + 13730229*x**9 + 32597882*x**8 + + 55077472*x**7 + 60452000*x**6 + 32172064*x**5 - 4383808*x**4 - + 11942912*x**3 - 1506304*x**2 + 1453312*x + 512) + a, b = rootof(eq, 1).n(2).as_real_imag() + c, d = rootof(eq, 2).n(2).as_real_imag() + assert a == c + assert b < d + assert b == -d + # issue 6451 + r = rootof(legendre_poly(64, x), 7) + assert r.n(2) == r.n(100).n(2) + # issue 9019 + r0 = rootof(x**2 + 1, 0, radicals=False) + r1 = rootof(x**2 + 1, 1, radicals=False) + assert r0.n(4) == Float(-1.0, 4) * I + assert r1.n(4) == Float(1.0, 4) * I + + # make sure verification is used in case a max/min traps the "root" + assert str(rootof(4*x**5 + 16*x**3 + 12*x**2 + 7, 0).n(3)) == '-0.976' + + # watch out for UnboundLocalError + c = CRootOf(90720*x**6 - 4032*x**4 + 84*x**2 - 1, 0) + assert c._eval_evalf(2) # doesn't fail + + # watch out for imaginary parts that don't want to evaluate + assert str(RootOf(x**16 + 32*x**14 + 508*x**12 + 5440*x**10 + + 39510*x**8 + 204320*x**6 + 755548*x**4 + 1434496*x**2 + + 877969, 10).n(2)) == '-3.4*I' + assert abs(RootOf(x**4 + 10*x**2 + 1, 0).n(2)) < 0.4 + + # check reset and args + r = [RootOf(x**3 + x + 3, i) for i in range(3)] + r[0]._reset() + for ri in r: + i = ri._get_interval() + ri.n(2) + assert i != ri._get_interval() + ri._reset() + assert i == ri._get_interval() + assert i == i.func(*i.args) + + +def test_issue_24978(): + # Irreducible poly with negative leading coeff is normalized + # (factor of -1 is extracted), before being stored as CRootOf.poly. + f = -x**2 + 2 + r = CRootOf(f, 0) + assert r.poly.as_expr() == x**2 - 2 + # An action that prompts calculation of an interval puts r.poly in + # the cache. + r.n() + assert r.poly in rootoftools._reals_cache + + +def test_CRootOf_evalf_caching_bug(): + r = rootof(x**5 - 5*x + 12, 1) + r.n() + a = r._get_interval() + r = rootof(x**5 - 5*x + 12, 1) + r.n() + b = r._get_interval() + assert a == b + + +def test_CRootOf_real_roots(): + assert Poly(x**5 + x + 1).real_roots() == [rootof(x**3 - x**2 + 1, 0)] + assert Poly(x**5 + x + 1).real_roots(radicals=False) == [rootof( + x**3 - x**2 + 1, 0)] + + # https://github.com/sympy/sympy/issues/20902 + p = Poly(-3*x**4 - 10*x**3 - 12*x**2 - 6*x - 1, x, domain='ZZ') + assert CRootOf.real_roots(p) == [S(-1), S(-1), S(-1), S(-1)/3] + + # with real algebraic coefficients + assert Poly(x**3 + sqrt(2)*x**2 - 1, x, extension=True).real_roots() == [ + rootof(x**6 - 2*x**4 - 2*x**3 + 1, 0) + ] + assert Poly(x**5 + sqrt(2) * x**3 - 1, x, extension=True).real_roots() == [ + rootof(x**10 - 2*x**6 - 2*x**5 + 1, 0) + ] + r = rootof(y**5 + y**3 - 1, 0) + assert Poly(x**5 + r*x - 1, x, extension=True).real_roots() ==\ + [ + rootof(x**25 - 5*x**20 + x**17 + 10*x**15 - 3*x**12 - + 10*x**10 + 3*x**7 + 6*x**5 - x**2 - 1, 0) + ] + # roots with multiplicity + assert Poly((x-1) * (x-sqrt(2))**2, x, extension=True).real_roots() ==\ + [ + S(1), sqrt(2), sqrt(2) + ] + + +def test_CRootOf_all_roots(): + assert Poly(x**5 + x + 1).all_roots() == [ + rootof(x**3 - x**2 + 1, 0), + Rational(-1, 2) - sqrt(3)*I/2, + Rational(-1, 2) + sqrt(3)*I/2, + rootof(x**3 - x**2 + 1, 1), + rootof(x**3 - x**2 + 1, 2), + ] + + assert Poly(x**5 + x + 1).all_roots(radicals=False) == [ + rootof(x**3 - x**2 + 1, 0), + rootof(x**2 + x + 1, 0, radicals=False), + rootof(x**2 + x + 1, 1, radicals=False), + rootof(x**3 - x**2 + 1, 1), + rootof(x**3 - x**2 + 1, 2), + ] + + # with real algebraic coefficients + assert Poly(x**3 + sqrt(2)*x**2 - 1, x, extension=True).all_roots() ==\ + [ + rootof(x**6 - 2*x**4 - 2*x**3 + 1, 0), + rootof(x**6 - 2*x**4 - 2*x**3 + 1, 2), + rootof(x**6 - 2*x**4 - 2*x**3 + 1, 3) + ] + # roots with multiplicity + assert Poly((x-1) * (x-sqrt(2))**2 * (x-I) * (x+I), x, extension=True).all_roots() ==\ + [ + S(1), sqrt(2), sqrt(2), -I, I + ] + + # imaginary algebraic coeffs (gaussian domain) + assert Poly(x**2 - I/2, x, extension=True).all_roots() ==\ + [ + S(1)/2 + I/2, + -S(1)/2 - I/2 + ] + + +def test_CRootOf_eval_rational(): + p = legendre_poly(4, x, polys=True) + roots = [r.eval_rational(n=18) for r in p.real_roots()] + for root in roots: + assert isinstance(root, Rational) + roots = [str(root.n(17)) for root in roots] + assert roots == [ + "-0.86113631159405258", + "-0.33998104358485626", + "0.33998104358485626", + "0.86113631159405258", + ] + + +def test_CRootOf_lazy(): + # irreducible poly with both real and complex roots: + f = Poly(x**3 + 2*x + 2) + + # real root: + CRootOf.clear_cache() + r = CRootOf(f, 0) + # Not yet in cache, after construction: + assert r.poly not in rootoftools._reals_cache + assert r.poly not in rootoftools._complexes_cache + r.evalf() + # In cache after evaluation: + assert r.poly in rootoftools._reals_cache + assert r.poly not in rootoftools._complexes_cache + + # complex root: + CRootOf.clear_cache() + r = CRootOf(f, 1) + # Not yet in cache, after construction: + assert r.poly not in rootoftools._reals_cache + assert r.poly not in rootoftools._complexes_cache + r.evalf() + # In cache after evaluation: + assert r.poly in rootoftools._reals_cache + assert r.poly in rootoftools._complexes_cache + + # composite poly with both real and complex roots: + f = Poly((x**2 - 2)*(x**2 + 1)) + + # real root: + CRootOf.clear_cache() + r = CRootOf(f, 0) + # In cache immediately after construction: + assert r.poly in rootoftools._reals_cache + assert r.poly not in rootoftools._complexes_cache + + # complex root: + CRootOf.clear_cache() + r = CRootOf(f, 2) + # In cache immediately after construction: + assert r.poly in rootoftools._reals_cache + assert r.poly in rootoftools._complexes_cache + + +def test_RootSum___new__(): + f = x**3 + x + 3 + + g = Lambda(r, log(r*x)) + s = RootSum(f, g) + + assert isinstance(s, RootSum) is True + + assert RootSum(f**2, g) == 2*RootSum(f, g) + assert RootSum((x - 7)*f**3, g) == log(7*x) + 3*RootSum(f, g) + + # issue 5571 + assert hash(RootSum((x - 7)*f**3, g)) == hash(log(7*x) + 3*RootSum(f, g)) + + raises(MultivariatePolynomialError, lambda: RootSum(x**3 + x + y)) + raises(ValueError, lambda: RootSum(x**2 + 3, lambda x: x)) + + assert RootSum(f, exp) == RootSum(f, Lambda(x, exp(x))) + assert RootSum(f, log) == RootSum(f, Lambda(x, log(x))) + + assert isinstance(RootSum(f, auto=False), RootSum) is True + + assert RootSum(f) == 0 + assert RootSum(f, Lambda(x, x)) == 0 + assert RootSum(f, Lambda(x, x**2)) == -2 + + assert RootSum(f, Lambda(x, 1)) == 3 + assert RootSum(f, Lambda(x, 2)) == 6 + + assert RootSum(f, auto=False).is_commutative is True + + assert RootSum(f, Lambda(x, 1/(x + x**2))) == Rational(11, 3) + assert RootSum(f, Lambda(x, y/(x + x**2))) == Rational(11, 3)*y + + assert RootSum(x**2 - 1, Lambda(x, 3*x**2), x) == 6 + assert RootSum(x**2 - y, Lambda(x, 3*x**2), x) == 6*y + + assert RootSum(x**2 - 1, Lambda(x, z*x**2), x) == 2*z + assert RootSum(x**2 - y, Lambda(x, z*x**2), x) == 2*z*y + + assert RootSum( + x**2 - 1, Lambda(x, exp(x)), quadratic=True) == exp(-1) + exp(1) + + assert RootSum(x**3 + a*x + a**3, tan, x) == \ + RootSum(x**3 + x + 1, Lambda(x, tan(a*x))) + assert RootSum(a**3*x**3 + a*x + 1, tan, x) == \ + RootSum(x**3 + x + 1, Lambda(x, tan(x/a))) + + +def test_RootSum_free_symbols(): + assert RootSum(x**3 + x + 3, Lambda(r, exp(r))).free_symbols == set() + assert RootSum(x**3 + x + 3, Lambda(r, exp(a*r))).free_symbols == {a} + assert RootSum( + x**3 + x + y, Lambda(r, exp(a*r)), x).free_symbols == {a, y} + + +def test_RootSum___eq__(): + f = Lambda(x, exp(x)) + + assert (RootSum(x**3 + x + 1, f) == RootSum(x**3 + x + 1, f)) is True + assert (RootSum(x**3 + x + 1, f) == RootSum(y**3 + y + 1, f)) is True + + assert (RootSum(x**3 + x + 1, f) == RootSum(x**3 + x + 2, f)) is False + assert (RootSum(x**3 + x + 1, f) == RootSum(y**3 + y + 2, f)) is False + + +def test_RootSum_doit(): + rs = RootSum(x**2 + 1, exp) + + assert isinstance(rs, RootSum) is True + assert rs.doit() == exp(-I) + exp(I) + + rs = RootSum(x**2 + a, exp, x) + + assert isinstance(rs, RootSum) is True + assert rs.doit() == exp(-sqrt(-a)) + exp(sqrt(-a)) + + +def test_RootSum_evalf(): + rs = RootSum(x**2 + 1, exp) + + assert rs.evalf(n=20, chop=True).epsilon_eq(Float("1.0806046117362794348")) + assert rs.evalf(n=15, chop=True).epsilon_eq(Float("1.08060461173628")) + + rs = RootSum(x**2 + a, exp, x) + + assert rs.evalf() == rs + + +def test_RootSum_diff(): + f = x**3 + x + 3 + + g = Lambda(r, exp(r*x)) + h = Lambda(r, r*exp(r*x)) + + assert RootSum(f, g).diff(x) == RootSum(f, h) + + +def test_RootSum_subs(): + f = x**3 + x + 3 + g = Lambda(r, exp(r*x)) + + F = y**3 + y + 3 + G = Lambda(r, exp(r*y)) + + assert RootSum(f, g).subs(y, 1) == RootSum(f, g) + assert RootSum(f, g).subs(x, y) == RootSum(F, G) + + +def test_RootSum_rational(): + assert RootSum( + z**5 - z + 1, Lambda(z, z/(x - z))) == (4*x - 5)/(x**5 - x + 1) + + f = 161*z**3 + 115*z**2 + 19*z + 1 + g = Lambda(z, z*log( + -3381*z**4/4 - 3381*z**3/4 - 625*z**2/2 - z*Rational(125, 2) - 5 + exp(x))) + + assert RootSum(f, g).diff(x) == -( + (5*exp(2*x) - 6*exp(x) + 4)*exp(x)/(exp(3*x) - exp(2*x) + 1))/7 + + +def test_RootSum_independent(): + f = (x**3 - a)**2*(x**4 - b)**3 + + g = Lambda(x, 5*tan(x) + 7) + h = Lambda(x, tan(x)) + + r0 = RootSum(x**3 - a, h, x) + r1 = RootSum(x**4 - b, h, x) + + assert RootSum(f, g, x).as_ordered_terms() == [10*r0, 15*r1, 126] + + +def test_issue_7876(): + l1 = Poly(x**6 - x + 1, x).all_roots() + l2 = [rootof(x**6 - x + 1, i) for i in range(6)] + assert frozenset(l1) == frozenset(l2) + + +def test_issue_8316(): + f = Poly(7*x**8 - 9) + assert len(f.all_roots()) == 8 + f = Poly(7*x**8 - 10) + assert len(f.all_roots()) == 8 + + +def test__imag_count(): + from sympy.polys.rootoftools import _imag_count_of_factor + def imag_count(p): + return sum(_imag_count_of_factor(f)*m for f, m in + p.factor_list()[1]) + assert imag_count(Poly(x**6 + 10*x**2 + 1)) == 2 + assert imag_count(Poly(x**2)) == 0 + assert imag_count(Poly([1]*3 + [-1], x)) == 0 + assert imag_count(Poly(x**3 + 1)) == 0 + assert imag_count(Poly(x**2 + 1)) == 2 + assert imag_count(Poly(x**2 - 1)) == 0 + assert imag_count(Poly(x**4 - 1)) == 2 + assert imag_count(Poly(x**4 + 1)) == 0 + assert imag_count(Poly([1, 2, 3], x)) == 0 + assert imag_count(Poly(x**3 + x + 1)) == 0 + assert imag_count(Poly(x**4 + x + 1)) == 0 + def q(r1, r2, p): + return Poly(((x - r1)*(x - r2)).subs(x, x**p), x) + assert imag_count(q(-1, -2, 2)) == 4 + assert imag_count(q(-1, 2, 2)) == 2 + assert imag_count(q(1, 2, 2)) == 0 + assert imag_count(q(1, 2, 4)) == 4 + assert imag_count(q(-1, 2, 4)) == 2 + assert imag_count(q(-1, -2, 4)) == 0 + + +def test_RootOf_is_imaginary(): + r = RootOf(x**4 + 4*x**2 + 1, 1) + i = r._get_interval() + assert r.is_imaginary and i.ax*i.bx <= 0 + + +def test_is_disjoint(): + eq = x**3 + 5*x + 1 + ir = rootof(eq, 0)._get_interval() + ii = rootof(eq, 1)._get_interval() + assert ir.is_disjoint(ii) + assert ii.is_disjoint(ir) + + +def test_pure_key_dict(): + p = D() + assert (x in p) is False + assert (1 in p) is False + p[x] = 1 + assert x in p + assert y in p + assert p[y] == 1 + raises(KeyError, lambda: p[1]) + def dont(k): + p[k] = 2 + raises(ValueError, lambda: dont(1)) + + +@slow +def test_eval_approx_relative(): + CRootOf.clear_cache() + t = [CRootOf(x**3 + 10*x + 1, i) for i in range(3)] + assert [i.eval_rational(1e-1) for i in t] == [ + Rational(-21, 220), Rational(15, 256) - I*805/256, + Rational(15, 256) + I*805/256] + t[0]._reset() + assert [i.eval_rational(1e-1, 1e-4) for i in t] == [ + Rational(-21, 220), Rational(3275, 65536) - I*414645/131072, + Rational(3275, 65536) + I*414645/131072] + assert S(t[0]._get_interval().dx) < 1e-1 + assert S(t[1]._get_interval().dx) < 1e-1 + assert S(t[1]._get_interval().dy) < 1e-4 + assert S(t[2]._get_interval().dx) < 1e-1 + assert S(t[2]._get_interval().dy) < 1e-4 + t[0]._reset() + assert [i.eval_rational(1e-4, 1e-4) for i in t] == [ + Rational(-2001, 20020), Rational(6545, 131072) - I*414645/131072, + Rational(6545, 131072) + I*414645/131072] + assert S(t[0]._get_interval().dx) < 1e-4 + assert S(t[1]._get_interval().dx) < 1e-4 + assert S(t[1]._get_interval().dy) < 1e-4 + assert S(t[2]._get_interval().dx) < 1e-4 + assert S(t[2]._get_interval().dy) < 1e-4 + # in the following, the actual relative precision is + # less than tested, but it should never be greater + t[0]._reset() + assert [i.eval_rational(n=2) for i in t] == [ + Rational(-202201, 2024022), Rational(104755, 2097152) - I*6634255/2097152, + Rational(104755, 2097152) + I*6634255/2097152] + assert abs(S(t[0]._get_interval().dx)/t[0]) < 1e-2 + assert abs(S(t[1]._get_interval().dx)/t[1]).n() < 1e-2 + assert abs(S(t[1]._get_interval().dy)/t[1]).n() < 1e-2 + assert abs(S(t[2]._get_interval().dx)/t[2]).n() < 1e-2 + assert abs(S(t[2]._get_interval().dy)/t[2]).n() < 1e-2 + t[0]._reset() + assert [i.eval_rational(n=3) for i in t] == [ + Rational(-202201, 2024022), Rational(1676045, 33554432) - I*106148135/33554432, + Rational(1676045, 33554432) + I*106148135/33554432] + assert abs(S(t[0]._get_interval().dx)/t[0]) < 1e-3 + assert abs(S(t[1]._get_interval().dx)/t[1]).n() < 1e-3 + assert abs(S(t[1]._get_interval().dy)/t[1]).n() < 1e-3 + assert abs(S(t[2]._get_interval().dx)/t[2]).n() < 1e-3 + assert abs(S(t[2]._get_interval().dy)/t[2]).n() < 1e-3 + + t[0]._reset() + a = [i.eval_approx(2) for i in t] + assert [str(i) for i in a] == [ + '-0.10', '0.05 - 3.2*I', '0.05 + 3.2*I'] + assert all(abs(((a[i] - t[i])/t[i]).n()) < 1e-2 for i in range(len(a))) + + +def test_issue_15920(): + r = rootof(x**5 - x + 1, 0) + p = Integral(x, (x, 1, y)) + assert unchanged(Eq, r, p) + + +def test_issue_19113(): + eq = y**3 - y + 1 + # generator is a canonical x in RootOf + assert str(Poly(eq).real_roots()) == '[CRootOf(x**3 - x + 1, 0)]' + assert str(Poly(eq.subs(y, tan(y))).real_roots() + ) == '[CRootOf(x**3 - x + 1, 0)]' + assert str(Poly(eq.subs(y, tan(x))).real_roots() + ) == '[CRootOf(x**3 - x + 1, 0)]' diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_solvers.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_solvers.py new file mode 100644 index 0000000000000000000000000000000000000000..bf8708314466b6a8676ba1a4438eb84924d0030c --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_solvers.py @@ -0,0 +1,112 @@ +"""Tests for low-level linear systems solver. """ + +from sympy.matrices import Matrix +from sympy.polys.domains import ZZ, QQ +from sympy.polys.fields import field +from sympy.polys.rings import ring +from sympy.polys.solvers import solve_lin_sys, eqs_to_matrix + + +def test_solve_lin_sys_2x2_one(): + domain, x1,x2 = ring("x1,x2", QQ) + eqs = [x1 + x2 - 5, + 2*x1 - x2] + sol = {x1: QQ(5, 3), x2: QQ(10, 3)} + _sol = solve_lin_sys(eqs, domain) + assert _sol == sol and all(s.ring == domain for s in _sol) + +def test_solve_lin_sys_2x4_none(): + domain, x1,x2 = ring("x1,x2", QQ) + eqs = [x1 - 1, + x1 - x2, + x1 - 2*x2, + x2 - 1] + assert solve_lin_sys(eqs, domain) is None + + +def test_solve_lin_sys_3x4_one(): + domain, x1,x2,x3 = ring("x1,x2,x3", QQ) + eqs = [x1 + 2*x2 + 3*x3, + 2*x1 - x2 + x3, + 3*x1 + x2 + x3, + 5*x2 + 2*x3] + sol = {x1: 0, x2: 0, x3: 0} + assert solve_lin_sys(eqs, domain) == sol + +def test_solve_lin_sys_3x3_inf(): + domain, x1,x2,x3 = ring("x1,x2,x3", QQ) + eqs = [x1 - x2 + 2*x3 - 1, + 2*x1 + x2 + x3 - 8, + x1 + x2 - 5] + sol = {x1: -x3 + 3, x2: x3 + 2} + assert solve_lin_sys(eqs, domain) == sol + +def test_solve_lin_sys_3x4_none(): + domain, x1,x2,x3,x4 = ring("x1,x2,x3,x4", QQ) + eqs = [2*x1 + x2 + 7*x3 - 7*x4 - 2, + -3*x1 + 4*x2 - 5*x3 - 6*x4 - 3, + x1 + x2 + 4*x3 - 5*x4 - 2] + assert solve_lin_sys(eqs, domain) is None + + +def test_solve_lin_sys_4x7_inf(): + domain, x1,x2,x3,x4,x5,x6,x7 = ring("x1,x2,x3,x4,x5,x6,x7", QQ) + eqs = [x1 + 4*x2 - x4 + 7*x6 - 9*x7 - 3, + 2*x1 + 8*x2 - x3 + 3*x4 + 9*x5 - 13*x6 + 7*x7 - 9, + 2*x3 - 3*x4 - 4*x5 + 12*x6 - 8*x7 - 1, + -x1 - 4*x2 + 2*x3 + 4*x4 + 8*x5 - 31*x6 + 37*x7 - 4] + sol = {x1: 4 - 4*x2 - 2*x5 - x6 + 3*x7, + x3: 2 - x5 + 3*x6 - 5*x7, + x4: 1 - 2*x5 + 6*x6 - 6*x7} + assert solve_lin_sys(eqs, domain) == sol + +def test_solve_lin_sys_5x5_inf(): + domain, x1,x2,x3,x4,x5 = ring("x1,x2,x3,x4,x5", QQ) + eqs = [x1 - x2 - 2*x3 + x4 + 11*x5 - 13, + x1 - x2 + x3 + x4 + 5*x5 - 16, + 2*x1 - 2*x2 + x4 + 10*x5 - 21, + 2*x1 - 2*x2 - x3 + 3*x4 + 20*x5 - 38, + 2*x1 - 2*x2 + x3 + x4 + 8*x5 - 22] + sol = {x1: 6 + x2 - 3*x5, + x3: 1 + 2*x5, + x4: 9 - 4*x5} + assert solve_lin_sys(eqs, domain) == sol + +def test_solve_lin_sys_6x6_1(): + ground, d,r,e,g,i,j,l,o,m,p,q = field("d,r,e,g,i,j,l,o,m,p,q", ZZ) + domain, c,f,h,k,n,b = ring("c,f,h,k,n,b", ground) + + eqs = [b + q/d - c/d, c*(1/d + 1/e + 1/g) - f/g - q/d, f*(1/g + 1/i + 1/j) - c/g - h/i, h*(1/i + 1/l + 1/m) - f/i - k/m, k*(1/m + 1/o + 1/p) - h/m - n/p, n/p - k/p] + sol = { + b: (e*i*l*q + e*i*m*q + e*i*o*q + e*j*l*q + e*j*m*q + e*j*o*q + e*l*m*q + e*l*o*q + g*i*l*q + g*i*m*q + g*i*o*q + g*j*l*q + g*j*m*q + g*j*o*q + g*l*m*q + g*l*o*q + i*j*l*q + i*j*m*q + i*j*o*q + j*l*m*q + j*l*o*q)/(-d*e*i*l - d*e*i*m - d*e*i*o - d*e*j*l - d*e*j*m - d*e*j*o - d*e*l*m - d*e*l*o - d*g*i*l - d*g*i*m - d*g*i*o - d*g*j*l - d*g*j*m - d*g*j*o - d*g*l*m - d*g*l*o - d*i*j*l - d*i*j*m - d*i*j*o - d*j*l*m - d*j*l*o - e*g*i*l - e*g*i*m - e*g*i*o - e*g*j*l - e*g*j*m - e*g*j*o - e*g*l*m - e*g*l*o - e*i*j*l - e*i*j*m - e*i*j*o - e*j*l*m - e*j*l*o), + c: (-e*g*i*l*q - e*g*i*m*q - e*g*i*o*q - e*g*j*l*q - e*g*j*m*q - e*g*j*o*q - e*g*l*m*q - e*g*l*o*q - e*i*j*l*q - e*i*j*m*q - e*i*j*o*q - e*j*l*m*q - e*j*l*o*q)/(-d*e*i*l - d*e*i*m - d*e*i*o - d*e*j*l - d*e*j*m - d*e*j*o - d*e*l*m - d*e*l*o - d*g*i*l - d*g*i*m - d*g*i*o - d*g*j*l - d*g*j*m - d*g*j*o - d*g*l*m - d*g*l*o - d*i*j*l - d*i*j*m - d*i*j*o - d*j*l*m - d*j*l*o - e*g*i*l - e*g*i*m - e*g*i*o - e*g*j*l - e*g*j*m - e*g*j*o - e*g*l*m - e*g*l*o - e*i*j*l - e*i*j*m - e*i*j*o - e*j*l*m - e*j*l*o), + f: (-e*i*j*l*q - e*i*j*m*q - e*i*j*o*q - e*j*l*m*q - e*j*l*o*q)/(-d*e*i*l - d*e*i*m - d*e*i*o - d*e*j*l - d*e*j*m - d*e*j*o - d*e*l*m - d*e*l*o - d*g*i*l - d*g*i*m - d*g*i*o - d*g*j*l - d*g*j*m - d*g*j*o - d*g*l*m - d*g*l*o - d*i*j*l - d*i*j*m - d*i*j*o - d*j*l*m - d*j*l*o - e*g*i*l - e*g*i*m - e*g*i*o - e*g*j*l - e*g*j*m - e*g*j*o - e*g*l*m - e*g*l*o - e*i*j*l - e*i*j*m - e*i*j*o - e*j*l*m - e*j*l*o), + h: (-e*j*l*m*q - e*j*l*o*q)/(-d*e*i*l - d*e*i*m - d*e*i*o - d*e*j*l - d*e*j*m - d*e*j*o - d*e*l*m - d*e*l*o - d*g*i*l - d*g*i*m - d*g*i*o - d*g*j*l - d*g*j*m - d*g*j*o - d*g*l*m - d*g*l*o - d*i*j*l - d*i*j*m - d*i*j*o - d*j*l*m - d*j*l*o - e*g*i*l - e*g*i*m - e*g*i*o - e*g*j*l - e*g*j*m - e*g*j*o - e*g*l*m - e*g*l*o - e*i*j*l - e*i*j*m - e*i*j*o - e*j*l*m - e*j*l*o), + k: e*j*l*o*q/(d*e*i*l + d*e*i*m + d*e*i*o + d*e*j*l + d*e*j*m + d*e*j*o + d*e*l*m + d*e*l*o + d*g*i*l + d*g*i*m + d*g*i*o + d*g*j*l + d*g*j*m + d*g*j*o + d*g*l*m + d*g*l*o + d*i*j*l + d*i*j*m + d*i*j*o + d*j*l*m + d*j*l*o + e*g*i*l + e*g*i*m + e*g*i*o + e*g*j*l + e*g*j*m + e*g*j*o + e*g*l*m + e*g*l*o + e*i*j*l + e*i*j*m + e*i*j*o + e*j*l*m + e*j*l*o), + n: e*j*l*o*q/(d*e*i*l + d*e*i*m + d*e*i*o + d*e*j*l + d*e*j*m + d*e*j*o + d*e*l*m + d*e*l*o + d*g*i*l + d*g*i*m + d*g*i*o + d*g*j*l + d*g*j*m + d*g*j*o + d*g*l*m + d*g*l*o + d*i*j*l + d*i*j*m + d*i*j*o + d*j*l*m + d*j*l*o + e*g*i*l + e*g*i*m + e*g*i*o + e*g*j*l + e*g*j*m + e*g*j*o + e*g*l*m + e*g*l*o + e*i*j*l + e*i*j*m + e*i*j*o + e*j*l*m + e*j*l*o), + } + + assert solve_lin_sys(eqs, domain) == sol + +def test_solve_lin_sys_6x6_2(): + ground, d,r,e,g,i,j,l,o,m,p,q = field("d,r,e,g,i,j,l,o,m,p,q", ZZ) + domain, c,f,h,k,n,b = ring("c,f,h,k,n,b", ground) + + eqs = [b + r/d - c/d, c*(1/d + 1/e + 1/g) - f/g - r/d, f*(1/g + 1/i + 1/j) - c/g - h/i, h*(1/i + 1/l + 1/m) - f/i - k/m, k*(1/m + 1/o + 1/p) - h/m - n/p, n*(1/p + 1/q) - k/p] + sol = { + b: -((l*q*e*o + l*q*g*o + i*m*q*e + i*l*q*e + i*l*p*e + i*j*o*q + j*e*o*q + g*j*o*q + i*e*o*q + g*i*o*q + e*l*o*p + e*l*m*p + e*l*m*o + e*i*o*p + e*i*m*p + e*i*m*o + e*i*l*o + j*e*o*p + j*e*m*q + j*e*m*p + j*e*m*o + j*l*m*q + j*l*m*p + j*l*m*o + i*j*m*p + i*j*m*o + i*j*l*q + i*j*l*o + i*j*m*q + j*l*o*p + j*e*l*o + g*j*o*p + g*j*m*q + g*j*m*p + i*j*l*p + i*j*o*p + j*e*l*q + j*e*l*p + j*l*o*q + g*j*m*o + g*j*l*q + g*j*l*p + g*j*l*o + g*l*o*p + g*l*m*p + g*l*m*o + g*i*m*o + g*i*o*p + g*i*m*q + g*i*m*p + g*i*l*q + g*i*l*p + g*i*l*o + l*m*q*e + l*m*q*g)*r)/(l*q*d*e*o + l*q*d*g*o + l*q*e*g*o + i*j*d*o*q + i*j*e*o*q + j*d*e*o*q + g*j*d*o*q + g*j*e*o*q + g*i*e*o*q + i*d*e*o*q + g*i*d*o*q + g*i*d*o*p + g*i*d*m*q + g*i*d*m*p + g*i*d*m*o + g*i*d*l*q + g*i*d*l*p + g*i*d*l*o + g*e*l*m*p + g*e*l*o*p + g*j*e*l*q + g*e*l*m*o + g*j*e*m*p + g*j*e*m*o + d*e*l*m*p + d*e*l*m*o + i*d*e*m*p + g*j*e*l*p + g*j*e*l*o + d*e*l*o*p + i*j*d*l*o + i*j*e*o*p + i*j*e*m*q + i*j*d*m*q + i*j*d*m*p + i*j*d*m*o + i*j*d*l*q + i*j*d*l*p + i*j*e*m*p + i*j*e*m*o + i*j*e*l*q + i*j*e*l*p + i*j*e*l*o + i*d*e*m*q + i*d*e*m*o + i*d*e*l*q + i*d*e*l*p + j*d*l*o*p + j*d*e*l*o + g*j*d*o*p + g*j*d*m*q + g*j*d*m*p + g*j*d*m*o + g*j*d*l*q + g*j*d*l*p + g*j*d*l*o + g*j*e*o*p + g*j*e*m*q + g*d*l*o*p + g*d*l*m*p + g*d*l*m*o + j*d*e*m*p + i*d*e*o*p + j*e*o*q*l + j*e*o*p*l + j*e*m*q*l + j*d*e*o*p + j*d*e*m*q + i*j*d*o*p + g*i*e*o*p + j*d*e*m*o + j*d*e*l*q + j*d*e*l*p + j*e*m*p*l + j*e*m*o*l + g*i*e*m*q + g*i*e*m*p + g*i*e*m*o + g*i*e*l*q + g*i*e*l*p + g*i*e*l*o + j*d*l*o*q + j*d*l*m*q + j*d*l*m*p + j*d*l*m*o + i*d*e*l*o + l*m*q*d*e + l*m*q*d*g + l*m*q*e*g), + c: (r*e*(l*q*g*o + i*j*o*q + g*j*o*q + g*i*o*q + j*l*m*q + j*l*m*p + j*l*m*o + i*j*m*p + i*j*m*o + i*j*l*q + i*j*l*o + i*j*m*q + j*l*o*p + g*j*o*p + g*j*m*q + g*j*m*p + i*j*l*p + i*j*o*p + j*l*o*q + g*j*m*o + g*j*l*q + g*j*l*p + g*j*l*o + g*l*o*p + g*l*m*p + g*l*m*o + g*i*m*o + g*i*o*p + g*i*m*q + g*i*m*p + g*i*l*q + g*i*l*p + g*i*l*o + l*m*q*g))/(l*q*d*e*o + l*q*d*g*o + l*q*e*g*o + i*j*d*o*q + i*j*e*o*q + j*d*e*o*q + g*j*d*o*q + g*j*e*o*q + g*i*e*o*q + i*d*e*o*q + g*i*d*o*q + g*i*d*o*p + g*i*d*m*q + g*i*d*m*p + g*i*d*m*o + g*i*d*l*q + g*i*d*l*p + g*i*d*l*o + g*e*l*m*p + g*e*l*o*p + g*j*e*l*q + g*e*l*m*o + g*j*e*m*p + g*j*e*m*o + d*e*l*m*p + d*e*l*m*o + i*d*e*m*p + g*j*e*l*p + g*j*e*l*o + d*e*l*o*p + i*j*d*l*o + i*j*e*o*p + i*j*e*m*q + i*j*d*m*q + i*j*d*m*p + i*j*d*m*o + i*j*d*l*q + i*j*d*l*p + i*j*e*m*p + i*j*e*m*o + i*j*e*l*q + i*j*e*l*p + i*j*e*l*o + i*d*e*m*q + i*d*e*m*o + i*d*e*l*q + i*d*e*l*p + j*d*l*o*p + j*d*e*l*o + g*j*d*o*p + g*j*d*m*q + g*j*d*m*p + g*j*d*m*o + g*j*d*l*q + g*j*d*l*p + g*j*d*l*o + g*j*e*o*p + g*j*e*m*q + g*d*l*o*p + g*d*l*m*p + g*d*l*m*o + j*d*e*m*p + i*d*e*o*p + j*e*o*q*l + j*e*o*p*l + j*e*m*q*l + j*d*e*o*p + j*d*e*m*q + i*j*d*o*p + g*i*e*o*p + j*d*e*m*o + j*d*e*l*q + j*d*e*l*p + j*e*m*p*l + j*e*m*o*l + g*i*e*m*q + g*i*e*m*p + g*i*e*m*o + g*i*e*l*q + g*i*e*l*p + g*i*e*l*o + j*d*l*o*q + j*d*l*m*q + j*d*l*m*p + j*d*l*m*o + i*d*e*l*o + l*m*q*d*e + l*m*q*d*g + l*m*q*e*g), + f: (r*e*j*(l*q*o + l*o*p + l*m*q + l*m*p + l*m*o + i*o*q + i*o*p + i*m*q + i*m*p + i*m*o + i*l*q + i*l*p + i*l*o))/(l*q*d*e*o + l*q*d*g*o + l*q*e*g*o + i*j*d*o*q + i*j*e*o*q + j*d*e*o*q + g*j*d*o*q + g*j*e*o*q + g*i*e*o*q + i*d*e*o*q + g*i*d*o*q + g*i*d*o*p + g*i*d*m*q + g*i*d*m*p + g*i*d*m*o + g*i*d*l*q + g*i*d*l*p + g*i*d*l*o + g*e*l*m*p + g*e*l*o*p + g*j*e*l*q + g*e*l*m*o + g*j*e*m*p + g*j*e*m*o + d*e*l*m*p + d*e*l*m*o + i*d*e*m*p + g*j*e*l*p + g*j*e*l*o + d*e*l*o*p + i*j*d*l*o + i*j*e*o*p + i*j*e*m*q + i*j*d*m*q + i*j*d*m*p + i*j*d*m*o + i*j*d*l*q + i*j*d*l*p + i*j*e*m*p + i*j*e*m*o + i*j*e*l*q + i*j*e*l*p + i*j*e*l*o + i*d*e*m*q + i*d*e*m*o + i*d*e*l*q + i*d*e*l*p + j*d*l*o*p + j*d*e*l*o + g*j*d*o*p + g*j*d*m*q + g*j*d*m*p + g*j*d*m*o + g*j*d*l*q + g*j*d*l*p + g*j*d*l*o + g*j*e*o*p + g*j*e*m*q + g*d*l*o*p + g*d*l*m*p + g*d*l*m*o + j*d*e*m*p + i*d*e*o*p + j*e*o*q*l + j*e*o*p*l + j*e*m*q*l + j*d*e*o*p + j*d*e*m*q + i*j*d*o*p + g*i*e*o*p + j*d*e*m*o + j*d*e*l*q + j*d*e*l*p + j*e*m*p*l + j*e*m*o*l + g*i*e*m*q + g*i*e*m*p + g*i*e*m*o + g*i*e*l*q + g*i*e*l*p + g*i*e*l*o + j*d*l*o*q + j*d*l*m*q + j*d*l*m*p + j*d*l*m*o + i*d*e*l*o + l*m*q*d*e + l*m*q*d*g + l*m*q*e*g), + h: (j*e*r*l*(o*q + o*p + m*q + m*p + m*o))/(l*q*d*e*o + l*q*d*g*o + l*q*e*g*o + i*j*d*o*q + i*j*e*o*q + j*d*e*o*q + g*j*d*o*q + g*j*e*o*q + g*i*e*o*q + i*d*e*o*q + g*i*d*o*q + g*i*d*o*p + g*i*d*m*q + g*i*d*m*p + g*i*d*m*o + g*i*d*l*q + g*i*d*l*p + g*i*d*l*o + g*e*l*m*p + g*e*l*o*p + g*j*e*l*q + g*e*l*m*o + g*j*e*m*p + g*j*e*m*o + d*e*l*m*p + d*e*l*m*o + i*d*e*m*p + g*j*e*l*p + g*j*e*l*o + d*e*l*o*p + i*j*d*l*o + i*j*e*o*p + i*j*e*m*q + i*j*d*m*q + i*j*d*m*p + i*j*d*m*o + i*j*d*l*q + i*j*d*l*p + i*j*e*m*p + i*j*e*m*o + i*j*e*l*q + i*j*e*l*p + i*j*e*l*o + i*d*e*m*q + i*d*e*m*o + i*d*e*l*q + i*d*e*l*p + j*d*l*o*p + j*d*e*l*o + g*j*d*o*p + g*j*d*m*q + g*j*d*m*p + g*j*d*m*o + g*j*d*l*q + g*j*d*l*p + g*j*d*l*o + g*j*e*o*p + g*j*e*m*q + g*d*l*o*p + g*d*l*m*p + g*d*l*m*o + j*d*e*m*p + i*d*e*o*p + j*e*o*q*l + j*e*o*p*l + j*e*m*q*l + j*d*e*o*p + j*d*e*m*q + i*j*d*o*p + g*i*e*o*p + j*d*e*m*o + j*d*e*l*q + j*d*e*l*p + j*e*m*p*l + j*e*m*o*l + g*i*e*m*q + g*i*e*m*p + g*i*e*m*o + g*i*e*l*q + g*i*e*l*p + g*i*e*l*o + j*d*l*o*q + j*d*l*m*q + j*d*l*m*p + j*d*l*m*o + i*d*e*l*o + l*m*q*d*e + l*m*q*d*g + l*m*q*e*g), + k: (j*e*r*o*l*(q + p))/(l*q*d*e*o + l*q*d*g*o + l*q*e*g*o + i*j*d*o*q + i*j*e*o*q + j*d*e*o*q + g*j*d*o*q + g*j*e*o*q + g*i*e*o*q + i*d*e*o*q + g*i*d*o*q + g*i*d*o*p + g*i*d*m*q + g*i*d*m*p + g*i*d*m*o + g*i*d*l*q + g*i*d*l*p + g*i*d*l*o + g*e*l*m*p + g*e*l*o*p + g*j*e*l*q + g*e*l*m*o + g*j*e*m*p + g*j*e*m*o + d*e*l*m*p + d*e*l*m*o + i*d*e*m*p + g*j*e*l*p + g*j*e*l*o + d*e*l*o*p + i*j*d*l*o + i*j*e*o*p + i*j*e*m*q + i*j*d*m*q + i*j*d*m*p + i*j*d*m*o + i*j*d*l*q + i*j*d*l*p + i*j*e*m*p + i*j*e*m*o + i*j*e*l*q + i*j*e*l*p + i*j*e*l*o + i*d*e*m*q + i*d*e*m*o + i*d*e*l*q + i*d*e*l*p + j*d*l*o*p + j*d*e*l*o + g*j*d*o*p + g*j*d*m*q + g*j*d*m*p + g*j*d*m*o + g*j*d*l*q + g*j*d*l*p + g*j*d*l*o + g*j*e*o*p + g*j*e*m*q + g*d*l*o*p + g*d*l*m*p + g*d*l*m*o + j*d*e*m*p + i*d*e*o*p + j*e*o*q*l + j*e*o*p*l + j*e*m*q*l + j*d*e*o*p + j*d*e*m*q + i*j*d*o*p + g*i*e*o*p + j*d*e*m*o + j*d*e*l*q + j*d*e*l*p + j*e*m*p*l + j*e*m*o*l + g*i*e*m*q + g*i*e*m*p + g*i*e*m*o + g*i*e*l*q + g*i*e*l*p + g*i*e*l*o + j*d*l*o*q + j*d*l*m*q + j*d*l*m*p + j*d*l*m*o + i*d*e*l*o + l*m*q*d*e + l*m*q*d*g + l*m*q*e*g), + n: (j*e*r*o*q*l)/(l*q*d*e*o + l*q*d*g*o + l*q*e*g*o + i*j*d*o*q + i*j*e*o*q + j*d*e*o*q + g*j*d*o*q + g*j*e*o*q + g*i*e*o*q + i*d*e*o*q + g*i*d*o*q + g*i*d*o*p + g*i*d*m*q + g*i*d*m*p + g*i*d*m*o + g*i*d*l*q + g*i*d*l*p + g*i*d*l*o + g*e*l*m*p + g*e*l*o*p + g*j*e*l*q + g*e*l*m*o + g*j*e*m*p + g*j*e*m*o + d*e*l*m*p + d*e*l*m*o + i*d*e*m*p + g*j*e*l*p + g*j*e*l*o + d*e*l*o*p + i*j*d*l*o + i*j*e*o*p + i*j*e*m*q + i*j*d*m*q + i*j*d*m*p + i*j*d*m*o + i*j*d*l*q + i*j*d*l*p + i*j*e*m*p + i*j*e*m*o + i*j*e*l*q + i*j*e*l*p + i*j*e*l*o + i*d*e*m*q + i*d*e*m*o + i*d*e*l*q + i*d*e*l*p + j*d*l*o*p + j*d*e*l*o + g*j*d*o*p + g*j*d*m*q + g*j*d*m*p + g*j*d*m*o + g*j*d*l*q + g*j*d*l*p + g*j*d*l*o + g*j*e*o*p + g*j*e*m*q + g*d*l*o*p + g*d*l*m*p + g*d*l*m*o + j*d*e*m*p + i*d*e*o*p + j*e*o*q*l + j*e*o*p*l + j*e*m*q*l + j*d*e*o*p + j*d*e*m*q + i*j*d*o*p + g*i*e*o*p + j*d*e*m*o + j*d*e*l*q + j*d*e*l*p + j*e*m*p*l + j*e*m*o*l + g*i*e*m*q + g*i*e*m*p + g*i*e*m*o + g*i*e*l*q + g*i*e*l*p + g*i*e*l*o + j*d*l*o*q + j*d*l*m*q + j*d*l*m*p + j*d*l*m*o + i*d*e*l*o + l*m*q*d*e + l*m*q*d*g + l*m*q*e*g), + } + + assert solve_lin_sys(eqs, domain) == sol + +def test_eqs_to_matrix(): + domain, x1,x2 = ring("x1,x2", QQ) + eqs_coeff = [{x1: QQ(1), x2: QQ(1)}, {x1: QQ(2), x2: QQ(-1)}] + eqs_rhs = [QQ(-5), QQ(0)] + M = eqs_to_matrix(eqs_coeff, eqs_rhs, [x1, x2], QQ) + assert M.to_Matrix() == Matrix([[1, 1, 5], [2, -1, 0]]) diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_specialpolys.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_specialpolys.py new file mode 100644 index 0000000000000000000000000000000000000000..39f551c9e70b5c2bae748ea681b9c8a8cb349fe1 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_specialpolys.py @@ -0,0 +1,152 @@ +"""Tests for functions for generating interesting polynomials. """ + +from sympy.core.add import Add +from sympy.core.symbol import symbols +from sympy.functions.elementary.miscellaneous import sqrt +from sympy.ntheory.generate import prime +from sympy.polys.domains.integerring import ZZ +from sympy.polys.polytools import Poly +from sympy.utilities.iterables import permute_signs +from sympy.testing.pytest import raises + +from sympy.polys.specialpolys import ( + swinnerton_dyer_poly, + cyclotomic_poly, + symmetric_poly, + random_poly, + interpolating_poly, + fateman_poly_F_1, + dmp_fateman_poly_F_1, + fateman_poly_F_2, + dmp_fateman_poly_F_2, + fateman_poly_F_3, + dmp_fateman_poly_F_3, +) + +from sympy.abc import x, y, z + + +def test_swinnerton_dyer_poly(): + raises(ValueError, lambda: swinnerton_dyer_poly(0, x)) + + assert swinnerton_dyer_poly(1, x, polys=True) == Poly(x**2 - 2) + + assert swinnerton_dyer_poly(1, x) == x**2 - 2 + assert swinnerton_dyer_poly(2, x) == x**4 - 10*x**2 + 1 + assert swinnerton_dyer_poly( + 3, x) == x**8 - 40*x**6 + 352*x**4 - 960*x**2 + 576 + # we only need to check that the polys arg works but + # we may as well test that the roots are correct + p = [sqrt(prime(i)) for i in range(1, 5)] + assert str([i.n(3) for i in + swinnerton_dyer_poly(4, polys=True).all_roots()] + ) == str(sorted([Add(*i).n(3) for i in permute_signs(p)])) + + +def test_cyclotomic_poly(): + raises(ValueError, lambda: cyclotomic_poly(0, x)) + + assert cyclotomic_poly(1, x, polys=True) == Poly(x - 1) + + assert cyclotomic_poly(1, x) == x - 1 + assert cyclotomic_poly(2, x) == x + 1 + assert cyclotomic_poly(3, x) == x**2 + x + 1 + assert cyclotomic_poly(4, x) == x**2 + 1 + assert cyclotomic_poly(5, x) == x**4 + x**3 + x**2 + x + 1 + assert cyclotomic_poly(6, x) == x**2 - x + 1 + + +def test_symmetric_poly(): + raises(ValueError, lambda: symmetric_poly(-1, x, y, z)) + raises(ValueError, lambda: symmetric_poly(5, x, y, z)) + + assert symmetric_poly(1, x, y, z, polys=True) == Poly(x + y + z) + assert symmetric_poly(1, (x, y, z), polys=True) == Poly(x + y + z) + + assert symmetric_poly(0, x, y, z) == 1 + assert symmetric_poly(1, x, y, z) == x + y + z + assert symmetric_poly(2, x, y, z) == x*y + x*z + y*z + assert symmetric_poly(3, x, y, z) == x*y*z + + +def test_random_poly(): + poly = random_poly(x, 10, -100, 100, polys=False) + + assert Poly(poly).degree() == 10 + assert all(-100 <= coeff <= 100 for coeff in Poly(poly).coeffs()) is True + + poly = random_poly(x, 10, -100, 100, polys=True) + + assert poly.degree() == 10 + assert all(-100 <= coeff <= 100 for coeff in poly.coeffs()) is True + + +def test_interpolating_poly(): + x0, x1, x2, x3, y0, y1, y2, y3 = symbols('x:4, y:4') + + assert interpolating_poly(0, x) == 0 + assert interpolating_poly(1, x) == y0 + + assert interpolating_poly(2, x) == \ + y0*(x - x1)/(x0 - x1) + y1*(x - x0)/(x1 - x0) + + assert interpolating_poly(3, x) == \ + y0*(x - x1)*(x - x2)/((x0 - x1)*(x0 - x2)) + \ + y1*(x - x0)*(x - x2)/((x1 - x0)*(x1 - x2)) + \ + y2*(x - x0)*(x - x1)/((x2 - x0)*(x2 - x1)) + + assert interpolating_poly(4, x) == \ + y0*(x - x1)*(x - x2)*(x - x3)/((x0 - x1)*(x0 - x2)*(x0 - x3)) + \ + y1*(x - x0)*(x - x2)*(x - x3)/((x1 - x0)*(x1 - x2)*(x1 - x3)) + \ + y2*(x - x0)*(x - x1)*(x - x3)/((x2 - x0)*(x2 - x1)*(x2 - x3)) + \ + y3*(x - x0)*(x - x1)*(x - x2)/((x3 - x0)*(x3 - x1)*(x3 - x2)) + + raises(ValueError, lambda: + interpolating_poly(2, x, (x, 2), (1, 3))) + raises(ValueError, lambda: + interpolating_poly(2, x, (x + y, 2), (1, 3))) + raises(ValueError, lambda: + interpolating_poly(2, x + y, (x, 2), (1, 3))) + raises(ValueError, lambda: + interpolating_poly(2, 3, (4, 5), (6, 7))) + raises(ValueError, lambda: + interpolating_poly(2, 3, (4, 5), (6, 7, 8))) + assert interpolating_poly(0, x, (1, 2), (3, 4)) == 0 + assert interpolating_poly(1, x, (1, 2), (3, 4)) == 3 + assert interpolating_poly(2, x, (1, 2), (3, 4)) == x + 2 + + +def test_fateman_poly_F_1(): + f, g, h = fateman_poly_F_1(1) + F, G, H = dmp_fateman_poly_F_1(1, ZZ) + + assert [ t.rep.to_list() for t in [f, g, h] ] == [F, G, H] + + f, g, h = fateman_poly_F_1(3) + F, G, H = dmp_fateman_poly_F_1(3, ZZ) + + assert [ t.rep.to_list() for t in [f, g, h] ] == [F, G, H] + + +def test_fateman_poly_F_2(): + f, g, h = fateman_poly_F_2(1) + F, G, H = dmp_fateman_poly_F_2(1, ZZ) + + assert [ t.rep.to_list() for t in [f, g, h] ] == [F, G, H] + + f, g, h = fateman_poly_F_2(3) + F, G, H = dmp_fateman_poly_F_2(3, ZZ) + + assert [ t.rep.to_list() for t in [f, g, h] ] == [F, G, H] + + +def test_fateman_poly_F_3(): + f, g, h = fateman_poly_F_3(1) + F, G, H = dmp_fateman_poly_F_3(1, ZZ) + + assert [ t.rep.to_list() for t in [f, g, h] ] == [F, G, H] + + f, g, h = fateman_poly_F_3(3) + F, G, H = dmp_fateman_poly_F_3(3, ZZ) + + assert [ t.rep.to_list() for t in [f, g, h] ] == [F, G, H] diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_sqfreetools.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_sqfreetools.py new file mode 100644 index 0000000000000000000000000000000000000000..b772a05a50e2eacd5a7c80352b1eadd52c69c3fa --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_sqfreetools.py @@ -0,0 +1,160 @@ +"""Tests for square-free decomposition algorithms and related tools. """ + +from sympy.polys.rings import ring +from sympy.polys.domains import FF, ZZ, QQ +from sympy.polys.specialpolys import f_polys + +from sympy.testing.pytest import raises +from sympy.external.gmpy import MPQ + +f_0, f_1, f_2, f_3, f_4, f_5, f_6 = f_polys() + +def test_dup_sqf(): + R, x = ring("x", ZZ) + + assert R.dup_sqf_part(0) == 0 + assert R.dup_sqf_p(0) is True + + assert R.dup_sqf_part(7) == 1 + assert R.dup_sqf_p(7) is True + + assert R.dup_sqf_part(2*x + 2) == x + 1 + assert R.dup_sqf_p(2*x + 2) is True + + assert R.dup_sqf_part(x**3 + x + 1) == x**3 + x + 1 + assert R.dup_sqf_p(x**3 + x + 1) is True + + assert R.dup_sqf_part(-x**3 + x + 1) == x**3 - x - 1 + assert R.dup_sqf_p(-x**3 + x + 1) is True + + assert R.dup_sqf_part(2*x**3 + 3*x**2) == 2*x**2 + 3*x + assert R.dup_sqf_p(2*x**3 + 3*x**2) is False + + assert R.dup_sqf_part(-2*x**3 + 3*x**2) == 2*x**2 - 3*x + assert R.dup_sqf_p(-2*x**3 + 3*x**2) is False + + assert R.dup_sqf_list(0) == (0, []) + assert R.dup_sqf_list(1) == (1, []) + + assert R.dup_sqf_list(x) == (1, [(x, 1)]) + assert R.dup_sqf_list(2*x**2) == (2, [(x, 2)]) + assert R.dup_sqf_list(3*x**3) == (3, [(x, 3)]) + + assert R.dup_sqf_list(-x**5 + x**4 + x - 1) == \ + (-1, [(x**3 + x**2 + x + 1, 1), (x - 1, 2)]) + assert R.dup_sqf_list(x**8 + 6*x**6 + 12*x**4 + 8*x**2) == \ + ( 1, [(x, 2), (x**2 + 2, 3)]) + + assert R.dup_sqf_list(2*x**2 + 4*x + 2) == (2, [(x + 1, 2)]) + + R, x = ring("x", QQ) + assert R.dup_sqf_list(2*x**2 + 4*x + 2) == (2, [(x + 1, 2)]) + + R, x = ring("x", FF(2)) + assert R.dup_sqf_list(x**2 + 1) == (1, [(x + 1, 2)]) + + R, x = ring("x", FF(3)) + assert R.dup_sqf_list(x**10 + 2*x**7 + 2*x**4 + x) == \ + (1, [(x, 1), + (x + 1, 3), + (x + 2, 6)]) + + R1, x = ring("x", ZZ) + R2, y = ring("y", FF(3)) + + f = x**3 + 1 + g = y**3 + 1 + + assert R1.dup_sqf_part(f) == f + assert R2.dup_sqf_part(g) == y + 1 + + assert R1.dup_sqf_p(f) is True + assert R2.dup_sqf_p(g) is False + + R, x, y = ring("x,y", ZZ) + + A = x**4 - 3*x**2 + 6 + D = x**6 - 5*x**4 + 5*x**2 + 4 + + f, g = D, R.dmp_sub(A, R.dmp_mul(R.dmp_diff(D, 1), y)) + res = R.dmp_resultant(f, g) + h = (4*y**2 + 1).drop(x) + + assert R.drop(x).dup_sqf_list(res) == (45796, [(h, 3)]) + + Rt, t = ring("t", ZZ) + R, x = ring("x", Rt) + assert R.dup_sqf_list_include(t**3*x**2) == [(t**3, 1), (x, 2)] + + +def test_dmp_sqf(): + R, x, y = ring("x,y", ZZ) + assert R.dmp_sqf_part(0) == 0 + assert R.dmp_sqf_p(0) is True + + assert R.dmp_sqf_part(7) == 1 + assert R.dmp_sqf_p(7) is True + + assert R.dmp_sqf_list(3) == (3, []) + assert R.dmp_sqf_list_include(3) == [(3, 1)] + + R, x, y, z = ring("x,y,z", ZZ) + assert R.dmp_sqf_p(f_0) is True + assert R.dmp_sqf_p(f_0**2) is False + assert R.dmp_sqf_p(f_1) is True + assert R.dmp_sqf_p(f_1**2) is False + assert R.dmp_sqf_p(f_2) is True + assert R.dmp_sqf_p(f_2**2) is False + assert R.dmp_sqf_p(f_3) is True + assert R.dmp_sqf_p(f_3**2) is False + assert R.dmp_sqf_p(f_5) is False + assert R.dmp_sqf_p(f_5**2) is False + + assert R.dmp_sqf_p(f_4) is True + assert R.dmp_sqf_part(f_4) == -f_4 + + assert R.dmp_sqf_part(f_5) == x + y - z + + R, x, y, z, t = ring("x,y,z,t", ZZ) + assert R.dmp_sqf_p(f_6) is True + assert R.dmp_sqf_part(f_6) == f_6 + + R, x = ring("x", ZZ) + f = -x**5 + x**4 + x - 1 + + assert R.dmp_sqf_list(f) == (-1, [(x**3 + x**2 + x + 1, 1), (x - 1, 2)]) + assert R.dmp_sqf_list_include(f) == [(-x**3 - x**2 - x - 1, 1), (x - 1, 2)] + + R, x, y = ring("x,y", ZZ) + f = -x**5 + x**4 + x - 1 + + assert R.dmp_sqf_list(f) == (-1, [(x**3 + x**2 + x + 1, 1), (x - 1, 2)]) + assert R.dmp_sqf_list_include(f) == [(-x**3 - x**2 - x - 1, 1), (x - 1, 2)] + + f = -x**2 + 2*x - 1 + assert R.dmp_sqf_list_include(f) == [(-1, 1), (x - 1, 2)] + + f = (y**2 + 1)**2*(x**2 + 2*x + 2) + assert R.dmp_sqf_p(f) is False + assert R.dmp_sqf_list(f) == (1, [(x**2 + 2*x + 2, 1), (y**2 + 1, 2)]) + + R, x, y = ring("x,y", FF(2)) + raises(NotImplementedError, lambda: R.dmp_sqf_list(y**2 + 1)) + + +def test_dup_gff_list(): + R, x = ring("x", ZZ) + + f = x**5 + 2*x**4 - x**3 - 2*x**2 + assert R.dup_gff_list(f) == [(x, 1), (x + 2, 4)] + + g = x**9 - 20*x**8 + 166*x**7 - 744*x**6 + 1965*x**5 - 3132*x**4 + 2948*x**3 - 1504*x**2 + 320*x + assert R.dup_gff_list(g) == [(x**2 - 5*x + 4, 1), (x**2 - 5*x + 4, 2), (x, 3)] + + raises(ValueError, lambda: R.dup_gff_list(0)) + +def test_issue_26178(): + R, x, y, z = ring(['x', 'y', 'z'], QQ) + assert (x**2 - 2*y**2 + 1).sqf_list() == (MPQ(1,1), [(x**2 - 2*y**2 + 1, 1)]) + assert (x**2 - 2*z**2 + 1).sqf_list() == (MPQ(1,1), [(x**2 - 2*z**2 + 1, 1)]) + assert (y**2 - 2*z**2 + 1).sqf_list() == (MPQ(1,1), [(y**2 - 2*z**2 + 1, 1)]) diff --git a/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_subresultants_qq_zz.py b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_subresultants_qq_zz.py new file mode 100644 index 0000000000000000000000000000000000000000..7f7560dfeaf93b20f7cf68cdc597c024cb519cca --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/polys/tests/test_subresultants_qq_zz.py @@ -0,0 +1,347 @@ +from sympy.core.symbol import Symbol +from sympy.polys.polytools import (pquo, prem, sturm, subresultants) +from sympy.matrices import Matrix +from sympy.polys.subresultants_qq_zz import (sylvester, res, res_q, res_z, bezout, + subresultants_sylv, modified_subresultants_sylv, + subresultants_bezout, modified_subresultants_bezout, + backward_eye, + sturm_pg, sturm_q, sturm_amv, euclid_pg, euclid_q, + euclid_amv, modified_subresultants_pg, subresultants_pg, + subresultants_amv_q, quo_z, rem_z, subresultants_amv, + modified_subresultants_amv, subresultants_rem, + subresultants_vv, subresultants_vv_2) + + +def test_sylvester(): + x = Symbol('x') + + assert sylvester(x**3 -7, 0, x) == sylvester(x**3 -7, 0, x, 1) == Matrix([[0]]) + assert sylvester(0, x**3 -7, x) == sylvester(0, x**3 -7, x, 1) == Matrix([[0]]) + assert sylvester(x**3 -7, 0, x, 2) == Matrix([[0]]) + assert sylvester(0, x**3 -7, x, 2) == Matrix([[0]]) + + assert sylvester(x**3 -7, 7, x).det() == sylvester(x**3 -7, 7, x, 1).det() == 343 + assert sylvester(7, x**3 -7, x).det() == sylvester(7, x**3 -7, x, 1).det() == 343 + assert sylvester(x**3 -7, 7, x, 2).det() == -343 + assert sylvester(7, x**3 -7, x, 2).det() == 343 + + assert sylvester(3, 7, x).det() == sylvester(3, 7, x, 1).det() == sylvester(3, 7, x, 2).det() == 1 + + assert sylvester(3, 0, x).det() == sylvester(3, 0, x, 1).det() == sylvester(3, 0, x, 2).det() == 1 + + assert sylvester(x - 3, x - 8, x) == sylvester(x - 3, x - 8, x, 1) == sylvester(x - 3, x - 8, x, 2) == Matrix([[1, -3], [1, -8]]) + + assert sylvester(x**3 - 7*x + 7, 3*x**2 - 7, x) == sylvester(x**3 - 7*x + 7, 3*x**2 - 7, x, 1) == Matrix([[1, 0, -7, 7, 0], [0, 1, 0, -7, 7], [3, 0, -7, 0, 0], [0, 3, 0, -7, 0], [0, 0, 3, 0, -7]]) + + assert sylvester(x**3 - 7*x + 7, 3*x**2 - 7, x, 2) == Matrix([ +[1, 0, -7, 7, 0, 0], [0, 3, 0, -7, 0, 0], [0, 1, 0, -7, 7, 0], [0, 0, 3, 0, -7, 0], [0, 0, 1, 0, -7, 7], [0, 0, 0, 3, 0, -7]]) + +def test_subresultants_sylv(): + x = Symbol('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + assert subresultants_sylv(p, q, x) == subresultants(p, q, x) + assert subresultants_sylv(p, q, x)[-1] == res(p, q, x) + assert subresultants_sylv(p, q, x) != euclid_amv(p, q, x) + amv_factors = [1, 1, -1, 1, -1, 1] + assert subresultants_sylv(p, q, x) == [i*j for i, j in zip(amv_factors, modified_subresultants_amv(p, q, x))] + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert subresultants_sylv(p, q, x) == euclid_amv(p, q, x) + +def test_modified_subresultants_sylv(): + x = Symbol('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + amv_factors = [1, 1, -1, 1, -1, 1] + assert modified_subresultants_sylv(p, q, x) == [i*j for i, j in zip(amv_factors, subresultants_amv(p, q, x))] + assert modified_subresultants_sylv(p, q, x)[-1] != res_q(p + x**8, q, x) + assert modified_subresultants_sylv(p, q, x) != sturm_amv(p, q, x) + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert modified_subresultants_sylv(p, q, x) == sturm_amv(p, q, x) + assert modified_subresultants_sylv(-p, q, x) != sturm_amv(-p, q, x) + +def test_res(): + x = Symbol('x') + + assert res(3, 5, x) == 1 + +def test_res_q(): + x = Symbol('x') + + assert res_q(3, 5, x) == 1 + +def test_res_z(): + x = Symbol('x') + + assert res_z(3, 5, x) == 1 + assert res(3, 5, x) == res_q(3, 5, x) == res_z(3, 5, x) + +def test_bezout(): + x = Symbol('x') + + p = -2*x**5+7*x**3+9*x**2-3*x+1 + q = -10*x**4+21*x**2+18*x-3 + assert bezout(p, q, x, 'bz').det() == sylvester(p, q, x, 2).det() + assert bezout(p, q, x, 'bz').det() != sylvester(p, q, x, 1).det() + assert bezout(p, q, x, 'prs') == backward_eye(5) * bezout(p, q, x, 'bz') * backward_eye(5) + +def test_subresultants_bezout(): + x = Symbol('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + assert subresultants_bezout(p, q, x) == subresultants(p, q, x) + assert subresultants_bezout(p, q, x)[-1] == sylvester(p, q, x).det() + assert subresultants_bezout(p, q, x) != euclid_amv(p, q, x) + amv_factors = [1, 1, -1, 1, -1, 1] + assert subresultants_bezout(p, q, x) == [i*j for i, j in zip(amv_factors, modified_subresultants_amv(p, q, x))] + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert subresultants_bezout(p, q, x) == euclid_amv(p, q, x) + +def test_modified_subresultants_bezout(): + x = Symbol('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + amv_factors = [1, 1, -1, 1, -1, 1] + assert modified_subresultants_bezout(p, q, x) == [i*j for i, j in zip(amv_factors, subresultants_amv(p, q, x))] + assert modified_subresultants_bezout(p, q, x)[-1] != sylvester(p + x**8, q, x).det() + assert modified_subresultants_bezout(p, q, x) != sturm_amv(p, q, x) + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert modified_subresultants_bezout(p, q, x) == sturm_amv(p, q, x) + assert modified_subresultants_bezout(-p, q, x) != sturm_amv(-p, q, x) + +def test_sturm_pg(): + x = Symbol('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + assert sturm_pg(p, q, x)[-1] != sylvester(p, q, x, 2).det() + sam_factors = [1, 1, -1, -1, 1, 1] + assert sturm_pg(p, q, x) == [i*j for i,j in zip(sam_factors, euclid_pg(p, q, x))] + + p = -9*x**5 - 5*x**3 - 9 + q = -45*x**4 - 15*x**2 + assert sturm_pg(p, q, x, 1)[-1] == sylvester(p, q, x, 1).det() + assert sturm_pg(p, q, x)[-1] != sylvester(p, q, x, 2).det() + assert sturm_pg(-p, q, x)[-1] == sylvester(-p, q, x, 2).det() + assert sturm_pg(-p, q, x) == modified_subresultants_pg(-p, q, x) + +def test_sturm_q(): + x = Symbol('x') + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert sturm_q(p, q, x) == sturm(p) + assert sturm_q(-p, -q, x) != sturm(-p) + + +def test_sturm_amv(): + x = Symbol('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + assert sturm_amv(p, q, x)[-1] != sylvester(p, q, x, 2).det() + sam_factors = [1, 1, -1, -1, 1, 1] + assert sturm_amv(p, q, x) == [i*j for i,j in zip(sam_factors, euclid_amv(p, q, x))] + + p = -9*x**5 - 5*x**3 - 9 + q = -45*x**4 - 15*x**2 + assert sturm_amv(p, q, x, 1)[-1] == sylvester(p, q, x, 1).det() + assert sturm_amv(p, q, x)[-1] != sylvester(p, q, x, 2).det() + assert sturm_amv(-p, q, x)[-1] == sylvester(-p, q, x, 2).det() + assert sturm_pg(-p, q, x) == modified_subresultants_pg(-p, q, x) + + +def test_euclid_pg(): + x = Symbol('x') + + p = x**6+x**5-x**4-x**3+x**2-x+1 + q = 6*x**5+5*x**4-4*x**3-3*x**2+2*x-1 + assert euclid_pg(p, q, x)[-1] == sylvester(p, q, x).det() + assert euclid_pg(p, q, x) == subresultants_pg(p, q, x) + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + assert euclid_pg(p, q, x)[-1] != sylvester(p, q, x, 2).det() + sam_factors = [1, 1, -1, -1, 1, 1] + assert euclid_pg(p, q, x) == [i*j for i,j in zip(sam_factors, sturm_pg(p, q, x))] + + +def test_euclid_q(): + x = Symbol('x') + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert euclid_q(p, q, x)[-1] == -sturm(p)[-1] + + +def test_euclid_amv(): + x = Symbol('x') + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert euclid_amv(p, q, x)[-1] == sylvester(p, q, x).det() + assert euclid_amv(p, q, x) == subresultants_amv(p, q, x) + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + assert euclid_amv(p, q, x)[-1] != sylvester(p, q, x, 2).det() + sam_factors = [1, 1, -1, -1, 1, 1] + assert euclid_amv(p, q, x) == [i*j for i,j in zip(sam_factors, sturm_amv(p, q, x))] + + +def test_modified_subresultants_pg(): + x = Symbol('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + amv_factors = [1, 1, -1, 1, -1, 1] + assert modified_subresultants_pg(p, q, x) == [i*j for i, j in zip(amv_factors, subresultants_pg(p, q, x))] + assert modified_subresultants_pg(p, q, x)[-1] != sylvester(p + x**8, q, x).det() + assert modified_subresultants_pg(p, q, x) != sturm_pg(p, q, x) + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert modified_subresultants_pg(p, q, x) == sturm_pg(p, q, x) + assert modified_subresultants_pg(-p, q, x) != sturm_pg(-p, q, x) + + +def test_subresultants_pg(): + x = Symbol('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + assert subresultants_pg(p, q, x) == subresultants(p, q, x) + assert subresultants_pg(p, q, x)[-1] == sylvester(p, q, x).det() + assert subresultants_pg(p, q, x) != euclid_pg(p, q, x) + amv_factors = [1, 1, -1, 1, -1, 1] + assert subresultants_pg(p, q, x) == [i*j for i, j in zip(amv_factors, modified_subresultants_amv(p, q, x))] + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert subresultants_pg(p, q, x) == euclid_pg(p, q, x) + + +def test_subresultants_amv_q(): + x = Symbol('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + assert subresultants_amv_q(p, q, x) == subresultants(p, q, x) + assert subresultants_amv_q(p, q, x)[-1] == sylvester(p, q, x).det() + assert subresultants_amv_q(p, q, x) != euclid_amv(p, q, x) + amv_factors = [1, 1, -1, 1, -1, 1] + assert subresultants_amv_q(p, q, x) == [i*j for i, j in zip(amv_factors, modified_subresultants_amv(p, q, x))] + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert subresultants_amv(p, q, x) == euclid_amv(p, q, x) + + +def test_rem_z(): + x = Symbol('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + assert rem_z(p, -q, x) != prem(p, -q, x) + +def test_quo_z(): + x = Symbol('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + assert quo_z(p, -q, x) != pquo(p, -q, x) + + y = Symbol('y') + q = 3*x**6 + 5*y**4 - 4*x**2 - 9*x + 21 + assert quo_z(p, -q, x) == pquo(p, -q, x) + +def test_subresultants_amv(): + x = Symbol('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + assert subresultants_amv(p, q, x) == subresultants(p, q, x) + assert subresultants_amv(p, q, x)[-1] == sylvester(p, q, x).det() + assert subresultants_amv(p, q, x) != euclid_amv(p, q, x) + amv_factors = [1, 1, -1, 1, -1, 1] + assert subresultants_amv(p, q, x) == [i*j for i, j in zip(amv_factors, modified_subresultants_amv(p, q, x))] + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert subresultants_amv(p, q, x) == euclid_amv(p, q, x) + + +def test_modified_subresultants_amv(): + x = Symbol('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + amv_factors = [1, 1, -1, 1, -1, 1] + assert modified_subresultants_amv(p, q, x) == [i*j for i, j in zip(amv_factors, subresultants_amv(p, q, x))] + assert modified_subresultants_amv(p, q, x)[-1] != sylvester(p + x**8, q, x).det() + assert modified_subresultants_amv(p, q, x) != sturm_amv(p, q, x) + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert modified_subresultants_amv(p, q, x) == sturm_amv(p, q, x) + assert modified_subresultants_amv(-p, q, x) != sturm_amv(-p, q, x) + + +def test_subresultants_rem(): + x = Symbol('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + assert subresultants_rem(p, q, x) == subresultants(p, q, x) + assert subresultants_rem(p, q, x)[-1] == sylvester(p, q, x).det() + assert subresultants_rem(p, q, x) != euclid_amv(p, q, x) + amv_factors = [1, 1, -1, 1, -1, 1] + assert subresultants_rem(p, q, x) == [i*j for i, j in zip(amv_factors, modified_subresultants_amv(p, q, x))] + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert subresultants_rem(p, q, x) == euclid_amv(p, q, x) + + +def test_subresultants_vv(): + x = Symbol('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + assert subresultants_vv(p, q, x) == subresultants(p, q, x) + assert subresultants_vv(p, q, x)[-1] == sylvester(p, q, x).det() + assert subresultants_vv(p, q, x) != euclid_amv(p, q, x) + amv_factors = [1, 1, -1, 1, -1, 1] + assert subresultants_vv(p, q, x) == [i*j for i, j in zip(amv_factors, modified_subresultants_amv(p, q, x))] + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert subresultants_vv(p, q, x) == euclid_amv(p, q, x) + + +def test_subresultants_vv_2(): + x = Symbol('x') + + p = x**8 + x**6 - 3*x**4 - 3*x**3 + 8*x**2 + 2*x - 5 + q = 3*x**6 + 5*x**4 - 4*x**2 - 9*x + 21 + assert subresultants_vv_2(p, q, x) == subresultants(p, q, x) + assert subresultants_vv_2(p, q, x)[-1] == sylvester(p, q, x).det() + assert subresultants_vv_2(p, q, x) != euclid_amv(p, q, x) + amv_factors = [1, 1, -1, 1, -1, 1] + assert subresultants_vv_2(p, q, x) == [i*j for i, j in zip(amv_factors, modified_subresultants_amv(p, q, x))] + + p = x**3 - 7*x + 7 + q = 3*x**2 - 7 + assert subresultants_vv_2(p, q, x) == euclid_amv(p, q, x) diff --git a/.venv/lib/python3.13/site-packages/sympy/strategies/branch/tests/__init__.py b/.venv/lib/python3.13/site-packages/sympy/strategies/branch/tests/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/.venv/lib/python3.13/site-packages/sympy/strategies/branch/tests/test_core.py b/.venv/lib/python3.13/site-packages/sympy/strategies/branch/tests/test_core.py new file mode 100644 index 0000000000000000000000000000000000000000..ac620b0afb6dbadc4d97b29ddbb341cd920b6588 --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/strategies/branch/tests/test_core.py @@ -0,0 +1,117 @@ +from sympy.strategies.branch.core import ( + exhaust, debug, multiplex, condition, notempty, chain, onaction, sfilter, + yieldify, do_one, identity) + + +def posdec(x): + if x > 0: + yield x - 1 + else: + yield x + + +def branch5(x): + if 0 < x < 5: + yield x - 1 + elif 5 < x < 10: + yield x + 1 + elif x == 5: + yield x + 1 + yield x - 1 + else: + yield x + + +def even(x): + return x % 2 == 0 + + +def inc(x): + yield x + 1 + + +def one_to_n(n): + yield from range(n) + + +def test_exhaust(): + brl = exhaust(branch5) + assert set(brl(3)) == {0} + assert set(brl(7)) == {10} + assert set(brl(5)) == {0, 10} + + +def test_debug(): + from io import StringIO + file = StringIO() + rl = debug(posdec, file) + list(rl(5)) + log = file.getvalue() + file.close() + + assert posdec.__name__ in log + assert '5' in log + assert '4' in log + + +def test_multiplex(): + brl = multiplex(posdec, branch5) + assert set(brl(3)) == {2} + assert set(brl(7)) == {6, 8} + assert set(brl(5)) == {4, 6} + + +def test_condition(): + brl = condition(even, branch5) + assert set(brl(4)) == set(branch5(4)) + assert set(brl(5)) == set() + + +def test_sfilter(): + brl = sfilter(even, one_to_n) + assert set(brl(10)) == {0, 2, 4, 6, 8} + + +def test_notempty(): + def ident_if_even(x): + if even(x): + yield x + + brl = notempty(ident_if_even) + assert set(brl(4)) == {4} + assert set(brl(5)) == {5} + + +def test_chain(): + assert list(chain()(2)) == [2] # identity + assert list(chain(inc, inc)(2)) == [4] + assert list(chain(branch5, inc)(4)) == [4] + assert set(chain(branch5, inc)(5)) == {5, 7} + assert list(chain(inc, branch5)(5)) == [7] + + +def test_onaction(): + L = [] + + def record(fn, input, output): + L.append((input, output)) + + list(onaction(inc, record)(2)) + assert L == [(2, 3)] + + list(onaction(identity, record)(2)) + assert L == [(2, 3)] + + +def test_yieldify(): + yinc = yieldify(lambda x: x + 1) + assert list(yinc(3)) == [4] + + +def test_do_one(): + def bad(expr): + raise ValueError + + assert list(do_one(inc)(3)) == [4] + assert list(do_one(inc, bad)(3)) == [4] + assert list(do_one(inc, posdec)(3)) == [4] diff --git a/.venv/lib/python3.13/site-packages/sympy/strategies/branch/tests/test_tools.py b/.venv/lib/python3.13/site-packages/sympy/strategies/branch/tests/test_tools.py new file mode 100644 index 0000000000000000000000000000000000000000..c2bd224030c337f0a000d94f6e7e65f3b8bd118f --- /dev/null +++ b/.venv/lib/python3.13/site-packages/sympy/strategies/branch/tests/test_tools.py @@ -0,0 +1,42 @@ +from sympy.strategies.branch.tools import canon +from sympy.core.basic import Basic +from sympy.core.numbers import Integer +from sympy.core.singleton import S + + +def posdec(x): + if isinstance(x, Integer) and x > 0: + yield x - 1 + else: + yield x + + +def branch5(x): + if isinstance(x, Integer): + if 0 < x < 5: + yield x - 1 + elif 5 < x < 10: + yield x + 1 + elif x == 5: + yield x + 1 + yield x - 1 + else: + yield x + + +def test_zero_ints(): + expr = Basic(S(2), Basic(S(5), S(3)), S(8)) + expected = {Basic(S(0), Basic(S(0), S(0)), S(0))} + + brl = canon(posdec) + assert set(brl(expr)) == expected + + +def test_split5(): + expr = Basic(S(2), Basic(S(5), S(3)), S(8)) + expected = { + Basic(S(0), Basic(S(0), S(0)), S(10)), + Basic(S(0), Basic(S(10), S(0)), S(10))} + + brl = canon(branch5) + assert set(brl(expr)) == expected