-
Notifications
You must be signed in to change notification settings - Fork 145
Description
Let me first say that I was very happy to find the capabilities of Arb within Nemo and Oscar!
I have one application in mind where I need to track roots of a univariate polynomial f(u) for varying coefficients a_i of f. It lies in the nature of my problem that the limit polynomial f -> f_1 has at least one multiple root. I learned from the Arb documentation that internally, the Durand-Kerner method is used. According to this article, it is possible to obtain reasonable convergence also in this degenerate case.
Playing around with the Arb methods, I found that the error bounds for the returned list of roots of f becomes significantly worse, once f approaches f_1 which has a multiple root. Usually, one would circumvent this problem by making f square free. But in my setting, it will be most convenient to have the input in inexact form, i.e. f_1 is not an exact polynomial with a multiple root, but rather an Arb-polynomial with an 'approximate multiple root'. Therefore, doing exact division beforehand is not a feasible way to go.
Question: Can we modify the implementation of the root-finding algorithm in such a way that it returns a list of roots with higher precision in the degenerate cases? It already returns a list of roots, making no claims about their multiplicities. And this is fine for me; it's only that the precision really seems to go down.
The following is a sample code in julia using Oscar, derived from @JHanselman's work.
using Oscar
function recursive_continuation(f::acb_poly, g::acb_poly, r::Vector{acb})
P = parent(f)
P === parent(g) || error("polynomials must belong to the same parent ring")
z = gens(P)[1]
CC = coefficient_ring(P)
prec = precision(CC)
n = degree(f)
degree(g) == n || error("number of coefficients must be equal to the degree of the polynomial")
length(r) == n || error("number of approximate roots must be equal to the degree of the polynomial")
temp_vec = Nemo.acb_vec(n)
temp_vec_res = Nemo.acb_vec(n)
# The following is an error estimation from Neurohr's thesis.
d = reduce(min, [abs(r[i] - r[j]) for (i, j) in filter(t-> t[1] != t[2], [a for a in Iterators.product((1:n), (1:n))])])
W = [ f(r[i]) // prod([r[i] - r[j] for j in vcat((1:i - 1), i+1:n)];init = one(CC)) for i in (1:n)]
w = reduce(max, map(t -> real(t)^2 + imag(t)^2, W))
if w < d // (2*n)
temp_vec = Nemo.acb_vec(copy(r)) # TODO: Why does fill! not work?
dd = ccall((:acb_poly_find_roots, Nemo.libarb), Cint, (Ptr{Nemo.acb_struct}, Ref{acb_poly}, Ptr{Nemo.acb_struct}, Int, Int), temp_vec_res, g, temp_vec, 0, prec)
dd == n || println("number of roots has dropped to $dd")
result = similar(r)
result .= Nemo.array(CC, temp_vec_res, n)
Nemo.acb_vec_clear(temp_vec, n)
Nemo.acb_vec_clear(temp_vec_res, n)
return result
else
h = CC(1//2).*(f+g)
return recursive_continuation(h, g, recursive_continuation(f, h, r))
end
end
prec = 128
CC = ComplexField(prec)
d = 5
P, a = PolynomialRing(QQ, push!(["a$i" for i in 0:d-1], "z"))
z = pop!(a)
F = sum([a[i]*z^(i-1) for i in 1:d]) + z^d
v = [CC(i) for i in 1:d]
CCu, u = CC["u"]
f = evaluate(F, push!(CCu.(v), u))
r = roots(f)
w = CC.([20, -10, 5, 25, -3])
g = u^d + sum([w[i]*u^(i-1) for i in 1:d])
@show r2 = recursive_continuation(f, g, r) # This produces good results!
g = u^d - u^(d-1)
@show recursive_continuation(f, g, r) # This produces worse results.