flint icon indicating copy to clipboard operation
flint copied to clipboard

access to generator names

Open mezzarobba opened this issue 9 months ago • 5 comments

An API for accessing generator names.

This includes some debatable choices. Most importantly, I am treating contexts as containers as regards generator names (not using status flags, providing pointers to names owned by the context rather than writing to a separate buffer), which basically makes it impossible to write a context that generates the names on the fly and does not want to store them forever. Also, the method for accessing the names returns an array of generator names rather than individual names.

mezzarobba avatar May 09 '25 12:05 mezzarobba

I'd rather have an API that preserves the ability for contexts to generate names on the fly.

fredrik-johansson avatar May 09 '25 15:05 fredrik-johansson

Ok, thanks. Would this version be acceptable with gr_ctx_gen_names_srcptr renamed to _gr_ctx_gen_names_srcptr (or _ptr, dropping the consts) and an explicit warning that it may not be implemented by all contexts? The other options I can think of make it a bit cumbersome to do simple thinks like copying the generator names of a ring to another one.

mezzarobba avatar May 09 '25 16:05 mezzarobba

What is the exact use case here? What is the problem with returning newly allocated strings?

fredrik-johansson avatar May 09 '25 20:05 fredrik-johansson

In general, I would like to have a way of accessing the names of the generators. In practice, in the cases I am really interested in, I could probably just call gr_gens() and print each generator to a string, but there is no reason in general that a generator prints to just its name.

A concrete use case is for changing the base ring of a polynomial ring.

There is no real problem with returning newly allocated strings, it just feels a bit complicated when in 99% of cases a pointer to the existing names array would do the job. Take the previous example: either you return the generators one by one and the user must arrange them in an array to call gr_ctx_set_gen_names. Or you return them all at once but then you need the infrastructure for the user to free an array of strings: either have them allocate the array and loop over it to free the individual strings, or provide dedicated objects with associated init/free functions, or write everything in a single malloc'd buffer. All doable but all a bit impractical IMO. But maybe I am missing something obvious!

mezzarobba avatar May 10 '25 07:05 mezzarobba

I could probably just call gr_gens() and print each generator to a string, but there is no reason in general that a generator prints to just its name.

Indeed, and this is already an issue:

>>> R = PowerSeriesModRing(ZZ, 3)
>>> R.gen()
x (mod x^3)
>>> R("x")
Traceback (most recent call last):
...
FlintUnableError: unable to create element of Power series over Integer ring (fmpz) mod x^3 from x of type <class 'str'>

For that reason, a method to return the generator names would indeed be useful.

However, I'm still not sure about returning names by reference.

Something to consider: gr_ctx_init_gr_poly and gr_ctx_init_gr_mpoly choose a default variable name so that creating the context object doesn't require a string allocation. But obviously as soon as you start printing you don't want to your nested ring to be $R[x][x][x]$. Perhaps a better solution would be to look at the recursive generators of the coefficient ring and choosing something not in that list so that you automatically get $R[x][y][z]$ or whatever. However, this should arguably be done on demand when printing or parsing rather than when constructing the ring. This means one can't guarantee that there is a persistent default variable string to point to.

All doable but all a bit impractical IMO. But maybe I am missing something obvious!

Not really, I think allocating temporary arrays and allocating new strings is the least hacky way to do it.

For internal use, life would be easier if we had a memory-managed flint_string_t. But then everyone downstream would just end up needing their own conversions between FLINT strings and C strings...

fredrik-johansson avatar May 16 '25 09:05 fredrik-johansson

Do you prefer

  • slong gr_ctx_ngens(gr_ctx_t) or int gr_ctx_ngens(slong * gr_ctx_t)?
  • int gr_ctx_gen_name(char * name, slong i, gr_ctx_t ctx) or int gr_ctx_gen_names(char ** names, gr_ctx_t ctx)?
  • or a single function int gr_ctx_gen_names(char ** names, slong * ngens, gr_ctx_t ctx) (where names is to be cleared with a single call to flint_free)
  • or some other variant?

mezzarobba avatar May 19 '25 15:05 mezzarobba

I think I'd go with

int gr_ctx_ngens(slong *, gr_ctx_t)

int gr_ctx_gen_name(char ** name, slong i, gr_ctx_t ctx)

fredrik-johansson avatar May 21 '25 20:05 fredrik-johansson

I think I'd go with

int gr_ctx_ngens(slong *, gr_ctx_t)

int gr_ctx_gen_name(char ** name, slong i, gr_ctx_t ctx)

I refreshed the PR with that interface, but I don't know what to make of the CI failures. Locally all tests pass with ×20 multiplier.

mezzarobba avatar Oct 27 '25 19:10 mezzarobba

Looks fine now.

fredrik-johansson avatar Oct 28 '25 20:10 fredrik-johansson