Nemocas / Nemo.jl

Julia bindings for various mathematical libraries (including flint2)
http://nemocas.github.io/Nemo.jl/
Other
176 stars 57 forks source link

Add many more low-level helper methods such as `add!`, `sub!`, etc., and use them systematically. #1812

Open fingolfin opened 1 week ago

fingolfin commented 1 week ago

Right now we ccall many FLINT functions from multiple places, e.g. fmpq_mpoly_add is called in three places in Nemo; so is fmpz_add but that one also has two callers in Hecke.

At the same time, we are not using a bunch of FLINT accessor at all, e.g. while we also wrap fmpq_mpoly_add_si we don't wrap fmpq_mpoly_add_ui (perhaps it didn't exist when these wrappers were originally written).

To address this, I suggest we make provide these low-level helpers in a more systematic way (see e.g. PR #1809), and also use them more systematically (see e.g. PR #1811). In the example above, each of these functions should ideally be ccall'ed from exactly one spot, namely an add! method. Those add! methods then can take care of dispatching to the right ccalls. The result is code that is easier to read, and often it enables us to reduce code duplication. E.g. with suitable add! methods in place, we can have this:

function +(x::QQMatrix, y::QQMatrix)
  check_parent(x, y)
  z = similar(x)
  add!(z, x, y)
end

and this

function +(x::ZZMatrix, y::ZZMatrix)
  check_parent(x, y)
  z = similar(x)
  add!(z, x, y)
end

So if we wanted to we could implement + for all Nemo matrix types in a single place. Maybe a more plausible example is how in PR #1810 several constructors for fqPolyRepField become essentially identical and could be merged.

Using macros to generate all sensible variants of some low-level wrappers as done in e.g. PR #1809 probably should be done in more place. I.e. we should do similar things for the other FLINT types we wrap. For starters let's focus on set/add/sub/mul/div; but on the long run many more could and should be added -- e.g. neg!, zero!, divexact! etc. etc.

Basically any fmpz_mat_* API in FLINT could be wrapped in such a low-level wrapper. Someone could perhaps even write a tool to generate the relevant helpers (or at least some of them) from FLINT headers? I think some language wrappers for FLINT are already handled that way; was it the Haskell one? Maybe more? Surely @albinahlback and @fredrik-johansson can clarify that.

A key for this is to have, say, add! methods which don't just accept, say, a ZZRingElem but also a Ptr{ZZRingElem} or Ref{ZZRingElem} -- this can be achieved with a union type, see also e.g. PR #1808. That way we can use these low-level helpers in many more places.

albinahlback commented 1 week ago

I will ping @edgarcosta as he has worked on some parsing of headers for documentation purposes.

albinahlback commented 1 week ago

Do you suggest implementing some sort of script which will automate the process of checking:

fingolfin commented 1 week ago

I would imagine a script that would generate the wrapper code from headers, and then we can on occasionally rerun (when FLINT_jll is updated). What exactly it generates remains to be seen. Also I don't need it to generate wrappers for everything, some selectiveness would be perfectly fine and perhaps even desirable.

(Even better would be if also tests could be generated, at least some basic ones; say some trivial ones which verify that e.g. add!(a,x,ZZ(1)) == add!(b,x,1) etc.)

edgarcosta commented 1 week ago

Here is the code that I wrote to parse the headers and the documentation (it is not super polished, and I am afraid that I overcomplicated it): https://gist.github.com/edgarcosta/888fcf83565871afd635f641341cc14e