Skip to content

Add generic Ore polynomial module#2299

Open
rburing wants to merge 6 commits intoflintlib:mainfrom
rburing:gr_ore_poly
Open

Add generic Ore polynomial module#2299
rburing wants to merge 6 commits intoflintlib:mainfrom
rburing:gr_ore_poly

Conversation

@rburing
Copy link
Copy Markdown
Contributor

@rburing rburing commented Apr 12, 2025

This contains only the basic structure so far, such as memory management, additive arithmetic, and multiplication from the left by an element of the base ring.

Feedback welcome!

We would like to use this data type as input to differential equation solvers, so we don't need the multiplicative structure initially, though it would be nice to have it in the future.

Multiplicative operations (to be defined later) depend on the Ore context object, so I figured that all operations should take the Ore context as input, to make the interface uniform. So now the interface looks like gr_mpoly, and the basic operations implemented so far call gr_poly methods.

Since the base ring will typically be polynomial, should gr_ore_poly_scalar_mul and GR_ORE_POLY_ELEM_CTX be named differently? Maybe using base / BASE?

I also tried to make the module "optional" in the sense that gr_mpoly is, let me know if I missed something in this regard.

cc @mezzarobba

@mezzarobba
Copy link
Copy Markdown
Contributor

mezzarobba commented Apr 12, 2025 via email

@rburing
Copy link
Copy Markdown
Contributor Author

rburing commented Apr 12, 2025

Some/most of the arithmetic/setter methods (gr_ore_poly_add for example) are not getting called in the ring tests.

I'm not sure what I did wrong since I did put them in the method table.

Edit: Never mind, I put some NULL pointers in there which is not allowed except to indicate the end of the table.

@rburing rburing force-pushed the gr_ore_poly branch 2 times, most recently from 4cfe741 to c5d790c Compare April 13, 2025 13:07
@fredrik-johansson
Copy link
Copy Markdown
Collaborator

So, working with Ore polynomials over say R[x], one might indeed want to do mixed operations with operands from either R or R[x]. The term "scalar" being ambiguous, instead of adding many extra methods, perhaps just define gr_ore_poly_add_other etc. that handles all cases?

@fredrik-johansson
Copy link
Copy Markdown
Collaborator

I don't see the need for ore_poly.h and ore_poly_types.h.

@fredrik-johansson
Copy link
Copy Markdown
Collaborator

Most of the one-line trivial methods could go in a single file.

@fredrik-johansson
Copy link
Copy Markdown
Collaborator

Multiplicative operations (to be defined later) depend on the Ore context object, so I figured that all operations should take the Ore context as input, to make the interface uniform.

Yes, this is the right way to do it. The way gr_poly and gr_mat work directly with the element context is an exception rather than the rule, and at some point we should add a public interface for those types using a poly/mat context as well.

@rburing
Copy link
Copy Markdown
Contributor Author

rburing commented Apr 18, 2025

I reorganized the code to collect the wrapper functions in one file, and applied all the other suggestions above. I removed all the methods with scalar in the name and added some other methods, indeed this looks much better.

Let me know if this needs any further changes.

@fredrik-johansson
Copy link
Copy Markdown
Collaborator

Looks good. The only thing missing is documentation.

@rburing rburing force-pushed the gr_ore_poly branch 2 times, most recently from e5ac464 to 6323582 Compare April 18, 2025 13:37
@fredrik-johansson
Copy link
Copy Markdown
Collaborator

Something that doesn't seem to be well specified in the documentation is the generator name (set_gen_name / string conversion methods). If I'm in R[x][D], say, shouldn't x already be named the base ring while the Ore ring generator is D?

@fredrik-johansson
Copy link
Copy Markdown
Collaborator

In fact, shouldn't the generator x (given as an element of the base ring) be a parameter to the Ore polynomial ring constructor? In general, the base ring could be something multivariate.

@rburing
Copy link
Copy Markdown
Contributor Author

rburing commented Apr 29, 2025

I updated the context and constructor to include an index of a generator of the base ring, and (hopefully) clarified the documentation. I think the restriction to generators of the base ring (rather than elements of the base ring) is reasonable, and e.g. in the univariate case it saves the user from having to define a variable that will probably never be used (e.g. multiplication by the variable will be replaced by a shift). Let me know if this is alright.

@rburing rburing requested a review from mezzarobba April 29, 2025 14:21
@rburing rburing force-pushed the gr_ore_poly branch 2 times, most recently from 5b16adc to ee4d57a Compare April 29, 2025 14:49
@mezzarobba
Copy link
Copy Markdown
Contributor

