Nemocas / Nemo.jl

Julia bindings for the FLINT number theory C library
http://nemocas.github.io/Nemo.jl/
Other
193 stars 58 forks source link

Various inplace operators #1166

Open 8d1h opened 3 years ago

8d1h commented 3 years ago

Hi!

I recently discovered that inplace operators can save a lot of memory allocations and in certain cases vastly improve performance.

However many inplace operators from FLINT are not yet wrapped in Nemo or only wrapped as non-inplace (here I'm mainly talking about fmpz and fmpq, haven't looked at other types yet).

Also some operators for example fmpq_add are wrapped multiple times as

function +(a::fmpq, b::fmpq)
   z = fmpq()
   ccall((:fmpq_add, libflint), Nothing,
         (Ref{fmpq}, Ref{fmpq}, Ref{fmpq}), z, a, b)
   return z
end
function add!(c::fmpq, a::fmpq, b::fmpq)
   ccall((:fmpq_add, libflint), Nothing,
         (Ref{fmpq}, Ref{fmpq}, Ref{fmpq}), c, a, b)
   return c
end
function addeq!(c::fmpq, a::fmpq)
   ccall((:fmpq_add, libflint), Nothing,
         (Ref{fmpq}, Ref{fmpq}, Ref{fmpq}), c, c, a)
   return c
end

while certain ad hoc operators are not defined at all and hence fallback to generic implementations

julia> functionloc(-, (Int, fmpq))
("/home/jieao/.julia/packages/AbstractAlgebra/PirHd/src/Fraction.jl", 339)

I purpose to systematically wrap all such operators as "unsafe" and base the safe ones on them. So for fmpq_add this is

function add!(c::fmpq, a::fmpq, b::fmpq)
   ccall((:fmpq_add, libflint), Nothing, (Ref{fmpq}, Ref{fmpq}, Ref{fmpq}), c, a, b)
   return c
end

+(a::fmpq, b::fmpq) = add!(fmpq(), a, b)
addeq!(a::fmpq, b::fmpq) = add!(a, a, b)

This way we can make all the inplace/"unsafe" operators available should one need to use them, and it's also easier to keep track of the ones that are already wrapped. And similarly ad hoc operators like +(a::fmpq, b::Int) should be add!(fmpq(), a, b) where add! wraps fmpq_add_si.

Also maybe some general interface could be implemented like

add!(a::RingElement, b::RingElement, c::RingElement) = b + c # this needs to check the type of a
addeq!(a::RingElement, b::RingElement) = add!(a, a, b)

and then one can define macros like @! a += b that expands into a = addeq!(a, b) to preserve the readability of the code.

tthsqe12 commented 3 years ago

So I am just now adding ZZi and QQi and will add more of these ! operations for ZZ (fmpz) and QQ (fmpq) in the process. AbstractAlgebra has fallbacks for add! and addeq!, but in general the usage of ! in generic code is a mess.

fingolfin commented 3 years ago

let me also mention issue Nemocas/AbstractAlgebra.jl#1000