Complex root isolation for square-free polynomials

Real coefficients

We isolate the roots of the polynomial $x^3 - 1$:

julia> roots, is_real, nbreals = isolate_roots([-1, 0, 0, 1])
(Complex{BigFloat}[1.0 + 0.0im, -0.5 + 0.8660254037844386467637231707529184425210602961357559512227876838408313631134661im, -0.5 - 0.8660254037844386467637231707529184425210602961357559512227876838408313631134661im], Bool[1, 0, 0], 1)

Optional output type with keyword parameter acb:

  • acb=false (default): roots are returned as Complex{BigFloat} (midpoints).
  • acb=true: roots are returned as Nemo.ComplexFieldElem (balls with error radii).
julia> roots, is_real, nbreals = isolate_roots([-1, 0, 0, 1], acb=true)
(Nemo.ComplexFieldElem[[1.00000000000000000 +/- 6.94e-18], [-0.50000000000000000000 +/- 1e-25] + [0.86602540378443864676 +/- 3.73e-21]*im, [-0.50000000000000000000 +/- 1e-25] + [-0.86602540378443864676 +/- 3.73e-21]*im], Bool[1, 0, 0], 1)

Optional precision: keyword log2eps (default -53) is the log of the desired relative precision passed to the solver. When acb=false, BigFloat uses 53 bits of precision ($-log2eps = 53$). When acb=true, each root ball’s coordinates have accuracy_bits equal to 53 under the default log2eps (see accuracy_bits in Nemo).

Optional search domain: domain_num and domain_den (each a length-4 BigInt vector) give a rectangle in the complex plane as rationals: lower real, upper real, lower imaginary, upper imaginary, with domain_num[i]//domain_den[i] for each bound. The default is all zeros, which means search the whole complex plane.

Complex coefficients

We isolate the roots of $(1+i)x^2 - 2x + (1-i) = 0$. The same optional parameters apply as above: log2eps, acb, domain_num and domain_den.

julia> roots = isolate_roots_complex([1, -2, 1], [-1, 0, 1])
2-element Vector{Complex{BigFloat}}:
 0.0 - 1.0im
 1.0 + 0.0im

Complex root approximation for non-square-free polynomials

Real coefficients

We approximate roots of $(x-1)^2(x+2) = x^3 - 3x + 2$ (clusters may have multiplicities). The same optional parameters apply as for isolate_roots: log2eps, acb, domain_num / domain_den, and nbMroots (maximum number of root clusters to find; -1 means all).

julia> roots, mults, is_real, nbreals = approximate_roots([2, -3, 0, 1])
(Complex{BigFloat}[-2.0 + 0.0im, 1.0 + 0.0im], [1, 2], Bool[1, 0], 1)

Complex coefficients

We approximate roots of $(x-i)^2 = x^2 - 2ix - 1$. The same optional parameters apply as for isolate_roots_complex: log2eps, acb, domain_num / domain_den, and nbMroots.

julia> roots, mults = approximate_roots_complex([-1, 0, 1], [0, -2, 0])
(Complex{BigFloat}[0.0 + 1.0im], [2])

Complex root refinement

Real coefficients

We refine roots of $3x^3 - 1$ inside the box $[0,2] \times [-1/2, 1/2]$ in the complex plane (real $\times$ imaginary). You must pass a finite rectangle via domain_num and domain_den (same encoding as in isolate_roots), the number of roots in that box counting multiplicity (nbMroots), and optionally epsilon (default -53, same role as log2eps for precision) and acb. When acb=false, BigFloat precision is temporarily set to -epsilon bits during conversion, then restored.

julia> roots, mults, is_real, nbreals = refine_roots([-1, 0, 0, 3];
           epsilon=-100, nbMroots=1,
           domain_num=BigInt[0, 2, -1, 1],
           domain_den=BigInt[1, 1, 2, 2])
(Complex{BigFloat}[0.69336127435063470484335227478632 + 0.0im], [1], Bool[1], 1)

Complex coefficients

We refine roots of $i x^2 + 1$ inside the box $[0,1] \times [0,1]$ (real $\times$ imaginary). As for refine_roots, you pass domain_num, domain_den, nbMroots, and optionally epsilon and acb. When acb=false, BigFloat precision is temporarily set to -epsilon bits during conversion, then restored.

julia> roots, mults = refine_roots_complex([1, 0, 0], [0, 0, 1];
           epsilon=-53, nbMroots=1,
           domain_num=BigInt[0, 1, 0, 1],
           domain_den=BigInt[1, 1, 1, 1], acb=true)
(Nemo.ComplexFieldElem[[0.70710678118654752440 +/- 8.45e-22] + [0.70710678118654752440 +/- 8.45e-22]*im], [1])

Multipoint polynomial evaluation

Real coefficients

We evaluate $p(x) = 1 + 2x + 3x^2$ at several complex points. Optional keyword precision (default 53) sets the target relative precision in bits passed to the backend. The points vector may be Complex{BigFloat} or Nemo.ComplexFieldElem; in the latter case you can use exact points or Arb complex balls (ACB).

julia> using Nemo

julia> CC = Nemo.ComplexField();

julia> coeffs = [1, 2, 3];

julia> points = [CC(1, 0), CC(2, 0), CC(0.5, 0.5)];

julia> vals = evaluate_polynomial(coeffs, points)
3-element Vector{ComplexFieldElem}:
 [6.0000000000000000000 +/- 1e-24] + [+/- 9.25e-33]*im
 [17.000000000000000000 +/- 1e-23] + [+/- 3.70e-32]*im
 [2.0000000000000000000 +/- 1e-24] + [2.5000000000000000000 +/- 1e-24]*im

You can pass ACB roots produced in the earlier—for example from refine_roots_complex with acb=true:

julia> roots, _ = refine_roots_complex([1, 0, 0], [0, 0, 1];
           epsilon=-53, nbMroots=1,
           domain_num=BigInt[0, 1, 0, 1],
           domain_den=BigInt[1, 1, 1, 1], acb=true)
(Nemo.ComplexFieldElem[[0.70710678118654752440 +/- 8.45e-22] + [0.70710678118654752440 +/- 8.45e-22]*im], [1])

julia> vals = evaluate_polynomial([1, 2, 3], roots)
1-element Vector{Nemo.ComplexFieldElem}:
 [2.4142135623730950488 +/- 1.69e-21] + [4.4142135623730950488 +/- 1.69e-21]*im

Complex coefficients

We evaluate $p(x) = (1 + x^2) + i x = 1 + i x + x^2$ at $1+i$. The same optional precision applies, and points may be Complex{BigFloat} or Nemo.ComplexFieldElem (ACB balls).

julia> using Nemo

julia> coeffs_re = [1, 0, 1];

julia> coeffs_im = [0, 1, 0];

julia> CC = Nemo.ComplexField();

julia> vals = evaluate_polynomial_complex(coeffs_re, coeffs_im, [CC(1, 1)])
1-element Vector{ComplexFieldElem}:
 [+/- 9.25e-33] + [3.0000000000000000000 +/- 1e-24]*im