mezzarobba commented Apr 30, 2025 via email

@rburing
Copy link
Copy Markdown
Contributor Author

rburing commented May 5, 2025

Another question: How should the q parameter for q-shifts or q-derivations be handled? The current gr_ctx_init_gr_ore_poly and the struct _gr_ore_poly_ctx_struct do not refer to it. Can this be left for later? I guess the main question is: do we want to put an extra parameter (again an index into the list of generators or a gr_srcptr?) already in gr_ctx_init_gr_ore_poly, or add a separate second constructor later (or break source compatibility by adding an extra parameter to gr_ctx_init_gr_ore_poly later)?

@rburing
Copy link
Copy Markdown
Contributor Author

rburing commented May 15, 2025

Thanks for the extensive feedback @mezzarobba, I think it looks better now.

What do you think about using the name ore_algebra_t for the enum? With values ORE_ALGEBRA_STANDARD_DERIVATIVE and ORE_ALGEBRA_EULER_DERIVATIVE for now.

@fredrik-johansson Should I keep entry in the name of gr_ore_poly_entry_ptr for consistency with gr_poly, or use coeff instead?

@fredrik-johansson
Copy link
Copy Markdown
Collaborator

I think the gr_poly method is misnamed and should use coeff as well.

@mezzarobba
Copy link
Copy Markdown
Contributor

What do you think about using the name ore_algebra_t for the enum? With values ORE_ALGEBRA_STANDARD_DERIVATIVE and ORE_ALGEBRA_EULER_DERIVATIVE for now.

Sounds good too. I think I would call d/dx DERIVATIVE instead of STANDARD_DERIVATIVE, and you could add SHIFT (or maybe FORWARD_SHIFT) and FORWARD_DIFFERENCE right away.

@mezzarobba
Copy link
Copy Markdown
Contributor

I added support for general (σ,δ) and implemented a number of standard (σ,δ) pairs, see https://github.com/mezzarobba/flint/tree/gr_ore_poly. Any feedback welcome. (Ricardo, feel free to merge my branch into this PR if you like.)

@mezzarobba
Copy link
Copy Markdown
Contributor

