Skip to content

Commit

Permalink
Optimize map_coefficients, change_base_ring for some inputs (#1994)
Browse files Browse the repository at this point in the history
Namely if the input polynomial is a `QQMPolyRingElem` and the
transformation "function" is `ZZ` resp. a `fpField`.
  • Loading branch information
fingolfin authored Feb 25, 2025
1 parent f23f1f3 commit 1945331
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 19 deletions.
15 changes: 0 additions & 15 deletions src/HeckeMoreStuff.jl
Original file line number Diff line number Diff line change
Expand Up @@ -182,21 +182,6 @@ function Base.hash(f::zzModMPolyRingElem, h::UInt)
return UInt(1) # TODO: enhance or throw error
end

function AbstractAlgebra.map_coefficients(F::fpField, f::QQMPolyRingElem; parent=polynomial_ring(F, nvars(parent(f)), cached=false)[1])
dF = denominator(f)
d = F(dF)
if iszero(d)
error("Denominator divisible by p!")
end
m = inv(d)
ctx = MPolyBuildCtx(parent)
for x in zip(coefficients(f), exponent_vectors(f))
el = numerator(x[1] * dF)
push_term!(ctx, F(el) * m, x[2])
end
return finish(ctx)
end

function tdivpow2!(B::ZZMatrix, t::Int)
@ccall libflint.fmpz_mat_scalar_tdiv_q_2exp(B::Ref{ZZMatrix}, B::Ref{ZZMatrix}, t::Cint)::Nothing
end
Expand Down
8 changes: 4 additions & 4 deletions src/flint/FlintTypes.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1008,8 +1008,8 @@ const flint_orderings = [:lex, :deglex, :degrevlex]

@attributes mutable struct ZZMPolyRing <: MPolyRing{ZZRingElem}
nvars::Int
nfields::Cint
ord::Int
nfields::Int
ord::Cint
deg::Cint
rev::Cint
lut::NTuple{Base.GMP.BITS_PER_LIMB, Int}
Expand Down Expand Up @@ -1136,8 +1136,8 @@ end

@attributes mutable struct QQMPolyRing <: MPolyRing{QQFieldElem}
nvars::Int
nfields::Cint
ord::Int
nfields::Int
ord::Cint
deg::Cint
rev::Cint
lut::NTuple{Base.GMP.BITS_PER_LIMB, Int}
Expand Down
70 changes: 70 additions & 0 deletions src/flint/fmpq_mpoly.jl
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,11 @@ end
#
###############################################################################

_content_ptr(c::QQMPolyRingElem) = Ptr{QQFieldElem}(pointer_from_objref(c))
_content_ptr(c::Ptr{QQMPolyRingElem}) = Ptr{QQFieldElem}(c)
_content_ptr(c::Ref{QQMPolyRingElem}) = _content_ptr(c[])
_zpoly_ptr(c::QQMPolyRingElemOrPtr) = Ptr{ZZMPolyRingElem}(_content_ptr(c) + sizeof(QQFieldElem))

function zero!(a::QQMPolyRingElem)
@ccall libflint.fmpq_mpoly_zero(a::Ref{QQMPolyRingElem}, a.parent::Ref{QQMPolyRing})::Nothing
return a
Expand Down Expand Up @@ -748,6 +753,20 @@ sub!(a::QQMPolyRingElem, b::RationalUnion, c::QQMPolyRingElem) = neg!(sub!(a, c,
mul!(a::QQMPolyRingElem, b::QQMPolyRingElem, c::RationalUnion) = mul!(a, b, flintify(c))
mul!(a::QQMPolyRingElem, b::RationalUnion, c::QQMPolyRingElem) = mul!(a, c, b)

# special: multiply a QQMPolyRingElem by an integer and store the result as a ZZMPolyRingElem.
# obviously this only works if the integers is a multiple of the denominator of the polynomial
function mul!(a::ZZMPolyRingElem, b::QQMPolyRingElem, c::IntegerUnion)
x = QQFieldElem()
GC.@preserve b x begin
x = mul!(x, _content_ptr(b), c)
bp = _zpoly_ptr(b)
xp = _num_ptr(x)
@ccall libflint.fmpz_mpoly_scalar_mul_fmpz(a::Ref{ZZMPolyRingElem}, bp::Ref{ZZMPolyRingElem}, xp::Ref{ZZRingElem}, parent(a)::Ref{ZZMPolyRing})::Nothing
end
return a
end


divexact!(a::QQMPolyRingElem, b::QQMPolyRingElem, c::RationalUnion) = divexact!(a, b, flintify(c))

# Set the n-th coefficient of a to c. If zero coefficients are inserted, they
Expand Down Expand Up @@ -1047,3 +1066,54 @@ function (R::QQMPolyRing)(a::Vector{Any}, b::Vector{Vector{T}}) where T

return R(newaa, newbb)
end

###############################################################################
#
# Changing base ring, mapping coefficients
#
###############################################################################

function map_coefficients(R::ZZRing, f::QQMPolyRingElem;
cached::Bool = true,
parent::ZZMPolyRing = AbstractAlgebra._change_mpoly_ring(R, parent(f), cached))
@req isinteger(_content_ptr(f)) "input polynomial must have integral coefficients"
@req ngens(parent) == ngens(Nemo.parent(f)) "parents must have matching numbers of generators"
if internal_ordering(parent) == internal_ordering(Nemo.parent(f))
return mul!(zero(parent), f, 1)
end

ctx = MPolyBuildCtx(parent)
for (c, ev) in zip(coefficients(f), exponent_vectors(f))
push_term!(ctx, ZZ(c), ev)
end
return finish(ctx)
end

function map_coefficients(F::fpField, f::QQMPolyRingElem;
cached::Bool = true,
parent::MPolyRing = AbstractAlgebra._change_mpoly_ring(F, parent(f), cached))
dF = denominator(f)
d = F(dF)
@req !iszero(d) "Denominator divisible by p!"
m = inv(d)
ctx = MPolyBuildCtx(parent)
# TODO: rewrite this code using `_zpoly_ptr(f)`: convert that into the desired
# element, then multiply the result once by m at the end
for (c, ev) in zip(coefficients(f), exponent_vectors(f))
el = numerator(c * dF)
push_term!(ctx, F(el) * m, ev)
end
return finish(ctx)
end

function change_base_ring(R::ZZRing, f::QQMPolyRingElem;
cached::Bool = true,
parent::ZZMPolyRing = AbstractAlgebra._change_mpoly_ring(R, parent(f), cached))
return map_coefficients(R, f; cached, parent)
end

function change_base_ring(F::fpField, f::QQMPolyRingElem;
cached::Bool = true,
parent::fpMPolyRing = AbstractAlgebra._change_mpoly_ring(F, parent(f), cached))
return map_coefficients(F, f; cached, parent)
end
47 changes: 47 additions & 0 deletions test/flint/fmpq_mpoly-test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -813,3 +813,50 @@ end
@test abc - a - b == c
@test abc - ab == c
end

@testset "QQMPolyRingElem.convert_to_ZZMPolyRingElem" begin
Qxy, (x,y) = QQ[:x,:y]
f = 11*(x^2/2 + ZZ(3)^50*x*y^5/7 + y^6/12)

R = ZZ
Rxy, (u,v) = polynomial_ring(R, [:u,:v])
g = 462*u^2 + 94762534375324541717672868*u*v^5 + 77*v^6

@test g == @inferred mul!(zero(Rxy), f, 84)
@test g == change_base_ring(R, f*84; parent = Rxy)
@test g == map_coefficients(R, f*84; parent = Rxy)

# test error handling
@test_throws ArgumentError change_base_ring(R, f/5; parent = Rxy)
@test_throws ArgumentError map_coefficients(R, f/5; parent = Rxy)

# test ordering mismatch
Rxy, (u,v) = polynomial_ring(R, [:u,:v]; internal_ordering = :deglex)
g = 462*u^2 + 94762534375324541717672868*u*v^5 + 77*v^6

@test g == change_base_ring(R, f*84; parent = Rxy)
@test g == map_coefficients(R, f*84; parent = Rxy)
end

@testset "QQMPolyRingElem.convert_to_fpMPolyRing" begin
Qxy, (x,y) = QQ[:x,:y]
f = 11*(x^2/2 + ZZ(3)^50*x*y^5/7 + y^6/12)

R = Native.GF(5)
Rxy, (u,v) = polynomial_ring(R, [:u,:v])
g = 462*u^2 + 94762534375324541717672868*u*v^5 + 77*v^6

@test g == change_base_ring(R, f*84; parent = Rxy)
@test g == map_coefficients(R, f*84; parent = Rxy)

# test error handling
@test_throws ArgumentError change_base_ring(R, f/5; parent = Rxy)
@test_throws ArgumentError map_coefficients(R, f/5; parent = Rxy)

# test ordering mismatch
Rxy, (u,v) = polynomial_ring(R, [:u,:v]; internal_ordering = :deglex)
g = 462*u^2 + 94762534375324541717672868*u*v^5 + 77*v^6

@test g == change_base_ring(R, f*84; parent = Rxy)
@test g == map_coefficients(R, f*84; parent = Rxy)
end

0 comments on commit 1945331

Please sign in to comment.