Skip to content

Commit

Permalink
refactor: evaluate/subst for univariate polynomials
Browse files Browse the repository at this point in the history
- rename `subst` to `evaluate_brent_kung` and add a hard alias
  for backwards compatibility
- introduce `evaluate_horner`
- use in-place operations where possible
  • Loading branch information
thofma committed Dec 25, 2024
1 parent 9843ed5 commit 9195527
Showing 1 changed file with 68 additions and 56 deletions.
124 changes: 68 additions & 56 deletions src/Poly.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2156,29 +2156,89 @@ end
###############################################################################

@doc raw"""
evaluate(a::PolyRingElem, b::T) where T <: RingElement
evaluate(a::PolyRingElem, b::RingElement)
Evaluate the polynomial expression $a$ at the value $b$ and return the result.
"""
function evaluate(a::PolyRingElem, b::T) where T <: RingElement
function evaluate(a::PolyRingElem, b::NCRingElement)

Check warning on line 2163 in src/Poly.jl

View check run for this annotation

Codecov / codecov/patch

src/Poly.jl#L2163

Added line #L2163 was not covered by tests
i = length(a)
R = base_ring(a)
S = parent(b)
if i == 0
return zero(R) + zero(S)
end
if i > 25
return subst(a, b)
return evaluate_brent_kung(a, b)
end
return evaluate_horner(a, b)

Check warning on line 2173 in src/Poly.jl

View check run for this annotation

Codecov / codecov/patch

src/Poly.jl#L2173

Added line #L2173 was not covered by tests
end

function evaluate_horner(a::PolyRingElem, b)
i = length(a)
R = base_ring(a)
S = parent(b)
if i == 0
return zero(R) + zero(S)
end
z = coeff(a, i - 1) * one(S)
while i > 1
i -= 1
z = coeff(a, i - 1) + z*b
parent(z) # To work around a bug in julia
z = mul!(z, z, b)
z = add!(z, z, coeff(a, i - 1))
end
return z
end

# uses the algorithm of "A Practical Univariate Polynomial Composition Algorithm"
# this is asymptotically faster, if scalar multiplications are cheaper than
# base ring multiplications
function evaluate_brent_kung(f::PolyRingElem{T}, a::U) where {T <: RingElement, U <: NCRingElement}
S = parent(a)
n = degree(f)
R = base_ring(f)
if n < 0
return zero(S) + zero(R)
elseif n == 0
return coeff(f, 0)*one(S)
elseif n == 1
return coeff(f, 0)*one(S) + coeff(f, 1)*a
end
d1 = isqrt(n)

Check warning on line 2206 in src/Poly.jl

View check run for this annotation

Codecov / codecov/patch

src/Poly.jl#L2203-L2206

Added lines #L2203 - L2206 were not covered by tests
d = div(n, d1)

Check warning on line 2208 in src/Poly.jl

View check run for this annotation

Codecov / codecov/patch

src/Poly.jl#L2208

Added line #L2208 was not covered by tests
if (U <: Integer && U != BigInt) ||
(U <: Rational && U != Rational{BigInt})
c = zero(R)*zero(U)
V = typeof(c)
if U != V
A = powers(map(parent(c), a), d)
else
A = powers(a, d)
end
else
A = powers(a, d)
end

s = coeff(f, d1*d)*A[1]
for j = 1:min(n - d1*d, d - 1)
c = coeff(f, d1*d + j)
if !iszero(c)
s = addmul!(s, c, A[j + 1])
end
end
for i = 1:d1
s = mul!(s, s, A[d + 1])
s = addmul!(s, coeff(f, (d1 - i)*d), A[1])
for j = 1:min(n - (d1 - i)*d, d - 1)
c = coeff(f, (d1 - i)*d + j)
if !iszero(c)
s = addmul!(s, c, A[j + 1])
end
end
end
return s
end

@doc raw"""
compose(f::PolyRingElem, g::PolyRingElem; inner)
Expand Down Expand Up @@ -2212,14 +2272,9 @@ function _compose_right(a::PolyRingElem, b::PolyRingElem)
return zero(R) + zero(S)
end
if i*length(b) > 25
return subst(a, b)
end
z = coeff(a, i - 1) * one(S)
while i > 1
i -= 1
z = z*b + coeff(a, i - 1)
return evaluate_brent_kung(a, b)
end
return z
return evaluate_horner(a, b)
end

###############################################################################
Expand Down Expand Up @@ -3386,50 +3441,7 @@ Evaluate the polynomial $f$ at $a$. Note that $a$ can be anything, whether
a ring element or not.
"""
function subst(f::PolyRingElem{T}, a::U) where {T <: RingElement, U}
S = parent(a)
n = degree(f)
R = base_ring(f)
if n < 0
return zero(S) + zero(R)
elseif n == 0
return coeff(f, 0)*one(S)
elseif n == 1
return coeff(f, 0)*one(S) + coeff(f, 1)*a
end
d1 = isqrt(n)
d = div(n, d1)

if (U <: Integer && U != BigInt) ||
(U <: Rational && U != Rational{BigInt})
c = zero(R)*zero(U)
V = typeof(c)
if U != V
A = powers(map(parent(c), a), d)
else
A = powers(a, d)
end
else
A = powers(a, d)
end

s = coeff(f, d1*d)*A[1]
for j = 1:min(n - d1*d, d - 1)
c = coeff(f, d1*d + j)
if !iszero(c)
s += c*A[j + 1]
end
end
for i = 1:d1
s *= A[d + 1]
s += coeff(f, (d1 - i)*d)*A[1]
for j = 1:min(n - (d1 - i)*d, d - 1)
c = coeff(f, (d1 - i)*d + j)
if !iszero(c)
s += c*A[j + 1]
end
end
end
return s
return evaluate_brent_kung(f, a)
end

###############################################################################
Expand Down

0 comments on commit 9195527

Please sign in to comment.