I should add that most predefined operator types are currently limited to base rings of type gr_poly but this restriction should be easy to lift if a few methods like gr_derivative or gr_subs are added to the general interface of generic rings. (I didn't want to embark in that now.)

rburing and others added 5 commits March 22, 2026 13:22
This contains only the basic structure so far, such as memory
management, additive arithmetic, and multiplication from the
left by an element of the base ring.
Mark some functions as static, add ngens to pass ring test, fix
mismatch of sigma_delta_compose function name in header.
@rburing
Copy link
Copy Markdown
Contributor Author

rburing commented Mar 22, 2026

I merged the implementation of Ore polynomial multiplication by @vioneers into this branch, and rebased it to resolve merge conflicts and a few technical issues (due to other developments in the codebase in the meantime).

@rburing
Copy link
Copy Markdown
Contributor Author

rburing commented Mar 22, 2026

I don't really understand why the tests (flint_fprintf with ASAN and codecov make check) are failing.

@rburing
Copy link
Copy Markdown
Contributor Author

rburing commented Mar 24, 2026

For the flint_fprintf test: it seems this

#define IS_FLINT_TYPE(ip, str) (memcmp(ip, str "}", sizeof(str)) == 0)

can read past the end of the buffer, e.g. when there is a {%gr_ formatter near the end of the format string and str is too large (here "gr_ore_poly" happens to be large).

How should we deal with this (pre-existing) issue?

@fredrik-johansson
Copy link
Copy Markdown
Collaborator

fredrik-johansson commented Mar 24, 2026

Good catch. I guess memcmp should be something like strcmp or strncmp.

found_algebra = 1;
else
{
if (algebra != ORE_ALGEBRA_FROBENIUS && ctx->which_ring == GR_CTX_GR_POLY)
Copy link
Copy Markdown
Contributor

@mezzarobba mezzarobba Mar 25, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why don't you like the Frobenius case? and what is this doing with the base ring??

while(!found_algebra)
{
// Random context
gr_ore_poly_ctx_init_randtest2(ctx, ore_ctx, state);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only the ctx and ore_ctx from the last iteration are cleared.

gr_ore_poly_ctx_t ore_ctx;
slong reps;
ore_algebra_t algebra;
int found_algebra = 0;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see the need for this flag.

@mezzarobba
Copy link
Copy Markdown
Contributor

mezzarobba commented Mar 25, 2026

The powering tests from the generic test suite are slow in the Mahler case (reached with FLINT_TEST_MULTIPLIER=10); we need a way to disable them or limit them to really really tiny examples. Maybe something like this:

diff --git a/src/gr_ore_poly/mul.c b/src/gr_ore_poly/mul.c
index 377ed6d78..d2d9e1999 100644
--- a/src/gr_ore_poly/mul.c
+++ b/src/gr_ore_poly/mul.c
@@ -146,6 +146,9 @@ int gr_ore_poly_mul(gr_ore_poly_t res, const gr_ore_poly_t poly1, const gr_ore_p
     if (len1 == 0 || len2 == 0)
         return gr_ore_poly_zero(res, ctx);

+    if (len1 + len2 - 1 > ctx->size_limit)
+        return GR_UNABLE;
+
     if (GR_ORE_POLY_CTX(ctx)->sigma_delta == NULL)
         return GR_UNABLE;

diff --git a/src/gr_ore_poly/test/t-ring.c b/src/gr_ore_poly/test/t-ring.c
index 82768e04f..e0e4f860b 100644
--- a/src/gr_ore_poly/test/t-ring.c
+++ b/src/gr_ore_poly/test/t-ring.c
@@ -28,6 +28,9 @@ TEST_FUNCTION_START(gr_ore_poly_ring, state)
         slong reps;

         gr_ore_poly_ctx_init_randtest2(ctx, ore_ctx, state);
+        if (GR_ORE_POLY_CTX(ore_ctx)->which_algebra == ORE_ALGEBRA_MAHLER
+            || GR_ORE_POLY_CTX(ore_ctx)->which_algebra == ORE_ALGEBRA_Q_SHIFT)
+            ore_ctx->size_limit = 2;

         if (gr_ctx_is_finite(ctx) == T_TRUE ||
             gr_ctx_has_real_prec(ctx) == T_TRUE)

#include "gr_vec.h"

// same as GR_MUST_SUCCEED but details of the failing printed
#define MUST_OK(expr) do { \
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought this was temporary debugging stuff that would be removed in the final code?

Note that requiring all products to succeed is not correct since it may happen, for instance, that multiplications in the base ring return GR_ENABLE.

But, btw: is this test file needed at all in the end? Or is it subsumed by t-ring?

reps = 3;
}

/* Hack: for string conversion tests, make sure we don't have
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What string conversion tests? (Eager copy-paste useless here, or am I missing something?)

@@ -0,0 +1,170 @@
/*
Copyright (C) 2026 Maria Neagoie, supervised by Marc Mezzarobba and Ricardo Buring
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think the “supervised by…” part is necessary :-)

slong el_size = gr_ctx_sizeof_elem(GR_ORE_POLY_ELEM_CTX(ctx));
int status = GR_SUCCESS;

if (GR_ORE_POLY_CTX(ctx)->sigma_delta == NULL)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

when/why would that be null?

TMP_INIT;
TMP_START;

gr_ptr temps = TMP_ALLOC(el_size);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not GR_TMP_INIT?

}
else
{
// Output initialized at 0
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do you need to zero all coefficients in the non-aliased case?

Sets *res* to *poly* multiplied by *c* (or *x* multiplied by *poly*)
which must be an element of or coercible to the coefficient ring.

.. function:: int _gr_ore_poly_lmul_gen(gr_ptr res, gr_srcptr poly, slong len, gr_ore_poly_ctx_t ctx)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you list the underscore variants, it would be good to document the assumptions they make.

// Initialize Q = b, then Q = D^i * b.
gr_ore_poly_t Q;
gr_ore_poly_init2(Q, len2, ctx);
for (slong i = 0; i < len2; i++)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doesn't gr_set work on Ore polynomials?

{
gr_srcptr qj = GR_ENTRY(Q->coeffs, j, el_size);

// Compute a_i * q_j
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gr_addmul?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's probably better to do it with gr_add/gr_mul for now. Many rings don't implement an optimized gr_addmul and the generic version will do a temporary allocation each time.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok, thanks!


void gr_ore_poly_ctx_init(gr_ore_poly_ctx_t ctx, gr_ctx_t base_ring, slong base_var, const ore_algebra_t which_algebra);
int gr_ore_poly_ctx_init_q_shift(gr_ore_poly_ctx_t ctx, gr_ctx_t base_ring, slong base_var, gr_srcptr q);
int gr_ore_poly_ctx_init_mahler(gr_ore_poly_ctx_t ctx, gr_ctx_t base_ring, slong base_var, long mahler_base);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The long type should be avoided; change to slong?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants