From 91f498f99f8a305eb36d175a0515140af9ce3d4f Mon Sep 17 00:00:00 2001 From: Jake Moss Date: Wed, 4 Sep 2024 15:54:32 +1000 Subject: [PATCH 01/27] Run mod_mpoly and vec doc tests, sort module imports --- src/flint/test/__main__.py | 48 ++++++++++++++++-------------- src/flint/types/fmpz_mod_mpoly.pyx | 12 ++++---- src/flint/types/nmod_mpoly.pyx | 16 +++++----- 3 files changed, 40 insertions(+), 36 deletions(-) diff --git a/src/flint/test/__main__.py b/src/flint/test/__main__.py index 21124c51..c9a1c19b 100644 --- a/src/flint/test/__main__.py +++ b/src/flint/test/__main__.py @@ -54,35 +54,39 @@ def run_doctests(verbose=None): modules = [flint.pyflint, flint.flint_base.flint_base, flint.flint_base.flint_context, - flint.types.fmpz, - flint.types.fmpz_poly, - flint.types.fmpz_mat, - flint.types.fmpz_mpoly, - flint.types.fmpz_series, - flint.types.fmpz_mod, - flint.types.fmpz_mod_poly, - flint.types.fmpz_mod_mat, + flint.types.acb, + flint.types.acb_mat, + flint.types.acb_poly, + flint.types.acb_series, + flint.types.arb, + flint.types.arb_mat, + flint.types.arb_poly, + flint.types.arb_series, + flint.types.arf, + flint.types.dirichlet, flint.types.fmpq, - flint.types.fmpq_poly, flint.types.fmpq_mat, flint.types.fmpq_mpoly, + flint.types.fmpq_poly, flint.types.fmpq_series, + flint.types.fmpq_vec, + flint.types.fmpz, + flint.types.fmpz_mat, + flint.types.fmpz_mod, + flint.types.fmpz_mod_mat, + flint.types.fmpz_mod_mpoly, + flint.types.fmpz_mod_poly, + flint.types.fmpz_mpoly, + flint.types.fmpz_poly, + flint.types.fmpz_series, + flint.types.fmpz_vec, + flint.types.fq_default, + flint.types.fq_default_poly, flint.types.nmod, - flint.types.nmod_poly, flint.types.nmod_mat, + flint.types.nmod_mpoly, + flint.types.nmod_poly, flint.types.nmod_series, - flint.types.fq_default, - flint.types.fq_default_poly, - flint.types.arf, - flint.types.arb, - flint.types.arb_poly, - flint.types.arb_mat, - flint.types.arb_series, - flint.types.acb, - flint.types.acb_poly, - flint.types.acb_mat, - flint.types.acb_series, - flint.types.dirichlet, flint.functions.showgood] try: from flint.types import acb_theta diff --git a/src/flint/types/fmpz_mod_mpoly.pyx b/src/flint/types/fmpz_mod_mpoly.pyx index 0ef2877b..e5a662a6 100644 --- a/src/flint/types/fmpz_mod_mpoly.pyx +++ b/src/flint/types/fmpz_mod_mpoly.pyx @@ -203,7 +203,7 @@ cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context): True """ if self.__prime_modulus is None: - self.__prime_modulus = self.modulus().is_prime() + self.__prime_modulus = self.modulus().is_prime() return self.__prime_modulus def gen(self, slong i): @@ -406,7 +406,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p[1, 1] = 20 >>> p - 20*x0*x1 + 2*x1 + 9*x0*x1 + 2*x1 """ cdef: @@ -829,7 +829,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): >>> f = ctx.from_dict({(1, 1): 4, (0, 0): 1}) >>> g = ctx.from_dict({(0, 1): 2, (1, 0): 2}) >>> (f * g).gcd(f) - 4*x0*x1 + 1 + x0*x1 + 3 """ cdef fmpz_mod_mpoly res if not typecheck(other, fmpz_mod_mpoly): @@ -875,9 +875,9 @@ cdef class fmpz_mod_mpoly(flint_mpoly): >>> p1 = Zm("2*x + 4", ctx) >>> p2 = Zm("3*x*z + 3*x + 3*z + 3", ctx) >>> (p1 * p2).factor() - (6, [(z + 1, 1), (x + 2, 1), (x + 1, 1)]) + (6, [(z + 1, 1), (x + 1, 1), (x + 2, 1)]) >>> (p2 * p1 * p2).factor() - (18, [(z + 1, 2), (x + 2, 1), (x + 1, 2)]) + (7, [(z + 1, 2), (x + 2, 1), (x + 1, 2)]) """ cdef: fmpz_mod_mpoly_factor_t fac @@ -920,7 +920,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): >>> (p1 * p2).factor_squarefree() (6, [(y + 1, 1), (x^2 + 3*x + 2, 1)]) >>> (p1 * p2 * p1).factor_squarefree() - (12, [(y + 1, 1), (x + 1, 1), (x + 2, 2)]) + (1, [(y + 1, 1), (x + 1, 1), (x + 2, 2)]) """ cdef: fmpz_mod_mpoly_factor_t fac diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx index 54ed769e..8ccee09e 100644 --- a/src/flint/types/nmod_mpoly.pyx +++ b/src/flint/types/nmod_mpoly.pyx @@ -186,15 +186,15 @@ cdef class nmod_mpoly_ctx(flint_mpoly_context): Return whether the modulus is prime >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(4, Ordering.degrevlex, 2**127, 'z') + >>> ctx = nmod_mpoly_ctx.get_context(4, Ordering.degrevlex, 2**32, 'z') >>> ctx.is_prime() False - >>> ctx = nmod_mpoly_ctx.get_context(4, Ordering.degrevlex, 2**127 - 1, 'z') + >>> ctx = nmod_mpoly_ctx.get_context(4, Ordering.degrevlex, 2**32 - 17, 'z') >>> ctx.is_prime() True """ if self.__prime_modulus is None: - self.__prime_modulus = n_is_prime(self.modulus()) + self.__prime_modulus = n_is_prime(self.modulus()) return self.__prime_modulus def gen(self, slong i): @@ -380,7 +380,7 @@ cdef class nmod_mpoly(flint_mpoly): >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p[1, 1] = 20 >>> p - 20*x0*x1 + 2*x1 + 9*x0*x1 + 2*x1 """ cdef: @@ -803,7 +803,7 @@ cdef class nmod_mpoly(flint_mpoly): >>> f = ctx.from_dict({(1, 1): 4, (0, 0): 1}) >>> g = ctx.from_dict({(0, 1): 2, (1, 0): 2}) >>> (f * g).gcd(f) - 4*x0*x1 + 1 + x0*x1 + 3 """ cdef nmod_mpoly res if not typecheck(other, nmod_mpoly): @@ -849,9 +849,9 @@ cdef class nmod_mpoly(flint_mpoly): >>> p1 = Zm("2*x + 4", ctx) >>> p2 = Zm("3*x*z + 3*x + 3*z + 3", ctx) >>> (p1 * p2).factor() - (6, [(z + 1, 1), (x + 2, 1), (x + 1, 1)]) + (6, [(z + 1, 1), (x + 1, 1), (x + 2, 1)]) >>> (p2 * p1 * p2).factor() - (18, [(z + 1, 2), (x + 2, 1), (x + 1, 2)]) + (7, [(z + 1, 2), (x + 2, 1), (x + 1, 2)]) """ cdef: nmod_mpoly_factor_t fac @@ -893,7 +893,7 @@ cdef class nmod_mpoly(flint_mpoly): >>> (p1 * p2).factor_squarefree() (6, [(y + 1, 1), (x^2 + 3*x + 2, 1)]) >>> (p1 * p2 * p1).factor_squarefree() - (12, [(y + 1, 1), (x + 1, 1), (x + 2, 2)]) + (1, [(y + 1, 1), (x + 1, 1), (x + 2, 2)]) """ cdef: nmod_mpoly_factor_t fac From e84d0e95d945be732cf130ab6386cb50ea675658 Mon Sep 17 00:00:00 2001 From: Jake Moss Date: Wed, 4 Sep 2024 15:56:37 +1000 Subject: [PATCH 02/27] Add mpoly discriminant, resultant, term_content, and deflation No tests currently, only doc tests --- src/flint/flintlib/nmod_mpoly.pxd | 4 ++ src/flint/types/fmpq_mpoly.pyx | 88 ++++++++++++++++++++++++- src/flint/types/fmpz_mod_mpoly.pyx | 93 +++++++++++++++++++++++++- src/flint/types/fmpz_mpoly.pyx | 101 +++++++++++++++++++++++++++++ src/flint/types/nmod_mpoly.pyx | 96 ++++++++++++++++++++++++++- 5 files changed, 377 insertions(+), 5 deletions(-) diff --git a/src/flint/flintlib/nmod_mpoly.pxd b/src/flint/flintlib/nmod_mpoly.pxd index 4185c3b1..7cf5296b 100644 --- a/src/flint/flintlib/nmod_mpoly.pxd +++ b/src/flint/flintlib/nmod_mpoly.pxd @@ -157,3 +157,7 @@ cdef extern from "flint/nmod_mpoly.h": void nmod_mpoly_div_monagan_pearce(nmod_mpoly_t polyq, const nmod_mpoly_t poly2, const nmod_mpoly_t poly3, const nmod_mpoly_ctx_t ctx) void nmod_mpoly_divrem_monagan_pearce(nmod_mpoly_t q, nmod_mpoly_t r, const nmod_mpoly_t poly2, const nmod_mpoly_t poly3, const nmod_mpoly_ctx_t ctx) void nmod_mpoly_divrem_ideal_monagan_pearce(nmod_mpoly_struct ** q, nmod_mpoly_t r, const nmod_mpoly_t poly2, nmod_mpoly_struct * const * poly3, slong len, const nmod_mpoly_ctx_t ctx) + + # These functions seem to be undocumented but appear in the header. + void nmod_mpoly_deflation(fmpz_struct * shift, fmpz_struct * stride, const nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_deflate(nmod_mpoly_t A, const nmod_mpoly_t B, const fmpz_struct * shift, const fmpz_struct * stride, const nmod_mpoly_ctx_t ctx) diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 14bc9098..64a1465c 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -24,6 +24,7 @@ from flint.flintlib.fmpq_mpoly cimport ( fmpq_mpoly_ctx_init, fmpq_mpoly_degrees_fmpz, fmpq_mpoly_derivative, + fmpq_mpoly_discriminant, fmpq_mpoly_div, fmpq_mpoly_divides, fmpq_mpoly_divrem, @@ -47,6 +48,7 @@ from flint.flintlib.fmpq_mpoly cimport ( fmpq_mpoly_pow_fmpz, fmpq_mpoly_push_term_fmpq_ffmpz, fmpq_mpoly_reduce, + fmpq_mpoly_resultant, fmpq_mpoly_scalar_mul_fmpq, fmpq_mpoly_set, fmpq_mpoly_set_coeff_fmpq_fmpz, @@ -56,6 +58,7 @@ from flint.flintlib.fmpq_mpoly cimport ( fmpq_mpoly_sqrt, fmpq_mpoly_sub, fmpq_mpoly_sub_fmpq, + fmpq_mpoly_term_content, fmpq_mpoly_total_degree_fmpz, ) from flint.flintlib.fmpq_mpoly_factor cimport ( @@ -67,7 +70,11 @@ from flint.flintlib.fmpq_mpoly_factor cimport ( ) from flint.flintlib.fmpz cimport fmpz_init_set -from flint.flintlib.fmpz_mpoly cimport fmpz_mpoly_set +from flint.flintlib.fmpz_mpoly cimport ( + fmpz_mpoly_set, + fmpz_mpoly_deflate, + fmpz_mpoly_deflation +) from cpython.object cimport Py_EQ, Py_NE cimport libc.stdlib @@ -767,6 +774,71 @@ cdef class fmpq_mpoly(flint_mpoly): fmpq_mpoly_gcd(res.val, (self).val, (other).val, res.ctx.val) return res + def term_content(self): + """ + Return the GCD of the terms of `self`. If `self` is zero, then the result will + be zero, otherwise it will be a monomial with positive coefficient. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> x0, x1 = ctx.gens() + >>> f = 3 * x0**2 * x1 + 6 * x0 * x1 + >>> f.term_content() + x0*x1 + """ + cdef fmpq_mpoly res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_term_content(res.val, self.val, self.ctx.val) + return res + + def resultant(self, other, var): + """ + Return the resultant of `self` and `other` with respect to variable `var`. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> x0, x1 = ctx.gens() + >>> f = x0**2 * x1 + x0 * x1 + >>> g = x0 + x1 + >>> f.resultant(g, 'x1') + x0^3 + x0^2 + """ + cdef: + fmpq_mpoly res + slong i + + if not typecheck(other, fmpq_mpoly): + raise TypeError("argument must be a fmpq_mpoly") + elif (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + + i = self.ctx.variable_to_index(var) + res = create_fmpq_mpoly(self.ctx) + if not fmpq_mpoly_resultant(res.val, self.val, (other).val, i, self.ctx.val): + raise RuntimeError(f"failed to compute resultant with respect to {var}") + return res + + def discriminant(self, var): + """ + Return the discriminant of `self` with respect to variable `var`. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> x0, x1 = ctx.gens() + >>> f = (x0 + x1)**2 + 1 + >>> f.discriminant('x1') + -4 + + """ + cdef: + fmpq_mpoly res + slong i + + i = self.ctx.variable_to_index(var) + res = create_fmpq_mpoly(self.ctx) + if not fmpq_mpoly_discriminant(res.val, self.val, i, self.ctx.val): + raise RuntimeError(f"failed to compute discriminant with respect to {var}") + return res + def sqrt(self): """ Return the square root of self. @@ -914,6 +986,20 @@ cdef class fmpq_mpoly(flint_mpoly): fmpq_mpoly_integral(res.val, self.val, i, self.ctx.val) return res + def deflation(self): + """ + """ + cdef: + fmpz_vec shift = fmpz_vec(self.ctx.nvars()) + fmpz_vec stride = fmpz_vec(self.ctx.nvars()) + fmpq_mpoly res = create_fmpq_mpoly(self.ctx) + + fmpz_mpoly_deflation(shift.val, stride.val, self.val.zpoly, self.ctx.val.zctx) + fmpz_mpoly_deflate(res.val.zpoly, self.val.zpoly, shift.val, stride.val, self.ctx.val.zctx) + + fmpq_set(res.val.content, self.val.content) + return res, stride + cdef class fmpq_mpoly_vec: """ diff --git a/src/flint/types/fmpz_mod_mpoly.pyx b/src/flint/types/fmpz_mod_mpoly.pyx index e5a662a6..5d584022 100644 --- a/src/flint/types/fmpz_mod_mpoly.pyx +++ b/src/flint/types/fmpz_mod_mpoly.pyx @@ -22,8 +22,11 @@ from flint.flintlib.fmpz_mod_mpoly cimport ( fmpz_mod_mpoly_compose_fmpz_mod_mpoly, fmpz_mod_mpoly_ctx_get_modulus, fmpz_mod_mpoly_ctx_init, + fmpz_mod_mpoly_deflate, + fmpz_mod_mpoly_deflation, fmpz_mod_mpoly_degrees_fmpz, fmpz_mod_mpoly_derivative, + fmpz_mod_mpoly_discriminant, fmpz_mod_mpoly_div, fmpz_mod_mpoly_divides, fmpz_mod_mpoly_divrem, @@ -44,16 +47,18 @@ from flint.flintlib.fmpz_mod_mpoly cimport ( fmpz_mod_mpoly_neg, fmpz_mod_mpoly_pow_fmpz, fmpz_mod_mpoly_push_term_fmpz_ffmpz, + fmpz_mod_mpoly_resultant, fmpz_mod_mpoly_scalar_mul_fmpz, fmpz_mod_mpoly_set, fmpz_mod_mpoly_set_coeff_fmpz_fmpz, fmpz_mod_mpoly_set_fmpz, fmpz_mod_mpoly_set_str_pretty, fmpz_mod_mpoly_sort_terms, + fmpz_mod_mpoly_sqrt, fmpz_mod_mpoly_sub, fmpz_mod_mpoly_sub_fmpz, + fmpz_mod_mpoly_term_content, fmpz_mod_mpoly_total_degree_fmpz, - fmpz_mod_mpoly_sqrt, ) from flint.flintlib.fmpz_mod_mpoly_factor cimport ( fmpz_mod_mpoly_factor, @@ -842,6 +847,79 @@ cdef class fmpz_mod_mpoly(flint_mpoly): fmpz_mod_mpoly_gcd(res.val, (self).val, (other).val, res.ctx.val) return res + def term_content(self): + """ + Return the GCD of the terms of `self`. If `self` is zero, then the result will + be zero, otherwise it will be a monomial with positive coefficient. + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> x0, x1 = ctx.gens() + >>> f = 3 * x0**2 * x1 + 6 * x0 * x1 + >>> f.term_content() + x0*x1 + """ + if not self.ctx.is_prime(): + raise DomainError("gcd with non-prime modulus is not supported") + + cdef fmpz_mod_mpoly res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_term_content(res.val, self.val, self.ctx.val) + return res + + def resultant(self, other, var): + """ + Return the resultant of `self` and `other` with respect to variable `var`. + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> x0, x1 = ctx.gens() + >>> f = x0**2 * x1 + x0 * x1 + >>> g = x0 + x1 + >>> f.resultant(g, 'x1') + x0^3 + x0^2 + """ + cdef: + fmpz_mod_mpoly res + slong i + + if not typecheck(other, fmpz_mod_mpoly): + raise TypeError("argument must be a fmpz_mod_mpoly") + elif (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + elif not self.ctx.is_prime(): + raise DomainError("gcd with non-prime modulus is not supported") + + i = self.ctx.variable_to_index(var) + res = create_fmpz_mod_mpoly(self.ctx) + if not fmpz_mod_mpoly_resultant(res.val, self.val, (other).val, i, self.ctx.val): + raise RuntimeError(f"failed to compute resultant with respect to {var}") + return res + + def discriminant(self, var): + """ + Return the discriminant of `self` with respect to variable `var`. + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> x0, x1 = ctx.gens() + >>> f = (x0 + x1)**2 + 1 + >>> f.discriminant('x1') + 7 + + """ + cdef: + fmpz_mod_mpoly res + slong i + + if not self.ctx.is_prime(): + raise DomainError("gcd with non-prime modulus is not supported") + + i = self.ctx.variable_to_index(var) + res = create_fmpz_mod_mpoly(self.ctx) + if not fmpz_mod_mpoly_discriminant(res.val, self.val, i, self.ctx.val): + raise RuntimeError(f"failed to compute discriminant with respect to {var}") + return res + def sqrt(self): """ Return the square root of self. @@ -1003,6 +1081,19 @@ cdef class fmpz_mod_mpoly(flint_mpoly): fmpz_mod_mpoly_derivative(res.val, self.val, i, self.ctx.val) return res + def deflation(self): + """ + """ + cdef: + fmpz_vec shift = fmpz_vec(self.ctx.nvars()) + fmpz_vec stride = fmpz_vec(self.ctx.nvars()) + fmpz_mod_mpoly res = create_fmpz_mod_mpoly(self.ctx) + + fmpz_mod_mpoly_deflation(shift.val, stride.val, self.val, self.ctx.val) + fmpz_mod_mpoly_deflate(res.val, self.val, shift.val, stride.val, self.ctx.val) + + return res, stride + cdef class fmpz_mod_mpoly_vec: """ diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index c7a3528e..c39108cf 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -20,8 +20,11 @@ from flint.flintlib.fmpz_mpoly cimport ( fmpz_mpoly_clear, fmpz_mpoly_compose_fmpz_mpoly, fmpz_mpoly_ctx_init, + fmpz_mpoly_deflate, + fmpz_mpoly_deflation, fmpz_mpoly_degrees_fmpz, fmpz_mpoly_derivative, + fmpz_mpoly_discriminant, fmpz_mpoly_div, fmpz_mpoly_divides, fmpz_mpoly_divrem, @@ -42,8 +45,10 @@ from flint.flintlib.fmpz_mpoly cimport ( fmpz_mpoly_mul, fmpz_mpoly_neg, fmpz_mpoly_pow_fmpz, + fmpz_mpoly_primitive_part, fmpz_mpoly_push_term_fmpz_ffmpz, fmpz_mpoly_reduction_primitive_part, + fmpz_mpoly_resultant, fmpz_mpoly_scalar_mul_fmpz, fmpz_mpoly_set, fmpz_mpoly_set_coeff_fmpz_fmpz, @@ -54,6 +59,7 @@ from flint.flintlib.fmpz_mpoly cimport ( fmpz_mpoly_sqrt_heap, fmpz_mpoly_sub, fmpz_mpoly_sub_fmpz, + fmpz_mpoly_term_content, fmpz_mpoly_total_degree_fmpz, fmpz_mpoly_vec_autoreduction, fmpz_mpoly_vec_autoreduction_groebner, @@ -748,6 +754,89 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_gcd(res.val, (self).val, (other).val, res.ctx.val) return res + def term_content(self): + """ + Return the GCD of the terms of `self`. If `self` is zero, then the result will + be zero, otherwise it will be a monomial with positive coefficient. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> x0, x1 = ctx.gens() + >>> f = 3 * x0**2 * x1 + 6 * x0 * x1 + >>> f.term_content() + 3*x0*x1 + """ + cdef fmpz_mpoly res = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_term_content(res.val, self.val, self.ctx.val) + return res + + def resultant(self, other, var): + """ + Return the resultant of `self` and `other` with respect to variable `var`. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> x0, x1 = ctx.gens() + >>> f = x0**2 * x1 + x0 * x1 + >>> g = x0 + x1 + >>> f.resultant(g, 'x1') + x0^3 + x0^2 + """ + cdef: + fmpz_mpoly res + slong i + + if not typecheck(other, fmpz_mpoly): + raise TypeError("argument must be a fmpz_mpoly") + elif (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + + i = self.ctx.variable_to_index(var) + res = create_fmpz_mpoly(self.ctx) + if not fmpz_mpoly_resultant(res.val, self.val, (other).val, i, self.ctx.val): + raise RuntimeError(f"failed to compute resultant with respect to {var}") + return res + + def discriminant(self, var): + """ + Return the discriminant of `self` with respect to variable `var`. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> x0, x1 = ctx.gens() + >>> f = (x0 + x1)**2 + 1 + >>> f.discriminant('x1') + -4 + + """ + cdef: + fmpz_mpoly res + slong i + + i = self.ctx.variable_to_index(var) + res = create_fmpz_mpoly(self.ctx) + if not fmpz_mpoly_discriminant(res.val, self.val, i, self.ctx.val): + raise RuntimeError(f"failed to compute discriminant with respect to {var}") + return res + + def primitive_part(self): + """ + Return the primitive part of `self` obtained by dividing out the content of + all coefficients and normalizing the leading coefficient to be positive. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> x, y = ctx.gens() + >>> f = x**2 * y + x * y + >>> f.primitive_part() + x0^2*x1 + x0*x1 + """ + cdef fmpz_mpoly res + + res = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_primitive_part(res.val, self.val, self.ctx.val) + return res + def sqrt(self, assume_perfect_square: bool = False): """ Return the square root of self. @@ -977,6 +1066,18 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_reduction_primitive_part(res.val, self.val, (vec).val, self.ctx.val) return res + def deflation(self): + """ + """ + cdef: + fmpz_vec shift = fmpz_vec(self.ctx.nvars()) + fmpz_vec stride = fmpz_vec(self.ctx.nvars()) + fmpz_mpoly res = create_fmpz_mpoly(self.ctx) + + fmpz_mpoly_deflation(shift.val, stride.val, self.val, self.ctx.val) + fmpz_mpoly_deflate(res.val, self.val, shift.val, stride.val, self.ctx.val) + return res, stride + cdef class fmpz_mpoly_vec: """ diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx index 8ccee09e..6f7cc64f 100644 --- a/src/flint/types/nmod_mpoly.pyx +++ b/src/flint/types/nmod_mpoly.pyx @@ -23,10 +23,13 @@ from flint.flintlib.nmod_mpoly cimport ( nmod_mpoly_add_ui, nmod_mpoly_clear, nmod_mpoly_compose_nmod_mpoly, - nmod_mpoly_ctx_modulus, nmod_mpoly_ctx_init, + nmod_mpoly_ctx_modulus, + nmod_mpoly_deflate, + nmod_mpoly_deflation, nmod_mpoly_degrees_fmpz, nmod_mpoly_derivative, + nmod_mpoly_discriminant, nmod_mpoly_div, nmod_mpoly_divides, nmod_mpoly_divrem, @@ -47,16 +50,18 @@ from flint.flintlib.nmod_mpoly cimport ( nmod_mpoly_neg, nmod_mpoly_pow_fmpz, nmod_mpoly_push_term_ui_ffmpz, + nmod_mpoly_resultant, nmod_mpoly_scalar_mul_ui, nmod_mpoly_set, nmod_mpoly_set_coeff_ui_fmpz, - nmod_mpoly_set_ui, nmod_mpoly_set_str_pretty, + nmod_mpoly_set_ui, nmod_mpoly_sort_terms, + nmod_mpoly_sqrt, nmod_mpoly_sub, nmod_mpoly_sub_ui, + nmod_mpoly_term_content, nmod_mpoly_total_degree_fmpz, - nmod_mpoly_sqrt, ) from flint.flintlib.nmod_mpoly_factor cimport ( nmod_mpoly_factor, @@ -816,6 +821,79 @@ cdef class nmod_mpoly(flint_mpoly): nmod_mpoly_gcd(res.val, (self).val, (other).val, res.ctx.val) return res + def term_content(self): + """ + Return the GCD of the terms of `self`. If `self` is zero, then the result will + be zero, otherwise it will be a monomial with positive coefficient. + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> x0, x1 = ctx.gens() + >>> f = 3 * x0**2 * x1 + 6 * x0 * x1 + >>> f.term_content() + x0*x1 + """ + if not self.ctx.is_prime(): + raise DomainError("gcd with non-prime modulus is not supported") + + cdef nmod_mpoly res = create_nmod_mpoly(self.ctx) + nmod_mpoly_term_content(res.val, self.val, self.ctx.val) + return res + + def resultant(self, other, var): + """ + Return the resultant of `self` and `other` with respect to variable `var`. + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> x0, x1 = ctx.gens() + >>> f = x0**2 * x1 + x0 * x1 + >>> g = x0 + x1 + >>> f.resultant(g, 'x1') + x0^3 + x0^2 + """ + cdef: + nmod_mpoly res + slong i + + if not typecheck(other, nmod_mpoly): + raise TypeError("argument must be a nmod_mpoly") + elif (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + elif not self.ctx.is_prime(): + raise DomainError("gcd with non-prime modulus is not supported") + + i = self.ctx.variable_to_index(var) + res = create_nmod_mpoly(self.ctx) + if not nmod_mpoly_resultant(res.val, self.val, (other).val, i, self.ctx.val): + raise RuntimeError(f"failed to compute resultant with respect to {var}") + return res + + def discriminant(self, var): + """ + Return the discriminant of `self` with respect to variable `var`. + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> x0, x1 = ctx.gens() + >>> f = (x0 + x1)**2 + 1 + >>> f.discriminant('x1') + 7 + + """ + cdef: + nmod_mpoly res + slong i + + if not self.ctx.is_prime(): + raise DomainError("gcd with non-prime modulus is not supported") + + i = self.ctx.variable_to_index(var) + res = create_nmod_mpoly(self.ctx) + if not nmod_mpoly_discriminant(res.val, self.val, i, self.ctx.val): + raise RuntimeError(f"failed to compute discriminant with respect to {var}") + return res + def sqrt(self): """ Return the square root of self. @@ -975,6 +1053,18 @@ cdef class nmod_mpoly(flint_mpoly): nmod_mpoly_derivative(res.val, self.val, i, self.ctx.val) return res + def deflation(self): + """ + """ + cdef: + fmpz_vec shift = fmpz_vec(self.ctx.nvars()) + fmpz_vec stride = fmpz_vec(self.ctx.nvars()) + nmod_mpoly res = create_nmod_mpoly(self.ctx) + + nmod_mpoly_deflation(shift.val, stride.val, self.val, self.ctx.val) + nmod_mpoly_deflate(res.val, self.val, shift.val, stride.val, self.ctx.val) + return res, stride + cdef class nmod_mpoly_vec: """ From 67e7aa5ec4838b8ff4a9981cc59fa0ffe232ae99 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Wed, 4 Sep 2024 21:41:20 +1000 Subject: [PATCH 03/27] Deflation docs --- src/flint/types/fmpq_mpoly.pyx | 9 +++++++++ src/flint/types/fmpz_mod_mpoly.pyx | 9 +++++++++ src/flint/types/fmpz_mpoly.pyx | 9 +++++++++ src/flint/types/nmod_mpoly.pyx | 9 +++++++++ 4 files changed, 36 insertions(+) diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 64a1465c..dbef06a7 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -988,6 +988,15 @@ cdef class fmpq_mpoly(flint_mpoly): def deflation(self): """ + Compute the deflation of `self`. See Flint documentation for + details. Returns deflated polynomial and the stride vector. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) + >>> x, y = ctx.gens() + >>> f = x**3 * y + x * y**4 + x * y + >>> f.deflation() + (x + y + 1, fmpz_vec(['2', '3'], 2)) """ cdef: fmpz_vec shift = fmpz_vec(self.ctx.nvars()) diff --git a/src/flint/types/fmpz_mod_mpoly.pyx b/src/flint/types/fmpz_mod_mpoly.pyx index 5d584022..741f1325 100644 --- a/src/flint/types/fmpz_mod_mpoly.pyx +++ b/src/flint/types/fmpz_mod_mpoly.pyx @@ -1083,6 +1083,15 @@ cdef class fmpz_mod_mpoly(flint_mpoly): def deflation(self): """ + Compute the deflation of `self`. See Flint documentation for + details. Returns deflated polynomial and the stride vector. + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, nametup=('x', 'y')) + >>> x, y = ctx.gens() + >>> f = x**3 * y + x * y**4 + x * y + >>> f.deflation() + (x + y + 1, fmpz_vec(['2', '3'], 2)) """ cdef: fmpz_vec shift = fmpz_vec(self.ctx.nvars()) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index c39108cf..22690573 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -1068,6 +1068,15 @@ cdef class fmpz_mpoly(flint_mpoly): def deflation(self): """ + Compute the deflation of `self`. See Flint documentation for + details. Returns deflated polynomial and the stride vector. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) + >>> x, y = ctx.gens() + >>> f = x**3 * y + x * y**4 + x * y + >>> f.deflation() + (x + y + 1, fmpz_vec(['2', '3'], 2)) """ cdef: fmpz_vec shift = fmpz_vec(self.ctx.nvars()) diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx index 6f7cc64f..d0e406e7 100644 --- a/src/flint/types/nmod_mpoly.pyx +++ b/src/flint/types/nmod_mpoly.pyx @@ -1055,6 +1055,15 @@ cdef class nmod_mpoly(flint_mpoly): def deflation(self): """ + Compute the deflation of `self`. See Flint documentation for + details. Returns deflated polynomial and the stride vector. + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, nametup=('x', 'y')) + >>> x, y = ctx.gens() + >>> f = x**3 * y + x * y**4 + x * y + >>> f.deflation() + (x + y + 1, fmpz_vec(['2', '3'], 2)) """ cdef: fmpz_vec shift = fmpz_vec(self.ctx.nvars()) From 86254f26eb5d3e84aff3df19092897dcdd3969a6 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Wed, 4 Sep 2024 22:16:48 +1000 Subject: [PATCH 04/27] Add mpoly docs, clean up verbatim text --- doc/source/fmpq_mpoly.rst | 18 +++++++++ doc/source/fmpz_mod_mpoly.rst | 18 +++++++++ doc/source/fmpz_mpoly.rst | 18 +++++++++ doc/source/index.rst | 4 ++ doc/source/nmod_mpoly.rst | 18 +++++++++ src/flint/flint_base/flint_base.pyx | 18 ++++----- src/flint/types/fmpq_mpoly.pyx | 22 +++++------ src/flint/types/fmpz_mod_mpoly.pyx | 26 ++++++------- src/flint/types/fmpz_mpoly.pyx | 58 ++++++++++++++--------------- src/flint/types/nmod_mpoly.pyx | 26 ++++++------- 10 files changed, 151 insertions(+), 75 deletions(-) create mode 100644 doc/source/fmpq_mpoly.rst create mode 100644 doc/source/fmpz_mod_mpoly.rst create mode 100644 doc/source/fmpz_mpoly.rst create mode 100644 doc/source/nmod_mpoly.rst diff --git a/doc/source/fmpq_mpoly.rst b/doc/source/fmpq_mpoly.rst new file mode 100644 index 00000000..bce01253 --- /dev/null +++ b/doc/source/fmpq_mpoly.rst @@ -0,0 +1,18 @@ +**fmpq_mpoly** -- multivariate polynomials over the rational numbers +=============================================================================== + +.. autoclass :: flint.fmpq_mpoly_ctx + :members: + :inherited-members: + :undoc-members: + +.. autoclass :: flint.fmpq_mpoly + :members: + :inherited-members: + :undoc-members: + +.. autoclass :: flint.fmpq_mpoly_vec + :members: + :inherited-members: + :undoc-members: + diff --git a/doc/source/fmpz_mod_mpoly.rst b/doc/source/fmpz_mod_mpoly.rst new file mode 100644 index 00000000..d28c0faa --- /dev/null +++ b/doc/source/fmpz_mod_mpoly.rst @@ -0,0 +1,18 @@ +**fmpz_mod_mpoly** -- multivariate polynomials over the integers mod n +=============================================================================== + +.. autoclass :: flint.fmpz_mod_mpoly_ctx + :members: + :inherited-members: + :undoc-members: + +.. autoclass :: flint.fmpz_mod_mpoly + :members: + :inherited-members: + :undoc-members: + +.. autoclass :: flint.fmpz_mod_mpoly_vec + :members: + :inherited-members: + :undoc-members: + diff --git a/doc/source/fmpz_mpoly.rst b/doc/source/fmpz_mpoly.rst new file mode 100644 index 00000000..cbdb776e --- /dev/null +++ b/doc/source/fmpz_mpoly.rst @@ -0,0 +1,18 @@ +**fmpz_mpoly** -- multivariate polynomials over the integers +=============================================================================== + +.. autoclass :: flint.fmpz_mpoly_ctx + :members: + :inherited-members: + :undoc-members: + +.. autoclass :: flint.fmpz_mpoly + :members: + :inherited-members: + :undoc-members: + +.. autoclass :: flint.fmpz_mpoly_vec + :members: + :inherited-members: + :undoc-members: + diff --git a/doc/source/index.rst b/doc/source/index.rst index 410b04ba..887c68a7 100644 --- a/doc/source/index.rst +++ b/doc/source/index.rst @@ -73,9 +73,13 @@ Polynomial types :maxdepth: 1 fmpz_poly.rst + fmpz_mpoly.rst fmpq_poly.rst + fmpq_mpoly.rst nmod_poly.rst + nmod_mpoly.rst fmpz_mod_poly.rst + fmpz_mod_mpoly.rst fq_default_poly.rst arb_poly.rst acb_poly.rst diff --git a/doc/source/nmod_mpoly.rst b/doc/source/nmod_mpoly.rst new file mode 100644 index 00000000..b20c45ed --- /dev/null +++ b/doc/source/nmod_mpoly.rst @@ -0,0 +1,18 @@ +**nmod_mpoly** -- multivariate polynomials over the integers mod n (word-size n) +=============================================================================== + +.. autoclass :: flint.nmod_mpoly_ctx + :members: + :inherited-members: + :undoc-members: + +.. autoclass :: flint.nmod_mpoly + :members: + :inherited-members: + :undoc-members: + +.. autoclass :: flint.nmod_mpoly_vec + :members: + :inherited-members: + :undoc-members: + diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index 2771c023..c02f8c9d 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -229,7 +229,7 @@ cdef class flint_poly(flint_elem): integer root and *m* is the multiplicity of the root. To compute complex roots of a polynomial, instead use - the `.complex_roots()` method, which is available on + the ``.complex_roots()`` method, which is available on certain polynomial rings. >>> from flint import fmpz_poly @@ -322,9 +322,9 @@ cdef class flint_mpoly_context(flint_elem): @staticmethod def create_variable_names(slong nvars, names: str): """ - Create a tuple of variable names based on the comma separated `names` string. + Create a tuple of variable names based on the comma separated ``names`` string. - If `names` contains a single value, and `nvars` > 1, then the variables are numbered, e.g. + If ``names`` contains a single value, and ``nvars`` > 1, then the variables are numbered, e.g. >>> flint_mpoly_context.create_variable_names(3, "x") ('x0', 'x1', 'x2') @@ -344,24 +344,24 @@ cdef class flint_mpoly_context(flint_elem): Create a key for the context cache via the number of variables, the ordering, and either a variable name string, or a tuple of variable names. """ - # A type hint of `ordering: Ordering` results in the error "TypeError: an integer is required" if a Ordering + # A type hint of ``ordering: Ordering`` results in the error "TypeError: an integer is required" if a Ordering # object is not provided. This is pretty obtuse so we check its type ourselves if not isinstance(ordering, Ordering): - raise TypeError(f"`ordering` ('{ordering}') is not an instance of flint.Ordering") + raise TypeError(f"``ordering`` ('{ordering}') is not an instance of flint.Ordering") if nametup is not None: key = nvars, ordering, nametup elif nametup is None and names is not None: key = nvars, ordering, cls.create_variable_names(nvars, names) else: - raise ValueError("must provide either `names` or `nametup`") + raise ValueError("must provide either ``names`` or ``nametup``") return key @classmethod def get_context(cls, *args, **kwargs): """ - Retrieve a context via the number of variables, `nvars`, the ordering, `ordering`, and either a variable - name string, `names`, or a tuple of variable names, `nametup`. + Retrieve a context via the number of variables, ``nvars``, the ordering, ``ordering``, and either a variable + name string, ``names``, or a tuple of variable names, ``nametup``. """ key = cls.create_context_key(*args, **kwargs) @@ -725,7 +725,7 @@ cdef class flint_mpoly(flint_elem): def __contains__(self, x): """ - Returns True if `self` contains a term with exponent vector `x` and a non-zero coefficient. + Returns True if ``self`` contains a term with exponent vector ``x`` and a non-zero coefficient. >>> from flint import fmpq_mpoly_ctx, Ordering >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index dbef06a7..8c5fe36b 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -90,7 +90,7 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): :param ordering: The term order for the ring :param names: A tuple containing the names of the variables of the ring. - Do not construct one of these directly, use `fmpz_mpoly_ctx.get_context`. + Do not construct one of these directly, use ``fmpz_mpoly_ctx.get_context``. """ _ctx_cache = _fmpq_mpoly_ctx_cache @@ -139,7 +139,7 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): def gen(self, slong i): """ - Return the `i`th generator of the polynomial ring + Return the ``i`` th generator of the polynomial ring >>> from flint import Ordering >>> ctx = fmpq_mpoly_ctx.get_context(3, Ordering.degrevlex, 'z') @@ -299,8 +299,8 @@ cdef class fmpq_mpoly(flint_mpoly): def __getitem__(self, x): """ - Return the coefficient of the term with the exponent vector `x`. - Always returns a value, missing keys will return `0`. + Return the coefficient of the term with the exponent vector ``x``. + Always returns a value, missing keys will return ``0``. Negative exponents are made positive. >>> from flint import Ordering @@ -325,7 +325,7 @@ cdef class fmpq_mpoly(flint_mpoly): def __setitem__(self, x, y): """ - Set the coefficient of the term with the exponent vector `x` to `y`. + Set the coefficient of the term with the exponent vector ``x`` to ``y``. Will always set a value, missing keys will create a new term. Negative exponents are made positive. @@ -657,7 +657,7 @@ cdef class fmpq_mpoly(flint_mpoly): def coefficient(self, slong i): """ - Return the coefficient at index `i`. + Return the coefficient at index ``i``. >>> from flint import Ordering >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') @@ -675,7 +675,7 @@ cdef class fmpq_mpoly(flint_mpoly): def monomial(self, slong i): """ - Return the exponent vector at index `i` as a tuple. + Return the exponent vector at index ``i`` as a tuple. >>> from flint import Ordering >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') @@ -776,7 +776,7 @@ cdef class fmpq_mpoly(flint_mpoly): def term_content(self): """ - Return the GCD of the terms of `self`. If `self` is zero, then the result will + Return the GCD of the terms of ``self``. If ``self`` is zero, then the result will be zero, otherwise it will be a monomial with positive coefficient. >>> from flint import Ordering @@ -792,7 +792,7 @@ cdef class fmpq_mpoly(flint_mpoly): def resultant(self, other, var): """ - Return the resultant of `self` and `other` with respect to variable `var`. + Return the resultant of ``self`` and ``other`` with respect to variable ``var``. >>> from flint import Ordering >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') @@ -819,7 +819,7 @@ cdef class fmpq_mpoly(flint_mpoly): def discriminant(self, var): """ - Return the discriminant of `self` with respect to variable `var`. + Return the discriminant of ``self`` with respect to variable ``var``. >>> from flint import Ordering >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') @@ -988,7 +988,7 @@ cdef class fmpq_mpoly(flint_mpoly): def deflation(self): """ - Compute the deflation of `self`. See Flint documentation for + Compute the deflation of ``self``. See Flint documentation for details. Returns deflated polynomial and the stride vector. >>> from flint import Ordering diff --git a/src/flint/types/fmpz_mod_mpoly.pyx b/src/flint/types/fmpz_mod_mpoly.pyx index 741f1325..84760b84 100644 --- a/src/flint/types/fmpz_mod_mpoly.pyx +++ b/src/flint/types/fmpz_mod_mpoly.pyx @@ -82,7 +82,7 @@ cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context): :param ordering: The term order for the ring :param names: A tuple containing the names of the variables of the ring. - Do not construct one of these directly, use `fmpz_mod_mpoly_ctx.get_context`. + Do not construct one of these directly, use ``fmpz_mod_mpoly_ctx.get_context``. """ _ctx_cache = _fmpz_mod_mpoly_ctx_cache @@ -112,7 +112,7 @@ cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context): Create a key for the context cache via the number of variables, the ordering, the modulus, and either a variable name string, or a tuple of variable names. """ - # A type hint of `ordering: Ordering` results in the error "TypeError: an integer is required" if a Ordering + # A type hint of ``ordering: Ordering`` results in the error "TypeError: an integer is required" if a Ordering # object is not provided. This is pretty obtuse so we check its type ourselves if not isinstance(ordering, Ordering): raise TypeError(f"`ordering` ('{ordering}') is not an instance of flint.Ordering") @@ -213,7 +213,7 @@ cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context): def gen(self, slong i): """ - Return the `i`th generator of the polynomial ring + Return the ``i`` th generator of the polynomial ring >>> from flint import Ordering >>> ctx = fmpz_mod_mpoly_ctx.get_context(3, Ordering.degrevlex, 11, 'z') @@ -286,7 +286,7 @@ cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context): cdef class fmpz_mod_mpoly(flint_mpoly): """ The *fmpz_mod_mpoly* type represents sparse multivariate polynomials over - the integers modulo `n`, for large `n`. + the integers modulo ``n``, for large ``n``. """ def __cinit__(self): @@ -376,8 +376,8 @@ cdef class fmpz_mod_mpoly(flint_mpoly): def __getitem__(self, x): """ - Return the coefficient of the term with the exponent vector `x`. - Always returns a value, missing keys will return `0`. + Return the coefficient of the term with the exponent vector ``x``. + Always returns a value, missing keys will return ``0``. Negative exponents are made positive. >>> from flint import Ordering @@ -402,7 +402,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): def __setitem__(self, x, y): """ - Set the coefficient of the term with the exponent vector `x` to `y`. + Set the coefficient of the term with the exponent vector ``x`` to ``y``. Will always set a value, missing keys will create a new term. Negative exponents are made positive. @@ -730,7 +730,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): def coefficient(self, slong i): """ - Return the coefficient at index `i`. + Return the coefficient at index ``i``. >>> from flint import Ordering >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') @@ -748,7 +748,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): def monomial(self, slong i): """ - Return the exponent vector at index `i` as a tuple. + Return the exponent vector at index ``i`` as a tuple. >>> from flint import Ordering >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') @@ -849,7 +849,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): def term_content(self): """ - Return the GCD of the terms of `self`. If `self` is zero, then the result will + Return the GCD of the terms of ``self``. If ``self`` is zero, then the result will be zero, otherwise it will be a monomial with positive coefficient. >>> from flint import Ordering @@ -868,7 +868,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): def resultant(self, other, var): """ - Return the resultant of `self` and `other` with respect to variable `var`. + Return the resultant of ``self`` and ``other`` with respect to variable ``var``. >>> from flint import Ordering >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') @@ -897,7 +897,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): def discriminant(self, var): """ - Return the discriminant of `self` with respect to variable `var`. + Return the discriminant of ``self`` with respect to variable ``var``. >>> from flint import Ordering >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') @@ -1083,7 +1083,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): def deflation(self): """ - Compute the deflation of `self`. See Flint documentation for + Compute the deflation of ``self``. See Flint documentation for details. Returns deflated polynomial and the stride vector. >>> from flint import Ordering diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 22690573..51dcf95c 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -91,7 +91,7 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): :param ordering: The term order for the ring :param names: A tuple containing the names of the variables of the ring. - Do not construct one of these directly, use `fmpz_mpoly_ctx.get_context`. + Do not construct one of these directly, use ``fmpz_mpoly_ctx.get_context``. """ _ctx_cache = _fmpz_mpoly_ctx_cache @@ -138,7 +138,7 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): def gen(self, slong i): """ - Return the `i`th generator of the polynomial ring + Return the ``i`` th generator of the polynomial ring >>> from flint import Ordering >>> ctx = fmpz_mpoly_ctx.get_context(3, Ordering.degrevlex, 'z') @@ -281,8 +281,8 @@ cdef class fmpz_mpoly(flint_mpoly): def __getitem__(self, x): """ - Return the coefficient of the term with the exponent vector `x`. - Always returns a value, missing keys will return `0`. + Return the coefficient of the term with the exponent vector ``x``. + Always returns a value, missing keys will return ``0``. Negative exponents are made positive. >>> from flint import Ordering @@ -307,7 +307,7 @@ cdef class fmpz_mpoly(flint_mpoly): def __setitem__(self, x, y): """ - Set the coefficient of the term with the exponent vector `x` to `y`. + Set the coefficient of the term with the exponent vector ``x`` to ``y``. Will always set a value, missing keys will create a new term. Negative exponents are made positive. @@ -639,7 +639,7 @@ cdef class fmpz_mpoly(flint_mpoly): def coefficient(self, slong i): """ - Return the coefficient at index `i`. + Return the coefficient at index ``i``. >>> from flint import Ordering >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') @@ -657,7 +657,7 @@ cdef class fmpz_mpoly(flint_mpoly): def monomial(self, slong i): """ - Return the exponent vector at index `i` as a tuple. + Return the exponent vector at index ``i`` as a tuple. >>> from flint import Ordering >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') @@ -756,7 +756,7 @@ cdef class fmpz_mpoly(flint_mpoly): def term_content(self): """ - Return the GCD of the terms of `self`. If `self` is zero, then the result will + Return the GCD of the terms of ``self``. If ``self`` is zero, then the result will be zero, otherwise it will be a monomial with positive coefficient. >>> from flint import Ordering @@ -772,7 +772,7 @@ cdef class fmpz_mpoly(flint_mpoly): def resultant(self, other, var): """ - Return the resultant of `self` and `other` with respect to variable `var`. + Return the resultant of ``self`` and ``other`` with respect to variable ``var``. >>> from flint import Ordering >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') @@ -799,7 +799,7 @@ cdef class fmpz_mpoly(flint_mpoly): def discriminant(self, var): """ - Return the discriminant of `self` with respect to variable `var`. + Return the discriminant of ``self`` with respect to variable ``var``. >>> from flint import Ordering >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') @@ -821,7 +821,7 @@ cdef class fmpz_mpoly(flint_mpoly): def primitive_part(self): """ - Return the primitive part of `self` obtained by dividing out the content of + Return the primitive part of ``self`` obtained by dividing out the content of all coefficients and normalizing the leading coefficient to be positive. >>> from flint import Ordering @@ -840,7 +840,7 @@ cdef class fmpz_mpoly(flint_mpoly): def sqrt(self, assume_perfect_square: bool = False): """ Return the square root of self. - If self is known to be a perfect square provide `assume_perfect_square=True` for a more efficient + If self is known to be a perfect square provide ``assume_perfect_square=True`` for a more efficient result. If self is not a square root the result is not guaranteed to be correct. >>> from flint import Ordering @@ -1022,7 +1022,7 @@ cdef class fmpz_mpoly(flint_mpoly): def spoly(self, g): """ - Compute the S-polynomial of `self` and `g`, scaled to an integer polynomial + Compute the S-polynomial of ``self`` and ``g``, scaled to an integer polynomial by computing the LCM of the leading coefficients. >>> from flint import Ordering @@ -1044,7 +1044,7 @@ cdef class fmpz_mpoly(flint_mpoly): def reduction_primitive_part(self, vec): """ Compute the the primitive part of the reduction (remainder of multivariate - quasi-division with remainder) with respect to the polynomials `vec`. + quasi-division with remainder) with respect to the polynomials ``vec``. >>> from flint import Ordering >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) @@ -1068,7 +1068,7 @@ cdef class fmpz_mpoly(flint_mpoly): def deflation(self): """ - Compute the deflation of `self`. See Flint documentation for + Compute the deflation of ``self``. See Flint documentation for details. Returns deflated polynomial and the stride vector. >>> from flint import Ordering @@ -1182,8 +1182,8 @@ cdef class fmpz_mpoly_vec: def is_groebner(self, other=None) -> bool: """ - Check if self is a Gröbner basis. If `other` is not None then check if self - is a Gröbner basis for `other`. + Check if self is a Gröbner basis. If ``other`` is not None then check if self + is a Gröbner basis for ``other``. >>> from flint import Ordering >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) @@ -1235,9 +1235,9 @@ cdef class fmpz_mpoly_vec: def autoreduction(self, groebner=False) -> fmpz_mpoly_vec: """ - Compute the autoreduction of `self`. If `groebner` is True and `self` is a - Gröbner basis, compute the reduced reduced Gröbner basis of `self`, throws an - `RuntimeError` otherwise. + Compute the autoreduction of ``self``. If ``groebner`` is True and ``self`` is a + Gröbner basis, compute the reduced reduced Gröbner basis of ``self``, throws an + ``RuntimeError`` otherwise. >>> from flint import Ordering >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) @@ -1260,7 +1260,7 @@ cdef class fmpz_mpoly_vec: if groebner: if not self.is_groebner(): raise RuntimeError( - "reduced Gröbner basis construction requires that `self` is a " + "reduced Gröbner basis construction requires that ``self`` is a " "Gröbner basis." ) fmpz_mpoly_vec_autoreduction_groebner(h.val, self.val, self.ctx.val) @@ -1271,21 +1271,21 @@ cdef class fmpz_mpoly_vec: def buchberger_naive(self, limits=None): """ - Compute the Gröbner basis of `self` using a naive implementation of + Compute the Gröbner basis of ``self`` using a naive implementation of Buchberger’s algorithm. - Provide `limits` in the form of a tuple of `(ideal_len_limit, poly_len_limit, - poly_bits_limit)` to halt execution if the length of the ideal basis set exceeds - `ideal_len_limit`, the length of any polynomial exceeds `poly_len_limit`, or the - size of the coefficients of any polynomial exceeds `poly_bits_limit`. + Provide ``limits`` in the form of a tuple of ``(ideal_len_limit, poly_len_limit, + poly_bits_limit)`` to halt execution if the length of the ideal basis set exceeds + ``ideal_len_limit``, the length of any polynomial exceeds ``poly_len_limit``, or the + size of the coefficients of any polynomial exceeds ``poly_bits_limit``. - If limits is provided return a tuple of `(result, success)`. If `success` is - False then `result` is a valid basis for `self`, but it may not be a Gröbner + If limits is provided return a tuple of ``(result, success)``. If ``success`` is + False then ``result`` is a valid basis for ``self``, but it may not be a Gröbner basis. NOTE: This function is exposed only for convenience, it is a naive implementation and does not compute a reduced basis. To construct a reduced - basis use `autoreduce`. + basis use ``autoreduce``. >>> from flint import Ordering >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx index d0e406e7..0af8d5b7 100644 --- a/src/flint/types/nmod_mpoly.pyx +++ b/src/flint/types/nmod_mpoly.pyx @@ -86,7 +86,7 @@ cdef class nmod_mpoly_ctx(flint_mpoly_context): :param ordering: The term order for the ring :param names: A tuple containing the names of the variables of the ring. - Do not construct one of these directly, use `nmod_mpoly_ctx.get_context`. + Do not construct one of these directly, use ``nmod_mpoly_ctx.get_context``. """ _ctx_cache = _nmod_mpoly_ctx_cache @@ -112,7 +112,7 @@ cdef class nmod_mpoly_ctx(flint_mpoly_context): Create a key for the context cache via the number of variables, the ordering, the modulus, and either a variable name string, or a tuple of variable names. """ - # A type hint of `ordering: Ordering` results in the error "TypeError: an integer is required" if a Ordering + # A type hint of ``ordering: Ordering`` results in the error "TypeError: an integer is required" if a Ordering # object is not provided. This is pretty obtuse so we check its type ourselves if not isinstance(ordering, Ordering): raise TypeError(f"`ordering` ('{ordering}') is not an instance of flint.Ordering") @@ -204,7 +204,7 @@ cdef class nmod_mpoly_ctx(flint_mpoly_context): def gen(self, slong i): """ - Return the `i`th generator of the polynomial ring + Return the ``i`` th generator of the polynomial ring >>> from flint import Ordering >>> ctx = nmod_mpoly_ctx.get_context(3, Ordering.degrevlex, 11, 'z') @@ -352,8 +352,8 @@ cdef class nmod_mpoly(flint_mpoly): def __getitem__(self, x): """ - Return the coefficient of the term with the exponent vector `x`. - Always returns a value, missing keys will return `0`. + Return the coefficient of the term with the exponent vector ``x``. + Always returns a value, missing keys will return ``0``. Negative exponents are made positive. >>> from flint import Ordering @@ -376,7 +376,7 @@ cdef class nmod_mpoly(flint_mpoly): def __setitem__(self, x, y): """ - Set the coefficient of the term with the exponent vector `x` to `y`. + Set the coefficient of the term with the exponent vector ``x`` to ``y``. Will always set a value, missing keys will create a new term. Negative exponents are made positive. @@ -541,7 +541,7 @@ cdef class nmod_mpoly(flint_mpoly): args = [self.ctx.any_as_scalar(x) for x in args] cdef: - # Using sizeof(ulong) here breaks on 64 windows machines because of the `ctypedef unsigned long ulong` in + # Using sizeof(ulong) here breaks on 64 windows machines because of the ``ctypedef unsigned long ulong`` in # flintlib/flint.pxd. Cython will inline this definition and then allocate the wrong amount of memory. ulong *vals = libc.stdlib.malloc(nargs * SIZEOF_ULONG) ulong res @@ -707,7 +707,7 @@ cdef class nmod_mpoly(flint_mpoly): def coefficient(self, slong i): """ - Return the coefficient at index `i`. + Return the coefficient at index ``i``. >>> from flint import Ordering >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') @@ -722,7 +722,7 @@ cdef class nmod_mpoly(flint_mpoly): def monomial(self, slong i): """ - Return the exponent vector at index `i` as a tuple. + Return the exponent vector at index ``i`` as a tuple. >>> from flint import Ordering >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') @@ -823,7 +823,7 @@ cdef class nmod_mpoly(flint_mpoly): def term_content(self): """ - Return the GCD of the terms of `self`. If `self` is zero, then the result will + Return the GCD of the terms of ``self``. If ``self`` is zero, then the result will be zero, otherwise it will be a monomial with positive coefficient. >>> from flint import Ordering @@ -842,7 +842,7 @@ cdef class nmod_mpoly(flint_mpoly): def resultant(self, other, var): """ - Return the resultant of `self` and `other` with respect to variable `var`. + Return the resultant of ``self`` and ``other`` with respect to variable ``var``. >>> from flint import Ordering >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') @@ -871,7 +871,7 @@ cdef class nmod_mpoly(flint_mpoly): def discriminant(self, var): """ - Return the discriminant of `self` with respect to variable `var`. + Return the discriminant of ``self`` with respect to variable ``var``. >>> from flint import Ordering >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') @@ -1055,7 +1055,7 @@ cdef class nmod_mpoly(flint_mpoly): def deflation(self): """ - Compute the deflation of `self`. See Flint documentation for + Compute the deflation of ``self``. See Flint documentation for details. Returns deflated polynomial and the stride vector. >>> from flint import Ordering From f07bdc9a254e0ecdbe273a398b5efd448bd19a94 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Wed, 4 Sep 2024 23:57:34 +1000 Subject: [PATCH 05/27] Add tests --- src/flint/test/test_all.py | 21 +++++++++++++++++++++ src/flint/types/fmpz_mod_mpoly.pyx | 7 ------- src/flint/types/nmod_mpoly.pyx | 7 ------- 3 files changed, 21 insertions(+), 14 deletions(-) diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index 9494302f..0d847afa 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -3229,6 +3229,27 @@ def quick_poly(): assert raises(lambda: quick_poly().gcd(None), TypeError) assert raises(lambda: quick_poly().gcd(P(ctx=ctx1)), IncompatibleContextError) + x0, x1 = ctx.gens() + assert (2*x0**3 + 4*x0**2 + 6*x0).term_content() == x0 if is_field else 2*x0 + assert (3*x0**2*x1 + 6*x0*x1**2 + 9*x1**3).term_content() == x1 if is_field else 3*x1 + + assert (x0**2 - 1).resultant(x0**2 - 2*x0 + 1, 'x0') == 0 + assert (x0**2 + x1**2 - 1).resultant(x0 - x1, 'x0') == 2*x1**2 - 1 + + assert (x0**3 - 6*x0**2 + 11*x0 - 6).discriminant('x0') == 4 + assert (x0**2 + 4*x0*x1 + 4*x1**2 - 1).discriminant('x0') == 4 + + res, stride = (3*x0**2*x1**2 + 6*x0*x1**2 + 9*x1**2).deflation() + assert res == 3*x0**2 + 6*x0 + 9 + assert stride.to_tuple() == (1, 0) + + res, stride = ((x0**2 + x1**2)**3 + (x0**2 + x1**2)**2 + 1).deflation() + assert res == x0**3 + 3*x0**2*x1 + x0**2 + 3*x0*x1**2 + 2*x0*x1 + x1**3 + x1**2 + 1 + assert stride.to_tuple() == (2, 2) + + if P is flint.fmpz_mpoly: + assert (x0**2 * x1 + x0 * x1).primitive_part() == x0**2*x1 + x0*x1 + if composite_characteristic: # Factorisation not allowed over Z/nZ for n not prime. # Flint would abort so we raise an exception instead: diff --git a/src/flint/types/fmpz_mod_mpoly.pyx b/src/flint/types/fmpz_mod_mpoly.pyx index 84760b84..ac50ec38 100644 --- a/src/flint/types/fmpz_mod_mpoly.pyx +++ b/src/flint/types/fmpz_mod_mpoly.pyx @@ -859,8 +859,6 @@ cdef class fmpz_mod_mpoly(flint_mpoly): >>> f.term_content() x0*x1 """ - if not self.ctx.is_prime(): - raise DomainError("gcd with non-prime modulus is not supported") cdef fmpz_mod_mpoly res = create_fmpz_mod_mpoly(self.ctx) fmpz_mod_mpoly_term_content(res.val, self.val, self.ctx.val) @@ -886,8 +884,6 @@ cdef class fmpz_mod_mpoly(flint_mpoly): raise TypeError("argument must be a fmpz_mod_mpoly") elif (self).ctx is not (other).ctx: raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - elif not self.ctx.is_prime(): - raise DomainError("gcd with non-prime modulus is not supported") i = self.ctx.variable_to_index(var) res = create_fmpz_mod_mpoly(self.ctx) @@ -911,9 +907,6 @@ cdef class fmpz_mod_mpoly(flint_mpoly): fmpz_mod_mpoly res slong i - if not self.ctx.is_prime(): - raise DomainError("gcd with non-prime modulus is not supported") - i = self.ctx.variable_to_index(var) res = create_fmpz_mod_mpoly(self.ctx) if not fmpz_mod_mpoly_discriminant(res.val, self.val, i, self.ctx.val): diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx index 0af8d5b7..847732b2 100644 --- a/src/flint/types/nmod_mpoly.pyx +++ b/src/flint/types/nmod_mpoly.pyx @@ -833,8 +833,6 @@ cdef class nmod_mpoly(flint_mpoly): >>> f.term_content() x0*x1 """ - if not self.ctx.is_prime(): - raise DomainError("gcd with non-prime modulus is not supported") cdef nmod_mpoly res = create_nmod_mpoly(self.ctx) nmod_mpoly_term_content(res.val, self.val, self.ctx.val) @@ -860,8 +858,6 @@ cdef class nmod_mpoly(flint_mpoly): raise TypeError("argument must be a nmod_mpoly") elif (self).ctx is not (other).ctx: raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - elif not self.ctx.is_prime(): - raise DomainError("gcd with non-prime modulus is not supported") i = self.ctx.variable_to_index(var) res = create_nmod_mpoly(self.ctx) @@ -885,9 +881,6 @@ cdef class nmod_mpoly(flint_mpoly): nmod_mpoly res slong i - if not self.ctx.is_prime(): - raise DomainError("gcd with non-prime modulus is not supported") - i = self.ctx.variable_to_index(var) res = create_nmod_mpoly(self.ctx) if not nmod_mpoly_discriminant(res.val, self.val, i, self.ctx.val): From ac4c3e4e38b4fcda631d59ca5391887a4c9b6faf Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Fri, 6 Sep 2024 00:37:59 +1000 Subject: [PATCH 06/27] Add content method for fmpz_mpoly and fmpz_poly --- src/flint/flintlib/fmpz_vec.pxd | 3 +++ src/flint/types/fmpz_mpoly.pyx | 16 ++++++++++++++++ src/flint/types/fmpz_poly.pyx | 12 ++++++++++++ 3 files changed, 31 insertions(+) diff --git a/src/flint/flintlib/fmpz_vec.pxd b/src/flint/flintlib/fmpz_vec.pxd index 26924893..8b1eb6c6 100644 --- a/src/flint/flintlib/fmpz_vec.pxd +++ b/src/flint/flintlib/fmpz_vec.pxd @@ -66,3 +66,6 @@ cdef extern from "flint/fmpz_vec.h": void _fmpz_vec_lcm(fmpz_t res, const fmpz_struct * vec, slong len) void _fmpz_vec_dot(fmpz_t res, const fmpz_struct * vec1, const fmpz_struct * vec2, slong len2) void _fmpz_vec_dot_ptr(fmpz_t res, const fmpz_struct * vec1, fmpz_struct ** const vec2, slong offset, slong len) + + # Undocumented internal functions + void _fmpz_vec_content(fmpz_t res, const fmpz_struct * vec, slong len) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 51dcf95c..a47a1340 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -76,6 +76,7 @@ from flint.flintlib.fmpz_mpoly_factor cimport ( fmpz_mpoly_factor_squarefree, fmpz_mpoly_factor_t, ) +from flint.flintlib.fmpz_vec cimport _fmpz_vec_content from cpython.object cimport Py_EQ, Py_NE cimport libc.stdlib @@ -754,6 +755,21 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_gcd(res.val, (self).val, (other).val, res.ctx.val) return res + def content(self): + """ + Return the GCD of the coefficients of ``self``. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> x0, x1 = ctx.gens() + >>> f = 3 * x0**2 * x1 + 6 * x0 * x1 + >>> f.content() + 3 + """ + cdef fmpz res = fmpz() + _fmpz_vec_content(res.val, self.val.coeffs, self.val.length) + return res + def term_content(self): """ Return the GCD of the terms of ``self``. If ``self`` is zero, then the result will diff --git a/src/flint/types/fmpz_poly.pyx b/src/flint/types/fmpz_poly.pyx index ec9b761f..6ecee9fd 100644 --- a/src/flint/types/fmpz_poly.pyx +++ b/src/flint/types/fmpz_poly.pyx @@ -25,6 +25,7 @@ from flint.flintlib.arith cimport * from flint.flintlib.acb cimport * from flint.flintlib.arb_poly cimport * from flint.flintlib.arb_fmpz_poly cimport * +from flint.flintlib.fmpz_vec cimport _fmpz_vec_content from flint.utils.flint_exceptions import DomainError @@ -647,3 +648,14 @@ cdef class fmpz_poly(flint_poly): return int(i) libc.stdlib.free(phi) return 0 + + def content(self): + """ + Return the GCD of the coefficients of ``self``. + + >>> fmpz_poly([3, 6, 0]).content() + 3 + """ + cdef fmpz res = fmpz() + _fmpz_vec_content(res.val, self.val.coeffs, self.val.length) + return res From a5f4d2cf4078c4c43711be9d3d4802514729bdcb Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Fri, 6 Sep 2024 01:20:12 +1000 Subject: [PATCH 07/27] Add missing truediv_scalar methods to fmpz_mpoly and fmpq_mpoly --- src/flint/flint_base/flint_base.pxd | 1 + src/flint/flint_base/flint_base.pyx | 11 +++++++++-- src/flint/types/fmpq_mpoly.pyx | 8 ++++++++ src/flint/types/fmpz_mpoly.pyx | 15 +++++++++++++++ 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/src/flint/flint_base/flint_base.pxd b/src/flint/flint_base/flint_base.pxd index d9d31c73..04483200 100644 --- a/src/flint/flint_base/flint_base.pxd +++ b/src/flint/flint_base/flint_base.pxd @@ -23,6 +23,7 @@ cdef class flint_mpoly(flint_elem): cdef _mul_mpoly_(self, other) cdef _divmod_mpoly_(self, other) + cdef _truediv_scalar_(self, other, assume_exact: bool) cdef _floordiv_mpoly_(self, other) cdef _truediv_mpoly_(self, other) cdef _mod_mpoly_(self, other) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index c02f8c9d..50a6b776 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -405,7 +405,7 @@ cdef class flint_mpoly(flint_elem): def _division_check(self, other): if not other: - raise ZeroDivisionError("nmod_mpoly division by zero") + raise ZeroDivisionError(f"{self.__class__.__name__} division by zero") cdef _add_scalar_(self, other): return NotImplemented @@ -431,6 +431,9 @@ cdef class flint_mpoly(flint_elem): cdef _floordiv_mpoly_(self, other): return NotImplemented + cdef _truediv_scalar_(self, other, assume_exact: bool): + return NotImplemented + cdef _truediv_mpoly_(self, other): return NotImplemented @@ -589,8 +592,12 @@ cdef class flint_mpoly(flint_elem): if other is NotImplemented: return NotImplemented - other = self.context().scalar_as_mpoly(other) self._division_check(other) + res = self._truediv_scalar_(other, False) + if res is not NotImplemented: + return res + + other = self.context().scalar_as_mpoly(other) return self._truediv_mpoly_(other) def __rtruediv__(self, other): diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 8c5fe36b..fbf8a006 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -49,6 +49,7 @@ from flint.flintlib.fmpq_mpoly cimport ( fmpq_mpoly_push_term_fmpq_ffmpz, fmpq_mpoly_reduce, fmpq_mpoly_resultant, + fmpq_mpoly_scalar_div_fmpq, fmpq_mpoly_scalar_mul_fmpq, fmpq_mpoly_set, fmpq_mpoly_set_coeff_fmpq_fmpz, @@ -417,6 +418,13 @@ cdef class fmpq_mpoly(flint_mpoly): fmpq_mpoly_div(quotient.val, self.val, other.val, self.ctx.val) return quotient + cdef _truediv_scalar_(self, arg, _: bool): + cdef fmpq_mpoly quotient + cdef fmpq other = arg + quotient = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_scalar_div_fmpq(quotient.val, self.val, other.val, self.ctx.val) + return quotient + cdef _truediv_mpoly_(self, arg): cdef fmpq_mpoly quotient, other = arg quotient = create_fmpq_mpoly(self.ctx) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index a47a1340..5b4b2742 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -49,6 +49,8 @@ from flint.flintlib.fmpz_mpoly cimport ( fmpz_mpoly_push_term_fmpz_ffmpz, fmpz_mpoly_reduction_primitive_part, fmpz_mpoly_resultant, + fmpz_mpoly_scalar_divexact_fmpz, + fmpz_mpoly_scalar_divides_fmpz, fmpz_mpoly_scalar_mul_fmpz, fmpz_mpoly_set, fmpz_mpoly_set_coeff_fmpz_fmpz, @@ -400,6 +402,19 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_div(quotient.val, self.val, other.val, self.ctx.val) return quotient + cdef _truediv_scalar_(self, arg, assume_exact: bool): + cdef fmpz_mpoly quotient, + cdef fmpz other = arg + quotient = create_fmpz_mpoly(self.ctx) + if assume_exact: + fmpz_mpoly_scalar_divexact_fmpz(quotient.val, self.val, other.val, self.ctx.val) + return quotient + + if fmpz_mpoly_scalar_divides_fmpz(quotient.val, self.val, other.val, self.ctx.val): + return quotient + else: + raise DomainError("fmpz_mpoly division is not exact") + cdef _truediv_mpoly_(self, arg): cdef fmpz_mpoly quotient, other = arg quotient = create_fmpz_mpoly(self.ctx) From 9e7bcb57a61c423bf90539489c16383c93502e87 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Fri, 6 Sep 2024 02:23:53 +1000 Subject: [PATCH 08/27] Remove to_tuple, replace with __iter__ generator --- src/flint/test/test_all.py | 9 +++++---- src/flint/types/fmpq_mpoly.pyx | 14 +++++++++----- src/flint/types/fmpq_vec.pyx | 10 +++++++--- src/flint/types/fmpz_mod_mpoly.pyx | 14 +++++++++----- src/flint/types/fmpz_mpoly.pyx | 14 +++++++++----- src/flint/types/fmpz_vec.pyx | 10 +++++++--- src/flint/types/nmod_mpoly.pyx | 14 +++++++++----- 7 files changed, 55 insertions(+), 30 deletions(-) diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index 0d847afa..8ca29d52 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -3241,14 +3241,15 @@ def quick_poly(): res, stride = (3*x0**2*x1**2 + 6*x0*x1**2 + 9*x1**2).deflation() assert res == 3*x0**2 + 6*x0 + 9 - assert stride.to_tuple() == (1, 0) + assert tuple(stride) == (1, 0) res, stride = ((x0**2 + x1**2)**3 + (x0**2 + x1**2)**2 + 1).deflation() assert res == x0**3 + 3*x0**2*x1 + x0**2 + 3*x0*x1**2 + 2*x0*x1 + x1**3 + x1**2 + 1 - assert stride.to_tuple() == (2, 2) + assert tuple(stride) == (2, 2) if P is flint.fmpz_mpoly: - assert (x0**2 * x1 + x0 * x1).primitive_part() == x0**2*x1 + x0*x1 + assert (x0**2 * x1 + x0 * x1).primitive() == (1, x0**2*x1 + x0*x1) + assert (4 * x0 + 2 * x0 * x1).primitive() == (2, x0 * x1 + 2 * x0) if composite_characteristic: # Factorisation not allowed over Z/nZ for n not prime. @@ -3325,7 +3326,7 @@ def test_fmpz_mpoly_vec(): assert vec != mpoly_vec([x, x * y, ctx.from_dict({})], ctx) assert vec != mpoly_vec([ctx.from_dict({})], ctx) assert vec != mpoly_vec([ctx1.from_dict({})], ctx1) - assert vec.to_tuple() == mpoly_vec([ctx.from_dict({}), x * y, ctx.from_dict({})], ctx).to_tuple() + assert tuple(vec) == tuple(mpoly_vec([ctx.from_dict({}), x * y, ctx.from_dict({})], ctx)) assert raises(lambda: vec.__setitem__(None, 0), TypeError) assert raises(lambda: vec.__setitem__(-1, 0), IndexError) assert raises(lambda: vec.__setitem__(0, 0), TypeError) diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index fbf8a006..a106df5b 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -520,7 +520,7 @@ cdef class fmpq_mpoly(flint_mpoly): res = [] for i in range(len(self)): fmpq_mpoly_get_term_exp_fmpz(vec.double_indirect, self.val, i, self.ctx.val) - res.append(vec.to_tuple()) + res.append(tuple(vec)) return res @@ -698,7 +698,7 @@ cdef class fmpq_mpoly(flint_mpoly): raise IndexError("term index out of range") res = fmpz_vec(nvars, double_indirect=True) fmpq_mpoly_get_term_exp_fmpz(res.double_indirect, self.val, i, self.ctx.val) - return res.to_tuple() + return tuple(res) def degrees(self): """ @@ -715,7 +715,7 @@ cdef class fmpq_mpoly(flint_mpoly): res = fmpz_vec(nvars, double_indirect=True) fmpq_mpoly_degrees_fmpz(res.double_indirect, self.val, self.ctx.val) - return res.to_tuple() + return tuple(res) def total_degree(self): """ @@ -1103,5 +1103,9 @@ cdef class fmpq_mpoly_vec: else: return NotImplemented - def to_tuple(self): - return tuple(self[i] for i in range(self.length)) + def __iter__(self): + cdef fmpq_mpoly z + for i in range(self.length): + z = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_set(z.val, &self.val[i], self.ctx.val) + yield z diff --git a/src/flint/types/fmpq_vec.pyx b/src/flint/types/fmpq_vec.pyx index a4d01d79..5ce54018 100644 --- a/src/flint/types/fmpq_vec.pyx +++ b/src/flint/types/fmpq_vec.pyx @@ -67,6 +67,13 @@ cdef class fmpq_vec: def __repr__(self): return self.repr() + def __iter__(self): + cdef fmpq z + for i in range(self.length): + z = fmpq.__new__(fmpq) + fmpq_set(z.val, &self.val[i]) + yield z + def str(self, *args): s = [None] * self.length for i in range(self.length): @@ -78,6 +85,3 @@ cdef class fmpq_vec: def repr(self, *args): return f"fmpq_vec({self.str(*args)}, {self.length})" - - def to_tuple(self): - return tuple(self[i] for i in range(self.length)) diff --git a/src/flint/types/fmpz_mod_mpoly.pyx b/src/flint/types/fmpz_mod_mpoly.pyx index ac50ec38..6063ee89 100644 --- a/src/flint/types/fmpz_mod_mpoly.pyx +++ b/src/flint/types/fmpz_mod_mpoly.pyx @@ -588,7 +588,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): res = [] for i in range(len(self)): fmpz_mod_mpoly_get_term_exp_fmpz(vec.double_indirect, self.val, i, self.ctx.val) - res.append(vec.to_tuple()) + res.append(tuple(vec)) return res @@ -763,7 +763,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): raise IndexError("term index out of range") res = fmpz_vec(nvars, double_indirect=True) fmpz_mod_mpoly_get_term_exp_fmpz(res.double_indirect, self.val, i, self.ctx.val) - return res.to_tuple() + return tuple(res) def degrees(self): """ @@ -780,7 +780,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): res = fmpz_vec(nvars, double_indirect=True) fmpz_mod_mpoly_degrees_fmpz(res.double_indirect, self.val, self.ctx.val) - return res.to_tuple() + return tuple(res) def total_degree(self): """ @@ -1171,5 +1171,9 @@ cdef class fmpz_mod_mpoly_vec: def __repr__(self): return f"fmpz_mod_mpoly_vec({self}, ctx={self.ctx})" - def to_tuple(self): - return tuple(self[i] for i in range(self.val.length)) + def __iter__(self): + cdef fmpz_mod_mpoly z + for i in range(self.val.length): + z = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly_set(z.val, &self.val[i], self.ctx.val) + yield z diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 5b4b2742..81d7b215 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -510,7 +510,7 @@ cdef class fmpz_mpoly(flint_mpoly): res = [] for i in range(len(self)): fmpz_mpoly_get_term_exp_fmpz(vec.double_indirect, self.val, i, self.ctx.val) - res.append(vec.to_tuple()) + res.append(tuple(vec)) return res @@ -688,7 +688,7 @@ cdef class fmpz_mpoly(flint_mpoly): raise IndexError("term index out of range") res = fmpz_vec(nvars, double_indirect=True) fmpz_mpoly_get_term_exp_fmpz(res.double_indirect, self.val, i, self.ctx.val) - return res.to_tuple() + return tuple(res) def degrees(self): """ @@ -705,7 +705,7 @@ cdef class fmpz_mpoly(flint_mpoly): res = fmpz_vec(nvars, double_indirect=True) fmpz_mpoly_degrees_fmpz(res.double_indirect, self.val, self.ctx.val) - return res.to_tuple() + return tuple(res) def total_degree(self): """ @@ -1208,8 +1208,12 @@ cdef class fmpz_mpoly_vec: else: return NotImplemented - def to_tuple(self): - return tuple(self[i] for i in range(self.val.length)) + def __iter__(self): + cdef fmpz_mpoly z + for i in range(self.val.length): + z = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_set(z.val, fmpz_mpoly_vec_entry(self.val, i), self.ctx.val) + yield z def is_groebner(self, other=None) -> bool: """ diff --git a/src/flint/types/fmpz_vec.pyx b/src/flint/types/fmpz_vec.pyx index 6f8b2044..e5eed3be 100644 --- a/src/flint/types/fmpz_vec.pyx +++ b/src/flint/types/fmpz_vec.pyx @@ -66,6 +66,13 @@ cdef class fmpz_vec: def __repr__(self): return self.repr() + def __iter__(self): + cdef fmpz z + for i in range(self.length): + z = fmpz.__new__(fmpz) + fmpz_init_set(z.val, &self.val[i]) + yield z + def str(self, *args): s = [None] * self.length for i in range(self.length): @@ -76,6 +83,3 @@ cdef class fmpz_vec: def repr(self, *args): return f"fmpz_vec({self.str(*args)}, {self.length})" - - def to_tuple(self): - return tuple(self[i] for i in range(self.length)) diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx index 847732b2..66dcd9c5 100644 --- a/src/flint/types/nmod_mpoly.pyx +++ b/src/flint/types/nmod_mpoly.pyx @@ -575,7 +575,7 @@ cdef class nmod_mpoly(flint_mpoly): res = [] for i in range(len(self)): nmod_mpoly_get_term_exp_fmpz(vec.double_indirect, self.val, i, self.ctx.val) - res.append(vec.to_tuple()) + res.append(tuple(vec)) return res @@ -737,7 +737,7 @@ cdef class nmod_mpoly(flint_mpoly): raise IndexError("term index out of range") res = fmpz_vec(nvars, double_indirect=True) nmod_mpoly_get_term_exp_fmpz(res.double_indirect, self.val, i, self.ctx.val) - return res.to_tuple() + return tuple(res) def degrees(self): """ @@ -754,7 +754,7 @@ cdef class nmod_mpoly(flint_mpoly): res = fmpz_vec(nvars, double_indirect=True) nmod_mpoly_degrees_fmpz(res.double_indirect, self.val, self.ctx.val) - return res.to_tuple() + return tuple(res) def total_degree(self): """ @@ -1142,5 +1142,9 @@ cdef class nmod_mpoly_vec: def __repr__(self): return f"nmod_mpoly_vec({self}, ctx={self.ctx})" - def to_tuple(self): - return tuple(self[i] for i in range(self.val.length)) + def __iter__(self): + cdef nmod_mpoly z + for i in range(self.length): + z = create_nmod_mpoly(self.ctx) + nmod_mpoly_set(z.val, &self.val[i], self.ctx.val) + yield z From 3db235bcd8c0dee96ffe960f845da20718ec6973 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Fri, 6 Sep 2024 02:24:29 +1000 Subject: [PATCH 09/27] Improve primitive function for fmpz_mpoly --- src/flint/types/fmpz_mpoly.pyx | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 81d7b215..8e014e63 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -850,23 +850,19 @@ cdef class fmpz_mpoly(flint_mpoly): raise RuntimeError(f"failed to compute discriminant with respect to {var}") return res - def primitive_part(self): + def primitive(self): """ - Return the primitive part of ``self`` obtained by dividing out the content of - all coefficients and normalizing the leading coefficient to be positive. + Return the content and primitive of ``self``. >>> from flint import Ordering >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> x, y = ctx.gens() - >>> f = x**2 * y + x * y - >>> f.primitive_part() - x0^2*x1 + x0*x1 + >>> f = 4*x + 2*x*y + >>> f.primitive() + (2, x0*x1 + 2*x0) """ - cdef fmpz_mpoly res - - res = create_fmpz_mpoly(self.ctx) - fmpz_mpoly_primitive_part(res.val, self.val, self.ctx.val) - return res + cdef fmpz content = self.content() + return content, self._truediv_scalar_(content, True) def sqrt(self, assume_perfect_square: bool = False): """ From 6d3c5de123def1bc03ad1d3fbfe90ab17ed9c173 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Fri, 6 Sep 2024 02:27:19 +1000 Subject: [PATCH 10/27] Linting --- src/flint/types/fmpz_mpoly.pyx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 8e014e63..c4d6c519 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -45,7 +45,6 @@ from flint.flintlib.fmpz_mpoly cimport ( fmpz_mpoly_mul, fmpz_mpoly_neg, fmpz_mpoly_pow_fmpz, - fmpz_mpoly_primitive_part, fmpz_mpoly_push_term_fmpz_ffmpz, fmpz_mpoly_reduction_primitive_part, fmpz_mpoly_resultant, @@ -1093,6 +1092,14 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_reduction_primitive_part(res.val, self.val, (vec).val, self.ctx.val) return res + # def inflate(self): + # pass + + # def deflate(self): + # pass + + # def deflation(self, stride: fmpz_vec | None, shift: fmpz_vec | None): + def deflation(self): """ Compute the deflation of ``self``. See Flint documentation for From 46d39cf2af6f463463ab025ec51c1c96a7e562cf Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Sat, 7 Sep 2024 00:49:50 +1000 Subject: [PATCH 11/27] Rework fmpz_[m]poly inflation and deflation, wip --- src/flint/types/fmpz_mpoly.pyx | 98 +++++++++++++++++++++++++++------- src/flint/types/fmpz_poly.pyx | 52 ++++++++++++++---- 2 files changed, 123 insertions(+), 27 deletions(-) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index c4d6c519..bb28a088 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -38,6 +38,7 @@ from flint.flintlib.fmpz_mpoly cimport ( fmpz_mpoly_get_str_pretty, fmpz_mpoly_get_term_coeff_fmpz, fmpz_mpoly_get_term_exp_fmpz, + fmpz_mpoly_inflate, fmpz_mpoly_integral, fmpz_mpoly_is_one, fmpz_mpoly_is_zero, @@ -46,6 +47,7 @@ from flint.flintlib.fmpz_mpoly cimport ( fmpz_mpoly_neg, fmpz_mpoly_pow_fmpz, fmpz_mpoly_push_term_fmpz_ffmpz, + fmpz_mpoly_push_term_ui_ffmpz, fmpz_mpoly_reduction_primitive_part, fmpz_mpoly_resultant, fmpz_mpoly_scalar_divexact_fmpz, @@ -1092,34 +1094,94 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_reduction_primitive_part(res.val, self.val, (vec).val, self.ctx.val) return res - # def inflate(self): - # pass + def inflate(self, N: list[int]) -> fmpz_mpoly: + """q s.t. q(X) = p(X^N)""" + cdef nvars = self.ctx.nvars() - # def deflate(self): - # pass + if nvars != len(N): + raise ValueError(f"expected list of length {nvars}, got {len(N)}") + elif any(n < 0 for n in N): + raise ValueError("all inflate strides must be non-negative") - # def deflation(self, stride: fmpz_vec | None, shift: fmpz_vec | None): + cdef: + fmpz_vec shift = fmpz_vec(nvars) + fmpz_vec stride = fmpz_vec(N) + fmpz_mpoly res = create_fmpz_mpoly(self.ctx) - def deflation(self): - """ - Compute the deflation of ``self``. See Flint documentation for - details. Returns deflated polynomial and the stride vector. + fmpz_mpoly_inflate(res.val, self.val, shift.val, stride.val, self.ctx.val) + return res - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) - >>> x, y = ctx.gens() - >>> f = x**3 * y + x * y**4 + x * y - >>> f.deflation() - (x + y + 1, fmpz_vec(['2', '3'], 2)) - """ + def deflate(self, N: list[int]) -> fmpz_mpoly: + """q s.t. p(X) = q(X^N)""" + cdef slong nvars = self.ctx.nvars() + + if nvars != len(N): + raise ValueError(f"expected list of length {nvars}, got {len(N)}") + + cdef: + fmpz_vec shift = fmpz_vec(nvars) + fmpz_vec stride = fmpz_vec(N) + fmpz_mpoly res = create_fmpz_mpoly(self.ctx) + + fmpz_mpoly_deflate(res.val, self.val, shift.val, stride.val, self.ctx.val) + return res + + def deflation(self) -> (fmpz_mpoly, list[int]): + """(q, N) s.t. p(X) = q(X^N) for maximal N""" cdef: - fmpz_vec shift = fmpz_vec(self.ctx.nvars()) + fmpz_vec _shift = fmpz_vec(self.ctx.nvars()) fmpz_vec stride = fmpz_vec(self.ctx.nvars()) fmpz_mpoly res = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_deflation(_shift.val, stride.val, self.val, self.ctx.val) + + cdef fmpz_vec zero_shift = fmpz_vec(self.ctx.nvars()) + fmpz_mpoly_deflate(res.val, self.val, zero_shift.val, stride.val, self.ctx.val) + + return res, list(stride) + + def deflation_monom(self) -> (fmpz_mpoly, list[int], fmpz_mpoly): + """(q, N, m) s.t. p(X) = m * q(X^N) for maximal N and monomial m""" + cdef: + fmpz_mpoly res, monom = create_fmpz_mpoly(self.ctx) + + res, stride, _shift = self.deflation_index() + + fmpz_mpoly_push_term_ui_ffmpz(monom.val, 1, fmpz_vec(_shift).val, self.ctx.val) + return res, list(stride), monom + + def deflation_index(self) -> (fmpz_mpoly, list[int], list[int]): + """(N, I) s.t. p(X) = X^I * q(X^N) for maximal N and I""" + cdef: + slong nvars = self.ctx.nvars() + fmpz_mpoly res = create_fmpz_mpoly(self.ctx) + fmpz_vec shift = fmpz_vec(nvars) + fmpz_vec stride = fmpz_vec(nvars) + fmpz_mpoly_deflation(shift.val, stride.val, self.val, self.ctx.val) fmpz_mpoly_deflate(res.val, self.val, shift.val, stride.val, self.ctx.val) - return res, stride + return res, list(stride), list(shift) + + # def deflation(self): + # """ + # Compute the deflation of ``self``. See Flint documentation for + # details. Returns deflated polynomial and the stride vector. + + # >>> from flint import Ordering + # >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) + # >>> x, y = ctx.gens() + # >>> f = x**3 * y + x * y**4 + x * y + # >>> f.deflation() + # (x + y + 1, fmpz_vec(['2', '3'], 2)) + # """ + # cdef: + # fmpz_vec shift = fmpz_vec(self.ctx.nvars()) + # fmpz_vec stride = fmpz_vec(self.ctx.nvars()) + # fmpz_mpoly res = create_fmpz_mpoly(self.ctx) + + # fmpz_mpoly_deflation(shift.val, stride.val, self.val, self.ctx.val) + # fmpz_mpoly_deflate(res.val, self.val, shift.val, stride.val, self.ctx.val) + # return res, stride cdef class fmpz_mpoly_vec: diff --git a/src/flint/types/fmpz_poly.pyx b/src/flint/types/fmpz_poly.pyx index 6ecee9fd..ec9e44da 100644 --- a/src/flint/types/fmpz_poly.pyx +++ b/src/flint/types/fmpz_poly.pyx @@ -593,18 +593,52 @@ cdef class fmpz_poly(flint_poly): else: raise DomainError(f"Cannot compute square root of {self}") - def deflation(self): - cdef fmpz_poly v + def inflate(self, n: int) -> fmpz_poly: + """q s.t. q(x) = p(x^n)""" + cdef fmpz_poly res = fmpz_poly() + fmpz_poly_inflate(res.val, self.val, n) + return res + + def deflate(self, n: int) -> fmpz_poly: + """q s.t. p(x) = q(x^n)""" + cdef fmpz_poly res = fmpz_poly() + if n > 0: + fmpz_poly_deflate(res.val, self.val, n) + return res + else: + raise ValueError("deflate requires n > 0") + + def deflation(self) -> (fmpz_poly, int): cdef ulong n if fmpz_poly_is_zero(self.val): return self, 1 - n = arb_fmpz_poly_deflation(self.val) - if n == 1: - return self, int(n) - else: - v = fmpz_poly() - arb_fmpz_poly_deflate(v.val, self.val, n) - return v, int(n) + n = fmpz_poly_deflation(self.val) + return self if n <= 1 else self.deflate(n), int(n) + + def deflation_monom(self) -> (fmpz_poly, int, fmpz_poly): + """(q, n, m) s.t. p(x) = m * q(x^n) for maximal n and monomial m""" + q, n, m = self.deflation_index() + + cdef fmpz_poly monom = fmpz_poly.__new__(fmpz_poly) + fmpz_poly_set_coeff_ui(monom.val, m, 1) + + return q, n, monom + + def deflation_index(self) -> (fmpz_poly, int, int): + """(q, n, i) s.t. p(x) = x^i * q(x^n) for maximal n and i""" + cdef fmpz_poly res = fmpz_poly.__new__(fmpz_poly) + cdef slong length = fmpz_poly_length(self.val) + + if length <= 0: + return self, 0, fmpz_poly([1]) + + # Find the smallest non-zero power, that is the gcd of the monomials + for i in range(1, length + 1): + if not fmpz_is_zero(&self.val.coeffs[length - i]): + break + + fmpz_poly_shift_right(res.val, self.val, i) + return *res.deflation(), int(i) def is_cyclotomic(self): cdef long * phi From 206d7b0a14fec66eb3ae944f8ed2d77019a5512f Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Sun, 8 Sep 2024 01:04:27 +1000 Subject: [PATCH 12/27] POC doctest discovery --- src/flint/test/__main__.py | 95 +++++++++++++++++--------------------- 1 file changed, 42 insertions(+), 53 deletions(-) diff --git a/src/flint/test/__main__.py b/src/flint/test/__main__.py index c9a1c19b..63a765f6 100644 --- a/src/flint/test/__main__.py +++ b/src/flint/test/__main__.py @@ -8,11 +8,17 @@ import doctest import traceback import argparse +import pkgutil +import importlib +import re import flint from flint.test.test_all import all_tests +dunder_test_regex = re.compile(r'^(.*?)__test__\..*?\.(.*) \(line (\d+)\)$') + + def run_tests(verbose=None): """Run all tests @@ -48,58 +54,41 @@ def run_tests(verbose=None): return failed, total -def run_doctests(verbose=None): - """Run the python-flint doctests""" - # Here verbose=True shows a lot of output. - modules = [flint.pyflint, - flint.flint_base.flint_base, - flint.flint_base.flint_context, - flint.types.acb, - flint.types.acb_mat, - flint.types.acb_poly, - flint.types.acb_series, - flint.types.arb, - flint.types.arb_mat, - flint.types.arb_poly, - flint.types.arb_series, - flint.types.arf, - flint.types.dirichlet, - flint.types.fmpq, - flint.types.fmpq_mat, - flint.types.fmpq_mpoly, - flint.types.fmpq_poly, - flint.types.fmpq_series, - flint.types.fmpq_vec, - flint.types.fmpz, - flint.types.fmpz_mat, - flint.types.fmpz_mod, - flint.types.fmpz_mod_mat, - flint.types.fmpz_mod_mpoly, - flint.types.fmpz_mod_poly, - flint.types.fmpz_mpoly, - flint.types.fmpz_poly, - flint.types.fmpz_series, - flint.types.fmpz_vec, - flint.types.fq_default, - flint.types.fq_default_poly, - flint.types.nmod, - flint.types.nmod_mat, - flint.types.nmod_mpoly, - flint.types.nmod_poly, - flint.types.nmod_series, - flint.functions.showgood] - try: - from flint.types import acb_theta - modules.append(acb_theta) - except ImportError: - pass - - results = [] - for x in modules: - if verbose: - print(f" {x.__name__}") - results.append(doctest.testmod(x)) - return tuple(sum(res) for res in zip(*results)) +def find_doctests(module): + finder = doctest.DocTestFinder() + tests = [] + for module_info in pkgutil.walk_packages(module.__path__, flint.__name__ + "."): + try: + module = importlib.import_module(module_info.name) + + res = [] + for test in filter(lambda x: bool(x.examples), finder.find(module)): + m = dunder_test_regex.match(test.name) + if m is not None: + groups = m.groups() + test.name = groups[0] + groups[1] + test.lineno = int(groups[2]) + res.append(test) + + tests.append((module_info.name, res)) + + except Exception as e: + print(f"Error importing {module_info.name}: {e}") + return tests + + +def run_doctests(tests, verbose=False): + runner = doctest.DocTestRunner() + for module, test_set in tests: + if test_set: + print(f"{module}...", end="", flush=True) + for test in test_set: + if verbose: + print("\tTesting:", test.name) + runner.run(test) + print("OK") + + return runner.summarize() def run_all_tests(tests=True, doctests=True, verbose=None): @@ -112,7 +101,7 @@ def run_all_tests(tests=True, doctests=True, verbose=None): if doctests: print("Running doctests...") - d_failed, d_total = run_doctests(verbose=verbose) + d_failed, d_total = run_doctests(find_doctests(flint), verbose=verbose) if tests: if t_failed: From 2f9c24ff010457abb788cb52ae1d58795b4a412b Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Sun, 8 Sep 2024 02:19:30 +1000 Subject: [PATCH 13/27] Add term helper method --- src/flint/flint_base/flint_base.pyx | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index 50a6b776..abcb350b 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -391,6 +391,24 @@ cdef class flint_mpoly_context(flint_elem): elif other is not self: raise IncompatibleContextError(f"{other} is not {self}") + def term(self, coeff = None, exp_vec = None): + """ + Create a monomial from a coefficient and exponent vector. ``coeff`` defaults + to ``1``. ``exp_vec``` defaults to ``(0,) * self.nvars()```. + + >>> from flint import fmpz_mpoly_ctx, Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx.term(coeff=5, exp_vec=(2, 3)) + 5*x0^2*x1^3 + >>> ctx.term() + 1 + """ + if coeff is None: + coeff = 1 + if exp_vec is None: + exp_vec = (0,) * self.nvars() + return self.from_dict({tuple(exp_vec): coeff}) + cdef class flint_mpoly(flint_elem): """ From 662e06d042247546cb46ff0ddf2c34d1a688d387 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Sun, 8 Sep 2024 02:31:23 +1000 Subject: [PATCH 14/27] New deflation functions --- src/flint/flintlib/nmod_mpoly.pxd | 1 + src/flint/types/fmpq_mpoly.pyx | 136 ++++++++++++++++++++++++++--- src/flint/types/fmpz_mod_mpoly.pyx | 132 ++++++++++++++++++++++++++-- src/flint/types/fmpz_mpoly.pyx | 117 +++++++++++++++++-------- src/flint/types/fmpz_poly.pyx | 60 ++++++++++--- src/flint/types/nmod_mpoly.pyx | 130 +++++++++++++++++++++++++-- 6 files changed, 503 insertions(+), 73 deletions(-) diff --git a/src/flint/flintlib/nmod_mpoly.pxd b/src/flint/flintlib/nmod_mpoly.pxd index 7cf5296b..0f1e8833 100644 --- a/src/flint/flintlib/nmod_mpoly.pxd +++ b/src/flint/flintlib/nmod_mpoly.pxd @@ -161,3 +161,4 @@ cdef extern from "flint/nmod_mpoly.h": # These functions seem to be undocumented but appear in the header. void nmod_mpoly_deflation(fmpz_struct * shift, fmpz_struct * stride, const nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx) void nmod_mpoly_deflate(nmod_mpoly_t A, const nmod_mpoly_t B, const fmpz_struct * shift, const fmpz_struct * stride, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_inflate(nmod_mpoly_t A, const nmod_mpoly_t B, const fmpz_struct * shift, const fmpz_struct * stride, const nmod_mpoly_ctx_t ctx) diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index a106df5b..9404f124 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -47,6 +47,7 @@ from flint.flintlib.fmpq_mpoly cimport ( fmpq_mpoly_neg, fmpq_mpoly_pow_fmpz, fmpq_mpoly_push_term_fmpq_ffmpz, + fmpq_mpoly_push_term_ui_ffmpz, fmpq_mpoly_reduce, fmpq_mpoly_resultant, fmpq_mpoly_scalar_div_fmpq, @@ -74,7 +75,8 @@ from flint.flintlib.fmpz cimport fmpz_init_set from flint.flintlib.fmpz_mpoly cimport ( fmpz_mpoly_set, fmpz_mpoly_deflate, - fmpz_mpoly_deflation + fmpz_mpoly_deflation, + fmpz_mpoly_inflate, ) from cpython.object cimport Py_EQ, Py_NE @@ -994,28 +996,142 @@ cdef class fmpq_mpoly(flint_mpoly): fmpq_mpoly_integral(res.val, self.val, i, self.ctx.val) return res - def deflation(self): + def inflate(self, N: list[int]) -> fmpq_mpoly: """ - Compute the deflation of ``self``. See Flint documentation for - details. Returns deflated polynomial and the stride vector. + Compute the inflation of ``self`` for a provided ``N``, that is return ``q`` + such that ``q(X) = p(X^N)``. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) + >>> x, y = ctx.gens() + >>> f = x + y + 1 + >>> f.inflate([2, 3]) + x^2 + y^3 + 1 + """ + + cdef nvars = self.ctx.nvars() + + if nvars != len(N): + raise ValueError(f"expected list of length {nvars}, got {len(N)}") + elif any(n < 0 for n in N): + raise ValueError("all inflate strides must be non-negative") + + cdef: + fmpz_vec shift = fmpz_vec(nvars) + fmpz_vec stride = fmpz_vec(N) + fmpq_mpoly res = create_fmpq_mpoly(self.ctx) + + fmpz_mpoly_inflate(res.val.zpoly, self.val.zpoly, shift.val, stride.val, self.ctx.val.zctx) + fmpq_set(res.val.content, self.val.content) + return res + + def deflate(self, N: list[int]) -> fmpq_mpoly: + """ + Compute the deflation of ``self`` for a provided ``N``, that is return ``q`` + such that ``q(X) = p(X^(1/N))``. >>> from flint import Ordering >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) >>> x, y = ctx.gens() >>> f = x**3 * y + x * y**4 + x * y - >>> f.deflation() - (x + y + 1, fmpz_vec(['2', '3'], 2)) + >>> f.deflate([2, 3]) + x + y + 1 """ + cdef slong nvars = self.ctx.nvars() + + if nvars != len(N): + raise ValueError(f"expected list of length {nvars}, got {len(N)}") + cdef: - fmpz_vec shift = fmpz_vec(self.ctx.nvars()) - fmpz_vec stride = fmpz_vec(self.ctx.nvars()) + fmpz_vec shift = fmpz_vec(nvars) + fmpz_vec stride = fmpz_vec(N) fmpq_mpoly res = create_fmpq_mpoly(self.ctx) - fmpz_mpoly_deflation(shift.val, stride.val, self.val.zpoly, self.ctx.val.zctx) fmpz_mpoly_deflate(res.val.zpoly, self.val.zpoly, shift.val, stride.val, self.ctx.val.zctx) + fmpq_set(res.val.content, self.val.content) + return res + + def deflation(self) -> tuple[fmpq_mpoly, list[int]]: + """ + Compute the deflation of ``self``, that is ``p(X^(1/N))`` for maximal + N. Returns ``q, N`` such that ``self == q.inflate(N)``. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) + >>> x, y = ctx.gens() + >>> f = x**3 * y + x * y**4 + x * y + >>> q, N = f.deflation() + >>> q, N + (x + y + 1, [2, 3]) + """ + cdef: + fmpz_vec _shift = fmpz_vec(self.ctx.nvars()) + fmpz_vec stride = fmpz_vec(self.ctx.nvars()) + fmpq_mpoly res = create_fmpq_mpoly(self.ctx) + fmpz_mpoly_deflation(_shift.val, stride.val, self.val.zpoly, self.ctx.val.zctx) + + cdef fmpz_vec zero_shift = fmpz_vec(self.ctx.nvars()) + fmpz_mpoly_deflate(res.val.zpoly, self.val.zpoly, zero_shift.val, stride.val, self.ctx.val.zctx) fmpq_set(res.val.content, self.val.content) - return res, stride + + return res, list(stride) + + def deflation_monom(self) -> tuple[list[int], fmpq_mpoly]: + """ + Compute the exponent vector ``N`` and monomial ``m`` such that ``p(X^(1/N)) + = m * q(X^N)`` for maximal N. Importantly the deflation itself is not computed + here. The returned monomial allows the undo-ing of the deflation. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) + >>> x, y = ctx.gens() + >>> f = x**3 * y + x * y**4 + x * y + >>> N, m = f.deflation_monom() + >>> N, m + ([2, 3], x*y) + >>> f_deflated = f.deflate(N) + >>> f_deflated + x + y + 1 + >>> m * f_deflated.inflate(N) + x^3*y + x*y^4 + x*y + """ + cdef fmpq_mpoly monom = create_fmpq_mpoly(self.ctx) + + stride, _shift = self.deflation_index() + + fmpq_mpoly_push_term_ui_ffmpz(monom.val, 1, fmpz_vec(_shift).val, self.ctx.val) + return list(stride), monom + + def deflation_index(self) -> tuple[list[int], list[int]]: + """ + Compute the exponent vectors ``N`` and ``I`` such that ``p(X^(1/N)) = X^I * + q(X^N)`` for maximal N. Importantly the deflation itself is not computed + here. The returned exponent vector ``I`` is the shift that was applied to the + exponents. It is the exponent vector of the monomial returned by + ``deflation_monom``. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) + >>> x, y = ctx.gens() + >>> f = x**3 * y + x * y**4 + x * y + >>> N, I = f.deflation_index() + >>> N, I + ([2, 3], [1, 1]) + >>> f_deflated = f.deflate(N) + >>> f_deflated + x + y + 1 + >>> m = ctx.term(exp_vec=I) + >>> m * f_deflated.inflate(N) + x^3*y + x*y^4 + x*y + """ + cdef: + slong nvars = self.ctx.nvars() + fmpz_vec shift = fmpz_vec(nvars) + fmpz_vec stride = fmpz_vec(nvars) + + fmpz_mpoly_deflation(shift.val, stride.val, self.val.zpoly, self.ctx.val.zctx) + return list(stride), list(shift) cdef class fmpq_mpoly_vec: diff --git a/src/flint/types/fmpz_mod_mpoly.pyx b/src/flint/types/fmpz_mod_mpoly.pyx index 6063ee89..38e064a5 100644 --- a/src/flint/types/fmpz_mod_mpoly.pyx +++ b/src/flint/types/fmpz_mod_mpoly.pyx @@ -40,6 +40,7 @@ from flint.flintlib.fmpz_mod_mpoly cimport ( fmpz_mod_mpoly_get_str_pretty, fmpz_mod_mpoly_get_term_coeff_fmpz, fmpz_mod_mpoly_get_term_exp_fmpz, + fmpz_mod_mpoly_inflate, fmpz_mod_mpoly_is_one, fmpz_mod_mpoly_is_zero, fmpz_mod_mpoly_length, @@ -47,6 +48,7 @@ from flint.flintlib.fmpz_mod_mpoly cimport ( fmpz_mod_mpoly_neg, fmpz_mod_mpoly_pow_fmpz, fmpz_mod_mpoly_push_term_fmpz_ffmpz, + fmpz_mod_mpoly_push_term_ui_ffmpz, fmpz_mod_mpoly_resultant, fmpz_mod_mpoly_scalar_mul_fmpz, fmpz_mod_mpoly_set, @@ -1074,27 +1076,139 @@ cdef class fmpz_mod_mpoly(flint_mpoly): fmpz_mod_mpoly_derivative(res.val, self.val, i, self.ctx.val) return res - def deflation(self): + def inflate(self, N: list[int]) -> fmpz_mod_mpoly: """ - Compute the deflation of ``self``. See Flint documentation for - details. Returns deflated polynomial and the stride vector. + Compute the inflation of ``self`` for a provided ``N``, that is return ``q`` + such that ``q(X) = p(X^N)``. + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, nametup=('x', 'y')) + >>> x, y = ctx.gens() + >>> f = x + y + 1 + >>> f.inflate([2, 3]) + x^2 + y^3 + 1 + """ + + cdef nvars = self.ctx.nvars() + + if nvars != len(N): + raise ValueError(f"expected list of length {nvars}, got {len(N)}") + elif any(n < 0 for n in N): + raise ValueError("all inflate strides must be non-negative") + + cdef: + fmpz_vec shift = fmpz_vec(nvars) + fmpz_vec stride = fmpz_vec(N) + fmpz_mod_mpoly res = create_fmpz_mod_mpoly(self.ctx) + + fmpz_mod_mpoly_inflate(res.val, self.val, shift.val, stride.val, self.ctx.val) + return res + + def deflate(self, N: list[int]) -> fmpz_mod_mpoly: + """ + Compute the deflation of ``self`` for a provided ``N``, that is return ``q`` + such that ``q(X) = p(X^(1/N))``. >>> from flint import Ordering >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, nametup=('x', 'y')) >>> x, y = ctx.gens() >>> f = x**3 * y + x * y**4 + x * y - >>> f.deflation() - (x + y + 1, fmpz_vec(['2', '3'], 2)) + >>> f.deflate([2, 3]) + x + y + 1 """ + cdef slong nvars = self.ctx.nvars() + + if nvars != len(N): + raise ValueError(f"expected list of length {nvars}, got {len(N)}") + cdef: - fmpz_vec shift = fmpz_vec(self.ctx.nvars()) - fmpz_vec stride = fmpz_vec(self.ctx.nvars()) + fmpz_vec shift = fmpz_vec(nvars) + fmpz_vec stride = fmpz_vec(N) fmpz_mod_mpoly res = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_deflation(shift.val, stride.val, self.val, self.ctx.val) fmpz_mod_mpoly_deflate(res.val, self.val, shift.val, stride.val, self.ctx.val) + return res + + def deflation(self) -> tuple[fmpz_mod_mpoly, list[int]]: + """ + Compute the deflation of ``self``, that is ``p(X^(1/N))`` for maximal + N. Returns ``q, N`` such that ``self == q.inflate(N)``. + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, nametup=('x', 'y')) + >>> x, y = ctx.gens() + >>> f = x**3 * y + x * y**4 + x * y + >>> q, N = f.deflation() + >>> q, N + (x + y + 1, [2, 3]) + """ + cdef: + fmpz_vec _shift = fmpz_vec(self.ctx.nvars()) + fmpz_vec stride = fmpz_vec(self.ctx.nvars()) + fmpz_mod_mpoly res = create_fmpz_mod_mpoly(self.ctx) + + fmpz_mod_mpoly_deflation(_shift.val, stride.val, self.val, self.ctx.val) + + cdef fmpz_vec zero_shift = fmpz_vec(self.ctx.nvars()) + fmpz_mod_mpoly_deflate(res.val, self.val, zero_shift.val, stride.val, self.ctx.val) + + return res, list(stride) + + def deflation_monom(self) -> tuple[list[int], fmpz_mod_mpoly]: + """ + Compute the exponent vector ``N`` and monomial ``m`` such that ``p(X^(1/N)) + = m * q(X^N)`` for maximal N. Importantly the deflation itself is not computed + here. The returned monomial allows the undo-ing of the deflation. + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, nametup=('x', 'y')) + >>> x, y = ctx.gens() + >>> f = x**3 * y + x * y**4 + x * y + >>> N, m = f.deflation_monom() + >>> N, m + ([2, 3], x*y) + >>> f_deflated = f.deflate(N) + >>> f_deflated + x + y + 1 + >>> m * f_deflated.inflate(N) + x^3*y + x*y^4 + x*y + """ + cdef fmpz_mod_mpoly monom = create_fmpz_mod_mpoly(self.ctx) - return res, stride + stride, _shift = self.deflation_index() + + fmpz_mod_mpoly_push_term_ui_ffmpz(monom.val, 1, fmpz_vec(_shift).val, self.ctx.val) + return list(stride), monom + + def deflation_index(self) -> tuple[list[int], list[int]]: + """ + Compute the exponent vectors ``N`` and ``I`` such that ``p(X^(1/N)) = X^I * + q(X^N)`` for maximal N. Importantly the deflation itself is not computed + here. The returned exponent vector ``I`` is the shift that was applied to the + exponents. It is the exponent vector of the monomial returned by + ``deflation_monom``. + + >>> from flint import Ordering + >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, nametup=('x', 'y')) + >>> x, y = ctx.gens() + >>> f = x**3 * y + x * y**4 + x * y + >>> N, I = f.deflation_index() + >>> N, I + ([2, 3], [1, 1]) + >>> f_deflated = f.deflate(N) + >>> f_deflated + x + y + 1 + >>> m = ctx.term(exp_vec=I) + >>> m * f_deflated.inflate(N) + x^3*y + x*y^4 + x*y + """ + cdef: + slong nvars = self.ctx.nvars() + fmpz_vec shift = fmpz_vec(nvars) + fmpz_vec stride = fmpz_vec(nvars) + + fmpz_mod_mpoly_deflation(shift.val, stride.val, self.val, self.ctx.val) + return list(stride), list(shift) cdef class fmpz_mod_mpoly_vec: diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index bb28a088..06dc0e2d 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -1095,7 +1095,18 @@ cdef class fmpz_mpoly(flint_mpoly): return res def inflate(self, N: list[int]) -> fmpz_mpoly: - """q s.t. q(X) = p(X^N)""" + """ + Compute the inflation of ``self`` for a provided ``N``, that is return ``q`` + such that ``q(X) = p(X^N)``. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) + >>> x, y = ctx.gens() + >>> f = x + y + 1 + >>> f.inflate([2, 3]) + x^2 + y^3 + 1 + """ + cdef nvars = self.ctx.nvars() if nvars != len(N): @@ -1112,7 +1123,17 @@ cdef class fmpz_mpoly(flint_mpoly): return res def deflate(self, N: list[int]) -> fmpz_mpoly: - """q s.t. p(X) = q(X^N)""" + """ + Compute the deflation of ``self`` for a provided ``N``, that is return ``q`` + such that ``q(X) = p(X^(1/N))``. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) + >>> x, y = ctx.gens() + >>> f = x**3 * y + x * y**4 + x * y + >>> f.deflate([2, 3]) + x + y + 1 + """ cdef slong nvars = self.ctx.nvars() if nvars != len(N): @@ -1126,8 +1147,19 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_deflate(res.val, self.val, shift.val, stride.val, self.ctx.val) return res - def deflation(self) -> (fmpz_mpoly, list[int]): - """(q, N) s.t. p(X) = q(X^N) for maximal N""" + def deflation(self) -> tuple[fmpz_mpoly, list[int]]: + """ + Compute the deflation of ``self``, that is ``p(X^(1/N))`` for maximal + N. Returns ``q, N`` such that ``self == q.inflate(N)``. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) + >>> x, y = ctx.gens() + >>> f = x**3 * y + x * y**4 + x * y + >>> q, N = f.deflation() + >>> q, N + (x + y + 1, [2, 3]) + """ cdef: fmpz_vec _shift = fmpz_vec(self.ctx.nvars()) fmpz_vec stride = fmpz_vec(self.ctx.nvars()) @@ -1140,48 +1172,61 @@ cdef class fmpz_mpoly(flint_mpoly): return res, list(stride) - def deflation_monom(self) -> (fmpz_mpoly, list[int], fmpz_mpoly): - """(q, N, m) s.t. p(X) = m * q(X^N) for maximal N and monomial m""" - cdef: - fmpz_mpoly res, monom = create_fmpz_mpoly(self.ctx) + def deflation_monom(self) -> tuple[list[int], fmpz_mpoly]: + """ + Compute the exponent vector ``N`` and monomial ``m`` such that ``p(X^(1/N)) + = m * q(X^N)`` for maximal N. Importantly the deflation itself is not computed + here. The returned monomial allows the undo-ing of the deflation. - res, stride, _shift = self.deflation_index() + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) + >>> x, y = ctx.gens() + >>> f = x**3 * y + x * y**4 + x * y + >>> N, m = f.deflation_monom() + >>> N, m + ([2, 3], x*y) + >>> f_deflated = f.deflate(N) + >>> f_deflated + x + y + 1 + >>> m * f_deflated.inflate(N) + x^3*y + x*y^4 + x*y + """ + cdef fmpz_mpoly monom = create_fmpz_mpoly(self.ctx) + + stride, _shift = self.deflation_index() fmpz_mpoly_push_term_ui_ffmpz(monom.val, 1, fmpz_vec(_shift).val, self.ctx.val) - return res, list(stride), monom + return list(stride), monom - def deflation_index(self) -> (fmpz_mpoly, list[int], list[int]): - """(N, I) s.t. p(X) = X^I * q(X^N) for maximal N and I""" + def deflation_index(self) -> tuple[list[int], list[int]]: + """ + Compute the exponent vectors ``N`` and ``I`` such that ``p(X^(1/N)) = X^I * + q(X^N)`` for maximal N. Importantly the deflation itself is not computed + here. The returned exponent vector ``I`` is the shift that was applied to the + exponents. It is the exponent vector of the monomial returned by + ``deflation_monom``. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) + >>> x, y = ctx.gens() + >>> f = x**3 * y + x * y**4 + x * y + >>> N, I = f.deflation_index() + >>> N, I + ([2, 3], [1, 1]) + >>> f_deflated = f.deflate(N) + >>> f_deflated + x + y + 1 + >>> m = ctx.term(exp_vec=I) + >>> m * f_deflated.inflate(N) + x^3*y + x*y^4 + x*y + """ cdef: slong nvars = self.ctx.nvars() - fmpz_mpoly res = create_fmpz_mpoly(self.ctx) fmpz_vec shift = fmpz_vec(nvars) fmpz_vec stride = fmpz_vec(nvars) fmpz_mpoly_deflation(shift.val, stride.val, self.val, self.ctx.val) - fmpz_mpoly_deflate(res.val, self.val, shift.val, stride.val, self.ctx.val) - return res, list(stride), list(shift) - - # def deflation(self): - # """ - # Compute the deflation of ``self``. See Flint documentation for - # details. Returns deflated polynomial and the stride vector. - - # >>> from flint import Ordering - # >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) - # >>> x, y = ctx.gens() - # >>> f = x**3 * y + x * y**4 + x * y - # >>> f.deflation() - # (x + y + 1, fmpz_vec(['2', '3'], 2)) - # """ - # cdef: - # fmpz_vec shift = fmpz_vec(self.ctx.nvars()) - # fmpz_vec stride = fmpz_vec(self.ctx.nvars()) - # fmpz_mpoly res = create_fmpz_mpoly(self.ctx) - - # fmpz_mpoly_deflation(shift.val, stride.val, self.val, self.ctx.val) - # fmpz_mpoly_deflate(res.val, self.val, shift.val, stride.val, self.ctx.val) - # return res, stride + return list(stride), list(shift) cdef class fmpz_mpoly_vec: diff --git a/src/flint/types/fmpz_poly.pyx b/src/flint/types/fmpz_poly.pyx index ec9e44da..52d4506d 100644 --- a/src/flint/types/fmpz_poly.pyx +++ b/src/flint/types/fmpz_poly.pyx @@ -594,13 +594,27 @@ cdef class fmpz_poly(flint_poly): raise DomainError(f"Cannot compute square root of {self}") def inflate(self, n: int) -> fmpz_poly: - """q s.t. q(x) = p(x^n)""" + """ + Compute the inflation of ``self`` for a provided ``n``, that is return ``q`` + such that ``q(x) = p(x^n)``. + + >>> f = fmpz_poly([1, 1]) + >>> f.inflate(2) + x^2 + 1 + """ cdef fmpz_poly res = fmpz_poly() fmpz_poly_inflate(res.val, self.val, n) return res def deflate(self, n: int) -> fmpz_poly: - """q s.t. p(x) = q(x^n)""" + """ + Compute the deflation of ``self`` for a provided ``n``, that is return ``q`` + such that ``q(x) = p(x^(1/n))``. + + >>> f = fmpz_poly([1, 0, 1]) + >>> f.deflate(2) + x + 1 + """ cdef fmpz_poly res = fmpz_poly() if n > 0: fmpz_poly_deflate(res.val, self.val, n) @@ -608,24 +622,50 @@ cdef class fmpz_poly(flint_poly): else: raise ValueError("deflate requires n > 0") - def deflation(self) -> (fmpz_poly, int): + def deflation(self) -> tuple[fmpz_poly, int]: + """ + Compute the deflation of ``self``, that is ``p(x^(1/n))`` for maximal + n. returns ``q, n`` such that ``self == q.inflate(n)``. + + >>> f = fmpz_poly([1, 0, 1]) + >>> f.deflation() + (x + 1, 2) + """ cdef ulong n if fmpz_poly_is_zero(self.val): return self, 1 n = fmpz_poly_deflation(self.val) return self if n <= 1 else self.deflate(n), int(n) - def deflation_monom(self) -> (fmpz_poly, int, fmpz_poly): - """(q, n, m) s.t. p(x) = m * q(x^n) for maximal n and monomial m""" - q, n, m = self.deflation_index() + def deflation_monom(self) -> tuple[fmpz_poly, int, fmpz_poly]: + """ + Compute the exponent vector ``n`` and monomial ``m`` such that ``p(x^(1/n)) + = m * q(x^n)`` for maximal n. Importantly the deflation itself is not computed + here. The returned monomial allows the undo-ing of the deflation. + + >>> f = fmpz_poly([1, 0, 1]) + >>> f.deflation() + (1, x) + """ + n, m = self.deflation_index() cdef fmpz_poly monom = fmpz_poly.__new__(fmpz_poly) fmpz_poly_set_coeff_ui(monom.val, m, 1) - return q, n, monom + return n, monom - def deflation_index(self) -> (fmpz_poly, int, int): - """(q, n, i) s.t. p(x) = x^i * q(x^n) for maximal n and i""" + def deflation_index(self) -> tuple[int, int]: + """ + Compute the exponent vectors ``N`` and ``I`` such that ``p(X^(1/N)) = X^I * + q(X^N)`` for maximal N. Importantly the deflation itself is not computed + here. The returned exponent vector ``I`` is the shift that was applied to the + exponents. It is the exponent vector of the monomial returned by + ``deflation_monom``. + + >>> f = fmpz_poly([1, 0, 1]) + >>> f.deflation() + (1, 1) + """ cdef fmpz_poly res = fmpz_poly.__new__(fmpz_poly) cdef slong length = fmpz_poly_length(self.val) @@ -638,7 +678,7 @@ cdef class fmpz_poly(flint_poly): break fmpz_poly_shift_right(res.val, self.val, i) - return *res.deflation(), int(i) + return int(fmpz_poly_deflation(res.val)), int(i) def is_cyclotomic(self): cdef long * phi diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx index 66dcd9c5..d7e8215f 100644 --- a/src/flint/types/nmod_mpoly.pyx +++ b/src/flint/types/nmod_mpoly.pyx @@ -43,6 +43,7 @@ from flint.flintlib.nmod_mpoly cimport ( nmod_mpoly_get_str_pretty, nmod_mpoly_get_term_coeff_ui, nmod_mpoly_get_term_exp_fmpz, + nmod_mpoly_inflate, nmod_mpoly_is_one, nmod_mpoly_is_zero, nmod_mpoly_length, @@ -1046,26 +1047,139 @@ cdef class nmod_mpoly(flint_mpoly): nmod_mpoly_derivative(res.val, self.val, i, self.ctx.val) return res - def deflation(self): + def inflate(self, N: list[int]) -> nmod_mpoly: """ - Compute the deflation of ``self``. See Flint documentation for - details. Returns deflated polynomial and the stride vector. + Compute the inflation of ``self`` for a provided ``N``, that is return ``q`` + such that ``q(X) = p(X^N)``. + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, nametup=('x', 'y')) + >>> x, y = ctx.gens() + >>> f = x + y + 1 + >>> f.inflate([2, 3]) + x^2 + y^3 + 1 + """ + + cdef nvars = self.ctx.nvars() + + if nvars != len(N): + raise ValueError(f"expected list of length {nvars}, got {len(N)}") + elif any(n < 0 for n in N): + raise ValueError("all inflate strides must be non-negative") + + cdef: + fmpz_vec shift = fmpz_vec(nvars) + fmpz_vec stride = fmpz_vec(N) + nmod_mpoly res = create_nmod_mpoly(self.ctx) + + nmod_mpoly_inflate(res.val, self.val, shift.val, stride.val, self.ctx.val) + return res + + def deflate(self, N: list[int]) -> nmod_mpoly: + """ + Compute the deflation of ``self`` for a provided ``N``, that is return ``q`` + such that ``q(X) = p(X^(1/N))``. >>> from flint import Ordering >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, nametup=('x', 'y')) >>> x, y = ctx.gens() >>> f = x**3 * y + x * y**4 + x * y - >>> f.deflation() - (x + y + 1, fmpz_vec(['2', '3'], 2)) + >>> f.deflate([2, 3]) + x + y + 1 """ + cdef slong nvars = self.ctx.nvars() + + if nvars != len(N): + raise ValueError(f"expected list of length {nvars}, got {len(N)}") + cdef: - fmpz_vec shift = fmpz_vec(self.ctx.nvars()) + fmpz_vec shift = fmpz_vec(nvars) + fmpz_vec stride = fmpz_vec(N) + nmod_mpoly res = create_nmod_mpoly(self.ctx) + + nmod_mpoly_deflate(res.val, self.val, shift.val, stride.val, self.ctx.val) + return res + + def deflation(self) -> tuple[nmod_mpoly, list[int]]: + """ + Compute the deflation of ``self``, that is ``p(X^(1/N))`` for maximal + N. Returns ``q, N`` such that ``self == q.inflate(N)``. + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, nametup=('x', 'y')) + >>> x, y = ctx.gens() + >>> f = x**3 * y + x * y**4 + x * y + >>> q, N = f.deflation() + >>> q, N + (x + y + 1, [2, 3]) + """ + cdef: + fmpz_vec _shift = fmpz_vec(self.ctx.nvars()) fmpz_vec stride = fmpz_vec(self.ctx.nvars()) nmod_mpoly res = create_nmod_mpoly(self.ctx) + nmod_mpoly_deflation(_shift.val, stride.val, self.val, self.ctx.val) + + cdef fmpz_vec zero_shift = fmpz_vec(self.ctx.nvars()) + nmod_mpoly_deflate(res.val, self.val, zero_shift.val, stride.val, self.ctx.val) + + return res, list(stride) + + def deflation_monom(self) -> tuple[list[int], nmod_mpoly]: + """ + Compute the exponent vector ``N`` and monomial ``m`` such that ``p(X^(1/N)) + = m * q(X^N)`` for maximal N. Importantly the deflation itself is not computed + here. The returned monomial allows the undo-ing of the deflation. + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, nametup=('x', 'y')) + >>> x, y = ctx.gens() + >>> f = x**3 * y + x * y**4 + x * y + >>> N, m = f.deflation_monom() + >>> N, m + ([2, 3], x*y) + >>> f_deflated = f.deflate(N) + >>> f_deflated + x + y + 1 + >>> m * f_deflated.inflate(N) + x^3*y + x*y^4 + x*y + """ + cdef nmod_mpoly monom = create_nmod_mpoly(self.ctx) + + stride, _shift = self.deflation_index() + + nmod_mpoly_push_term_ui_ffmpz(monom.val, 1, fmpz_vec(_shift).val, self.ctx.val) + return list(stride), monom + + def deflation_index(self) -> tuple[list[int], list[int]]: + """ + Compute the exponent vectors ``N`` and ``I`` such that ``p(X^(1/N)) = X^I * + q(X^N)`` for maximal N. Importantly the deflation itself is not computed + here. The returned exponent vector ``I`` is the shift that was applied to the + exponents. It is the exponent vector of the monomial returned by + ``deflation_monom``. + + >>> from flint import Ordering + >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, nametup=('x', 'y')) + >>> x, y = ctx.gens() + >>> f = x**3 * y + x * y**4 + x * y + >>> N, I = f.deflation_index() + >>> N, I + ([2, 3], [1, 1]) + >>> f_deflated = f.deflate(N) + >>> f_deflated + x + y + 1 + >>> m = ctx.term(exp_vec=I) + >>> m * f_deflated.inflate(N) + x^3*y + x*y^4 + x*y + """ + cdef: + slong nvars = self.ctx.nvars() + fmpz_vec shift = fmpz_vec(nvars) + fmpz_vec stride = fmpz_vec(nvars) + nmod_mpoly_deflation(shift.val, stride.val, self.val, self.ctx.val) - nmod_mpoly_deflate(res.val, self.val, shift.val, stride.val, self.ctx.val) - return res, stride + return list(stride), list(shift) cdef class nmod_mpoly_vec: From 9c06381b5fb7ba86c3b61d46f9e676159c99e513 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Sun, 8 Sep 2024 14:04:05 +1000 Subject: [PATCH 15/27] Integrate doctests with pytest --- src/flint/test/__main__.py | 30 +---------------- src/flint/test/meson.build | 1 + src/flint/test/test_docstrings.py | 55 +++++++++++++++++++++++++++++++ src/flint/types/fmpz_poly.pyx | 4 +-- 4 files changed, 59 insertions(+), 31 deletions(-) create mode 100644 src/flint/test/test_docstrings.py diff --git a/src/flint/test/__main__.py b/src/flint/test/__main__.py index 63a765f6..9c84fc2e 100644 --- a/src/flint/test/__main__.py +++ b/src/flint/test/__main__.py @@ -8,15 +8,10 @@ import doctest import traceback import argparse -import pkgutil -import importlib -import re import flint from flint.test.test_all import all_tests - - -dunder_test_regex = re.compile(r'^(.*?)__test__\..*?\.(.*) \(line (\d+)\)$') +from flint.test.test_docstrings import find_doctests def run_tests(verbose=None): @@ -54,29 +49,6 @@ def run_tests(verbose=None): return failed, total -def find_doctests(module): - finder = doctest.DocTestFinder() - tests = [] - for module_info in pkgutil.walk_packages(module.__path__, flint.__name__ + "."): - try: - module = importlib.import_module(module_info.name) - - res = [] - for test in filter(lambda x: bool(x.examples), finder.find(module)): - m = dunder_test_regex.match(test.name) - if m is not None: - groups = m.groups() - test.name = groups[0] + groups[1] - test.lineno = int(groups[2]) - res.append(test) - - tests.append((module_info.name, res)) - - except Exception as e: - print(f"Error importing {module_info.name}: {e}") - return tests - - def run_doctests(tests, verbose=False): runner = doctest.DocTestRunner() for module, test_set in tests: diff --git a/src/flint/test/meson.build b/src/flint/test/meson.build index 4aa245a1..4f93f395 100644 --- a/src/flint/test/meson.build +++ b/src/flint/test/meson.build @@ -4,6 +4,7 @@ pyfiles = [ '__init__.py', '__main__.py', 'test_all.py', + 'test_docstrings.py', ] py.install_sources( diff --git a/src/flint/test/test_docstrings.py b/src/flint/test/test_docstrings.py new file mode 100644 index 00000000..70c956ab --- /dev/null +++ b/src/flint/test/test_docstrings.py @@ -0,0 +1,55 @@ +import doctest +import importlib +import pkgutil +import pytest +import re + +import flint + +dunder_test_regex = re.compile(r'^(.*?)__test__\..*?\.(.*) \(line (\d+)\)$') + + +def find_doctests(module): + finder = doctest.DocTestFinder() + tests = [] + for module_info in pkgutil.walk_packages(module.__path__, flint.__name__ + "."): + try: + module = importlib.import_module(module_info.name) + + res = [] + for test in filter(lambda x: bool(x.examples), finder.find(module)): + m = dunder_test_regex.match(test.name) + if m is not None: + groups = m.groups() + test.name = groups[0] + groups[1] + test.lineno = int(groups[2]) + res.append(test) + + tests.append((module_info.name, res)) + + except Exception as e: + print(f"Error importing {module_info.name}: {e}") + return tests + + +class PyTestDocTestRunner(doctest.DocTestRunner): + def report_failure(self, out, test, example, got): + pytest.fail( + "\n".join([ + f"Failed: {test.name}, line: {test.lineno}", + "Failed example:", + f"\t{example.source.strip()}", + "Expected:", + f"\t{example.want.strip()}", + "Got:", + f"\t{got.strip()}" + ]), + pytrace=False, + ) + + +runner = PyTestDocTestRunner() + +@pytest.mark.parametrize("module,test", [(module, test) for module, test_set in find_doctests(flint) for test in test_set]) +def test_docstrings(module, test): + runner.run(test) diff --git a/src/flint/types/fmpz_poly.pyx b/src/flint/types/fmpz_poly.pyx index 52d4506d..1ebab864 100644 --- a/src/flint/types/fmpz_poly.pyx +++ b/src/flint/types/fmpz_poly.pyx @@ -644,7 +644,7 @@ cdef class fmpz_poly(flint_poly): here. The returned monomial allows the undo-ing of the deflation. >>> f = fmpz_poly([1, 0, 1]) - >>> f.deflation() + >>> f.deflation_monom() (1, x) """ n, m = self.deflation_index() @@ -663,7 +663,7 @@ cdef class fmpz_poly(flint_poly): ``deflation_monom``. >>> f = fmpz_poly([1, 0, 1]) - >>> f.deflation() + >>> f.deflation_index() (1, 1) """ cdef fmpz_poly res = fmpz_poly.__new__(fmpz_poly) From 9dc4f4cce9f6d668fd7686e1ca32da42221f64fa Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Sun, 8 Sep 2024 14:05:48 +1000 Subject: [PATCH 16/27] Don't say "Failed:" twice --- src/flint/test/test_docstrings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/flint/test/test_docstrings.py b/src/flint/test/test_docstrings.py index 70c956ab..5fa0b37e 100644 --- a/src/flint/test/test_docstrings.py +++ b/src/flint/test/test_docstrings.py @@ -36,7 +36,7 @@ class PyTestDocTestRunner(doctest.DocTestRunner): def report_failure(self, out, test, example, got): pytest.fail( "\n".join([ - f"Failed: {test.name}, line: {test.lineno}", + f"{test.name}, line: {test.lineno}", "Failed example:", f"\t{example.source.strip()}", "Expected:", From 170f212255db3f106fbb716d2598bcebdaa3e035 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Sun, 8 Sep 2024 19:06:20 +1000 Subject: [PATCH 17/27] Add invariant tests and correct doc strings --- src/flint/test/test_all.py | 14 ++++++++++++-- src/flint/types/fmpq_mpoly.pyx | 3 +-- src/flint/types/fmpz_mpoly.pyx | 3 +-- src/flint/types/nmod_mpoly.pyx | 3 +-- 4 files changed, 15 insertions(+), 8 deletions(-) diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index 8ca29d52..09904b0f 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -3239,14 +3239,24 @@ def quick_poly(): assert (x0**3 - 6*x0**2 + 11*x0 - 6).discriminant('x0') == 4 assert (x0**2 + 4*x0*x1 + 4*x1**2 - 1).discriminant('x0') == 4 - res, stride = (3*x0**2*x1**2 + 6*x0*x1**2 + 9*x1**2).deflation() + f1 = 3*x0**2*x1**2 + 6*x0*x1**2 + 9*x1**2 + res, stride = f1.deflation() assert res == 3*x0**2 + 6*x0 + 9 assert tuple(stride) == (1, 0) - res, stride = ((x0**2 + x1**2)**3 + (x0**2 + x1**2)**2 + 1).deflation() + g1 = ((x0**2 + x1**2)**3 + (x0**2 + x1**2)**2 + 1) + res, stride = g1.deflation() assert res == x0**3 + 3*x0**2*x1 + x0**2 + 3*x0*x1**2 + 2*x0*x1 + x1**3 + x1**2 + 1 assert tuple(stride) == (2, 2) + for p in [f1, g1]: + n, m = p.deflation_monom() + assert m * p.deflate(n).inflate(n) == p + + n, i = p.deflation_index() + m = ctx.term(exp_vec=i) + assert p.deflate(n).inflate(n) * m == p + if P is flint.fmpz_mpoly: assert (x0**2 * x1 + x0 * x1).primitive() == (1, x0**2*x1 + x0*x1) assert (4 * x0 + 2 * x0 * x1).primitive() == (2, x0 * x1 + 2 * x0) diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 9404f124..95ee500e 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -1053,8 +1053,7 @@ cdef class fmpq_mpoly(flint_mpoly): def deflation(self) -> tuple[fmpq_mpoly, list[int]]: """ - Compute the deflation of ``self``, that is ``p(X^(1/N))`` for maximal - N. Returns ``q, N`` such that ``self == q.inflate(N)``. + Compute the deflation of ``self``, that is ``p(X^(1/N))`` for maximal N. >>> from flint import Ordering >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 06dc0e2d..71c48e6e 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -1149,8 +1149,7 @@ cdef class fmpz_mpoly(flint_mpoly): def deflation(self) -> tuple[fmpz_mpoly, list[int]]: """ - Compute the deflation of ``self``, that is ``p(X^(1/N))`` for maximal - N. Returns ``q, N`` such that ``self == q.inflate(N)``. + Compute the deflation of ``self``, that is ``p(X^(1/N))`` for maximal N. >>> from flint import Ordering >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx index d7e8215f..3a48a507 100644 --- a/src/flint/types/nmod_mpoly.pyx +++ b/src/flint/types/nmod_mpoly.pyx @@ -1102,8 +1102,7 @@ cdef class nmod_mpoly(flint_mpoly): def deflation(self) -> tuple[nmod_mpoly, list[int]]: """ - Compute the deflation of ``self``, that is ``p(X^(1/N))`` for maximal - N. Returns ``q, N`` such that ``self == q.inflate(N)``. + Compute the deflation of ``self``, that is ``p(X^(1/N))`` for maximal N. >>> from flint import Ordering >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, nametup=('x', 'y')) From e001635ce1e6d8b9a2d66b7b2e23bd73ae3c6745 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Sun, 8 Sep 2024 19:28:14 +1000 Subject: [PATCH 18/27] Conditionally define the pytest doctest target --- src/flint/test/test_docstrings.py | 56 ++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/src/flint/test/test_docstrings.py b/src/flint/test/test_docstrings.py index 5fa0b37e..f4ce9e95 100644 --- a/src/flint/test/test_docstrings.py +++ b/src/flint/test/test_docstrings.py @@ -1,7 +1,6 @@ import doctest import importlib import pkgutil -import pytest import re import flint @@ -32,24 +31,43 @@ def find_doctests(module): return tests -class PyTestDocTestRunner(doctest.DocTestRunner): - def report_failure(self, out, test, example, got): - pytest.fail( - "\n".join([ - f"{test.name}, line: {test.lineno}", - "Failed example:", - f"\t{example.source.strip()}", - "Expected:", - f"\t{example.want.strip()}", - "Got:", - f"\t{got.strip()}" - ]), - pytrace=False, - ) +# The below definitions are only useful when pytest is a) installed, and b) being currently run. +# We don't want to impose pytest on those that just want to use `python -m flint.test` +try: + import pytest + class PyTestDocTestRunner(doctest.DocTestRunner): + def report_failure(self, out, test, example, got): + pytest.fail( + "\n".join([ + f"{test.name}, line: {test.lineno}", + "Failed example:", + f"\t{example.source.strip()}", + "Expected:", + f"\t{example.want.strip()}", + "Got:", + f"\t{got.strip()}" + ]), + pytrace=False, + ) -runner = PyTestDocTestRunner() + runner = PyTestDocTestRunner() -@pytest.mark.parametrize("module,test", [(module, test) for module, test_set in find_doctests(flint) for test in test_set]) -def test_docstrings(module, test): - runner.run(test) + @pytest.mark.parametrize( + "test", + [ + test for _, test_set in find_doctests(flint) + for test in test_set + ] + ) + def test_docstrings(test): + runner.run(test) + +except ImportError: + class PyTestDocTestRunner(doctest.DocTestRunner): + pass + + runner = None + + def test_docstrings(module, test): + pass From f99158d74de85c00e1a12fdae820030e3e4612ca Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Sun, 8 Sep 2024 19:30:29 +1000 Subject: [PATCH 19/27] fixup! Conditionally define the pytest doctest target --- src/flint/test/test_docstrings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/flint/test/test_docstrings.py b/src/flint/test/test_docstrings.py index f4ce9e95..1240b958 100644 --- a/src/flint/test/test_docstrings.py +++ b/src/flint/test/test_docstrings.py @@ -69,5 +69,5 @@ class PyTestDocTestRunner(doctest.DocTestRunner): runner = None - def test_docstrings(module, test): + def test_docstrings(test): pass From 2e0ebc7276c6623d670d68e60b64e4b4f61f9d02 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Sun, 8 Sep 2024 20:53:19 +1000 Subject: [PATCH 20/27] Fix doctest exceptions not being reported as pytest failures --- src/flint/test/__main__.py | 2 +- src/flint/test/test_docstrings.py | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/flint/test/__main__.py b/src/flint/test/__main__.py index 9c84fc2e..0dea1419 100644 --- a/src/flint/test/__main__.py +++ b/src/flint/test/__main__.py @@ -53,7 +53,7 @@ def run_doctests(tests, verbose=False): runner = doctest.DocTestRunner() for module, test_set in tests: if test_set: - print(f"{module}...", end="", flush=True) + print(f"{module}...", end="" if not verbose else "\n", flush=True) for test in test_set: if verbose: print("\tTesting:", test.name) diff --git a/src/flint/test/test_docstrings.py b/src/flint/test/test_docstrings.py index 1240b958..5ed06189 100644 --- a/src/flint/test/test_docstrings.py +++ b/src/flint/test/test_docstrings.py @@ -51,6 +51,18 @@ def report_failure(self, out, test, example, got): pytrace=False, ) + def report_unexpected_exception(self, out, test, example, exc_info): + pytest.fail( + "\n".join([ + f"{test.name}, line: {test.lineno}", + "Failed example:", + f"\t{example.source.strip()}", + "Exception raised:", + doctest._indent(doctest._exception_traceback(exc_info)) + ]), + pytrace=False, + ) + runner = PyTestDocTestRunner() @pytest.mark.parametrize( From 1b31a67f9772fa864d3e22ccf67ff10d325ca078 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Sun, 8 Sep 2024 23:02:19 +1000 Subject: [PATCH 21/27] Fix mpoly.deflation, fix invariant tests --- src/flint/test/test_all.py | 19 +++++++----- src/flint/types/fmpq_mpoly.pyx | 48 ++++++++++++++++++------------ src/flint/types/fmpz_mod_mpoly.pyx | 47 +++++++++++++++++------------ src/flint/types/fmpz_mpoly.pyx | 47 +++++++++++++++++------------ src/flint/types/fmpz_poly.pyx | 12 ++++++-- src/flint/types/nmod_mpoly.pyx | 47 +++++++++++++++++------------ 6 files changed, 134 insertions(+), 86 deletions(-) diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index 09904b0f..a9e3ee75 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -3241,8 +3241,8 @@ def quick_poly(): f1 = 3*x0**2*x1**2 + 6*x0*x1**2 + 9*x1**2 res, stride = f1.deflation() - assert res == 3*x0**2 + 6*x0 + 9 - assert tuple(stride) == (1, 0) + assert res == 3*x0**2*x1 + 6*x0*x1 + 9*x1 + assert tuple(stride) == (1, 2) g1 = ((x0**2 + x1**2)**3 + (x0**2 + x1**2)**2 + 1) res, stride = g1.deflation() @@ -3250,12 +3250,17 @@ def quick_poly(): assert tuple(stride) == (2, 2) for p in [f1, g1]: - n, m = p.deflation_monom() - assert m * p.deflate(n).inflate(n) == p + pd, n = p.deflation() + assert pd.inflate(n) == p + assert p.deflate(n).inflate(n) == p - n, i = p.deflation_index() - m = ctx.term(exp_vec=i) - assert p.deflate(n).inflate(n) * m == p + pd, n, m = p.deflation_monom() + assert m * pd.inflate(n) == p + + if not composite_characteristic: + n, i = p.deflation_index() + m = ctx.term(exp_vec=i) + assert (p / m).deflate(n).inflate(n) * m == p if P is flint.fmpz_mpoly: assert (x0**2 * x1 + x0 * x1).primitive() == (1, x0**2*x1 + x0*x1) diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index a9390469..900d422d 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -1058,25 +1058,31 @@ cdef class fmpq_mpoly(flint_mpoly): >>> from flint import Ordering >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) >>> x, y = ctx.gens() - >>> f = x**3 * y + x * y**4 + x * y + >>> f = x**2 * y**2 + x * y**2 >>> q, N = f.deflation() >>> q, N - (x + y + 1, [2, 3]) + (x^2*y + x*y, [1, 2]) + >>> q.inflate(N) == f + True """ cdef: - fmpz_vec _shift = fmpz_vec(self.ctx.nvars()) - fmpz_vec stride = fmpz_vec(self.ctx.nvars()) + slong nvars = self.ctx.nvars() + fmpz_vec shift = fmpz_vec(nvars) + fmpz_vec stride = fmpz_vec(nvars) fmpq_mpoly res = create_fmpq_mpoly(self.ctx) - fmpz_mpoly_deflation(_shift.val, stride.val, self.val.zpoly, self.ctx.val.zctx) + fmpz_mpoly_deflation(shift.val, stride.val, self.val.zpoly, self.ctx.val.zctx) + + for i in range(nvars): + stride[i] = shift[i].gcd(stride[i]) + shift[i] = 0 - cdef fmpz_vec zero_shift = fmpz_vec(self.ctx.nvars()) - fmpz_mpoly_deflate(res.val.zpoly, self.val.zpoly, zero_shift.val, stride.val, self.ctx.val.zctx) + fmpz_mpoly_deflate(res.val.zpoly, self.val.zpoly, shift.val, stride.val, self.ctx.val.zctx) fmpq_set(res.val.content, self.val.content) return res, list(stride) - def deflation_monom(self) -> tuple[list[int], fmpq_mpoly]: + def deflation_monom(self) -> tuple[fmpq_mpoly, list[int], fmpq_mpoly]: """ Compute the exponent vector ``N`` and monomial ``m`` such that ``p(X^(1/N)) = m * q(X^N)`` for maximal N. Importantly the deflation itself is not computed @@ -1086,21 +1092,25 @@ cdef class fmpq_mpoly(flint_mpoly): >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) >>> x, y = ctx.gens() >>> f = x**3 * y + x * y**4 + x * y - >>> N, m = f.deflation_monom() - >>> N, m - ([2, 3], x*y) - >>> f_deflated = f.deflate(N) - >>> f_deflated - x + y + 1 - >>> m * f_deflated.inflate(N) + >>> fd, N, m = f.deflation_monom() + >>> fd, N, m + (x + y + 1, [2, 3], x*y) + >>> m * fd.inflate(N) x^3*y + x*y^4 + x*y """ - cdef fmpq_mpoly monom = create_fmpq_mpoly(self.ctx) + cdef: + slong nvars = self.ctx.nvars() + fmpq_mpoly res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly monom = create_fmpq_mpoly(self.ctx) + fmpz_vec shift = fmpz_vec(nvars) + fmpz_vec stride = fmpz_vec(nvars) - stride, _shift = self.deflation_index() + fmpz_mpoly_deflation(shift.val, stride.val, self.val.zpoly, self.ctx.val.zctx) + fmpq_mpoly_push_term_ui_ffmpz(monom.val, 1, fmpz_vec(shift).val, self.ctx.val) + fmpz_mpoly_deflate(res.val.zpoly, self.val.zpoly, shift.val, stride.val, self.ctx.val.zctx) + fmpq_set(res.val.content, self.val.content) - fmpq_mpoly_push_term_ui_ffmpz(monom.val, 1, fmpz_vec(_shift).val, self.ctx.val) - return list(stride), monom + return res, list(stride), monom def deflation_index(self) -> tuple[list[int], list[int]]: """ diff --git a/src/flint/types/fmpz_mod_mpoly.pyx b/src/flint/types/fmpz_mod_mpoly.pyx index 64106b68..1e21a5ca 100644 --- a/src/flint/types/fmpz_mod_mpoly.pyx +++ b/src/flint/types/fmpz_mod_mpoly.pyx @@ -1137,24 +1137,30 @@ cdef class fmpz_mod_mpoly(flint_mpoly): >>> from flint import Ordering >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, nametup=('x', 'y')) >>> x, y = ctx.gens() - >>> f = x**3 * y + x * y**4 + x * y + >>> f = x**2 * y**2 + x * y**2 >>> q, N = f.deflation() >>> q, N - (x + y + 1, [2, 3]) + (x^2*y + x*y, [1, 2]) + >>> q.inflate(N) == f + True """ cdef: - fmpz_vec _shift = fmpz_vec(self.ctx.nvars()) - fmpz_vec stride = fmpz_vec(self.ctx.nvars()) + slong nvars = self.ctx.nvars() + fmpz_vec shift = fmpz_vec(nvars) + fmpz_vec stride = fmpz_vec(nvars) fmpz_mod_mpoly res = create_fmpz_mod_mpoly(self.ctx) - fmpz_mod_mpoly_deflation(_shift.val, stride.val, self.val, self.ctx.val) + fmpz_mod_mpoly_deflation(shift.val, stride.val, self.val, self.ctx.val) + + for i in range(nvars): + stride[i] = shift[i].gcd(stride[i]) + shift[i] = 0 - cdef fmpz_vec zero_shift = fmpz_vec(self.ctx.nvars()) - fmpz_mod_mpoly_deflate(res.val, self.val, zero_shift.val, stride.val, self.ctx.val) + fmpz_mod_mpoly_deflate(res.val, self.val, shift.val, stride.val, self.ctx.val) return res, list(stride) - def deflation_monom(self) -> tuple[list[int], fmpz_mod_mpoly]: + def deflation_monom(self) -> tuple[fmpz_mod_mpoly, list[int], fmpz_mod_mpoly]: """ Compute the exponent vector ``N`` and monomial ``m`` such that ``p(X^(1/N)) = m * q(X^N)`` for maximal N. Importantly the deflation itself is not computed @@ -1164,21 +1170,24 @@ cdef class fmpz_mod_mpoly(flint_mpoly): >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, nametup=('x', 'y')) >>> x, y = ctx.gens() >>> f = x**3 * y + x * y**4 + x * y - >>> N, m = f.deflation_monom() - >>> N, m - ([2, 3], x*y) - >>> f_deflated = f.deflate(N) - >>> f_deflated - x + y + 1 - >>> m * f_deflated.inflate(N) + >>> fd, N, m = f.deflation_monom() + >>> fd, N, m + (x + y + 1, [2, 3], x*y) + >>> m * fd.inflate(N) x^3*y + x*y^4 + x*y """ - cdef fmpz_mod_mpoly monom = create_fmpz_mod_mpoly(self.ctx) + cdef: + slong nvars = self.ctx.nvars() + fmpz_mod_mpoly res = create_fmpz_mod_mpoly(self.ctx) + fmpz_mod_mpoly monom = create_fmpz_mod_mpoly(self.ctx) + fmpz_vec shift = fmpz_vec(nvars) + fmpz_vec stride = fmpz_vec(nvars) - stride, _shift = self.deflation_index() + fmpz_mod_mpoly_deflation(shift.val, stride.val, self.val, self.ctx.val) + fmpz_mod_mpoly_push_term_ui_ffmpz(monom.val, 1, fmpz_vec(shift).val, self.ctx.val) + fmpz_mod_mpoly_deflate(res.val, self.val, shift.val, stride.val, self.ctx.val) - fmpz_mod_mpoly_push_term_ui_ffmpz(monom.val, 1, fmpz_vec(_shift).val, self.ctx.val) - return list(stride), monom + return res, list(stride), monom def deflation_index(self) -> tuple[list[int], list[int]]: """ diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 1d059a6a..1dc82fec 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -1156,24 +1156,30 @@ cdef class fmpz_mpoly(flint_mpoly): >>> from flint import Ordering >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) >>> x, y = ctx.gens() - >>> f = x**3 * y + x * y**4 + x * y + >>> f = x**2 * y**2 + x * y**2 >>> q, N = f.deflation() >>> q, N - (x + y + 1, [2, 3]) + (x^2*y + x*y, [1, 2]) + >>> q.inflate(N) == f + True """ cdef: - fmpz_vec _shift = fmpz_vec(self.ctx.nvars()) - fmpz_vec stride = fmpz_vec(self.ctx.nvars()) + slong nvars = self.ctx.nvars() + fmpz_vec shift = fmpz_vec(nvars) + fmpz_vec stride = fmpz_vec(nvars) fmpz_mpoly res = create_fmpz_mpoly(self.ctx) - fmpz_mpoly_deflation(_shift.val, stride.val, self.val, self.ctx.val) + fmpz_mpoly_deflation(shift.val, stride.val, self.val, self.ctx.val) + + for i in range(nvars): + stride[i] = shift[i].gcd(stride[i]) + shift[i] = 0 - cdef fmpz_vec zero_shift = fmpz_vec(self.ctx.nvars()) - fmpz_mpoly_deflate(res.val, self.val, zero_shift.val, stride.val, self.ctx.val) + fmpz_mpoly_deflate(res.val, self.val, shift.val, stride.val, self.ctx.val) return res, list(stride) - def deflation_monom(self) -> tuple[list[int], fmpz_mpoly]: + def deflation_monom(self) -> tuple[fmpz_mpoly, list[int], fmpz_mpoly]: """ Compute the exponent vector ``N`` and monomial ``m`` such that ``p(X^(1/N)) = m * q(X^N)`` for maximal N. Importantly the deflation itself is not computed @@ -1183,21 +1189,24 @@ cdef class fmpz_mpoly(flint_mpoly): >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) >>> x, y = ctx.gens() >>> f = x**3 * y + x * y**4 + x * y - >>> N, m = f.deflation_monom() - >>> N, m - ([2, 3], x*y) - >>> f_deflated = f.deflate(N) - >>> f_deflated - x + y + 1 - >>> m * f_deflated.inflate(N) + >>> fd, N, m = f.deflation_monom() + >>> fd, N, m + (x + y + 1, [2, 3], x*y) + >>> m * fd.inflate(N) x^3*y + x*y^4 + x*y """ - cdef fmpz_mpoly monom = create_fmpz_mpoly(self.ctx) + cdef: + slong nvars = self.ctx.nvars() + fmpz_mpoly res = create_fmpz_mpoly(self.ctx) + fmpz_mpoly monom = create_fmpz_mpoly(self.ctx) + fmpz_vec shift = fmpz_vec(nvars) + fmpz_vec stride = fmpz_vec(nvars) - stride, _shift = self.deflation_index() + fmpz_mpoly_deflation(shift.val, stride.val, self.val, self.ctx.val) + fmpz_mpoly_push_term_ui_ffmpz(monom.val, 1, fmpz_vec(shift).val, self.ctx.val) + fmpz_mpoly_deflate(res.val, self.val, shift.val, stride.val, self.ctx.val) - fmpz_mpoly_push_term_ui_ffmpz(monom.val, 1, fmpz_vec(_shift).val, self.ctx.val) - return list(stride), monom + return res, list(stride), monom def deflation_index(self) -> tuple[list[int], list[int]]: """ diff --git a/src/flint/types/fmpz_poly.pyx b/src/flint/types/fmpz_poly.pyx index 024a1bc5..91510016 100644 --- a/src/flint/types/fmpz_poly.pyx +++ b/src/flint/types/fmpz_poly.pyx @@ -630,8 +630,11 @@ cdef class fmpz_poly(flint_poly): n. returns ``q, n`` such that ``self == q.inflate(n)``. >>> f = fmpz_poly([1, 0, 1]) - >>> f.deflation() + >>> q, n = f.deflation() + >>> q, n (x + 1, 2) + >>> q.inflate(n) == f + True """ cdef ulong n if fmpz_poly_is_zero(self.val): @@ -647,14 +650,17 @@ cdef class fmpz_poly(flint_poly): >>> f = fmpz_poly([1, 0, 1]) >>> f.deflation_monom() - (1, x) + (x^2 + 1, 1, x) """ n, m = self.deflation_index() cdef fmpz_poly monom = fmpz_poly.__new__(fmpz_poly) + cdef fmpz_poly res = fmpz_poly.__new__(fmpz_poly) + fmpz_poly_set_coeff_ui(monom.val, m, 1) + fmpz_poly_deflate(res.val, self.val, n) - return n, monom + return res, n, monom def deflation_index(self) -> tuple[int, int]: """ diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx index a16c5bf0..99247cfb 100644 --- a/src/flint/types/nmod_mpoly.pyx +++ b/src/flint/types/nmod_mpoly.pyx @@ -1107,24 +1107,30 @@ cdef class nmod_mpoly(flint_mpoly): >>> from flint import Ordering >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, nametup=('x', 'y')) >>> x, y = ctx.gens() - >>> f = x**3 * y + x * y**4 + x * y + >>> f = x**2 * y**2 + x * y**2 >>> q, N = f.deflation() >>> q, N - (x + y + 1, [2, 3]) + (x^2*y + x*y, [1, 2]) + >>> q.inflate(N) == f + True """ cdef: - fmpz_vec _shift = fmpz_vec(self.ctx.nvars()) - fmpz_vec stride = fmpz_vec(self.ctx.nvars()) + slong nvars = self.ctx.nvars() + fmpz_vec shift = fmpz_vec(nvars) + fmpz_vec stride = fmpz_vec(nvars) nmod_mpoly res = create_nmod_mpoly(self.ctx) - nmod_mpoly_deflation(_shift.val, stride.val, self.val, self.ctx.val) + nmod_mpoly_deflation(shift.val, stride.val, self.val, self.ctx.val) + + for i in range(nvars): + stride[i] = shift[i].gcd(stride[i]) + shift[i] = 0 - cdef fmpz_vec zero_shift = fmpz_vec(self.ctx.nvars()) - nmod_mpoly_deflate(res.val, self.val, zero_shift.val, stride.val, self.ctx.val) + nmod_mpoly_deflate(res.val, self.val, shift.val, stride.val, self.ctx.val) return res, list(stride) - def deflation_monom(self) -> tuple[list[int], nmod_mpoly]: + def deflation_monom(self) -> tuple[nmod_mpoly, list[int], nmod_mpoly]: """ Compute the exponent vector ``N`` and monomial ``m`` such that ``p(X^(1/N)) = m * q(X^N)`` for maximal N. Importantly the deflation itself is not computed @@ -1134,21 +1140,24 @@ cdef class nmod_mpoly(flint_mpoly): >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, nametup=('x', 'y')) >>> x, y = ctx.gens() >>> f = x**3 * y + x * y**4 + x * y - >>> N, m = f.deflation_monom() - >>> N, m - ([2, 3], x*y) - >>> f_deflated = f.deflate(N) - >>> f_deflated - x + y + 1 - >>> m * f_deflated.inflate(N) + >>> fd, N, m = f.deflation_monom() + >>> fd, N, m + (x + y + 1, [2, 3], x*y) + >>> m * fd.inflate(N) x^3*y + x*y^4 + x*y """ - cdef nmod_mpoly monom = create_nmod_mpoly(self.ctx) + cdef: + slong nvars = self.ctx.nvars() + nmod_mpoly res = create_nmod_mpoly(self.ctx) + nmod_mpoly monom = create_nmod_mpoly(self.ctx) + fmpz_vec shift = fmpz_vec(nvars) + fmpz_vec stride = fmpz_vec(nvars) - stride, _shift = self.deflation_index() + nmod_mpoly_deflation(shift.val, stride.val, self.val, self.ctx.val) + nmod_mpoly_push_term_ui_ffmpz(monom.val, 1, fmpz_vec(shift).val, self.ctx.val) + nmod_mpoly_deflate(res.val, self.val, shift.val, stride.val, self.ctx.val) - nmod_mpoly_push_term_ui_ffmpz(monom.val, 1, fmpz_vec(_shift).val, self.ctx.val) - return list(stride), monom + return res, list(stride), monom def deflation_index(self) -> tuple[list[int], list[int]]: """ From b2d2694fcf669a3b28cae4a01aa77b44ca0547bf Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Sun, 8 Sep 2024 23:02:46 +1000 Subject: [PATCH 22/27] Make verbose tests identifible --- src/flint/test/test_docstrings.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/flint/test/test_docstrings.py b/src/flint/test/test_docstrings.py index 5ed06189..7a4c0287 100644 --- a/src/flint/test/test_docstrings.py +++ b/src/flint/test/test_docstrings.py @@ -70,7 +70,8 @@ def report_unexpected_exception(self, out, test, example, exc_info): [ test for _, test_set in find_doctests(flint) for test in test_set - ] + ], + ids=lambda test: test.name, ) def test_docstrings(test): runner.run(test) From f34e2287e4d69c1704a3eb632a50b731e48f549b Mon Sep 17 00:00:00 2001 From: Jake Moss Date: Mon, 9 Sep 2024 13:37:29 +1000 Subject: [PATCH 23/27] Replace back ticks with quotes in exception strings --- src/flint/flint_base/flint_base.pyx | 6 +++--- src/flint/types/fmpz_mod.pyx | 2 +- src/flint/types/fmpz_mod_mpoly.pyx | 6 +++--- src/flint/types/fmpz_mod_poly.pyx | 8 ++++---- src/flint/types/fq_default_poly.pyx | 4 ++-- src/flint/types/nmod_mpoly.pyx | 4 ++-- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index c4569063..61de7402 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -347,14 +347,14 @@ cdef class flint_mpoly_context(flint_elem): # A type hint of ``ordering: Ordering`` results in the error "TypeError: an integer is required" if a Ordering # object is not provided. This is pretty obtuse so we check its type ourselves if not isinstance(ordering, Ordering): - raise TypeError(f"``ordering`` ('{ordering}') is not an instance of flint.Ordering") + raise TypeError(f"'ordering' ('{ordering}') is not an instance of flint.Ordering") if nametup is not None: key = nvars, ordering, nametup elif nametup is None and names is not None: key = nvars, ordering, cls.create_variable_names(nvars, names) else: - raise ValueError("must provide either ``names`` or ``nametup``") + raise ValueError("must provide either 'names' or 'nametup'") return key @classmethod @@ -854,7 +854,7 @@ cdef class flint_mat(flint_elem): cdef ordering_t ordering_py_to_c(ordering): # Cython does not like an "Ordering" type hint here if not isinstance(ordering, Ordering): - raise TypeError(f"`ordering` ('{ordering}') is not an instance of flint.Ordering") + raise TypeError(f"'ordering' ('{ordering}') is not an instance of flint.Ordering") if ordering == Ordering.lex: return ordering_t.ORD_LEX diff --git a/src/flint/types/fmpz_mod.pyx b/src/flint/types/fmpz_mod.pyx index c92ed650..5559e20e 100644 --- a/src/flint/types/fmpz_mod.pyx +++ b/src/flint/types/fmpz_mod.pyx @@ -246,7 +246,7 @@ cdef class fmpz_mod(flint_scalar): self.ctx = ctx check = self.ctx.set_any_as_fmpz_mod(self.val, val) if check is NotImplemented: - raise NotImplementedError(f"Cannot convert {val} to type `fmpz_mod`") + raise NotImplementedError(f"Cannot convert {val} to type 'fmpz_mod'") def is_zero(self): """ diff --git a/src/flint/types/fmpz_mod_mpoly.pyx b/src/flint/types/fmpz_mod_mpoly.pyx index 1e21a5ca..a85a3252 100644 --- a/src/flint/types/fmpz_mod_mpoly.pyx +++ b/src/flint/types/fmpz_mod_mpoly.pyx @@ -117,11 +117,11 @@ cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context): # A type hint of ``ordering: Ordering`` results in the error "TypeError: an integer is required" if a Ordering # object is not provided. This is pretty obtuse so we check its type ourselves if not isinstance(ordering, Ordering): - raise TypeError(f"`ordering` ('{ordering}') is not an instance of flint.Ordering") + raise TypeError(f"'ordering' ('{ordering}') is not an instance of flint.Ordering") elif not typecheck(modulus, fmpz): m = any_as_fmpz(modulus) if m is NotImplemented: - raise TypeError(f"`modulus` ('{modulus}') is not coercible to fmpz") + raise TypeError(f"'modulus' ('{modulus}') is not coercible to fmpz") else: modulus = m @@ -130,7 +130,7 @@ cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context): elif nametup is None and names is not None: key = nvars, ordering, cls.create_variable_names(nvars, names), modulus else: - raise ValueError("must provide either `names` or `nametup`") + raise ValueError("must provide either 'names' or 'nametup'") return key def any_as_scalar(self, other): diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index a28e56be..5204db35 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -130,7 +130,7 @@ cdef class fmpz_mod_poly_ctx: """ cdef slong length if not (isinstance(monic, bool) and isinstance(irreducible, bool)): - raise ValueError("Both `monic` and `irreducible` must be of type bool") + raise ValueError("Both 'monic' and 'irreducible' must be of type bool") length = degree + 1 if length <= 0: @@ -292,7 +292,7 @@ cdef class fmpz_mod_poly_ctx: for i in range(n): check = self.mod.set_any_as_fmpz_mod(&xs.val[i], vals[i]) if check is NotImplemented: - raise ValueError(f"Unable to cast {vals[i]} to an `fmpz_mod`") + raise ValueError(f"Unable to cast {vals[i]} to an 'fmpz_mod'") res = self.new_ctype_poly() fmpz_mod_poly_minpoly(res.val, xs.val, n, self.mod.val) @@ -482,7 +482,7 @@ cdef class fmpz_mod_poly(flint_poly): # Case when right is not fmpz_mod_poly, try to convert to fmpz right = self.ctx.any_as_fmpz_mod_poly(right) if right is NotImplemented: - raise TypeError(f"Cannot convert {right} to `fmpz_mod_poly` type.") + raise TypeError(f"Cannot convert {right} to 'fmpz_mod_poly' type.") if right == 0: raise ZeroDivisionError("Cannot divide by zero") @@ -765,7 +765,7 @@ cdef class fmpz_mod_poly(flint_poly): for i in range(n): check = self.ctx.mod.set_any_as_fmpz_mod(&xs.val[i], vals[i]) if check is NotImplemented: - raise ValueError(f"Unable to cast {vals[i]} to an `fmpz_mod`") + raise ValueError(f"Unable to cast {vals[i]} to an 'fmpz_mod'") # Call for multipoint eval, iterative horner will be used # for small arrays (len < 32) and a fast eval for larger ones diff --git a/src/flint/types/fq_default_poly.pyx b/src/flint/types/fq_default_poly.pyx index ec2dfe83..8b323b57 100644 --- a/src/flint/types/fq_default_poly.pyx +++ b/src/flint/types/fq_default_poly.pyx @@ -188,7 +188,7 @@ cdef class fq_default_poly_ctx: """ cdef slong length if not (isinstance(monic, bool) and isinstance(irreducible, bool) and isinstance(not_zero, bool)): - raise TypeError("All of `not_zero`, `monic` and `irreducible` must be of type bool") + raise TypeError("All of 'not_zero', 'monic' and 'irreducible' must be of type bool") length = degree + 1 if length <= 0: @@ -748,7 +748,7 @@ cdef class fq_default_poly(flint_poly): # Case when right is not fq_default_poly, try to convert to fmpz other = self.ctx.any_as_fq_default_poly(other) if other is NotImplemented: - raise TypeError(f"Cannot convert {other} to `fq_default_poly` type.") + raise TypeError(f"Cannot convert {other} to 'fq_default_poly' type.") if other.is_zero(): raise ZeroDivisionError("Cannot divide by zero") diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx index 99247cfb..5ff97c1a 100644 --- a/src/flint/types/nmod_mpoly.pyx +++ b/src/flint/types/nmod_mpoly.pyx @@ -116,14 +116,14 @@ cdef class nmod_mpoly_ctx(flint_mpoly_context): # A type hint of ``ordering: Ordering`` results in the error "TypeError: an integer is required" if a Ordering # object is not provided. This is pretty obtuse so we check its type ourselves if not isinstance(ordering, Ordering): - raise TypeError(f"`ordering` ('{ordering}') is not an instance of flint.Ordering") + raise TypeError(f"'ordering' ('{ordering}') is not an instance of flint.Ordering") if nametup is not None: key = nvars, ordering, nametup, modulus elif nametup is None and names is not None: key = nvars, ordering, cls.create_variable_names(nvars, names), modulus else: - raise ValueError("must provide either `names` or `nametup`") + raise ValueError("must provide either 'names' or 'nametup'") return key def any_as_scalar(self, other): From f4825401d78c1b63e935c07acd5a52913f76a1a7 Mon Sep 17 00:00:00 2001 From: Jake Moss Date: Mon, 9 Sep 2024 14:20:57 +1000 Subject: [PATCH 24/27] Clarify doc strings --- src/flint/types/fmpq_mpoly.pyx | 2 ++ src/flint/types/fmpz_mod_mpoly.pyx | 2 ++ src/flint/types/fmpz_mpoly.pyx | 2 ++ src/flint/types/fmpz_poly.pyx | 10 +++++----- src/flint/types/nmod_mpoly.pyx | 2 ++ 5 files changed, 13 insertions(+), 5 deletions(-) diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 900d422d..31c25330 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -1131,6 +1131,8 @@ cdef class fmpq_mpoly(flint_mpoly): >>> f_deflated x + y + 1 >>> m = ctx.term(exp_vec=I) + >>> m + x*y >>> m * f_deflated.inflate(N) x^3*y + x*y^4 + x*y """ diff --git a/src/flint/types/fmpz_mod_mpoly.pyx b/src/flint/types/fmpz_mod_mpoly.pyx index a85a3252..576477b5 100644 --- a/src/flint/types/fmpz_mod_mpoly.pyx +++ b/src/flint/types/fmpz_mod_mpoly.pyx @@ -1208,6 +1208,8 @@ cdef class fmpz_mod_mpoly(flint_mpoly): >>> f_deflated x + y + 1 >>> m = ctx.term(exp_vec=I) + >>> m + x*y >>> m * f_deflated.inflate(N) x^3*y + x*y^4 + x*y """ diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 1dc82fec..b8123816 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -1227,6 +1227,8 @@ cdef class fmpz_mpoly(flint_mpoly): >>> f_deflated x + y + 1 >>> m = ctx.term(exp_vec=I) + >>> m + x*y >>> m * f_deflated.inflate(N) x^3*y + x*y^4 + x*y """ diff --git a/src/flint/types/fmpz_poly.pyx b/src/flint/types/fmpz_poly.pyx index 91510016..6b080333 100644 --- a/src/flint/types/fmpz_poly.pyx +++ b/src/flint/types/fmpz_poly.pyx @@ -644,7 +644,7 @@ cdef class fmpz_poly(flint_poly): def deflation_monom(self) -> tuple[fmpz_poly, int, fmpz_poly]: """ - Compute the exponent vector ``n`` and monomial ``m`` such that ``p(x^(1/n)) + Compute the exponent ``n`` and monomial ``m`` such that ``p(x^(1/n)) = m * q(x^n)`` for maximal n. Importantly the deflation itself is not computed here. The returned monomial allows the undo-ing of the deflation. @@ -664,10 +664,10 @@ cdef class fmpz_poly(flint_poly): def deflation_index(self) -> tuple[int, int]: """ - Compute the exponent vectors ``N`` and ``I`` such that ``p(X^(1/N)) = X^I * - q(X^N)`` for maximal N. Importantly the deflation itself is not computed - here. The returned exponent vector ``I`` is the shift that was applied to the - exponents. It is the exponent vector of the monomial returned by + Compute the exponent ``n`` and ``i`` such that ``p(x^(1/n)) = x^i * + q(x^n)`` for maximal ``n``. Importantly the deflation itself is not computed + here. The returned exponent ``i`` is the shift that was applied to the + exponents. It is the exponent of the monomial returned by ``deflation_monom``. >>> f = fmpz_poly([1, 0, 1]) diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx index 5ff97c1a..d90ecffd 100644 --- a/src/flint/types/nmod_mpoly.pyx +++ b/src/flint/types/nmod_mpoly.pyx @@ -1178,6 +1178,8 @@ cdef class nmod_mpoly(flint_mpoly): >>> f_deflated x + y + 1 >>> m = ctx.term(exp_vec=I) + >>> m + x*y >>> m * f_deflated.inflate(N) x^3*y + x*y^4 + x*y """ From 5fc1d74f6d083e4ec82027a748ecde17946fa5b3 Mon Sep 17 00:00:00 2001 From: Jake Moss Date: Mon, 9 Sep 2024 14:45:54 +1000 Subject: [PATCH 25/27] Create uncocumented.pxd, move undocumented function uses --- src/flint/flintlib/functions/fmpz_vec.pxd | 3 --- src/flint/flintlib/functions/nmod_mpoly.pxd | 5 ----- src/flint/flintlib/types/nmod.pxd | 3 --- src/flint/flintlib/types/undocumented.pxd | 12 ++++++++++++ src/flint/types/nmod_mat.pyx | 6 ++---- src/flint/types/nmod_mpoly.pyx | 8 +++++--- 6 files changed, 19 insertions(+), 18 deletions(-) create mode 100644 src/flint/flintlib/types/undocumented.pxd diff --git a/src/flint/flintlib/functions/fmpz_vec.pxd b/src/flint/flintlib/functions/fmpz_vec.pxd index 1bce5f5b..9ec83eed 100644 --- a/src/flint/flintlib/functions/fmpz_vec.pxd +++ b/src/flint/flintlib/functions/fmpz_vec.pxd @@ -68,6 +68,3 @@ cdef extern from "flint/fmpz_vec.h": void _fmpz_vec_dot_general_naive(fmpz_t res, const fmpz_t initial, int subtract, const fmpz_struct * a, const fmpz_struct * b, int reverse, slong len) void _fmpz_vec_dot_general(fmpz_t res, const fmpz_t initial, int subtract, const fmpz_struct * a, const fmpz_struct * b, int reverse, slong len) void _fmpz_vec_dot(fmpz_t res, const fmpz_struct * vec1, const fmpz_struct * vec2, slong len2) - - # Undocumented internal functions - void _fmpz_vec_content(fmpz_t res, const fmpz_struct * vec, slong len) diff --git a/src/flint/flintlib/functions/nmod_mpoly.pxd b/src/flint/flintlib/functions/nmod_mpoly.pxd index 8104c033..973a1cfe 100644 --- a/src/flint/flintlib/functions/nmod_mpoly.pxd +++ b/src/flint/flintlib/functions/nmod_mpoly.pxd @@ -133,8 +133,3 @@ cdef extern from "flint/nmod_mpoly.h": void nmod_mpoly_div_monagan_pearce(nmod_mpoly_t polyq, const nmod_mpoly_t poly2, const nmod_mpoly_t poly3, const nmod_mpoly_ctx_t ctx) void nmod_mpoly_divrem_monagan_pearce(nmod_mpoly_t q, nmod_mpoly_t r, const nmod_mpoly_t poly2, const nmod_mpoly_t poly3, const nmod_mpoly_ctx_t ctx) void nmod_mpoly_divrem_ideal_monagan_pearce(nmod_mpoly_struct ** q, nmod_mpoly_t r, const nmod_mpoly_t poly2, nmod_mpoly_struct * const * poly3, slong len, const nmod_mpoly_ctx_t ctx) - - # These functions seem to be undocumented but appear in the header. - void nmod_mpoly_deflation(fmpz_struct * shift, fmpz_struct * stride, const nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx) - void nmod_mpoly_deflate(nmod_mpoly_t A, const nmod_mpoly_t B, const fmpz_struct * shift, const fmpz_struct * stride, const nmod_mpoly_ctx_t ctx) - void nmod_mpoly_inflate(nmod_mpoly_t A, const nmod_mpoly_t B, const fmpz_struct * shift, const fmpz_struct * stride, const nmod_mpoly_ctx_t ctx) diff --git a/src/flint/flintlib/types/nmod.pxd b/src/flint/flintlib/types/nmod.pxd index 1b7616e3..c1324908 100644 --- a/src/flint/flintlib/types/nmod.pxd +++ b/src/flint/flintlib/types/nmod.pxd @@ -13,9 +13,6 @@ cdef extern from "flint/nmod_types.h": ctypedef nmod_mat_struct nmod_mat_t[1] - # XXX: Undocumented function: - int nmod_mat_is_square(const nmod_mat_t mat) - # Macros: ulong nmod_mat_entry(nmod_mat_t mat, slong i, slong j) diff --git a/src/flint/flintlib/types/undocumented.pxd b/src/flint/flintlib/types/undocumented.pxd new file mode 100644 index 00000000..247a3912 --- /dev/null +++ b/src/flint/flintlib/types/undocumented.pxd @@ -0,0 +1,12 @@ +from flint.flintlib.types.nmod cimport nmod_mat_t + +from flint.flintlib.types.flint cimport fmpz_struct +from flint.flintlib.types.nmod cimport nmod_mpoly_ctx_t, nmod_mpoly_t + +cdef extern from "flint/nmod_types.h": + int nmod_mat_is_square(const nmod_mat_t mat) + +cdef extern from "flint/nmod_mpoly.h": + void nmod_mpoly_deflation(fmpz_struct * shift, fmpz_struct * stride, const nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_deflate(nmod_mpoly_t A, const nmod_mpoly_t B, const fmpz_struct * shift, const fmpz_struct * stride, const nmod_mpoly_ctx_t ctx) + void nmod_mpoly_inflate(nmod_mpoly_t A, const nmod_mpoly_t B, const fmpz_struct * shift, const fmpz_struct * stride, const nmod_mpoly_ctx_t ctx) diff --git a/src/flint/types/nmod_mat.pyx b/src/flint/types/nmod_mat.pyx index 60f12f5c..4ba93a9c 100644 --- a/src/flint/types/nmod_mat.pyx +++ b/src/flint/types/nmod_mat.pyx @@ -12,10 +12,8 @@ from flint.flintlib.functions.fmpz_mat cimport fmpz_mat_get_nmod_mat from flint.flintlib.types.nmod cimport nmod_mat_struct -from flint.flintlib.types.nmod cimport ( - nmod_mat_is_square, - nmod_mat_entry, -) +from flint.flintlib.types.nmod cimport nmod_mat_entry +from flint.flintlib.types.undocumented cimport nmod_mat_is_square from flint.flintlib.functions.nmod_mat cimport ( nmod_mat_init, diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx index d90ecffd..d21b233a 100644 --- a/src/flint/types/nmod_mpoly.pyx +++ b/src/flint/types/nmod_mpoly.pyx @@ -25,8 +25,6 @@ from flint.flintlib.functions.nmod_mpoly cimport ( nmod_mpoly_compose_nmod_mpoly, nmod_mpoly_ctx_init, nmod_mpoly_ctx_modulus, - nmod_mpoly_deflate, - nmod_mpoly_deflation, nmod_mpoly_degrees_fmpz, nmod_mpoly_derivative, nmod_mpoly_discriminant, @@ -43,7 +41,6 @@ from flint.flintlib.functions.nmod_mpoly cimport ( nmod_mpoly_get_str_pretty, nmod_mpoly_get_term_coeff_ui, nmod_mpoly_get_term_exp_fmpz, - nmod_mpoly_inflate, nmod_mpoly_is_one, nmod_mpoly_is_zero, nmod_mpoly_length, @@ -72,6 +69,11 @@ from flint.flintlib.functions.nmod_mpoly_factor cimport ( nmod_mpoly_factor_t, ) from flint.flintlib.functions.ulong_extras cimport n_is_prime +from flint.flintlib.types.undocumented cimport ( + nmod_mpoly_deflate, + nmod_mpoly_deflation, + nmod_mpoly_inflate, +) from cpython.object cimport Py_EQ, Py_NE cimport libc.stdlib From 703e0dcf4e009717be2aad9ebee42e3ed0ab6c0b Mon Sep 17 00:00:00 2001 From: Jake Moss Date: Mon, 9 Sep 2024 15:10:38 +1000 Subject: [PATCH 26/27] Split `_truediv_scalar_` into two functions --- src/flint/flint_base/flint_base.pxd | 3 ++- src/flint/flint_base/flint_base.pyx | 7 +++++-- src/flint/types/fmpq_mpoly.pyx | 5 ++++- src/flint/types/fmpz_mpoly.pyx | 15 +++++++++------ 4 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/flint/flint_base/flint_base.pxd b/src/flint/flint_base/flint_base.pxd index c45cdad5..1f1d2371 100644 --- a/src/flint/flint_base/flint_base.pxd +++ b/src/flint/flint_base/flint_base.pxd @@ -23,7 +23,8 @@ cdef class flint_mpoly(flint_elem): cdef _mul_mpoly_(self, other) cdef _divmod_mpoly_(self, other) - cdef _truediv_scalar_(self, other, assume_exact: bool) + cdef _truediv_scalar_(self, other) + cdef _divexact_scalar_(self, other) cdef _floordiv_mpoly_(self, other) cdef _truediv_mpoly_(self, other) cdef _mod_mpoly_(self, other) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index 61de7402..18ac3e0c 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -449,7 +449,10 @@ cdef class flint_mpoly(flint_elem): cdef _floordiv_mpoly_(self, other): return NotImplemented - cdef _truediv_scalar_(self, other, assume_exact: bool): + cdef _truediv_scalar_(self, other): + return NotImplemented + + cdef _divexact_scalar_(self, other): return NotImplemented cdef _truediv_mpoly_(self, other): @@ -611,7 +614,7 @@ cdef class flint_mpoly(flint_elem): return NotImplemented self._division_check(other) - res = self._truediv_scalar_(other, False) + res = self._truediv_scalar_(other) if res is not NotImplemented: return res diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 31c25330..6e585a51 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -420,13 +420,16 @@ cdef class fmpq_mpoly(flint_mpoly): fmpq_mpoly_div(quotient.val, self.val, other.val, self.ctx.val) return quotient - cdef _truediv_scalar_(self, arg, _: bool): + cdef _truediv_scalar_(self, arg): cdef fmpq_mpoly quotient cdef fmpq other = arg quotient = create_fmpq_mpoly(self.ctx) fmpq_mpoly_scalar_div_fmpq(quotient.val, self.val, other.val, self.ctx.val) return quotient + cdef _divexact_scalar_(self, arg): + return self._truediv_scalar_(arg) + cdef _truediv_mpoly_(self, arg): cdef fmpq_mpoly quotient, other = arg quotient = create_fmpq_mpoly(self.ctx) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index b8123816..719dff41 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -405,19 +405,22 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_div(quotient.val, self.val, other.val, self.ctx.val) return quotient - cdef _truediv_scalar_(self, arg, assume_exact: bool): + cdef _truediv_scalar_(self, arg): cdef fmpz_mpoly quotient, cdef fmpz other = arg quotient = create_fmpz_mpoly(self.ctx) - if assume_exact: - fmpz_mpoly_scalar_divexact_fmpz(quotient.val, self.val, other.val, self.ctx.val) - return quotient - if fmpz_mpoly_scalar_divides_fmpz(quotient.val, self.val, other.val, self.ctx.val): return quotient else: raise DomainError("fmpz_mpoly division is not exact") + cdef _divexact_scalar_(self, arg): + cdef fmpz_mpoly quotient, + cdef fmpz other = arg + quotient = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_scalar_divexact_fmpz(quotient.val, self.val, other.val, self.ctx.val) + return quotient + cdef _truediv_mpoly_(self, arg): cdef fmpz_mpoly quotient, other = arg quotient = create_fmpz_mpoly(self.ctx) @@ -865,7 +868,7 @@ cdef class fmpz_mpoly(flint_mpoly): (2, x0*x1 + 2*x0) """ cdef fmpz content = self.content() - return content, self._truediv_scalar_(content, True) + return content, self._divexact_scalar_(content) def sqrt(self, assume_perfect_square: bool = False): """ From 4d22d95912f094ea36736abc8b33c42be025b059 Mon Sep 17 00:00:00 2001 From: Jake Moss Date: Mon, 9 Sep 2024 15:11:51 +1000 Subject: [PATCH 27/27] Skip arb.hypgeom_2f1 non-convergence doctest --- src/flint/types/arb.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/flint/types/arb.pyx b/src/flint/types/arb.pyx index 2553a92f..94c281ae 100644 --- a/src/flint/types/arb.pyx +++ b/src/flint/types/arb.pyx @@ -2268,7 +2268,7 @@ cdef class arb(flint_scalar): >>> from flint import showgood >>> showgood(lambda: arb("9/10").hypgeom_2f1(arb(2).sqrt(), 0.5, arb(2).sqrt()+1.5, abc=True), dps=25) 1.447530478120770807945697 - >>> showgood(lambda: arb("9/10").hypgeom_2f1(arb(2).sqrt(), 0.5, arb(2).sqrt()+1.5), dps=25) + >>> showgood(lambda: arb("9/10").hypgeom_2f1(arb(2).sqrt(), 0.5, arb(2).sqrt()+1.5), dps=25) # doctest: +SKIP Traceback (most recent call last): ... ValueError: no convergence (maxprec=960, try higher maxprec)