From 357e4355e4a326ad57da9369337fb19c23997803 Mon Sep 17 00:00:00 2001 From: David Einstein Date: Fri, 11 Aug 2023 12:46:21 -0400 Subject: [PATCH 01/95] Make it work with flint 3.0 Split nmod_poly_factor out from nmod_poly Remove arb from setup.py --- setup.py | 6 +++--- src/flint/_flint.pxd | 16 +++++++++------- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/setup.py b/setup.py index 5888fc1a..4181600a 100644 --- a/setup.py +++ b/setup.py @@ -40,11 +40,11 @@ else: arb = 'arb' - libraries = [arb, "flint"] + libraries = ["flint"] (opt,) = get_config_vars('OPT') os.environ['OPT'] = " ".join(flag for flag in opt.split() if flag != '-Wstrict-prototypes') - +default_include_dirs += ["/Users/davideinstein/opt/flint/include"] default_include_dirs += [ os.path.join(d, "flint") for d in default_include_dirs ] @@ -67,7 +67,7 @@ Extension( "flint._flint", ["src/flint/pyflint.pyx"], libraries=libraries, - library_dirs=default_lib_dirs, + library_dirs=default_lib_dirs + ["/Users/davideinstein/opt/flint/lib"], include_dirs=default_include_dirs, define_macros=define_macros, ) diff --git a/src/flint/_flint.pxd b/src/flint/_flint.pxd index 166e14a6..5f1592e8 100644 --- a/src/flint/_flint.pxd +++ b/src/flint/_flint.pxd @@ -88,13 +88,6 @@ cdef extern from "flint/nmod_poly.h": nmod_t mod ctypedef nmod_poly_struct nmod_poly_t[1] - ctypedef struct nmod_poly_factor_struct: - nmod_poly_struct *p - long *exp - long num - long alloc - ctypedef nmod_poly_factor_struct nmod_poly_factor_t[1] - void nmod_poly_init(nmod_poly_t poly, mp_limb_t n) void nmod_poly_init_preinv(nmod_poly_t poly, mp_limb_t n, mp_limb_t ninv) void nmod_poly_init2(nmod_poly_t poly, mp_limb_t n, long alloc) @@ -156,6 +149,15 @@ cdef extern from "flint/nmod_poly.h": void nmod_poly_exp_series(nmod_poly_t f, nmod_poly_t h, long n) int nmod_poly_is_irreducible(nmod_poly_t f) + +cdef extern from "flint/nmod_poly_factor.h": + ctypedef struct nmod_poly_factor_struct: + nmod_poly_struct *p + long *exp + long num + long alloc + ctypedef nmod_poly_factor_struct nmod_poly_factor_t[1] + mp_limb_t nmod_poly_factor_with_berlekamp(nmod_poly_factor_t result, nmod_poly_t poly) mp_limb_t nmod_poly_factor_with_cantor_zassenhaus(nmod_poly_factor_t result, nmod_poly_t poly) mp_limb_t nmod_poly_factor(nmod_poly_factor_t result, nmod_poly_t input) From e97310550c338b9031e5202c3df27b0736e0e629 Mon Sep 17 00:00:00 2001 From: David Einstein Date: Fri, 11 Aug 2023 14:03:57 -0400 Subject: [PATCH 02/95] Change from distutils to setuptools Added a skeleton pyproject.toml added a simple setuptools setup.py THere is no good replacement to use instead of numpy.distutils to get the default library and include paths. Any suggestions would be welcome. --- pyproject.toml | 2 ++ setup.py | 19 +++++-------------- 2 files changed, 7 insertions(+), 14 deletions(-) create mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 00000000..fcc670ea --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,2 @@ +[build-system] +requires=["setuptools", "cython", "wheel"] diff --git a/setup.py b/setup.py index 4181600a..4396cc53 100644 --- a/setup.py +++ b/setup.py @@ -2,9 +2,8 @@ import os from subprocess import check_call -from distutils.core import setup -from distutils.extension import Extension -from Cython.Distutils import build_ext +from setuptools import setup +from setuptools import Extension from Cython.Build import cythonize from numpy.distutils.system_info import default_include_dirs, default_lib_dirs @@ -29,22 +28,15 @@ elif os.getenv('PYTHON_FLINT_MINGW64_TMP'): # This would be used to build under Windows against these libraries if # they have been installed somewhere other than .local - libraries = ["arb", "flint", "mpfr", "gmp"] + libraries = ["flint", "mpfr", "gmp"] else: # For the MSVC toolchain link with mpir instead of gmp - libraries = ["arb", "flint", "mpir", "mpfr", "pthreads"] + libraries = ["flint", "mpir", "mpfr", "pthreads"] else: - # On Ubuntu libarb.so is called libflint-arb.so - if os.getenv('PYTHON_FLINT_LIBFLINT_ARB'): - arb = 'flint-arb' - else: - arb = 'arb' - libraries = ["flint"] (opt,) = get_config_vars('OPT') os.environ['OPT'] = " ".join(flag for flag in opt.split() if flag != '-Wstrict-prototypes') -default_include_dirs += ["/Users/davideinstein/opt/flint/include"] default_include_dirs += [ os.path.join(d, "flint") for d in default_include_dirs ] @@ -67,7 +59,7 @@ Extension( "flint._flint", ["src/flint/pyflint.pyx"], libraries=libraries, - library_dirs=default_lib_dirs + ["/Users/davideinstein/opt/flint/lib"], + library_dirs=default_lib_dirs, include_dirs=default_include_dirs, define_macros=define_macros, ) @@ -78,7 +70,6 @@ setup( name='python-flint', - cmdclass={'build_ext': build_ext}, ext_modules=cythonize(ext_modules, compiler_directives=compiler_directives), #ext_modules=cythonize(ext_modules, compiler_directives=compiler_directives, annotate=True), packages=['flint', 'flint.test'], From 74c5f85ee2c7a029071fd6398303da2b6d59c5e3 Mon Sep 17 00:00:00 2001 From: David Einstein Date: Wed, 16 Aug 2023 14:52:26 -0400 Subject: [PATCH 03/95] Change include paths for flint 3.0 The arb lib is now part of flint so all paths are prefixed with flint/ --- src/flint/_flint.pxd | 38 +++++++++++++++++++------------------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/src/flint/_flint.pxd b/src/flint/_flint.pxd index 5f1592e8..33dcbe06 100644 --- a/src/flint/_flint.pxd +++ b/src/flint/_flint.pxd @@ -755,7 +755,7 @@ cdef extern from "flint/arith.h": void arith_chebyshev_u_polynomial(fmpz_poly_t v, ulong n) void arith_cyclotomic_polynomial(fmpz_poly_t v, ulong n) -cdef extern from "mag.h": +cdef extern from "flint/mag.h": ctypedef struct mag_struct: fmpz_struct exp mp_limb_t man @@ -770,7 +770,7 @@ cdef extern from "mag.h": void mag_set_ui_2exp_si(mag_t x, ulong v, long e) void mag_hypot(mag_t x, const mag_t y, const mag_t z) -cdef extern from "arf.h": +cdef extern from "flint/arf.h": ctypedef struct arf_struct: fmpz_struct exp long size @@ -904,7 +904,7 @@ cdef extern from "arf.h": double arf_get_d(const arf_t x, arf_rnd_t rnd) void arf_set_d(arf_t x, double v) -cdef extern from "arb.h": +cdef extern from "flint/arb.h": ctypedef struct arb_struct: arf_struct mid mag_struct rad @@ -1195,7 +1195,7 @@ cdef extern from "arb.h": cdef ulong ARB_STR_CONDENSE char * arb_get_str(const arb_t x, long n, ulong flags) -cdef extern from "acb.h": +cdef extern from "flint/acb.h": ctypedef struct acb_struct: arb_struct real arb_struct imag @@ -1386,15 +1386,15 @@ cdef extern from "acb.h": void acb_root_ui(acb_t z, const acb_t x, ulong k, long prec) -cdef extern from "partitions.h": +cdef extern from "flint/partitions.h": void partitions_fmpz_fmpz(fmpz_t, const fmpz_t, int) -cdef extern from "bernoulli.h": +cdef extern from "flint/bernoulli.h": void bernoulli_fmpq_ui(fmpq_t, ulong) void bernoulli_cache_compute(long n) -cdef extern from "arb_poly.h": +cdef extern from "flint/arb_poly.h": ctypedef struct arb_poly_struct: arb_ptr coeffs long length @@ -1602,7 +1602,7 @@ cdef extern from "arb_poly.h": void arb_poly_lambertw_series(arb_poly_t res, const arb_poly_t z, int flags, long len, long prec) -cdef extern from "arb_mat.h": +cdef extern from "flint/arb_mat.h": ctypedef struct arb_mat_struct: arb_ptr entries long r @@ -1683,7 +1683,7 @@ cdef extern from "arb_mat.h": int arb_mat_approx_solve(arb_mat_t X, const arb_mat_t A, const arb_mat_t B, long prec) -cdef extern from "acb_poly.h": +cdef extern from "flint/acb_poly.h": ctypedef struct acb_poly_struct: acb_ptr coeffs long length @@ -1888,7 +1888,7 @@ cdef extern from "acb_poly.h": void acb_poly_lambertw_series(acb_poly_t res, const acb_poly_t z, const fmpz_t k, int flags, long len, long prec) -cdef extern from "acb_mat.h": +cdef extern from "flint/acb_mat.h": ctypedef struct acb_mat_struct: acb_ptr entries long r @@ -1978,7 +1978,7 @@ cdef extern from "acb_mat.h": int acb_mat_eig_multiple(acb_ptr E, const acb_mat_t A, acb_srcptr E_approx, const acb_mat_t R_approx, long prec) -cdef extern from "acb_modular.h": +cdef extern from "flint/acb_modular.h": void acb_modular_theta(acb_t theta1, acb_t theta2, acb_t theta3, acb_t theta4, const acb_t z, const acb_t tau, long prec) void acb_modular_theta_jet(acb_ptr theta1, acb_ptr theta2, acb_ptr theta3, acb_ptr theta4, const acb_t z, const acb_t tau, long len, long prec) void acb_modular_theta_series(acb_poly_t theta1, acb_poly_t theta2, acb_poly_t theta3, acb_poly_t theta4, const acb_poly_t z, const acb_t tau, long len, long prec) @@ -1994,7 +1994,7 @@ cdef extern from "acb_modular.h": void acb_modular_elliptic_e(acb_t w, const acb_t m, long prec) void acb_modular_hilbert_class_poly(fmpz_poly_t res, long D) -cdef extern from "acb_hypgeom.h": +cdef extern from "flint/acb_hypgeom.h": void acb_hypgeom_bessel_j(acb_t res, const acb_t nu, const acb_t z, long prec) void acb_hypgeom_bessel_k(acb_t res, const acb_t nu, const acb_t z, long prec) void acb_hypgeom_bessel_i(acb_t res, const acb_t nu, const acb_t z, long prec) @@ -2072,7 +2072,7 @@ cdef extern from "acb_hypgeom.h": void acb_hypgeom_chi_series(acb_poly_t res, const acb_poly_t h, long n, long prec) void acb_hypgeom_li_series(acb_poly_t res, const acb_poly_t h, int offset, long n, long prec) -cdef extern from "arb_hypgeom.h": +cdef extern from "flint/arb_hypgeom.h": void arb_hypgeom_pfq(arb_t res, arb_srcptr a, long p, arb_srcptr b, long q, const arb_t z, int regularized, long prec) void arb_hypgeom_0f1(arb_t res, const arb_t a, const arb_t z, int regularized, long prec) void arb_hypgeom_m(arb_t res, const arb_t a, const arb_t b, const arb_t z, int regularized, long prec) @@ -2137,7 +2137,7 @@ cdef extern from "arb_hypgeom.h": void arb_hypgeom_legendre_p_ui_root(arb_t res, arb_t weight, ulong n, ulong k, long prec) -cdef extern from "dirichlet.h": +cdef extern from "flint/dirichlet.h": ctypedef struct dirichlet_group_struct: ulong q ulong q_even @@ -2196,7 +2196,7 @@ cdef extern from "dirichlet.h": int dirichlet_char_is_primitive(const dirichlet_group_t G, const dirichlet_char_t chi) ulong dirichlet_chi(const dirichlet_group_t G, const dirichlet_char_t chi, ulong n) -cdef extern from "acb_dirichlet.h": +cdef extern from "flint/acb_dirichlet.h": void acb_dirichlet_eta(acb_t res, const acb_t s, long prec) void acb_dirichlet_chi(acb_t res, const dirichlet_group_t G, const dirichlet_char_t chi, ulong n, long prec) @@ -2213,7 +2213,7 @@ cdef extern from "acb_dirichlet.h": void acb_dirichlet_zeta_zero(acb_t res, const fmpz_t n, long prec) void acb_dirichlet_zeta_zeros(acb_ptr res, const fmpz_t n, long len, long prec) -cdef extern from "acb_elliptic.h": +cdef extern from "flint/acb_elliptic.h": void acb_elliptic_rf(acb_t res, const acb_t x, const acb_t y, const acb_t z, int flags, long prec) void acb_elliptic_rj(acb_t res, const acb_t x, const acb_t y, const acb_t z, const acb_t p, int flags, long prec) void acb_elliptic_rg(acb_t res, const acb_t x, const acb_t y, const acb_t z, int flags, long prec) @@ -2228,7 +2228,7 @@ cdef extern from "acb_elliptic.h": void acb_elliptic_invariants(acb_t g2, acb_t g3, const acb_t tau, long prec) void acb_elliptic_inv_p(acb_t res, const acb_t z, const acb_t tau, long prec) -cdef extern from "acb_calc.h": +cdef extern from "flint/acb_calc.h": ctypedef int (*acb_calc_func_t)(acb_ptr out, const acb_t inp, void * param, long order, long prec) ctypedef struct acb_calc_integrate_opt_struct: @@ -2248,14 +2248,14 @@ cdef extern from "acb_calc.h": const acb_calc_integrate_opt_t options, long prec) -cdef extern from "arb_fmpz_poly.h": +cdef extern from "flint/arb_fmpz_poly.h": void arb_fmpz_poly_evaluate_arb(arb_t res, const fmpz_poly_t poly, const arb_t x, long prec) void arb_fmpz_poly_evaluate_acb(acb_t res, const fmpz_poly_t poly, const acb_t x, long prec) void arb_fmpz_poly_complex_roots(acb_ptr roots, const fmpz_poly_t poly, int flags, long prec) ulong arb_fmpz_poly_deflation(const fmpz_poly_t poly) void arb_fmpz_poly_deflate(fmpz_poly_t res, const fmpz_poly_t poly, ulong deflation) -cdef extern from "acb_dft.h": +cdef extern from "flint/acb_dft.h": void acb_dft(acb_ptr w, acb_srcptr v, long n, long prec) void acb_dft_inverse(acb_ptr w, acb_srcptr v, long n, long prec) From c24b2ff0a5983d9047e974b030677bb79d72ec7b Mon Sep 17 00:00:00 2001 From: David Einstein Date: Thu, 17 Aug 2023 08:05:49 -0400 Subject: [PATCH 04/95] Starting attack at fmpz_mpoly added missing fmpz_mpoly_sort_terms to _flint.pxd updated fmpz_mpoly_context to include names and other term orders Fixed up arithmetic to use python 3 protocol and removed automatic conversion. --- src/flint/_flint.pxd | 2 + src/flint/fmpz_mpoly.pyx | 424 ++++++++++++++++++++++++++------------- src/flint/test/test.py | 13 ++ 3 files changed, 295 insertions(+), 144 deletions(-) diff --git a/src/flint/_flint.pxd b/src/flint/_flint.pxd index 33dcbe06..dae41ce4 100644 --- a/src/flint/_flint.pxd +++ b/src/flint/_flint.pxd @@ -2397,6 +2397,8 @@ cdef extern from "flint/fmpz_mpoly.h": void fmpz_mpoly_get_term_monomial(fmpz_mpoly_t M, const fmpz_mpoly_t A, slong i, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_sort_terms(fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) + # Addition/Subtraction void fmpz_mpoly_add_fmpz(fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_t c, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_add_si(fmpz_mpoly_t A, const fmpz_mpoly_t B, slong c, const fmpz_mpoly_ctx_t ctx) diff --git a/src/flint/fmpz_mpoly.pyx b/src/flint/fmpz_mpoly.pyx index 32ed47d3..2a07705f 100644 --- a/src/flint/fmpz_mpoly.pyx +++ b/src/flint/fmpz_mpoly.pyx @@ -41,27 +41,79 @@ cdef dict _fmpz_mpoly_ctx_cache = {} @cython.auto_pickle(False) cdef class fmpz_mpoly_ctx: + """ + A class for storing the polynomial context + + :param nvars: The number of variables in the ring + :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 `get_fmpz_mpoly_context`. + """ cdef fmpz_mpoly_ctx_t val + cdef public object py_names + cdef char ** c_names + cdef bint _init + + def __cinit__(self): + self._init = False - def __init__(self, slong nvars, ordering="lex"): + def __init__(self, slong nvars, ordering, names): assert nvars >= 1 - fmpz_mpoly_ctx_init(self.val, nvars, ordering_t.ORD_LEX) + assert len(names) == nvars + if ordering == "lex": + fmpz_mpoly_ctx_init(self.val, nvars, ordering_t.ORD_LEX) + elif ordering == "deglex": + fmpz_mpoly_ctx_init(self.val, nvars, ordering_t.ORD_DEGLEX) + elif ordering == "degrevlex": + fmpz_mpoly_ctx_init(self.val, nvars, ordering_t.ORD_DEGREVLEX) + else: + raise ValueError("Unimplemented term order %s" % ordering) + self.py_names = tuple(bytes(name, 'utf-8') for name in names) + self.c_names = libc.stdlib.malloc(nvars * sizeof(char *)) + for i in range(nvars): + self.c_names[i] = self.py_names[i] cpdef slong nvars(self): return self.val.minfo.nvars cpdef ordering(self): - return "lex" + if self.val.minfo.ord == ordering_t.ORD_LEX: + return "lex" + if self.val.minfo.ord == ordering_t.ORD_DEGLEX: + return "deglex" + if self.val.minfo.ord == ordering_t.ORD_DEGREVLEX: + return "degrevlex" + + cpdef name(self, slong i): + assert i >= 0 and i < self.val.minfo.nvars + return self.py_names[i] + + cpdef gen(self, slong i): + cdef fmpz_mpoly res + assert i >= 0 and i < self.val.minfo.nvars + res = fmpz_mpoly.__new__(fmpz_mpoly) + res.ctx = self + fmpz_mpoly_init(res.val, res.ctx.val) + res._init = True + fmpz_mpoly_gen(res.val, i, res.ctx.val) + return res + + def gens(self): + return tuple(self.gen(i) for i in range(self.nvars())) -cdef get_fmpz_mpoly_context(slong nvars=1, ordering=None): +def get_fmpz_mpoly_context(slong nvars=1, ordering="lex", names='x'): if nvars <= 0: nvars = 1 - if ordering is None: - ordering = "lex" - key = (nvars, ordering) + nametup = tuple(name.strip() for name in names.split(',')) + if len(nametup) != nvars: + if len(nametup) != 1: + raise ValueError("Number of variables does not equal number of names") + nametup = tuple(nametup[0] + str(i) for i in range(nvars)) + key = (nvars, ordering, nametup) ctx = _fmpz_mpoly_ctx_cache.get(key) if ctx is None: - ctx = fmpz_mpoly_ctx(nvars, ordering) + ctx = fmpz_mpoly_ctx(nvars, ordering, nametup) _fmpz_mpoly_ctx_cache[key] = ctx return ctx @@ -123,23 +175,31 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_clear(self.val, self.ctx.val) self._init = False - def __init__(self, val=0, slong nvars=-1, ordering=None): + def __init__(self, val=0, ctx=None): if typecheck(val, fmpz_mpoly): - if nvars == -1 and ordering is None: + if ctx is None or ctx == (val).ctx: self.ctx = (val).ctx fmpz_mpoly_init(self.val, self.ctx.val) self._init = True fmpz_mpoly_set(self.val, (val).val, self.ctx.val) else: - self.ctx = get_fmpz_mpoly_context(nvars, ordering) - fmpz_mpoly_init(self.val, self.ctx.val) - self._init = True - _fmpz_mpoly_set2(self.val, self.ctx.val, (val).val, (val).ctx.val) + raise ValueError("Cannot automatically coerce contexts") + elif isinstance(val, str): + if ctx is None: + raise ValueError("Cannot parse a polynomial without context") + val = bytes(val, 'utf-8') + self.ctx = ctx + fmpz_mpoly_init(self.val, self.ctx.val) + self._init = True + fmpz_mpoly_set_str_pretty(self.val, val, self.ctx.c_names, self.ctx.val) + fmpz_mpoly_sort_terms(self.val, self.ctx.val) else: v = any_as_fmpz(val) if v is NotImplemented: raise TypeError("cannot create fmpz_mpoly from type %s" % type(val)) - self.ctx = get_fmpz_mpoly_context(nvars, ordering) + if ctx is None: + raise ValueError("Need context to convert fmpz to fmpz_mpoly") + self.ctx = ctx fmpz_mpoly_init(self.val, self.ctx.val) self._init = True fmpz_mpoly_set_fmpz(self.val, (v).val, self.ctx.val) @@ -147,6 +207,9 @@ cdef class fmpz_mpoly(flint_mpoly): def __nonzero__(self): return not fmpz_mpoly_is_zero(self.val, self.ctx.val) + def __bool__(self): + return not fmpz_mpoly_is_zero(self.val, self.ctx.val) + def is_one(self): return fmpz_mpoly_is_one(self.val, self.ctx.val) @@ -159,6 +222,11 @@ cdef class fmpz_mpoly(flint_mpoly): return bool(fmpz_mpoly_equal((self).val, (other).val, (self).ctx.val)) else: return not bool(fmpz_mpoly_equal((self).val, (other).val, (self).ctx.val)) + else: + if op == 2: + return False + else: + return True if op == 2: return not bool(self - other) else: @@ -169,8 +237,8 @@ cdef class fmpz_mpoly(flint_mpoly): def __hash__(self): s = str(self) - i = s.index("(nvars") - s = s[:i] +# i = s.index("(nvars") +# s = s[:i] return hash(s) def coefficient(self, slong i): @@ -201,27 +269,30 @@ cdef class fmpz_mpoly(flint_mpoly): libc.stdlib.free(tmp) return res - @staticmethod - def gen(slong i, slong nvars=-1, ordering=None): - cdef fmpz_mpoly res - assert i >= 0 - if nvars <= 0: - nvars = i + 1 - assert i < nvars - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = get_fmpz_mpoly_context(nvars, ordering) - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True - fmpz_mpoly_gen(res.val, i, res.ctx.val) - return res - - @staticmethod - def gens(slong n, ordering=None): - # todo: (i, n)? or just (i)? - return tuple(fmpz_mpoly.gen(i, n) for i in range(n)) + # @staticmethod + # def gen(slong i, slong nvars=-1, ordering=None): + # cdef fmpz_mpoly res + # assert i >= 0 + # if nvars <= 0: + # nvars = i + 1 + # assert i < nvars + # res = fmpz_mpoly.__new__(fmpz_mpoly) + # res.ctx = get_fmpz_mpoly_context(nvars, ordering) + # fmpz_mpoly_init(res.val, res.ctx.val) + # res._init = True + # fmpz_mpoly_gen(res.val, i, res.ctx.val) + # return res + + # @staticmethod + # def gens(slong n, ordering=None): + # # todo: (i, n)? or just (i)? + # return tuple(fmpz_mpoly.gen(i, n) for i in range(n)) def repr(self): - cdef char * s = fmpz_mpoly_get_str_pretty(self.val, NULL, self.ctx.val) + return self.str() + " (nvars=%s, ordering=%s names=%s)" % (self.ctx.nvars(), self.ctx.ordering(), self.ctx.py_names) + + def str(self): + cdef char * s = fmpz_mpoly_get_str_pretty(self.val, self.ctx.c_names, self.ctx.val) try: res = str_from_chars(s) finally: @@ -230,9 +301,8 @@ cdef class fmpz_mpoly(flint_mpoly): res = res.replace("-", " - ") if res.startswith(" - "): res = "-" + res[3:] - return res + " (nvars=%s, ordering=%s)" % (self.ctx.nvars(), self.ctx.ordering()) + return res - def str(self): return self.repr() def __neg__(self): @@ -246,150 +316,216 @@ cdef class fmpz_mpoly(flint_mpoly): def __add__(self, other): cdef fmpz_mpoly res - if typecheck(self, fmpz_mpoly): - if typecheck(other, fmpz_mpoly): - if (self).ctx is not (other).ctx: - ctx, (self, other) = coerce_fmpz_mpolys(self, other) - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True - fmpz_mpoly_add(res.val, (self).val, (other).val, res.ctx.val) - return res - else: - other = any_as_fmpz(other) - if other is not NotImplemented: - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True - fmpz_mpoly_add_fmpz(res.val, (self).val, (other).val, res.ctx.val) - return res + if typecheck(other, fmpz_mpoly): + if (self).ctx is not (other).ctx: + return NotImplemented + res = fmpz_mpoly.__new__(fmpz_mpoly) + res.ctx =(self).ctx + fmpz_mpoly_init(res.val, res.ctx.val) + res._init = True + fmpz_mpoly_add(res.val, (self).val, (other).val, res.ctx.val) + return res else: - self = any_as_fmpz(self) - if self is not NotImplemented: + other = any_as_fmpz(other) + if other is not NotImplemented: res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (other).ctx + res.ctx = (self).ctx fmpz_mpoly_init(res.val, res.ctx.val) res._init = True - fmpz_mpoly_add_fmpz(res.val, (other).val, (self).val, res.ctx.val) + fmpz_mpoly_add_fmpz(res.val, (self).val, (other).val, res.ctx.val) return res return NotImplemented + def __radd__(self, other): + cdef fmpz_mpoly res + other = any_as_fmpz(other) + if other is not NotImplemented: + res = fmpz_mpoly.__new__(fmpz_mpoly) + res.ctx = (self).ctx + fmpz_mpoly_init(res.val, res.ctx.val) + res._init = True + fmpz_mpoly_add_fmpz(res.val, (self).val, (other).val, res.ctx.val) + return res + return NotImplemented + def __sub__(self, other): cdef fmpz_mpoly res - if typecheck(self, fmpz_mpoly): - if typecheck(other, fmpz_mpoly): - if (self).ctx is not (other).ctx: - ctx, (self, other) = coerce_fmpz_mpolys(self, other) - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True - fmpz_mpoly_sub(res.val, (self).val, (other).val, res.ctx.val) - return res - else: - other = any_as_fmpz(other) - if other is not NotImplemented: - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True - fmpz_mpoly_sub_fmpz(res.val, (self).val, (other).val, res.ctx.val) - return res + if typecheck(other, fmpz_mpoly): + if (self).ctx is not (other).ctx: + return NotImplemented + res = fmpz_mpoly.__new__(fmpz_mpoly) + res.ctx =(self).ctx + fmpz_mpoly_init(res.val, res.ctx.val) + res._init = True + fmpz_mpoly_sub(res.val, (self).val, (other).val, res.ctx.val) + return res else: - self = any_as_fmpz(self) - if self is not NotImplemented: + other = any_as_fmpz(other) + if other is not NotImplemented: res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (other).ctx + res.ctx = (self).ctx fmpz_mpoly_init(res.val, res.ctx.val) res._init = True - fmpz_mpoly_sub_fmpz(res.val, (other).val, (self).val, res.ctx.val) - fmpz_mpoly_neg(res.val, res.val, res.ctx.val) + fmpz_mpoly_sub_fmpz(res.val, (self).val, (other).val, res.ctx.val) return res return NotImplemented + def __rsub__(self, other): + cdef fmpz_mpoly res + other = any_as_fmpz(other) + if other is not NotImplemented: + res = fmpz_mpoly.__new__(fmpz_mpoly) + res.ctx = (self).ctx + fmpz_mpoly_init(res.val, res.ctx.val) + res._init = True + fmpz_mpoly_sub_fmpz(res.val, (self).val, (other).val, res.ctx.val) + return -res + return NotImplemented + + # def __sub__(self, other): + # cdef fmpz_mpoly res + # if typecheck(self, fmpz_mpoly): + # if typecheck(other, fmpz_mpoly): + # if (self).ctx is not (other).ctx: + # ctx, (self, other) = coerce_fmpz_mpolys(self, other) + # res = fmpz_mpoly.__new__(fmpz_mpoly) + # res.ctx = (self).ctx + # fmpz_mpoly_init(res.val, res.ctx.val) + # res._init = True + # fmpz_mpoly_sub(res.val, (self).val, (other).val, res.ctx.val) + # return res + # else: + # other = any_as_fmpz(other) + # if other is not NotImplemented: + # res = fmpz_mpoly.__new__(fmpz_mpoly) + # res.ctx = (self).ctx + # fmpz_mpoly_init(res.val, res.ctx.val) + # res._init = True + # fmpz_mpoly_sub_fmpz(res.val, (self).val, (other).val, res.ctx.val) + # return res + # else: + # self = any_as_fmpz(self) + # if self is not NotImplemented: + # res = fmpz_mpoly.__new__(fmpz_mpoly) + # res.ctx = (other).ctx + # fmpz_mpoly_init(res.val, res.ctx.val) + # res._init = True + # fmpz_mpoly_sub_fmpz(res.val, (other).val, (self).val, res.ctx.val) + # fmpz_mpoly_neg(res.val, res.val, res.ctx.val) + # return res + # return NotImplemented + def __mul__(self, other): cdef fmpz_mpoly res - if typecheck(self, fmpz_mpoly): - if typecheck(other, fmpz_mpoly): - if (self).ctx is not (other).ctx: - ctx, (self, other) = coerce_fmpz_mpolys(self, other) - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True - fmpz_mpoly_mul(res.val, (self).val, (other).val, res.ctx.val) - return res - else: - other = any_as_fmpz(other) - if other is not NotImplemented: - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True - fmpz_mpoly_scalar_mul_fmpz(res.val, (self).val, (other).val, res.ctx.val) - return res + if typecheck(other, fmpz_mpoly): + if (self).ctx is not (other).ctx: + return NotImplemented + res = fmpz_mpoly.__new__(fmpz_mpoly) + res.ctx =(self).ctx + fmpz_mpoly_init(res.val, res.ctx.val) + res._init = True + fmpz_mpoly_mul(res.val, (self).val, (other).val, res.ctx.val) + return res else: - self = any_as_fmpz(self) - if self is not NotImplemented: + other = any_as_fmpz(other) + if other is not NotImplemented: res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (other).ctx + res.ctx = (self).ctx fmpz_mpoly_init(res.val, res.ctx.val) res._init = True - fmpz_mpoly_scalar_mul_fmpz(res.val, (other).val, (self).val, res.ctx.val) + fmpz_mpoly_scalar_mul_fmpz(res.val, (self).val, (other).val, res.ctx.val) return res return NotImplemented - def __pow__(self, other, modulus): + def __rmul__(self, other): cdef fmpz_mpoly res - if modulus is not None: - raise NotImplementedError - if typecheck(self, fmpz_mpoly): - other = any_as_fmpz(other) - if other is NotImplemented: - return other - if other < 0: - raise ValueError("cannot raise fmpz_mpoly to negative power") + other = any_as_fmpz(other) + if other is not NotImplemented: res = fmpz_mpoly.__new__(fmpz_mpoly) res.ctx = (self).ctx fmpz_mpoly_init(res.val, res.ctx.val) res._init = True - if fmpz_mpoly_pow_fmpz(res.val, (self).val, (other).val, res.ctx.val) == 0: - raise ValueError("unreasonably large polynomial") + fmpz_mpoly_scalar_mul_fmpz(res.val, (self).val, (other).val, res.ctx.val) return res return NotImplemented + # def __mul__(self, other): + # cdef fmpz_mpoly res + # if typecheck(self, fmpz_mpoly): + # if typecheck(other, fmpz_mpoly): + # if (self).ctx is not (other).ctx: + # ctx, (self, other) = coerce_fmpz_mpolys(self, other) + # res = fmpz_mpoly.__new__(fmpz_mpoly) + # res.ctx = (self).ctx + # fmpz_mpoly_init(res.val, res.ctx.val) + # res._init = True + # fmpz_mpoly_mul(res.val, (self).val, (other).val, res.ctx.val) + # return res + # else: + # other = any_as_fmpz(other) + # if other is not NotImplemented: + # res = fmpz_mpoly.__new__(fmpz_mpoly) + # res.ctx = (self).ctx + # fmpz_mpoly_init(res.val, res.ctx.val) + # res._init = True + # fmpz_mpoly_scalar_mul_fmpz(res.val, (self).val, (other).val, res.ctx.val) + # return res + # else: + # self = any_as_fmpz(self) + # if self is not NotImplemented: + # res = fmpz_mpoly.__new__(fmpz_mpoly) + # res.ctx = (other).ctx + # fmpz_mpoly_init(res.val, res.ctx.val) + # res._init = True + # fmpz_mpoly_scalar_mul_fmpz(res.val, (other).val, (self).val, res.ctx.val) + # return res + # return NotImplemented + + def __pow__(self, other, modulus): + cdef fmpz_mpoly res + if modulus is not None: + raise NotImplementedError + other = any_as_fmpz(other) + if other is NotImplemented: + return other + if other < 0: + raise ValueError("cannot raise fmpz_mpoly to negative power") + res = fmpz_mpoly.__new__(fmpz_mpoly) + res.ctx = (self).ctx + fmpz_mpoly_init(res.val, res.ctx.val) + res._init = True + if fmpz_mpoly_pow_fmpz(res.val, (self).val, (other).val, res.ctx.val) == 0: + raise ValueError("unreasonably large polynomial") + return res + def __divmod__(self, other): cdef fmpz_mpoly res, res2 - if typecheck(self, fmpz_mpoly): - if typecheck(other, fmpz_mpoly): - if (self).ctx is not (other).ctx: - ctx, (self, other) = coerce_fmpz_mpolys(self, other) - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True - res2 = fmpz_mpoly.__new__(fmpz_mpoly) - res2.ctx = (self).ctx - fmpz_mpoly_init(res2.val, res2.ctx.val) - res2._init = True - fmpz_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) - return (res, res2) + if typecheck(other, fmpz_mpoly): + if (self).ctx is not (other).ctx: + return NotImplemented + res = fmpz_mpoly.__new__(fmpz_mpoly) + res.ctx = (self).ctx + fmpz_mpoly_init(res.val, res.ctx.val) + res._init = True + res2 = fmpz_mpoly.__new__(fmpz_mpoly) + res2.ctx = (self).ctx + fmpz_mpoly_init(res2.val, res2.ctx.val) + res2._init = True + fmpz_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) + return (res, res2) return NotImplemented def __floordiv__(self, other): cdef fmpz_mpoly res - if typecheck(self, fmpz_mpoly): - if typecheck(other, fmpz_mpoly): - if (self).ctx is not (other).ctx: - ctx, (self, other) = coerce_fmpz_mpolys(self, other) - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True - fmpz_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) - return res + if typecheck(other, fmpz_mpoly): + if (self).ctx is not (other).ctx: + return NotImplemented + res = fmpz_mpoly.__new__(fmpz_mpoly) + res.ctx = (self).ctx + fmpz_mpoly_init(res.val, res.ctx.val) + res._init = True + fmpz_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) + return res return NotImplemented def __mod__(self, other): @@ -399,7 +535,7 @@ cdef class fmpz_mpoly(flint_mpoly): cdef fmpz_mpoly res assert isinstance(other, fmpz_mpoly) if (self).ctx is not (other).ctx: - ctx, (self, other) = coerce_fmpz_mpolys(self, other) + return NotImplemented res = fmpz_mpoly.__new__(fmpz_mpoly) res.ctx = (self).ctx fmpz_mpoly_init(res.val, res.ctx.val) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 69c62ecc..439914a2 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -626,6 +626,19 @@ def set_bad(i,j): assert M6.minpoly() == flint.fmpz_poly([4,-4,1]) assert list(M6) == [2,0,0,0,2,1,0,0,2] +def test_fmpz_mpoly(): + Zp = flint.fmpz_mpoly + getctx = flint.get_fmpz_mpoly_ctx + ctx = getctx(4) + assert ctx.nvars() == 4 + assert ctx.ordering() == "lex" + assert [ctx.name(i) for i in range(4)] == ['x0', 'x1', 'x2', 'x3'] + for order in ['lex', 'deglex', 'degrevlex']: + ctx1 = getctx(4, order) + assert ctx1.ordering() == order + ctx = getctx(4, "lex", 'w,x,y,z') + + def test_fmpz_series(): Zp = flint.fmpz_poly Z = flint.fmpz_series From 048915293fc58ab30f895d9a50469a9cb8d3b071 Mon Sep 17 00:00:00 2001 From: David Einstein Date: Thu, 17 Aug 2023 09:27:28 -0400 Subject: [PATCH 05/95] Made the tests pass. Made fmpz_mpoly_ctx return a string instead of bytes. --- src/flint/fmpz_mpoly.pyx | 94 +++------------------------------------- src/flint/test/test.py | 5 ++- 2 files changed, 11 insertions(+), 88 deletions(-) diff --git a/src/flint/fmpz_mpoly.pyx b/src/flint/fmpz_mpoly.pyx index 2a07705f..e793a670 100644 --- a/src/flint/fmpz_mpoly.pyx +++ b/src/flint/fmpz_mpoly.pyx @@ -71,9 +71,15 @@ cdef class fmpz_mpoly_ctx: raise ValueError("Unimplemented term order %s" % ordering) self.py_names = tuple(bytes(name, 'utf-8') for name in names) self.c_names = libc.stdlib.malloc(nvars * sizeof(char *)) + self._init = True for i in range(nvars): self.c_names[i] = self.py_names[i] + def __dealloc__(self): + if self._init: + libc.stdlib.free(self.c_names) + self._init = False + cpdef slong nvars(self): return self.val.minfo.nvars @@ -87,7 +93,7 @@ cdef class fmpz_mpoly_ctx: cpdef name(self, slong i): assert i >= 0 and i < self.val.minfo.nvars - return self.py_names[i] + return self.py_names[i].decode('utf-8') cpdef gen(self, slong i): cdef fmpz_mpoly res @@ -237,8 +243,6 @@ cdef class fmpz_mpoly(flint_mpoly): def __hash__(self): s = str(self) -# i = s.index("(nvars") -# s = s[:i] return hash(s) def coefficient(self, slong i): @@ -269,25 +273,6 @@ cdef class fmpz_mpoly(flint_mpoly): libc.stdlib.free(tmp) return res - # @staticmethod - # def gen(slong i, slong nvars=-1, ordering=None): - # cdef fmpz_mpoly res - # assert i >= 0 - # if nvars <= 0: - # nvars = i + 1 - # assert i < nvars - # res = fmpz_mpoly.__new__(fmpz_mpoly) - # res.ctx = get_fmpz_mpoly_context(nvars, ordering) - # fmpz_mpoly_init(res.val, res.ctx.val) - # res._init = True - # fmpz_mpoly_gen(res.val, i, res.ctx.val) - # return res - - # @staticmethod - # def gens(slong n, ordering=None): - # # todo: (i, n)? or just (i)? - # return tuple(fmpz_mpoly.gen(i, n) for i in range(n)) - def repr(self): return self.str() + " (nvars=%s, ordering=%s names=%s)" % (self.ctx.nvars(), self.ctx.ordering(), self.ctx.py_names) @@ -382,39 +367,6 @@ cdef class fmpz_mpoly(flint_mpoly): return -res return NotImplemented - # def __sub__(self, other): - # cdef fmpz_mpoly res - # if typecheck(self, fmpz_mpoly): - # if typecheck(other, fmpz_mpoly): - # if (self).ctx is not (other).ctx: - # ctx, (self, other) = coerce_fmpz_mpolys(self, other) - # res = fmpz_mpoly.__new__(fmpz_mpoly) - # res.ctx = (self).ctx - # fmpz_mpoly_init(res.val, res.ctx.val) - # res._init = True - # fmpz_mpoly_sub(res.val, (self).val, (other).val, res.ctx.val) - # return res - # else: - # other = any_as_fmpz(other) - # if other is not NotImplemented: - # res = fmpz_mpoly.__new__(fmpz_mpoly) - # res.ctx = (self).ctx - # fmpz_mpoly_init(res.val, res.ctx.val) - # res._init = True - # fmpz_mpoly_sub_fmpz(res.val, (self).val, (other).val, res.ctx.val) - # return res - # else: - # self = any_as_fmpz(self) - # if self is not NotImplemented: - # res = fmpz_mpoly.__new__(fmpz_mpoly) - # res.ctx = (other).ctx - # fmpz_mpoly_init(res.val, res.ctx.val) - # res._init = True - # fmpz_mpoly_sub_fmpz(res.val, (other).val, (self).val, res.ctx.val) - # fmpz_mpoly_neg(res.val, res.val, res.ctx.val) - # return res - # return NotImplemented - def __mul__(self, other): cdef fmpz_mpoly res if typecheck(other, fmpz_mpoly): @@ -449,38 +401,6 @@ cdef class fmpz_mpoly(flint_mpoly): return res return NotImplemented - # def __mul__(self, other): - # cdef fmpz_mpoly res - # if typecheck(self, fmpz_mpoly): - # if typecheck(other, fmpz_mpoly): - # if (self).ctx is not (other).ctx: - # ctx, (self, other) = coerce_fmpz_mpolys(self, other) - # res = fmpz_mpoly.__new__(fmpz_mpoly) - # res.ctx = (self).ctx - # fmpz_mpoly_init(res.val, res.ctx.val) - # res._init = True - # fmpz_mpoly_mul(res.val, (self).val, (other).val, res.ctx.val) - # return res - # else: - # other = any_as_fmpz(other) - # if other is not NotImplemented: - # res = fmpz_mpoly.__new__(fmpz_mpoly) - # res.ctx = (self).ctx - # fmpz_mpoly_init(res.val, res.ctx.val) - # res._init = True - # fmpz_mpoly_scalar_mul_fmpz(res.val, (self).val, (other).val, res.ctx.val) - # return res - # else: - # self = any_as_fmpz(self) - # if self is not NotImplemented: - # res = fmpz_mpoly.__new__(fmpz_mpoly) - # res.ctx = (other).ctx - # fmpz_mpoly_init(res.val, res.ctx.val) - # res._init = True - # fmpz_mpoly_scalar_mul_fmpz(res.val, (other).val, (self).val, res.ctx.val) - # return res - # return NotImplemented - def __pow__(self, other, modulus): cdef fmpz_mpoly res if modulus is not None: diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 439914a2..7d30a34e 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -628,7 +628,7 @@ def set_bad(i,j): def test_fmpz_mpoly(): Zp = flint.fmpz_mpoly - getctx = flint.get_fmpz_mpoly_ctx + getctx = flint.get_fmpz_mpoly_context ctx = getctx(4) assert ctx.nvars() == 4 assert ctx.ordering() == "lex" @@ -637,6 +637,8 @@ def test_fmpz_mpoly(): ctx1 = getctx(4, order) assert ctx1.ordering() == order ctx = getctx(4, "lex", 'w,x,y,z') + p1 = ctx.gen(0) + ctx.gen(1) - ctx.gen(2) * ctx.gen(3) + assert p1 == Zp("w + x - y * z", ctx) def test_fmpz_series(): @@ -1581,6 +1583,7 @@ def test_pickling(): test_fmpz_poly_factor, test_fmpz_poly_functions, test_fmpz_mat, + test_fmpz_mpoly, test_fmpz_series, test_fmpq, test_fmpq_poly, From 89d567ca8fd9a64e945f8c618523ce4bc1d2600a Mon Sep 17 00:00:00 2001 From: David Einstein Date: Thu, 17 Aug 2023 14:56:44 -0400 Subject: [PATCH 06/95] Back out changes for flint3 and setuptools I hope these work for CI. I currently don't have flint 3.0 or whatever the matching arb lib is. --- pyproject.toml | 2 -- setup.py | 19 ++++++++++---- src/flint/_flint.pxd | 56 +++++++++++++++++++--------------------- src/flint/fmpz_mpoly.pyx | 2 +- 4 files changed, 41 insertions(+), 38 deletions(-) delete mode 100644 pyproject.toml diff --git a/pyproject.toml b/pyproject.toml deleted file mode 100644 index fcc670ea..00000000 --- a/pyproject.toml +++ /dev/null @@ -1,2 +0,0 @@ -[build-system] -requires=["setuptools", "cython", "wheel"] diff --git a/setup.py b/setup.py index ecd4267a..85b92404 100644 --- a/setup.py +++ b/setup.py @@ -2,8 +2,9 @@ import os from subprocess import check_call -from setuptools import setup -from setuptools import Extension +from distutils.core import setup +from distutils.extension import Extension +from Cython.Distutils import build_ext from Cython.Build import cythonize from numpy.distutils.system_info import default_include_dirs, default_lib_dirs @@ -28,15 +29,22 @@ elif os.getenv('PYTHON_FLINT_MINGW64_TMP'): # This would be used to build under Windows against these libraries if # they have been installed somewhere other than .local - libraries = ["flint", "mpfr", "gmp"] + libraries = ["arb", "flint", "mpfr", "gmp"] else: # For the MSVC toolchain link with mpir instead of gmp - libraries = ["flint", "mpir", "mpfr", "pthreads"] + libraries = ["arb", "flint", "mpir", "mpfr", "pthreads"] else: - libraries = ["flint"] + # On Ubuntu libarb.so is called libflint-arb.so + if os.getenv('PYTHON_FLINT_LIBFLINT_ARB'): + arb = 'flint-arb' + else: + arb = 'arb' + + libraries = [arb, "flint"] (opt,) = get_config_vars('OPT') os.environ['OPT'] = " ".join(flag for flag in opt.split() if flag != '-Wstrict-prototypes') + default_include_dirs += [ os.path.join(d, "flint") for d in default_include_dirs ] @@ -70,6 +78,7 @@ setup( name='python-flint', + cmdclass={'build_ext': build_ext}, ext_modules=cythonize(ext_modules, compiler_directives=compiler_directives), #ext_modules=cythonize(ext_modules, compiler_directives=compiler_directives, annotate=True), packages=['flint', 'flint.test'], diff --git a/src/flint/_flint.pxd b/src/flint/_flint.pxd index dae41ce4..166e14a6 100644 --- a/src/flint/_flint.pxd +++ b/src/flint/_flint.pxd @@ -88,6 +88,13 @@ cdef extern from "flint/nmod_poly.h": nmod_t mod ctypedef nmod_poly_struct nmod_poly_t[1] + ctypedef struct nmod_poly_factor_struct: + nmod_poly_struct *p + long *exp + long num + long alloc + ctypedef nmod_poly_factor_struct nmod_poly_factor_t[1] + void nmod_poly_init(nmod_poly_t poly, mp_limb_t n) void nmod_poly_init_preinv(nmod_poly_t poly, mp_limb_t n, mp_limb_t ninv) void nmod_poly_init2(nmod_poly_t poly, mp_limb_t n, long alloc) @@ -149,15 +156,6 @@ cdef extern from "flint/nmod_poly.h": void nmod_poly_exp_series(nmod_poly_t f, nmod_poly_t h, long n) int nmod_poly_is_irreducible(nmod_poly_t f) - -cdef extern from "flint/nmod_poly_factor.h": - ctypedef struct nmod_poly_factor_struct: - nmod_poly_struct *p - long *exp - long num - long alloc - ctypedef nmod_poly_factor_struct nmod_poly_factor_t[1] - mp_limb_t nmod_poly_factor_with_berlekamp(nmod_poly_factor_t result, nmod_poly_t poly) mp_limb_t nmod_poly_factor_with_cantor_zassenhaus(nmod_poly_factor_t result, nmod_poly_t poly) mp_limb_t nmod_poly_factor(nmod_poly_factor_t result, nmod_poly_t input) @@ -755,7 +753,7 @@ cdef extern from "flint/arith.h": void arith_chebyshev_u_polynomial(fmpz_poly_t v, ulong n) void arith_cyclotomic_polynomial(fmpz_poly_t v, ulong n) -cdef extern from "flint/mag.h": +cdef extern from "mag.h": ctypedef struct mag_struct: fmpz_struct exp mp_limb_t man @@ -770,7 +768,7 @@ cdef extern from "flint/mag.h": void mag_set_ui_2exp_si(mag_t x, ulong v, long e) void mag_hypot(mag_t x, const mag_t y, const mag_t z) -cdef extern from "flint/arf.h": +cdef extern from "arf.h": ctypedef struct arf_struct: fmpz_struct exp long size @@ -904,7 +902,7 @@ cdef extern from "flint/arf.h": double arf_get_d(const arf_t x, arf_rnd_t rnd) void arf_set_d(arf_t x, double v) -cdef extern from "flint/arb.h": +cdef extern from "arb.h": ctypedef struct arb_struct: arf_struct mid mag_struct rad @@ -1195,7 +1193,7 @@ cdef extern from "flint/arb.h": cdef ulong ARB_STR_CONDENSE char * arb_get_str(const arb_t x, long n, ulong flags) -cdef extern from "flint/acb.h": +cdef extern from "acb.h": ctypedef struct acb_struct: arb_struct real arb_struct imag @@ -1386,15 +1384,15 @@ cdef extern from "flint/acb.h": void acb_root_ui(acb_t z, const acb_t x, ulong k, long prec) -cdef extern from "flint/partitions.h": +cdef extern from "partitions.h": void partitions_fmpz_fmpz(fmpz_t, const fmpz_t, int) -cdef extern from "flint/bernoulli.h": +cdef extern from "bernoulli.h": void bernoulli_fmpq_ui(fmpq_t, ulong) void bernoulli_cache_compute(long n) -cdef extern from "flint/arb_poly.h": +cdef extern from "arb_poly.h": ctypedef struct arb_poly_struct: arb_ptr coeffs long length @@ -1602,7 +1600,7 @@ cdef extern from "flint/arb_poly.h": void arb_poly_lambertw_series(arb_poly_t res, const arb_poly_t z, int flags, long len, long prec) -cdef extern from "flint/arb_mat.h": +cdef extern from "arb_mat.h": ctypedef struct arb_mat_struct: arb_ptr entries long r @@ -1683,7 +1681,7 @@ cdef extern from "flint/arb_mat.h": int arb_mat_approx_solve(arb_mat_t X, const arb_mat_t A, const arb_mat_t B, long prec) -cdef extern from "flint/acb_poly.h": +cdef extern from "acb_poly.h": ctypedef struct acb_poly_struct: acb_ptr coeffs long length @@ -1888,7 +1886,7 @@ cdef extern from "flint/acb_poly.h": void acb_poly_lambertw_series(acb_poly_t res, const acb_poly_t z, const fmpz_t k, int flags, long len, long prec) -cdef extern from "flint/acb_mat.h": +cdef extern from "acb_mat.h": ctypedef struct acb_mat_struct: acb_ptr entries long r @@ -1978,7 +1976,7 @@ cdef extern from "flint/acb_mat.h": int acb_mat_eig_multiple(acb_ptr E, const acb_mat_t A, acb_srcptr E_approx, const acb_mat_t R_approx, long prec) -cdef extern from "flint/acb_modular.h": +cdef extern from "acb_modular.h": void acb_modular_theta(acb_t theta1, acb_t theta2, acb_t theta3, acb_t theta4, const acb_t z, const acb_t tau, long prec) void acb_modular_theta_jet(acb_ptr theta1, acb_ptr theta2, acb_ptr theta3, acb_ptr theta4, const acb_t z, const acb_t tau, long len, long prec) void acb_modular_theta_series(acb_poly_t theta1, acb_poly_t theta2, acb_poly_t theta3, acb_poly_t theta4, const acb_poly_t z, const acb_t tau, long len, long prec) @@ -1994,7 +1992,7 @@ cdef extern from "flint/acb_modular.h": void acb_modular_elliptic_e(acb_t w, const acb_t m, long prec) void acb_modular_hilbert_class_poly(fmpz_poly_t res, long D) -cdef extern from "flint/acb_hypgeom.h": +cdef extern from "acb_hypgeom.h": void acb_hypgeom_bessel_j(acb_t res, const acb_t nu, const acb_t z, long prec) void acb_hypgeom_bessel_k(acb_t res, const acb_t nu, const acb_t z, long prec) void acb_hypgeom_bessel_i(acb_t res, const acb_t nu, const acb_t z, long prec) @@ -2072,7 +2070,7 @@ cdef extern from "flint/acb_hypgeom.h": void acb_hypgeom_chi_series(acb_poly_t res, const acb_poly_t h, long n, long prec) void acb_hypgeom_li_series(acb_poly_t res, const acb_poly_t h, int offset, long n, long prec) -cdef extern from "flint/arb_hypgeom.h": +cdef extern from "arb_hypgeom.h": void arb_hypgeom_pfq(arb_t res, arb_srcptr a, long p, arb_srcptr b, long q, const arb_t z, int regularized, long prec) void arb_hypgeom_0f1(arb_t res, const arb_t a, const arb_t z, int regularized, long prec) void arb_hypgeom_m(arb_t res, const arb_t a, const arb_t b, const arb_t z, int regularized, long prec) @@ -2137,7 +2135,7 @@ cdef extern from "flint/arb_hypgeom.h": void arb_hypgeom_legendre_p_ui_root(arb_t res, arb_t weight, ulong n, ulong k, long prec) -cdef extern from "flint/dirichlet.h": +cdef extern from "dirichlet.h": ctypedef struct dirichlet_group_struct: ulong q ulong q_even @@ -2196,7 +2194,7 @@ cdef extern from "flint/dirichlet.h": int dirichlet_char_is_primitive(const dirichlet_group_t G, const dirichlet_char_t chi) ulong dirichlet_chi(const dirichlet_group_t G, const dirichlet_char_t chi, ulong n) -cdef extern from "flint/acb_dirichlet.h": +cdef extern from "acb_dirichlet.h": void acb_dirichlet_eta(acb_t res, const acb_t s, long prec) void acb_dirichlet_chi(acb_t res, const dirichlet_group_t G, const dirichlet_char_t chi, ulong n, long prec) @@ -2213,7 +2211,7 @@ cdef extern from "flint/acb_dirichlet.h": void acb_dirichlet_zeta_zero(acb_t res, const fmpz_t n, long prec) void acb_dirichlet_zeta_zeros(acb_ptr res, const fmpz_t n, long len, long prec) -cdef extern from "flint/acb_elliptic.h": +cdef extern from "acb_elliptic.h": void acb_elliptic_rf(acb_t res, const acb_t x, const acb_t y, const acb_t z, int flags, long prec) void acb_elliptic_rj(acb_t res, const acb_t x, const acb_t y, const acb_t z, const acb_t p, int flags, long prec) void acb_elliptic_rg(acb_t res, const acb_t x, const acb_t y, const acb_t z, int flags, long prec) @@ -2228,7 +2226,7 @@ cdef extern from "flint/acb_elliptic.h": void acb_elliptic_invariants(acb_t g2, acb_t g3, const acb_t tau, long prec) void acb_elliptic_inv_p(acb_t res, const acb_t z, const acb_t tau, long prec) -cdef extern from "flint/acb_calc.h": +cdef extern from "acb_calc.h": ctypedef int (*acb_calc_func_t)(acb_ptr out, const acb_t inp, void * param, long order, long prec) ctypedef struct acb_calc_integrate_opt_struct: @@ -2248,14 +2246,14 @@ cdef extern from "flint/acb_calc.h": const acb_calc_integrate_opt_t options, long prec) -cdef extern from "flint/arb_fmpz_poly.h": +cdef extern from "arb_fmpz_poly.h": void arb_fmpz_poly_evaluate_arb(arb_t res, const fmpz_poly_t poly, const arb_t x, long prec) void arb_fmpz_poly_evaluate_acb(acb_t res, const fmpz_poly_t poly, const acb_t x, long prec) void arb_fmpz_poly_complex_roots(acb_ptr roots, const fmpz_poly_t poly, int flags, long prec) ulong arb_fmpz_poly_deflation(const fmpz_poly_t poly) void arb_fmpz_poly_deflate(fmpz_poly_t res, const fmpz_poly_t poly, ulong deflation) -cdef extern from "flint/acb_dft.h": +cdef extern from "acb_dft.h": void acb_dft(acb_ptr w, acb_srcptr v, long n, long prec) void acb_dft_inverse(acb_ptr w, acb_srcptr v, long n, long prec) @@ -2397,8 +2395,6 @@ cdef extern from "flint/fmpz_mpoly.h": void fmpz_mpoly_get_term_monomial(fmpz_mpoly_t M, const fmpz_mpoly_t A, slong i, const fmpz_mpoly_ctx_t ctx) - void fmpz_mpoly_sort_terms(fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) - # Addition/Subtraction void fmpz_mpoly_add_fmpz(fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_t c, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_add_si(fmpz_mpoly_t A, const fmpz_mpoly_t B, slong c, const fmpz_mpoly_ctx_t ctx) diff --git a/src/flint/fmpz_mpoly.pyx b/src/flint/fmpz_mpoly.pyx index e793a670..f2f1ee97 100644 --- a/src/flint/fmpz_mpoly.pyx +++ b/src/flint/fmpz_mpoly.pyx @@ -198,7 +198,7 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_init(self.val, self.ctx.val) self._init = True fmpz_mpoly_set_str_pretty(self.val, val, self.ctx.c_names, self.ctx.val) - fmpz_mpoly_sort_terms(self.val, self.ctx.val) +# fmpz_mpoly_sort_terms(self.val, self.ctx.val) else: v = any_as_fmpz(val) if v is NotImplemented: From 7176fe725d51da4c18d4eba63a476e61b3065d06 Mon Sep 17 00:00:00 2001 From: David Einstein Date: Fri, 18 Aug 2023 08:41:09 -0400 Subject: [PATCH 07/95] add prototype for fmpz_mpoly_sort_terms. --- src/flint/_flint.pxd | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/flint/_flint.pxd b/src/flint/_flint.pxd index 166e14a6..537c77e4 100644 --- a/src/flint/_flint.pxd +++ b/src/flint/_flint.pxd @@ -2395,6 +2395,8 @@ cdef extern from "flint/fmpz_mpoly.h": void fmpz_mpoly_get_term_monomial(fmpz_mpoly_t M, const fmpz_mpoly_t A, slong i, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_sort_terms(fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) + # Addition/Subtraction void fmpz_mpoly_add_fmpz(fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_t c, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_add_si(fmpz_mpoly_t A, const fmpz_mpoly_t B, slong c, const fmpz_mpoly_ctx_t ctx) From 3dbc6aed75bbe4cf48f7db93ab011ba7d3378a3b Mon Sep 17 00:00:00 2001 From: David Einstein Date: Fri, 18 Aug 2023 08:43:08 -0400 Subject: [PATCH 08/95] Moved generic pieces of fmpz_mpoly_context to base class. --- src/flint/fmpz_mpoly.pyx | 36 +++--------------------------------- src/flint/pyflint.pyx | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 33 deletions(-) diff --git a/src/flint/fmpz_mpoly.pyx b/src/flint/fmpz_mpoly.pyx index f2f1ee97..0755a756 100644 --- a/src/flint/fmpz_mpoly.pyx +++ b/src/flint/fmpz_mpoly.pyx @@ -40,7 +40,7 @@ cdef fmpz_poly_set_list(fmpz_poly_t poly, list val): cdef dict _fmpz_mpoly_ctx_cache = {} @cython.auto_pickle(False) -cdef class fmpz_mpoly_ctx: +cdef class fmpz_mpoly_ctx(flint_mpoly_context): """ A class for storing the polynomial context @@ -51,16 +51,8 @@ cdef class fmpz_mpoly_ctx: Do not construct one of these directly, use `get_fmpz_mpoly_context`. """ cdef fmpz_mpoly_ctx_t val - cdef public object py_names - cdef char ** c_names - cdef bint _init - - def __cinit__(self): - self._init = False def __init__(self, slong nvars, ordering, names): - assert nvars >= 1 - assert len(names) == nvars if ordering == "lex": fmpz_mpoly_ctx_init(self.val, nvars, ordering_t.ORD_LEX) elif ordering == "deglex": @@ -69,16 +61,8 @@ cdef class fmpz_mpoly_ctx: fmpz_mpoly_ctx_init(self.val, nvars, ordering_t.ORD_DEGREVLEX) else: raise ValueError("Unimplemented term order %s" % ordering) - self.py_names = tuple(bytes(name, 'utf-8') for name in names) - self.c_names = libc.stdlib.malloc(nvars * sizeof(char *)) - self._init = True - for i in range(nvars): - self.c_names[i] = self.py_names[i] - def __dealloc__(self): - if self._init: - libc.stdlib.free(self.c_names) - self._init = False + super().__init__(nvars, names) cpdef slong nvars(self): return self.val.minfo.nvars @@ -91,10 +75,6 @@ cdef class fmpz_mpoly_ctx: if self.val.minfo.ord == ordering_t.ORD_DEGREVLEX: return "degrevlex" - cpdef name(self, slong i): - assert i >= 0 and i < self.val.minfo.nvars - return self.py_names[i].decode('utf-8') - cpdef gen(self, slong i): cdef fmpz_mpoly res assert i >= 0 and i < self.val.minfo.nvars @@ -105,9 +85,6 @@ cdef class fmpz_mpoly_ctx: fmpz_mpoly_gen(res.val, i, res.ctx.val) return res - def gens(self): - return tuple(self.gen(i) for i in range(self.nvars())) - def get_fmpz_mpoly_context(slong nvars=1, ordering="lex", names='x'): if nvars <= 0: nvars = 1 @@ -198,7 +175,7 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_init(self.val, self.ctx.val) self._init = True fmpz_mpoly_set_str_pretty(self.val, val, self.ctx.c_names, self.ctx.val) -# fmpz_mpoly_sort_terms(self.val, self.ctx.val) + fmpz_mpoly_sort_terms(self.val, self.ctx.val) else: v = any_as_fmpz(val) if v is NotImplemented: @@ -241,10 +218,6 @@ cdef class fmpz_mpoly(flint_mpoly): def __len__(self): return fmpz_mpoly_length(self.val, self.ctx.val) - def __hash__(self): - s = str(self) - return hash(s) - def coefficient(self, slong i): cdef fmpz v if i < 0 or i >= fmpz_mpoly_length(self.val, self.ctx.val): @@ -254,9 +227,6 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_get_term_coeff_fmpz(v.val, self.val, i, self.ctx.val) return v - def leading_coefficient(self): - return self.coefficient(0) - def exponent_tuple(self, slong i): cdef slong j, nvars cdef fmpz_struct ** tmp diff --git a/src/flint/pyflint.pyx b/src/flint/pyflint.pyx index 63af457c..efc6261f 100644 --- a/src/flint/pyflint.pyx +++ b/src/flint/pyflint.pyx @@ -219,11 +219,51 @@ cdef class flint_poly(flint_elem): """ return acb_poly(self).roots(**kwargs) +cdef class flint_mpoly_context(flint_elem): + """ + Base class for sparse multivariate ring contexts. + """ + cdef public object py_names + cdef char ** c_names + cdef bint _init + + def __cinit__(self): + self._init = False + + def __init__(self, slong nvars, names): + assert nvars >= 1 + assert len(names) == nvars + self.py_names = tuple(bytes(name, 'utf-8') for name in names) + self.c_names = libc.stdlib.malloc(nvars * sizeof(char *)) + self._init = True + for i in range(nvars): + self.c_names[i] = self.py_names[i] + + def __dealloc__(self): + if self._init: + libc.stdlib.free(self.c_names) + self._init = False + + cpdef name(self, slong i): + assert i >= 0 and i < len(self.py_names) + return self.py_names[i].decode('utf-8') + + def gens(self): + return tuple(self.gen(i) for i in range(self.nvars())) + + cdef class flint_mpoly(flint_elem): """ Base class for multivariate polynomials. """ + def leading_coefficient(self): + return self.coefficient(0) + + def __hash__(self): + s = repr(self) + return hash(s) + cdef class flint_mat(flint_elem): """ From dc7be110baa6494339772d9e1ea59d17a34f6761 Mon Sep 17 00:00:00 2001 From: David Einstein Date: Sat, 19 Aug 2023 15:54:17 -0400 Subject: [PATCH 09/95] doctests for fmpz_mpoly_context object --- src/flint/fmpz_mpoly.pyx | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/src/flint/fmpz_mpoly.pyx b/src/flint/fmpz_mpoly.pyx index 0755a756..0e92b02d 100644 --- a/src/flint/fmpz_mpoly.pyx +++ b/src/flint/fmpz_mpoly.pyx @@ -65,9 +65,23 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): super().__init__(nvars, names) cpdef slong nvars(self): + """ + Return the number of variables in the context + + >>> ctx = get_fmpz_mpoly_context(4, "lex", 'x') + >>> ctx.nvars() + 4 + """ return self.val.minfo.nvars cpdef ordering(self): + """ + Return the term order of the context object. + + >>> ctx = get_fmpz_mpoly_context(4, "deglex", 'w') + >>> ctx.ordering() + 'deglex' + """ if self.val.minfo.ord == ordering_t.ORD_LEX: return "lex" if self.val.minfo.ord == ordering_t.ORD_DEGLEX: @@ -76,6 +90,13 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): return "degrevlex" cpdef gen(self, slong i): + """ + Return the `i`'th generator of the polynomial ring + + >>> ctx = get_fmpz_mpoly_context(3, 'degrevlex', 'z') + >>> ctx.gen(1) + z1 + """ cdef fmpz_mpoly res assert i >= 0 and i < self.val.minfo.nvars res = fmpz_mpoly.__new__(fmpz_mpoly) @@ -85,6 +106,16 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): fmpz_mpoly_gen(res.val, i, res.ctx.val) return res + # cpdef from_dict(self, d): + # cdef fmpz_mpoly res + # res = fmpz_mpoly.__new__(fmpz_mpoly) + # res.ctx = self + # fmpz_mpoly_init(res.val, res.ctx.val) + # res._init = True + # for k, v in d.items(): + + + def get_fmpz_mpoly_context(slong nvars=1, ordering="lex", names='x'): if nvars <= 0: nvars = 1 From 843b9f34f281ede855ad093ad806b2af6b798739 Mon Sep 17 00:00:00 2001 From: David Einstein Date: Wed, 23 Aug 2023 17:37:46 -0400 Subject: [PATCH 10/95] Added ability to construct an fmpz_mpoly from a dictionary. Also added tests, and a few __iop__ methods to fmpz_mpoly --- src/flint/_flint.pxd | 6 +- src/flint/fmpz_mpoly.pyx | 123 +++++++++++++++++++++++++++++++++++---- src/flint/test/test.py | 17 ++++++ 3 files changed, 133 insertions(+), 13 deletions(-) diff --git a/src/flint/_flint.pxd b/src/flint/_flint.pxd index 537c77e4..92cbaa3c 100644 --- a/src/flint/_flint.pxd +++ b/src/flint/_flint.pxd @@ -2276,7 +2276,7 @@ cdef extern from "flint/mpoly.h": void mpoly_ctx_init(mpoly_ctx_t ctx, slong nvars, const ordering_t ord) void mpoly_ctx_clear(mpoly_ctx_t mctx) - + flint_bitcnt_t mpoly_exp_bits_required_ffmpz(const fmpz_struct *user_exp, const mpoly_ctx_t mctx) cdef extern from "flint/fmpz_mpoly.h": ctypedef struct fmpz_mpoly_ctx_struct: @@ -2394,7 +2394,9 @@ cdef extern from "flint/fmpz_mpoly.h": void fmpz_mpoly_get_term_monomial(fmpz_mpoly_t M, const fmpz_mpoly_t A, slong i, const fmpz_mpoly_ctx_t ctx) - + void fmpz_mpoly_push_term_fmpz_fmpz(fmpz_mpoly_t A, const fmpz_t c, fmpz_struct *const *exp, const fmpz_mpoly_ctx_t ctx) + void _fmpz_mpoly_push_exp_ffmpz(fmpz_mpoly_t A, const fmpz_struct * exp, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_set_term_coeff_fmpz(fmpz_mpoly_t A, slong i, const fmpz_t c, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_sort_terms(fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) # Addition/Subtraction diff --git a/src/flint/fmpz_mpoly.pyx b/src/flint/fmpz_mpoly.pyx index 1a7f3828..89fb9514 100644 --- a/src/flint/fmpz_mpoly.pyx +++ b/src/flint/fmpz_mpoly.pyx @@ -1,4 +1,6 @@ from cpython.version cimport PY_MAJOR_VERSION +from cpython.dict cimport PyDict_Size, PyDict_Check, PyDict_Next +from cpython.tuple cimport PyTuple_Check, PyTuple_GET_SIZE from flint.utils.conversion cimport str_from_chars from flint.utils.typecheck cimport typecheck @@ -96,9 +98,9 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): if self.val.minfo.ord == ordering_t.ORD_DEGREVLEX: return "degrevlex" - cpdef gen(self, slong i): + def gen(self, slong i): """ - Return the `i`'th generator of the polynomial ring + Return the `i`th generator of the polynomial ring >>> ctx = get_fmpz_mpoly_context(3, 'degrevlex', 'z') >>> ctx.gen(1) @@ -113,14 +115,60 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): fmpz_mpoly_gen(res.val, i, res.ctx.val) return res - # cpdef from_dict(self, d): - # cdef fmpz_mpoly res - # res = fmpz_mpoly.__new__(fmpz_mpoly) - # res.ctx = self - # fmpz_mpoly_init(res.val, res.ctx.val) - # res._init = True - # for k, v in d.items(): - + def from_dict(self, d): + """ + Create a fmpz_mpoly from a dictionary. + """ + cdef long n + cdef fmpz_t coefficient + cdef fmpz_struct *exponents + cdef int xtype + cdef int nvars = self.nvars() + cdef int i,j + cdef int count + cdef fmpz_mpoly res + + if not PyDict_Check(d): + raise ValueError("expected a dictionary") + n = PyDict_Size(d) + fmpz_init(coefficient) + exponents = libc.stdlib.calloc(nvars, sizeof(fmpz_struct)) + if exponents == NULL: + raise MemoryError() + for i in range(nvars): + fmpz_init(exponents + i) + fmpz_init(coefficient) + res = fmpz_mpoly.__new__(fmpz_mpoly) + res.ctx = self + fmpz_mpoly_init(res.val, res.ctx.val) + res._init = True + count = 0 + for k,v in d.items(): + xtype = fmpz_set_any_ref(coefficient, v) + if xtype == FMPZ_UNKNOWN: + libc.stdlib.free(exponents) + raise TypeError("invalid coefficient type %s" % type(v)) + if not PyTuple_Check(k): + libc.stdlib.free(exponents) + raise TypeError("Expected tuple of ints as key not %s" % type(k)) + if PyTuple_GET_SIZE(k) != nvars: + libc.stdlib.free(exponents) + raise TypeError("Expected exponent tuple of length %d" % nvars) + for i,tup in enumerate(k): + xtype = fmpz_set_any_ref(exponents + i, tup) + if xtype == FMPZ_UNKNOWN: + libc.stdlib.free(exponents) + raise TypeError("Invalid exponent type %s" % type(tup)) + #Todo lobby for fmpz_mpoly_push_term_fmpz_ffmpz + if not fmpz_is_zero(coefficient): + _fmpz_mpoly_push_exp_ffmpz(res.val, exponents, self.val) + fmpz_mpoly_set_term_coeff_fmpz(res.val, count, coefficient, self.val) + count += 1 + for i in range(nvars): + fmpz_clear(exponents + i) + fmpz_clear(coefficient) + fmpz_mpoly_sort_terms(res.val, self.val) + return res def get_fmpz_mpoly_context(slong nvars=1, ordering="lex", names='x'): @@ -205,6 +253,20 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_set(self.val, (val).val, self.ctx.val) else: raise ValueError("Cannot automatically coerce contexts") + elif isinstance(val, dict): + if ctx is None: + if len(val) == 0: + raise ValueError("Need context for zero polynomial") + k = list(val.keys())[0] + if not isinstance(k, tuple): + raise ValueError("Dict should be keyed with tuples of integers") + ctx = get_fmpz_mpoly_context(len(k)) + x = ctx.from_dict(val) + #XXX this copy is silly, have a ctx function that assigns an fmpz_mpoly_t + self.ctx = ctx + fmpz_mpoly_init(self.val, self.ctx.val) + self._init = True + fmpz_mpoly_set(self.val, (x).val, self.ctx.val) elif isinstance(val, str): if ctx is None: raise ValueError("Cannot parse a polynomial without context") @@ -341,6 +403,19 @@ cdef class fmpz_mpoly(flint_mpoly): return res return NotImplemented + def __iadd__(self, other): + if typecheck(other, fmpz_mpoly): + if (self).ctx is not (other).ctx: + return NotImplemented + fmpz_mpoly_add((self).val, (self).val, (other).val, self.ctx.val) + return self + else: + other = any_as_fmpz(other) + if other is not NotImplemented: + fmpz_mpoly_add_fmpz((self).val, (self).val, (other).val, self.ctx.val) + return self + return NotImplemented + def __sub__(self, other): cdef fmpz_mpoly res if typecheck(other, fmpz_mpoly): @@ -375,6 +450,19 @@ cdef class fmpz_mpoly(flint_mpoly): return -res return NotImplemented + def __isub__(self, other): + if typecheck(other, fmpz_mpoly): + if (self).ctx is not (other).ctx: + return NotImplemented + fmpz_mpoly_sub((self).val, (self).val, (other).val, self.ctx.val) + return self + else: + other = any_as_fmpz(other) + if other is not NotImplemented: + fmpz_mpoly_sub_fmpz((self).val, (self).val, (other).val, self.ctx.val) + return self + return NotImplemented + def __mul__(self, other): cdef fmpz_mpoly res if typecheck(other, fmpz_mpoly): @@ -409,6 +497,19 @@ cdef class fmpz_mpoly(flint_mpoly): return res return NotImplemented + def __imul__(self, other): + if typecheck(other, fmpz_mpoly): + if (self).ctx is not (other).ctx: + return NotImplemented + fmpz_mpoly_mul((self).val, (self).val, (other).val, self.ctx.val) + return self + else: + other = any_as_fmpz(other) + if other is not NotImplemented: + fmpz_mpoly_scalar_mul_fmpz(self.val, (self).val, (other).val, self.ctx.val) + return self + return NotImplemented + def __pow__(self, other, modulus): cdef fmpz_mpoly res if modulus is not None: @@ -429,7 +530,7 @@ cdef class fmpz_mpoly(flint_mpoly): def __divmod__(self, other): cdef fmpz_mpoly res, res2 if typecheck(other, fmpz_mpoly): - if (self).ctx is not (other).ctx: + if (self).ctx is not (other).ctx return NotImplemented res = fmpz_mpoly.__new__(fmpz_mpoly) res.ctx = (self).ctx diff --git a/src/flint/test/test.py b/src/flint/test/test.py index f865163b..4f3f1da9 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -639,6 +639,23 @@ def test_fmpz_mpoly(): ctx = getctx(4, "lex", 'w,x,y,z') p1 = ctx.gen(0) + ctx.gen(1) - ctx.gen(2) * ctx.gen(3) assert p1 == Zp("w + x - y * z", ctx) + ctx = getctx(2, "lex", "x,y") + assert ctx.from_dict({(1,0):1, (0,1):2}) == Zp("x + 2*y", ctx) + assert raises(lambda: ctx.from_dict("b"), ValueError) + assert raises(lambda: ctx.from_dict({(1,2):"b"}), TypeError) + assert raises(lambda: ctx.from_dict({"b":1}), TypeError) + assert raises(lambda: ctx.from_dict({(1,2,3):1}), TypeError) + assert raises(lambda: ctx.from_dict({(1,"a"):1}), TypeError) + ctx = getctx(2, "lex", 'x,y') + p1 = ctx.from_dict({(1,0):4,(0,3):4,(2,4):9}) + for ztype in [int, long, flint.fmpz]: + assert p1 + ztype(3) == ctx.from_dict({(1,0):4,(0,3):4,(2,4):9,(0,0):3}) + assert ztype(3) + p1 == ctx.from_dict({(1,0):4,(0,3):4,(2,4):9,(0,0):3}) + assert p1 - ztype(3) == ctx.from_dict({(1,0):4,(0,3):4,(2,4):9,(0,0):-3}) + assert ztype(3) - p1 == ctx.from_dict({(1,0):-4,(0,3):-4,(2,4):-9,(0,0):3}) + assert p1 * ztype(3) == ctx.from_dict({(1,0):12,(0,3):12,(2,4):27}) + assert ztype(3) * p1 == ctx.from_dict({(1,0):12,(0,3):12,(2,4):27}) + assert p1 // ztype(3) == ctx.from_dict({(1,0):1,(0.3):1,(2,4):3}) def test_fmpz_series(): From f346904a40ac0f719de604ebaf1afea2a3bbdc50 Mon Sep 17 00:00:00 2001 From: David Einstein Date: Fri, 25 Aug 2023 11:12:37 -0400 Subject: [PATCH 11/95] Added create_fmpz_mpoly to remove blocks of redundant code. --- src/flint/fmpz_mpoly.pyx | 165 +++++++++++++++++++++------------------ src/flint/test/test.py | 30 +++---- 2 files changed, 106 insertions(+), 89 deletions(-) diff --git a/src/flint/fmpz_mpoly.pyx b/src/flint/fmpz_mpoly.pyx index 89fb9514..440bf194 100644 --- a/src/flint/fmpz_mpoly.pyx +++ b/src/flint/fmpz_mpoly.pyx @@ -115,9 +115,28 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): fmpz_mpoly_gen(res.val, i, res.ctx.val) return res - def from_dict(self, d): + def constant(self, z): + cdef fmpz_mpoly res + z = any_as_fmpz(z) + if z is NotImplemented: + raise ValueError() + res = fmpz_mpoly.__new__(fmpz_mpoly) + res.ctx = self + fmpz_mpoly_init(res.val, res.ctx.val) + res._init = True + fmpz_mpoly_set_fmpz(res.val, (z).val, res.ctx.val) + return res + + def fmpz_mpoly_from_dict(self, d): """ Create a fmpz_mpoly from a dictionary. + + The dictionary's keys are tuples of ints (or anything that implicitly converts + to fmpz) representing exponents, and corresponding values of fmpz. + + >>> ctx = get_fmpz_mpoly_context(2,'lex','x,y') + >>> ctx.fmpz_mpoly_from_dict({(1,0):2, (1,1):3, (0,1):1}) + 3*x*y + 2*x + y """ cdef long n cdef fmpz_t coefficient @@ -224,6 +243,19 @@ def coerce_fmpz_mpolys(*args): _fmpz_mpoly_set2(( args2[i]).val, ctx.val, ( args[i]).val, ( args[i]).ctx.val) return ctx, args2 +cdef inline init_fmpz_mpoly(fmpz_mpoly var, fmpz_mpoly_ctx ctx): + var.ctx = ctx + fmpz_mpoly_init(var.val, ctx.val) + var._init = True + +cdef inline create_fmpz_mpoly(fmpz_mpoly_ctx ctx): + cdef fmpz_mpoly var + var = fmpz_mpoly.__new__(fmpz_mpoly) + var.ctx = ctx + fmpz_mpoly_init(var.val, ctx.val) + var._init = True + return var + # todo: store cached context objects externally cdef class fmpz_mpoly(flint_mpoly): @@ -247,9 +279,7 @@ cdef class fmpz_mpoly(flint_mpoly): def __init__(self, val=0, ctx=None): if typecheck(val, fmpz_mpoly): if ctx is None or ctx == (val).ctx: - self.ctx = (val).ctx - fmpz_mpoly_init(self.val, self.ctx.val) - self._init = True + init_fmpz_mpoly(self, (val).ctx) fmpz_mpoly_set(self.val, (val).val, self.ctx.val) else: raise ValueError("Cannot automatically coerce contexts") @@ -261,19 +291,15 @@ cdef class fmpz_mpoly(flint_mpoly): if not isinstance(k, tuple): raise ValueError("Dict should be keyed with tuples of integers") ctx = get_fmpz_mpoly_context(len(k)) - x = ctx.from_dict(val) + x = ctx.fmpz_mpoly_from_dict(val) #XXX this copy is silly, have a ctx function that assigns an fmpz_mpoly_t - self.ctx = ctx - fmpz_mpoly_init(self.val, self.ctx.val) - self._init = True + init_fmpz_mpoly(self, ctx) fmpz_mpoly_set(self.val, (x).val, self.ctx.val) elif isinstance(val, str): if ctx is None: raise ValueError("Cannot parse a polynomial without context") val = bytes(val, 'utf-8') - self.ctx = ctx - fmpz_mpoly_init(self.val, self.ctx.val) - self._init = True + init_fmpz_mpoly(self, ctx) fmpz_mpoly_set_str_pretty(self.val, val, self.ctx.c_names, self.ctx.val) fmpz_mpoly_sort_terms(self.val, self.ctx.val) else: @@ -282,9 +308,7 @@ cdef class fmpz_mpoly(flint_mpoly): raise TypeError("cannot create fmpz_mpoly from type %s" % type(val)) if ctx is None: raise ValueError("Need context to convert fmpz to fmpz_mpoly") - self.ctx = ctx - fmpz_mpoly_init(self.val, self.ctx.val) - self._init = True + init_fmpz_mpoly(self, ctx) fmpz_mpoly_set_fmpz(self.val, (v).val, self.ctx.val) def __nonzero__(self): @@ -362,10 +386,7 @@ cdef class fmpz_mpoly(flint_mpoly): def __neg__(self): cdef fmpz_mpoly res - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True + res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_neg(res.val, (self).val, res.ctx.val) return res @@ -374,19 +395,13 @@ cdef class fmpz_mpoly(flint_mpoly): if typecheck(other, fmpz_mpoly): if (self).ctx is not (other).ctx: return NotImplemented - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx =(self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True + res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_add(res.val, (self).val, (other).val, res.ctx.val) return res else: other = any_as_fmpz(other) if other is not NotImplemented: - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True + res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_add_fmpz(res.val, (self).val, (other).val, res.ctx.val) return res return NotImplemented @@ -395,10 +410,7 @@ cdef class fmpz_mpoly(flint_mpoly): cdef fmpz_mpoly res other = any_as_fmpz(other) if other is not NotImplemented: - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True + res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_add_fmpz(res.val, (self).val, (other).val, res.ctx.val) return res return NotImplemented @@ -421,19 +433,13 @@ cdef class fmpz_mpoly(flint_mpoly): if typecheck(other, fmpz_mpoly): if (self).ctx is not (other).ctx: return NotImplemented - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx =(self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True + res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_sub(res.val, (self).val, (other).val, res.ctx.val) return res else: other = any_as_fmpz(other) if other is not NotImplemented: - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True + res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_sub_fmpz(res.val, (self).val, (other).val, res.ctx.val) return res return NotImplemented @@ -442,10 +448,7 @@ cdef class fmpz_mpoly(flint_mpoly): cdef fmpz_mpoly res other = any_as_fmpz(other) if other is not NotImplemented: - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True + res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_sub_fmpz(res.val, (self).val, (other).val, res.ctx.val) return -res return NotImplemented @@ -468,19 +471,13 @@ cdef class fmpz_mpoly(flint_mpoly): if typecheck(other, fmpz_mpoly): if (self).ctx is not (other).ctx: return NotImplemented - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx =(self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True + res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_mul(res.val, (self).val, (other).val, res.ctx.val) return res else: other = any_as_fmpz(other) if other is not NotImplemented: - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True + res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_scalar_mul_fmpz(res.val, (self).val, (other).val, res.ctx.val) return res return NotImplemented @@ -489,10 +486,7 @@ cdef class fmpz_mpoly(flint_mpoly): cdef fmpz_mpoly res other = any_as_fmpz(other) if other is not NotImplemented: - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True + res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_scalar_mul_fmpz(res.val, (self).val, (other).val, res.ctx.val) return res return NotImplemented @@ -519,10 +513,7 @@ cdef class fmpz_mpoly(flint_mpoly): return other if other < 0: raise ValueError("cannot raise fmpz_mpoly to negative power") - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True + res = create_fmpz_mpoly(self.ctx) if fmpz_mpoly_pow_fmpz(res.val, (self).val, (other).val, res.ctx.val) == 0: raise ValueError("unreasonably large polynomial") return res @@ -530,18 +521,31 @@ cdef class fmpz_mpoly(flint_mpoly): def __divmod__(self, other): cdef fmpz_mpoly res, res2 if typecheck(other, fmpz_mpoly): - if (self).ctx is not (other).ctx + if (self).ctx is not (other).ctx: return NotImplemented - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True - res2 = fmpz_mpoly.__new__(fmpz_mpoly) - res2.ctx = (self).ctx - fmpz_mpoly_init(res2.val, res2.ctx.val) - res2._init = True + res = create_fmpz_mpoly(self.ctx) + res2 = create_fmpz_mpoly(self.ctx) fmpz_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) return (res, res2) + else: + other = any_as_fmpz(other) + if other is not NotImplemented: + other= fmpz_mpoly(other, self.ctx) + res = create_fmpz_mpoly(self.ctx) + res2 = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) + return (res, res2) + return NotImplemented + + def __rdivmod__(self, other): + cdef fmpz_mpoly res, res2 + other = any_as_fmpz(other) + if other is not NotImplemented: + other = fmpz_mpoly(other, self.ctx) + res = create_fmpz_mpoly(self.ctx) + res2 = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_divrem(res.val, res2.val, (other).val, (self).val, res.ctx.val) + return res return NotImplemented def __floordiv__(self, other): @@ -549,12 +553,26 @@ cdef class fmpz_mpoly(flint_mpoly): if typecheck(other, fmpz_mpoly): if (self).ctx is not (other).ctx: return NotImplemented - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True + res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) return res + else: + other = any_as_fmpz(other) + if other is not NotImplemented: + other = fmpz_mpoly(other, self.ctx) + res = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) + return res + return NotImplemented + + def __rfloordiv__(self,other): + cdef fmpz_mpoly res + other = any_as_fmpz(other) + if other is not NotImplemented: + other = fmpz_mpoly(other, self.ctx) + res = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_div(res.val, (other).val, self.val, res.ctx.val) + return res return NotImplemented def __mod__(self, other): @@ -565,10 +583,7 @@ cdef class fmpz_mpoly(flint_mpoly): assert isinstance(other, fmpz_mpoly) if (self).ctx is not (other).ctx: return NotImplemented - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = (self).ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True + res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_gcd(res.val, (self).val, (other).val, res.ctx.val) return res diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 4f3f1da9..6ab31df6 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -640,22 +640,24 @@ def test_fmpz_mpoly(): p1 = ctx.gen(0) + ctx.gen(1) - ctx.gen(2) * ctx.gen(3) assert p1 == Zp("w + x - y * z", ctx) ctx = getctx(2, "lex", "x,y") - assert ctx.from_dict({(1,0):1, (0,1):2}) == Zp("x + 2*y", ctx) - assert raises(lambda: ctx.from_dict("b"), ValueError) - assert raises(lambda: ctx.from_dict({(1,2):"b"}), TypeError) - assert raises(lambda: ctx.from_dict({"b":1}), TypeError) - assert raises(lambda: ctx.from_dict({(1,2,3):1}), TypeError) - assert raises(lambda: ctx.from_dict({(1,"a"):1}), TypeError) + assert ctx.fmpz_mpoly_from_dict({(1,0):1, (0,1):2}) == Zp("x + 2*y", ctx) + assert raises(lambda: ctx.fmpz_mpoly_from_dict("b"), ValueError) + assert raises(lambda: ctx.fmpz_mpoly_from_dict({(1,2):"b"}), TypeError) + assert raises(lambda: ctx.fmpz_mpoly_from_dict({"b":1}), TypeError) + assert raises(lambda: ctx.fmpz_mpoly_from_dict({(1,2,3):1}), TypeError) + assert raises(lambda: ctx.fmpz_mpoly_from_dict({(1,"a"):1}), TypeError) ctx = getctx(2, "lex", 'x,y') - p1 = ctx.from_dict({(1,0):4,(0,3):4,(2,4):9}) + p1 = ctx.fmpz_mpoly_from_dict({(1,0):4,(0,3):4,(2,4):9}) for ztype in [int, long, flint.fmpz]: - assert p1 + ztype(3) == ctx.from_dict({(1,0):4,(0,3):4,(2,4):9,(0,0):3}) - assert ztype(3) + p1 == ctx.from_dict({(1,0):4,(0,3):4,(2,4):9,(0,0):3}) - assert p1 - ztype(3) == ctx.from_dict({(1,0):4,(0,3):4,(2,4):9,(0,0):-3}) - assert ztype(3) - p1 == ctx.from_dict({(1,0):-4,(0,3):-4,(2,4):-9,(0,0):3}) - assert p1 * ztype(3) == ctx.from_dict({(1,0):12,(0,3):12,(2,4):27}) - assert ztype(3) * p1 == ctx.from_dict({(1,0):12,(0,3):12,(2,4):27}) - assert p1 // ztype(3) == ctx.from_dict({(1,0):1,(0.3):1,(2,4):3}) + assert p1 + ztype(3) == ctx.fmpz_mpoly_from_dict({(1,0):4,(0,3):4,(2,4):9,(0,0):3}) + assert ztype(3) + p1 == ctx.fmpz_mpoly_from_dict({(1,0):4,(0,3):4,(2,4):9,(0,0):3}) + assert p1 - ztype(3) == ctx.fmpz_mpoly_from_dict({(1,0):4,(0,3):4,(2,4):9,(0,0):-3}) + assert ztype(3) - p1 == ctx.fmpz_mpoly_from_dict({(1,0):-4,(0,3):-4,(2,4):-9,(0,0):3}) + assert p1 * ztype(3) == ctx.fmpz_mpoly_from_dict({(1,0):12,(0,3):12,(2,4):27}) + assert ztype(3) * p1 == ctx.fmpz_mpoly_from_dict({(1,0):12,(0,3):12,(2,4):27}) + assert p1 // ztype(3) == ctx.fmpz_mpoly_from_dict({(1,0):1,(0,3):1,(2,4):3}) + assert ztype(3) // p1 == Zp(0,ctx) + assert ctx.constant(7) + ztype(3) == Zp(10, ctx) def test_fmpz_series(): From 3407bce747ad4e9b4ed294618009abee0c4304f6 Mon Sep 17 00:00:00 2001 From: David Einstein Date: Mon, 28 Aug 2023 17:36:16 -0400 Subject: [PATCH 12/95] Initial steps towards fmpz_mpoly. Yes, this does not compile. I need to go off on another branch to experiment with solutions. --- src/flint/_flint.pxd | 157 +++++++++ src/flint/flint_base/flint_base.pyx | 6 + src/flint/fmpq_mpoly.pyx | 511 ++++++++++++++++++++++++++++ src/flint/fmpz_mpoly.pyx | 167 ++++----- src/flint/pyflint.pxd | 6 + src/flint/pyflint.pyx | 1 + src/flint/test/test.py | 34 ++ 7 files changed, 800 insertions(+), 82 deletions(-) create mode 100644 src/flint/fmpq_mpoly.pyx diff --git a/src/flint/_flint.pxd b/src/flint/_flint.pxd index 92cbaa3c..9384813c 100644 --- a/src/flint/_flint.pxd +++ b/src/flint/_flint.pxd @@ -2447,6 +2447,163 @@ cdef extern from "flint/fmpz_mpoly.h": void fmpz_mpoly_term_content(fmpz_mpoly_t M, const fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) int fmpz_mpoly_gcd(fmpz_mpoly_t G, const fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_mpoly_ctx_t ctx) +cdef extern from "flint/fmpq_mpoly.h": + ctypedef struct fmpq_mpoly_ctx_struct: + fmpz_mpoly_ctx_t zctx; + + ctypedef fmpq_mpoly_ctx_struct fmpq_mpoly_ctx_t[1] + + ctypedef struct fmpq_mpoly_struct: + fmpq_t content + fmpz_mpoly_t zpoly + + ctypedef fmpq_mpoly_struct fmpq_mpoly_t[1] + + void fmpq_mpoly_ctx_init(fmpq_mpoly_ctx_t ctx, slong nvars, const ordering_t ord) + slong fmpq_mpoly_ctx_nvars(const fmpq_mpoly_ctx_t ctx) + ordering_t fmpq_mpoly_ctx_ord(const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_ctx_clear(fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_init(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_init2(fmpq_mpoly_t A, slong alloc, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_init3(fmpq_mpoly_t A, slong alloc, flint_bitcnt_t bits, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_fit_length(fmpq_mpoly_t A, slong len, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_fit_bits(fmpq_mpoly_t A, flint_bitcnt_t bits, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_realloc(fmpq_mpoly_t A, slong alloc, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_clear(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + char * fmpq_mpoly_get_str_pretty(const fmpq_mpoly_t A, const char ** x, const fmpq_mpoly_ctx_t ctx) + # int fmpq_mpoly_fprint_pretty(FILE * file, const fmpq_mpoly_t A, const char ** x, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_print_pretty(const fmpq_mpoly_t A, const char ** x, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_set_str_pretty(fmpq_mpoly_t A, const char * str, const char ** x, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_gen(fmpq_mpoly_t A, slong var, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_is_gen(const fmpq_mpoly_t A, slong var, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_set(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_equal(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_swap(fmpq_mpoly_t A, fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_is_fmpq(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_get_fmpq(fmpq_t c, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_set_fmpq(fmpq_mpoly_t A, const fmpq_t c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_set_fmpz(fmpq_mpoly_t A, const fmpz_t c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_set_ui(fmpq_mpoly_t A, ulong c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_set_si(fmpq_mpoly_t A, slong c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_zero(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_one(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_equal_fmpq(const fmpq_mpoly_t A, fmpq_t c, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_equal_fmpz(const fmpq_mpoly_t A, fmpz_t c, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_equal_ui(const fmpq_mpoly_t A, ulong c, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_equal_si(const fmpq_mpoly_t A, slong c, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_is_zero(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_is_one(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_degrees_fit_si(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_degrees_fmpz(fmpz_t ** degs, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_degrees_si(slong * degs, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_degree_fmpz(fmpz_t deg, const fmpq_mpoly_t A, slong var, const fmpq_mpoly_ctx_t ctx) + slong fmpq_mpoly_degree_si(const fmpq_mpoly_t A, slong var, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_total_degree_fits_si(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_total_degree_fmpz(fmpz_t tdeg, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + slong fmpq_mpoly_total_degree_si(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_used_vars(int * used, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_get_denominator(fmpz_t d, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_get_coeff_fmpq_monomial(fmpq_t c, const fmpq_mpoly_t A, const fmpq_mpoly_t M, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_set_coeff_fmpq_monomial(fmpq_mpoly_t A, const fmpq_t c, const fmpq_mpoly_t M, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_get_coeff_fmpq_fmpz(fmpq_t c, const fmpq_mpoly_t A, fmpz_t * const * exp, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_get_coeff_fmpq_ui(fmpq_t c, const fmpq_mpoly_t A, ulong const * exp, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_set_coeff_fmpq_fmpz(fmpq_mpoly_t A, const fmpq_t c, fmpz_t * const * exp, fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_set_coeff_fmpq_ui(fmpq_mpoly_t A, const fmpq_t c, ulong const * exp, fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_get_coeff_vars_ui(fmpq_mpoly_t C, const fmpq_mpoly_t A, const slong * vars, const ulong * exps, slong length, const fmpq_mpoly_ctx_t ctx) + # int fmpq_mpoly_cmp(const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + # fmpq * fmpq_mpoly_content_ref(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + # fmpz_mpoly_struct * fmpq_mpoly_zpoly_ref(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + # fmpz_t * fmpq_mpoly_zpoly_term_coeff_ref(fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) + # int fmpq_mpoly_is_canonical(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + slong fmpq_mpoly_length(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_resize(fmpq_mpoly_t A, slong new_length, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_get_term_coeff_fmpq(fmpq_t c, const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_set_term_coeff_fmpq(fmpq_mpoly_t A, slong i, const fmpq_t c, const fmpq_mpoly_ctx_t ctx) + # int fmpq_mpoly_term_exp_fits_si(const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) + # int fmpq_mpoly_term_exp_fits_ui(const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_get_term_exp_fmpz(fmpz_struct ** exps, const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_get_term_exp_ui(ulong * exps, const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_get_term_exp_si(slong * exps, const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) + # ulong fmpq_mpoly_get_term_var_exp_ui(const fmpq_mpoly_t A, slong i, slong var, const fmpq_mpoly_ctx_t ctx) + # slong fmpq_mpoly_get_term_var_exp_si(const fmpq_mpoly_t A, slong i, slong var, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_set_term_exp_fmpz(fmpq_mpoly_t A, slong i, fmpz_t * const * exps, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_set_term_exp_ui(fmpq_mpoly_t A, slong i, const ulong * exps, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_get_term(fmpq_mpoly_t M, const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_get_term_monomial(fmpq_mpoly_t M, const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_push_term_fmpq_fmpz(fmpq_mpoly_t A, const fmpq_t c, fmpz_t * const * exp, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_push_term_fmpz_fmpz(fmpq_mpoly_t A, const fmpz_t c, fmpz_t * const * exp, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_push_term_ui_fmpz(fmpq_mpoly_t A, ulong c, fmpz_t * const * exp, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_push_term_si_fmpz(fmpq_mpoly_t A, slong c, fmpz_t * const * exp, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_push_term_fmpq_ui(fmpq_mpoly_t A, const fmpq_t c, const ulong * exp, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_push_term_fmpz_ui(fmpq_mpoly_t A, const fmpz_t c, const ulong * exp, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_push_term_ui_ui(fmpq_mpoly_t A, ulong c, const ulong * exp, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_push_term_si_ui(fmpq_mpoly_t A, slong c, const ulong * exp, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_reduce(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_sort_terms(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_combine_like_terms(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_reverse(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_randtest_bound(fmpq_mpoly_t A, flint_rand_t state, slong length, mp_limb_t coeff_bits, ulong exp_bound, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_randtest_bounds(fmpq_mpoly_t A, flint_rand_t state, slong length, mp_limb_t coeff_bits, ulong * exp_bounds, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_randtest_bits(fmpq_mpoly_t A, flint_rand_t state, slong length, mp_limb_t coeff_bits, mp_limb_t exp_bits, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_add_fmpq(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_t c, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_add_fmpz(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpz_t c, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_add_ui(fmpq_mpoly_t A, const fmpq_mpoly_t B, ulong c, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_add_si(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_sub_fmpq(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_t c, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_sub_fmpz(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpz_t c, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_sub_ui(fmpq_mpoly_t A, const fmpq_mpoly_t B, ulong c, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_sub_si(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_add(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_t C, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_sub(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_t C, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_neg(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_scalar_mul_fmpq(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_t c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_scalar_mul_fmpz(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpz_t c, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_scalar_mul_ui(fmpq_mpoly_t A, const fmpq_mpoly_t B, ulong c, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_scalar_mul_si(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong c, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_scalar_div_fmpq(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_t c, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_scalar_div_fmpz(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpz_t c, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_scalar_div_ui(fmpq_mpoly_t A, const fmpq_mpoly_t B, ulong c, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_scalar_div_si(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong c, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_make_monic(fmpq_mpoly_t A, fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_derivative(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong var, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_integral(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong var, const fmpq_mpoly_ctx_t ctx) + # int fmpq_mpoly_evaluate_all_fmpq(fmpq_t ev, const fmpq_mpoly_t A, fmpq * const * vals, const fmpq_mpoly_ctx_t ctx) + # int fmpq_mpoly_evaluate_one_fmpq(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong var, const fmpq_t val, const fmpq_mpoly_ctx_t ctx) + # int fmpq_mpoly_compose_fmpq_poly(fmpq_poly_t A, const fmpq_mpoly_t B, fmpq_poly_struct * const * C, const fmpq_mpoly_ctx_t ctxB) + # int fmpq_mpoly_compose_fmpq_mpoly(fmpq_mpoly_t A, const fmpq_mpoly_t B, fmpq_mpoly_struct * const * C, const fmpq_mpoly_ctx_t ctxB, const fmpq_mpoly_ctx_t ctxAC) + # void fmpq_mpoly_compose_fmpq_mpoly_gen(fmpq_mpoly_t A, const fmpq_mpoly_t B, const slong * c, const fmpq_mpoly_ctx_t ctxB, const fmpq_mpoly_ctx_t ctxAC) + void fmpq_mpoly_mul(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_t C, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_pow_fmpz(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpz_t k, const fmpq_mpoly_ctx_t ctx) + # int fmpq_mpoly_pow_ui(fmpq_mpoly_t A, const fmpq_mpoly_t B, ulong k, const fmpq_mpoly_ctx_t ctx) + # int fmpq_mpoly_divides(fmpq_mpoly_t Q, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_div(fmpq_mpoly_t Q, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_divrem(fmpq_mpoly_t Q, fmpq_mpoly_t R, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_divrem_ideal(fmpq_mpoly_struct ** Q, fmpq_mpoly_t R, const fmpq_mpoly_t A, fmpq_mpoly_struct * const * B, slong len, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_content(fmpq_t g, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_term_content(fmpq_mpoly_t M, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + # int fmpq_mpoly_content_vars(fmpq_mpoly_t g, const fmpq_mpoly_t A, slong * vars, slong vars_length, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_gcd(fmpq_mpoly_t G, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + # int fmpq_mpoly_gcd_cofactors(fmpq_mpoly_t G, fmpq_mpoly_t Abar, fmpq_mpoly_t Bbar, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + # int fmpq_mpoly_gcd_brown(fmpq_mpoly_t G, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + # int fmpq_mpoly_gcd_hensel(fmpq_mpoly_t G, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + # int fmpq_mpoly_gcd_subresultant(fmpq_mpoly_t G, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + # int fmpq_mpoly_gcd_zippel(fmpq_mpoly_t G, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + # int fmpq_mpoly_gcd_zippel2(fmpq_mpoly_t G, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + # int fmpq_mpoly_resultant(fmpq_mpoly_t R, const fmpq_mpoly_t A, const fmpq_mpoly_t B, slong var, const fmpq_mpoly_ctx_t ctx) + # int fmpq_mpoly_discriminant(fmpq_mpoly_t D, const fmpq_mpoly_t A, slong var, const fmpq_mpoly_ctx_t ctx) + # int fmpq_mpoly_sqrt(fmpq_mpoly_t Q, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + # int fmpq_mpoly_is_square(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_univar_init(fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_univar_clear(fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_univar_swap(fmpq_mpoly_univar_t A, fmpq_mpoly_univar_t B, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_to_univar(fmpq_mpoly_univar_t A, const fmpq_mpoly_t B, slong var, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_from_univar(fmpq_mpoly_t A, const fmpq_mpoly_univar_t B, slong var, const fmpq_mpoly_ctx_t ctx) + # int fmpq_mpoly_univar_degree_fits_si(const fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) + # slong fmpq_mpoly_univar_length(const fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) + # slong fmpq_mpoly_univar_get_term_exp_si(fmpq_mpoly_univar_t A, slong i, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_univar_get_term_coeff(fmpq_mpoly_t c, const fmpq_mpoly_univar_t A, slong i, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_univar_swap_term_coeff(fmpq_mpoly_t c, fmpq_mpoly_univar_t A, slong i, const fmpq_mpoly_ctx_t ctx) + """ cdef extern from "flint/fmpz_mpoly_factor.h": diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index aff5aed8..c8d773df 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -118,6 +118,12 @@ cdef class flint_mpoly(flint_elem): s = repr(self) return hash(s) + def to_dict(self): + d = {} + for i in range(len(self)): + d[self.exponent_tuple(i)] = self.coefficient(i) + return d + cdef class flint_series(flint_elem): """ diff --git a/src/flint/fmpq_mpoly.pyx b/src/flint/fmpq_mpoly.pyx new file mode 100644 index 00000000..cd31e179 --- /dev/null +++ b/src/flint/fmpq_mpoly.pyx @@ -0,0 +1,511 @@ +cdef dict _fmpq_mpoly_ctx_cache = {} + +@cython.auto_pickle(False) +cdef class fmpq_mpoly_ctx(flint_mpoly_context): + """ + A class for storing the polynomial context + + :param nvars: The number of variables in the ring + :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 `get_fmpq_mpoly_context`. + """ + cdef fmpq_mpoly_ctx_t val + + def __init__(self, slong nvars, ordering, names): + if ordering == "lex": + fmpq_mpoly_ctx_init(self.val, nvars, ordering_t.ORD_LEX) + elif ordering == "deglex": + fmpq_mpoly_ctx_init(self.val, nvars, ordering_t.ORD_DEGLEX) + elif ordering == "degrevlex": + fmpq_mpoly_ctx_init(self.val, nvars, ordering_t.ORD_DEGREVLEX) + else: + raise ValueError("Unimplemented term order %s" % ordering) + + super().__init__(nvars, names) + + cpdef slong nvars(self): + """ + Return the number of variables in the context + + >>> ctx = get_fmpq_mpoly_context(4, "lex", 'x') + >>> ctx.nvars() + 4 + """ + return self.val.zctx.minfo.nvars + + cpdef ordering(self): + """ + Return the term order of the context object. + + >>> ctx = get_fmpq_mpoly_context(4, "deglex", 'w') + >>> ctx.ordering() + 'deglex' + """ + if self.val.zctx.minfo.ord == ordering_t.ORD_LEX: + return "lex" + if self.val.zctx.minfo.ord == ordering_t.ORD_DEGLEX: + return "deglex" + if self.val.zctx.minfo.ord == ordering_t.ORD_DEGREVLEX: + return "degrevlex" + + def gen(self, slong i): + """ + Return the `i`th generator of the polynomial ring + + >>> ctx = get_fmpq_mpoly_context(3, 'degrevlex', 'z') + >>> ctx.gen(1) + z1 + """ + cdef fmpq_mpoly res + assert i >= 0 and i < self.val.zctx.minfo.nvars + res = fmpq_mpoly.__new__(fmpq_mpoly) + res.ctx = self + fmpq_mpoly_init(res.val, res.ctx.val) + res._init = True + fmpq_mpoly_gen(res.val, i, res.ctx.val) + return res + + def constant(self, z): + """ + Create a constant polynomial in this context + """ + cdef fmpq_mpoly res + z = any_as_fmpz(z) + if z is NotImplemented: + raise ValueError("A constant fmpq_mpoly is a fmpq") + res = fmpq_mpoly.__new__(fmpq_mpoly) + res.ctx = self + fmpq_mpoly_init(res.val, res.ctx.val) + res._init = True + fmpq_mpoly_set_fmpq(res.val, (z).val, res.ctx.val) + return res + + def fmpq_mpoly_from_dict(self, d): + """ + Create a fmpq_mpoly from a dictionary. + + The dictionary's keys are tuples of ints (or anything that implicitly converts + to fmpz) representing exponents, and corresponding values of fmpq. + + >>> ctx = get_fmpq_mpoly_context(2,'lex','x,y') + >>> ctx.fmpq_mpoly_from_dict({(1,0):2, (1,1):3, (0,1):1}) + 3*x*y + 2*x + y + """ + cdef long n + cdef fmpq_t coefficient + cdef fmpz_struct *exponents + cdef int xtype + cdef int nvars = self.nvars() + cdef int i,j + cdef int count + cdef fmpq_mpoly res + + if not PyDict_Check(d): + raise ValueError("expected a dictionary") + n = PyDict_Size(d) + fmpq_init(coefficient) + exponents = libc.stdlib.calloc(nvars, sizeof(fmpz_struct)) + if exponents == NULL: + raise MemoryError() + for i in range(nvars): + fmpz_init(exponents + i) + fmpq_init(coefficient) + res = fmpq_mpoly.__new__(fmpq_mpoly) + res.ctx = self + fmpq_mpoly_init(res.val, res.ctx.val) + res._init = True + count = 0 + for k,v in d.items(): + coefficient = any_as_fmpq(v) + # xtype = fmpq_set_any_ref(coefficient, v) + # if xtype == FMPZ_UNKNOWN: + # libc.stdlib.free(exponents) + # raise TypeError("invalid coefficient type %s" % type(v)) + if coefficient is NotImplemented: + libc.stdlib.free(exponents) + raise TypeError("invalid coefficient type %s" % type(v)) + if not PyTuple_Check(k): + libc.stdlib.free(exponents) + raise TypeError("Expected tuple of ints as key not %s" % type(k)) + if PyTuple_GET_SIZE(k) != nvars: + libc.stdlib.free(exponents) + raise TypeError("Expected exponent tuple of length %d" % nvars) + for i,tup in enumerate(k): + xtype = fmpz_set_any_ref(exponents + i, tup) + if xtype == FMPZ_UNKNOWN: + libc.stdlib.free(exponents) + raise TypeError("Invalid exponent type %s" % type(tup)) + #Todo lobby for fmpz_mpoly_push_term_fmpz_ffmpz + if not fmpq_is_zero(coefficient): + # fmpq_mpoly_push_term_fmpq_fmpz(res.val, coefficient, exponents, self.val) + _fmpz_mpoly_push_exp_ffmpz(res.val.zpoly, exponents, self.val.zctx) + fmpq_mpoly_set_term_coeff_fmpq(res.val, count, coefficient, self.val) + count += 1 + for i in range(nvars): + fmpz_clear(exponents + i) + fmpq_clear(coefficient) + fmpq_mpoly_sort_terms(res.val, self.val) + return res + + +def get_fmpq_mpoly_context(slong nvars=1, ordering="lex", names='x'): + if nvars <= 0: + nvars = 1 + nametup = tuple(name.strip() for name in names.split(',')) + if len(nametup) != nvars: + if len(nametup) != 1: + raise ValueError("Number of variables does not equal number of names") + nametup = tuple(nametup[0] + str(i) for i in range(nvars)) + key = (nvars, ordering, nametup) + ctx = _fmpq_mpoly_ctx_cache.get(key) + if ctx is None: + ctx = fmpq_mpoly_ctx(nvars, ordering, nametup) + _fmpq_mpoly_ctx_cache[key] = ctx + return ctx + +cdef inline init_fmpq_mpoly(fmpq_mpoly var, fmpq_mpoly_ctx ctx): + var.ctx = ctx + fmpq_mpoly_init(var.val, ctx.val) + var._init = True + +cdef inline create_fmpq_mpoly(fmpq_mpoly_ctx ctx): + cdef fmpq_mpoly var + var = fmpq_mpoly.__new__(fmpq_mpoly) + var.ctx = ctx + fmpq_mpoly_init(var.val, ctx.val) + var._init = True + return var + + + +# todo: store cached context objects externally +cdef class fmpq_mpoly(flint_mpoly): + """ + The *fmpq_poly* type represents sparse multivariate polynomials over + the integers. + """ + + cdef fmpq_mpoly_t val + cdef fmpq_mpoly_ctx ctx + cdef bint _init + + def __cinit__(self): + self._init = False + + def __dealloc__(self): + if self._init: + fmpq_mpoly_clear(self.val, self.ctx.val) + self._init = False + + def __init__(self, val=0, ctx=None): + if typecheck(val, fmpq_mpoly): + if ctx is None or ctx == (val).ctx: + init_fmpq_mpoly(self, (val).ctx) + fmpq_mpoly_set(self.val, (val).val, self.ctx.val) + else: + raise ValueError("Cannot automatically coerce contexts") + elif isinstance(val, dict): + if ctx is None: + if len(val) == 0: + raise ValueError("Need context for zero polynomial") + k = list(val.keys())[0] + if not isinstance(k, tuple): + raise ValueError("Dict should be keyed with tuples of integers") + ctx = get_fmpq_mpoly_context(len(k)) + x = ctx.fmpq_mpoly_from_dict(val) + #XXX this copy is silly, have a ctx function that assigns an fmpq_mpoly_t + init_fmpq_mpoly(self, ctx) + fmpq_mpoly_set(self.val, (x).val, self.ctx.val) + elif isinstance(val, str): + if ctx is None: + raise ValueError("Cannot parse a polynomial without context") + val = bytes(val, 'utf-8') + init_fmpq_mpoly(self, ctx) + fmpq_mpoly_set_str_pretty(self.val, val, self.ctx.c_names, self.ctx.val) + fmpq_mpoly_sort_terms(self.val, self.ctx.val) + else: + v = any_as_fmpq(val) + if v is NotImplemented: + raise TypeError("cannot create fmpq_mpoly from type %s" % type(val)) + if ctx is None: + raise ValueError("Need context to convert fmpq to fmpq_mpoly") + init_fmpq_mpoly(self, ctx) + fmpq_mpoly_set_fmpq(self.val, (v).val, self.ctx.val) + + def __nonzero__(self): + return not fmpq_mpoly_is_zero(self.val, self.ctx.val) + + def __bool__(self): + return not fmpq_mpoly_is_zero(self.val, self.ctx.val) + + def is_one(self): + return fmpq_mpoly_is_one(self.val, self.ctx.val) + + def __richcmp__(self, other, int op): + if op != 2 and op != 3: + return NotImplemented + if typecheck(self, fmpq_mpoly) and typecheck(other, fmpq_mpoly): + if (self).ctx is (other).ctx: + if op == 2: + return bool(fmpq_mpoly_equal((self).val, (other).val, (self).ctx.val)) + else: + return not bool(fmpq_mpoly_equal((self).val, (other).val, (self).ctx.val)) + else: + if op == 2: + return False + else: + return True + if op == 2: + return not bool(self - other) + else: + return bool(self - other) + + def __len__(self): + return fmpq_mpoly_length(self.val, self.ctx.val) + + def coefficient(self, slong i): + cdef fmpq v + if i < 0 or i >= fmpq_mpoly_length(self.val, self.ctx.val): + return fmpq(0) + else: + v = fmpq.__new__(fmpq) + fmpq_mpoly_get_term_coeff_fmpq(v.val, self.val, i, self.ctx.val) + return v + + def exponent_tuple(self, slong i): + cdef slong j, nvars + cdef fmpz_struct ** tmp + if i < 0 or i >= fmpq_mpoly_length(self.val, self.ctx.val): + raise ValueError + nvars = self.ctx.nvars() + res = tuple(fmpz() for j in range(nvars)) + tmp = libc.stdlib.malloc(nvars * sizeof(fmpz_struct *)) + try: + for j in range(nvars): + tmp[j] = &(( (res[j])).val[0]) + fmpq_mpoly_get_term_exp_fmpz(tmp, self.val, i, self.ctx.val) + finally: + libc.stdlib.free(tmp) + return res + + def repr(self): + return self.str() + " (nvars=%s, ordering=%s names=%s)" % (self.ctx.nvars(), self.ctx.ordering(), self.ctx.py_names) + + def str(self): + cdef char * s = fmpq_mpoly_get_str_pretty(self.val, self.ctx.c_names, self.ctx.val) + try: + res = str_from_chars(s) + finally: + libc.stdlib.free(s) + res = res.replace("+", " + ") + res = res.replace("-", " - ") + if res.startswith(" - "): + res = "-" + res[3:] + return res + + return self.repr() + + def __neg__(self): + cdef fmpq_mpoly res + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_neg(res.val, (self).val, res.ctx.val) + return res + + def __add__(self, other): + cdef fmpq_mpoly res + if typecheck(other, fmpq_mpoly): + if (self).ctx is not (other).ctx: + return NotImplemented + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_add(res.val, (self).val, (other).val, res.ctx.val) + return res + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_add_fmpq(res.val, (self).val, (other).val, res.ctx.val) + return res + return NotImplemented + + def __radd__(self, other): + cdef fmpq_mpoly res + other = any_as_fmpq(other) + if other is not NotImplemented: + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_add_fmpq(res.val, (self).val, (other).val, res.ctx.val) + return res + return NotImplemented + + def __iadd__(self, other): + if typecheck(other, fmpq_mpoly): + if (self).ctx is not (other).ctx: + return NotImplemented + fmpq_mpoly_add((self).val, (self).val, (other).val, self.ctx.val) + return self + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + fmpq_mpoly_add_fmpq((self).val, (self).val, (other).val, self.ctx.val) + return self + return NotImplemented + + def __sub__(self, other): + cdef fmpq_mpoly res + if typecheck(other, fmpq_mpoly): + if (self).ctx is not (other).ctx: + return NotImplemented + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_sub(res.val, (self).val, (other).val, res.ctx.val) + return res + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_sub_fmpq(res.val, (self).val, (other).val, res.ctx.val) + return res + return NotImplemented + + def __rsub__(self, other): + cdef fmpq_mpoly res + other = any_as_fmpq(other) + if other is not NotImplemented: + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_sub_fmpq(res.val, (self).val, (other).val, res.ctx.val) + return -res + return NotImplemented + + def __isub__(self, other): + if typecheck(other, fmpq_mpoly): + if (self).ctx is not (other).ctx: + return NotImplemented + fmpq_mpoly_sub((self).val, (self).val, (other).val, self.ctx.val) + return self + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + fmpq_mpoly_sub_fmpq((self).val, (self).val, (other).val, self.ctx.val) + return self + return NotImplemented + + def __mul__(self, other): + cdef fmpq_mpoly res + if typecheck(other, fmpq_mpoly): + if (self).ctx is not (other).ctx: + return NotImplemented + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_mul(res.val, (self).val, (other).val, res.ctx.val) + return res + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_scalar_mul_fmpq(res.val, (self).val, (other).val, res.ctx.val) + return res + return NotImplemented + + def __rmul__(self, other): + cdef fmpq_mpoly res + other = any_as_fmpq(other) + if other is not NotImplemented: + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_scalar_mul_fmpq(res.val, (self).val, (other).val, res.ctx.val) + return res + return NotImplemented + + def __imul__(self, other): + if typecheck(other, fmpq_mpoly): + if (self).ctx is not (other).ctx: + return NotImplemented + fmpq_mpoly_mul((self).val, (self).val, (other).val, self.ctx.val) + return self + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + fmpq_mpoly_scalar_mul_fmpq(self.val, (self).val, (other).val, self.ctx.val) + return self + return NotImplemented + + def __pow__(self, other, modulus): + cdef fmpq_mpoly res + if modulus is not None: + raise NotImplementedError + other = any_as_fmpz(other) + if other is NotImplemented: + return other + if other < 0: + raise ValueError("cannot raise fmpq_mpoly to negative power") + res = create_fmpq_mpoly(self.ctx) + if fmpq_mpoly_pow_fmpz(res.val, (self).val, (other).val, res.ctx.val) == 0: + raise ValueError("unreasonably large polynomial") + return res + + def __divmod__(self, other): + cdef fmpq_mpoly res, res2 + if typecheck(other, fmpq_mpoly): + if (self).ctx is not (other).ctx: + return NotImplemented + res = create_fmpq_mpoly(self.ctx) + res2 = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) + return (res, res2) + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + other= fmpq_mpoly(other, self.ctx) + res = create_fmpq_mpoly(self.ctx) + res2 = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) + return (res, res2) + return NotImplemented + + def __rdivmod__(self, other): + cdef fmpq_mpoly res, res2 + other = any_as_fmpq(other) + if other is not NotImplemented: + other = fmpq_mpoly(other, self.ctx) + res = create_fmpq_mpoly(self.ctx) + res2 = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_divrem(res.val, res2.val, (other).val, (self).val, res.ctx.val) + return res + return NotImplemented + + # def __floordiv__(self, other): + # cdef fmpq_mpoly res + # if typecheck(other, fmpq_mpoly): + # if (self).ctx is not (other).ctx: + # return NotImplemented + # res = create_fmpq_mpoly(self.ctx) + # fmpq_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) + # return res + # else: + # other = any_as_fmpq(other) + # if other is not NotImplemented: + # other = fmpq_mpoly(other, self.ctx) + # res = create_fmpq_mpoly(self.ctx) + # fmpq_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) + # return res + # return NotImplemented + + # def __rfloordiv__(self,other): + # cdef fmpq_mpoly res + # other = any_as_fmpq(other) + # if other is not NotImplemented: + # other = fmpq_mpoly(other, self.ctx) + # res = create_fmpq_mpoly(self.ctx) + # fmpq_mpoly_div(res.val, (other).val, self.val, res.ctx.val) + # return res + # return NotImplemented + + def __mod__(self, other): + return divmod(self, other)[1] + + def gcd(self, other): + cdef fmpq_mpoly res + assert isinstance(other, fmpq_mpoly) + if (self).ctx is not (other).ctx: + return NotImplemented + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_gcd(res.val, (self).val, (other).val, res.ctx.val) + return res diff --git a/src/flint/fmpz_mpoly.pyx b/src/flint/fmpz_mpoly.pyx index 440bf194..2fcf8f1a 100644 --- a/src/flint/fmpz_mpoly.pyx +++ b/src/flint/fmpz_mpoly.pyx @@ -116,10 +116,13 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): return res def constant(self, z): + """ + Create a constant polynomial in this context + """ cdef fmpz_mpoly res z = any_as_fmpz(z) if z is NotImplemented: - raise ValueError() + raise ValueError("A constant fmpz_mpoly is a fmpz") res = fmpz_mpoly.__new__(fmpz_mpoly) res.ctx = self fmpz_mpoly_init(res.val, res.ctx.val) @@ -205,43 +208,43 @@ def get_fmpz_mpoly_context(slong nvars=1, ordering="lex", names='x'): _fmpz_mpoly_ctx_cache[key] = ctx return ctx -cdef _fmpz_mpoly_set2(fmpz_mpoly_t out, fmpz_mpoly_ctx_t outctx, fmpz_mpoly_t inp, fmpz_mpoly_ctx_t inpctx): - cdef slong * C - cdef slong i - cdef slong inpvars, outvars - if outctx == inpctx: - fmpz_mpoly_set(out, inp, inpctx) - else: - inpvars = inpctx.minfo.nvars - outvars = inpctx.minfo.nvars - C = libc.stdlib.malloc(inpvars * sizeof(slong *)) - for i in range(min(outvars, inpvars)): - C[i] = i - for i in range(outvars, inpvars): - C[i] = -1 - fmpz_mpoly_compose_fmpz_mpoly_gen(out, inp, C, inpctx, outctx) - libc.stdlib.free(C) - -def coerce_fmpz_mpolys(*args): - cdef fmpz_mpoly_ctx ctx - ctx = get_fmpz_mpoly_context() - if not args: - return ctx, [] - args = list(args) - if typecheck(args[0], fmpz_mpoly): - ctx = ( args[0]).ctx - if all(typecheck(args[i], fmpz_mpoly) and ( args[i]).ctx is ctx for i in range(1, len(args))): - return ctx, args - for i in range(len(args)): - if not typecheck(args[i], fmpz_mpoly): - args[i] = fmpz_mpoly(args[i]) - nvars = max((pol).ctx.nvars() for pol in args) - ctx = get_fmpz_mpoly_context(nvars) - args2 = [fmpz_mpoly() for i in range(len(args))] - for i in range(len(args)): - ( args2[i]).ctx = ctx - _fmpz_mpoly_set2(( args2[i]).val, ctx.val, ( args[i]).val, ( args[i]).ctx.val) - return ctx, args2 +# cdef _fmpz_mpoly_set2(fmpz_mpoly_t out, fmpz_mpoly_ctx_t outctx, fmpz_mpoly_t inp, fmpz_mpoly_ctx_t inpctx): +# cdef slong * C +# cdef slong i +# cdef slong inpvars, outvars +# if outctx == inpctx: +# fmpz_mpoly_set(out, inp, inpctx) +# else: +# inpvars = inpctx.minfo.nvars +# outvars = inpctx.minfo.nvars +# C = libc.stdlib.malloc(inpvars * sizeof(slong *)) +# for i in range(min(outvars, inpvars)): +# C[i] = i +# for i in range(outvars, inpvars): +# C[i] = -1 +# fmpz_mpoly_compose_fmpz_mpoly_gen(out, inp, C, inpctx, outctx) +# libc.stdlib.free(C) + +# def coerce_fmpz_mpolys(*args): +# cdef fmpz_mpoly_ctx ctx +# ctx = get_fmpz_mpoly_context() +# if not args: +# return ctx, [] +# args = list(args) +# if typecheck(args[0], fmpz_mpoly): +# ctx = ( args[0]).ctx +# if all(typecheck(args[i], fmpz_mpoly) and ( args[i]).ctx is ctx for i in range(1, len(args))): +# return ctx, args +# for i in range(len(args)): +# if not typecheck(args[i], fmpz_mpoly): +# args[i] = fmpz_mpoly(args[i]) +# nvars = max((pol).ctx.nvars() for pol in args) +# ctx = get_fmpz_mpoly_context(nvars) +# args2 = [fmpz_mpoly() for i in range(len(args))] +# for i in range(len(args)): +# ( args2[i]).ctx = ctx +# _fmpz_mpoly_set2(( args2[i]).val, ctx.val, ( args[i]).val, ( args[i]).ctx.val) +# return ctx, args2 cdef inline init_fmpz_mpoly(fmpz_mpoly var, fmpz_mpoly_ctx ctx): var.ctx = ctx @@ -587,50 +590,50 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_gcd(res.val, (self).val, (other).val, res.ctx.val) return res - def __call__(self, *args): - cdef fmpz_mpoly res - cdef fmpz_mpoly_ctx res_ctx - cdef fmpz_struct ** V - cdef fmpz vres - cdef fmpz_mpoly_struct ** C - cdef slong i, nvars, nargs - other = tuple(args) - nargs = len(args) - nvars = self.ctx.nvars() - # todo: should extend with generators instead? - if nargs < nvars: - args = args + (0,) * (nvars - nargs) - if nargs > nvars: - args = args[:nvars] - args_fmpz = [any_as_fmpz(v) for v in args] - # todo: for combination, compose - # todo: if fewer than number of variables, evaluate partially? - if NotImplemented not in args_fmpz: - V = libc.stdlib.malloc(nvars * sizeof(fmpz_struct *)) - try: - for i in range(nvars): - V[i] = &(( args_fmpz[i]).val[0]) - vres = fmpz.__new__(fmpz) - if fmpz_mpoly_evaluate_all_fmpz(vres.val, self.val, V, self.ctx.val) == 0: - raise ValueError("unreasonably large polynomial") - return vres - finally: - libc.stdlib.free(V) - else: - res_ctx, args = coerce_fmpz_mpolys(*args) - C = libc.stdlib.malloc(nvars * sizeof(fmpz_mpoly_struct *)) - try: - for i in range(nvars): - C[i] = &(( args[i]).val[0]) - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = res_ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True - if fmpz_mpoly_compose_fmpz_mpoly(res.val, self.val, C, self.ctx.val, res_ctx.val) == 0: - raise ValueError("unreasonably large polynomial") - return res - finally: - libc.stdlib.free(C) + # def __call__(self, *args): + # cdef fmpz_mpoly res + # cdef fmpz_mpoly_ctx res_ctx + # cdef fmpz_struct ** V + # cdef fmpz vres + # cdef fmpz_mpoly_struct ** C + # cdef slong i, nvars, nargs + # other = tuple(args) + # nargs = len(args) + # nvars = self.ctx.nvars() + # # todo: should extend with generators instead? + # if nargs < nvars: + # args = args + (0,) * (nvars - nargs) + # if nargs > nvars: + # args = args[:nvars] + # args_fmpz = [any_as_fmpz(v) for v in args] + # # todo: for combination, compose + # # todo: if fewer than number of variables, evaluate partially? + # if NotImplemented not in args_fmpz: + # V = libc.stdlib.malloc(nvars * sizeof(fmpz_struct *)) + # try: + # for i in range(nvars): + # V[i] = &(( args_fmpz[i]).val[0]) + # vres = fmpz.__new__(fmpz) + # if fmpz_mpoly_evaluate_all_fmpz(vres.val, self.val, V, self.ctx.val) == 0: + # raise ValueError("unreasonably large polynomial") + # return vres + # finally: + # libc.stdlib.free(V) + # else: + # res_ctx, args = coerce_fmpz_mpolys(*args) + # C = libc.stdlib.malloc(nvars * sizeof(fmpz_mpoly_struct *)) + # try: + # for i in range(nvars): + # C[i] = &(( args[i]).val[0]) + # res = fmpz_mpoly.__new__(fmpz_mpoly) + # res.ctx = res_ctx + # fmpz_mpoly_init(res.val, res.ctx.val) + # res._init = True + # if fmpz_mpoly_compose_fmpz_mpoly(res.val, self.val, C, self.ctx.val, res_ctx.val) == 0: + # raise ValueError("unreasonably large polynomial") + # return res + # finally: + # libc.stdlib.free(C) ''' def factor(self): diff --git a/src/flint/pyflint.pxd b/src/flint/pyflint.pxd index abfe739f..61682225 100644 --- a/src/flint/pyflint.pxd +++ b/src/flint/pyflint.pxd @@ -81,3 +81,9 @@ cdef class fmpz_mpoly: cdef fmpz_mpoly_t val cdef bint initialized +cdef class fmpq_mpoly_ctx: + cdef fmpq_mpoly_ctx_t val + +cdef class fmpq_mpoly: + cdef fmpq_mpoly_t val + cdef bint initialized diff --git a/src/flint/pyflint.pyx b/src/flint/pyflint.pyx index a9c14ccb..02eb81bb 100644 --- a/src/flint/pyflint.pyx +++ b/src/flint/pyflint.pyx @@ -38,6 +38,7 @@ include "fmpz_series.pyx" include "fmpq.pyx" include "fmpq_poly.pyx" +include "fmpq_mpoly.pyx" include "fmpq_mat.pyx" include "fmpq_series.pyx" diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 6ab31df6..63f71ab3 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -1138,6 +1138,39 @@ def set_bad(i): assert M3.charpoly() == flint.fmpq_poly([-1,6,-12,8]) / 8 assert M3.minpoly() == flint.fmpq_poly([1,-4,4]) / 4 +def test_fmpq_mpoly(): + Zp = flint.fmpq_mpoly + getctx = flint.get_fmpq_mpoly_context + ctx = getctx(4) + assert ctx.nvars() == 4 + assert ctx.ordering() == "lex" + assert [ctx.name(i) for i in range(4)] == ['x0', 'x1', 'x2', 'x3'] + for order in ['lex', 'deglex', 'degrevlex']: + ctx1 = getctx(4, order) + assert ctx1.ordering() == order + ctx = getctx(4, "lex", 'w,x,y,z') + p1 = ctx.gen(0) + ctx.gen(1) - ctx.gen(2) * ctx.gen(3) + assert p1 == Zp("w + x - y * z", ctx) + ctx = getctx(2, "lex", "x,y") + assert ctx.fmpq_mpoly_from_dict({(1,0):1, (0,1):2}) == Zp("x + 2*y", ctx) + assert raises(lambda: ctx.fmpq_mpoly_from_dict("b"), ValueError) + assert raises(lambda: ctx.fmpq_mpoly_from_dict({(1,2):"b"}), TypeError) + assert raises(lambda: ctx.fmpq_mpoly_from_dict({"b":1}), TypeError) + assert raises(lambda: ctx.fmpq_mpoly_from_dict({(1,2,3):1}), TypeError) + assert raises(lambda: ctx.fmpq_mpoly_from_dict({(1,"a"):1}), TypeError) + ctx = getctx(2, "lex", 'x,y') + p1 = ctx.fmpq_mpoly_from_dict({(1,0):4,(0,3):4,(2,4):9}) + for ztype in [int, long, flint.fmpz]: + assert p1 + ztype(3) == ctx.fmpq_mpoly_from_dict({(1,0):4,(0,3):4,(2,4):9,(0,0):3}) + assert ztype(3) + p1 == ctx.fmpq_mpoly_from_dict({(1,0):4,(0,3):4,(2,4):9,(0,0):3}) + assert p1 - ztype(3) == ctx.fmpq_mpoly_from_dict({(1,0):4,(0,3):4,(2,4):9,(0,0):-3}) + assert ztype(3) - p1 == ctx.fmpq_mpoly_from_dict({(1,0):-4,(0,3):-4,(2,4):-9,(0,0):3}) + assert p1 * ztype(3) == ctx.fmpq_mpoly_from_dict({(1,0):12,(0,3):12,(2,4):27}) + assert ztype(3) * p1 == ctx.fmpq_mpoly_from_dict({(1,0):12,(0,3):12,(2,4):27}) + assert p1 // ztype(3) == ctx.fmpq_mpoly_from_dict({(1,0):1,(0,3):1,(2,4):3}) + assert ztype(3) // p1 == Zp(0,ctx) + assert ctx.constant(7) + ztype(3) == Zp(10, ctx) + def test_fmpq_series(): Qp = flint.fmpq_poly Q = flint.fmpq_series @@ -1607,6 +1640,7 @@ def test_pickling(): test_fmpq, test_fmpq_poly, test_fmpq_mat, + test_fmpq_mpoly, test_fmpq_series, test_nmod, test_nmod_poly, From 0f2fceb6a5273c4336721847ed64b16e2d8b328d Mon Sep 17 00:00:00 2001 From: David Einstein Date: Mon, 11 Sep 2023 21:29:06 -0400 Subject: [PATCH 13/95] Small advances in fmpz_mpoly. Added infrastructure for fmpq_mpoly fixed up new import system --- src/flint/flint_base/flint_context.pyx | 2 +- src/flint/flintlib/fmpq_mpoly.pxd | 162 ++++++++++++------------ src/flint/types/fmpq_mpoly.pxd | 16 +++ src/flint/types/fmpq_mpoly.pyx | 163 +++++++++++++++++++++++++ src/flint/types/fmpz_mpoly.pxd | 1 - src/flint/types/fmpz_poly.pxd | 1 - 6 files changed, 261 insertions(+), 84 deletions(-) create mode 100644 src/flint/types/fmpq_mpoly.pxd create mode 100644 src/flint/types/fmpq_mpoly.pyx diff --git a/src/flint/flint_base/flint_context.pyx b/src/flint/flint_base/flint_context.pyx index c73c83bc..297005f1 100644 --- a/src/flint/flint_base/flint_context.pyx +++ b/src/flint/flint_base/flint_context.pyx @@ -18,7 +18,7 @@ cdef class FlintContext: self.threads = 1 self.cap = 10 - @property + @property def prec(self): return self._prec diff --git a/src/flint/flintlib/fmpq_mpoly.pxd b/src/flint/flintlib/fmpq_mpoly.pxd index 33a42ad9..e37e276d 100644 --- a/src/flint/flintlib/fmpq_mpoly.pxd +++ b/src/flint/flintlib/fmpq_mpoly.pxd @@ -57,104 +57,104 @@ cdef extern from "flint/fmpq_mpoly.h": void fmpq_mpoly_get_denominator(fmpz_t d, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_get_coeff_fmpq_monomial(fmpq_t c, const fmpq_mpoly_t A, const fmpq_mpoly_t M, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_set_coeff_fmpq_monomial(fmpq_mpoly_t A, const fmpq_t c, const fmpq_mpoly_t M, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_get_coeff_fmpq_fmpz(fmpq_t c, const fmpq_mpoly_t A, fmpz_t * const * exp, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_get_coeff_fmpq_ui(fmpq_t c, const fmpq_mpoly_t A, ulong const * exp, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_set_coeff_fmpq_fmpz(fmpq_mpoly_t A, const fmpq_t c, fmpz_t * const * exp, fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_set_coeff_fmpq_ui(fmpq_mpoly_t A, const fmpq_t c, ulong const * exp, fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_get_coeff_vars_ui(fmpq_mpoly_t C, const fmpq_mpoly_t A, const slong * vars, const ulong * exps, slong length, const fmpq_mpoly_ctx_t ctx) - # int fmpq_mpoly_cmp(const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) - # fmpq * fmpq_mpoly_content_ref(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) - # fmpz_mpoly_struct * fmpq_mpoly_zpoly_ref(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) - # fmpz_t * fmpq_mpoly_zpoly_term_coeff_ref(fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) - # int fmpq_mpoly_is_canonical(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_get_coeff_fmpq_fmpz(fmpq_t c, const fmpq_mpoly_t A, fmpz_t * const * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_get_coeff_fmpq_ui(fmpq_t c, const fmpq_mpoly_t A, ulong const * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_set_coeff_fmpq_fmpz(fmpq_mpoly_t A, const fmpq_t c, fmpz_t * const * exp, fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_set_coeff_fmpq_ui(fmpq_mpoly_t A, const fmpq_t c, ulong const * exp, fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_get_coeff_vars_ui(fmpq_mpoly_t C, const fmpq_mpoly_t A, const slong * vars, const ulong * exps, slong length, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_cmp(const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + fmpq * fmpq_mpoly_content_ref(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + fmpz_mpoly_struct * fmpq_mpoly_zpoly_ref(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + fmpz_t * fmpq_mpoly_zpoly_term_coeff_ref(fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_is_canonical(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) slong fmpq_mpoly_length(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_resize(fmpq_mpoly_t A, slong new_length, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_resize(fmpq_mpoly_t A, slong new_length, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_get_term_coeff_fmpq(fmpq_t c, const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_set_term_coeff_fmpq(fmpq_mpoly_t A, slong i, const fmpq_t c, const fmpq_mpoly_ctx_t ctx) - # int fmpq_mpoly_term_exp_fits_si(const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) - # int fmpq_mpoly_term_exp_fits_ui(const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_term_exp_fits_si(const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_term_exp_fits_ui(const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_get_term_exp_fmpz(fmpz_struct ** exps, const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_get_term_exp_ui(ulong * exps, const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_get_term_exp_si(slong * exps, const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) - # ulong fmpq_mpoly_get_term_var_exp_ui(const fmpq_mpoly_t A, slong i, slong var, const fmpq_mpoly_ctx_t ctx) - # slong fmpq_mpoly_get_term_var_exp_si(const fmpq_mpoly_t A, slong i, slong var, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_set_term_exp_fmpz(fmpq_mpoly_t A, slong i, fmpz_t * const * exps, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_set_term_exp_ui(fmpq_mpoly_t A, slong i, const ulong * exps, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_get_term(fmpq_mpoly_t M, const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_get_term_monomial(fmpq_mpoly_t M, const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_get_term_exp_ui(ulong * exps, const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_get_term_exp_si(slong * exps, const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) + ulong fmpq_mpoly_get_term_var_exp_ui(const fmpq_mpoly_t A, slong i, slong var, const fmpq_mpoly_ctx_t ctx) + slong fmpq_mpoly_get_term_var_exp_si(const fmpq_mpoly_t A, slong i, slong var, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_set_term_exp_fmpz(fmpq_mpoly_t A, slong i, fmpz_t * const * exps, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_set_term_exp_ui(fmpq_mpoly_t A, slong i, const ulong * exps, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_get_term(fmpq_mpoly_t M, const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_get_term_monomial(fmpq_mpoly_t M, const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_push_term_fmpq_fmpz(fmpq_mpoly_t A, const fmpq_t c, fmpz_t * const * exp, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_push_term_fmpz_fmpz(fmpq_mpoly_t A, const fmpz_t c, fmpz_t * const * exp, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_push_term_ui_fmpz(fmpq_mpoly_t A, ulong c, fmpz_t * const * exp, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_push_term_si_fmpz(fmpq_mpoly_t A, slong c, fmpz_t * const * exp, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_push_term_fmpq_ui(fmpq_mpoly_t A, const fmpq_t c, const ulong * exp, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_push_term_fmpz_ui(fmpq_mpoly_t A, const fmpz_t c, const ulong * exp, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_push_term_ui_ui(fmpq_mpoly_t A, ulong c, const ulong * exp, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_push_term_si_ui(fmpq_mpoly_t A, slong c, const ulong * exp, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_reduce(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_push_term_fmpz_fmpz(fmpq_mpoly_t A, const fmpz_t c, fmpz_t * const * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_push_term_ui_fmpz(fmpq_mpoly_t A, ulong c, fmpz_t * const * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_push_term_si_fmpz(fmpq_mpoly_t A, slong c, fmpz_t * const * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_push_term_fmpq_ui(fmpq_mpoly_t A, const fmpq_t c, const ulong * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_push_term_fmpz_ui(fmpq_mpoly_t A, const fmpz_t c, const ulong * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_push_term_ui_ui(fmpq_mpoly_t A, ulong c, const ulong * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_push_term_si_ui(fmpq_mpoly_t A, slong c, const ulong * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_reduce(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_sort_terms(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_combine_like_terms(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_reverse(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_randtest_bound(fmpq_mpoly_t A, flint_rand_t state, slong length, mp_limb_t coeff_bits, ulong exp_bound, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_randtest_bounds(fmpq_mpoly_t A, flint_rand_t state, slong length, mp_limb_t coeff_bits, ulong * exp_bounds, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_randtest_bits(fmpq_mpoly_t A, flint_rand_t state, slong length, mp_limb_t coeff_bits, mp_limb_t exp_bits, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_combine_like_terms(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_reverse(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_randtest_bound(fmpq_mpoly_t A, flint_rand_t state, slong length, mp_limb_t coeff_bits, ulong exp_bound, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_randtest_bounds(fmpq_mpoly_t A, flint_rand_t state, slong length, mp_limb_t coeff_bits, ulong * exp_bounds, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_randtest_bits(fmpq_mpoly_t A, flint_rand_t state, slong length, mp_limb_t coeff_bits, mp_limb_t exp_bits, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_add_fmpq(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_t c, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_add_fmpz(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpz_t c, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_add_ui(fmpq_mpoly_t A, const fmpq_mpoly_t B, ulong c, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_add_si(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_add_fmpz(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpz_t c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_add_ui(fmpq_mpoly_t A, const fmpq_mpoly_t B, ulong c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_add_si(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong c, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_sub_fmpq(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_t c, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_sub_fmpz(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpz_t c, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_sub_ui(fmpq_mpoly_t A, const fmpq_mpoly_t B, ulong c, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_sub_si(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_sub_fmpz(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpz_t c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_sub_ui(fmpq_mpoly_t A, const fmpq_mpoly_t B, ulong c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_sub_si(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong c, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_add(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_t C, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_sub(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_t C, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_neg(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_scalar_mul_fmpq(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_t c, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_scalar_mul_fmpz(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpz_t c, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_scalar_mul_ui(fmpq_mpoly_t A, const fmpq_mpoly_t B, ulong c, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_scalar_mul_si(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong c, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_scalar_div_fmpq(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_t c, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_scalar_div_fmpz(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpz_t c, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_scalar_div_ui(fmpq_mpoly_t A, const fmpq_mpoly_t B, ulong c, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_scalar_div_si(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong c, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_make_monic(fmpq_mpoly_t A, fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_derivative(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong var, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_integral(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong var, const fmpq_mpoly_ctx_t ctx) - # int fmpq_mpoly_evaluate_all_fmpq(fmpq_t ev, const fmpq_mpoly_t A, fmpq * const * vals, const fmpq_mpoly_ctx_t ctx) - # int fmpq_mpoly_evaluate_one_fmpq(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong var, const fmpq_t val, const fmpq_mpoly_ctx_t ctx) - # int fmpq_mpoly_compose_fmpq_poly(fmpq_poly_t A, const fmpq_mpoly_t B, fmpq_poly_struct * const * C, const fmpq_mpoly_ctx_t ctxB) - # int fmpq_mpoly_compose_fmpq_mpoly(fmpq_mpoly_t A, const fmpq_mpoly_t B, fmpq_mpoly_struct * const * C, const fmpq_mpoly_ctx_t ctxB, const fmpq_mpoly_ctx_t ctxAC) - # void fmpq_mpoly_compose_fmpq_mpoly_gen(fmpq_mpoly_t A, const fmpq_mpoly_t B, const slong * c, const fmpq_mpoly_ctx_t ctxB, const fmpq_mpoly_ctx_t ctxAC) + void fmpq_mpoly_scalar_mul_ui(fmpq_mpoly_t A, const fmpq_mpoly_t B, ulong c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_scalar_mul_si(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_scalar_div_fmpq(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_t c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_scalar_div_fmpz(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpz_t c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_scalar_div_ui(fmpq_mpoly_t A, const fmpq_mpoly_t B, ulong c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_scalar_div_si(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong c, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_make_monic(fmpq_mpoly_t A, fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_derivative(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong var, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_integral(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong var, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_evaluate_all_fmpq(fmpq_t ev, const fmpq_mpoly_t A, fmpq * const * vals, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_evaluate_one_fmpq(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong var, const fmpq_t val, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_compose_fmpq_poly(fmpq_poly_t A, const fmpq_mpoly_t B, fmpq_poly_struct * const * C, const fmpq_mpoly_ctx_t ctxB) + int fmpq_mpoly_compose_fmpq_mpoly(fmpq_mpoly_t A, const fmpq_mpoly_t B, fmpq_mpoly_struct * const * C, const fmpq_mpoly_ctx_t ctxB, const fmpq_mpoly_ctx_t ctxAC) + void fmpq_mpoly_compose_fmpq_mpoly_gen(fmpq_mpoly_t A, const fmpq_mpoly_t B, const slong * c, const fmpq_mpoly_ctx_t ctxB, const fmpq_mpoly_ctx_t ctxAC) void fmpq_mpoly_mul(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_t C, const fmpq_mpoly_ctx_t ctx) int fmpq_mpoly_pow_fmpz(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpz_t k, const fmpq_mpoly_ctx_t ctx) - # int fmpq_mpoly_pow_ui(fmpq_mpoly_t A, const fmpq_mpoly_t B, ulong k, const fmpq_mpoly_ctx_t ctx) - # int fmpq_mpoly_divides(fmpq_mpoly_t Q, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_div(fmpq_mpoly_t Q, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_pow_ui(fmpq_mpoly_t A, const fmpq_mpoly_t B, ulong k, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_divides(fmpq_mpoly_t Q, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_div(fmpq_mpoly_t Q, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_divrem(fmpq_mpoly_t Q, fmpq_mpoly_t R, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_divrem_ideal(fmpq_mpoly_struct ** Q, fmpq_mpoly_t R, const fmpq_mpoly_t A, fmpq_mpoly_struct * const * B, slong len, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_content(fmpq_t g, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_term_content(fmpq_mpoly_t M, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) - # int fmpq_mpoly_content_vars(fmpq_mpoly_t g, const fmpq_mpoly_t A, slong * vars, slong vars_length, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_divrem_ideal(fmpq_mpoly_struct ** Q, fmpq_mpoly_t R, const fmpq_mpoly_t A, fmpq_mpoly_struct * const * B, slong len, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_content(fmpq_t g, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_term_content(fmpq_mpoly_t M, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_content_vars(fmpq_mpoly_t g, const fmpq_mpoly_t A, slong * vars, slong vars_length, const fmpq_mpoly_ctx_t ctx) int fmpq_mpoly_gcd(fmpq_mpoly_t G, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) - # int fmpq_mpoly_gcd_cofactors(fmpq_mpoly_t G, fmpq_mpoly_t Abar, fmpq_mpoly_t Bbar, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) - # int fmpq_mpoly_gcd_brown(fmpq_mpoly_t G, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) - # int fmpq_mpoly_gcd_hensel(fmpq_mpoly_t G, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) - # int fmpq_mpoly_gcd_subresultant(fmpq_mpoly_t G, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) - # int fmpq_mpoly_gcd_zippel(fmpq_mpoly_t G, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) - # int fmpq_mpoly_gcd_zippel2(fmpq_mpoly_t G, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) - # int fmpq_mpoly_resultant(fmpq_mpoly_t R, const fmpq_mpoly_t A, const fmpq_mpoly_t B, slong var, const fmpq_mpoly_ctx_t ctx) - # int fmpq_mpoly_discriminant(fmpq_mpoly_t D, const fmpq_mpoly_t A, slong var, const fmpq_mpoly_ctx_t ctx) - # int fmpq_mpoly_sqrt(fmpq_mpoly_t Q, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) - # int fmpq_mpoly_is_square(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_univar_init(fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_univar_clear(fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_univar_swap(fmpq_mpoly_univar_t A, fmpq_mpoly_univar_t B, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_to_univar(fmpq_mpoly_univar_t A, const fmpq_mpoly_t B, slong var, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_from_univar(fmpq_mpoly_t A, const fmpq_mpoly_univar_t B, slong var, const fmpq_mpoly_ctx_t ctx) - # int fmpq_mpoly_univar_degree_fits_si(const fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) - # slong fmpq_mpoly_univar_length(const fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) - # slong fmpq_mpoly_univar_get_term_exp_si(fmpq_mpoly_univar_t A, slong i, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_univar_get_term_coeff(fmpq_mpoly_t c, const fmpq_mpoly_univar_t A, slong i, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_univar_swap_term_coeff(fmpq_mpoly_t c, fmpq_mpoly_univar_t A, slong i, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_gcd_cofactors(fmpq_mpoly_t G, fmpq_mpoly_t Abar, fmpq_mpoly_t Bbar, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_gcd_brown(fmpq_mpoly_t G, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_gcd_hensel(fmpq_mpoly_t G, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_gcd_subresultant(fmpq_mpoly_t G, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_gcd_zippel(fmpq_mpoly_t G, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_gcd_zippel2(fmpq_mpoly_t G, const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_resultant(fmpq_mpoly_t R, const fmpq_mpoly_t A, const fmpq_mpoly_t B, slong var, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_discriminant(fmpq_mpoly_t D, const fmpq_mpoly_t A, slong var, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_sqrt(fmpq_mpoly_t Q, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_is_square(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_univar_init(fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_univar_clear(fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_univar_swap(fmpq_mpoly_univar_t A, fmpq_mpoly_univar_t B, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_to_univar(fmpq_mpoly_univar_t A, const fmpq_mpoly_t B, slong var, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_from_univar(fmpq_mpoly_t A, const fmpq_mpoly_univar_t B, slong var, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_univar_degree_fits_si(const fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) + slong fmpq_mpoly_univar_length(const fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) + slong fmpq_mpoly_univar_get_term_exp_si(fmpq_mpoly_univar_t A, slong i, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_univar_get_term_coeff(fmpq_mpoly_t c, const fmpq_mpoly_univar_t A, slong i, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_univar_swap_term_coeff(fmpq_mpoly_t c, fmpq_mpoly_univar_t A, slong i, const fmpq_mpoly_ctx_t ctx) """ cdef extern from "flint/fmpz_mpoly_factor.h": diff --git a/src/flint/types/fmpq_mpoly.pxd b/src/flint/types/fmpq_mpoly.pxd new file mode 100644 index 00000000..04a4cdb7 --- /dev/null +++ b/src/flint/types/fmpq_mpoly.pxd @@ -0,0 +1,16 @@ +from flint.flint_base.flint_base cimport flint_mpoly +from flint.flint_base.flint_base cimport flint_mpoly_context + +from flint.flintlib.fmpq_mpoly cimport fmpq_mpoly_ctx_t +from flint.flintlib.fmpq_mpoly cimport fmpq_mpoly_t +from flint.flintlib.flint cimport slong + +cdef class fmpq_mpoly_ctx(flint_mpoly_context): + cdef fmpq_mpoly_ctx_t val + cpdef slong nvars(self) + cpdef ordering(self) + +cdef class fmpq_mpoly(flint_mpoly): + cdef fmpq_mpoly_t val + cdef fmpq_mpoly_ctx ctx + cdef bint _init diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx new file mode 100644 index 00000000..11922e15 --- /dev/null +++ b/src/flint/types/fmpq_mpoly.pyx @@ -0,0 +1,163 @@ + + +cdef dict _fmpq_mpoly_ctx_cache = {} + +@cython.auto_pickle(False) +cdef class fmpq_mpoly_ctx(flint_mpoly_context): + """ + A class for storing the polynomial context + + :param nvars: The number of variables in the ring + :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 `get_fmpz_mpoly_context`. + """ +# cdef fmpz_mpoly_ctx_t val + + def __init__(self, slong nvars, ordering, names): + if ordering == "lex": + fmpq_mpoly_ctx_init(self.val, nvars, ordering_t.ORD_LEX) + elif ordering == "deglex": + fmpq_mpoly_ctx_init(self.val, nvars, ordering_t.ORD_DEGLEX) + elif ordering == "degrevlex": + fmpq_mpoly_ctx_init(self.val, nvars, ordering_t.ORD_DEGREVLEX) + else: + raise ValueError("Unimplemented term order %s" % ordering) + + super().__init__(nvars, names) + + cpdef slong nvars(self): + """ + Return the number of variables in the context + + >>> ctx = get_fmpz_mpoly_context(4, "lex", 'x') + >>> ctx.nvars() + 4 + """ + return self.val.zctx.minfo.nvars + + cpdef ordering(self): + """ + Return the term order of the context object. + + >>> ctx = get_fmpz_mpoly_context(4, "deglex", 'w') + >>> ctx.ordering() + 'deglex' + """ + if self.val.zctx.minfo.ord == ordering_t.ORD_LEX: + return "lex" + if self.val.zctx.minfo.ord == ordering_t.ORD_DEGLEX: + return "deglex" + if self.val.zctx.minfo.ord == ordering_t.ORD_DEGREVLEX: + return "degrevlex" + + def gen(self, slong i): + """ + Return the `i`th generator of the polynomial ring + + >>> ctx = get_fmpz_mpoly_context(3, 'degrevlex', 'z') + >>> ctx.gen(1) + z1 + """ + cdef fmpq_mpoly res + assert i >= 0 and i < self.val.zctx.minfo.nvars + res = fmpq_mpoly.__new__(fmpz_mpoly) + res.ctx = self + fmpq_mpoly_init(res.val, res.ctx.val) + res._init = True + fmpq_mpoly_gen(res.val, i, res.ctx.val) + return res + + def constant(self, z): + """ + Create a constant polynomial in this context + """ + cdef fmpq_mpoly res + z = any_as_fmpq(z) + if z is NotImplemented: + raise ValueError("A constant fmpq_mpoly is a fmpq") + res = fmpz_mpoly.__new__(fmpz_mpoly) + res.ctx = self + fmpz_mpoly_init(res.val, res.ctx.val) + res._init = True + fmpz_mpoly_set_fmpz(res.val, (z).val, res.ctx.val) + return res + + def fmpz_mpoly_from_dict(self, d): + """ + Create a fmpz_mpoly from a dictionary. + + The dictionary's keys are tuples of ints (or anything that implicitly converts + to fmpz) representing exponents, and corresponding values of fmpz. + + >>> ctx = get_fmpz_mpoly_context(2,'lex','x,y') + >>> ctx.fmpz_mpoly_from_dict({(1,0):2, (1,1):3, (0,1):1}) + 3*x*y + 2*x + y + """ + cdef long n + cdef fmpz_t coefficient + cdef fmpz_struct *exponents + cdef int xtype + cdef int nvars = self.nvars() + cdef int i,j + cdef int count + cdef fmpz_mpoly res + + if not PyDict_Check(d): + raise ValueError("expected a dictionary") + n = PyDict_Size(d) + fmpz_init(coefficient) + exponents = libc.stdlib.calloc(nvars, sizeof(fmpz_struct)) + if exponents == NULL: + raise MemoryError() + for i in range(nvars): + fmpz_init(exponents + i) + fmpz_init(coefficient) + res = fmpz_mpoly.__new__(fmpz_mpoly) + res.ctx = self + fmpz_mpoly_init(res.val, res.ctx.val) + res._init = True + count = 0 + for k,v in d.items(): + xtype = fmpz_set_any_ref(coefficient, v) + if xtype == FMPZ_UNKNOWN: + libc.stdlib.free(exponents) + raise TypeError("invalid coefficient type %s" % type(v)) + if not PyTuple_Check(k): + libc.stdlib.free(exponents) + raise TypeError("Expected tuple of ints as key not %s" % type(k)) + if PyTuple_GET_SIZE(k) != nvars: + libc.stdlib.free(exponents) + raise TypeError("Expected exponent tuple of length %d" % nvars) + for i,tup in enumerate(k): + xtype = fmpz_set_any_ref(exponents + i, tup) + if xtype == FMPZ_UNKNOWN: + libc.stdlib.free(exponents) + raise TypeError("Invalid exponent type %s" % type(tup)) + #Todo lobby for fmpz_mpoly_push_term_fmpz_ffmpz + if not fmpz_is_zero(coefficient): + _fmpz_mpoly_push_exp_ffmpz(res.val, exponents, self.val) + fmpz_mpoly_set_term_coeff_fmpz(res.val, count, coefficient, self.val) + count += 1 + for i in range(nvars): + fmpz_clear(exponents + i) + fmpz_clear(coefficient) + fmpz_mpoly_sort_terms(res.val, self.val) + return res + + +def get_fmpz_mpoly_context(slong nvars=1, ordering="lex", names='x'): + if nvars <= 0: + nvars = 1 + nametup = tuple(name.strip() for name in names.split(',')) + if len(nametup) != nvars: + if len(nametup) != 1: + raise ValueError("Number of variables does not equal number of names") + nametup = tuple(nametup[0] + str(i) for i in range(nvars)) + key = (nvars, ordering, nametup) + ctx = _fmpz_mpoly_ctx_cache.get(key) + if ctx is None: + ctx = fmpz_mpoly_ctx(nvars, ordering, nametup) + _fmpz_mpoly_ctx_cache[key] = ctx + return ctx diff --git a/src/flint/types/fmpz_mpoly.pxd b/src/flint/types/fmpz_mpoly.pxd index f978f45b..a33d6f00 100644 --- a/src/flint/types/fmpz_mpoly.pxd +++ b/src/flint/types/fmpz_mpoly.pxd @@ -8,7 +8,6 @@ from flint.flintlib.flint cimport slong cdef class fmpz_mpoly_ctx(flint_mpoly_context): cdef fmpz_mpoly_ctx_t val cpdef slong nvars(self) - cpdef ordering(self) cdef class fmpz_mpoly(flint_mpoly): diff --git a/src/flint/types/fmpz_poly.pxd b/src/flint/types/fmpz_poly.pxd index 61bc109d..a91ad265 100644 --- a/src/flint/types/fmpz_poly.pxd +++ b/src/flint/types/fmpz_poly.pxd @@ -1,4 +1,3 @@ - from flint.flint_base.flint_base cimport flint_poly from flint.flintlib.fmpz_poly cimport fmpz_poly_t From fbe31b7d503e7238f259b00e01b1e36db2614eac Mon Sep 17 00:00:00 2001 From: David Einstein Date: Fri, 22 Sep 2023 11:50:51 -0400 Subject: [PATCH 14/95] Added fmpz_mpoly_factor and fmpq_mpoly. --- src/flint/__init__.py | 1 + src/flint/flintlib/fmpq.pxd | 1 + src/flint/flintlib/fmpq_mpoly.pxd | 79 ++--- src/flint/flintlib/fmpz_mpoly_factor.pxd | 32 ++ src/flint/test/test.py | 6 +- src/flint/types/fmpq.pxd | 1 + src/flint/types/fmpq.pyx | 4 +- src/flint/types/fmpq_mpoly.pyx | 425 +++++++++++++++++++++-- src/flint/types/fmpz_mpoly.pyx | 56 ++- 9 files changed, 527 insertions(+), 78 deletions(-) create mode 100644 src/flint/flintlib/fmpz_mpoly_factor.pxd diff --git a/src/flint/__init__.py b/src/flint/__init__.py index 74b306a0..a63a969c 100644 --- a/src/flint/__init__.py +++ b/src/flint/__init__.py @@ -21,6 +21,7 @@ from .types.acb_mat import * from .types.acb_series import * from .types.fmpz_mpoly import * +from .types.fmpq_mpoly import * from .functions.showgood import showgood __version__ = '0.4.4' diff --git a/src/flint/flintlib/fmpq.pxd b/src/flint/flintlib/fmpq.pxd index 12a5d400..e03ddb1d 100644 --- a/src/flint/flintlib/fmpq.pxd +++ b/src/flint/flintlib/fmpq.pxd @@ -5,6 +5,7 @@ cdef extern from "flint/fmpq.h": ctypedef struct fmpq_struct: fmpz_struct num fmpz_struct den + ctypedef fmpq_struct fmpq_t[1] fmpz_struct * fmpq_numref(fmpq_t x) fmpz_struct * fmpq_denref(fmpq_t x) diff --git a/src/flint/flintlib/fmpq_mpoly.pxd b/src/flint/flintlib/fmpq_mpoly.pxd index e37e276d..766794de 100644 --- a/src/flint/flintlib/fmpq_mpoly.pxd +++ b/src/flint/flintlib/fmpq_mpoly.pxd @@ -1,3 +1,12 @@ +from flint.flintlib.flint cimport flint_rand_t, ulong, slong, mp_limb_t, flint_bitcnt_t, fmpz_struct +from flint.flintlib.fmpq_poly cimport fmpq_poly_struct, fmpq_poly_t +from flint.flintlib.fmpz cimport fmpz_t +from flint.flintlib.fmpq cimport fmpq_t, fmpq_struct +from flint.flintlib.mpoly cimport ordering_t +from flint.flintlib.fmpz_mpoly cimport fmpz_mpoly_ctx_t, fmpz_mpoly_t, fmpz_mpoly_struct + + +# unimported types {'fmpq_mpoly_univar_t'} cdef extern from "flint/fmpq_mpoly.h": ctypedef struct fmpq_mpoly_ctx_struct: @@ -46,7 +55,7 @@ cdef extern from "flint/fmpq_mpoly.h": int fmpq_mpoly_is_zero(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) int fmpq_mpoly_is_one(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) int fmpq_mpoly_degrees_fit_si(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) - void fmpq_mpoly_degrees_fmpz(fmpz_t ** degs, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_degrees_fmpz(fmpz_struct ** degs, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_degrees_si(slong * degs, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_degree_fmpz(fmpz_t deg, const fmpq_mpoly_t A, slong var, const fmpq_mpoly_ctx_t ctx) slong fmpq_mpoly_degree_si(const fmpq_mpoly_t A, slong var, const fmpq_mpoly_ctx_t ctx) @@ -57,15 +66,15 @@ cdef extern from "flint/fmpq_mpoly.h": void fmpq_mpoly_get_denominator(fmpz_t d, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_get_coeff_fmpq_monomial(fmpq_t c, const fmpq_mpoly_t A, const fmpq_mpoly_t M, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_set_coeff_fmpq_monomial(fmpq_mpoly_t A, const fmpq_t c, const fmpq_mpoly_t M, const fmpq_mpoly_ctx_t ctx) - void fmpq_mpoly_get_coeff_fmpq_fmpz(fmpq_t c, const fmpq_mpoly_t A, fmpz_t * const * exp, const fmpq_mpoly_ctx_t ctx) - void fmpq_mpoly_get_coeff_fmpq_ui(fmpq_t c, const fmpq_mpoly_t A, ulong const * exp, const fmpq_mpoly_ctx_t ctx) - void fmpq_mpoly_set_coeff_fmpq_fmpz(fmpq_mpoly_t A, const fmpq_t c, fmpz_t * const * exp, fmpq_mpoly_ctx_t ctx) - void fmpq_mpoly_set_coeff_fmpq_ui(fmpq_mpoly_t A, const fmpq_t c, ulong const * exp, fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_get_coeff_fmpq_fmpz(fmpq_t c, const fmpq_mpoly_t A, fmpz_struct * const * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_get_coeff_fmpq_ui(fmpq_t c, const fmpq_mpoly_t A, ulong * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_set_coeff_fmpq_fmpz(fmpq_mpoly_t A, const fmpq_t c, fmpz_struct * const * exp, fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_set_coeff_fmpq_ui(fmpq_mpoly_t A, const fmpq_t c, ulong * exp, fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_get_coeff_vars_ui(fmpq_mpoly_t C, const fmpq_mpoly_t A, const slong * vars, const ulong * exps, slong length, const fmpq_mpoly_ctx_t ctx) int fmpq_mpoly_cmp(const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) - fmpq * fmpq_mpoly_content_ref(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + fmpq_struct * fmpq_mpoly_content_ref(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) fmpz_mpoly_struct * fmpq_mpoly_zpoly_ref(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) - fmpz_t * fmpq_mpoly_zpoly_term_coeff_ref(fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) + fmpz_struct * fmpq_mpoly_zpoly_term_coeff_ref(fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) int fmpq_mpoly_is_canonical(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) slong fmpq_mpoly_length(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_resize(fmpq_mpoly_t A, slong new_length, const fmpq_mpoly_ctx_t ctx) @@ -78,14 +87,18 @@ cdef extern from "flint/fmpq_mpoly.h": void fmpq_mpoly_get_term_exp_si(slong * exps, const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) ulong fmpq_mpoly_get_term_var_exp_ui(const fmpq_mpoly_t A, slong i, slong var, const fmpq_mpoly_ctx_t ctx) slong fmpq_mpoly_get_term_var_exp_si(const fmpq_mpoly_t A, slong i, slong var, const fmpq_mpoly_ctx_t ctx) - void fmpq_mpoly_set_term_exp_fmpz(fmpq_mpoly_t A, slong i, fmpz_t * const * exps, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_set_term_exp_fmpz(fmpq_mpoly_t A, slong i, fmpz_struct * const * exps, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_set_term_exp_ui(fmpq_mpoly_t A, slong i, const ulong * exps, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_get_term(fmpq_mpoly_t M, const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_get_term_monomial(fmpq_mpoly_t M, const fmpq_mpoly_t A, slong i, const fmpq_mpoly_ctx_t ctx) - void fmpq_mpoly_push_term_fmpq_fmpz(fmpq_mpoly_t A, const fmpq_t c, fmpz_t * const * exp, const fmpq_mpoly_ctx_t ctx) - void fmpq_mpoly_push_term_fmpz_fmpz(fmpq_mpoly_t A, const fmpz_t c, fmpz_t * const * exp, const fmpq_mpoly_ctx_t ctx) - void fmpq_mpoly_push_term_ui_fmpz(fmpq_mpoly_t A, ulong c, fmpz_t * const * exp, const fmpq_mpoly_ctx_t ctx) - void fmpq_mpoly_push_term_si_fmpz(fmpq_mpoly_t A, slong c, fmpz_t * const * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_push_term_fmpq_fmpz(fmpq_mpoly_t A, const fmpq_t c, fmpz_struct * const * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_push_term_fmpq_ffmpz(fmpq_mpoly_t A, const fmpq_t c, const fmpz_struct * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_push_term_fmpz_fmpz(fmpq_mpoly_t A, const fmpz_t c, fmpz_struct * const * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_push_term_fmpz_ffmpz(fmpq_mpoly_t A, const fmpz_t c, const fmpz_struct * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_push_term_ui_fmpz(fmpq_mpoly_t A, ulong c, fmpz_struct * const * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_push_term_ui_ffmpz(fmpq_mpoly_t A, ulong c, const fmpz_struct * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_push_term_si_fmpz(fmpq_mpoly_t A, slong c, fmpz_struct * const * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_push_term_si_ffmpz(fmpq_mpoly_t A, slong c, const fmpz_struct * exp, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_push_term_fmpq_ui(fmpq_mpoly_t A, const fmpq_t c, const ulong * exp, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_push_term_fmpz_ui(fmpq_mpoly_t A, const fmpz_t c, const ulong * exp, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_push_term_ui_ui(fmpq_mpoly_t A, ulong c, const ulong * exp, const fmpq_mpoly_ctx_t ctx) @@ -119,7 +132,7 @@ cdef extern from "flint/fmpq_mpoly.h": void fmpq_mpoly_make_monic(fmpq_mpoly_t A, fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_derivative(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong var, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_integral(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong var, const fmpq_mpoly_ctx_t ctx) - int fmpq_mpoly_evaluate_all_fmpq(fmpq_t ev, const fmpq_mpoly_t A, fmpq * const * vals, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_evaluate_all_fmpq(fmpq_t ev, const fmpq_mpoly_t A, fmpq_struct * const * vals, const fmpq_mpoly_ctx_t ctx) int fmpq_mpoly_evaluate_one_fmpq(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong var, const fmpq_t val, const fmpq_mpoly_ctx_t ctx) int fmpq_mpoly_compose_fmpq_poly(fmpq_poly_t A, const fmpq_mpoly_t B, fmpq_poly_struct * const * C, const fmpq_mpoly_ctx_t ctxB) int fmpq_mpoly_compose_fmpq_mpoly(fmpq_mpoly_t A, const fmpq_mpoly_t B, fmpq_mpoly_struct * const * C, const fmpq_mpoly_ctx_t ctxB, const fmpq_mpoly_ctx_t ctxAC) @@ -145,33 +158,13 @@ cdef extern from "flint/fmpq_mpoly.h": int fmpq_mpoly_discriminant(fmpq_mpoly_t D, const fmpq_mpoly_t A, slong var, const fmpq_mpoly_ctx_t ctx) int fmpq_mpoly_sqrt(fmpq_mpoly_t Q, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) int fmpq_mpoly_is_square(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) - void fmpq_mpoly_univar_init(fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) - void fmpq_mpoly_univar_clear(fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) - void fmpq_mpoly_univar_swap(fmpq_mpoly_univar_t A, fmpq_mpoly_univar_t B, const fmpq_mpoly_ctx_t ctx) - void fmpq_mpoly_to_univar(fmpq_mpoly_univar_t A, const fmpq_mpoly_t B, slong var, const fmpq_mpoly_ctx_t ctx) - void fmpq_mpoly_from_univar(fmpq_mpoly_t A, const fmpq_mpoly_univar_t B, slong var, const fmpq_mpoly_ctx_t ctx) - int fmpq_mpoly_univar_degree_fits_si(const fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) - slong fmpq_mpoly_univar_length(const fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) - slong fmpq_mpoly_univar_get_term_exp_si(fmpq_mpoly_univar_t A, slong i, const fmpq_mpoly_ctx_t ctx) - void fmpq_mpoly_univar_get_term_coeff(fmpq_mpoly_t c, const fmpq_mpoly_univar_t A, slong i, const fmpq_mpoly_ctx_t ctx) - void fmpq_mpoly_univar_swap_term_coeff(fmpq_mpoly_t c, fmpq_mpoly_univar_t A, slong i, const fmpq_mpoly_ctx_t ctx) - -""" -cdef extern from "flint/fmpz_mpoly_factor.h": - - ctypedef struct fmpz_mpoly_factor_struct: - fmpz_t content - fmpz_mpoly_struct * poly - fmpz_struct * exp - slong length - slong alloc - - ctypedef fmpz_mpoly_factor_struct fmpz_mpoly_factor_t[1] - - - void fmpz_mpoly_factor_init(fmpz_mpoly_factor_t fac, const fmpz_mpoly_ctx_t ctx) - - void fmpz_mpoly_factor_clear(fmpz_mpoly_factor_t fac, const fmpz_mpoly_ctx_t ctx) - - int fmpz_mpoly_factor(fmpz_mpoly_factor_t fac, const fmpz_mpoly_t A, int full, const fmpz_mpoly_ctx_t ctx) -""" + # void fmpq_mpoly_univar_init(fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_univar_clear(fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_univar_swap(fmpq_mpoly_univar_t A, fmpq_mpoly_univar_t B, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_to_univar(fmpq_mpoly_univar_t A, const fmpq_mpoly_t B, slong var, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_from_univar(fmpq_mpoly_t A, const fmpq_mpoly_univar_t B, slong var, const fmpq_mpoly_ctx_t ctx) + # int fmpq_mpoly_univar_degree_fits_si(const fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) + # slong fmpq_mpoly_univar_length(const fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) + # slong fmpq_mpoly_univar_get_term_exp_si(fmpq_mpoly_univar_t A, slong i, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_univar_get_term_coeff(fmpq_mpoly_t c, const fmpq_mpoly_univar_t A, slong i, const fmpq_mpoly_ctx_t ctx) + # void fmpq_mpoly_univar_swap_term_coeff(fmpq_mpoly_t c, fmpq_mpoly_univar_t A, slong i, const fmpq_mpoly_ctx_t ctx) diff --git a/src/flint/flintlib/fmpz_mpoly_factor.pxd b/src/flint/flintlib/fmpz_mpoly_factor.pxd new file mode 100644 index 00000000..bba20a9b --- /dev/null +++ b/src/flint/flintlib/fmpz_mpoly_factor.pxd @@ -0,0 +1,32 @@ +from flint.flintlib.fmpz_mpoly cimport fmpz_mpoly_t, fmpz_mpoly_ctx_t, fmpz_mpoly_struct +from flint.flintlib.fmpz cimport fmpz_t +from flint.flintlib.flint cimport slong, fmpz_struct +from flint.flintlib.fmpq cimport fmpq_t + + +# unimported types set() + +cdef extern from "flint/fmpz_mpoly_factor.h": + + ctypedef struct fmpz_mpoly_factor_struct: + fmpz_t constant + fmpz_t constant_den + fmpz_mpoly_struct * poly + fmpz_struct * exp + slong num + slong alloc + + ctypedef fmpz_mpoly_factor_struct fmpz_mpoly_factor_t[1]; + + void fmpz_mpoly_factor_init(fmpz_mpoly_factor_t f, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_factor_clear(fmpz_mpoly_factor_t f, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_factor_swap(fmpz_mpoly_factor_t f, fmpz_mpoly_factor_t g, const fmpz_mpoly_ctx_t ctx) + slong fmpz_mpoly_factor_length(const fmpz_mpoly_factor_t f, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_factor_get_constant_fmpz(fmpz_t c, const fmpz_mpoly_factor_t f, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_factor_get_constant_fmpq(fmpq_t c, const fmpz_mpoly_factor_t f, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_factor_get_base(fmpz_mpoly_t B, const fmpz_mpoly_factor_t f, slong i, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_factor_swap_base(fmpz_mpoly_t B, fmpz_mpoly_factor_t f, slong i, const fmpz_mpoly_ctx_t ctx) + slong fmpz_mpoly_factor_get_exp_si(fmpz_mpoly_factor_t f, slong i, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_factor_sort(fmpz_mpoly_factor_t f, const fmpz_mpoly_ctx_t ctx) + int fmpz_mpoly_factor_squarefree(fmpz_mpoly_factor_t f, const fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) + int fmpz_mpoly_factor(fmpz_mpoly_factor_t f, const fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index e14bd8aa..5b1e3287 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -1175,8 +1175,8 @@ def test_fmpq_mpoly(): assert ztype(3) - p1 == ctx.fmpq_mpoly_from_dict({(1,0):-4,(0,3):-4,(2,4):-9,(0,0):3}) assert p1 * ztype(3) == ctx.fmpq_mpoly_from_dict({(1,0):12,(0,3):12,(2,4):27}) assert ztype(3) * p1 == ctx.fmpq_mpoly_from_dict({(1,0):12,(0,3):12,(2,4):27}) - assert p1 // ztype(3) == ctx.fmpq_mpoly_from_dict({(1,0):1,(0,3):1,(2,4):3}) - assert ztype(3) // p1 == Zp(0,ctx) + # assert p1 // ztype(3) == ctx.fmpq_mpoly_from_dict({(1,0):1,(0,3):1,(2,4):3}) + # assert ztype(3) // p1 == Zp(0,ctx) assert ctx.constant(7) + ztype(3) == Zp(10, ctx) def test_fmpq_series(): @@ -1666,7 +1666,7 @@ def test_pickling(): test_fmpq, test_fmpq_poly, test_fmpq_mat, - # test_fmpq_mpoly, + test_fmpq_mpoly, test_fmpq_series, test_nmod, test_nmod_poly, diff --git a/src/flint/types/fmpq.pxd b/src/flint/types/fmpq.pxd index 3ef00f3c..efe0931f 100644 --- a/src/flint/types/fmpq.pxd +++ b/src/flint/types/fmpq.pxd @@ -3,6 +3,7 @@ from flint.flint_base.flint_base cimport flint_scalar from flint.flintlib.fmpq cimport fmpq_t cdef any_as_fmpq(obj) + cdef class fmpq(flint_scalar): cdef fmpq_t val diff --git a/src/flint/types/fmpq.pyx b/src/flint/types/fmpq.pyx index 83c9c4b5..05dfd71e 100644 --- a/src/flint/types/fmpq.pyx +++ b/src/flint/types/fmpq.pyx @@ -4,8 +4,8 @@ from flint.types.fmpz cimport fmpz_set_any_ref from flint.types.fmpz cimport fmpz from flint.types.fmpz cimport any_as_fmpz -from flint.flintlib.flint cimport FMPZ_UNKNOWN, FMPZ_TMP -from flint.flintlib.fmpz cimport fmpz_set, fmpz_one +from flint.flintlib.flint cimport FMPZ_UNKNOWN, FMPZ_TMP, FMPZ_REF +from flint.flintlib.fmpz cimport fmpz_set, fmpz_one, fmpz_t from flint.flintlib.fmpz cimport fmpz_is_zero, fmpz_sgn from flint.flintlib.fmpz cimport fmpz_fdiv_q, fmpz_bits from flint.flintlib.fmpz cimport fmpz_cdiv_q diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 11922e15..d769b61b 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -1,4 +1,19 @@ +from cpython.dict cimport PyDict_Size, PyDict_Check, PyDict_Next +from cpython.tuple cimport PyTuple_Check, PyTuple_GET_SIZE +from flint.types.fmpq cimport any_as_fmpq, fmpq +from flint.types.fmpz cimport fmpz, fmpz_set_any_ref, any_as_fmpz +from flint.utils.typecheck cimport typecheck +from flint.utils.conversion cimport str_from_chars + +from flint.flintlib.flint cimport * +from flint.flintlib.fmpq cimport fmpq_init, fmpq_clear, fmpq_is_zero +from flint.flintlib.fmpz cimport fmpz_clear, fmpz_init +from flint.flintlib.fmpq_mpoly cimport * + + +cimport cython +cimport libc.stdlib cdef dict _fmpq_mpoly_ctx_cache = {} @@ -62,7 +77,7 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): """ cdef fmpq_mpoly res assert i >= 0 and i < self.val.zctx.minfo.nvars - res = fmpq_mpoly.__new__(fmpz_mpoly) + res = fmpq_mpoly.__new__(fmpq_mpoly) res.ctx = self fmpq_mpoly_init(res.val, res.ctx.val) res._init = True @@ -77,77 +92,92 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): z = any_as_fmpq(z) if z is NotImplemented: raise ValueError("A constant fmpq_mpoly is a fmpq") - res = fmpz_mpoly.__new__(fmpz_mpoly) + res = fmpq_mpoly.__new__(fmpq_mpoly) res.ctx = self - fmpz_mpoly_init(res.val, res.ctx.val) + fmpq_mpoly_init(res.val, res.ctx.val) res._init = True - fmpz_mpoly_set_fmpz(res.val, (z).val, res.ctx.val) + fmpq_mpoly_set_fmpq(res.val, (z).val, res.ctx.val) return res - def fmpz_mpoly_from_dict(self, d): + def fmpq_mpoly_from_dict(self, d): """ Create a fmpz_mpoly from a dictionary. The dictionary's keys are tuples of ints (or anything that implicitly converts - to fmpz) representing exponents, and corresponding values of fmpz. + to fmpz) representing exponents, and corresponding coefficient values of fmpq. >>> ctx = get_fmpz_mpoly_context(2,'lex','x,y') - >>> ctx.fmpz_mpoly_from_dict({(1,0):2, (1,1):3, (0,1):1}) + >>> ctx.fmpq_mpoly_from_dict({(1,0):2, (1,1):3, (0,1):1}) 3*x*y + 2*x + y """ cdef long n - cdef fmpz_t coefficient + cdef fmpq coefficient cdef fmpz_struct *exponents + cdef fmpz_struct **exp_ptr cdef int xtype cdef int nvars = self.nvars() cdef int i,j cdef int count - cdef fmpz_mpoly res + cdef fmpq_mpoly res if not PyDict_Check(d): raise ValueError("expected a dictionary") n = PyDict_Size(d) - fmpz_init(coefficient) exponents = libc.stdlib.calloc(nvars, sizeof(fmpz_struct)) if exponents == NULL: raise MemoryError() + exp_ptr = libc.stdlib.calloc(nvars, sizeof(fmpz_struct *)) + if exp_ptr == NULL: + libc.stdlib.free(exponents) + raise MemoryError() for i in range(nvars): fmpz_init(exponents + i) - fmpz_init(coefficient) - res = fmpz_mpoly.__new__(fmpz_mpoly) + exp_ptr[i] = exponents + i + # fmpq_init(coefficient) + res = fmpq_mpoly.__new__(fmpq_mpoly) res.ctx = self - fmpz_mpoly_init(res.val, res.ctx.val) + fmpq_mpoly_init(res.val, res.ctx.val) res._init = True count = 0 for k,v in d.items(): - xtype = fmpz_set_any_ref(coefficient, v) - if xtype == FMPZ_UNKNOWN: + coefficient = any_as_fmpq(v) + if coefficient == NotImplemented: + for i in range(nvars): + fmpz_clear(exponents + i) libc.stdlib.free(exponents) + libc.stdlib.free(exp_ptr) raise TypeError("invalid coefficient type %s" % type(v)) if not PyTuple_Check(k): + for i in range(nvars): + fmpz_clear(exponents + i) libc.stdlib.free(exponents) raise TypeError("Expected tuple of ints as key not %s" % type(k)) if PyTuple_GET_SIZE(k) != nvars: + for i in range(nvars): + fmpz_clear(exponents + i) libc.stdlib.free(exponents) raise TypeError("Expected exponent tuple of length %d" % nvars) for i,tup in enumerate(k): xtype = fmpz_set_any_ref(exponents + i, tup) if xtype == FMPZ_UNKNOWN: + for i in range(nvars): + fmpz_clear(exponents + i) libc.stdlib.free(exponents) raise TypeError("Invalid exponent type %s" % type(tup)) #Todo lobby for fmpz_mpoly_push_term_fmpz_ffmpz - if not fmpz_is_zero(coefficient): - _fmpz_mpoly_push_exp_ffmpz(res.val, exponents, self.val) - fmpz_mpoly_set_term_coeff_fmpz(res.val, count, coefficient, self.val) + if not fmpq_is_zero(coefficient.val): + fmpq_mpoly_push_term_fmpq_fmpz(res.val, coefficient.val, exp_ptr, self.val) + # _fmpq_mpoly_push_exp_ffmpz(res.val, exponents, self.val) + # fmpq_mpoly_set_term_coeff_fmpz(res.val, count, coefficient, self.val) count += 1 for i in range(nvars): fmpz_clear(exponents + i) - fmpz_clear(coefficient) - fmpz_mpoly_sort_terms(res.val, self.val) + fmpq_mpoly_sort_terms(res.val, self.val) + fmpq_mpoly_reduce(res.val, self.val) return res -def get_fmpz_mpoly_context(slong nvars=1, ordering="lex", names='x'): +def get_fmpq_mpoly_context(slong nvars=1, ordering="lex", names='x'): if nvars <= 0: nvars = 1 nametup = tuple(name.strip() for name in names.split(',')) @@ -156,8 +186,355 @@ def get_fmpz_mpoly_context(slong nvars=1, ordering="lex", names='x'): raise ValueError("Number of variables does not equal number of names") nametup = tuple(nametup[0] + str(i) for i in range(nvars)) key = (nvars, ordering, nametup) - ctx = _fmpz_mpoly_ctx_cache.get(key) + ctx = _fmpq_mpoly_ctx_cache.get(key) if ctx is None: - ctx = fmpz_mpoly_ctx(nvars, ordering, nametup) - _fmpz_mpoly_ctx_cache[key] = ctx + ctx = fmpq_mpoly_ctx(nvars, ordering, nametup) + _fmpq_mpoly_ctx_cache[key] = ctx return ctx + +cdef inline init_fmpq_mpoly(fmpq_mpoly var, fmpq_mpoly_ctx ctx): + var.ctx = ctx + fmpq_mpoly_init(var.val, ctx.val) + var._init = True + +cdef inline create_fmpq_mpoly(fmpq_mpoly_ctx ctx): + cdef fmpq_mpoly var + var = fmpq_mpoly.__new__(fmpq_mpoly) + var.ctx = ctx + fmpq_mpoly_init(var.val, ctx.val) + var._init = True + return var + + + + +cdef class fmpq_mpoly(flint_mpoly): + """ + The *fmpz_poly* type represents sparse multivariate polynomials over + the integers. + """ + + # cdef fmpz_mpoly_t val + # cdef fmpz_mpoly_ctx ctx + # cdef bint _init + + def __cinit__(self): + self._init = False + + def __dealloc__(self): + if self._init: + fmpq_mpoly_clear(self.val, self.ctx.val) + self._init = False + + def __init__(self, val=0, ctx=None): + if typecheck(val, fmpq_mpoly): + if ctx is None or ctx == (val).ctx: + init_fmpq_mpoly(self, (val).ctx) + fmpq_mpoly_set(self.val, (val).val, self.ctx.val) + else: + raise ValueError("Cannot automatically coerce contexts") + elif isinstance(val, dict): + if ctx is None: + if len(val) == 0: + raise ValueError("Need context for zero polynomial") + k = list(val.keys())[0] + if not isinstance(k, tuple): + raise ValueError("Dict should be keyed with tuples of integers") + ctx = get_fmpq_mpoly_context(len(k)) + x = ctx.fmpq_mpoly_from_dict(val) + #XXX this copy is silly, have a ctx function that assigns an fmpz_mpoly_t + init_fmpq_mpoly(self, ctx) + fmpq_mpoly_set(self.val, (x).val, self.ctx.val) + elif isinstance(val, str): + if ctx is None: + raise ValueError("Cannot parse a polynomial without context") + val = bytes(val, 'utf-8') + init_fmpq_mpoly(self, ctx) + fmpq_mpoly_set_str_pretty(self.val, val, self.ctx.c_names, self.ctx.val) + fmpq_mpoly_sort_terms(self.val, self.ctx.val) + else: + v = any_as_fmpz(val) + if v is NotImplemented: + raise TypeError("cannot create fmpz_mpoly from type %s" % type(val)) + if ctx is None: + raise ValueError("Need context to convert fmpz to fmpq_mpoly") + init_fmpq_mpoly(self, ctx) + fmpq_mpoly_set_fmpz(self.val, (v).val, self.ctx.val) + + def __nonzero__(self): + return not fmpq_mpoly_is_zero(self.val, self.ctx.val) + + def __bool__(self): + return not fmpq_mpoly_is_zero(self.val, self.ctx.val) + + def is_one(self): + return fmpq_mpoly_is_one(self.val, self.ctx.val) + + def __richcmp__(self, other, int op): + if op != 2 and op != 3: + return NotImplemented + if typecheck(self, fmpq_mpoly) and typecheck(other, fmpq_mpoly): + if (self).ctx is (other).ctx: + if op == 2: + return bool(fmpq_mpoly_equal((self).val, (other).val, (self).ctx.val)) + else: + return not bool(fmpq_mpoly_equal((self).val, (other).val, (self).ctx.val)) + else: + if op == 2: + return False + else: + return True + if op == 2: + return not bool(self - other) + else: + return bool(self - other) + + def __len__(self): + return fmpq_mpoly_length(self.val, self.ctx.val) + + def coefficient(self, slong i): + cdef fmpq v + if i < 0 or i >= fmpq_mpoly_length(self.val, self.ctx.val): + return fmpq(0) + else: + v = fmpq.__new__(fmpz) + fmpq_mpoly_get_term_coeff_fmpq(v.val, self.val, i, self.ctx.val) + return v + + def exponent_tuple(self, slong i): + cdef slong j, nvars + cdef fmpz_struct ** tmp + if i < 0 or i >= fmpq_mpoly_length(self.val, self.ctx.val): + raise ValueError + nvars = self.ctx.nvars() + res = tuple(fmpz() for j in range(nvars)) + tmp = libc.stdlib.malloc(nvars * sizeof(fmpz_struct **)) + try: + for j in range(nvars): + tmp[j] = &(( (res[j])).val[0]) + fmpq_mpoly_get_term_exp_fmpz(tmp, self.val, i, self.ctx.val) + finally: + libc.stdlib.free(tmp) + return res + + def repr(self): + return self.str() + " (nvars=%s, ordering=%s names=%s)" % (self.ctx.nvars(), self.ctx.ordering(), self.ctx.py_names) + + def str(self): + cdef char * s = fmpq_mpoly_get_str_pretty(self.val, self.ctx.c_names, self.ctx.val) + try: + res = str_from_chars(s) + finally: + libc.stdlib.free(s) + res = res.replace("+", " + ") + res = res.replace("-", " - ") + if res.startswith(" - "): + res = "-" + res[3:] + return res + + def __neg__(self): + cdef fmpq_mpoly res + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_neg(res.val, (self).val, res.ctx.val) + return res + + def __add__(self, other): + cdef fmpq_mpoly res + if typecheck(other, fmpq_mpoly): + if (self).ctx is not (other).ctx: + return NotImplemented + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_add(res.val, (self).val, (other).val, res.ctx.val) + return res + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_add_fmpq(res.val, (self).val, (other).val, res.ctx.val) + return res + return NotImplemented + + def __radd__(self, other): + cdef fmpq_mpoly res + other = any_as_fmpq(other) + if other is not NotImplemented: + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_add_fmpq(res.val, (self).val, (other).val, res.ctx.val) + return res + return NotImplemented + + def __iadd__(self, other): + if typecheck(other, fmpq_mpoly): + if (self).ctx is not (other).ctx: + return NotImplemented + fmpq_mpoly_add((self).val, (self).val, (other).val, self.ctx.val) + return self + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + fmpq_mpoly_add_fmpq((self).val, (self).val, (other).val, self.ctx.val) + return self + return NotImplemented + + def __sub__(self, other): + cdef fmpq_mpoly res + if typecheck(other, fmpq_mpoly): + if (self).ctx is not (other).ctx: + return NotImplemented + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_sub(res.val, (self).val, (other).val, res.ctx.val) + return res + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_sub_fmpq(res.val, (self).val, (other).val, res.ctx.val) + return res + return NotImplemented + + def __rsub__(self, other): + cdef fmpq_mpoly res + other = any_as_fmpq(other) + if other is not NotImplemented: + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_sub_fmpq(res.val, (self).val, (other).val, res.ctx.val) + return -res + return NotImplemented + + def __isub__(self, other): + if typecheck(other, fmpq_mpoly): + if (self).ctx is not (other).ctx: + return NotImplemented + fmpq_mpoly_sub((self).val, (self).val, (other).val, self.ctx.val) + return self + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + fmpq_mpoly_sub_fmpq((self).val, (self).val, (other).val, self.ctx.val) + return self + return NotImplemented + + def __mul__(self, other): + cdef fmpq_mpoly res + if typecheck(other, fmpq_mpoly): + if (self).ctx is not (other).ctx: + return NotImplemented + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_mul(res.val, (self).val, (other).val, res.ctx.val) + return res + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_scalar_mul_fmpq(res.val, (self).val, (other).val, res.ctx.val) + return res + return NotImplemented + + def __rmul__(self, other): + cdef fmpq_mpoly res + other = any_as_fmpq(other) + if other is not NotImplemented: + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_scalar_mul_fmpq(res.val, (self).val, (other).val, res.ctx.val) + return res + return NotImplemented + + def __imul__(self, other): + if typecheck(other, fmpq_mpoly): + if (self).ctx is not (other).ctx: + return NotImplemented + fmpq_mpoly_mul((self).val, (self).val, (other).val, self.ctx.val) + return self + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + fmpq_mpoly_scalar_mul_fmpq(self.val, (self).val, (other).val, self.ctx.val) + return self + return NotImplemented + + def __pow__(self, other, modulus): + cdef fmpq_mpoly res + if modulus is not None: + raise NotImplementedError + other = any_as_fmpz(other) + if other is NotImplemented: + return other + if other < 0: + raise ValueError("cannot raise fmpz_mpoly to negative power") + res = create_fmpq_mpoly(self.ctx) + if fmpq_mpoly_pow_fmpz(res.val, (self).val, (other).val, res.ctx.val) == 0: + raise ValueError("unreasonably large polynomial") + return res + + def __divmod__(self, other): + cdef fmpq_mpoly res, res2 + if typecheck(other, fmpq_mpoly): + if (self).ctx is not (other).ctx: + return NotImplemented + res = create_fmpq_mpoly(self.ctx) + res2 = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) + return (res, res2) + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + other= fmpq_mpoly(other, self.ctx) + res = create_fmpq_mpoly(self.ctx) + res2 = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) + return (res, res2) + return NotImplemented + + def __rdivmod__(self, other): + cdef fmpq_mpoly res, res2 + other = any_as_fmpq(other) + if other is not NotImplemented: + other = fmpq_mpoly(other, self.ctx) + res = create_fmpq_mpoly(self.ctx) + res2 = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_divrem(res.val, res2.val, (other).val, (self).val, res.ctx.val) + return res + return NotImplemented + + def __floordiv__(self, other): + cdef fmpq_mpoly res + if typecheck(other, fmpq_mpoly): + if (self).ctx is not (other).ctx: + return NotImplemented + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) + return res + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + other = fmpq_mpoly(other, self.ctx) + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) + return res + return NotImplemented + + def __rfloordiv__(self,other): + cdef fmpq_mpoly res + other = any_as_fmpq(other) + if other is not NotImplemented: + other = fmpq_mpoly(other, self.ctx) + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_div(res.val, (other).val, self.val, res.ctx.val) + return res + return NotImplemented + + + def __mod__(self, other): + return divmod(self, other)[1] + + def gcd(self, other): + cdef fmpq_mpoly res + assert isinstance(other, fmpq_mpoly) + if (self).ctx is not (other).ctx: + return NotImplemented + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_gcd(res.val, (self).val, (other).val, res.ctx.val) + return res + + + diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 7958f584..04084543 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -14,7 +14,9 @@ from flint.types.fmpz cimport fmpz, fmpz_set_any_ref cimport cython cimport libc.stdlib +from flint.flintlib.fmpz cimport fmpz_set from flint.flintlib.fmpz_mpoly cimport * +from flint.flintlib.fmpz_mpoly_factor cimport * cdef any_as_fmpz_mpoly(x): cdef fmpz_mpoly res @@ -642,22 +644,30 @@ cdef class fmpz_mpoly(flint_mpoly): # finally: # libc.stdlib.free(C) - ''' def factor(self): """ Factors self into irreducible factors, returning a tuple (c, factors) where c is the content of the coefficients and factors is a list of (poly, exp) pairs. + >>> Zm = fmpz_mpoly + >>> ctx = get_fmpz_mpoly_context(3, 'lex', 'x,y,z') + >>> 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)]) + >>> (p2 * p1 * p2).factor() + (18, [(z + 1, 2), (x + 2, 1), (x + 1, 2)]) + (18, [(x + 1, 1), (x*y + z + 1, 2)]) """ cdef fmpz_mpoly_factor_t fac cdef int i cdef fmpz c cdef fmpz_mpoly u fmpz_mpoly_factor_init(fac, self.ctx.val) - fmpz_mpoly_factor(fac, self.val, 1, self.ctx.val) - res = [0] * fac.length - for 0 <= i < fac.length: + fmpz_mpoly_factor(fac, self.val, self.ctx.val) + res = [0] * fac.num + for 0 <= i < fac.num: u = fmpz_mpoly.__new__(fmpz_mpoly) u.ctx = self.ctx fmpz_mpoly_init(u.val, u.ctx.val) @@ -667,8 +677,42 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_set((c).val, &fac.exp[i]) res[i] = (u, c) c = fmpz.__new__(fmpz) - fmpz_set((c).val, fac.content) # should be & with ... + fmpz_set((c).val, fac.constant) fmpz_mpoly_factor_clear(fac, self.ctx.val) return c, res - ''' + def factor_squarefree(self): + """ + Factors self into irreducible factors, returning a tuple + (c, factors) where c is the content of the coefficients and + factors is a list of (poly, exp) pairs. + + >>> Zm = fmpz_mpoly + >>> ctx = get_fmpz_mpoly_context(3, 'lex', 'x,y,z') + >>> p1 = Zm("2*x + 4", ctx) + >>> p2 = Zm("3*x*y + 3*x + 3*y + 3", ctx) + >>> (p1 * p2).factor_squarefree() + (6, [(x + 2, 1), (x*y + x + y + 1, 1)]) + >>> (p1 * p2 * p1).factor_squarefree() + (18, [(x + 2, 2), (x*y + x + y + 1, 1)]) + """ + cdef fmpz_mpoly_factor_t fac + cdef int i + cdef fmpz c + cdef fmpz_mpoly u + fmpz_mpoly_factor_init(fac, self.ctx.val) + fmpz_mpoly_factor_squarefree(fac, self.val, self.ctx.val) + res = [0] * fac.num + for 0 <= i < fac.num: + u = fmpz_mpoly.__new__(fmpz_mpoly) + u.ctx = self.ctx + fmpz_mpoly_init(u.val, u.ctx.val) + u._init = True + fmpz_mpoly_set((u).val, &fac.poly[i], self.ctx.val) + c = fmpz.__new__(fmpz) + fmpz_set((c).val, &fac.exp[i]) + res[i] = (u, c) + c = fmpz.__new__(fmpz) + fmpz_set((c).val, fac.constant) + fmpz_mpoly_factor_clear(fac, self.ctx.val) + return c, res From 7f9c1932634a756f82efb366a79299a075d3e836 Mon Sep 17 00:00:00 2001 From: David Einstein Date: Fri, 22 Sep 2023 11:53:11 -0400 Subject: [PATCH 15/95] Added fmpq_mpoly to setup.py Forgot to add it to last commit --- setup.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index d8b1c043..b94b4d94 100644 --- a/setup.py +++ b/setup.py @@ -94,6 +94,7 @@ ("flint.types.acb_mat", ["src/flint/types/acb_mat.pyx"]), ("flint.types.acb_series", ["src/flint/types/acb_series.pyx"]), ("flint.types.fmpz_mpoly", ["src/flint/types/fmpz_mpoly.pyx"]), + ("flint.types.fmpq_mpoly", ["src/flint/types/fmpq_mpoly.pyx"]), ("flint.types.dirichlet", ["src/flint/types/dirichlet.pyx"]), ("flint.flint_base.flint_base", ["src/flint/flint_base/flint_base.pyx"]), ("flint.flint_base.flint_context", ["src/flint/flint_base/flint_context.pyx"]), @@ -104,8 +105,8 @@ ext_options = { "libraries" : libraries, - "library_dirs" : default_lib_dirs + ["/Users/davideinstein/opt/flint2.9/lib", "/Users/davideinstein/opt/arb/lib"], - "include_dirs" : default_include_dirs + ["/Users/davideinstein/opt/flint2.9/include", "/Users/davideinstein/opt/arb/include"], + "library_dirs" : default_lib_dirs, + "include_dirs" : default_include_dirs, "define_macros" : define_macros, } From ab7e1144c91e3a29cf5b31b4862250d65c863460 Mon Sep 17 00:00:00 2001 From: David Einstein Date: Sat, 23 Sep 2023 22:19:49 -0400 Subject: [PATCH 16/95] Added factoring to fmpq_mpoly Also respliced fmpz_mpoly and fmpq_mpoly back into doctests. --- src/flint/flintlib/fmpq_mpoly_factor.pxd | 27 ++++++ src/flint/test/__main__.py | 2 + src/flint/types/fmpq_mpoly.pyx | 108 ++++++++++++++++++++--- src/flint/types/fmpz_mpoly.pyx | 5 +- 4 files changed, 125 insertions(+), 17 deletions(-) create mode 100644 src/flint/flintlib/fmpq_mpoly_factor.pxd diff --git a/src/flint/flintlib/fmpq_mpoly_factor.pxd b/src/flint/flintlib/fmpq_mpoly_factor.pxd new file mode 100644 index 00000000..3336acf6 --- /dev/null +++ b/src/flint/flintlib/fmpq_mpoly_factor.pxd @@ -0,0 +1,27 @@ +from flint.flintlib.fmpq cimport fmpq_t +from flint.flintlib.fmpz cimport fmpz_t +from flint.flintlib.fmpq_mpoly cimport fmpq_mpoly_ctx_t, fmpq_mpoly_t, fmpq_mpoly_struct +from flint.flintlib.flint cimport slong, fmpz_struct + +cdef extern from "flint/fmpq_mpoly_factor.h": + ctypedef struct fmpq_mpoly_factor_struct: + fmpq_t constant + fmpq_mpoly_struct * poly + fmpz_struct * exp + slong num + slong alloc + ctypedef fmpq_mpoly_factor_struct fmpq_mpoly_factor_t[1] + + void fmpq_mpoly_factor_init(fmpq_mpoly_factor_t f, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_factor_clear(fmpq_mpoly_factor_t f, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_factor_swap(fmpq_mpoly_factor_t f, fmpq_mpoly_factor_t g, const fmpq_mpoly_ctx_t ctx) + slong fmpq_mpoly_factor_length(const fmpq_mpoly_factor_t f, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_factor_get_constant_fmpq(fmpq_t c, const fmpq_mpoly_factor_t f, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_factor_get_base(fmpq_mpoly_t B, const fmpq_mpoly_factor_t f, slong i, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_factor_swap_base(fmpq_mpoly_t B, fmpq_mpoly_factor_t f, slong i, const fmpq_mpoly_ctx_t ctx) + slong fmpq_mpoly_factor_get_exp_si(fmpq_mpoly_factor_t f, slong i, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_factor_sort(fmpq_mpoly_factor_t f, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_factor_make_monic(fmpq_mpoly_factor_t f, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_factor_make_integral(fmpq_mpoly_factor_t f, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_factor_squarefree(fmpq_mpoly_factor_t f, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_factor(fmpq_mpoly_factor_t f, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) diff --git a/src/flint/test/__main__.py b/src/flint/test/__main__.py index 542d6739..c1466e1f 100644 --- a/src/flint/test/__main__.py +++ b/src/flint/test/__main__.py @@ -55,10 +55,12 @@ def run_doctests(verbose=None): flint.types.fmpz, flint.types.fmpz_poly, flint.types.fmpz_mat, + flint.types.fmpz_mpoly, flint.types.fmpz_series, flint.types.fmpq, flint.types.fmpq_poly, flint.types.fmpq_mat, + flint.types.fmpq_mpoly, flint.types.fmpq_series, flint.types.nmod, flint.types.nmod_poly, diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index d769b61b..712346a3 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -1,15 +1,22 @@ from cpython.dict cimport PyDict_Size, PyDict_Check, PyDict_Next from cpython.tuple cimport PyTuple_Check, PyTuple_GET_SIZE -from flint.types.fmpq cimport any_as_fmpq, fmpq +from flint.types.fmpq cimport any_as_fmpq, fmpq, fmpq_set_any_ref from flint.types.fmpz cimport fmpz, fmpz_set_any_ref, any_as_fmpz from flint.utils.typecheck cimport typecheck from flint.utils.conversion cimport str_from_chars from flint.flintlib.flint cimport * -from flint.flintlib.fmpq cimport fmpq_init, fmpq_clear, fmpq_is_zero -from flint.flintlib.fmpz cimport fmpz_clear, fmpz_init +from flint.flintlib.fmpq cimport fmpq_init, fmpq_clear, fmpq_is_zero, fmpq_set +from flint.flintlib.fmpz cimport fmpz_clear, fmpz_init, fmpz_set from flint.flintlib.fmpq_mpoly cimport * +from flint.flintlib.fmpq_mpoly_factor cimport * + +cdef extern from *: + """ + /* An ugly hack to get around the ugly hack of renaming fmpq to avoid a c/python name collision */ + typedef fmpq fmpq_struct; + """ cimport cython @@ -46,7 +53,7 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): """ Return the number of variables in the context - >>> ctx = get_fmpz_mpoly_context(4, "lex", 'x') + >>> ctx = get_fmpq_mpoly_context(4, "lex", 'x') >>> ctx.nvars() 4 """ @@ -56,7 +63,7 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): """ Return the term order of the context object. - >>> ctx = get_fmpz_mpoly_context(4, "deglex", 'w') + >>> ctx = get_fmpq_mpoly_context(4, "deglex", 'w') >>> ctx.ordering() 'deglex' """ @@ -71,7 +78,7 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): """ Return the `i`th generator of the polynomial ring - >>> ctx = get_fmpz_mpoly_context(3, 'degrevlex', 'z') + >>> ctx = get_fmpq_mpoly_context(3, 'degrevlex', 'z') >>> ctx.gen(1) z1 """ @@ -106,15 +113,15 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): The dictionary's keys are tuples of ints (or anything that implicitly converts to fmpz) representing exponents, and corresponding coefficient values of fmpq. - >>> ctx = get_fmpz_mpoly_context(2,'lex','x,y') + >>> ctx = get_fmpq_mpoly_context(2,'lex','x,y') >>> ctx.fmpq_mpoly_from_dict({(1,0):2, (1,1):3, (0,1):1}) - 3*x*y + 2*x + y + 3*x*y + 2*x + y """ cdef long n - cdef fmpq coefficient + cdef fmpq_t coefficient + cdef int xtype cdef fmpz_struct *exponents cdef fmpz_struct **exp_ptr - cdef int xtype cdef int nvars = self.nvars() cdef int i,j cdef int count @@ -140,8 +147,8 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): res._init = True count = 0 for k,v in d.items(): - coefficient = any_as_fmpq(v) - if coefficient == NotImplemented: + xtype = fmpq_set_any_ref(coefficient, v) + if xtype == FMPZ_UNKNOWN: for i in range(nvars): fmpz_clear(exponents + i) libc.stdlib.free(exponents) @@ -165,8 +172,8 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): libc.stdlib.free(exponents) raise TypeError("Invalid exponent type %s" % type(tup)) #Todo lobby for fmpz_mpoly_push_term_fmpz_ffmpz - if not fmpq_is_zero(coefficient.val): - fmpq_mpoly_push_term_fmpq_fmpz(res.val, coefficient.val, exp_ptr, self.val) + if not fmpq_is_zero(coefficient): + fmpq_mpoly_push_term_fmpq_fmpz(res.val, coefficient, exp_ptr, self.val) # _fmpq_mpoly_push_exp_ffmpz(res.val, exponents, self.val) # fmpq_mpoly_set_term_coeff_fmpz(res.val, count, coefficient, self.val) count += 1 @@ -538,3 +545,76 @@ cdef class fmpq_mpoly(flint_mpoly): + def factor(self): + """ + Factors self into irreducible factors, returning a tuple + (c, factors) where c is the content of the coefficients and + factors is a list of (poly, exp) pairs. + + >>> Zm = fmpq_mpoly + >>> ctx = get_fmpq_mpoly_context(3, 'lex', 'x,y,z') + >>> 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)]) + >>> (p2 * p1 * p2).factor() + (18, [(z + 1, 2), (x + 2, 1), (x + 1, 2)]) + """ + cdef fmpq_mpoly_factor_t fac + cdef int i + cdef fmpq c + cdef fmpz exp + cdef fmpq_mpoly u + fmpq_mpoly_factor_init(fac, self.ctx.val) + fmpq_mpoly_factor(fac, self.val, self.ctx.val) + res = [0] * fac.num + for i in range(fac.num): + u = fmpq_mpoly.__new__(fmpq_mpoly) + u.ctx = self.ctx + fmpq_mpoly_init(u.val, u.ctx.val) + u._init = True + fmpq_mpoly_set((u).val, &fac.poly[i], self.ctx.val) + exp = fmpz.__new__(fmpz) + fmpz_set((exp).val, fac.exp + i) + res[i] = (u, exp) + c = fmpq.__new__(fmpq) + fmpq_set((c).val, fac.constant) + fmpq_mpoly_factor_clear(fac, self.ctx.val) + return c, res + + def factor_squarefree(self): + """ + Factors self into irreducible factors, returning a tuple + (c, factors) where c is the content of the coefficients and + factors is a list of (poly, exp) pairs. + + >>> Zm = fmpq_mpoly + >>> ctx = get_fmpq_mpoly_context(3, 'lex', 'x,y,z') + >>> p1 = Zm("2*x + 4", ctx) + >>> p2 = Zm("3*x*y + 3*x + 3*y + 3", ctx) + >>> (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)]) + """ + cdef fmpq_mpoly_factor_t fac + cdef int i + cdef fmpq c + cdef fmpz exp + cdef fmpq_mpoly u + fmpq_mpoly_factor_init(fac, self.ctx.val) + fmpq_mpoly_factor_squarefree(fac, self.val, self.ctx.val) + res = [0] * fac.num + for 0 <= i < fac.num: + u = fmpq_mpoly.__new__(fmpq_mpoly) + u.ctx = self.ctx + fmpq_mpoly_init(u.val, u.ctx.val) + u._init = True + fmpq_mpoly_set((u).val, &fac.poly[i], self.ctx.val) + exp = fmpz.__new__(fmpz) + fmpz_set((exp).val, fac.exp + i) + res[i] = (u, exp) + c = fmpq.__new__(fmpq) + fmpq_set((c).val, fac.constant) + fmpq_mpoly_factor_clear(fac, self.ctx.val) + return c, res diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 04084543..682b2428 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -658,7 +658,6 @@ cdef class fmpz_mpoly(flint_mpoly): (6, [(z + 1, 1), (x + 2, 1), (x + 1, 1)]) >>> (p2 * p1 * p2).factor() (18, [(z + 1, 2), (x + 2, 1), (x + 1, 2)]) - (18, [(x + 1, 1), (x*y + z + 1, 2)]) """ cdef fmpz_mpoly_factor_t fac cdef int i @@ -692,9 +691,9 @@ cdef class fmpz_mpoly(flint_mpoly): >>> p1 = Zm("2*x + 4", ctx) >>> p2 = Zm("3*x*y + 3*x + 3*y + 3", ctx) >>> (p1 * p2).factor_squarefree() - (6, [(x + 2, 1), (x*y + x + y + 1, 1)]) + (6, [(y + 1, 1), (x^2 + 3*x + 2, 1)]) >>> (p1 * p2 * p1).factor_squarefree() - (18, [(x + 2, 2), (x*y + x + y + 1, 1)]) + (12, [(y + 1, 1), (x + 1, 1), (x + 2, 2)]) """ cdef fmpz_mpoly_factor_t fac cdef int i From 2ebf15d18853d0f8e3c26f449553847263fcfa3e Mon Sep 17 00:00:00 2001 From: David Einstein Date: Sun, 24 Sep 2023 10:54:51 -0400 Subject: [PATCH 17/95] Framing of fmpz_mpoly_q laid out. Added submodule to setup Added rational function class to flint_base Added interface to flintlib Added skeleton fmpz_mpoly_q to types --- setup.py | 1 + src/flint/flint_base/flint_base.pxd | 3 ++ src/flint/flint_base/flint_base.pyx | 4 +++ src/flint/flintlib/fmpz_mpoly_q.pxd | 53 +++++++++++++++++++++++++++++ src/flint/types/fmpz_mpoly_q.pxd | 9 +++++ src/flint/types/fmpz_mpoly_q.pyx | 35 +++++++++++++++++++ 6 files changed, 105 insertions(+) create mode 100644 src/flint/flintlib/fmpz_mpoly_q.pxd create mode 100644 src/flint/types/fmpz_mpoly_q.pxd create mode 100644 src/flint/types/fmpz_mpoly_q.pyx diff --git a/setup.py b/setup.py index f84964c9..97ea040c 100644 --- a/setup.py +++ b/setup.py @@ -95,6 +95,7 @@ ("flint.types.acb_series", ["src/flint/types/acb_series.pyx"]), ("flint.types.fmpz_mpoly", ["src/flint/types/fmpz_mpoly.pyx"]), ("flint.types.fmpq_mpoly", ["src/flint/types/fmpq_mpoly.pyx"]), + ("flint.types.fmpz_mpoly_q", ["src/flint/types/fmpz_mpoly_q.pyx"]), ("flint.types.fmpz_mod", ["src/flint/types/fmpz_mod.pyx"]), ("flint.types.dirichlet", ["src/flint/types/dirichlet.pyx"]), ("flint.flint_base.flint_base", ["src/flint/flint_base/flint_base.pyx"]), diff --git a/src/flint/flint_base/flint_base.pxd b/src/flint/flint_base/flint_base.pxd index f6b0f7e8..6cbb465b 100644 --- a/src/flint/flint_base/flint_base.pxd +++ b/src/flint/flint_base/flint_base.pxd @@ -20,3 +20,6 @@ cdef class flint_mat(flint_elem): cdef class flint_series(flint_elem): pass + +cdef class flint_rational_function(flint_elem): + pass diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index 29096292..7fb47a12 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -218,3 +218,7 @@ cdef class flint_mat(flint_elem): # supports mpmath conversions tolist = table + + +cdef class flint_rational_function(flint_elem): + pass diff --git a/src/flint/flintlib/fmpz_mpoly_q.pxd b/src/flint/flintlib/fmpz_mpoly_q.pxd new file mode 100644 index 00000000..87605c52 --- /dev/null +++ b/src/flint/flintlib/fmpz_mpoly_q.pxd @@ -0,0 +1,53 @@ +from flint.flintlib.flint cimport mp_limb_t, flint_rand_t, slong +from flint.flintlib.fmpq cimport fmpq_t +from flint.flintlib.fmpz cimport fmpz_t +from flint.flintlib.fmpz_mpoly cimport fmpz_mpoly_ctx_t, fmpz_mpoly_t, fmpz_mpoly_struct + +cdef extern from "flint/fmpz_mpoly_q.h": + ctypedef struct fmpz_mpoly_q_struct: + fmpz_mpoly_struct num + fmpz_mpoly_struct num + ctypedef fmpz_mpoly_q_struct fmpz_mpoly_q_t[1] + + fmpz_mpoly_struct * fmpz_mpoly_q_numref(fmpz_mpoly_q_t x) + fmpz_mpoly_struct * fmpz_mpoly_q_denref(fmpz_mpoly_q_t x) + void fmpz_mpoly_q_init(fmpz_mpoly_q_t res, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_clear(fmpz_mpoly_q_t res, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_swap(fmpz_mpoly_q_t x, fmpz_mpoly_q_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_set(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_set_fmpq(fmpz_mpoly_q_t res, const fmpq_t x, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_set_fmpz(fmpz_mpoly_q_t res, const fmpz_t x, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_set_si(fmpz_mpoly_q_t res, slong x, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_canonicalise(fmpz_mpoly_q_t x, const fmpz_mpoly_ctx_t ctx) + int fmpz_mpoly_q_is_canonical(const fmpz_mpoly_q_t x, const fmpz_mpoly_ctx_t ctx) + int fmpz_mpoly_q_is_zero(const fmpz_mpoly_q_t x, const fmpz_mpoly_ctx_t ctx) + int fmpz_mpoly_q_is_one(const fmpz_mpoly_q_t x, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_used_vars(int * used, const fmpz_mpoly_q_t f, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_used_vars_num(int * used, const fmpz_mpoly_q_t f, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_used_vars_den(int * used, const fmpz_mpoly_q_t f, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_zero(fmpz_mpoly_q_t res, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_one(fmpz_mpoly_q_t res, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_gen(fmpz_mpoly_q_t res, slong i, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_print_pretty(const fmpz_mpoly_q_t f, const char ** x, fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_randtest(fmpz_mpoly_q_t res, flint_rand_t state, slong length, mp_limb_t coeff_bits, slong exp_bound, const fmpz_mpoly_ctx_t ctx) + int fmpz_mpoly_q_equal(const fmpz_mpoly_q_t x, const fmpz_mpoly_q_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_neg(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_add(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpz_mpoly_q_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_add_fmpq(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpq_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_add_fmpz(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpz_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_add_si(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, slong y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_sub(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpz_mpoly_q_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_sub_fmpq(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpq_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_sub_fmpz(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpz_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_sub_si(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, slong y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_mul(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpz_mpoly_q_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_mul_fmpq(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpq_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_mul_fmpz(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpz_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_mul_si(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, slong y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_div(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpz_mpoly_q_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_div_fmpq(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpq_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_div_fmpz(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpz_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_div_si(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, slong y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_inv(fmpz_mpoly_q_t res, const fmpz_mpoly_q_t x, const fmpz_mpoly_ctx_t ctx) + void _fmpz_mpoly_q_content(fmpz_t num, fmpz_t den, const fmpz_mpoly_t xnum, const fmpz_mpoly_t xden, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_q_content(fmpq_t res, const fmpz_mpoly_q_t x, const fmpz_mpoly_ctx_t ctx) diff --git a/src/flint/types/fmpz_mpoly_q.pxd b/src/flint/types/fmpz_mpoly_q.pxd new file mode 100644 index 00000000..c80a7fde --- /dev/null +++ b/src/flint/types/fmpz_mpoly_q.pxd @@ -0,0 +1,9 @@ +from flint.flint_base.flint_base cimport flint_rational_function +from flint.flintlib.fmpz_mpoly_q cimport fmpz_mpoly_q_t + +from flint.types.fmpz_mpoly cimport fmpz_mpoly, fmpz_mpoly_ctx + +cdef class fmpz_mpoly_q(flint_rational_function): + cdef fmpz_mpoly_q_t fraction + cdef fmpz_mpoly_ctx ctx + cdef bint _init diff --git a/src/flint/types/fmpz_mpoly_q.pyx b/src/flint/types/fmpz_mpoly_q.pyx new file mode 100644 index 00000000..768d59ca --- /dev/null +++ b/src/flint/types/fmpz_mpoly_q.pyx @@ -0,0 +1,35 @@ +from flint.flint_base.flint_base cimport flint_rational_function +from flint.utils.typecheck cimport typecheck +from flint.flintlib.fmpz_mpoly cimport fmpz_mpoly_set +from flint.flintlib.fmpz_mpoly_q cimport * +from flint.types.fmpz_mpoly cimport fmpz_mpoly, fmpz_mpoly_ctx + + + +cdef class fmpz_mpoly_q(flint_rational_function): + """ + The `fmpz_mpoly_q` represents multivariate rational functions + over the integers + """ + def __cinit__(self): + self._init = False + + def __dealloc__(self): + if self._init: + fmpz_mpoly_q_clear(self.fraction, self.ctx.val) + self._init = False + + def __init__(self, num, den): + if typecheck(num, fmpz_mpoly) and typecheck(den, fmpz_mpoly): + if (num).ctx == (den).ctx: + self.ctx = (num).ctx + fmpz_mpoly_q_init(self.fraction, self.ctx.val) + fmpz_mpoly_set(fmpz_mpoly_q_numref(self.fraction), + (num).val, self.ctx.val) + fmpz_mpoly_set(fmpz_mpoly_q_denref(self.fraction), + (den).val, self.ctx.val) + self._init = True + else: + raise ValueError("numerator and denominator must have identical contexts") + else: + raise TypeError("fmpz_mpoly_q is a fraction of two fmpz_mpolys fs") From ca4391d355f806b7c63ff7a56674183f3fc5aa32 Mon Sep 17 00:00:00 2001 From: David Einstein Date: Sun, 24 Sep 2023 12:18:05 -0400 Subject: [PATCH 18/95] Fixed small bugs in fmpz_mpoly and fmpq_mpoly Added missing fmpz_clear for exponents in creating fmpz_mpoly from dictionary Fixed spacing in converting fmpq_mpoly to string --- src/flint/types/fmpq_mpoly.pyx | 12 +++++------- src/flint/types/fmpz_mpoly.pyx | 12 ++++++++---- 2 files changed, 13 insertions(+), 11 deletions(-) diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 712346a3..48937526 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -115,7 +115,7 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): >>> ctx = get_fmpq_mpoly_context(2,'lex','x,y') >>> ctx.fmpq_mpoly_from_dict({(1,0):2, (1,1):3, (0,1):1}) - 3*x*y + 2*x + y + 3*x*y + 2*x + y """ cdef long n cdef fmpq_t coefficient @@ -333,8 +333,6 @@ cdef class fmpq_mpoly(flint_mpoly): res = str_from_chars(s) finally: libc.stdlib.free(s) - res = res.replace("+", " + ") - res = res.replace("-", " - ") if res.startswith(" - "): res = "-" + res[3:] return res @@ -556,9 +554,9 @@ cdef class fmpq_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 + 2, 1), (x + 1, 1)]) >>> (p2 * p1 * p2).factor() - (18, [(z + 1, 2), (x + 2, 1), (x + 1, 2)]) + (18, [(z + 1, 2), (x + 2, 1), (x + 1, 2)]) """ cdef fmpq_mpoly_factor_t fac cdef int i @@ -593,9 +591,9 @@ cdef class fmpq_mpoly(flint_mpoly): >>> p1 = Zm("2*x + 4", ctx) >>> p2 = Zm("3*x*y + 3*x + 3*y + 3", ctx) >>> (p1 * p2).factor_squarefree() - (6, [(y + 1, 1), (x^2 + 3*x + 2, 1)]) + (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)]) + (12, [(y + 1, 1), (x + 1, 1), (x + 2, 2)]) """ cdef fmpq_mpoly_factor_t fac cdef int i diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 682b2428..088792c9 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -70,8 +70,6 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): Do not construct one of these directly, use `get_fmpz_mpoly_context`. """ -# cdef fmpz_mpoly_ctx_t val - def __init__(self, slong nvars, ordering, names): if ordering == "lex": fmpz_mpoly_ctx_init(self.val, nvars, ordering_t.ORD_LEX) @@ -81,7 +79,6 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): fmpz_mpoly_ctx_init(self.val, nvars, ordering_t.ORD_DEGREVLEX) else: raise ValueError("Unimplemented term order %s" % ordering) - super().__init__(nvars, names) cpdef slong nvars(self): @@ -179,17 +176,25 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): for k,v in d.items(): xtype = fmpz_set_any_ref(coefficient, v) if xtype == FMPZ_UNKNOWN: + for i in range(nvars): + fmpz_clear(exponents + i) libc.stdlib.free(exponents) raise TypeError("invalid coefficient type %s" % type(v)) if not PyTuple_Check(k): + for i in range(nvars): + fmpz_clear(exponents + i) libc.stdlib.free(exponents) raise TypeError("Expected tuple of ints as key not %s" % type(k)) if PyTuple_GET_SIZE(k) != nvars: + for i in range(nvars): + fmpz_clear(exponents + i) libc.stdlib.free(exponents) raise TypeError("Expected exponent tuple of length %d" % nvars) for i,tup in enumerate(k): xtype = fmpz_set_any_ref(exponents + i, tup) if xtype == FMPZ_UNKNOWN: + for i in range(nvars): + fmpz_clear(exponents + i) libc.stdlib.free(exponents) raise TypeError("Invalid exponent type %s" % type(tup)) #Todo lobby for fmpz_mpoly_push_term_fmpz_ffmpz @@ -270,7 +275,6 @@ cdef inline create_fmpz_mpoly(fmpz_mpoly_ctx ctx): var._init = True return var - # todo: store cached context objects externally cdef class fmpz_mpoly(flint_mpoly): """ From 13136376b69297c75f10b347f9692d2eee0f61b6 Mon Sep 17 00:00:00 2001 From: David Einstein Date: Sat, 30 Sep 2023 11:21:07 -0400 Subject: [PATCH 19/95] Added printing of fmpz_mpoly_q --- src/flint/__init__.py | 1 + src/flint/flintlib/fmpz_mpoly_q.pxd | 2 +- src/flint/types/fmpz_mpoly.pyx | 1 + src/flint/types/fmpz_mpoly_q.pyx | 59 ++++++++++++++++++++++++++++- 4 files changed, 61 insertions(+), 2 deletions(-) diff --git a/src/flint/__init__.py b/src/flint/__init__.py index 89d21e4c..b5c90387 100644 --- a/src/flint/__init__.py +++ b/src/flint/__init__.py @@ -23,6 +23,7 @@ from .types.fmpz_mpoly import * from .types.fmpq_mpoly import * from .types.fmpz_mod import * +from .types.fmpz_mpoly_q import * from .types.dirichlet import * from .functions.showgood import showgood diff --git a/src/flint/flintlib/fmpz_mpoly_q.pxd b/src/flint/flintlib/fmpz_mpoly_q.pxd index 87605c52..682e4263 100644 --- a/src/flint/flintlib/fmpz_mpoly_q.pxd +++ b/src/flint/flintlib/fmpz_mpoly_q.pxd @@ -6,7 +6,7 @@ from flint.flintlib.fmpz_mpoly cimport fmpz_mpoly_ctx_t, fmpz_mpoly_t, fmpz_mpol cdef extern from "flint/fmpz_mpoly_q.h": ctypedef struct fmpz_mpoly_q_struct: fmpz_mpoly_struct num - fmpz_mpoly_struct num + fmpz_mpoly_struct den ctypedef fmpz_mpoly_q_struct fmpz_mpoly_q_t[1] fmpz_mpoly_struct * fmpz_mpoly_q_numref(fmpz_mpoly_q_t x) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 088792c9..beb12175 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -275,6 +275,7 @@ cdef inline create_fmpz_mpoly(fmpz_mpoly_ctx ctx): var._init = True return var + # todo: store cached context objects externally cdef class fmpz_mpoly(flint_mpoly): """ diff --git a/src/flint/types/fmpz_mpoly_q.pyx b/src/flint/types/fmpz_mpoly_q.pyx index 768d59ca..939c3ebc 100644 --- a/src/flint/types/fmpz_mpoly_q.pyx +++ b/src/flint/types/fmpz_mpoly_q.pyx @@ -1,9 +1,21 @@ from flint.flint_base.flint_base cimport flint_rational_function from flint.utils.typecheck cimport typecheck -from flint.flintlib.fmpz_mpoly cimport fmpz_mpoly_set +from flint.flintlib.fmpz_mpoly cimport fmpz_mpoly_set, fmpz_mpoly_get_str_pretty from flint.flintlib.fmpz_mpoly_q cimport * from flint.types.fmpz_mpoly cimport fmpz_mpoly, fmpz_mpoly_ctx +cdef inline init_fmpz_mpoly_q(fmpz_mpoly_q var, fmpz_mpoly_ctx ctx): + var.ctx = ctx + fmpz_mpoly_q_init(var.fraction, ctx.val) + var._init = True + +cdef inline create_fmpz_mpoly_q(fmpz_mpoly_ctx ctx): + cdef fmpz_mpoly_q var + var = fmpz_mpoly_q.__new__(fmpz_mpoly_q) + var.ctx = ctx + fmpz_mpoly_q_init(var.fraction, ctx.val) + var._init = True + return var cdef class fmpz_mpoly_q(flint_rational_function): @@ -33,3 +45,48 @@ cdef class fmpz_mpoly_q(flint_rational_function): raise ValueError("numerator and denominator must have identical contexts") else: raise TypeError("fmpz_mpoly_q is a fraction of two fmpz_mpolys fs") + + + def __nonzero__(self): + return not fmpz_mpoly_q_is_zero(self.fraction, self.ctx.val) + + def __bool__(self): + return not fmpz_mpoly_q_is_zero(self.fraction, self.ctx.val) + + def is_one(self): + return fmpz_mpoly_q_is_one(self.fraction, self.ctx.val) + + def __richcmp__(self, other, int op): + if op != 2 and op != 3: + return NotImplemented + if typecheck(self, fmpz_mpoly_q) and typecheck(other, fmpz_mpoly_q): + if ( self).ctx is ( other).ctx: + if op == 2: + return bool(fmpz_mpoly_q_equal((self).fraction, (other).fraction, self.ctx.val)) + else: + return not bool(fmpz_mpoly_q_equal((self).fraction, (other).fraction, self.ctx.val)) + else: + if op == 2: + return False + else: + return True + if op == 2: + return not bool(self - other) + else: + return bool(self - other) + + def repr(self): + return self.str() + " (nvars=%s, ordering=%s names=%s)" % (self.ctx.nvars(), self.ctx.ordering(), self.ctx.py_names) + + def str(self): + cdef bytes numerator = fmpz_mpoly_get_str_pretty(&(self.fraction.num), self.ctx.c_names, self.ctx.val) + cdef bytes denominator = fmpz_mpoly_get_str_pretty(&(self.fraction.den), self.ctx.c_names, self.ctx.val) + res = str(b"(" + numerator + b")/(" + denominator + b")", encoding='utf-8') + res = res.replace("+", " + ") + res = res.replace("-", " - ") + return res + + def __neg__(self): + cdef fmpz_mpoly_q res + res = create_fmpz_mpoly_q(self.ctx) + fmpz_mpoly_q_neg(res.fraction, self.fraction, res.ctx.val) From 80b132535638218e1494313095e5664643ba3b0f Mon Sep 17 00:00:00 2001 From: David Einstein Date: Sun, 8 Oct 2023 21:58:29 -0400 Subject: [PATCH 20/95] Added operations on fmpz_mpoly that return fmpq_mpoly of fmpz_mpoly_q also basic ops on fmpz_mpoly_q --- src/flint/types/fmpq_mpoly.pxd | 20 +++++- src/flint/types/fmpq_mpoly.pyx | 57 ++++++++++----- src/flint/types/fmpz_mpoly.pxd | 15 +++- src/flint/types/fmpz_mpoly.pyx | 119 +++++++++++++++++++++++-------- src/flint/types/fmpz_mpoly_q.pyx | 48 ++++++++++++- 5 files changed, 208 insertions(+), 51 deletions(-) diff --git a/src/flint/types/fmpq_mpoly.pxd b/src/flint/types/fmpq_mpoly.pxd index 04a4cdb7..8240e736 100644 --- a/src/flint/types/fmpq_mpoly.pxd +++ b/src/flint/types/fmpq_mpoly.pxd @@ -1,10 +1,26 @@ from flint.flint_base.flint_base cimport flint_mpoly from flint.flint_base.flint_base cimport flint_mpoly_context -from flint.flintlib.fmpq_mpoly cimport fmpq_mpoly_ctx_t -from flint.flintlib.fmpq_mpoly cimport fmpq_mpoly_t +from flint.flintlib.fmpq_mpoly cimport * from flint.flintlib.flint cimport slong +from flint.types.fmpz_mpoly cimport fmpz_mpoly_ctx + +cdef inline init_fmpq_mpoly(fmpq_mpoly var, fmpq_mpoly_ctx ctx): + var.ctx = ctx + fmpq_mpoly_init(var.val, ctx.val) + var._init = True + +cdef inline create_fmpq_mpoly(fmpq_mpoly_ctx ctx): + cdef fmpq_mpoly var + var = fmpq_mpoly.__new__(fmpq_mpoly) + var.ctx = ctx + fmpq_mpoly_init(var.val, ctx.val) + var._init = True + return var + +cdef fmpq_mpoly_ctx create_fmpq_mpoly_ctx_from_fmpz_mpoly_ctx(fmpz_mpoly_ctx ctx) + cdef class fmpq_mpoly_ctx(flint_mpoly_context): cdef fmpq_mpoly_ctx_t val cpdef slong nvars(self) diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 48937526..f2064cdf 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -7,11 +7,14 @@ from flint.utils.typecheck cimport typecheck from flint.utils.conversion cimport str_from_chars from flint.flintlib.flint cimport * -from flint.flintlib.fmpq cimport fmpq_init, fmpq_clear, fmpq_is_zero, fmpq_set +from flint.flintlib.fmpq cimport fmpq_init, fmpq_clear, fmpq_is_zero, fmpq_set, fmpq_one from flint.flintlib.fmpz cimport fmpz_clear, fmpz_init, fmpz_set +from flint.flintlib.fmpz_mpoly cimport fmpz_mpoly_set from flint.flintlib.fmpq_mpoly cimport * from flint.flintlib.fmpq_mpoly_factor cimport * +from flint.types.fmpz_mpoly cimport fmpz_mpoly, fmpz_mpoly_ctx + cdef extern from *: """ /* An ugly hack to get around the ugly hack of renaming fmpq to avoid a c/python name collision */ @@ -184,14 +187,18 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): return res -def get_fmpq_mpoly_context(slong nvars=1, ordering="lex", names='x'): +def get_fmpq_mpoly_context(slong nvars=1, ordering="lex", names='x', nametup=None): if nvars <= 0: nvars = 1 - nametup = tuple(name.strip() for name in names.split(',')) - if len(nametup) != nvars: - if len(nametup) != 1: + if nametup is None: + nametup = tuple(name.strip() for name in names.split(',')) + if len(nametup) != nvars: + if len(nametup) != 1: + raise ValueError("Number of variables does not equal number of names") + nametup = tuple(nametup[0] + str(i) for i in range(nvars)) + else: + if len(nametup) != nvars: raise ValueError("Number of variables does not equal number of names") - nametup = tuple(nametup[0] + str(i) for i in range(nvars)) key = (nvars, ordering, nametup) ctx = _fmpq_mpoly_ctx_cache.get(key) if ctx is None: @@ -199,18 +206,25 @@ def get_fmpq_mpoly_context(slong nvars=1, ordering="lex", names='x'): _fmpq_mpoly_ctx_cache[key] = ctx return ctx -cdef inline init_fmpq_mpoly(fmpq_mpoly var, fmpq_mpoly_ctx ctx): - var.ctx = ctx - fmpq_mpoly_init(var.val, ctx.val) - var._init = True +cdef fmpq_mpoly_ctx create_fmpq_mpoly_ctx_from_fmpz_mpoly_ctx(fmpz_mpoly_ctx ctx): + return get_fmpq_mpoly_context(nvars=ctx.nvars(), ordering=ctx.ordering(), names=None, + nametup=tuple(str(s, 'utf-8') for s in ctx.py_names)) + + + -cdef inline create_fmpq_mpoly(fmpq_mpoly_ctx ctx): - cdef fmpq_mpoly var - var = fmpq_mpoly.__new__(fmpq_mpoly) - var.ctx = ctx - fmpq_mpoly_init(var.val, ctx.val) - var._init = True - return var +# cdef inline init_fmpq_mpoly(fmpq_mpoly var, fmpq_mpoly_ctx ctx): +# var.ctx = ctx +# fmpq_mpoly_init(var.val, ctx.val) +# var._init = True + +# cdef inline create_fmpq_mpoly(fmpq_mpoly_ctx ctx): +# cdef fmpq_mpoly var +# var = fmpq_mpoly.__new__(fmpq_mpoly) +# var.ctx = ctx +# fmpq_mpoly_init(var.val, ctx.val) +# var._init = True +# return var @@ -240,6 +254,15 @@ cdef class fmpq_mpoly(flint_mpoly): fmpq_mpoly_set(self.val, (val).val, self.ctx.val) else: raise ValueError("Cannot automatically coerce contexts") + if typecheck(val, fmpz_mpoly): + if ctx is None: + ctx = create_fmpq_mpoly_ctx_from_fmpz_mpoly_ctx((val).ctx) + elif ctx != create_fmpq_mpoly_ctx_from_fmpz_mpoly_ctx((val).ctx): + raise ValueError("Cannot automatically coerce contexts") + init_fmpq_mpoly(self, ctx) + fmpz_mpoly_set(self.val.zpoly, (val).val, ( val).ctx.val) + fmpq_one(self.val.content) + fmpq_mpoly_reduce(self.val, self.ctx.val) elif isinstance(val, dict): if ctx is None: if len(val) == 0: diff --git a/src/flint/types/fmpz_mpoly.pxd b/src/flint/types/fmpz_mpoly.pxd index a33d6f00..c3e47a23 100644 --- a/src/flint/types/fmpz_mpoly.pxd +++ b/src/flint/types/fmpz_mpoly.pxd @@ -2,9 +2,22 @@ from flint.flint_base.flint_base cimport flint_mpoly from flint.flint_base.flint_base cimport flint_mpoly_context from flint.flintlib.fmpz_mpoly cimport fmpz_mpoly_ctx_t -from flint.flintlib.fmpz_mpoly cimport fmpz_mpoly_t +from flint.flintlib.fmpz_mpoly cimport fmpz_mpoly_t, fmpz_mpoly_init from flint.flintlib.flint cimport slong +cdef inline init_fmpz_mpoly(fmpz_mpoly var, fmpz_mpoly_ctx ctx): + var.ctx = ctx + fmpz_mpoly_init(var.val, ctx.val) + var._init = True + +cdef inline create_fmpz_mpoly(fmpz_mpoly_ctx ctx): + cdef fmpz_mpoly var + var = fmpz_mpoly.__new__(fmpz_mpoly) + var.ctx = ctx + fmpz_mpoly_init(var.val, ctx.val) + var._init = True + return var + cdef class fmpz_mpoly_ctx(flint_mpoly_context): cdef fmpz_mpoly_ctx_t val cpdef slong nvars(self) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index beb12175..b4ea3938 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -2,6 +2,7 @@ from cpython.version cimport PY_MAJOR_VERSION from cpython.dict cimport PyDict_Size, PyDict_Check, PyDict_Next from cpython.tuple cimport PyTuple_Check, PyTuple_GET_SIZE from flint.flintlib.fmpz cimport fmpz_init, fmpz_clear, fmpz_is_zero +from flint.flintlib.fmpq_mpoly cimport fmpq_mpoly_t, fmpq_mpoly_add_fmpq, fmpq_mpoly_sub_fmpq, fmpq_mpoly_scalar_mul_fmpq from flint.flintlib.flint cimport * from flint.utils.conversion cimport str_from_chars @@ -10,6 +11,9 @@ from flint.flint_base.flint_base cimport flint_mpoly from flint.flint_base.flint_base cimport flint_mpoly_context from flint.types.fmpz cimport any_as_fmpz from flint.types.fmpz cimport fmpz, fmpz_set_any_ref +from flint.types.fmpq cimport any_as_fmpq, fmpq_set_any_ref +from flint.types.fmpq_mpoly cimport fmpq_mpoly +from flint.types.fmpz_mpoly_q cimport fmpz_mpoly_q cimport cython @@ -18,6 +22,13 @@ from flint.flintlib.fmpz cimport fmpz_set from flint.flintlib.fmpz_mpoly cimport * from flint.flintlib.fmpz_mpoly_factor cimport * +cdef extern from *: + """ + /* An ugly hack to get around the ugly hack of renaming fmpq to avoid a c/python name collision */ + typedef fmpq fmpq_struct; + """ + + cdef any_as_fmpz_mpoly(x): cdef fmpz_mpoly res """ @@ -262,18 +273,6 @@ def get_fmpz_mpoly_context(slong nvars=1, ordering="lex", names='x'): # _fmpz_mpoly_set2(( args2[i]).val, ctx.val, ( args[i]).val, ( args[i]).ctx.val) # return ctx, args2 -cdef inline init_fmpz_mpoly(fmpz_mpoly var, fmpz_mpoly_ctx ctx): - var.ctx = ctx - fmpz_mpoly_init(var.val, ctx.val) - var._init = True - -cdef inline create_fmpz_mpoly(fmpz_mpoly_ctx ctx): - cdef fmpz_mpoly var - var = fmpz_mpoly.__new__(fmpz_mpoly) - var.ctx = ctx - fmpz_mpoly_init(var.val, ctx.val) - var._init = True - return var # todo: store cached context objects externally @@ -409,6 +408,10 @@ cdef class fmpz_mpoly(flint_mpoly): def __add__(self, other): cdef fmpz_mpoly res + cdef fmpq_mpoly qres + cdef fmpz_t z_other + cdef fmpq_t q_other + cdef int xtype if typecheck(other, fmpz_mpoly): if (self).ctx is not (other).ctx: return NotImplemented @@ -416,20 +419,35 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_add(res.val, (self).val, (other).val, res.ctx.val) return res else: - other = any_as_fmpz(other) - if other is not NotImplemented: + xtype = fmpz_set_any_ref(z_other, other) + if xtype != FMPZ_UNKNOWN: res = create_fmpz_mpoly(self.ctx) - fmpz_mpoly_add_fmpz(res.val, (self).val, (other).val, res.ctx.val) + fmpz_mpoly_add_fmpz(res.val, (self).val, z_other, res.ctx.val) return res + xtype = fmpq_set_any_ref(q_other, other) + if xtype != FMPZ_UNKNOWN: + qres = fmpq_mpoly(self) + fmpq_mpoly_add_fmpq(qres.val, qres.val, q_other, qres.ctx.val) + return qres return NotImplemented def __radd__(self, other): cdef fmpz_mpoly res - other = any_as_fmpz(other) - if other is not NotImplemented: + cdef fmpq_mpoly qres + cdef fmpz_t z_other + cdef fmpq_t q_other + cdef int xtype + + xtype = fmpz_set_any_ref(z_other, other) + if xtype != FMPZ_UNKNOWN: res = create_fmpz_mpoly(self.ctx) - fmpz_mpoly_add_fmpz(res.val, (self).val, (other).val, res.ctx.val) + fmpz_mpoly_add_fmpz(res.val, (self).val, z_other, res.ctx.val) return res + xtype = fmpq_set_any_ref(q_other, other) + if xtype != FMPZ_UNKNOWN: + qres = fmpq_mpoly(self) + fmpq_mpoly_add_fmpq(qres.val, qres.val, q_other, qres.ctx.val) + return qres return NotImplemented def __iadd__(self, other): @@ -439,14 +457,18 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_add((self).val, (self).val, (other).val, self.ctx.val) return self else: - other = any_as_fmpz(other) - if other is not NotImplemented: - fmpz_mpoly_add_fmpz((self).val, (self).val, (other).val, self.ctx.val) + zval = any_as_fmpz(other) + if zval is not NotImplemented: + fmpz_mpoly_add_fmpz((self).val, (self).val, (zval).val, self.ctx.val) return self return NotImplemented def __sub__(self, other): cdef fmpz_mpoly res + cdef fmpq_mpoly qres + cdef fmpz_t z_other + cdef fmpq_t q_other + cdef int xtype if typecheck(other, fmpz_mpoly): if (self).ctx is not (other).ctx: return NotImplemented @@ -454,20 +476,35 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_sub(res.val, (self).val, (other).val, res.ctx.val) return res else: - other = any_as_fmpz(other) - if other is not NotImplemented: + xtype = fmpz_set_any_ref(z_other, other) + if xtype != FMPZ_UNKNOWN: res = create_fmpz_mpoly(self.ctx) - fmpz_mpoly_sub_fmpz(res.val, (self).val, (other).val, res.ctx.val) + fmpz_mpoly_sub_fmpz(res.val, (self).val, z_other, res.ctx.val) return res + xtype = fmpq_set_any_ref(q_other, other) + if xtype != FMPZ_UNKNOWN: + qres = fmpq_mpoly(self) + fmpq_mpoly_sub_fmpq(qres.val, qres.val, q_other, qres.ctx.val) + return qres return NotImplemented def __rsub__(self, other): cdef fmpz_mpoly res - other = any_as_fmpz(other) - if other is not NotImplemented: + cdef fmpq_mpoly qres + cdef fmpz_t z_other + cdef fmpq_t q_other + cdef int xtype + + xtype = fmpz_set_any_ref(z_other, other) + if xtype != FMPZ_UNKNOWN: res = create_fmpz_mpoly(self.ctx) - fmpz_mpoly_sub_fmpz(res.val, (self).val, (other).val, res.ctx.val) + fmpz_mpoly_sub_fmpz(res.val, (self).val, z_other, res.ctx.val) return -res + xtype = fmpq_set_any_ref(q_other, other) + if xtype != FMPZ_UNKNOWN: + qres = fmpq_mpoly(self) + fmpq_mpoly_sub_fmpq(qres.val, qres.val, q_other, qres.ctx.val) + return -qres return NotImplemented def __isub__(self, other): @@ -485,6 +522,11 @@ cdef class fmpz_mpoly(flint_mpoly): def __mul__(self, other): cdef fmpz_mpoly res + cdef fmpq_mpoly qres + cdef fmpz_t z_other + cdef fmpq_t q_other + cdef int xtype + if typecheck(other, fmpz_mpoly): if (self).ctx is not (other).ctx: return NotImplemented @@ -492,11 +534,15 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_mul(res.val, (self).val, (other).val, res.ctx.val) return res else: - other = any_as_fmpz(other) - if other is not NotImplemented: + xtype = fmpz_set_any_ref(z_other, other) + if xtype != FMPZ_UNKNOWN: res = create_fmpz_mpoly(self.ctx) - fmpz_mpoly_scalar_mul_fmpz(res.val, (self).val, (other).val, res.ctx.val) + fmpz_mpoly_scalar_mul_fmpz(res.val, (self).val, z_other, res.ctx.val) return res + xtype = fmpq_set_any_ref(q_other, other) + if xtype != FMPZ_UNKNOWN: + qres = fmpq_mpoly(self) + fmpq_mpoly_scalar_mul_fmpq(qres.val, qres.val, q_other, qres.ctx.val) return NotImplemented def __rmul__(self, other): @@ -506,6 +552,9 @@ cdef class fmpz_mpoly(flint_mpoly): res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_scalar_mul_fmpz(res.val, (self).val, (other).val, res.ctx.val) return res + other = any_as_fmpq(other) + if other is not NotImplemented: + return fmpq_mpoly(self) * other return NotImplemented def __imul__(self, other): @@ -519,6 +568,9 @@ cdef class fmpz_mpoly(flint_mpoly): if other is not NotImplemented: fmpz_mpoly_scalar_mul_fmpz(self.val, (self).val, (other).val, self.ctx.val) return self + other = any_as_fmpq(other) + if other is not NotImplemented: + return fmpq_mpoly(self) * other return NotImplemented def __pow__(self, other, modulus): @@ -592,6 +644,13 @@ cdef class fmpz_mpoly(flint_mpoly): return res return NotImplemented + def __truediv__(self, other): + # TODO division by fmpq + if typecheck(other, fmpz_mpoly): + if self.ctx is not ( other).ctx: + return NotImplemented + return fmpz_mpoly_q(self, other) + def __mod__(self, other): return divmod(self, other)[1] diff --git a/src/flint/types/fmpz_mpoly_q.pyx b/src/flint/types/fmpz_mpoly_q.pyx index 939c3ebc..640d9029 100644 --- a/src/flint/types/fmpz_mpoly_q.pyx +++ b/src/flint/types/fmpz_mpoly_q.pyx @@ -2,7 +2,8 @@ from flint.flint_base.flint_base cimport flint_rational_function from flint.utils.typecheck cimport typecheck from flint.flintlib.fmpz_mpoly cimport fmpz_mpoly_set, fmpz_mpoly_get_str_pretty from flint.flintlib.fmpz_mpoly_q cimport * -from flint.types.fmpz_mpoly cimport fmpz_mpoly, fmpz_mpoly_ctx +from flint.types.fmpz_mpoly cimport fmpz_mpoly, fmpz_mpoly_ctx, create_fmpz_mpoly + cdef inline init_fmpz_mpoly_q(fmpz_mpoly_q var, fmpz_mpoly_ctx ctx): var.ctx = ctx @@ -86,7 +87,52 @@ cdef class fmpz_mpoly_q(flint_rational_function): res = res.replace("-", " - ") return res + def numer(self): + """ + Returns the numerator of *self* as an *fmpz_mpoly* + """ + cdef fmpz_mpoly num = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_set(num.val, &(self.fraction.num), self.ctx.val) + return num + + def denom(self): + """ + Returns the denominator of *self* as an *fmpz_mpoly*. + """ + cdef fmpz_mpoly num = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_set(num.val, &(self.fraction.den), self.ctx.val) + return num + def __neg__(self): cdef fmpz_mpoly_q res res = create_fmpz_mpoly_q(self.ctx) fmpz_mpoly_q_neg(res.fraction, self.fraction, res.ctx.val) + return res + + def __add__(s, t): + cdef fmpz_mpoly_q res + if typecheck(t, fmpz_mpoly_q) and typecheck(s, fmpz_mpoly_q): + if (s).ctx is (t).ctx: + res = create_fmpz_mpoly_q(s.ctx) + fmpz_mpoly_q_add(res.fraction, (s).fraction, (t).fraction, ( s).ctx.val) + return res + return NotImplemented + + def __sub__(s,t): + cdef fmpz_mpoly_q res + if typecheck(t, fmpz_mpoly_q) and typecheck(s, fmpz_mpoly_q): + if (s).ctx is (t).ctx: + res = create_fmpz_mpoly_q(s.ctx) + fmpz_mpoly_q_sub(res.fraction, (s).fraction, (t).fraction, ( s).ctx.val) + return res + return NotImplemented + + def __mul__(s,t): + cdef fmpz_mpoly_q res + if typecheck(t, fmpz_mpoly_q) and typecheck(s, fmpz_mpoly_q): + if (s).ctx is (t).ctx: + res = create_fmpz_mpoly_q(s.ctx) + fmpz_mpoly_q_mul(res.fraction, (s).fraction, (t).fraction, ( s).ctx.val) + return res + return NotImplemented + From e3c81d8ec9df882ab46cfe519c05ee07d9003fb0 Mon Sep 17 00:00:00 2001 From: David Einstein Date: Mon, 9 Oct 2023 22:35:05 -0400 Subject: [PATCH 21/95] Added tests, checks for division by zero to fmpz_mpoly Also added method to get context from a fmpq_mpoly so the tests could check that it was correct. --- src/flint/test/__main__.py | 1 + src/flint/test/test.py | 23 ++++++++++ src/flint/types/fmpq_mpoly.pyx | 7 ++- src/flint/types/fmpz_mpoly.pyx | 80 +++++++++++++++++++++++++++++++--- 4 files changed, 102 insertions(+), 9 deletions(-) diff --git a/src/flint/test/__main__.py b/src/flint/test/__main__.py index c1466e1f..be1e2692 100644 --- a/src/flint/test/__main__.py +++ b/src/flint/test/__main__.py @@ -56,6 +56,7 @@ def run_doctests(verbose=None): flint.types.fmpz_poly, flint.types.fmpz_mat, flint.types.fmpz_mpoly, + flint.types.fmpz_mpoly_q, flint.types.fmpz_series, flint.types.fmpq, flint.types.fmpq_poly, diff --git a/src/flint/test/test.py b/src/flint/test/test.py index d164ce97..1429f7f3 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -665,6 +665,29 @@ def test_fmpz_mpoly(): assert p1 // ztype(3) == ctx.fmpz_mpoly_from_dict({(1,0):1,(0,3):1,(2,4):3}) assert ztype(3) // p1 == Zp(0,ctx) assert ctx.constant(7) + ztype(3) == Zp(10, ctx) + q1 = flint.fmpq_mpoly(p1) + qctx = q1.context() + assert qctx.nvars() == 2 + assert qctx.ordering() == 'lex' + QQ = flint.fmpq + assert p1 + QQ(1,2) == qctx.fmpq_mpoly_from_dict({(1,0):4,(0,3):4,(2,4):9,(0,0):QQ(1,2)}) + assert QQ(1,2) + p1 == qctx.fmpq_mpoly_from_dict({(1,0):4,(0,3):4,(2,4):9,(0,0):QQ(1,2)}) + assert p1 - QQ(1,2) == qctx.fmpq_mpoly_from_dict({(1,0):4,(0,3):4,(2,4):9,(0,0):QQ(-1,2)}) + assert QQ(1,2) - p1 == qctx.fmpq_mpoly_from_dict({(1,0):-4,(0,3):-4,(2,4):-9,(0,0):QQ(1,2)}) + assert QQ(1,2) * p1 == qctx.fmpq_mpoly_from_dict({(1,0):2,(0,3):2,(2,4):QQ(9,2)}) + assert p1 * QQ(1,2) == qctx.fmpq_mpoly_from_dict({(1,0):2,(0,3):2,(2,4):QQ(9,2)}) + assert p1 / 2 == qctx.fmpq_mpoly_from_dict({(1,0):2,(0,3):2,(2,4):QQ(9,2)}) + assert p1 / QQ(1,2) == flint.fmpq_mpoly(p1 * 2) + p0 = Zp(0, ctx) + assert raises(lambda: p1 // p0 , ZeroDivisionError) + assert raises(lambda: p1 // 0 , ZeroDivisionError) + assert raises(lambda: p1 // QQ(1,1) , TypeError) + assert raises(lambda: p1 % p0 , ZeroDivisionError) + assert raises(lambda: p1 % 0 , ZeroDivisionError) + assert raises(lambda: p1 % QQ(1,1) , TypeError) + assert raises(lambda: p1 / p0 , ZeroDivisionError) + assert raises(lambda: p1 / 0 , ZeroDivisionError) + assert raises(lambda: p1 / QQ(0,1) , ZeroDivisionError) def test_fmpz_series(): diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index f2064cdf..473054d0 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -283,13 +283,13 @@ cdef class fmpq_mpoly(flint_mpoly): fmpq_mpoly_set_str_pretty(self.val, val, self.ctx.c_names, self.ctx.val) fmpq_mpoly_sort_terms(self.val, self.ctx.val) else: - v = any_as_fmpz(val) + v = any_as_fmpq(val) if v is NotImplemented: raise TypeError("cannot create fmpz_mpoly from type %s" % type(val)) if ctx is None: raise ValueError("Need context to convert fmpz to fmpq_mpoly") init_fmpq_mpoly(self, ctx) - fmpq_mpoly_set_fmpz(self.val, (v).val, self.ctx.val) + fmpq_mpoly_set_fmpq(self.val, (v).val, self.ctx.val) def __nonzero__(self): return not fmpq_mpoly_is_zero(self.val, self.ctx.val) @@ -319,6 +319,9 @@ cdef class fmpq_mpoly(flint_mpoly): else: return bool(self - other) + def context(self): + return self.ctx + def __len__(self): return fmpq_mpoly_length(self.val, self.ctx.val) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index b4ea3938..4cd1c39e 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -3,7 +3,10 @@ from cpython.dict cimport PyDict_Size, PyDict_Check, PyDict_Next from cpython.tuple cimport PyTuple_Check, PyTuple_GET_SIZE from flint.flintlib.fmpz cimport fmpz_init, fmpz_clear, fmpz_is_zero from flint.flintlib.fmpq_mpoly cimport fmpq_mpoly_t, fmpq_mpoly_add_fmpq, fmpq_mpoly_sub_fmpq, fmpq_mpoly_scalar_mul_fmpq +from flint.flintlib.fmpq_mpoly cimport fmpq_mpoly_scalar_div_fmpq from flint.flintlib.flint cimport * +from flint.flintlib.fmpq cimport fmpq_numref, fmpq_denref +from flint.flintlib.fmpz_mpoly_q cimport fmpz_mpoly_q_div_fmpz from flint.utils.conversion cimport str_from_chars from flint.utils.typecheck cimport typecheck @@ -329,6 +332,9 @@ cdef class fmpz_mpoly(flint_mpoly): init_fmpz_mpoly(self, ctx) fmpz_mpoly_set_fmpz(self.val, (v).val, self.ctx.val) + def context(self): + return self.ctx + def __nonzero__(self): return not fmpz_mpoly_is_zero(self.val, self.ctx.val) @@ -543,18 +549,26 @@ cdef class fmpz_mpoly(flint_mpoly): if xtype != FMPZ_UNKNOWN: qres = fmpq_mpoly(self) fmpq_mpoly_scalar_mul_fmpq(qres.val, qres.val, q_other, qres.ctx.val) + return qres return NotImplemented def __rmul__(self, other): cdef fmpz_mpoly res - other = any_as_fmpz(other) - if other is not NotImplemented: + cdef fmpq_mpoly qres + cdef fmpz_t z_other + cdef fmpq_t q_other + cdef int xtype + + xtype = fmpz_set_any_ref(z_other, other) + if xtype != FMPZ_UNKNOWN: res = create_fmpz_mpoly(self.ctx) - fmpz_mpoly_scalar_mul_fmpz(res.val, (self).val, (other).val, res.ctx.val) + fmpz_mpoly_scalar_mul_fmpz(res.val, (self).val, z_other, res.ctx.val) return res - other = any_as_fmpq(other) - if other is not NotImplemented: - return fmpq_mpoly(self) * other + xtype = fmpq_set_any_ref(q_other, other) + if xtype != FMPZ_UNKNOWN: + qres = fmpq_mpoly(self) + fmpq_mpoly_scalar_mul_fmpq(qres.val, qres.val, q_other, qres.ctx.val) + return qres return NotImplemented def __imul__(self, other): @@ -590,6 +604,8 @@ cdef class fmpz_mpoly(flint_mpoly): def __divmod__(self, other): cdef fmpz_mpoly res, res2 if typecheck(other, fmpz_mpoly): + if not other: + raise ZeroDivisionError("fmpz_mpoly_divison by zero") if (self).ctx is not (other).ctx: return NotImplemented res = create_fmpz_mpoly(self.ctx) @@ -600,6 +616,8 @@ cdef class fmpz_mpoly(flint_mpoly): other = any_as_fmpz(other) if other is not NotImplemented: other= fmpz_mpoly(other, self.ctx) + if not other: + raise ZeroDivisionError("fmpz_mpoly divison by zero") res = create_fmpz_mpoly(self.ctx) res2 = create_fmpz_mpoly(self.ctx) fmpz_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) @@ -608,6 +626,8 @@ cdef class fmpz_mpoly(flint_mpoly): def __rdivmod__(self, other): cdef fmpz_mpoly res, res2 + if not self: + raise ZeroDivisionError("fmpz_mpoly divison by zero") other = any_as_fmpz(other) if other is not NotImplemented: other = fmpz_mpoly(other, self.ctx) @@ -620,6 +640,8 @@ cdef class fmpz_mpoly(flint_mpoly): def __floordiv__(self, other): cdef fmpz_mpoly res if typecheck(other, fmpz_mpoly): + if not other: + raise ZeroDivisionError("fmpz_mpoly division by zero") if (self).ctx is not (other).ctx: return NotImplemented res = create_fmpz_mpoly(self.ctx) @@ -628,6 +650,8 @@ cdef class fmpz_mpoly(flint_mpoly): else: other = any_as_fmpz(other) if other is not NotImplemented: + if not other: + raise ZeroDivisionError("fmpz_mpoly division by zero") other = fmpz_mpoly(other, self.ctx) res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) @@ -636,6 +660,8 @@ cdef class fmpz_mpoly(flint_mpoly): def __rfloordiv__(self,other): cdef fmpz_mpoly res + if not self: + raise ZeroDivisionError("fmpz_mpoly division by zero") other = any_as_fmpz(other) if other is not NotImplemented: other = fmpz_mpoly(other, self.ctx) @@ -645,11 +671,51 @@ cdef class fmpz_mpoly(flint_mpoly): return NotImplemented def __truediv__(self, other): - # TODO division by fmpq + cdef fmpq_mpoly qres + cdef fmpq_t q_other + cdef int xtype + if typecheck(other, fmpz_mpoly): + if not other: + raise ZeroDivisionError("fmpz_mpoly division by zero") if self.ctx is not ( other).ctx: return NotImplemented return fmpz_mpoly_q(self, other) + xtype = fmpq_set_any_ref(q_other, other) + if xtype != FMPZ_UNKNOWN: + if not other: + raise ZeroDivisionError("fmpz_mpoly division by zero") + qres = fmpq_mpoly(self) + fmpq_mpoly_scalar_div_fmpq(qres.val, qres.val, q_other, qres.ctx.val) + return qres + + def __rtruediv__(self, other): + cdef fmpz_mpoly num + cdef fmpz_mpoly_q ret + cdef fmpz_t z_other + cdef fmpq_t q_other + cdef int xtype + + if not self: + raise ZeroDivisionError("fmpz_mpoly division by zero") + if typecheck(other, fmpz_mpoly): + if self.ctx is not ( other).ctx: + return NotImplemented + return fmpz_mpoly_q(other, self) + xtype = fmpz_set_any_ref(z_other, other) + if xtype != FMPZ_UNKNOWN: + num = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_set_fmpz(num.val, z_other, self.ctx.val) + return fmpz_mpoly_q(num, self) + xtype = fmpq_set_any_ref(q_other, other) + if xtype != FMPZ_UNKNOWN: + num = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_set_fmpz(num.val, fmpq_numref(q_other),self.ctx.val) + ret = fmpz_mpoly_q(num, self) + fmpz_mpoly_q_div_fmpz(ret.fraction, ret.fraction, fmpq_denref(q_other), self.ctx.val) + return ret + return NotImplemented + def __mod__(self, other): return divmod(self, other)[1] From f2a8bbbff80ce1f16a5a50c4eb7be9c85aa5c9ca Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Thu, 11 Apr 2024 00:12:54 +1000 Subject: [PATCH 22/95] Small refactor and general tidy of fmpz_mpoly and fmpq_mpoly Moves ctx lookup into something based on the ctx class rather than aptly named functions. More to just get myself acquainted with the code base than anything else. --- setup.py | 10 +- src/flint/__init__.py | 8 +- src/flint/flint_base/flint_base.pyx | 41 ++- src/flint/fmpq_mpoly.pyx | 511 ---------------------------- src/flint/test/test.py | 4 +- src/flint/types/fmpq_mpoly.pxd | 2 - src/flint/types/fmpq_mpoly.pyx | 237 ++++++------- src/flint/types/fmpz_mpoly.pyx | 182 +++++----- 8 files changed, 226 insertions(+), 769 deletions(-) delete mode 100644 src/flint/fmpq_mpoly.pyx diff --git a/setup.py b/setup.py index 29defe72..71bf5079 100644 --- a/setup.py +++ b/setup.py @@ -100,6 +100,9 @@ ("flint.types.fmpz_mod_poly", ["src/flint/types/fmpz_mod_poly.pyx"]), ("flint.types.fmpz_mod_mat", ["src/flint/types/fmpz_mod_mat.pyx"]), + ("flint.types.fmpq_mpoly", ["src/flint/types/fmpq_mpoly.pyx"]), + ("flint.types.fmpz_mpoly_q", ["src/flint/types/fmpz_mpoly_q.pyx"]), + ("flint.types.arf", ["src/flint/types/arf.pyx"]), ("flint.types.arb", ["src/flint/types/arb.pyx"]), ("flint.types.arb_poly", ["src/flint/types/arb_poly.pyx"]), @@ -110,13 +113,6 @@ ("flint.types.acb_mat", ["src/flint/types/acb_mat.pyx"]), ("flint.types.acb_series", ["src/flint/types/acb_series.pyx"]), - ("flint.types.fmpz_mpoly", ["src/flint/types/fmpz_mpoly.pyx"]), - ("flint.types.fmpq_mpoly", ["src/flint/types/fmpq_mpoly.pyx"]), - ("flint.types.fmpz_mpoly_q", ["src/flint/types/fmpz_mpoly_q.pyx"]), - ("flint.types.fmpz_mod", ["src/flint/types/fmpz_mod.pyx"]), - ("flint.flint_base.flint_base", ["src/flint/flint_base/flint_base.pyx"]), - ("flint.flint_base.flint_context", ["src/flint/flint_base/flint_context.pyx"]), - ("flint.types.dirichlet", ["src/flint/types/dirichlet.pyx"]), ("flint.functions.showgood", ["src/flint/functions/showgood.pyx"]), diff --git a/src/flint/__init__.py b/src/flint/__init__.py index 75f3d13a..cdf0f41b 100644 --- a/src/flint/__init__.py +++ b/src/flint/__init__.py @@ -20,6 +20,9 @@ from .types.fmpz_mod_poly import * from .types.fmpz_mod_mat import fmpz_mod_mat +from .types.fmpq_mpoly import * +from .types.fmpz_mpoly_q import * + from .types.arf import * from .types.arb import * from .types.arb_poly import * @@ -30,11 +33,6 @@ from .types.acb_mat import * from .types.acb_series import * -from .types.fmpz_mpoly import * -from .types.fmpq_mpoly import * -from .types.fmpz_mod import * -from .types.fmpz_mpoly_q import * - from .types.dirichlet import * from .functions.showgood import good, showgood diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index fb14a0c3..65afb0f2 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -2,9 +2,11 @@ from flint.flintlib.flint cimport ( FLINT_BITS as _FLINT_BITS, FLINT_VERSION as _FLINT_VERSION, __FLINT_RELEASE as _FLINT_RELEASE, + slong ) from flint.flint_base.flint_context cimport thectx cimport libc.stdlib +from typing import Optional FLINT_BITS = _FLINT_BITS @@ -125,8 +127,10 @@ cdef class flint_mpoly_context(flint_elem): Base class for multivariate ring contexts """ + _ctx_cache = None + def __cinit__(self): - self._init = False + self._init = False def __init__(self, long nvars, names): assert nvars >= 1 @@ -149,6 +153,41 @@ cdef class flint_mpoly_context(flint_elem): def gens(self): return tuple(self.gen(i) for i in range(self.nvars())) + @staticmethod + def create_cache_key(slong nvars, ordering: str, names: str): + nametup = tuple(name.strip() for name in names.split(',')) + if len(nametup) != nvars: + if len(nametup) != 1: + raise ValueError("Number of variables does not equal number of names") + nametup = tuple(nametup[0] + str(i) for i in range(nvars)) + return nvars, ordering, nametup + + @classmethod + def get_context(cls, slong nvars=1, ordering: str = "lex", names: Optional[str] = "x", nametup: Optional[tuple] = None): + if nvars <= 0: + nvars = 1 + + if nametup is None and names is not None: + key = cls.create_cache_key(nvars, ordering, names) + elif len(nametup) != nvars: + raise ValueError("Number of variables does not equal number of names") + else: + key = (nvars, ordering, nametup) + + ctx = cls._ctx_cache.get(key) + if ctx is None: + ctx = cls(*key) + cls._ctx_cache[key] = ctx + return ctx + + @classmethod + def from_context(cls, ctx: flint_mpoly_context): + return cls.get_context( + nvars=ctx.nvars(), + ordering=ctx.ordering(), + names=None, + nametup=tuple(str(s, 'utf-8') for s in ctx.py_names) + ) cdef class flint_mpoly(flint_elem): diff --git a/src/flint/fmpq_mpoly.pyx b/src/flint/fmpq_mpoly.pyx deleted file mode 100644 index cd31e179..00000000 --- a/src/flint/fmpq_mpoly.pyx +++ /dev/null @@ -1,511 +0,0 @@ -cdef dict _fmpq_mpoly_ctx_cache = {} - -@cython.auto_pickle(False) -cdef class fmpq_mpoly_ctx(flint_mpoly_context): - """ - A class for storing the polynomial context - - :param nvars: The number of variables in the ring - :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 `get_fmpq_mpoly_context`. - """ - cdef fmpq_mpoly_ctx_t val - - def __init__(self, slong nvars, ordering, names): - if ordering == "lex": - fmpq_mpoly_ctx_init(self.val, nvars, ordering_t.ORD_LEX) - elif ordering == "deglex": - fmpq_mpoly_ctx_init(self.val, nvars, ordering_t.ORD_DEGLEX) - elif ordering == "degrevlex": - fmpq_mpoly_ctx_init(self.val, nvars, ordering_t.ORD_DEGREVLEX) - else: - raise ValueError("Unimplemented term order %s" % ordering) - - super().__init__(nvars, names) - - cpdef slong nvars(self): - """ - Return the number of variables in the context - - >>> ctx = get_fmpq_mpoly_context(4, "lex", 'x') - >>> ctx.nvars() - 4 - """ - return self.val.zctx.minfo.nvars - - cpdef ordering(self): - """ - Return the term order of the context object. - - >>> ctx = get_fmpq_mpoly_context(4, "deglex", 'w') - >>> ctx.ordering() - 'deglex' - """ - if self.val.zctx.minfo.ord == ordering_t.ORD_LEX: - return "lex" - if self.val.zctx.minfo.ord == ordering_t.ORD_DEGLEX: - return "deglex" - if self.val.zctx.minfo.ord == ordering_t.ORD_DEGREVLEX: - return "degrevlex" - - def gen(self, slong i): - """ - Return the `i`th generator of the polynomial ring - - >>> ctx = get_fmpq_mpoly_context(3, 'degrevlex', 'z') - >>> ctx.gen(1) - z1 - """ - cdef fmpq_mpoly res - assert i >= 0 and i < self.val.zctx.minfo.nvars - res = fmpq_mpoly.__new__(fmpq_mpoly) - res.ctx = self - fmpq_mpoly_init(res.val, res.ctx.val) - res._init = True - fmpq_mpoly_gen(res.val, i, res.ctx.val) - return res - - def constant(self, z): - """ - Create a constant polynomial in this context - """ - cdef fmpq_mpoly res - z = any_as_fmpz(z) - if z is NotImplemented: - raise ValueError("A constant fmpq_mpoly is a fmpq") - res = fmpq_mpoly.__new__(fmpq_mpoly) - res.ctx = self - fmpq_mpoly_init(res.val, res.ctx.val) - res._init = True - fmpq_mpoly_set_fmpq(res.val, (z).val, res.ctx.val) - return res - - def fmpq_mpoly_from_dict(self, d): - """ - Create a fmpq_mpoly from a dictionary. - - The dictionary's keys are tuples of ints (or anything that implicitly converts - to fmpz) representing exponents, and corresponding values of fmpq. - - >>> ctx = get_fmpq_mpoly_context(2,'lex','x,y') - >>> ctx.fmpq_mpoly_from_dict({(1,0):2, (1,1):3, (0,1):1}) - 3*x*y + 2*x + y - """ - cdef long n - cdef fmpq_t coefficient - cdef fmpz_struct *exponents - cdef int xtype - cdef int nvars = self.nvars() - cdef int i,j - cdef int count - cdef fmpq_mpoly res - - if not PyDict_Check(d): - raise ValueError("expected a dictionary") - n = PyDict_Size(d) - fmpq_init(coefficient) - exponents = libc.stdlib.calloc(nvars, sizeof(fmpz_struct)) - if exponents == NULL: - raise MemoryError() - for i in range(nvars): - fmpz_init(exponents + i) - fmpq_init(coefficient) - res = fmpq_mpoly.__new__(fmpq_mpoly) - res.ctx = self - fmpq_mpoly_init(res.val, res.ctx.val) - res._init = True - count = 0 - for k,v in d.items(): - coefficient = any_as_fmpq(v) - # xtype = fmpq_set_any_ref(coefficient, v) - # if xtype == FMPZ_UNKNOWN: - # libc.stdlib.free(exponents) - # raise TypeError("invalid coefficient type %s" % type(v)) - if coefficient is NotImplemented: - libc.stdlib.free(exponents) - raise TypeError("invalid coefficient type %s" % type(v)) - if not PyTuple_Check(k): - libc.stdlib.free(exponents) - raise TypeError("Expected tuple of ints as key not %s" % type(k)) - if PyTuple_GET_SIZE(k) != nvars: - libc.stdlib.free(exponents) - raise TypeError("Expected exponent tuple of length %d" % nvars) - for i,tup in enumerate(k): - xtype = fmpz_set_any_ref(exponents + i, tup) - if xtype == FMPZ_UNKNOWN: - libc.stdlib.free(exponents) - raise TypeError("Invalid exponent type %s" % type(tup)) - #Todo lobby for fmpz_mpoly_push_term_fmpz_ffmpz - if not fmpq_is_zero(coefficient): - # fmpq_mpoly_push_term_fmpq_fmpz(res.val, coefficient, exponents, self.val) - _fmpz_mpoly_push_exp_ffmpz(res.val.zpoly, exponents, self.val.zctx) - fmpq_mpoly_set_term_coeff_fmpq(res.val, count, coefficient, self.val) - count += 1 - for i in range(nvars): - fmpz_clear(exponents + i) - fmpq_clear(coefficient) - fmpq_mpoly_sort_terms(res.val, self.val) - return res - - -def get_fmpq_mpoly_context(slong nvars=1, ordering="lex", names='x'): - if nvars <= 0: - nvars = 1 - nametup = tuple(name.strip() for name in names.split(',')) - if len(nametup) != nvars: - if len(nametup) != 1: - raise ValueError("Number of variables does not equal number of names") - nametup = tuple(nametup[0] + str(i) for i in range(nvars)) - key = (nvars, ordering, nametup) - ctx = _fmpq_mpoly_ctx_cache.get(key) - if ctx is None: - ctx = fmpq_mpoly_ctx(nvars, ordering, nametup) - _fmpq_mpoly_ctx_cache[key] = ctx - return ctx - -cdef inline init_fmpq_mpoly(fmpq_mpoly var, fmpq_mpoly_ctx ctx): - var.ctx = ctx - fmpq_mpoly_init(var.val, ctx.val) - var._init = True - -cdef inline create_fmpq_mpoly(fmpq_mpoly_ctx ctx): - cdef fmpq_mpoly var - var = fmpq_mpoly.__new__(fmpq_mpoly) - var.ctx = ctx - fmpq_mpoly_init(var.val, ctx.val) - var._init = True - return var - - - -# todo: store cached context objects externally -cdef class fmpq_mpoly(flint_mpoly): - """ - The *fmpq_poly* type represents sparse multivariate polynomials over - the integers. - """ - - cdef fmpq_mpoly_t val - cdef fmpq_mpoly_ctx ctx - cdef bint _init - - def __cinit__(self): - self._init = False - - def __dealloc__(self): - if self._init: - fmpq_mpoly_clear(self.val, self.ctx.val) - self._init = False - - def __init__(self, val=0, ctx=None): - if typecheck(val, fmpq_mpoly): - if ctx is None or ctx == (val).ctx: - init_fmpq_mpoly(self, (val).ctx) - fmpq_mpoly_set(self.val, (val).val, self.ctx.val) - else: - raise ValueError("Cannot automatically coerce contexts") - elif isinstance(val, dict): - if ctx is None: - if len(val) == 0: - raise ValueError("Need context for zero polynomial") - k = list(val.keys())[0] - if not isinstance(k, tuple): - raise ValueError("Dict should be keyed with tuples of integers") - ctx = get_fmpq_mpoly_context(len(k)) - x = ctx.fmpq_mpoly_from_dict(val) - #XXX this copy is silly, have a ctx function that assigns an fmpq_mpoly_t - init_fmpq_mpoly(self, ctx) - fmpq_mpoly_set(self.val, (x).val, self.ctx.val) - elif isinstance(val, str): - if ctx is None: - raise ValueError("Cannot parse a polynomial without context") - val = bytes(val, 'utf-8') - init_fmpq_mpoly(self, ctx) - fmpq_mpoly_set_str_pretty(self.val, val, self.ctx.c_names, self.ctx.val) - fmpq_mpoly_sort_terms(self.val, self.ctx.val) - else: - v = any_as_fmpq(val) - if v is NotImplemented: - raise TypeError("cannot create fmpq_mpoly from type %s" % type(val)) - if ctx is None: - raise ValueError("Need context to convert fmpq to fmpq_mpoly") - init_fmpq_mpoly(self, ctx) - fmpq_mpoly_set_fmpq(self.val, (v).val, self.ctx.val) - - def __nonzero__(self): - return not fmpq_mpoly_is_zero(self.val, self.ctx.val) - - def __bool__(self): - return not fmpq_mpoly_is_zero(self.val, self.ctx.val) - - def is_one(self): - return fmpq_mpoly_is_one(self.val, self.ctx.val) - - def __richcmp__(self, other, int op): - if op != 2 and op != 3: - return NotImplemented - if typecheck(self, fmpq_mpoly) and typecheck(other, fmpq_mpoly): - if (self).ctx is (other).ctx: - if op == 2: - return bool(fmpq_mpoly_equal((self).val, (other).val, (self).ctx.val)) - else: - return not bool(fmpq_mpoly_equal((self).val, (other).val, (self).ctx.val)) - else: - if op == 2: - return False - else: - return True - if op == 2: - return not bool(self - other) - else: - return bool(self - other) - - def __len__(self): - return fmpq_mpoly_length(self.val, self.ctx.val) - - def coefficient(self, slong i): - cdef fmpq v - if i < 0 or i >= fmpq_mpoly_length(self.val, self.ctx.val): - return fmpq(0) - else: - v = fmpq.__new__(fmpq) - fmpq_mpoly_get_term_coeff_fmpq(v.val, self.val, i, self.ctx.val) - return v - - def exponent_tuple(self, slong i): - cdef slong j, nvars - cdef fmpz_struct ** tmp - if i < 0 or i >= fmpq_mpoly_length(self.val, self.ctx.val): - raise ValueError - nvars = self.ctx.nvars() - res = tuple(fmpz() for j in range(nvars)) - tmp = libc.stdlib.malloc(nvars * sizeof(fmpz_struct *)) - try: - for j in range(nvars): - tmp[j] = &(( (res[j])).val[0]) - fmpq_mpoly_get_term_exp_fmpz(tmp, self.val, i, self.ctx.val) - finally: - libc.stdlib.free(tmp) - return res - - def repr(self): - return self.str() + " (nvars=%s, ordering=%s names=%s)" % (self.ctx.nvars(), self.ctx.ordering(), self.ctx.py_names) - - def str(self): - cdef char * s = fmpq_mpoly_get_str_pretty(self.val, self.ctx.c_names, self.ctx.val) - try: - res = str_from_chars(s) - finally: - libc.stdlib.free(s) - res = res.replace("+", " + ") - res = res.replace("-", " - ") - if res.startswith(" - "): - res = "-" + res[3:] - return res - - return self.repr() - - def __neg__(self): - cdef fmpq_mpoly res - res = create_fmpq_mpoly(self.ctx) - fmpq_mpoly_neg(res.val, (self).val, res.ctx.val) - return res - - def __add__(self, other): - cdef fmpq_mpoly res - if typecheck(other, fmpq_mpoly): - if (self).ctx is not (other).ctx: - return NotImplemented - res = create_fmpq_mpoly(self.ctx) - fmpq_mpoly_add(res.val, (self).val, (other).val, res.ctx.val) - return res - else: - other = any_as_fmpq(other) - if other is not NotImplemented: - res = create_fmpq_mpoly(self.ctx) - fmpq_mpoly_add_fmpq(res.val, (self).val, (other).val, res.ctx.val) - return res - return NotImplemented - - def __radd__(self, other): - cdef fmpq_mpoly res - other = any_as_fmpq(other) - if other is not NotImplemented: - res = create_fmpq_mpoly(self.ctx) - fmpq_mpoly_add_fmpq(res.val, (self).val, (other).val, res.ctx.val) - return res - return NotImplemented - - def __iadd__(self, other): - if typecheck(other, fmpq_mpoly): - if (self).ctx is not (other).ctx: - return NotImplemented - fmpq_mpoly_add((self).val, (self).val, (other).val, self.ctx.val) - return self - else: - other = any_as_fmpq(other) - if other is not NotImplemented: - fmpq_mpoly_add_fmpq((self).val, (self).val, (other).val, self.ctx.val) - return self - return NotImplemented - - def __sub__(self, other): - cdef fmpq_mpoly res - if typecheck(other, fmpq_mpoly): - if (self).ctx is not (other).ctx: - return NotImplemented - res = create_fmpq_mpoly(self.ctx) - fmpq_mpoly_sub(res.val, (self).val, (other).val, res.ctx.val) - return res - else: - other = any_as_fmpq(other) - if other is not NotImplemented: - res = create_fmpq_mpoly(self.ctx) - fmpq_mpoly_sub_fmpq(res.val, (self).val, (other).val, res.ctx.val) - return res - return NotImplemented - - def __rsub__(self, other): - cdef fmpq_mpoly res - other = any_as_fmpq(other) - if other is not NotImplemented: - res = create_fmpq_mpoly(self.ctx) - fmpq_mpoly_sub_fmpq(res.val, (self).val, (other).val, res.ctx.val) - return -res - return NotImplemented - - def __isub__(self, other): - if typecheck(other, fmpq_mpoly): - if (self).ctx is not (other).ctx: - return NotImplemented - fmpq_mpoly_sub((self).val, (self).val, (other).val, self.ctx.val) - return self - else: - other = any_as_fmpq(other) - if other is not NotImplemented: - fmpq_mpoly_sub_fmpq((self).val, (self).val, (other).val, self.ctx.val) - return self - return NotImplemented - - def __mul__(self, other): - cdef fmpq_mpoly res - if typecheck(other, fmpq_mpoly): - if (self).ctx is not (other).ctx: - return NotImplemented - res = create_fmpq_mpoly(self.ctx) - fmpq_mpoly_mul(res.val, (self).val, (other).val, res.ctx.val) - return res - else: - other = any_as_fmpq(other) - if other is not NotImplemented: - res = create_fmpq_mpoly(self.ctx) - fmpq_mpoly_scalar_mul_fmpq(res.val, (self).val, (other).val, res.ctx.val) - return res - return NotImplemented - - def __rmul__(self, other): - cdef fmpq_mpoly res - other = any_as_fmpq(other) - if other is not NotImplemented: - res = create_fmpq_mpoly(self.ctx) - fmpq_mpoly_scalar_mul_fmpq(res.val, (self).val, (other).val, res.ctx.val) - return res - return NotImplemented - - def __imul__(self, other): - if typecheck(other, fmpq_mpoly): - if (self).ctx is not (other).ctx: - return NotImplemented - fmpq_mpoly_mul((self).val, (self).val, (other).val, self.ctx.val) - return self - else: - other = any_as_fmpq(other) - if other is not NotImplemented: - fmpq_mpoly_scalar_mul_fmpq(self.val, (self).val, (other).val, self.ctx.val) - return self - return NotImplemented - - def __pow__(self, other, modulus): - cdef fmpq_mpoly res - if modulus is not None: - raise NotImplementedError - other = any_as_fmpz(other) - if other is NotImplemented: - return other - if other < 0: - raise ValueError("cannot raise fmpq_mpoly to negative power") - res = create_fmpq_mpoly(self.ctx) - if fmpq_mpoly_pow_fmpz(res.val, (self).val, (other).val, res.ctx.val) == 0: - raise ValueError("unreasonably large polynomial") - return res - - def __divmod__(self, other): - cdef fmpq_mpoly res, res2 - if typecheck(other, fmpq_mpoly): - if (self).ctx is not (other).ctx: - return NotImplemented - res = create_fmpq_mpoly(self.ctx) - res2 = create_fmpq_mpoly(self.ctx) - fmpq_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) - return (res, res2) - else: - other = any_as_fmpq(other) - if other is not NotImplemented: - other= fmpq_mpoly(other, self.ctx) - res = create_fmpq_mpoly(self.ctx) - res2 = create_fmpq_mpoly(self.ctx) - fmpq_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) - return (res, res2) - return NotImplemented - - def __rdivmod__(self, other): - cdef fmpq_mpoly res, res2 - other = any_as_fmpq(other) - if other is not NotImplemented: - other = fmpq_mpoly(other, self.ctx) - res = create_fmpq_mpoly(self.ctx) - res2 = create_fmpq_mpoly(self.ctx) - fmpq_mpoly_divrem(res.val, res2.val, (other).val, (self).val, res.ctx.val) - return res - return NotImplemented - - # def __floordiv__(self, other): - # cdef fmpq_mpoly res - # if typecheck(other, fmpq_mpoly): - # if (self).ctx is not (other).ctx: - # return NotImplemented - # res = create_fmpq_mpoly(self.ctx) - # fmpq_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) - # return res - # else: - # other = any_as_fmpq(other) - # if other is not NotImplemented: - # other = fmpq_mpoly(other, self.ctx) - # res = create_fmpq_mpoly(self.ctx) - # fmpq_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) - # return res - # return NotImplemented - - # def __rfloordiv__(self,other): - # cdef fmpq_mpoly res - # other = any_as_fmpq(other) - # if other is not NotImplemented: - # other = fmpq_mpoly(other, self.ctx) - # res = create_fmpq_mpoly(self.ctx) - # fmpq_mpoly_div(res.val, (other).val, self.val, res.ctx.val) - # return res - # return NotImplemented - - def __mod__(self, other): - return divmod(self, other)[1] - - def gcd(self, other): - cdef fmpq_mpoly res - assert isinstance(other, fmpq_mpoly) - if (self).ctx is not (other).ctx: - return NotImplemented - res = create_fmpq_mpoly(self.ctx) - fmpq_mpoly_gcd(res.val, (self).val, (other).val, res.ctx.val) - return res diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 26049d9a..13e7cb14 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -656,7 +656,7 @@ def set_bad(i,j): def test_fmpz_mpoly(): Zp = flint.fmpz_mpoly - getctx = flint.get_fmpz_mpoly_context + getctx = flint.fmpz_mpoly_ctx.get_context ctx = getctx(4) assert ctx.nvars() == 4 assert ctx.ordering() == "lex" @@ -1192,7 +1192,7 @@ def set_bad(i): def test_fmpq_mpoly(): Zp = flint.fmpq_mpoly - getctx = flint.get_fmpq_mpoly_context + getctx = flint.fmpq_mpoly_ctx.get_context ctx = getctx(4) assert ctx.nvars() == 4 assert ctx.ordering() == "lex" diff --git a/src/flint/types/fmpq_mpoly.pxd b/src/flint/types/fmpq_mpoly.pxd index 8240e736..d01e8d7a 100644 --- a/src/flint/types/fmpq_mpoly.pxd +++ b/src/flint/types/fmpq_mpoly.pxd @@ -19,8 +19,6 @@ cdef inline create_fmpq_mpoly(fmpq_mpoly_ctx ctx): var._init = True return var -cdef fmpq_mpoly_ctx create_fmpq_mpoly_ctx_from_fmpz_mpoly_ctx(fmpz_mpoly_ctx ctx) - cdef class fmpq_mpoly_ctx(flint_mpoly_context): cdef fmpq_mpoly_ctx_t val cpdef slong nvars(self) diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 473054d0..76d9cd86 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -1,25 +1,28 @@ from cpython.dict cimport PyDict_Size, PyDict_Check, PyDict_Next from cpython.tuple cimport PyTuple_Check, PyTuple_GET_SIZE + from flint.types.fmpq cimport any_as_fmpq, fmpq, fmpq_set_any_ref from flint.types.fmpz cimport fmpz, fmpz_set_any_ref, any_as_fmpz +from flint.types.fmpz_mpoly cimport fmpz_mpoly, fmpz_mpoly_ctx from flint.utils.typecheck cimport typecheck from flint.utils.conversion cimport str_from_chars -from flint.flintlib.flint cimport * +from flint.flint_base.flint_base cimport flint_mpoly_context + +from flint.flintlib.flint cimport FMPZ_UNKNOWN from flint.flintlib.fmpq cimport fmpq_init, fmpq_clear, fmpq_is_zero, fmpq_set, fmpq_one from flint.flintlib.fmpz cimport fmpz_clear, fmpz_init, fmpz_set from flint.flintlib.fmpz_mpoly cimport fmpz_mpoly_set -from flint.flintlib.fmpq_mpoly cimport * -from flint.flintlib.fmpq_mpoly_factor cimport * - -from flint.types.fmpz_mpoly cimport fmpz_mpoly, fmpz_mpoly_ctx +from flint.flintlib.fmpq_mpoly_factor cimport fmpq_mpoly_factor_t, \ + fmpq_mpoly_factor_init, fmpq_mpoly_factor, fmpq_mpoly_factor_clear, \ + fmpq_mpoly_factor_squarefree cdef extern from *: """ /* An ugly hack to get around the ugly hack of renaming fmpq to avoid a c/python name collision */ typedef fmpq fmpq_struct; - """ + """ cimport cython @@ -27,6 +30,7 @@ cimport libc.stdlib cdef dict _fmpq_mpoly_ctx_cache = {} + @cython.auto_pickle(False) cdef class fmpq_mpoly_ctx(flint_mpoly_context): """ @@ -36,9 +40,10 @@ 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 `get_fmpz_mpoly_context`. + Do not construct one of these directly, use `fmpz_mpoly_ctx.get_context`. """ -# cdef fmpz_mpoly_ctx_t val + + _ctx_cache = _fmpq_mpoly_ctx_cache def __init__(self, slong nvars, ordering, names): if ordering == "lex": @@ -56,7 +61,7 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): """ Return the number of variables in the context - >>> ctx = get_fmpq_mpoly_context(4, "lex", 'x') + >>> ctx = fmpq_mpoly_ctx.get_context(4, "lex", 'x') >>> ctx.nvars() 4 """ @@ -66,7 +71,7 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): """ Return the term order of the context object. - >>> ctx = get_fmpq_mpoly_context(4, "deglex", 'w') + >>> ctx = fmpq_mpoly_ctx.get_context(4, "deglex", 'w') >>> ctx.ordering() 'deglex' """ @@ -76,12 +81,11 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): return "deglex" if self.val.zctx.minfo.ord == ordering_t.ORD_DEGREVLEX: return "degrevlex" - def gen(self, slong i): """ Return the `i`th generator of the polynomial ring - >>> ctx = get_fmpq_mpoly_context(3, 'degrevlex', 'z') + >>> ctx = fmpq_mpoly_ctx.get_context(3, 'degrevlex', 'z') >>> ctx.gen(1) z1 """ @@ -110,123 +114,81 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): return res def fmpq_mpoly_from_dict(self, d): - """ - Create a fmpz_mpoly from a dictionary. - - The dictionary's keys are tuples of ints (or anything that implicitly converts - to fmpz) representing exponents, and corresponding coefficient values of fmpq. - - >>> ctx = get_fmpq_mpoly_context(2,'lex','x,y') - >>> ctx.fmpq_mpoly_from_dict({(1,0):2, (1,1):3, (0,1):1}) - 3*x*y + 2*x + y - """ - cdef long n - cdef fmpq_t coefficient - cdef int xtype - cdef fmpz_struct *exponents - cdef fmpz_struct **exp_ptr - cdef int nvars = self.nvars() - cdef int i,j - cdef int count - cdef fmpq_mpoly res - - if not PyDict_Check(d): - raise ValueError("expected a dictionary") - n = PyDict_Size(d) - exponents = libc.stdlib.calloc(nvars, sizeof(fmpz_struct)) - if exponents == NULL: - raise MemoryError() - exp_ptr = libc.stdlib.calloc(nvars, sizeof(fmpz_struct *)) - if exp_ptr == NULL: - libc.stdlib.free(exponents) - raise MemoryError() - for i in range(nvars): - fmpz_init(exponents + i) - exp_ptr[i] = exponents + i - # fmpq_init(coefficient) - res = fmpq_mpoly.__new__(fmpq_mpoly) - res.ctx = self - fmpq_mpoly_init(res.val, res.ctx.val) - res._init = True - count = 0 - for k,v in d.items(): - xtype = fmpq_set_any_ref(coefficient, v) - if xtype == FMPZ_UNKNOWN: - for i in range(nvars): - fmpz_clear(exponents + i) - libc.stdlib.free(exponents) - libc.stdlib.free(exp_ptr) - raise TypeError("invalid coefficient type %s" % type(v)) - if not PyTuple_Check(k): - for i in range(nvars): - fmpz_clear(exponents + i) - libc.stdlib.free(exponents) - raise TypeError("Expected tuple of ints as key not %s" % type(k)) - if PyTuple_GET_SIZE(k) != nvars: - for i in range(nvars): - fmpz_clear(exponents + i) - libc.stdlib.free(exponents) - raise TypeError("Expected exponent tuple of length %d" % nvars) - for i,tup in enumerate(k): - xtype = fmpz_set_any_ref(exponents + i, tup) - if xtype == FMPZ_UNKNOWN: - for i in range(nvars): - fmpz_clear(exponents + i) - libc.stdlib.free(exponents) - raise TypeError("Invalid exponent type %s" % type(tup)) - #Todo lobby for fmpz_mpoly_push_term_fmpz_ffmpz - if not fmpq_is_zero(coefficient): - fmpq_mpoly_push_term_fmpq_fmpz(res.val, coefficient, exp_ptr, self.val) - # _fmpq_mpoly_push_exp_ffmpz(res.val, exponents, self.val) - # fmpq_mpoly_set_term_coeff_fmpz(res.val, count, coefficient, self.val) - count += 1 - for i in range(nvars): - fmpz_clear(exponents + i) - fmpq_mpoly_sort_terms(res.val, self.val) - fmpq_mpoly_reduce(res.val, self.val) - return res - - -def get_fmpq_mpoly_context(slong nvars=1, ordering="lex", names='x', nametup=None): - if nvars <= 0: - nvars = 1 - if nametup is None: - nametup = tuple(name.strip() for name in names.split(',')) - if len(nametup) != nvars: - if len(nametup) != 1: - raise ValueError("Number of variables does not equal number of names") - nametup = tuple(nametup[0] + str(i) for i in range(nvars)) - else: - if len(nametup) != nvars: - raise ValueError("Number of variables does not equal number of names") - key = (nvars, ordering, nametup) - ctx = _fmpq_mpoly_ctx_cache.get(key) - if ctx is None: - ctx = fmpq_mpoly_ctx(nvars, ordering, nametup) - _fmpq_mpoly_ctx_cache[key] = ctx - return ctx - -cdef fmpq_mpoly_ctx create_fmpq_mpoly_ctx_from_fmpz_mpoly_ctx(fmpz_mpoly_ctx ctx): - return get_fmpq_mpoly_context(nvars=ctx.nvars(), ordering=ctx.ordering(), names=None, - nametup=tuple(str(s, 'utf-8') for s in ctx.py_names)) - - - - -# cdef inline init_fmpq_mpoly(fmpq_mpoly var, fmpq_mpoly_ctx ctx): -# var.ctx = ctx -# fmpq_mpoly_init(var.val, ctx.val) -# var._init = True - -# cdef inline create_fmpq_mpoly(fmpq_mpoly_ctx ctx): -# cdef fmpq_mpoly var -# var = fmpq_mpoly.__new__(fmpq_mpoly) -# var.ctx = ctx -# fmpq_mpoly_init(var.val, ctx.val) -# var._init = True -# return var + """ + Create a fmpz_mpoly from a dictionary. + + The dictionary's keys are tuples of ints (or anything that implicitly converts + to fmpz) representing exponents, and corresponding coefficient values of fmpq. + >>> ctx = fmpq_mpoly_ctx.get_context(2,'lex','x,y') + >>> ctx.fmpq_mpoly_from_dict({(1,0):2, (1,1):3, (0,1):1}) + 3*x*y + 2*x + y + """ + cdef long n + cdef fmpq_t coefficient + cdef int xtype + cdef fmpz_struct *exponents + cdef fmpz_struct **exp_ptr + cdef int nvars = self.nvars() + cdef int i,j + cdef int count + cdef fmpq_mpoly res + if not PyDict_Check(d): + raise ValueError("expected a dictionary") + n = PyDict_Size(d) + exponents = libc.stdlib.calloc(nvars, sizeof(fmpz_struct)) + if exponents == NULL: + raise MemoryError() + exp_ptr = libc.stdlib.calloc(nvars, sizeof(fmpz_struct *)) + if exp_ptr == NULL: + libc.stdlib.free(exponents) + raise MemoryError() + for i in range(nvars): + fmpz_init(exponents + i) + exp_ptr[i] = exponents + i + # fmpq_init(coefficient) + res = fmpq_mpoly.__new__(fmpq_mpoly) + res.ctx = self + fmpq_mpoly_init(res.val, res.ctx.val) + res._init = True + count = 0 + for k, v in d.items(): + xtype = fmpq_set_any_ref(coefficient, v) + if xtype == FMPZ_UNKNOWN: + for i in range(nvars): + fmpz_clear(exponents + i) + libc.stdlib.free(exponents) + libc.stdlib.free(exp_ptr) + raise TypeError("invalid coefficient type %s" % type(v)) + if not PyTuple_Check(k): + for i in range(nvars): + fmpz_clear(exponents + i) + libc.stdlib.free(exponents) + raise TypeError("Expected tuple of ints as key not %s" % type(k)) + if PyTuple_GET_SIZE(k) != nvars: + for i in range(nvars): + fmpz_clear(exponents + i) + libc.stdlib.free(exponents) + raise TypeError("Expected exponent tuple of length %d" % nvars) + for i, tup in enumerate(k): + xtype = fmpz_set_any_ref(exponents + i, tup) + if xtype == FMPZ_UNKNOWN: + for i in range(nvars): + fmpz_clear(exponents + i) + libc.stdlib.free(exponents) + raise TypeError("Invalid exponent type %s" % type(tup)) + #Todo lobby for fmpz_mpoly_push_term_fmpz_ffmpz + if not fmpq_is_zero(coefficient): + fmpq_mpoly_push_term_fmpq_fmpz(res.val, coefficient, exp_ptr, self.val) + # _fmpq_mpoly_push_exp_ffmpz(res.val, exponents, self.val) + # fmpq_mpoly_set_term_coeff_fmpz(res.val, count, coefficient, self.val) + count += 1 + for i in range(nvars): + fmpz_clear(exponents + i) + fmpq_mpoly_sort_terms(res.val, self.val) + fmpq_mpoly_reduce(res.val, self.val) + return res cdef class fmpq_mpoly(flint_mpoly): @@ -235,10 +197,6 @@ cdef class fmpq_mpoly(flint_mpoly): the integers. """ - # cdef fmpz_mpoly_t val - # cdef fmpz_mpoly_ctx ctx - # cdef bint _init - def __cinit__(self): self._init = False @@ -256,8 +214,8 @@ cdef class fmpq_mpoly(flint_mpoly): raise ValueError("Cannot automatically coerce contexts") if typecheck(val, fmpz_mpoly): if ctx is None: - ctx = create_fmpq_mpoly_ctx_from_fmpz_mpoly_ctx((val).ctx) - elif ctx != create_fmpq_mpoly_ctx_from_fmpz_mpoly_ctx((val).ctx): + ctx = fmpq_mpoly_ctx.from_context((val).ctx) + elif ctx != fmpq_mpoly_ctx.from_context((val).ctx): raise ValueError("Cannot automatically coerce contexts") init_fmpq_mpoly(self, ctx) fmpz_mpoly_set(self.val.zpoly, (val).val, ( val).ctx.val) @@ -270,7 +228,7 @@ cdef class fmpq_mpoly(flint_mpoly): k = list(val.keys())[0] if not isinstance(k, tuple): raise ValueError("Dict should be keyed with tuples of integers") - ctx = get_fmpq_mpoly_context(len(k)) + ctx = fmpq_mpoly_ctx.get_context(len(k)) x = ctx.fmpq_mpoly_from_dict(val) #XXX this copy is silly, have a ctx function that assigns an fmpz_mpoly_t init_fmpq_mpoly(self, ctx) @@ -544,17 +502,16 @@ cdef class fmpq_mpoly(flint_mpoly): return res return NotImplemented - def __rfloordiv__(self,other): + def __rfloordiv__(self, other): cdef fmpq_mpoly res other = any_as_fmpq(other) if other is not NotImplemented: other = fmpq_mpoly(other, self.ctx) res = create_fmpq_mpoly(self.ctx) - fmpq_mpoly_div(res.val, (other).val, self.val, res.ctx.val) + fmpq_mpoly_div(res.val, (other).val, self.val, res.ctx.val) return res return NotImplemented - def __mod__(self, other): return divmod(self, other)[1] @@ -567,8 +524,6 @@ cdef class fmpq_mpoly(flint_mpoly): fmpq_mpoly_gcd(res.val, (self).val, (other).val, res.ctx.val) return res - - def factor(self): """ Factors self into irreducible factors, returning a tuple @@ -576,7 +531,7 @@ cdef class fmpq_mpoly(flint_mpoly): factors is a list of (poly, exp) pairs. >>> Zm = fmpq_mpoly - >>> ctx = get_fmpq_mpoly_context(3, 'lex', 'x,y,z') + >>> ctx = fmpq_mpoly_ctx.get_context(3, 'lex', 'x,y,z') >>> p1 = Zm("2*x + 4", ctx) >>> p2 = Zm("3*x*z + + 3*x + 3*z + 3", ctx) >>> (p1 * p2).factor() @@ -613,7 +568,7 @@ cdef class fmpq_mpoly(flint_mpoly): factors is a list of (poly, exp) pairs. >>> Zm = fmpq_mpoly - >>> ctx = get_fmpq_mpoly_context(3, 'lex', 'x,y,z') + >>> ctx = fmpq_mpoly_ctx.get_context(3, 'lex', 'x,y,z') >>> p1 = Zm("2*x + 4", ctx) >>> p2 = Zm("3*x*y + 3*x + 3*y + 3", ctx) >>> (p1 * p2).factor_squarefree() diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 4cd1c39e..a8b72630 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -73,6 +73,7 @@ cdef fmpz_poly_set_list(fmpz_poly_t poly, list val): cdef dict _fmpz_mpoly_ctx_cache = {} + @cython.auto_pickle(False) cdef class fmpz_mpoly_ctx(flint_mpoly_context): """ @@ -82,8 +83,11 @@ 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 `get_fmpz_mpoly_context`. + Do not construct one of these directly, use `fmpz_mpoly_ctx.get_context`. """ + + _ctx_cache = _fmpz_mpoly_ctx_cache + def __init__(self, slong nvars, ordering, names): if ordering == "lex": fmpz_mpoly_ctx_init(self.val, nvars, ordering_t.ORD_LEX) @@ -99,7 +103,7 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): """ Return the number of variables in the context - >>> ctx = get_fmpz_mpoly_context(4, "lex", 'x') + >>> ctx = fmpz_mpoly_ctx.get_context(4, "lex", 'x') >>> ctx.nvars() 4 """ @@ -109,7 +113,7 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): """ Return the term order of the context object. - >>> ctx = get_fmpz_mpoly_context(4, "deglex", 'w') + >>> ctx = fmpz_mpoly_ctx.get_context(4, "deglex", 'w') >>> ctx.ordering() 'deglex' """ @@ -124,7 +128,7 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): """ Return the `i`th generator of the polynomial ring - >>> ctx = get_fmpz_mpoly_context(3, 'degrevlex', 'z') + >>> ctx = fmpz_mpoly_ctx.get_context(3, 'degrevlex', 'z') >>> ctx.gen(1) z1 """ @@ -153,90 +157,75 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): return res def fmpz_mpoly_from_dict(self, d): - """ - Create a fmpz_mpoly from a dictionary. - - The dictionary's keys are tuples of ints (or anything that implicitly converts - to fmpz) representing exponents, and corresponding values of fmpz. - - >>> ctx = get_fmpz_mpoly_context(2,'lex','x,y') - >>> ctx.fmpz_mpoly_from_dict({(1,0):2, (1,1):3, (0,1):1}) - 3*x*y + 2*x + y - """ - cdef long n - cdef fmpz_t coefficient - cdef fmpz_struct *exponents - cdef int xtype - cdef int nvars = self.nvars() - cdef int i,j - cdef int count - cdef fmpz_mpoly res - - if not PyDict_Check(d): - raise ValueError("expected a dictionary") - n = PyDict_Size(d) - fmpz_init(coefficient) - exponents = libc.stdlib.calloc(nvars, sizeof(fmpz_struct)) - if exponents == NULL: - raise MemoryError() - for i in range(nvars): - fmpz_init(exponents + i) - fmpz_init(coefficient) - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = self - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True - count = 0 - for k,v in d.items(): - xtype = fmpz_set_any_ref(coefficient, v) - if xtype == FMPZ_UNKNOWN: - for i in range(nvars): - fmpz_clear(exponents + i) - libc.stdlib.free(exponents) - raise TypeError("invalid coefficient type %s" % type(v)) - if not PyTuple_Check(k): - for i in range(nvars): - fmpz_clear(exponents + i) - libc.stdlib.free(exponents) - raise TypeError("Expected tuple of ints as key not %s" % type(k)) - if PyTuple_GET_SIZE(k) != nvars: - for i in range(nvars): - fmpz_clear(exponents + i) - libc.stdlib.free(exponents) - raise TypeError("Expected exponent tuple of length %d" % nvars) - for i,tup in enumerate(k): - xtype = fmpz_set_any_ref(exponents + i, tup) - if xtype == FMPZ_UNKNOWN: - for i in range(nvars): - fmpz_clear(exponents + i) - libc.stdlib.free(exponents) - raise TypeError("Invalid exponent type %s" % type(tup)) - #Todo lobby for fmpz_mpoly_push_term_fmpz_ffmpz - if not fmpz_is_zero(coefficient): - _fmpz_mpoly_push_exp_ffmpz(res.val, exponents, self.val) - fmpz_mpoly_set_term_coeff_fmpz(res.val, count, coefficient, self.val) - count += 1 - for i in range(nvars): - fmpz_clear(exponents + i) - fmpz_clear(coefficient) - fmpz_mpoly_sort_terms(res.val, self.val) - return res - - -def get_fmpz_mpoly_context(slong nvars=1, ordering="lex", names='x'): - if nvars <= 0: - nvars = 1 - nametup = tuple(name.strip() for name in names.split(',')) - if len(nametup) != nvars: - if len(nametup) != 1: - raise ValueError("Number of variables does not equal number of names") - nametup = tuple(nametup[0] + str(i) for i in range(nvars)) - key = (nvars, ordering, nametup) - ctx = _fmpz_mpoly_ctx_cache.get(key) - if ctx is None: - ctx = fmpz_mpoly_ctx(nvars, ordering, nametup) - _fmpz_mpoly_ctx_cache[key] = ctx - return ctx + """ + Create a fmpz_mpoly from a dictionary. + + The dictionary's keys are tuples of ints (or anything that implicitly converts + to fmpz) representing exponents, and corresponding values of fmpz. + + >>> ctx = fmpz_mpoly_ctx.get_context(2,'lex','x,y') + >>> ctx.fmpz_mpoly_from_dict({(1,0):2, (1,1):3, (0,1):1}) + 3*x*y + 2*x + y + """ + cdef long n + cdef fmpz_t coefficient + cdef fmpz_struct *exponents + cdef int xtype + cdef int nvars = self.nvars() + cdef int i,j + cdef int count + cdef fmpz_mpoly res + + if not PyDict_Check(d): + raise ValueError("expected a dictionary") + n = PyDict_Size(d) + fmpz_init(coefficient) + exponents = libc.stdlib.calloc(nvars, sizeof(fmpz_struct)) + if exponents == NULL: + raise MemoryError() + for i in range(nvars): + fmpz_init(exponents + i) + fmpz_init(coefficient) + res = fmpz_mpoly.__new__(fmpz_mpoly) + res.ctx = self + fmpz_mpoly_init(res.val, res.ctx.val) + res._init = True + count = 0 + for k, v in d.items(): + xtype = fmpz_set_any_ref(coefficient, v) + if xtype == FMPZ_UNKNOWN: + for i in range(nvars): + fmpz_clear(exponents + i) + libc.stdlib.free(exponents) + raise TypeError("invalid coefficient type %s" % type(v)) + if not PyTuple_Check(k): + for i in range(nvars): + fmpz_clear(exponents + i) + libc.stdlib.free(exponents) + raise TypeError("Expected tuple of ints as key not %s" % type(k)) + if PyTuple_GET_SIZE(k) != nvars: + for i in range(nvars): + fmpz_clear(exponents + i) + libc.stdlib.free(exponents) + raise TypeError("Expected exponent tuple of length %d" % nvars) + for i, tup in enumerate(k): + xtype = fmpz_set_any_ref(exponents + i, tup) + if xtype == FMPZ_UNKNOWN: + for i in range(nvars): + fmpz_clear(exponents + i) + libc.stdlib.free(exponents) + raise TypeError("Invalid exponent type %s" % type(tup)) + #Todo lobby for fmpz_mpoly_push_term_fmpz_ffmpz + if not fmpz_is_zero(coefficient): + _fmpz_mpoly_push_exp_ffmpz(res.val, exponents, self.val) + fmpz_mpoly_set_term_coeff_fmpz(res.val, count, coefficient, self.val) + count += 1 + for i in range(nvars): + fmpz_clear(exponents + i) + fmpz_clear(coefficient) + fmpz_mpoly_sort_terms(res.val, self.val) + return res + # cdef _fmpz_mpoly_set2(fmpz_mpoly_t out, fmpz_mpoly_ctx_t outctx, fmpz_mpoly_t inp, fmpz_mpoly_ctx_t inpctx): # cdef slong * C @@ -257,7 +246,7 @@ def get_fmpz_mpoly_context(slong nvars=1, ordering="lex", names='x'): # def coerce_fmpz_mpolys(*args): # cdef fmpz_mpoly_ctx ctx -# ctx = get_fmpz_mpoly_context() +# ctx = fmpz_mpoly_ctx.get_context() # if not args: # return ctx, [] # args = list(args) @@ -269,7 +258,7 @@ def get_fmpz_mpoly_context(slong nvars=1, ordering="lex", names='x'): # if not typecheck(args[i], fmpz_mpoly): # args[i] = fmpz_mpoly(args[i]) # nvars = max((pol).ctx.nvars() for pol in args) -# ctx = get_fmpz_mpoly_context(nvars) +# ctx = fmpz_mpoly_ctx.get_context(nvars) # args2 = [fmpz_mpoly() for i in range(len(args))] # for i in range(len(args)): # ( args2[i]).ctx = ctx @@ -277,18 +266,12 @@ def get_fmpz_mpoly_context(slong nvars=1, ordering="lex", names='x'): # return ctx, args2 - -# todo: store cached context objects externally cdef class fmpz_mpoly(flint_mpoly): """ The *fmpz_poly* type represents sparse multivariate polynomials over the integers. """ - # cdef fmpz_mpoly_t val - # cdef fmpz_mpoly_ctx ctx - # cdef bint _init - def __cinit__(self): self._init = False @@ -311,7 +294,7 @@ cdef class fmpz_mpoly(flint_mpoly): k = list(val.keys())[0] if not isinstance(k, tuple): raise ValueError("Dict should be keyed with tuples of integers") - ctx = get_fmpz_mpoly_context(len(k)) + ctx = fmpz_mpoly_ctx.get_context(len(k)) x = ctx.fmpz_mpoly_from_dict(val) #XXX this copy is silly, have a ctx function that assigns an fmpz_mpoly_t init_fmpz_mpoly(self, ctx) @@ -716,7 +699,6 @@ cdef class fmpz_mpoly(flint_mpoly): return ret return NotImplemented - def __mod__(self, other): return divmod(self, other)[1] @@ -781,7 +763,7 @@ cdef class fmpz_mpoly(flint_mpoly): factors is a list of (poly, exp) pairs. >>> Zm = fmpz_mpoly - >>> ctx = get_fmpz_mpoly_context(3, 'lex', 'x,y,z') + >>> ctx = fmpz_mpoly_ctx.get_context(3, 'lex', 'x,y,z') >>> p1 = Zm("2*x + 4", ctx) >>> p2 = Zm("3*x*z + + 3*x + 3*z + 3", ctx) >>> (p1 * p2).factor() @@ -817,7 +799,7 @@ cdef class fmpz_mpoly(flint_mpoly): factors is a list of (poly, exp) pairs. >>> Zm = fmpz_mpoly - >>> ctx = get_fmpz_mpoly_context(3, 'lex', 'x,y,z') + >>> ctx = fmpz_mpoly_ctx.get_context(3, 'lex', 'x,y,z') >>> p1 = Zm("2*x + 4", ctx) >>> p2 = Zm("3*x*y + 3*x + 3*y + 3", ctx) >>> (p1 * p2).factor_squarefree() From 9e14bcb8790c3291216dbfd2a7f84f37708ca389 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Sun, 14 Apr 2024 23:29:28 +1000 Subject: [PATCH 23/95] Reimplement fmpz_mpoly.__call__, add joint_context method --- src/flint/flint_base/flint_base.pyx | 13 +- src/flint/types/fmpz_mpoly.pyx | 215 +++++++++++++++++----------- 2 files changed, 145 insertions(+), 83 deletions(-) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index 65afb0f2..9ec3da70 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -135,7 +135,7 @@ cdef class flint_mpoly_context(flint_elem): def __init__(self, long nvars, names): assert nvars >= 1 assert len(names) == nvars - self.py_names = tuple(bytes(name, 'utf-8') for name in names) + self.py_names = tuple(bytes(name, 'utf-8') if not isinstance(name, bytes) else name for name in names) self.c_names = libc.stdlib.malloc(nvars * sizeof(char *)) self._init = True for i in range(nvars): @@ -146,10 +146,16 @@ cdef class flint_mpoly_context(flint_elem): libc.stdlib.free(self.c_names) self._init = False + def __repr__(self): + return f"{self.__class__.__name__}({self.nvars()}, '{self.ordering()}', {self.names()})" + def name(self, long i): assert i >= 0 and i < len(self.py_names) return self.py_names[i].decode('utf-8') + def names(self): + return tuple(name.decode('utf-8') for name in self.py_names) + def gens(self): return tuple(self.gen(i) for i in range(self.nvars())) @@ -189,6 +195,11 @@ cdef class flint_mpoly_context(flint_elem): nametup=tuple(str(s, 'utf-8') for s in ctx.py_names) ) + @classmethod + def joint_context(cls, ctxs): + vars = {x: i for i, x in enumerate({var for ctx in ctxs for var in ctx.py_names})} + return cls.get_context(nvars=len(vars), nametup=tuple(vars.keys())), vars + cdef class flint_mpoly(flint_elem): """ diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index a8b72630..1ab6dae0 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -29,7 +29,7 @@ cdef extern from *: """ /* An ugly hack to get around the ugly hack of renaming fmpq to avoid a c/python name collision */ typedef fmpq fmpq_struct; - """ + """ cdef any_as_fmpz_mpoly(x): @@ -227,43 +227,44 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): return res -# cdef _fmpz_mpoly_set2(fmpz_mpoly_t out, fmpz_mpoly_ctx_t outctx, fmpz_mpoly_t inp, fmpz_mpoly_ctx_t inpctx): -# cdef slong * C -# cdef slong i -# cdef slong inpvars, outvars -# if outctx == inpctx: -# fmpz_mpoly_set(out, inp, inpctx) -# else: -# inpvars = inpctx.minfo.nvars -# outvars = inpctx.minfo.nvars -# C = libc.stdlib.malloc(inpvars * sizeof(slong *)) -# for i in range(min(outvars, inpvars)): -# C[i] = i -# for i in range(outvars, inpvars): -# C[i] = -1 -# fmpz_mpoly_compose_fmpz_mpoly_gen(out, inp, C, inpctx, outctx) -# libc.stdlib.free(C) - -# def coerce_fmpz_mpolys(*args): -# cdef fmpz_mpoly_ctx ctx -# ctx = fmpz_mpoly_ctx.get_context() -# if not args: -# return ctx, [] -# args = list(args) -# if typecheck(args[0], fmpz_mpoly): -# ctx = ( args[0]).ctx -# if all(typecheck(args[i], fmpz_mpoly) and ( args[i]).ctx is ctx for i in range(1, len(args))): -# return ctx, args -# for i in range(len(args)): -# if not typecheck(args[i], fmpz_mpoly): -# args[i] = fmpz_mpoly(args[i]) -# nvars = max((pol).ctx.nvars() for pol in args) -# ctx = fmpz_mpoly_ctx.get_context(nvars) -# args2 = [fmpz_mpoly() for i in range(len(args))] -# for i in range(len(args)): -# ( args2[i]).ctx = ctx -# _fmpz_mpoly_set2(( args2[i]).val, ctx.val, ( args[i]).val, ( args[i]).ctx.val) -# return ctx, args2 +def coerce_fmpz_mpolys(args): + cdef: + fmpz_mpoly_ctx ctx + fmpz_mpoly inpoly, outpoly + slong *C + slong i + + if not args: + return ctx, [] + + # If all arguments are fmpz_mpolys and share the same context then nothing needs to be done + if typecheck(args[0], fmpz_mpoly): + ctx = ( args[0]).ctx + if all(typecheck(args[i], fmpz_mpoly) and ( args[i]).ctx is ctx for i in range(1, len(args))): + return ctx, list(args) + + for i in range(len(args)): + if not typecheck(args[i], fmpz_mpoly): + args[i] = fmpz_mpoly(args[i]) + + ctx, vars = fmpz_mpoly_ctx.joint_context((inpoly).ctx for inpoly in args) + + out = [fmpz_mpoly.__new__(fmpz_mpoly) for _ in range(len(args))] + + nvars = max((inpoly).ctx.nvars() for inpoly in args) + C = libc.stdlib.malloc(nvars * sizeof(slong *)) + for inpoly, outpoly in zip(args, out): + inpoly = inpoly + outpoly = outpoly + + init_fmpz_mpoly(outpoly, ctx) + for i, var in enumerate(inpoly.ctx.py_names): + C[i] = vars[var] + + fmpz_mpoly_compose_fmpz_mpoly_gen(outpoly.val, inpoly.val, C, inpoly.ctx.val, ctx.val) + + libc.stdlib.free(C) + return ctx, out cdef class fmpz_mpoly(flint_mpoly): @@ -711,50 +712,100 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_gcd(res.val, (self).val, (other).val, res.ctx.val) return res - # def __call__(self, *args): - # cdef fmpz_mpoly res - # cdef fmpz_mpoly_ctx res_ctx - # cdef fmpz_struct ** V - # cdef fmpz vres - # cdef fmpz_mpoly_struct ** C - # cdef slong i, nvars, nargs - # other = tuple(args) - # nargs = len(args) - # nvars = self.ctx.nvars() - # # todo: should extend with generators instead? - # if nargs < nvars: - # args = args + (0,) * (nvars - nargs) - # if nargs > nvars: - # args = args[:nvars] - # args_fmpz = [any_as_fmpz(v) for v in args] - # # todo: for combination, compose - # # todo: if fewer than number of variables, evaluate partially? - # if NotImplemented not in args_fmpz: - # V = libc.stdlib.malloc(nvars * sizeof(fmpz_struct *)) - # try: - # for i in range(nvars): - # V[i] = &(( args_fmpz[i]).val[0]) - # vres = fmpz.__new__(fmpz) - # if fmpz_mpoly_evaluate_all_fmpz(vres.val, self.val, V, self.ctx.val) == 0: - # raise ValueError("unreasonably large polynomial") - # return vres - # finally: - # libc.stdlib.free(V) - # else: - # res_ctx, args = coerce_fmpz_mpolys(*args) - # C = libc.stdlib.malloc(nvars * sizeof(fmpz_mpoly_struct *)) - # try: - # for i in range(nvars): - # C[i] = &(( args[i]).val[0]) - # res = fmpz_mpoly.__new__(fmpz_mpoly) - # res.ctx = res_ctx - # fmpz_mpoly_init(res.val, res.ctx.val) - # res._init = True - # if fmpz_mpoly_compose_fmpz_mpoly(res.val, self.val, C, self.ctx.val, res_ctx.val) == 0: - # raise ValueError("unreasonably large polynomial") - # return res - # finally: - # libc.stdlib.free(C) + def __call__(self, *args, **kwargs): + cdef: + fmpz_mpoly res + fmpz_mpoly res2 + fmpz_mpoly_ctx res_ctx + fmpz_struct ** V + fmpz vres + fmpz_mpoly_struct ** C + slong i, nvars = self.ctx.nvars(), nargs = len(args) + + if args and kwargs: + raise ValueError("only supply positional or keyword arguments") + + if kwargs: + # Sort and filter the provided keyword args to only valid ones + args = tuple((i, kwargs[x]) for i, x in enumerate(self.ctx.names()) if x in kwargs) + nargs = len(args) + + # If we've been provided with an invalid keyword arg then the length of our filter + # args will be less than what we've been provided with. + # If the length is equal to the number of variables then all arguments have been provided + # otherwise we need to do partial application + if nargs < len(kwargs): + raise ValueError("unknown keyword argument provided") + elif nargs == nvars: + args = tuple(x for _, x in args) + partial = False + else: + partial = True + elif nargs > nvars: + raise ValueError("more arguments provided than variables") + elif nargs < nvars: + # Positional partial application + args = tuple((i, x) for i, x in enumerate(args)) + partial = True + + if partial: + args_fmpz = tuple(any_as_fmpz(v) for _, v in args) + else: + args_fmpz = tuple(any_as_fmpz(v) for v in args) + + all_fmpz = NotImplemented not in args_fmpz + # if NotImplemented in args_fmpz: + # raise NotImplementedError("an argument was unable to be coerced to fmpz") + + if not partial and all_fmpz: + try: + V = libc.stdlib.malloc(nvars * sizeof(fmpz_struct *)) + for i in range(nvars): + V[i] = &(( args_fmpz[i]).val[0]) + vres = fmpz.__new__(fmpz) + if fmpz_mpoly_evaluate_all_fmpz(vres.val, self.val, V, self.ctx.val) == 0: + raise ValueError("unreasonably large polynomial") + return vres + finally: + libc.stdlib.free(V) + elif partial and all_fmpz: + res = fmpz_mpoly.__new__(fmpz_mpoly) + res2 = fmpz_mpoly.__new__(fmpz_mpoly) + res.ctx = self.ctx + res2.ctx = self.ctx + + fmpz_mpoly_set(res2.val, self.val, self.ctx.val) + for (i, _), arg in zip(args, args_fmpz): + if fmpz_mpoly_evaluate_one_fmpz(res.val, res2.val, i, (arg).val, self.ctx.val) == 0: + raise ValueError("unreasonably large polynomial") + fmpz_mpoly_set(res2.val, res.val, self.ctx.val) + return res + elif not partial and not all_fmpz: + res_ctx, args = coerce_fmpz_mpolys(args) + C = libc.stdlib.malloc(nvars * sizeof(fmpz_mpoly_struct *)) + try: + for i in range(nvars): + C[i] = &(( args[i]).val[0]) + res = fmpz_mpoly.__new__(fmpz_mpoly) + res.ctx = res_ctx + fmpz_mpoly_init(res.val, res.ctx.val) + res._init = True + if fmpz_mpoly_compose_fmpz_mpoly(res.val, self.val, C, self.ctx.val, res_ctx.val) == 0: + raise ValueError("unreasonably large polynomial") + return res + finally: + libc.stdlib.free(C) + else: + raise NotImplemented("partial composition not implemented") + # res_ctx, new_args = coerce_fmpz_mpolys(tuple(x for _, x in args)) + + # polys = [None] * nvars + # for (i, _), poly in zip(args, new_args): + # polys[i] = poly + + # for i in range(nvars): + # if polys[i] is None: + # polys[i] = fmpz_mpoly({ : }) def factor(self): """ From d5e2b56076a14d75f13f4fc065715c978da0993e Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Mon, 15 Apr 2024 23:01:05 +1000 Subject: [PATCH 24/95] Make joint context creation and coercion explicit --- src/flint/flint_base/flint_base.pyx | 17 ++++++-- src/flint/types/fmpq_mpoly.pyx | 1 + src/flint/types/fmpz_mpoly.pyx | 65 +++++++++++------------------ 3 files changed, 40 insertions(+), 43 deletions(-) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index 9ec3da70..953abd7a 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -5,7 +5,9 @@ from flint.flintlib.flint cimport ( slong ) from flint.flint_base.flint_context cimport thectx +from flint.utils.typecheck cimport typecheck cimport libc.stdlib + from typing import Optional @@ -146,6 +148,9 @@ cdef class flint_mpoly_context(flint_elem): libc.stdlib.free(self.c_names) self._init = False + def __str__(self): + return self.__repr__() + def __repr__(self): return f"{self.__class__.__name__}({self.nvars()}, '{self.ordering()}', {self.names()})" @@ -196,9 +201,15 @@ cdef class flint_mpoly_context(flint_elem): ) @classmethod - def joint_context(cls, ctxs): - vars = {x: i for i, x in enumerate({var for ctx in ctxs for var in ctx.py_names})} - return cls.get_context(nvars=len(vars), nametup=tuple(vars.keys())), vars + def joint_context(cls, *ctxs): + vars = set() + for ctx in ctxs: + if not typecheck(ctx, cls): + raise ValueError(f"{ctx} is not a {cls}") + else: + for var in ctx.py_names: + vars.add(var) + return cls.get_context(nvars=len(vars), nametup=tuple(vars)) cdef class flint_mpoly(flint_elem): diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 76d9cd86..6844793e 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -81,6 +81,7 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): return "deglex" if self.val.zctx.minfo.ord == ordering_t.ORD_DEGREVLEX: return "degrevlex" + def gen(self, slong i): """ Return the `i`th generator of the polynomial ring diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 1ab6dae0..ecdf0853 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -227,46 +227,6 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): return res -def coerce_fmpz_mpolys(args): - cdef: - fmpz_mpoly_ctx ctx - fmpz_mpoly inpoly, outpoly - slong *C - slong i - - if not args: - return ctx, [] - - # If all arguments are fmpz_mpolys and share the same context then nothing needs to be done - if typecheck(args[0], fmpz_mpoly): - ctx = ( args[0]).ctx - if all(typecheck(args[i], fmpz_mpoly) and ( args[i]).ctx is ctx for i in range(1, len(args))): - return ctx, list(args) - - for i in range(len(args)): - if not typecheck(args[i], fmpz_mpoly): - args[i] = fmpz_mpoly(args[i]) - - ctx, vars = fmpz_mpoly_ctx.joint_context((inpoly).ctx for inpoly in args) - - out = [fmpz_mpoly.__new__(fmpz_mpoly) for _ in range(len(args))] - - nvars = max((inpoly).ctx.nvars() for inpoly in args) - C = libc.stdlib.malloc(nvars * sizeof(slong *)) - for inpoly, outpoly in zip(args, out): - inpoly = inpoly - outpoly = outpoly - - init_fmpz_mpoly(outpoly, ctx) - for i, var in enumerate(inpoly.ctx.py_names): - C[i] = vars[var] - - fmpz_mpoly_compose_fmpz_mpoly_gen(outpoly.val, inpoly.val, C, inpoly.ctx.val, ctx.val) - - libc.stdlib.free(C) - return ctx, out - - cdef class fmpz_mpoly(flint_mpoly): """ The *fmpz_poly* type represents sparse multivariate polynomials over @@ -878,3 +838,28 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_set((c).val, fac.constant) fmpz_mpoly_factor_clear(fac, self.ctx.val) return c, res + + def coerce_to_context(self, ctx): + cdef: + fmpz_mpoly outpoly + slong *C + slong i + + if not typecheck(ctx, fmpz_mpoly_ctx): + raise ValueError("provided context is not a fmpz_mpoly_ctx") + + if self.ctx is ctx: + return self + + C = libc.stdlib.malloc(self.ctx.val.minfo.nvars * sizeof(slong *)) + outpoly = fmpz_mpoly.__new__(fmpz_mpoly) + init_fmpz_mpoly(outpoly, ctx) + + vars = {x: i for i, x in enumerate(ctx.py_names)} + for i, var in enumerate(self.ctx.py_names): + C[i] = vars[var] + + fmpz_mpoly_compose_fmpz_mpoly_gen(outpoly.val, self.val, C, self.ctx.val, (ctx).val) + + libc.stdlib.free(C) + return outpoly From 941041c42313a824533ada2de24039d83ae0140d Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Mon, 15 Apr 2024 23:01:48 +1000 Subject: [PATCH 25/95] Implemented fmpz_mpoly partial function composition --- src/flint/types/fmpz_mpoly.pyx | 51 +++++++++++++++++++++++----------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index ecdf0853..e2bf07b8 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -18,7 +18,6 @@ from flint.types.fmpq cimport any_as_fmpq, fmpq_set_any_ref from flint.types.fmpq_mpoly cimport fmpq_mpoly from flint.types.fmpz_mpoly_q cimport fmpz_mpoly_q - cimport cython cimport libc.stdlib from flint.flintlib.fmpz cimport fmpz_set @@ -680,10 +679,13 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_struct ** V fmpz vres fmpz_mpoly_struct ** C - slong i, nvars = self.ctx.nvars(), nargs = len(args) + ulong *exponents + slong i, prev_i, nvars = self.ctx.nvars(), nargs = len(args) if args and kwargs: raise ValueError("only supply positional or keyword arguments") + elif not args and not kwargs: + return self if kwargs: # Sort and filter the provided keyword args to only valid ones @@ -714,8 +716,6 @@ cdef class fmpz_mpoly(flint_mpoly): args_fmpz = tuple(any_as_fmpz(v) for v in args) all_fmpz = NotImplemented not in args_fmpz - # if NotImplemented in args_fmpz: - # raise NotImplementedError("an argument was unable to be coerced to fmpz") if not partial and all_fmpz: try: @@ -741,31 +741,50 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_set(res2.val, res.val, self.ctx.val) return res elif not partial and not all_fmpz: - res_ctx, args = coerce_fmpz_mpolys(args) + res_ctx = ( args[0]).ctx + if not all(typecheck(args[i], fmpz_mpoly) and ( args[i]).ctx is res_ctx for i in range(1, len(args))): + raise ValueError("all arguments must share the same context") + C = libc.stdlib.malloc(nvars * sizeof(fmpz_mpoly_struct *)) try: for i in range(nvars): C[i] = &(( args[i]).val[0]) res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = res_ctx - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True + init_fmpz_mpoly(res, res_ctx) if fmpz_mpoly_compose_fmpz_mpoly(res.val, self.val, C, self.ctx.val, res_ctx.val) == 0: raise ValueError("unreasonably large polynomial") return res finally: libc.stdlib.free(C) else: - raise NotImplemented("partial composition not implemented") - # res_ctx, new_args = coerce_fmpz_mpolys(tuple(x for _, x in args)) + if not all(typecheck(arg, fmpz_mpoly) and ( arg).ctx is self.ctx for _, arg in args): + raise ValueError("the mpoly and all arguments must share the same context") - # polys = [None] * nvars - # for (i, _), poly in zip(args, new_args): - # polys[i] = poly + polys = [None] * nvars + for i, poly in args: + polys[i] = poly - # for i in range(nvars): - # if polys[i] is None: - # polys[i] = fmpz_mpoly({ : }) + for i in range(nvars): + if polys[i] is None: + res = fmpz_mpoly.__new__(fmpz_mpoly) + init_fmpz_mpoly(res, self.ctx) + exponents = libc.stdlib.calloc(nvars, sizeof(ulong)) + exponents[i] = 1 + fmpz_mpoly_push_term_ui_ui(res.val, 1, exponents, self.ctx.val) + + polys[i] = res + + C = libc.stdlib.malloc(nvars * sizeof(fmpz_mpoly_struct *)) + try: + for i in range(len(polys)): + C[i] = &(( polys[i]).val[0]) + res = fmpz_mpoly.__new__(fmpz_mpoly) + init_fmpz_mpoly(res, self.ctx) + if fmpz_mpoly_compose_fmpz_mpoly(res.val, self.val, C, self.ctx.val, self.ctx.val) == 0: + raise ValueError("unreasonably large polynomial") + return res + finally: + libc.stdlib.free(C) def factor(self): """ From 2ba6a29dd82c25661b180622b86f171e0ae87f3f Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Mon, 15 Apr 2024 23:18:44 +1000 Subject: [PATCH 26/95] Disallow positional partial application This caused too many headaches during testing. Since the order of variables within a context is not stable during joint context construction, it's simply one giant walking gotcha. --- src/flint/types/fmpz_mpoly.pyx | 45 ++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index e2bf07b8..7f7b5954 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -680,17 +680,19 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz vres fmpz_mpoly_struct ** C ulong *exponents - slong i, prev_i, nvars = self.ctx.nvars(), nargs = len(args) + slong i, nvars = self.ctx.nvars(), nargs = len(args) - if args and kwargs: - raise ValueError("only supply positional or keyword arguments") - elif not args and not kwargs: + if not args and not kwargs: return self + elif args and kwargs: + raise ValueError("only supply positional or keyword arguments") + elif len(args) < nvars and not kwargs: + raise ValueError("partial application requires keyword arguments") if kwargs: # Sort and filter the provided keyword args to only valid ones - args = tuple((i, kwargs[x]) for i, x in enumerate(self.ctx.names()) if x in kwargs) - nargs = len(args) + partial_args = tuple((i, kwargs[x]) for i, x in enumerate(self.ctx.names()) if x in kwargs) + nargs = len(partial_args) # If we've been provided with an invalid keyword arg then the length of our filter # args will be less than what we've been provided with. @@ -699,25 +701,20 @@ cdef class fmpz_mpoly(flint_mpoly): if nargs < len(kwargs): raise ValueError("unknown keyword argument provided") elif nargs == nvars: - args = tuple(x for _, x in args) - partial = False - else: - partial = True + args = tuple(arg for _, arg in partial_args) + kwargs = None elif nargs > nvars: raise ValueError("more arguments provided than variables") - elif nargs < nvars: - # Positional partial application - args = tuple((i, x) for i, x in enumerate(args)) - partial = True - if partial: - args_fmpz = tuple(any_as_fmpz(v) for _, v in args) + if kwargs: + args_fmpz = tuple(any_as_fmpz(v) for _, v in partial_args) else: args_fmpz = tuple(any_as_fmpz(v) for v in args) all_fmpz = NotImplemented not in args_fmpz - if not partial and all_fmpz: + if args and all_fmpz: + # Normal evaluation try: V = libc.stdlib.malloc(nvars * sizeof(fmpz_struct *)) for i in range(nvars): @@ -728,19 +725,21 @@ cdef class fmpz_mpoly(flint_mpoly): return vres finally: libc.stdlib.free(V) - elif partial and all_fmpz: + elif kwargs and all_fmpz: + # Partial application with args in Z. We evaluate the polynomial one variable at a time res = fmpz_mpoly.__new__(fmpz_mpoly) res2 = fmpz_mpoly.__new__(fmpz_mpoly) res.ctx = self.ctx res2.ctx = self.ctx fmpz_mpoly_set(res2.val, self.val, self.ctx.val) - for (i, _), arg in zip(args, args_fmpz): + for (i, _), arg in zip(partial_args, args_fmpz): if fmpz_mpoly_evaluate_one_fmpz(res.val, res2.val, i, (arg).val, self.ctx.val) == 0: raise ValueError("unreasonably large polynomial") fmpz_mpoly_set(res2.val, res.val, self.ctx.val) return res - elif not partial and not all_fmpz: + elif args and not all_fmpz: + # Complete function composition res_ctx = ( args[0]).ctx if not all(typecheck(args[i], fmpz_mpoly) and ( args[i]).ctx is res_ctx for i in range(1, len(args))): raise ValueError("all arguments must share the same context") @@ -757,11 +756,15 @@ cdef class fmpz_mpoly(flint_mpoly): finally: libc.stdlib.free(C) else: + # Partial function composition. We do this by composing with all arguments, however the ones + # that have not be provided are set to the trivial monomial. This is why we require all the + # polynomial itself, and all arguments exist in the same context, otherwise we have no way of + # finding the correct monomial to use. if not all(typecheck(arg, fmpz_mpoly) and ( arg).ctx is self.ctx for _, arg in args): raise ValueError("the mpoly and all arguments must share the same context") polys = [None] * nvars - for i, poly in args: + for i, poly in partial_args: polys[i] = poly for i in range(nvars): From 109fad8c2de58d6bea0e106a6180bdf560d41355 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Sun, 21 Apr 2024 23:52:26 +1000 Subject: [PATCH 27/95] Revamp fmpz_mpoly tests, add a handful of functions Add getitem and setitem, add derivative and integral, and degree and total_degree. Add IncompatibleContextError. Fix behaviour of div functions to align with new domain errors. Add sqrt. I'm experiencing a really weird issue with test coverage atm, it's influencing the out come of the getitem tests? TODO: Remove coercion in operators, copy work over to fmpq_mpoly --- src/flint/test/test.py | 420 ++++++++++++++++++++++------ src/flint/types/fmpz_mpoly.pyx | 282 ++++++++++++++----- src/flint/utils/flint_exceptions.py | 8 + 3 files changed, 550 insertions(+), 160 deletions(-) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 13e7cb14..49e2e1dc 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -5,7 +5,7 @@ import doctest import platform -from flint.utils.flint_exceptions import DomainError +from flint.utils.flint_exceptions import DomainError, IncompatibleContextError import flint @@ -654,62 +654,6 @@ def set_bad(i,j): assert M6.minpoly() == flint.fmpz_poly([4,-4,1]) assert list(M6) == [2,0,0,0,2,1,0,0,2] -def test_fmpz_mpoly(): - Zp = flint.fmpz_mpoly - getctx = flint.fmpz_mpoly_ctx.get_context - ctx = getctx(4) - assert ctx.nvars() == 4 - assert ctx.ordering() == "lex" - assert [ctx.name(i) for i in range(4)] == ['x0', 'x1', 'x2', 'x3'] - for order in ['lex', 'deglex', 'degrevlex']: - ctx1 = getctx(4, order) - assert ctx1.ordering() == order - ctx = getctx(4, "lex", 'w,x,y,z') - p1 = ctx.gen(0) + ctx.gen(1) - ctx.gen(2) * ctx.gen(3) - assert p1 == Zp("w + x - y * z", ctx) - ctx = getctx(2, "lex", "x,y") - assert ctx.fmpz_mpoly_from_dict({(1,0):1, (0,1):2}) == Zp("x + 2*y", ctx) - assert raises(lambda: ctx.fmpz_mpoly_from_dict("b"), ValueError) - assert raises(lambda: ctx.fmpz_mpoly_from_dict({(1,2):"b"}), TypeError) - assert raises(lambda: ctx.fmpz_mpoly_from_dict({"b":1}), TypeError) - assert raises(lambda: ctx.fmpz_mpoly_from_dict({(1,2,3):1}), TypeError) - assert raises(lambda: ctx.fmpz_mpoly_from_dict({(1,"a"):1}), TypeError) - ctx = getctx(2, "lex", 'x,y') - p1 = ctx.fmpz_mpoly_from_dict({(1,0):4,(0,3):4,(2,4):9}) - for ztype in [int, long, flint.fmpz]: - assert p1 + ztype(3) == ctx.fmpz_mpoly_from_dict({(1,0):4,(0,3):4,(2,4):9,(0,0):3}) - assert ztype(3) + p1 == ctx.fmpz_mpoly_from_dict({(1,0):4,(0,3):4,(2,4):9,(0,0):3}) - assert p1 - ztype(3) == ctx.fmpz_mpoly_from_dict({(1,0):4,(0,3):4,(2,4):9,(0,0):-3}) - assert ztype(3) - p1 == ctx.fmpz_mpoly_from_dict({(1,0):-4,(0,3):-4,(2,4):-9,(0,0):3}) - assert p1 * ztype(3) == ctx.fmpz_mpoly_from_dict({(1,0):12,(0,3):12,(2,4):27}) - assert ztype(3) * p1 == ctx.fmpz_mpoly_from_dict({(1,0):12,(0,3):12,(2,4):27}) - assert p1 // ztype(3) == ctx.fmpz_mpoly_from_dict({(1,0):1,(0,3):1,(2,4):3}) - assert ztype(3) // p1 == Zp(0,ctx) - assert ctx.constant(7) + ztype(3) == Zp(10, ctx) - q1 = flint.fmpq_mpoly(p1) - qctx = q1.context() - assert qctx.nvars() == 2 - assert qctx.ordering() == 'lex' - QQ = flint.fmpq - assert p1 + QQ(1,2) == qctx.fmpq_mpoly_from_dict({(1,0):4,(0,3):4,(2,4):9,(0,0):QQ(1,2)}) - assert QQ(1,2) + p1 == qctx.fmpq_mpoly_from_dict({(1,0):4,(0,3):4,(2,4):9,(0,0):QQ(1,2)}) - assert p1 - QQ(1,2) == qctx.fmpq_mpoly_from_dict({(1,0):4,(0,3):4,(2,4):9,(0,0):QQ(-1,2)}) - assert QQ(1,2) - p1 == qctx.fmpq_mpoly_from_dict({(1,0):-4,(0,3):-4,(2,4):-9,(0,0):QQ(1,2)}) - assert QQ(1,2) * p1 == qctx.fmpq_mpoly_from_dict({(1,0):2,(0,3):2,(2,4):QQ(9,2)}) - assert p1 * QQ(1,2) == qctx.fmpq_mpoly_from_dict({(1,0):2,(0,3):2,(2,4):QQ(9,2)}) - assert p1 / 2 == qctx.fmpq_mpoly_from_dict({(1,0):2,(0,3):2,(2,4):QQ(9,2)}) - assert p1 / QQ(1,2) == flint.fmpq_mpoly(p1 * 2) - p0 = Zp(0, ctx) - assert raises(lambda: p1 // p0 , ZeroDivisionError) - assert raises(lambda: p1 // 0 , ZeroDivisionError) - assert raises(lambda: p1 // QQ(1,1) , TypeError) - assert raises(lambda: p1 % p0 , ZeroDivisionError) - assert raises(lambda: p1 % 0 , ZeroDivisionError) - assert raises(lambda: p1 % QQ(1,1) , TypeError) - assert raises(lambda: p1 / p0 , ZeroDivisionError) - assert raises(lambda: p1 / 0 , ZeroDivisionError) - assert raises(lambda: p1 / QQ(0,1) , ZeroDivisionError) - def test_fmpz_series(): Zp = flint.fmpz_poly @@ -1190,38 +1134,6 @@ def set_bad(i): assert M3.charpoly() == flint.fmpq_poly([-1,6,-12,8]) / 8 assert M3.minpoly() == flint.fmpq_poly([1,-4,4]) / 4 -def test_fmpq_mpoly(): - Zp = flint.fmpq_mpoly - getctx = flint.fmpq_mpoly_ctx.get_context - ctx = getctx(4) - assert ctx.nvars() == 4 - assert ctx.ordering() == "lex" - assert [ctx.name(i) for i in range(4)] == ['x0', 'x1', 'x2', 'x3'] - for order in ['lex', 'deglex', 'degrevlex']: - ctx1 = getctx(4, order) - assert ctx1.ordering() == order - ctx = getctx(4, "lex", 'w,x,y,z') - p1 = ctx.gen(0) + ctx.gen(1) - ctx.gen(2) * ctx.gen(3) - assert p1 == Zp("w + x - y * z", ctx) - ctx = getctx(2, "lex", "x,y") - assert ctx.fmpq_mpoly_from_dict({(1,0):1, (0,1):2}) == Zp("x + 2*y", ctx) - assert raises(lambda: ctx.fmpq_mpoly_from_dict("b"), ValueError) - assert raises(lambda: ctx.fmpq_mpoly_from_dict({(1,2):"b"}), TypeError) - assert raises(lambda: ctx.fmpq_mpoly_from_dict({"b":1}), TypeError) - assert raises(lambda: ctx.fmpq_mpoly_from_dict({(1,2,3):1}), TypeError) - assert raises(lambda: ctx.fmpq_mpoly_from_dict({(1,"a"):1}), TypeError) - ctx = getctx(2, "lex", 'x,y') - p1 = ctx.fmpq_mpoly_from_dict({(1,0):4,(0,3):4,(2,4):9}) - for ztype in [int, long, flint.fmpz]: - assert p1 + ztype(3) == ctx.fmpq_mpoly_from_dict({(1,0):4,(0,3):4,(2,4):9,(0,0):3}) - assert ztype(3) + p1 == ctx.fmpq_mpoly_from_dict({(1,0):4,(0,3):4,(2,4):9,(0,0):3}) - assert p1 - ztype(3) == ctx.fmpq_mpoly_from_dict({(1,0):4,(0,3):4,(2,4):9,(0,0):-3}) - assert ztype(3) - p1 == ctx.fmpq_mpoly_from_dict({(1,0):-4,(0,3):-4,(2,4):-9,(0,0):3}) - assert p1 * ztype(3) == ctx.fmpq_mpoly_from_dict({(1,0):12,(0,3):12,(2,4):27}) - assert ztype(3) * p1 == ctx.fmpq_mpoly_from_dict({(1,0):12,(0,3):12,(2,4):27}) - # assert p1 // ztype(3) == ctx.fmpq_mpoly_from_dict({(1,0):1,(0,3):1,(2,4):3}) - # assert ztype(3) // p1 == Zp(0,ctx) - assert ctx.constant(7) + ztype(3) == Zp(10, ctx) def test_fmpq_series(): Qp = flint.fmpq_poly @@ -2728,6 +2640,333 @@ def setbad(obj, i, val): assert P([1, 2, 1]).integral() == P([0, 1, 1, S(1)/3]) +def _all_mpolys(): + return [ + (flint.fmpz_mpoly, flint.fmpz_mpoly_ctx, flint.fmpz, False), + # (flint.fmpq_mpoly, flint.fmpq_mpoly_ctx, flint.fmpq, True), + ] + + +def test_mpolys(): + for P, C, S, is_field in _all_mpolys(): + + ctx = C.get_context(nvars=2) + + assert raises(lambda: C.get_context(nvars=2, ordering="bad"), ValueError) + assert raises(lambda: ctx.constant("bad"), ValueError) + assert raises(lambda: ctx.from_dict("bad"), ValueError) + assert raises(lambda: ctx.from_dict({(0, 0): "bad"}), TypeError) + assert raises(lambda: ctx.from_dict({(0, "bad"): 1}), TypeError) + assert raises(lambda: ctx.from_dict({(0,): 1}), TypeError) + + assert raises(lambda: P(val=C.get_context(nvars=1).constant(0), ctx=ctx), ValueError) + assert raises(lambda: P(val={}, ctx=None), ValueError) + assert raises(lambda: P(val={"bad": 1}, ctx=None), ValueError) + assert raises(lambda: P(val="1", ctx=None), ValueError) + + assert P(val={(0, 0): 1}, ctx=ctx) == ctx.from_dict({(0, 0): 1}) + assert P(ctx=ctx).context() == ctx + assert P(1, ctx=ctx).is_one() + + def mpoly(x): + return ctx.from_dict(x) + + def quick_poly(): + return mpoly({(0, 0): 1, (0, 1): 2, (1, 0): 3, (2, 2): 4}) + + assert ctx.nvars() == 2 + assert ctx.ordering() == "lex" + + ctx1 = C.get_context(4) + assert [ctx1.name(i) for i in range(4)] == ['x0', 'x1', 'x2', 'x3'] + for order in ['lex', 'deglex', 'degrevlex']: + ctx1 = C.get_context(4, order) + assert ctx1.ordering() == order + + assert ctx.constant(1) == mpoly({(0, 0): 1}) == P(1, ctx=ctx) + + assert raises(lambda: P([None]), TypeError) + assert raises(lambda: P(object()), TypeError), f"{P(object()) = }" + assert raises(lambda: P(None), TypeError) + assert raises(lambda: P(None, None), TypeError) + assert raises(lambda: P([1,2], None), TypeError) + assert raises(lambda: P(1, None), ValueError) + + assert len(P(ctx=ctx)) == len(mpoly({(0, 0): 0})) == 0 + assert len(P(1, ctx=ctx)) == len(mpoly({(0, 0): 1})) == 1 + assert len(mpoly({(0, 0): 1, (0, 1): 1})) == 2 + assert len(mpoly({(0, 0): 1, (0, 1): 1, (1, 0): 1})) == 3 + + # degree is -1 when 0 poly + assert P(ctx=ctx).degrees() == mpoly({(0, 0): 0}).degrees() == {"x0": -1, "x1": -1} + assert P(1, ctx=ctx).degrees() == mpoly({(0, 0): 1}).degrees() == {"x0": 0, "x1": 0} + assert mpoly({(0, 0): 1, (0, 1): 1}).degrees() == {"x0": 0, "x1": 1} + assert mpoly({(0, 0): 1, (0, 1): 1, (1, 0): 1}).degrees() == {"x0": 1, "x1": 1} + assert mpoly({(0, 0): 1, (0, 1): 1, (1, 0): 1, (2, 2): 2}).degrees() == {"x0": 2, "x1": 2} + + assert (P(1, ctx=ctx) == P(1, ctx=ctx)) is True + assert (P(1, ctx=ctx) != P(1, ctx=ctx)) is False + assert (P(1, ctx=ctx) == P(2, ctx=ctx)) is False + assert (P(1, ctx=ctx) != P(2, ctx=ctx)) is True + + ctx1 = C.get_context(1) + assert (P(1, ctx=ctx) == P(1, ctx=ctx1)) is False + assert (P(1, ctx=ctx) != P(1, ctx=ctx1)) is True + + assert (P(1, ctx=ctx) == None) is False + assert (P(1, ctx=ctx) != None) is True + assert (None == P(1, ctx=ctx)) is False + assert (None != P(1, ctx=ctx)) is True + + assert raises(lambda: P(ctx=ctx) < P(ctx=ctx), TypeError) + assert raises(lambda: P(ctx=ctx) <= P(ctx=ctx), TypeError) + assert raises(lambda: P(ctx=ctx) > P(ctx=ctx), TypeError) + assert raises(lambda: P(ctx=ctx) >= P(ctx=ctx), TypeError) + assert raises(lambda: P(ctx=ctx) < None, TypeError) + assert raises(lambda: P(ctx=ctx) <= None, TypeError) + assert raises(lambda: P(ctx=ctx) > None, TypeError) + assert raises(lambda: P(ctx=ctx) >= None, TypeError) + assert raises(lambda: None < P(ctx=ctx), TypeError) + assert raises(lambda: None <= P(ctx=ctx), TypeError) + assert raises(lambda: None > P(ctx=ctx), TypeError) + assert raises(lambda: None >= P(ctx=ctx), TypeError) + + p = quick_poly() + assert p.coefficient(2) == S(2) + assert p.coefficient(-1) == S(0) + assert p.coefficient(10) == S(0) + + assert p[0] == mpoly({(2, 2): 4}) + assert p[3] == mpoly({(0, 0): 1}) + assert raises(lambda: p[-1], IndexError) + assert raises(lambda: p[4], IndexError) + + # assert p[(2, 2)] == 4 + # assert p[(0, 0)] == 1 + # assert raises(lambda: p[(1,)], ValueError) + # assert raises(lambda: p[(1, "bad")], TypeError) + # assert raises(lambda: p["bad"], TypeError) + + # p = quick_poly() + # p[1] = S(10) + # assert p == mpoly({(0, 0): 1, (0, 1): 2, (1, 0): 10, (2, 2): 4}) + + # p = quick_poly() + # p[(1, 0)] = S(10) + # assert p == mpoly({(0, 0): 1, (0, 1): 2, (1, 0): 10, (2, 2): 4}) + + assert raises(lambda: p.__setitem__(-1, 1), IndexError) + assert raises(lambda: p.__setitem__(4, 1), IndexError) + + assert raises(lambda: p.__setitem__((1,), 1), ValueError) + assert raises(lambda: p.__setitem__((1, "bad"), 1), TypeError) + assert raises(lambda: p.__setitem__("bad", 1), TypeError) + + assert raises(lambda: p.__setitem__(2, None), TypeError) + assert raises(lambda: p.__setitem__(-1, 1), IndexError) + + assert repr(P(ctx=ctx)) == '0' + assert repr(P(1, ctx=ctx)) == '1' + assert repr(quick_poly()) == '4*x0^2*x1^2 + 3*x0 + 2*x1 + 1' + + assert p.exponent_tuple(0) == (2, 2) + assert p.exponent_tuple(3) == (0, 0) + assert raises(lambda: p.exponent_tuple(-1), IndexError) + assert raises(lambda: p.exponent_tuple(4), IndexError) + + assert p.total_degree() == 4 + assert P(ctx=ctx).total_degree() == -1 + assert P(1, ctx=ctx).total_degree() == 0 + + p = quick_poly() + assert p(0, 0) == p(0, S(0)) == p(S(0), S(0)) == p(x1=S(0), x0=S(0)) == S(1) == 1 + assert p(1, 1) == p(x1=S(1), x0=S(1)) == S(10) == 10 + assert p(x0=p(x1=0)) == mpoly({ + (2, 2): 36, + (1, 2): 24, + (1, 0): 9, + (0, 2): 4, + (0, 1): 2, + (0, 0): 4 + }) + assert raises(lambda: p(x0=None), TypeError) + + assert bool(P(ctx=ctx)) is False + assert bool(P(1, ctx=ctx)) is True + + assert +quick_poly() \ + == quick_poly() + assert -quick_poly() \ + == mpoly({(0, 0): -1, (0, 1): -2, (1, 0): -3, (2, 2): -4}) + + assert quick_poly() \ + + mpoly({(0, 0): 5, (0, 1): 6, (1, 0): 7, (2, 2): 8}) \ + == mpoly({(0, 0): 6, (0, 1): 8, (1, 0): 10, (2, 2): 12}) + + for T in [int, S, flint.fmpz]: + assert quick_poly() + T(1) \ + == mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}) + assert T(1) + quick_poly() \ + == mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}) + + assert raises(lambda: mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}) + None, TypeError) + assert raises(lambda: None + mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}), TypeError) + assert raises(lambda: quick_poly() + P(ctx=ctx1), IncompatibleContextError) + assert raises(lambda: quick_poly().__iadd__(P(ctx=ctx1)), IncompatibleContextError) + + assert quick_poly() - mpoly({(0, 0): 5, (0, 1): 6, (1, 0): 7, (2, 2): 8}) \ + == mpoly({(0, 0): -4, (0, 1): -4, (1, 0): -4, (2, 2): -4}) + + for T in [int, S, flint.fmpz]: + assert quick_poly() - T(1) == mpoly({(0, 1): 2, (1, 0): 3, (2, 2): 4}) + assert T(1) - quick_poly() == mpoly({(0, 1): -2, (1, 0): -3, (2, 2): -4}) + + assert raises(lambda: quick_poly() - None, TypeError) + assert raises(lambda: None - quick_poly(), TypeError) + assert raises(lambda: quick_poly() - P(ctx=ctx1), IncompatibleContextError) + assert raises(lambda: quick_poly().__isub__(P(ctx=ctx1)), IncompatibleContextError) + + assert quick_poly() * mpoly({(1, 0): 5, (0, 1): 6}) \ + == mpoly({ + (3, 2): 20, + (2, 3): 24, + (2, 0): 15, + (1, 1): 28, + (1, 0): 5, + (0, 2): 12, + (0, 1): 6 + }) + + for T in [int, S, flint.fmpz]: + assert quick_poly() * T(2) == mpoly({(0, 0): 2, (0, 1): 4, (1, 0): 6, (2, 2): 8}) + assert T(2) * quick_poly() == mpoly({(0, 0): 2, (0, 1): 4, (1, 0): 6, (2, 2): 8}) + + assert raises(lambda: quick_poly() * None, TypeError) + assert raises(lambda: None * quick_poly(), TypeError) + assert raises(lambda: quick_poly() * P(ctx=ctx1), IncompatibleContextError) + assert raises(lambda: quick_poly().__imul__(P(ctx=ctx1)), IncompatibleContextError) + + assert quick_poly() // mpoly({(1, 1): 1}) == mpoly({(1, 1): 4}) + assert quick_poly() % mpoly({(1, 1): 1}) \ + == mpoly({(1, 0): 3, (0, 1): 2, (0, 0): 1}) + assert divmod(quick_poly(), mpoly({(1, 1): 1})) \ + == (mpoly({(1, 1): 4}), mpoly({(1, 0): 3, (0, 1): 2, (0, 0): 1})) + + assert quick_poly() // 1 == quick_poly() + assert quick_poly() % 1 == P(ctx=ctx) + assert divmod(quick_poly(), 1) == (quick_poly(), P(ctx=ctx)) + + # TODO FIXME + # if is_field: + # assert P([1, 1]) // 2 == P([S(1)/2, S(1)/2]) + # assert P([1, 1]) % 2 == P([0]) + # else: + # assert P([1, 1]) // 2 == P([0, 0]) + # assert P([1, 1]) % 2 == P([1, 1]) + + assert 1 // quick_poly() == P(ctx=ctx) + assert 1 % quick_poly() == P(1, ctx=ctx) + assert divmod(1, quick_poly()) == (P(ctx=ctx), P(1, ctx=ctx)) + + assert raises(lambda: quick_poly() // None, TypeError) + assert raises(lambda: quick_poly() % None, TypeError) + assert raises(lambda: divmod(quick_poly(), None), TypeError) + + assert raises(lambda: None // quick_poly(), TypeError) + assert raises(lambda: None % quick_poly(), TypeError) + assert raises(lambda: divmod(None, quick_poly()), TypeError) + + assert raises(lambda: quick_poly() // 0, ZeroDivisionError) + assert raises(lambda: quick_poly() % 0, ZeroDivisionError) + assert raises(lambda: divmod(quick_poly(), 0), ZeroDivisionError) + + assert raises(lambda: quick_poly() // P(ctx=ctx), ZeroDivisionError) + assert raises(lambda: quick_poly() % P(ctx=ctx), ZeroDivisionError) + assert raises(lambda: divmod(quick_poly(), P(ctx=ctx)), ZeroDivisionError) + + assert raises(lambda: quick_poly() // P(1, ctx=ctx1), IncompatibleContextError) + assert raises(lambda: quick_poly() % P(1, ctx=ctx1), IncompatibleContextError) + assert raises(lambda: divmod(quick_poly(), P(1, ctx=ctx1)), IncompatibleContextError) + + + # TODO FIXME + # # Exact/field scalar division + # if is_field: + # assert P([2, 2]) / 2 == P([1, 1]) + # assert P([1, 2]) / 2 == P([S(1)/2, 1]) + # else: + # assert P([2, 2]) / 2 == P([1, 1]) + # assert raises(lambda: P([1, 2]) / 2, DomainError) + # assert raises(lambda: P([1, 2]) / 0, ZeroDivisionError) + + f = mpoly({(1, 1): 4, (0, 0): 1}) + g = mpoly({(0, 1): 2, (1, 0): 2}) + assert f \ + * g \ + / mpoly({(0, 1): 1, (1, 0): 1}) \ + == mpoly({(1, 1): 8, (0, 0): 2}) + assert raises(lambda: 1 / quick_poly(), DomainError) + assert raises(lambda: quick_poly() / P(2, ctx=ctx), DomainError) + + assert quick_poly() ** 0 == P(1, ctx=ctx) + assert quick_poly() ** 1 == quick_poly() + assert quick_poly() ** 2 == mpoly({ + (4, 4): 16, + (3, 2): 24, + (2, 3): 16, + (2, 2): 8, + (2, 0): 9, + (1, 1): 12, + (1, 0): 6, + (0, 2): 4, + (0, 1): 4, + (0, 0): 1, + }) + assert raises(lambda: P(ctx=ctx) ** -1, ValueError) + assert raises(lambda: P(ctx=ctx) ** None, TypeError) + + # # XXX: Not sure what this should do in general: + assert raises(lambda: pow(P(1, ctx=ctx), 2, 3), NotImplementedError) + + assert (f * g).gcd(f) == f + assert raises(lambda: quick_poly().gcd(None), TypeError) + assert raises(lambda: quick_poly().gcd(P(ctx=ctx1)), IncompatibleContextError) + + # TODO FIXME + # if is_field: + # p1 = P([1, 0, 1]) + # p2 = P([2, 1]) + # g, s, t = P([1]), P([1])/5, P([2, -1])/5 + # assert p1.xgcd(p2) == (g, s, t) + # assert raises(lambda: p1.xgcd(None), TypeError) + + assert (f * g).factor() == (S(2), [(mpoly({(0, 1): 1, (1, 0): 1}), 1), (f, 1)]) + + assert (f * f).sqrt() == f + assert (f * f).sqrt(assume_perfect_square=True) == f + assert raises(lambda: quick_poly().sqrt(), ValueError) + + # TODO FIXME + # if P == flint.fmpq_mpoly: + # assert raises(lambda: P([1, 2, 1], 3).sqrt(), ValueError) + # assert P([1, 2, 1], 4).sqrt() == P([1, 1], 2) + + # assert P(ctx=C).deflation() == (P(ctx=C), 1) + # assert P([1, 2]).deflation() == (P([1, 2]), 1) + # assert P([1, 0, 2]).deflation() == (P([1, 2]), 2) + + assert quick_poly().derivative(0) == quick_poly().derivative("x0") == mpoly({(0, 0): 3, (1, 2): 8}) + assert quick_poly().derivative(1) == quick_poly().derivative("x1") == mpoly({(0, 0): 2, (2, 1): 8}) + + assert quick_poly().integral(0) == quick_poly().integral("x0") == \ + (6, mpoly({(3, 2): 8, (2, 0): 9, (1, 1): 12, (1, 0): 6})) + assert quick_poly().integral(1) == quick_poly().integral("x1") == \ + (3, mpoly({(2, 3): 4, (1, 1): 9, (0, 2): 3, (0, 1): 3})) + + # if is_field: + # assert P([1, 2, 1]).integral() == P([0, 1, 1, S(1)/3]) + + def _all_matrices(): """Return a list of matrix types and scalar types.""" R163 = flint.fmpz_mod_ctx(163) @@ -3249,13 +3488,11 @@ def test_all_tests(): test_fmpz_poly_factor, test_fmpz_poly_functions, test_fmpz_mat, - test_fmpz_mpoly, test_fmpz_series, test_fmpq, test_fmpq_poly, test_fmpq_mat, - test_fmpq_mpoly, test_fmpq_series, test_nmod, @@ -3273,6 +3510,7 @@ def test_all_tests(): test_division_matrix, test_polys, + test_mpolys, test_matrices_eq, test_matrices_constructor, diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 7f7b5954..61a707c9 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -1,9 +1,9 @@ from cpython.version cimport PY_MAJOR_VERSION from cpython.dict cimport PyDict_Size, PyDict_Check, PyDict_Next from cpython.tuple cimport PyTuple_Check, PyTuple_GET_SIZE +from cpython.object cimport Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE from flint.flintlib.fmpz cimport fmpz_init, fmpz_clear, fmpz_is_zero from flint.flintlib.fmpq_mpoly cimport fmpq_mpoly_t, fmpq_mpoly_add_fmpq, fmpq_mpoly_sub_fmpq, fmpq_mpoly_scalar_mul_fmpq -from flint.flintlib.fmpq_mpoly cimport fmpq_mpoly_scalar_div_fmpq from flint.flintlib.flint cimport * from flint.flintlib.fmpq cimport fmpq_numref, fmpq_denref from flint.flintlib.fmpz_mpoly_q cimport fmpz_mpoly_q_div_fmpz @@ -18,6 +18,8 @@ from flint.types.fmpq cimport any_as_fmpq, fmpq_set_any_ref from flint.types.fmpq_mpoly cimport fmpq_mpoly from flint.types.fmpz_mpoly_q cimport fmpz_mpoly_q +from flint.utils.flint_exceptions import DomainError, IncompatibleContextError + cimport cython cimport libc.stdlib from flint.flintlib.fmpz cimport fmpz_set @@ -155,15 +157,15 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): fmpz_mpoly_set_fmpz(res.val, (z).val, res.ctx.val) return res - def fmpz_mpoly_from_dict(self, d): + def from_dict(self, d): """ - Create a fmpz_mpoly from a dictionary. + Create a fmpz_mpoly from a dictionary in this context. The dictionary's keys are tuples of ints (or anything that implicitly converts to fmpz) representing exponents, and corresponding values of fmpz. >>> ctx = fmpz_mpoly_ctx.get_context(2,'lex','x,y') - >>> ctx.fmpz_mpoly_from_dict({(1,0):2, (1,1):3, (0,1):1}) + >>> ctx.from_dict({(1,0):2, (1,1):3, (0,1):1}) 3*x*y + 2*x + y """ cdef long n @@ -255,7 +257,7 @@ cdef class fmpz_mpoly(flint_mpoly): if not isinstance(k, tuple): raise ValueError("Dict should be keyed with tuples of integers") ctx = fmpz_mpoly_ctx.get_context(len(k)) - x = ctx.fmpz_mpoly_from_dict(val) + x = ctx.from_dict(val) #XXX this copy is silly, have a ctx function that assigns an fmpz_mpoly_t init_fmpz_mpoly(self, ctx) fmpz_mpoly_set(self.val, (x).val, self.ctx.val) @@ -278,9 +280,6 @@ cdef class fmpz_mpoly(flint_mpoly): def context(self): return self.ctx - def __nonzero__(self): - return not fmpz_mpoly_is_zero(self.val, self.ctx.val) - def __bool__(self): return not fmpz_mpoly_is_zero(self.val, self.ctx.val) @@ -288,27 +287,81 @@ cdef class fmpz_mpoly(flint_mpoly): return fmpz_mpoly_is_one(self.val, self.ctx.val) def __richcmp__(self, other, int op): - if op != 2 and op != 3: + if not (op == Py_EQ or op == Py_NE): return NotImplemented - if typecheck(self, fmpz_mpoly) and typecheck(other, fmpz_mpoly): + elif other is None: + return op == Py_NE + elif typecheck(self, fmpz_mpoly) and typecheck(other, fmpz_mpoly): if (self).ctx is (other).ctx: - if op == 2: - return bool(fmpz_mpoly_equal((self).val, (other).val, (self).ctx.val)) - else: - return not bool(fmpz_mpoly_equal((self).val, (other).val, (self).ctx.val)) + return (op == Py_NE) ^ bool(fmpz_mpoly_equal((self).val, (other).val, (self).ctx.val)) else: - if op == 2: - return False - else: - return True - if op == 2: - return not bool(self - other) + return op == Py_NE else: - return bool(self - other) + return NotImplemented def __len__(self): return fmpz_mpoly_length(self.val, self.ctx.val) + def __getitem__(self, x): + cdef: + fmpz_struct ** tmp + slong nvars = self.ctx.nvars() + + if isinstance(x, int): + if not 0 <= x < fmpz_mpoly_length(self.val, self.ctx.val): + raise IndexError("term index out of range") + res = fmpz_mpoly.__new__(fmpz_mpoly) + init_fmpz_mpoly(res, self.ctx) + fmpz_mpoly_get_term(( res).val, self.val, x, self.ctx.val) + return res + elif isinstance(x, tuple): + if len(x) != nvars: + raise ValueError("exponent vector provided does not match number of variables") + res = fmpz() + try: + tmp = libc.stdlib.malloc(nvars * sizeof(fmpz_struct *)) + for i in range(nvars): + exp = any_as_fmpz(x[i]) + if exp is NotImplemented: + raise TypeError("exponent not coercible to fmpz") + tmp[i] = &((exp).val[0]) + fmpz_mpoly_get_coeff_fmpz_fmpz((res).val, self.val, tmp, self.ctx.val) + return res + finally: + libc.stdlib.free(tmp) + else: + raise TypeError("index is not integer or tuple") + + def __setitem__(self, x, y): + cdef: + fmpz_struct ** tmp + slong nvars = self.ctx.nvars() + + coeff = any_as_fmpz(y) + if coeff is NotImplemented: + raise TypeError("provided coefficient not coercible to fmpz") + + if isinstance(x, int): + if not 0 <= x < fmpz_mpoly_length(self.val, self.ctx.val): + raise IndexError("term index out of range") + fmpz_mpoly_set_term_coeff_fmpz(self.val, x, (coeff).val, self.ctx.val) + elif isinstance(x, tuple): + if len(x) != nvars: + raise ValueError("exponent vector provided does not match number of variables") + + try: + tmp = libc.stdlib.malloc(nvars * sizeof(fmpz_struct *)) + for i in range(nvars): + exp = any_as_fmpz(x[i]) + if exp is NotImplemented: + raise TypeError("exponent not coercible to fmpz") + tmp[i] = &((exp).val[0]) + fmpz_mpoly_set_coeff_fmpz_fmpz(self.val, (coeff).val, tmp, self.ctx.val) + finally: + libc.stdlib.free(tmp) + else: + raise TypeError("index is not integer or tuple") + def coefficient(self, slong i): cdef fmpz v if i < 0 or i >= fmpz_mpoly_length(self.val, self.ctx.val): @@ -322,10 +375,10 @@ cdef class fmpz_mpoly(flint_mpoly): cdef slong j, nvars cdef fmpz_struct ** tmp if i < 0 or i >= fmpz_mpoly_length(self.val, self.ctx.val): - raise ValueError + raise IndexError("term index out of range") nvars = self.ctx.nvars() res = tuple(fmpz() for j in range(nvars)) - tmp = libc.stdlib.malloc(nvars * sizeof(fmpz_struct **)) + tmp = libc.stdlib.malloc(nvars * sizeof(fmpz_struct *)) try: for j in range(nvars): tmp[j] = &(( (res[j])).val[0]) @@ -334,6 +387,25 @@ cdef class fmpz_mpoly(flint_mpoly): libc.stdlib.free(tmp) return res + def degrees(self): + cdef: + fmpz_struct ** tmp + slong i, nvars = self.ctx.nvars() + + res = tuple(fmpz() for _ in range(nvars)) + tmp = libc.stdlib.malloc(nvars * sizeof(fmpz_struct *)) + + for i in range(nvars): + tmp[i] = &(( res[i]).val[0]) + fmpz_mpoly_degrees_fmpz(tmp, self.val, self.ctx.val) + libc.stdlib.free(tmp) + return dict(zip(self.ctx.names(), res)) + + def total_degree(self): + cdef fmpz res = fmpz() + fmpz_mpoly_total_degree_fmpz(( res).val, self.val, self.ctx.val) + return res + def repr(self): return self.str() + " (nvars=%s, ordering=%s names=%s)" % (self.ctx.nvars(), self.ctx.ordering(), self.ctx.py_names) @@ -349,6 +421,9 @@ cdef class fmpz_mpoly(flint_mpoly): res = "-" + res[3:] return res + def __pos__(self): + return self + def __neg__(self): cdef fmpz_mpoly res res = create_fmpz_mpoly(self.ctx) @@ -363,7 +438,7 @@ cdef class fmpz_mpoly(flint_mpoly): cdef int xtype if typecheck(other, fmpz_mpoly): if (self).ctx is not (other).ctx: - return NotImplemented + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_add(res.val, (self).val, (other).val, res.ctx.val) return res @@ -402,7 +477,7 @@ cdef class fmpz_mpoly(flint_mpoly): def __iadd__(self, other): if typecheck(other, fmpz_mpoly): if (self).ctx is not (other).ctx: - return NotImplemented + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") fmpz_mpoly_add((self).val, (self).val, (other).val, self.ctx.val) return self else: @@ -420,7 +495,7 @@ cdef class fmpz_mpoly(flint_mpoly): cdef int xtype if typecheck(other, fmpz_mpoly): if (self).ctx is not (other).ctx: - return NotImplemented + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_sub(res.val, (self).val, (other).val, res.ctx.val) return res @@ -459,7 +534,7 @@ cdef class fmpz_mpoly(flint_mpoly): def __isub__(self, other): if typecheck(other, fmpz_mpoly): if (self).ctx is not (other).ctx: - return NotImplemented + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") fmpz_mpoly_sub((self).val, (self).val, (other).val, self.ctx.val) return self else: @@ -478,7 +553,7 @@ cdef class fmpz_mpoly(flint_mpoly): if typecheck(other, fmpz_mpoly): if (self).ctx is not (other).ctx: - return NotImplemented + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_mul(res.val, (self).val, (other).val, res.ctx.val) return res @@ -517,7 +592,7 @@ cdef class fmpz_mpoly(flint_mpoly): def __imul__(self, other): if typecheck(other, fmpz_mpoly): if (self).ctx is not (other).ctx: - return NotImplemented + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") fmpz_mpoly_mul((self).val, (self).val, (other).val, self.ctx.val) return self else: @@ -550,7 +625,7 @@ cdef class fmpz_mpoly(flint_mpoly): if not other: raise ZeroDivisionError("fmpz_mpoly_divison by zero") if (self).ctx is not (other).ctx: - return NotImplemented + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") res = create_fmpz_mpoly(self.ctx) res2 = create_fmpz_mpoly(self.ctx) fmpz_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) @@ -577,7 +652,7 @@ cdef class fmpz_mpoly(flint_mpoly): res = create_fmpz_mpoly(self.ctx) res2 = create_fmpz_mpoly(self.ctx) fmpz_mpoly_divrem(res.val, res2.val, (other).val, (self).val, res.ctx.val) - return res + return (res, res2) return NotImplemented def __floordiv__(self, other): @@ -586,7 +661,7 @@ cdef class fmpz_mpoly(flint_mpoly): if not other: raise ZeroDivisionError("fmpz_mpoly division by zero") if (self).ctx is not (other).ctx: - return NotImplemented + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) return res @@ -614,63 +689,81 @@ cdef class fmpz_mpoly(flint_mpoly): return NotImplemented def __truediv__(self, other): - cdef fmpq_mpoly qres - cdef fmpq_t q_other - cdef int xtype + cdef: + fmpz_mpoly res + int xtype if typecheck(other, fmpz_mpoly): if not other: raise ZeroDivisionError("fmpz_mpoly division by zero") if self.ctx is not ( other).ctx: + raise IncompatibleContextError(f"{self.ctx} is not {(other).ctx}") + res, r = divmod(self, other) + if not r: + return res + else: + raise DomainError("fmpz_mpoly division is not exact") + else: + o = any_as_fmpz(other) + if o is NotImplemented: return NotImplemented - return fmpz_mpoly_q(self, other) - xtype = fmpq_set_any_ref(q_other, other) - if xtype != FMPZ_UNKNOWN: - if not other: + elif not o: raise ZeroDivisionError("fmpz_mpoly division by zero") - qres = fmpq_mpoly(self) - fmpq_mpoly_scalar_div_fmpq(qres.val, qres.val, q_other, qres.ctx.val) - return qres + res = fmpz_mpoly.__new__(fmpz_mpoly) + init_fmpz_mpoly(res, self.ctx) + if fmpz_mpoly_scalar_divides_fmpz(res.val, self.val, (o).val, self.ctx.val): + return res + else: + raise DomainError("fmpz_mpoly division is not exact") def __rtruediv__(self, other): - cdef fmpz_mpoly num - cdef fmpz_mpoly_q ret - cdef fmpz_t z_other - cdef fmpq_t q_other - cdef int xtype + cdef fmpz_mpoly res + + # If other was a fmpz_mpoly then __truediv__ would have already been called + # and it only reflects when other was not a fmpz_mpoly nor a fmpz. If other + # was not a fmpz_mpoly so our __truediv__ was not called then it should + # handle having a fmpz_mpoly as the divisor if it's part of python-flint and + # it's reasonable for the domain. Otherwise it is something outside of + # python-flint. We attempt to construct a fmpz out of it, then convert that to + # fmpz_mpoly if not self: raise ZeroDivisionError("fmpz_mpoly division by zero") - if typecheck(other, fmpz_mpoly): - if self.ctx is not ( other).ctx: - return NotImplemented - return fmpz_mpoly_q(other, self) - xtype = fmpz_set_any_ref(z_other, other) - if xtype != FMPZ_UNKNOWN: - num = create_fmpz_mpoly(self.ctx) - fmpz_mpoly_set_fmpz(num.val, z_other, self.ctx.val) - return fmpz_mpoly_q(num, self) - xtype = fmpq_set_any_ref(q_other, other) - if xtype != FMPZ_UNKNOWN: - num = create_fmpz_mpoly(self.ctx) - fmpz_mpoly_set_fmpz(num.val, fmpq_numref(q_other),self.ctx.val) - ret = fmpz_mpoly_q(num, self) - fmpz_mpoly_q_div_fmpz(ret.fraction, ret.fraction, fmpq_denref(q_other), self.ctx.val) - return ret - return NotImplemented + + o = any_as_fmpz(other) + if o is NotImplemented: + return NotImplemented + res = fmpz_mpoly.__new__(fmpz_mpoly) + init_fmpz_mpoly(res, self.ctx) + fmpz_mpoly_set_fmpz(res.val, (o).val, self.ctx.val) + return res / self def __mod__(self, other): return divmod(self, other)[1] + def __rmod__(self, other): + return divmod(other, self)[1] + def gcd(self, other): cdef fmpz_mpoly res - assert isinstance(other, fmpz_mpoly) + if not typecheck(other, fmpz_mpoly): + raise TypeError("argument must be a fmpz_mpoly") if (self).ctx is not (other).ctx: - return NotImplemented + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_gcd(res.val, (self).val, (other).val, res.ctx.val) return res + def sqrt(self, assume_perfect_square: bool = False): + cdef fmpz_mpoly res + res = fmpz_mpoly.__new__(fmpz_mpoly) + init_fmpz_mpoly(res, self.ctx) + + if fmpz_mpoly_sqrt_heap(res.val, self.val, self.ctx.val, not assume_perfect_square): + return res + else: + raise ValueError("polynomial is not a perfect square") + def __call__(self, *args, **kwargs): cdef: fmpz_mpoly res @@ -740,8 +833,10 @@ cdef class fmpz_mpoly(flint_mpoly): return res elif args and not all_fmpz: # Complete function composition + if not all(typecheck(arg, fmpz_mpoly) for arg in args): + raise TypeError("all arguments must be fmpz_mpolys") res_ctx = ( args[0]).ctx - if not all(typecheck(args[i], fmpz_mpoly) and ( args[i]).ctx is res_ctx for i in range(1, len(args))): + if not all(( args[i]).ctx is res_ctx for i in range(1, len(args))): raise ValueError("all arguments must share the same context") C = libc.stdlib.malloc(nvars * sizeof(fmpz_mpoly_struct *)) @@ -760,8 +855,10 @@ cdef class fmpz_mpoly(flint_mpoly): # that have not be provided are set to the trivial monomial. This is why we require all the # polynomial itself, and all arguments exist in the same context, otherwise we have no way of # finding the correct monomial to use. - if not all(typecheck(arg, fmpz_mpoly) and ( arg).ctx is self.ctx for _, arg in args): - raise ValueError("the mpoly and all arguments must share the same context") + if not all(typecheck(arg, fmpz_mpoly) for _, arg in partial_args): + raise TypeError("all arguments must be fmpz_mpolys for partial composition") + if not all(( arg).ctx is self.ctx for _, arg in partial_args): + raise ValueError("all arguments must share the same context") polys = [None] * nvars for i, poly in partial_args: @@ -885,3 +982,50 @@ cdef class fmpz_mpoly(flint_mpoly): libc.stdlib.free(C) return outpoly + + def derivative(self, var): + cdef: + fmpz_mpoly res + slong i + + if isinstance(var, str): + vars = {x: i for i, x in enumerate(self.ctx.names())} + if var not in vars: + raise ValueError("variable not in context") + else: + i = vars[var] + elif isinstance(var, int): + i = var + else: + raise TypeError("invalid variable type") + + res = fmpz_mpoly.__new__(fmpz_mpoly) + init_fmpz_mpoly(res, self.ctx) + + fmpz_mpoly_derivative(res.val, self.val, i, self.ctx.val) + return res + + def integral(self, var): + cdef: + fmpz_mpoly res + fmpz scale + slong i + + if isinstance(var, str): + vars = {x: i for i, x in enumerate(self.ctx.names())} + if var not in vars: + raise ValueError("variable not in context") + else: + i = vars[var] + elif isinstance(var, int): + i = var + else: + raise TypeError("invalid variable type") + + res = fmpz_mpoly.__new__(fmpz_mpoly) + init_fmpz_mpoly(res, self.ctx) + + scale = fmpz() + + fmpz_mpoly_integral(res.val, scale.val, self.val, i, self.ctx.val) + return scale, res diff --git a/src/flint/utils/flint_exceptions.py b/src/flint/utils/flint_exceptions.py index 580b4549..fb79c537 100644 --- a/src/flint/utils/flint_exceptions.py +++ b/src/flint/utils/flint_exceptions.py @@ -6,3 +6,11 @@ class DomainError(Exception): ring or field for which the domain is invalid. """ pass + + +class IncompatibleContextError(Exception): + """ + Exception intended to be called when a method involves two or more + incompatible contexts. + """ + pass From ce5c9e77004e4159d43b6e9384c32a3b6049023d Mon Sep 17 00:00:00 2001 From: Jake Moss Date: Mon, 22 Apr 2024 12:24:14 +1000 Subject: [PATCH 28/95] Use create_fmpz_mpoly in favour of __new__ init pattern --- src/flint/types/fmpz_mpoly.pxd | 2 +- src/flint/types/fmpz_mpoly.pyx | 62 ++++++++++++---------------------- 2 files changed, 22 insertions(+), 42 deletions(-) diff --git a/src/flint/types/fmpz_mpoly.pxd b/src/flint/types/fmpz_mpoly.pxd index c3e47a23..4b6520e5 100644 --- a/src/flint/types/fmpz_mpoly.pxd +++ b/src/flint/types/fmpz_mpoly.pxd @@ -10,7 +10,7 @@ cdef inline init_fmpz_mpoly(fmpz_mpoly var, fmpz_mpoly_ctx ctx): fmpz_mpoly_init(var.val, ctx.val) var._init = True -cdef inline create_fmpz_mpoly(fmpz_mpoly_ctx ctx): +cdef inline fmpz_mpoly create_fmpz_mpoly(fmpz_mpoly_ctx ctx): cdef fmpz_mpoly var var = fmpz_mpoly.__new__(fmpz_mpoly) var.ctx = ctx diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 61a707c9..006e2922 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -134,11 +134,9 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): z1 """ cdef fmpz_mpoly res - assert i >= 0 and i < self.val.minfo.nvars - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = self - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True + if not 0 <= i < self.val.minfo.nvars: + raise IndexError("generator index out of range") + res = create_fmpz_mpoly(self) fmpz_mpoly_gen(res.val, i, res.ctx.val) return res @@ -149,11 +147,8 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): cdef fmpz_mpoly res z = any_as_fmpz(z) if z is NotImplemented: - raise ValueError("A constant fmpz_mpoly is a fmpz") - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = self - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True + raise TypeError("cannot coerce argument to fmpz") + res = create_fmpz_mpoly(self) fmpz_mpoly_set_fmpz(res.val, (z).val, res.ctx.val) return res @@ -187,10 +182,7 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): for i in range(nvars): fmpz_init(exponents + i) fmpz_init(coefficient) - res = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = self - fmpz_mpoly_init(res.val, res.ctx.val) - res._init = True + res = create_fmpz_mpoly(self) count = 0 for k, v in d.items(): xtype = fmpz_set_any_ref(coefficient, v) @@ -310,8 +302,7 @@ cdef class fmpz_mpoly(flint_mpoly): if isinstance(x, int): if not 0 <= x < fmpz_mpoly_length(self.val, self.ctx.val): raise IndexError("term index out of range") - res = fmpz_mpoly.__new__(fmpz_mpoly) - init_fmpz_mpoly(res, self.ctx) + res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_get_term(( res).val, self.val, x, self.ctx.val) return res elif isinstance(x, tuple): @@ -709,8 +700,7 @@ cdef class fmpz_mpoly(flint_mpoly): return NotImplemented elif not o: raise ZeroDivisionError("fmpz_mpoly division by zero") - res = fmpz_mpoly.__new__(fmpz_mpoly) - init_fmpz_mpoly(res, self.ctx) + res = create_fmpz_mpoly(self.ctx) if fmpz_mpoly_scalar_divides_fmpz(res.val, self.val, (o).val, self.ctx.val): return res else: @@ -733,8 +723,7 @@ cdef class fmpz_mpoly(flint_mpoly): o = any_as_fmpz(other) if o is NotImplemented: return NotImplemented - res = fmpz_mpoly.__new__(fmpz_mpoly) - init_fmpz_mpoly(res, self.ctx) + res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_set_fmpz(res.val, (o).val, self.ctx.val) return res / self @@ -756,8 +745,7 @@ cdef class fmpz_mpoly(flint_mpoly): def sqrt(self, assume_perfect_square: bool = False): cdef fmpz_mpoly res - res = fmpz_mpoly.__new__(fmpz_mpoly) - init_fmpz_mpoly(res, self.ctx) + res = create_fmpz_mpoly(self.ctx) if fmpz_mpoly_sqrt_heap(res.val, self.val, self.ctx.val, not assume_perfect_square): return res @@ -820,10 +808,8 @@ cdef class fmpz_mpoly(flint_mpoly): libc.stdlib.free(V) elif kwargs and all_fmpz: # Partial application with args in Z. We evaluate the polynomial one variable at a time - res = fmpz_mpoly.__new__(fmpz_mpoly) - res2 = fmpz_mpoly.__new__(fmpz_mpoly) - res.ctx = self.ctx - res2.ctx = self.ctx + res = create_fmpz_mpoly(self.ctx) + res2 = create_fmpz_mpoly(self.ctx) fmpz_mpoly_set(res2.val, self.val, self.ctx.val) for (i, _), arg in zip(partial_args, args_fmpz): @@ -843,8 +829,7 @@ cdef class fmpz_mpoly(flint_mpoly): try: for i in range(nvars): C[i] = &(( args[i]).val[0]) - res = fmpz_mpoly.__new__(fmpz_mpoly) - init_fmpz_mpoly(res, res_ctx) + res = create_fmpz_mpoly(self.ctx) if fmpz_mpoly_compose_fmpz_mpoly(res.val, self.val, C, self.ctx.val, res_ctx.val) == 0: raise ValueError("unreasonably large polynomial") return res @@ -866,8 +851,7 @@ cdef class fmpz_mpoly(flint_mpoly): for i in range(nvars): if polys[i] is None: - res = fmpz_mpoly.__new__(fmpz_mpoly) - init_fmpz_mpoly(res, self.ctx) + res = create_fmpz_mpoly(self.ctx) exponents = libc.stdlib.calloc(nvars, sizeof(ulong)) exponents[i] = 1 fmpz_mpoly_push_term_ui_ui(res.val, 1, exponents, self.ctx.val) @@ -878,8 +862,7 @@ cdef class fmpz_mpoly(flint_mpoly): try: for i in range(len(polys)): C[i] = &(( polys[i]).val[0]) - res = fmpz_mpoly.__new__(fmpz_mpoly) - init_fmpz_mpoly(res, self.ctx) + res = create_fmpz_mpoly(self.ctx) if fmpz_mpoly_compose_fmpz_mpoly(res.val, self.val, C, self.ctx.val, self.ctx.val) == 0: raise ValueError("unreasonably large polynomial") return res @@ -960,7 +943,7 @@ cdef class fmpz_mpoly(flint_mpoly): def coerce_to_context(self, ctx): cdef: - fmpz_mpoly outpoly + fmpz_mpoly res slong *C slong i @@ -971,17 +954,16 @@ cdef class fmpz_mpoly(flint_mpoly): return self C = libc.stdlib.malloc(self.ctx.val.minfo.nvars * sizeof(slong *)) - outpoly = fmpz_mpoly.__new__(fmpz_mpoly) - init_fmpz_mpoly(outpoly, ctx) + res = create_fmpz_mpoly(self.ctx) vars = {x: i for i, x in enumerate(ctx.py_names)} for i, var in enumerate(self.ctx.py_names): C[i] = vars[var] - fmpz_mpoly_compose_fmpz_mpoly_gen(outpoly.val, self.val, C, self.ctx.val, (ctx).val) + fmpz_mpoly_compose_fmpz_mpoly_gen(res.val, self.val, C, self.ctx.val, (ctx).val) libc.stdlib.free(C) - return outpoly + return res def derivative(self, var): cdef: @@ -999,8 +981,7 @@ cdef class fmpz_mpoly(flint_mpoly): else: raise TypeError("invalid variable type") - res = fmpz_mpoly.__new__(fmpz_mpoly) - init_fmpz_mpoly(res, self.ctx) + res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_derivative(res.val, self.val, i, self.ctx.val) return res @@ -1022,8 +1003,7 @@ cdef class fmpz_mpoly(flint_mpoly): else: raise TypeError("invalid variable type") - res = fmpz_mpoly.__new__(fmpz_mpoly) - init_fmpz_mpoly(res, self.ctx) + res = create_fmpz_mpoly(self.ctx) scale = fmpz() From 8752894b4d36564eae8993c5cd3a4d228b747653 Mon Sep 17 00:00:00 2001 From: Jake Moss Date: Mon, 22 Apr 2024 12:24:42 +1000 Subject: [PATCH 29/95] Add super class for Flint exceptions --- src/flint/utils/flint_exceptions.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/flint/utils/flint_exceptions.py b/src/flint/utils/flint_exceptions.py index fb79c537..9d9c271b 100644 --- a/src/flint/utils/flint_exceptions.py +++ b/src/flint/utils/flint_exceptions.py @@ -1,6 +1,8 @@ +class FlintError(Exception): + """Generic parent class for all flint relation exceptions.""" + pass - -class DomainError(Exception): +class DomainError(FlintError): """ Exception intended to be called when a method is called on a ring or field for which the domain is invalid. @@ -8,7 +10,7 @@ class DomainError(Exception): pass -class IncompatibleContextError(Exception): +class IncompatibleContextError(FlintError): """ Exception intended to be called when a method involves two or more incompatible contexts. From 1839619fb7b2c0a470f2c870258e71ad7efbf22b Mon Sep 17 00:00:00 2001 From: Jake Moss Date: Mon, 22 Apr 2024 12:35:48 +1000 Subject: [PATCH 30/95] Add exception to ordering if ladder --- src/flint/types/fmpz_mpoly.pyx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 006e2922..149fda1c 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -120,10 +120,13 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): """ if self.val.minfo.ord == ordering_t.ORD_LEX: return "lex" - if self.val.minfo.ord == ordering_t.ORD_DEGLEX: + elif self.val.minfo.ord == ordering_t.ORD_DEGLEX: return "deglex" - if self.val.minfo.ord == ordering_t.ORD_DEGREVLEX: + elif self.val.minfo.ord == ordering_t.ORD_DEGREVLEX: return "degrevlex" + else: + # This should be unreachable + raise ValueError("unknown ordering") def gen(self, slong i): """ From ca3dd7059d544b3ba3ff82b48813e13f32e2c41d Mon Sep 17 00:00:00 2001 From: Jake Moss Date: Mon, 22 Apr 2024 12:39:07 +1000 Subject: [PATCH 31/95] Avoid from ... import * --- src/flint/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/flint/__init__.py b/src/flint/__init__.py index cdf0f41b..74b542ca 100644 --- a/src/flint/__init__.py +++ b/src/flint/__init__.py @@ -15,12 +15,12 @@ from .types.nmod_mat import * from .types.nmod_series import * -from .types.fmpz_mpoly import * +from .types.fmpz_mpoly import fmpz_mpoly_ctx, fmpz_mpoly from .types.fmpz_mod import * from .types.fmpz_mod_poly import * from .types.fmpz_mod_mat import fmpz_mod_mat -from .types.fmpq_mpoly import * +from .types.fmpq_mpoly import fmpq_mpoly_ctx, fmpq_mpoly from .types.fmpz_mpoly_q import * from .types.arf import * From ea195116f51195c2cddacca95f6936a2cf1569b0 Mon Sep 17 00:00:00 2001 From: Jake Moss Date: Mon, 22 Apr 2024 12:56:56 +1000 Subject: [PATCH 32/95] Allow 0 variables and replace asserts --- src/flint/flint_base/flint_base.pyx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index 953abd7a..3684964f 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -134,9 +134,11 @@ cdef class flint_mpoly_context(flint_elem): def __cinit__(self): self._init = False - def __init__(self, long nvars, names): - assert nvars >= 1 - assert len(names) == nvars + def __init__(self, int nvars, names): + if nvars < 0: + raise ValueError("cannot have a negative amount of variables") + elif len(names) != nvars: + raise ValueError("number of variables must match lens of variable names") self.py_names = tuple(bytes(name, 'utf-8') if not isinstance(name, bytes) else name for name in names) self.c_names = libc.stdlib.malloc(nvars * sizeof(char *)) self._init = True @@ -155,7 +157,8 @@ cdef class flint_mpoly_context(flint_elem): return f"{self.__class__.__name__}({self.nvars()}, '{self.ordering()}', {self.names()})" def name(self, long i): - assert i >= 0 and i < len(self.py_names) + if not 0 <= i < len(self.py_names): + raise IndexError("variable name index out of range") return self.py_names[i].decode('utf-8') def names(self): From 62f1446fc3abade70494d696e7bfcfaec2b9e22c Mon Sep 17 00:00:00 2001 From: Jake Moss Date: Mon, 22 Apr 2024 13:16:00 +1000 Subject: [PATCH 33/95] Support 0 var fmpz_mpolys --- src/flint/flint_base/flint_base.pyx | 4 ++-- src/flint/test/test.py | 4 +++- src/flint/types/fmpz_mpoly.pyx | 4 ++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index 3684964f..ccd27541 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -178,8 +178,8 @@ cdef class flint_mpoly_context(flint_elem): @classmethod def get_context(cls, slong nvars=1, ordering: str = "lex", names: Optional[str] = "x", nametup: Optional[tuple] = None): - if nvars <= 0: - nvars = 1 + if nvars < 0: + raise ValueError("cannot have a negative amount of variables") if nametup is None and names is not None: key = cls.create_cache_key(nvars, ordering, names) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 49e2e1dc..18c231b3 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -2653,7 +2653,9 @@ def test_mpolys(): ctx = C.get_context(nvars=2) assert raises(lambda: C.get_context(nvars=2, ordering="bad"), ValueError) - assert raises(lambda: ctx.constant("bad"), ValueError) + assert raises(lambda: C.get_context(nvars=-1), ValueError) + assert raises(lambda: C(-1, "lex", []), ValueError) + assert raises(lambda: ctx.constant("bad"), TypeError) assert raises(lambda: ctx.from_dict("bad"), ValueError) assert raises(lambda: ctx.from_dict({(0, 0): "bad"}), TypeError) assert raises(lambda: ctx.from_dict({(0, "bad"): 1}), TypeError) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 149fda1c..134a9863 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -980,6 +980,8 @@ cdef class fmpz_mpoly(flint_mpoly): else: i = vars[var] elif isinstance(var, int): + if not 0 <= i < self.ctx.nvars(): + raise IndexError("generator index out of range") i = var else: raise TypeError("invalid variable type") @@ -1002,6 +1004,8 @@ cdef class fmpz_mpoly(flint_mpoly): else: i = vars[var] elif isinstance(var, int): + if not 0 <= i < self.ctx.nvars(): + raise IndexError("generator index out of range") i = var else: raise TypeError("invalid variable type") From df9b929cd8c899d96fc8094d0a1d5552bf960bc9 Mon Sep 17 00:00:00 2001 From: Jake Moss Date: Mon, 22 Apr 2024 14:34:58 +1000 Subject: [PATCH 34/95] Fix uninitialised variable warnings --- src/flint/types/fmpz_mpoly.pyx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 134a9863..2a285f5c 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -299,7 +299,7 @@ cdef class fmpz_mpoly(flint_mpoly): def __getitem__(self, x): cdef: - fmpz_struct ** tmp + fmpz_struct ** tmp = NULL slong nvars = self.ctx.nvars() if isinstance(x, int): @@ -328,7 +328,7 @@ cdef class fmpz_mpoly(flint_mpoly): def __setitem__(self, x, y): cdef: - fmpz_struct ** tmp + fmpz_struct ** tmp = NULL slong nvars = self.ctx.nvars() coeff = any_as_fmpz(y) @@ -971,7 +971,7 @@ cdef class fmpz_mpoly(flint_mpoly): def derivative(self, var): cdef: fmpz_mpoly res - slong i + slong i = 0 if isinstance(var, str): vars = {x: i for i, x in enumerate(self.ctx.names())} @@ -995,7 +995,7 @@ cdef class fmpz_mpoly(flint_mpoly): cdef: fmpz_mpoly res fmpz scale - slong i + slong i = 0 if isinstance(var, str): vars = {x: i for i, x in enumerate(self.ctx.names())} From 40b383d753954494fd9f4aaea4749d6e8a1f2278 Mon Sep 17 00:00:00 2001 From: Jake Moss Date: Mon, 22 Apr 2024 15:47:38 +1000 Subject: [PATCH 35/95] Remove unused imports and coercions to fmpq_mpoly --- src/flint/types/fmpz_mpoly.pyx | 56 ++-------------------------------- 1 file changed, 2 insertions(+), 54 deletions(-) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 2a285f5c..a7512b76 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -1,12 +1,8 @@ -from cpython.version cimport PY_MAJOR_VERSION -from cpython.dict cimport PyDict_Size, PyDict_Check, PyDict_Next +from cpython.dict cimport PyDict_Size, PyDict_Check from cpython.tuple cimport PyTuple_Check, PyTuple_GET_SIZE -from cpython.object cimport Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE +from cpython.object cimport Py_EQ, Py_NE from flint.flintlib.fmpz cimport fmpz_init, fmpz_clear, fmpz_is_zero -from flint.flintlib.fmpq_mpoly cimport fmpq_mpoly_t, fmpq_mpoly_add_fmpq, fmpq_mpoly_sub_fmpq, fmpq_mpoly_scalar_mul_fmpq from flint.flintlib.flint cimport * -from flint.flintlib.fmpq cimport fmpq_numref, fmpq_denref -from flint.flintlib.fmpz_mpoly_q cimport fmpz_mpoly_q_div_fmpz from flint.utils.conversion cimport str_from_chars from flint.utils.typecheck cimport typecheck @@ -14,9 +10,7 @@ from flint.flint_base.flint_base cimport flint_mpoly from flint.flint_base.flint_base cimport flint_mpoly_context from flint.types.fmpz cimport any_as_fmpz from flint.types.fmpz cimport fmpz, fmpz_set_any_ref -from flint.types.fmpq cimport any_as_fmpq, fmpq_set_any_ref from flint.types.fmpq_mpoly cimport fmpq_mpoly -from flint.types.fmpz_mpoly_q cimport fmpz_mpoly_q from flint.utils.flint_exceptions import DomainError, IncompatibleContextError @@ -426,9 +420,7 @@ cdef class fmpz_mpoly(flint_mpoly): def __add__(self, other): cdef fmpz_mpoly res - cdef fmpq_mpoly qres cdef fmpz_t z_other - cdef fmpq_t q_other cdef int xtype if typecheck(other, fmpz_mpoly): if (self).ctx is not (other).ctx: @@ -442,18 +434,11 @@ cdef class fmpz_mpoly(flint_mpoly): res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_add_fmpz(res.val, (self).val, z_other, res.ctx.val) return res - xtype = fmpq_set_any_ref(q_other, other) - if xtype != FMPZ_UNKNOWN: - qres = fmpq_mpoly(self) - fmpq_mpoly_add_fmpq(qres.val, qres.val, q_other, qres.ctx.val) - return qres return NotImplemented def __radd__(self, other): cdef fmpz_mpoly res - cdef fmpq_mpoly qres cdef fmpz_t z_other - cdef fmpq_t q_other cdef int xtype xtype = fmpz_set_any_ref(z_other, other) @@ -461,11 +446,6 @@ cdef class fmpz_mpoly(flint_mpoly): res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_add_fmpz(res.val, (self).val, z_other, res.ctx.val) return res - xtype = fmpq_set_any_ref(q_other, other) - if xtype != FMPZ_UNKNOWN: - qres = fmpq_mpoly(self) - fmpq_mpoly_add_fmpq(qres.val, qres.val, q_other, qres.ctx.val) - return qres return NotImplemented def __iadd__(self, other): @@ -483,9 +463,7 @@ cdef class fmpz_mpoly(flint_mpoly): def __sub__(self, other): cdef fmpz_mpoly res - cdef fmpq_mpoly qres cdef fmpz_t z_other - cdef fmpq_t q_other cdef int xtype if typecheck(other, fmpz_mpoly): if (self).ctx is not (other).ctx: @@ -499,18 +477,11 @@ cdef class fmpz_mpoly(flint_mpoly): res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_sub_fmpz(res.val, (self).val, z_other, res.ctx.val) return res - xtype = fmpq_set_any_ref(q_other, other) - if xtype != FMPZ_UNKNOWN: - qres = fmpq_mpoly(self) - fmpq_mpoly_sub_fmpq(qres.val, qres.val, q_other, qres.ctx.val) - return qres return NotImplemented def __rsub__(self, other): cdef fmpz_mpoly res - cdef fmpq_mpoly qres cdef fmpz_t z_other - cdef fmpq_t q_other cdef int xtype xtype = fmpz_set_any_ref(z_other, other) @@ -518,11 +489,6 @@ cdef class fmpz_mpoly(flint_mpoly): res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_sub_fmpz(res.val, (self).val, z_other, res.ctx.val) return -res - xtype = fmpq_set_any_ref(q_other, other) - if xtype != FMPZ_UNKNOWN: - qres = fmpq_mpoly(self) - fmpq_mpoly_sub_fmpq(qres.val, qres.val, q_other, qres.ctx.val) - return -qres return NotImplemented def __isub__(self, other): @@ -540,9 +506,7 @@ cdef class fmpz_mpoly(flint_mpoly): def __mul__(self, other): cdef fmpz_mpoly res - cdef fmpq_mpoly qres cdef fmpz_t z_other - cdef fmpq_t q_other cdef int xtype if typecheck(other, fmpz_mpoly): @@ -557,18 +521,11 @@ cdef class fmpz_mpoly(flint_mpoly): res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_scalar_mul_fmpz(res.val, (self).val, z_other, res.ctx.val) return res - xtype = fmpq_set_any_ref(q_other, other) - if xtype != FMPZ_UNKNOWN: - qres = fmpq_mpoly(self) - fmpq_mpoly_scalar_mul_fmpq(qres.val, qres.val, q_other, qres.ctx.val) - return qres return NotImplemented def __rmul__(self, other): cdef fmpz_mpoly res - cdef fmpq_mpoly qres cdef fmpz_t z_other - cdef fmpq_t q_other cdef int xtype xtype = fmpz_set_any_ref(z_other, other) @@ -576,11 +533,6 @@ cdef class fmpz_mpoly(flint_mpoly): res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_scalar_mul_fmpz(res.val, (self).val, z_other, res.ctx.val) return res - xtype = fmpq_set_any_ref(q_other, other) - if xtype != FMPZ_UNKNOWN: - qres = fmpq_mpoly(self) - fmpq_mpoly_scalar_mul_fmpq(qres.val, qres.val, q_other, qres.ctx.val) - return qres return NotImplemented def __imul__(self, other): @@ -594,9 +546,6 @@ cdef class fmpz_mpoly(flint_mpoly): if other is not NotImplemented: fmpz_mpoly_scalar_mul_fmpz(self.val, (self).val, (other).val, self.ctx.val) return self - other = any_as_fmpq(other) - if other is not NotImplemented: - return fmpq_mpoly(self) * other return NotImplemented def __pow__(self, other, modulus): @@ -685,7 +634,6 @@ cdef class fmpz_mpoly(flint_mpoly): def __truediv__(self, other): cdef: fmpz_mpoly res - int xtype if typecheck(other, fmpz_mpoly): if not other: From 35c23a40783180455b4dea82d9cb92d7724fc975 Mon Sep 17 00:00:00 2001 From: Jake Moss Date: Mon, 22 Apr 2024 15:48:09 +1000 Subject: [PATCH 36/95] Enabling the troublesome tests --- src/flint/test/test.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 18c231b3..e6973999 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -2743,19 +2743,19 @@ def quick_poly(): assert raises(lambda: p[-1], IndexError) assert raises(lambda: p[4], IndexError) - # assert p[(2, 2)] == 4 - # assert p[(0, 0)] == 1 - # assert raises(lambda: p[(1,)], ValueError) - # assert raises(lambda: p[(1, "bad")], TypeError) - # assert raises(lambda: p["bad"], TypeError) - - # p = quick_poly() - # p[1] = S(10) - # assert p == mpoly({(0, 0): 1, (0, 1): 2, (1, 0): 10, (2, 2): 4}) - - # p = quick_poly() - # p[(1, 0)] = S(10) - # assert p == mpoly({(0, 0): 1, (0, 1): 2, (1, 0): 10, (2, 2): 4}) + assert p[(2, 2)] == 4 + assert p[(0, 0)] == 1 + assert raises(lambda: p[(1,)], ValueError) + assert raises(lambda: p[(1, "bad")], TypeError) + assert raises(lambda: p["bad"], TypeError) + + p = quick_poly() + p[1] = S(10) + assert p == mpoly({(0, 0): 1, (0, 1): 2, (1, 0): 10, (2, 2): 4}) + + p = quick_poly() + p[(1, 0)] = S(10) + assert p == mpoly({(0, 0): 1, (0, 1): 2, (1, 0): 10, (2, 2): 4}) assert raises(lambda: p.__setitem__(-1, 1), IndexError) assert raises(lambda: p.__setitem__(4, 1), IndexError) From 3d51daa0362bf0d05efc73cc58bc72936c1045da Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Wed, 24 Apr 2024 22:21:00 +1000 Subject: [PATCH 37/95] Fix variable being prematurely garbage collected --- src/flint/types/fmpz_mpoly.pyx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index a7512b76..d278c085 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -308,11 +308,11 @@ cdef class fmpz_mpoly(flint_mpoly): res = fmpz() try: tmp = libc.stdlib.malloc(nvars * sizeof(fmpz_struct *)) + exp_vec = tuple(any_as_fmpz(exp) for exp in x) for i in range(nvars): - exp = any_as_fmpz(x[i]) - if exp is NotImplemented: + if exp_vec[i] is NotImplemented: raise TypeError("exponent not coercible to fmpz") - tmp[i] = &((exp).val[0]) + tmp[i] = &((exp_vec[i]).val[0]) fmpz_mpoly_get_coeff_fmpz_fmpz((res).val, self.val, tmp, self.ctx.val) return res finally: @@ -339,11 +339,11 @@ cdef class fmpz_mpoly(flint_mpoly): try: tmp = libc.stdlib.malloc(nvars * sizeof(fmpz_struct *)) + exp_vec = tuple(any_as_fmpz(exp) for exp in x) for i in range(nvars): - exp = any_as_fmpz(x[i]) - if exp is NotImplemented: + if exp_vec[i] is NotImplemented: raise TypeError("exponent not coercible to fmpz") - tmp[i] = &((exp).val[0]) + tmp[i] = &((exp_vec[i]).val[0]) fmpz_mpoly_set_coeff_fmpz_fmpz(self.val, (coeff).val, tmp, self.ctx.val) finally: libc.stdlib.free(tmp) From e19044ebe75565f6f5210f9655b3b36bce8cf996 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Thu, 25 Apr 2024 17:49:50 +1000 Subject: [PATCH 38/95] Check malloc return value --- src/flint/types/fmpz_mpoly.pyx | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index d278c085..3309743b 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -175,7 +175,7 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): fmpz_init(coefficient) exponents = libc.stdlib.calloc(nvars, sizeof(fmpz_struct)) if exponents == NULL: - raise MemoryError() + raise MemoryError("malloc returned a null pointer") for i in range(nvars): fmpz_init(exponents + i) fmpz_init(coefficient) @@ -308,6 +308,8 @@ cdef class fmpz_mpoly(flint_mpoly): res = fmpz() try: tmp = libc.stdlib.malloc(nvars * sizeof(fmpz_struct *)) + if tmp is NULL: + raise MemoryError("malloc returned a null pointer") exp_vec = tuple(any_as_fmpz(exp) for exp in x) for i in range(nvars): if exp_vec[i] is NotImplemented: @@ -339,6 +341,8 @@ cdef class fmpz_mpoly(flint_mpoly): try: tmp = libc.stdlib.malloc(nvars * sizeof(fmpz_struct *)) + if tmp is NULL: + raise MemoryError("malloc returned a null pointer") exp_vec = tuple(any_as_fmpz(exp) for exp in x) for i in range(nvars): if exp_vec[i] is NotImplemented: @@ -367,6 +371,8 @@ cdef class fmpz_mpoly(flint_mpoly): nvars = self.ctx.nvars() res = tuple(fmpz() for j in range(nvars)) tmp = libc.stdlib.malloc(nvars * sizeof(fmpz_struct *)) + if tmp is NULL: + raise MemoryError("malloc returned a null pointer") try: for j in range(nvars): tmp[j] = &(( (res[j])).val[0]) @@ -382,6 +388,8 @@ cdef class fmpz_mpoly(flint_mpoly): res = tuple(fmpz() for _ in range(nvars)) tmp = libc.stdlib.malloc(nvars * sizeof(fmpz_struct *)) + if tmp is NULL: + raise MemoryError("malloc returned a null pointer") for i in range(nvars): tmp[i] = &(( res[i]).val[0]) @@ -749,6 +757,8 @@ cdef class fmpz_mpoly(flint_mpoly): # Normal evaluation try: V = libc.stdlib.malloc(nvars * sizeof(fmpz_struct *)) + if V is NULL: + raise MemoryError("malloc returned a null pointer") for i in range(nvars): V[i] = &(( args_fmpz[i]).val[0]) vres = fmpz.__new__(fmpz) @@ -777,6 +787,8 @@ cdef class fmpz_mpoly(flint_mpoly): raise ValueError("all arguments must share the same context") C = libc.stdlib.malloc(nvars * sizeof(fmpz_mpoly_struct *)) + if C is NULL: + raise MemoryError("malloc returned a null pointer") try: for i in range(nvars): C[i] = &(( args[i]).val[0]) @@ -810,6 +822,8 @@ cdef class fmpz_mpoly(flint_mpoly): polys[i] = res C = libc.stdlib.malloc(nvars * sizeof(fmpz_mpoly_struct *)) + if C is NULL: + raise MemoryError("malloc returned a null pointer") try: for i in range(len(polys)): C[i] = &(( polys[i]).val[0]) @@ -905,6 +919,8 @@ cdef class fmpz_mpoly(flint_mpoly): return self C = libc.stdlib.malloc(self.ctx.val.minfo.nvars * sizeof(slong *)) + if C is NULL: + raise MemoryError("malloc returned a null pointer") res = create_fmpz_mpoly(self.ctx) vars = {x: i for i, x in enumerate(ctx.py_names)} From fa07adfb0a99a30e81dc78774d0a9d471ab45081 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Thu, 25 Apr 2024 17:57:37 +1000 Subject: [PATCH 39/95] Instrument windows CI: will be reverted --- src/flint/test/test.py | 118 ++++++++++++++++++++--------------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index e6973999..2caab1dd 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -2660,66 +2660,66 @@ def test_mpolys(): assert raises(lambda: ctx.from_dict({(0, 0): "bad"}), TypeError) assert raises(lambda: ctx.from_dict({(0, "bad"): 1}), TypeError) assert raises(lambda: ctx.from_dict({(0,): 1}), TypeError) - + print("mpoly test section: 1", flush=True) assert raises(lambda: P(val=C.get_context(nvars=1).constant(0), ctx=ctx), ValueError) assert raises(lambda: P(val={}, ctx=None), ValueError) assert raises(lambda: P(val={"bad": 1}, ctx=None), ValueError) assert raises(lambda: P(val="1", ctx=None), ValueError) - + print("mpoly test section: 2", flush=True) assert P(val={(0, 0): 1}, ctx=ctx) == ctx.from_dict({(0, 0): 1}) assert P(ctx=ctx).context() == ctx assert P(1, ctx=ctx).is_one() - + print("mpoly test section: 3", flush=True) def mpoly(x): return ctx.from_dict(x) - + print("mpoly test section: 4", flush=True) def quick_poly(): return mpoly({(0, 0): 1, (0, 1): 2, (1, 0): 3, (2, 2): 4}) - + print("mpoly test section: 5", flush=True) assert ctx.nvars() == 2 assert ctx.ordering() == "lex" - + print("mpoly test section: 6", flush=True) ctx1 = C.get_context(4) assert [ctx1.name(i) for i in range(4)] == ['x0', 'x1', 'x2', 'x3'] for order in ['lex', 'deglex', 'degrevlex']: ctx1 = C.get_context(4, order) assert ctx1.ordering() == order - + print("mpoly test section: 7", flush=True) assert ctx.constant(1) == mpoly({(0, 0): 1}) == P(1, ctx=ctx) - + print("mpoly test section: 8", flush=True) assert raises(lambda: P([None]), TypeError) assert raises(lambda: P(object()), TypeError), f"{P(object()) = }" assert raises(lambda: P(None), TypeError) assert raises(lambda: P(None, None), TypeError) assert raises(lambda: P([1,2], None), TypeError) assert raises(lambda: P(1, None), ValueError) - + print("mpoly test section: 9", flush=True) assert len(P(ctx=ctx)) == len(mpoly({(0, 0): 0})) == 0 assert len(P(1, ctx=ctx)) == len(mpoly({(0, 0): 1})) == 1 assert len(mpoly({(0, 0): 1, (0, 1): 1})) == 2 assert len(mpoly({(0, 0): 1, (0, 1): 1, (1, 0): 1})) == 3 - + print("mpoly test section: 10", flush=True) # degree is -1 when 0 poly assert P(ctx=ctx).degrees() == mpoly({(0, 0): 0}).degrees() == {"x0": -1, "x1": -1} assert P(1, ctx=ctx).degrees() == mpoly({(0, 0): 1}).degrees() == {"x0": 0, "x1": 0} assert mpoly({(0, 0): 1, (0, 1): 1}).degrees() == {"x0": 0, "x1": 1} assert mpoly({(0, 0): 1, (0, 1): 1, (1, 0): 1}).degrees() == {"x0": 1, "x1": 1} assert mpoly({(0, 0): 1, (0, 1): 1, (1, 0): 1, (2, 2): 2}).degrees() == {"x0": 2, "x1": 2} - + print("mpoly test section: 11", flush=True) assert (P(1, ctx=ctx) == P(1, ctx=ctx)) is True assert (P(1, ctx=ctx) != P(1, ctx=ctx)) is False assert (P(1, ctx=ctx) == P(2, ctx=ctx)) is False assert (P(1, ctx=ctx) != P(2, ctx=ctx)) is True - + print("mpoly test section: 12", flush=True) ctx1 = C.get_context(1) assert (P(1, ctx=ctx) == P(1, ctx=ctx1)) is False assert (P(1, ctx=ctx) != P(1, ctx=ctx1)) is True - + print("mpoly test section: 13", flush=True) assert (P(1, ctx=ctx) == None) is False assert (P(1, ctx=ctx) != None) is True assert (None == P(1, ctx=ctx)) is False assert (None != P(1, ctx=ctx)) is True - + print("mpoly test section: 14", flush=True) assert raises(lambda: P(ctx=ctx) < P(ctx=ctx), TypeError) assert raises(lambda: P(ctx=ctx) <= P(ctx=ctx), TypeError) assert raises(lambda: P(ctx=ctx) > P(ctx=ctx), TypeError) @@ -2732,54 +2732,54 @@ def quick_poly(): assert raises(lambda: None <= P(ctx=ctx), TypeError) assert raises(lambda: None > P(ctx=ctx), TypeError) assert raises(lambda: None >= P(ctx=ctx), TypeError) - + print("mpoly test section: 15", flush=True) p = quick_poly() assert p.coefficient(2) == S(2) assert p.coefficient(-1) == S(0) assert p.coefficient(10) == S(0) - + print("mpoly test section: 16", flush=True) assert p[0] == mpoly({(2, 2): 4}) assert p[3] == mpoly({(0, 0): 1}) assert raises(lambda: p[-1], IndexError) assert raises(lambda: p[4], IndexError) - + print("mpoly test section: 17", flush=True) assert p[(2, 2)] == 4 assert p[(0, 0)] == 1 assert raises(lambda: p[(1,)], ValueError) assert raises(lambda: p[(1, "bad")], TypeError) assert raises(lambda: p["bad"], TypeError) - + print("mpoly test section: 18", flush=True) p = quick_poly() p[1] = S(10) assert p == mpoly({(0, 0): 1, (0, 1): 2, (1, 0): 10, (2, 2): 4}) - + print("mpoly test section: 19", flush=True) p = quick_poly() p[(1, 0)] = S(10) assert p == mpoly({(0, 0): 1, (0, 1): 2, (1, 0): 10, (2, 2): 4}) - + print("mpoly test section: 20", flush=True) assert raises(lambda: p.__setitem__(-1, 1), IndexError) assert raises(lambda: p.__setitem__(4, 1), IndexError) - + print("mpoly test section: 21", flush=True) assert raises(lambda: p.__setitem__((1,), 1), ValueError) assert raises(lambda: p.__setitem__((1, "bad"), 1), TypeError) assert raises(lambda: p.__setitem__("bad", 1), TypeError) - + print("mpoly test section: 22", flush=True) assert raises(lambda: p.__setitem__(2, None), TypeError) assert raises(lambda: p.__setitem__(-1, 1), IndexError) - + print("mpoly test section: 23", flush=True) assert repr(P(ctx=ctx)) == '0' assert repr(P(1, ctx=ctx)) == '1' assert repr(quick_poly()) == '4*x0^2*x1^2 + 3*x0 + 2*x1 + 1' - + print("mpoly test section: 24", flush=True) assert p.exponent_tuple(0) == (2, 2) assert p.exponent_tuple(3) == (0, 0) assert raises(lambda: p.exponent_tuple(-1), IndexError) assert raises(lambda: p.exponent_tuple(4), IndexError) - + print("mpoly test section: 25", flush=True) assert p.total_degree() == 4 assert P(ctx=ctx).total_degree() == -1 assert P(1, ctx=ctx).total_degree() == 0 - + print("mpoly test section: 26", flush=True) p = quick_poly() assert p(0, 0) == p(0, S(0)) == p(S(0), S(0)) == p(x1=S(0), x0=S(0)) == S(1) == 1 assert p(1, 1) == p(x1=S(1), x0=S(1)) == S(10) == 10 @@ -2792,42 +2792,42 @@ def quick_poly(): (0, 0): 4 }) assert raises(lambda: p(x0=None), TypeError) - + print("mpoly test section: 27", flush=True) assert bool(P(ctx=ctx)) is False assert bool(P(1, ctx=ctx)) is True - + print("mpoly test section: 28", flush=True) assert +quick_poly() \ == quick_poly() assert -quick_poly() \ == mpoly({(0, 0): -1, (0, 1): -2, (1, 0): -3, (2, 2): -4}) - + print("mpoly test section: 29", flush=True) assert quick_poly() \ + mpoly({(0, 0): 5, (0, 1): 6, (1, 0): 7, (2, 2): 8}) \ == mpoly({(0, 0): 6, (0, 1): 8, (1, 0): 10, (2, 2): 12}) - + print("mpoly test section: 30", flush=True) for T in [int, S, flint.fmpz]: assert quick_poly() + T(1) \ == mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}) assert T(1) + quick_poly() \ == mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}) - + print("mpoly test section: 31", flush=True) assert raises(lambda: mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}) + None, TypeError) assert raises(lambda: None + mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}), TypeError) assert raises(lambda: quick_poly() + P(ctx=ctx1), IncompatibleContextError) assert raises(lambda: quick_poly().__iadd__(P(ctx=ctx1)), IncompatibleContextError) - + print("mpoly test section: 32", flush=True) assert quick_poly() - mpoly({(0, 0): 5, (0, 1): 6, (1, 0): 7, (2, 2): 8}) \ == mpoly({(0, 0): -4, (0, 1): -4, (1, 0): -4, (2, 2): -4}) - + print("mpoly test section: 33", flush=True) for T in [int, S, flint.fmpz]: assert quick_poly() - T(1) == mpoly({(0, 1): 2, (1, 0): 3, (2, 2): 4}) assert T(1) - quick_poly() == mpoly({(0, 1): -2, (1, 0): -3, (2, 2): -4}) - + print("mpoly test section: 34", flush=True) assert raises(lambda: quick_poly() - None, TypeError) assert raises(lambda: None - quick_poly(), TypeError) assert raises(lambda: quick_poly() - P(ctx=ctx1), IncompatibleContextError) assert raises(lambda: quick_poly().__isub__(P(ctx=ctx1)), IncompatibleContextError) - + print("mpoly test section: 35", flush=True) assert quick_poly() * mpoly({(1, 0): 5, (0, 1): 6}) \ == mpoly({ (3, 2): 20, @@ -2838,26 +2838,26 @@ def quick_poly(): (0, 2): 12, (0, 1): 6 }) - + print("mpoly test section: 36", flush=True) for T in [int, S, flint.fmpz]: assert quick_poly() * T(2) == mpoly({(0, 0): 2, (0, 1): 4, (1, 0): 6, (2, 2): 8}) assert T(2) * quick_poly() == mpoly({(0, 0): 2, (0, 1): 4, (1, 0): 6, (2, 2): 8}) - + print("mpoly test section: 37", flush=True) assert raises(lambda: quick_poly() * None, TypeError) assert raises(lambda: None * quick_poly(), TypeError) assert raises(lambda: quick_poly() * P(ctx=ctx1), IncompatibleContextError) assert raises(lambda: quick_poly().__imul__(P(ctx=ctx1)), IncompatibleContextError) - + print("mpoly test section: 38", flush=True) assert quick_poly() // mpoly({(1, 1): 1}) == mpoly({(1, 1): 4}) assert quick_poly() % mpoly({(1, 1): 1}) \ == mpoly({(1, 0): 3, (0, 1): 2, (0, 0): 1}) assert divmod(quick_poly(), mpoly({(1, 1): 1})) \ == (mpoly({(1, 1): 4}), mpoly({(1, 0): 3, (0, 1): 2, (0, 0): 1})) - + print("mpoly test section: 39", flush=True) assert quick_poly() // 1 == quick_poly() assert quick_poly() % 1 == P(ctx=ctx) assert divmod(quick_poly(), 1) == (quick_poly(), P(ctx=ctx)) - + print("mpoly test section: 40", flush=True) # TODO FIXME # if is_field: # assert P([1, 1]) // 2 == P([S(1)/2, S(1)/2]) @@ -2865,31 +2865,31 @@ def quick_poly(): # else: # assert P([1, 1]) // 2 == P([0, 0]) # assert P([1, 1]) % 2 == P([1, 1]) - + print("mpoly test section: 41", flush=True) assert 1 // quick_poly() == P(ctx=ctx) assert 1 % quick_poly() == P(1, ctx=ctx) assert divmod(1, quick_poly()) == (P(ctx=ctx), P(1, ctx=ctx)) - + print("mpoly test section: 42", flush=True) assert raises(lambda: quick_poly() // None, TypeError) assert raises(lambda: quick_poly() % None, TypeError) assert raises(lambda: divmod(quick_poly(), None), TypeError) - + print("mpoly test section: 43", flush=True) assert raises(lambda: None // quick_poly(), TypeError) assert raises(lambda: None % quick_poly(), TypeError) assert raises(lambda: divmod(None, quick_poly()), TypeError) - + print("mpoly test section: 44", flush=True) assert raises(lambda: quick_poly() // 0, ZeroDivisionError) assert raises(lambda: quick_poly() % 0, ZeroDivisionError) assert raises(lambda: divmod(quick_poly(), 0), ZeroDivisionError) - + print("mpoly test section: 45", flush=True) assert raises(lambda: quick_poly() // P(ctx=ctx), ZeroDivisionError) assert raises(lambda: quick_poly() % P(ctx=ctx), ZeroDivisionError) assert raises(lambda: divmod(quick_poly(), P(ctx=ctx)), ZeroDivisionError) - + print("mpoly test section: 46", flush=True) assert raises(lambda: quick_poly() // P(1, ctx=ctx1), IncompatibleContextError) assert raises(lambda: quick_poly() % P(1, ctx=ctx1), IncompatibleContextError) assert raises(lambda: divmod(quick_poly(), P(1, ctx=ctx1)), IncompatibleContextError) - + print("mpoly test section: 47", flush=True) # TODO FIXME # # Exact/field scalar division @@ -2900,7 +2900,7 @@ def quick_poly(): # assert P([2, 2]) / 2 == P([1, 1]) # assert raises(lambda: P([1, 2]) / 2, DomainError) # assert raises(lambda: P([1, 2]) / 0, ZeroDivisionError) - + print("mpoly test section: 48", flush=True) f = mpoly({(1, 1): 4, (0, 0): 1}) g = mpoly({(0, 1): 2, (1, 0): 2}) assert f \ @@ -2909,7 +2909,7 @@ def quick_poly(): == mpoly({(1, 1): 8, (0, 0): 2}) assert raises(lambda: 1 / quick_poly(), DomainError) assert raises(lambda: quick_poly() / P(2, ctx=ctx), DomainError) - + print("mpoly test section: 49", flush=True) assert quick_poly() ** 0 == P(1, ctx=ctx) assert quick_poly() ** 1 == quick_poly() assert quick_poly() ** 2 == mpoly({ @@ -2926,14 +2926,14 @@ def quick_poly(): }) assert raises(lambda: P(ctx=ctx) ** -1, ValueError) assert raises(lambda: P(ctx=ctx) ** None, TypeError) - + print("mpoly test section: 50", flush=True) # # XXX: Not sure what this should do in general: assert raises(lambda: pow(P(1, ctx=ctx), 2, 3), NotImplementedError) - + print("mpoly test section: 51", flush=True) assert (f * g).gcd(f) == f assert raises(lambda: quick_poly().gcd(None), TypeError) assert raises(lambda: quick_poly().gcd(P(ctx=ctx1)), IncompatibleContextError) - + print("mpoly test section: 52", flush=True) # TODO FIXME # if is_field: # p1 = P([1, 0, 1]) @@ -2941,30 +2941,30 @@ def quick_poly(): # g, s, t = P([1]), P([1])/5, P([2, -1])/5 # assert p1.xgcd(p2) == (g, s, t) # assert raises(lambda: p1.xgcd(None), TypeError) - + print("mpoly test section: 53", flush=True) assert (f * g).factor() == (S(2), [(mpoly({(0, 1): 1, (1, 0): 1}), 1), (f, 1)]) - + print("mpoly test section: 54", flush=True) assert (f * f).sqrt() == f assert (f * f).sqrt(assume_perfect_square=True) == f assert raises(lambda: quick_poly().sqrt(), ValueError) - + print("mpoly test section: 55", flush=True) # TODO FIXME # if P == flint.fmpq_mpoly: # assert raises(lambda: P([1, 2, 1], 3).sqrt(), ValueError) # assert P([1, 2, 1], 4).sqrt() == P([1, 1], 2) - + print("mpoly test section: 56", flush=True) # assert P(ctx=C).deflation() == (P(ctx=C), 1) # assert P([1, 2]).deflation() == (P([1, 2]), 1) # assert P([1, 0, 2]).deflation() == (P([1, 2]), 2) - + print("mpoly test section: 57", flush=True) assert quick_poly().derivative(0) == quick_poly().derivative("x0") == mpoly({(0, 0): 3, (1, 2): 8}) assert quick_poly().derivative(1) == quick_poly().derivative("x1") == mpoly({(0, 0): 2, (2, 1): 8}) - + print("mpoly test section: 58", flush=True) assert quick_poly().integral(0) == quick_poly().integral("x0") == \ (6, mpoly({(3, 2): 8, (2, 0): 9, (1, 1): 12, (1, 0): 6})) assert quick_poly().integral(1) == quick_poly().integral("x1") == \ (3, mpoly({(2, 3): 4, (1, 1): 9, (0, 2): 3, (0, 1): 3})) - + print("mpoly test section: 59", flush=True) # if is_field: # assert P([1, 2, 1]).integral() == P([0, 1, 1, S(1)/3]) From 63f22fa90158cbfaba059ec01567fb3b362ec3ec Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Thu, 25 Apr 2024 19:06:17 +1000 Subject: [PATCH 40/95] Revert "Instrument windows CI: will be reverted" This reverts commit fa07adfb0a99a30e81dc78774d0a9d471ab45081. --- src/flint/test/test.py | 118 ++++++++++++++++++++--------------------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/src/flint/test/test.py b/src/flint/test/test.py index 2caab1dd..e6973999 100644 --- a/src/flint/test/test.py +++ b/src/flint/test/test.py @@ -2660,66 +2660,66 @@ def test_mpolys(): assert raises(lambda: ctx.from_dict({(0, 0): "bad"}), TypeError) assert raises(lambda: ctx.from_dict({(0, "bad"): 1}), TypeError) assert raises(lambda: ctx.from_dict({(0,): 1}), TypeError) - print("mpoly test section: 1", flush=True) + assert raises(lambda: P(val=C.get_context(nvars=1).constant(0), ctx=ctx), ValueError) assert raises(lambda: P(val={}, ctx=None), ValueError) assert raises(lambda: P(val={"bad": 1}, ctx=None), ValueError) assert raises(lambda: P(val="1", ctx=None), ValueError) - print("mpoly test section: 2", flush=True) + assert P(val={(0, 0): 1}, ctx=ctx) == ctx.from_dict({(0, 0): 1}) assert P(ctx=ctx).context() == ctx assert P(1, ctx=ctx).is_one() - print("mpoly test section: 3", flush=True) + def mpoly(x): return ctx.from_dict(x) - print("mpoly test section: 4", flush=True) + def quick_poly(): return mpoly({(0, 0): 1, (0, 1): 2, (1, 0): 3, (2, 2): 4}) - print("mpoly test section: 5", flush=True) + assert ctx.nvars() == 2 assert ctx.ordering() == "lex" - print("mpoly test section: 6", flush=True) + ctx1 = C.get_context(4) assert [ctx1.name(i) for i in range(4)] == ['x0', 'x1', 'x2', 'x3'] for order in ['lex', 'deglex', 'degrevlex']: ctx1 = C.get_context(4, order) assert ctx1.ordering() == order - print("mpoly test section: 7", flush=True) + assert ctx.constant(1) == mpoly({(0, 0): 1}) == P(1, ctx=ctx) - print("mpoly test section: 8", flush=True) + assert raises(lambda: P([None]), TypeError) assert raises(lambda: P(object()), TypeError), f"{P(object()) = }" assert raises(lambda: P(None), TypeError) assert raises(lambda: P(None, None), TypeError) assert raises(lambda: P([1,2], None), TypeError) assert raises(lambda: P(1, None), ValueError) - print("mpoly test section: 9", flush=True) + assert len(P(ctx=ctx)) == len(mpoly({(0, 0): 0})) == 0 assert len(P(1, ctx=ctx)) == len(mpoly({(0, 0): 1})) == 1 assert len(mpoly({(0, 0): 1, (0, 1): 1})) == 2 assert len(mpoly({(0, 0): 1, (0, 1): 1, (1, 0): 1})) == 3 - print("mpoly test section: 10", flush=True) + # degree is -1 when 0 poly assert P(ctx=ctx).degrees() == mpoly({(0, 0): 0}).degrees() == {"x0": -1, "x1": -1} assert P(1, ctx=ctx).degrees() == mpoly({(0, 0): 1}).degrees() == {"x0": 0, "x1": 0} assert mpoly({(0, 0): 1, (0, 1): 1}).degrees() == {"x0": 0, "x1": 1} assert mpoly({(0, 0): 1, (0, 1): 1, (1, 0): 1}).degrees() == {"x0": 1, "x1": 1} assert mpoly({(0, 0): 1, (0, 1): 1, (1, 0): 1, (2, 2): 2}).degrees() == {"x0": 2, "x1": 2} - print("mpoly test section: 11", flush=True) + assert (P(1, ctx=ctx) == P(1, ctx=ctx)) is True assert (P(1, ctx=ctx) != P(1, ctx=ctx)) is False assert (P(1, ctx=ctx) == P(2, ctx=ctx)) is False assert (P(1, ctx=ctx) != P(2, ctx=ctx)) is True - print("mpoly test section: 12", flush=True) + ctx1 = C.get_context(1) assert (P(1, ctx=ctx) == P(1, ctx=ctx1)) is False assert (P(1, ctx=ctx) != P(1, ctx=ctx1)) is True - print("mpoly test section: 13", flush=True) + assert (P(1, ctx=ctx) == None) is False assert (P(1, ctx=ctx) != None) is True assert (None == P(1, ctx=ctx)) is False assert (None != P(1, ctx=ctx)) is True - print("mpoly test section: 14", flush=True) + assert raises(lambda: P(ctx=ctx) < P(ctx=ctx), TypeError) assert raises(lambda: P(ctx=ctx) <= P(ctx=ctx), TypeError) assert raises(lambda: P(ctx=ctx) > P(ctx=ctx), TypeError) @@ -2732,54 +2732,54 @@ def quick_poly(): assert raises(lambda: None <= P(ctx=ctx), TypeError) assert raises(lambda: None > P(ctx=ctx), TypeError) assert raises(lambda: None >= P(ctx=ctx), TypeError) - print("mpoly test section: 15", flush=True) + p = quick_poly() assert p.coefficient(2) == S(2) assert p.coefficient(-1) == S(0) assert p.coefficient(10) == S(0) - print("mpoly test section: 16", flush=True) + assert p[0] == mpoly({(2, 2): 4}) assert p[3] == mpoly({(0, 0): 1}) assert raises(lambda: p[-1], IndexError) assert raises(lambda: p[4], IndexError) - print("mpoly test section: 17", flush=True) + assert p[(2, 2)] == 4 assert p[(0, 0)] == 1 assert raises(lambda: p[(1,)], ValueError) assert raises(lambda: p[(1, "bad")], TypeError) assert raises(lambda: p["bad"], TypeError) - print("mpoly test section: 18", flush=True) + p = quick_poly() p[1] = S(10) assert p == mpoly({(0, 0): 1, (0, 1): 2, (1, 0): 10, (2, 2): 4}) - print("mpoly test section: 19", flush=True) + p = quick_poly() p[(1, 0)] = S(10) assert p == mpoly({(0, 0): 1, (0, 1): 2, (1, 0): 10, (2, 2): 4}) - print("mpoly test section: 20", flush=True) + assert raises(lambda: p.__setitem__(-1, 1), IndexError) assert raises(lambda: p.__setitem__(4, 1), IndexError) - print("mpoly test section: 21", flush=True) + assert raises(lambda: p.__setitem__((1,), 1), ValueError) assert raises(lambda: p.__setitem__((1, "bad"), 1), TypeError) assert raises(lambda: p.__setitem__("bad", 1), TypeError) - print("mpoly test section: 22", flush=True) + assert raises(lambda: p.__setitem__(2, None), TypeError) assert raises(lambda: p.__setitem__(-1, 1), IndexError) - print("mpoly test section: 23", flush=True) + assert repr(P(ctx=ctx)) == '0' assert repr(P(1, ctx=ctx)) == '1' assert repr(quick_poly()) == '4*x0^2*x1^2 + 3*x0 + 2*x1 + 1' - print("mpoly test section: 24", flush=True) + assert p.exponent_tuple(0) == (2, 2) assert p.exponent_tuple(3) == (0, 0) assert raises(lambda: p.exponent_tuple(-1), IndexError) assert raises(lambda: p.exponent_tuple(4), IndexError) - print("mpoly test section: 25", flush=True) + assert p.total_degree() == 4 assert P(ctx=ctx).total_degree() == -1 assert P(1, ctx=ctx).total_degree() == 0 - print("mpoly test section: 26", flush=True) + p = quick_poly() assert p(0, 0) == p(0, S(0)) == p(S(0), S(0)) == p(x1=S(0), x0=S(0)) == S(1) == 1 assert p(1, 1) == p(x1=S(1), x0=S(1)) == S(10) == 10 @@ -2792,42 +2792,42 @@ def quick_poly(): (0, 0): 4 }) assert raises(lambda: p(x0=None), TypeError) - print("mpoly test section: 27", flush=True) + assert bool(P(ctx=ctx)) is False assert bool(P(1, ctx=ctx)) is True - print("mpoly test section: 28", flush=True) + assert +quick_poly() \ == quick_poly() assert -quick_poly() \ == mpoly({(0, 0): -1, (0, 1): -2, (1, 0): -3, (2, 2): -4}) - print("mpoly test section: 29", flush=True) + assert quick_poly() \ + mpoly({(0, 0): 5, (0, 1): 6, (1, 0): 7, (2, 2): 8}) \ == mpoly({(0, 0): 6, (0, 1): 8, (1, 0): 10, (2, 2): 12}) - print("mpoly test section: 30", flush=True) + for T in [int, S, flint.fmpz]: assert quick_poly() + T(1) \ == mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}) assert T(1) + quick_poly() \ == mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}) - print("mpoly test section: 31", flush=True) + assert raises(lambda: mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}) + None, TypeError) assert raises(lambda: None + mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}), TypeError) assert raises(lambda: quick_poly() + P(ctx=ctx1), IncompatibleContextError) assert raises(lambda: quick_poly().__iadd__(P(ctx=ctx1)), IncompatibleContextError) - print("mpoly test section: 32", flush=True) + assert quick_poly() - mpoly({(0, 0): 5, (0, 1): 6, (1, 0): 7, (2, 2): 8}) \ == mpoly({(0, 0): -4, (0, 1): -4, (1, 0): -4, (2, 2): -4}) - print("mpoly test section: 33", flush=True) + for T in [int, S, flint.fmpz]: assert quick_poly() - T(1) == mpoly({(0, 1): 2, (1, 0): 3, (2, 2): 4}) assert T(1) - quick_poly() == mpoly({(0, 1): -2, (1, 0): -3, (2, 2): -4}) - print("mpoly test section: 34", flush=True) + assert raises(lambda: quick_poly() - None, TypeError) assert raises(lambda: None - quick_poly(), TypeError) assert raises(lambda: quick_poly() - P(ctx=ctx1), IncompatibleContextError) assert raises(lambda: quick_poly().__isub__(P(ctx=ctx1)), IncompatibleContextError) - print("mpoly test section: 35", flush=True) + assert quick_poly() * mpoly({(1, 0): 5, (0, 1): 6}) \ == mpoly({ (3, 2): 20, @@ -2838,26 +2838,26 @@ def quick_poly(): (0, 2): 12, (0, 1): 6 }) - print("mpoly test section: 36", flush=True) + for T in [int, S, flint.fmpz]: assert quick_poly() * T(2) == mpoly({(0, 0): 2, (0, 1): 4, (1, 0): 6, (2, 2): 8}) assert T(2) * quick_poly() == mpoly({(0, 0): 2, (0, 1): 4, (1, 0): 6, (2, 2): 8}) - print("mpoly test section: 37", flush=True) + assert raises(lambda: quick_poly() * None, TypeError) assert raises(lambda: None * quick_poly(), TypeError) assert raises(lambda: quick_poly() * P(ctx=ctx1), IncompatibleContextError) assert raises(lambda: quick_poly().__imul__(P(ctx=ctx1)), IncompatibleContextError) - print("mpoly test section: 38", flush=True) + assert quick_poly() // mpoly({(1, 1): 1}) == mpoly({(1, 1): 4}) assert quick_poly() % mpoly({(1, 1): 1}) \ == mpoly({(1, 0): 3, (0, 1): 2, (0, 0): 1}) assert divmod(quick_poly(), mpoly({(1, 1): 1})) \ == (mpoly({(1, 1): 4}), mpoly({(1, 0): 3, (0, 1): 2, (0, 0): 1})) - print("mpoly test section: 39", flush=True) + assert quick_poly() // 1 == quick_poly() assert quick_poly() % 1 == P(ctx=ctx) assert divmod(quick_poly(), 1) == (quick_poly(), P(ctx=ctx)) - print("mpoly test section: 40", flush=True) + # TODO FIXME # if is_field: # assert P([1, 1]) // 2 == P([S(1)/2, S(1)/2]) @@ -2865,31 +2865,31 @@ def quick_poly(): # else: # assert P([1, 1]) // 2 == P([0, 0]) # assert P([1, 1]) % 2 == P([1, 1]) - print("mpoly test section: 41", flush=True) + assert 1 // quick_poly() == P(ctx=ctx) assert 1 % quick_poly() == P(1, ctx=ctx) assert divmod(1, quick_poly()) == (P(ctx=ctx), P(1, ctx=ctx)) - print("mpoly test section: 42", flush=True) + assert raises(lambda: quick_poly() // None, TypeError) assert raises(lambda: quick_poly() % None, TypeError) assert raises(lambda: divmod(quick_poly(), None), TypeError) - print("mpoly test section: 43", flush=True) + assert raises(lambda: None // quick_poly(), TypeError) assert raises(lambda: None % quick_poly(), TypeError) assert raises(lambda: divmod(None, quick_poly()), TypeError) - print("mpoly test section: 44", flush=True) + assert raises(lambda: quick_poly() // 0, ZeroDivisionError) assert raises(lambda: quick_poly() % 0, ZeroDivisionError) assert raises(lambda: divmod(quick_poly(), 0), ZeroDivisionError) - print("mpoly test section: 45", flush=True) + assert raises(lambda: quick_poly() // P(ctx=ctx), ZeroDivisionError) assert raises(lambda: quick_poly() % P(ctx=ctx), ZeroDivisionError) assert raises(lambda: divmod(quick_poly(), P(ctx=ctx)), ZeroDivisionError) - print("mpoly test section: 46", flush=True) + assert raises(lambda: quick_poly() // P(1, ctx=ctx1), IncompatibleContextError) assert raises(lambda: quick_poly() % P(1, ctx=ctx1), IncompatibleContextError) assert raises(lambda: divmod(quick_poly(), P(1, ctx=ctx1)), IncompatibleContextError) - print("mpoly test section: 47", flush=True) + # TODO FIXME # # Exact/field scalar division @@ -2900,7 +2900,7 @@ def quick_poly(): # assert P([2, 2]) / 2 == P([1, 1]) # assert raises(lambda: P([1, 2]) / 2, DomainError) # assert raises(lambda: P([1, 2]) / 0, ZeroDivisionError) - print("mpoly test section: 48", flush=True) + f = mpoly({(1, 1): 4, (0, 0): 1}) g = mpoly({(0, 1): 2, (1, 0): 2}) assert f \ @@ -2909,7 +2909,7 @@ def quick_poly(): == mpoly({(1, 1): 8, (0, 0): 2}) assert raises(lambda: 1 / quick_poly(), DomainError) assert raises(lambda: quick_poly() / P(2, ctx=ctx), DomainError) - print("mpoly test section: 49", flush=True) + assert quick_poly() ** 0 == P(1, ctx=ctx) assert quick_poly() ** 1 == quick_poly() assert quick_poly() ** 2 == mpoly({ @@ -2926,14 +2926,14 @@ def quick_poly(): }) assert raises(lambda: P(ctx=ctx) ** -1, ValueError) assert raises(lambda: P(ctx=ctx) ** None, TypeError) - print("mpoly test section: 50", flush=True) + # # XXX: Not sure what this should do in general: assert raises(lambda: pow(P(1, ctx=ctx), 2, 3), NotImplementedError) - print("mpoly test section: 51", flush=True) + assert (f * g).gcd(f) == f assert raises(lambda: quick_poly().gcd(None), TypeError) assert raises(lambda: quick_poly().gcd(P(ctx=ctx1)), IncompatibleContextError) - print("mpoly test section: 52", flush=True) + # TODO FIXME # if is_field: # p1 = P([1, 0, 1]) @@ -2941,30 +2941,30 @@ def quick_poly(): # g, s, t = P([1]), P([1])/5, P([2, -1])/5 # assert p1.xgcd(p2) == (g, s, t) # assert raises(lambda: p1.xgcd(None), TypeError) - print("mpoly test section: 53", flush=True) + assert (f * g).factor() == (S(2), [(mpoly({(0, 1): 1, (1, 0): 1}), 1), (f, 1)]) - print("mpoly test section: 54", flush=True) + assert (f * f).sqrt() == f assert (f * f).sqrt(assume_perfect_square=True) == f assert raises(lambda: quick_poly().sqrt(), ValueError) - print("mpoly test section: 55", flush=True) + # TODO FIXME # if P == flint.fmpq_mpoly: # assert raises(lambda: P([1, 2, 1], 3).sqrt(), ValueError) # assert P([1, 2, 1], 4).sqrt() == P([1, 1], 2) - print("mpoly test section: 56", flush=True) + # assert P(ctx=C).deflation() == (P(ctx=C), 1) # assert P([1, 2]).deflation() == (P([1, 2]), 1) # assert P([1, 0, 2]).deflation() == (P([1, 2]), 2) - print("mpoly test section: 57", flush=True) + assert quick_poly().derivative(0) == quick_poly().derivative("x0") == mpoly({(0, 0): 3, (1, 2): 8}) assert quick_poly().derivative(1) == quick_poly().derivative("x1") == mpoly({(0, 0): 2, (2, 1): 8}) - print("mpoly test section: 58", flush=True) + assert quick_poly().integral(0) == quick_poly().integral("x0") == \ (6, mpoly({(3, 2): 8, (2, 0): 9, (1, 1): 12, (1, 0): 6})) assert quick_poly().integral(1) == quick_poly().integral("x1") == \ (3, mpoly({(2, 3): 4, (1, 1): 9, (0, 2): 3, (0, 1): 3})) - print("mpoly test section: 59", flush=True) + # if is_field: # assert P([1, 2, 1]).integral() == P([0, 1, 1, S(1)/3]) From 8a09691c7aed72b31aeaf9851f819b6bac38cc4e Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Thu, 25 Apr 2024 19:11:04 +1000 Subject: [PATCH 41/95] Add fmpq_mpoly and fmpz_mpoly_q to meson.build --- src/flint/types/meson.build | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/flint/types/meson.build b/src/flint/types/meson.build index bf8caec5..ddd382f0 100644 --- a/src/flint/types/meson.build +++ b/src/flint/types/meson.build @@ -39,6 +39,8 @@ exts = [ 'dirichlet', 'fmpz_mpoly', + 'fmpq_mpoly', + 'fmpz_mpoly_q', ] py.install_sources( From 6c0e0ccde2c90e3a0860cee8f71f3c1849c5cedb Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Fri, 3 May 2024 22:17:05 +1000 Subject: [PATCH 42/95] Add fmpz_vec and use it --- src/flint/__init__.py | 1 + src/flint/types/fmpz_mod_poly.pyx | 24 ++++----- src/flint/types/fmpz_mpoly.pyx | 83 ++++++++++--------------------- src/flint/types/fmpz_vec.pxd | 7 +++ src/flint/types/fmpz_vec.pyx | 80 +++++++++++++++++++++++++++++ src/flint/types/meson.build | 1 + 6 files changed, 123 insertions(+), 73 deletions(-) create mode 100644 src/flint/types/fmpz_vec.pxd create mode 100644 src/flint/types/fmpz_vec.pyx diff --git a/src/flint/__init__.py b/src/flint/__init__.py index 74b542ca..e910ed3b 100644 --- a/src/flint/__init__.py +++ b/src/flint/__init__.py @@ -4,6 +4,7 @@ from .types.fmpz_poly import * from .types.fmpz_mat import * from .types.fmpz_series import * +from .types.fmpz_vec import fmpz_vec from .types.fmpq import * from .types.fmpq_poly import * diff --git a/src/flint/types/fmpz_mod_poly.pyx b/src/flint/types/fmpz_mod_poly.pyx index f5b5d60b..fa334c63 100644 --- a/src/flint/types/fmpz_mod_poly.pyx +++ b/src/flint/types/fmpz_mod_poly.pyx @@ -9,7 +9,7 @@ from flint.flintlib.fmpz cimport( fmpz_clear, fmpz_is_one ) -from flint.flintlib.fmpz_vec cimport _fmpz_vec_init, _fmpz_vec_clear +from flint.types.fmpz_vec cimport fmpz_vec from flint.types.fmpz cimport fmpz, any_as_fmpz from flint.types.fmpz_mod cimport fmpz_mod_ctx, fmpz_mod @@ -291,17 +291,15 @@ cdef class fmpz_mod_poly_ctx: raise ValueError("Input must be a list or tuple of points") n = len(vals) - xs = _fmpz_vec_init(n) + xs = fmpz_vec(n) for i in range(n): - check = self.mod.set_any_as_fmpz_mod(&xs[i], vals[i]) + check = self.mod.set_any_as_fmpz_mod(&xs.val[i], vals[i]) if check is NotImplemented: - _fmpz_vec_clear(xs, n) raise ValueError(f"Unable to cast {vals[i]} to an `fmpz_mod`") res = self.new_ctype_poly() - fmpz_mod_poly_minpoly(res.val, xs, n, self.mod.val) + fmpz_mod_poly_minpoly(res.val, xs.val, n, self.mod.val) - _fmpz_vec_clear(xs, n) return res cdef class fmpz_mod_poly(flint_poly): @@ -759,29 +757,25 @@ cdef class fmpz_mod_poly(flint_poly): raise ValueError("Input must be a list of points") n = len(vals) - xs = _fmpz_vec_init(n) + xs = fmpz_vec(n) for i in range(n): - check = self.ctx.mod.set_any_as_fmpz_mod(&xs[i], vals[i]) + check = self.ctx.mod.set_any_as_fmpz_mod(&xs.val[i], vals[i]) if check is NotImplemented: - _fmpz_vec_clear(xs, n) 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 # using a subproduct tree - ys = _fmpz_vec_init(n) - fmpz_mod_poly_evaluate_fmpz_vec(ys, self.val, xs, n, self.ctx.mod.val) + ys = fmpz_vec(n) + fmpz_mod_poly_evaluate_fmpz_vec(ys.val, self.val, xs.val, n, self.ctx.mod.val) evaluations = [] for i in range(n): f = fmpz_mod.__new__(fmpz_mod) f.ctx = self.ctx.mod - fmpz_mod_set_fmpz(f.val, &ys[i], self.ctx.mod.val) + fmpz_mod_set_fmpz(f.val, &ys.val[i], self.ctx.mod.val) evaluations.append(f) - _fmpz_vec_clear(xs, n) - _fmpz_vec_clear(ys, n) - return evaluations def compose(self, input): diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 3309743b..d8d0e58d 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -11,6 +11,7 @@ from flint.flint_base.flint_base cimport flint_mpoly_context from flint.types.fmpz cimport any_as_fmpz from flint.types.fmpz cimport fmpz, fmpz_set_any_ref from flint.types.fmpq_mpoly cimport fmpq_mpoly +from flint.types.fmpz_vec cimport fmpz_vec from flint.utils.flint_exceptions import DomainError, IncompatibleContextError @@ -305,20 +306,13 @@ cdef class fmpz_mpoly(flint_mpoly): elif isinstance(x, tuple): if len(x) != nvars: raise ValueError("exponent vector provided does not match number of variables") - res = fmpz() try: - tmp = libc.stdlib.malloc(nvars * sizeof(fmpz_struct *)) - if tmp is NULL: - raise MemoryError("malloc returned a null pointer") - exp_vec = tuple(any_as_fmpz(exp) for exp in x) - for i in range(nvars): - if exp_vec[i] is NotImplemented: - raise TypeError("exponent not coercible to fmpz") - tmp[i] = &((exp_vec[i]).val[0]) - fmpz_mpoly_get_coeff_fmpz_fmpz((res).val, self.val, tmp, self.ctx.val) - return res + res = fmpz() + exp_vec = fmpz_vec.from_iterable(x, double_indirect=True) + fmpz_mpoly_get_coeff_fmpz_fmpz((res).val, self.val, exp_vec.double_indirect, self.ctx.val) finally: libc.stdlib.free(tmp) + return res else: raise TypeError("index is not integer or tuple") @@ -340,15 +334,9 @@ cdef class fmpz_mpoly(flint_mpoly): raise ValueError("exponent vector provided does not match number of variables") try: - tmp = libc.stdlib.malloc(nvars * sizeof(fmpz_struct *)) - if tmp is NULL: - raise MemoryError("malloc returned a null pointer") - exp_vec = tuple(any_as_fmpz(exp) for exp in x) - for i in range(nvars): - if exp_vec[i] is NotImplemented: - raise TypeError("exponent not coercible to fmpz") - tmp[i] = &((exp_vec[i]).val[0]) - fmpz_mpoly_set_coeff_fmpz_fmpz(self.val, (coeff).val, tmp, self.ctx.val) + res = fmpz() + exp_vec = fmpz_vec.from_iterable(x, double_indirect=True) + fmpz_mpoly_set_coeff_fmpz_fmpz(self.val, (coeff).val, exp_vec.double_indirect, self.ctx.val) finally: libc.stdlib.free(tmp) else: @@ -364,38 +352,24 @@ cdef class fmpz_mpoly(flint_mpoly): return v def exponent_tuple(self, slong i): - cdef slong j, nvars - cdef fmpz_struct ** tmp + cdef: + fmpz_struct ** tmp + slong j, nvars = self.ctx.nvars() + if i < 0 or i >= fmpz_mpoly_length(self.val, self.ctx.val): raise IndexError("term index out of range") - nvars = self.ctx.nvars() - res = tuple(fmpz() for j in range(nvars)) - tmp = libc.stdlib.malloc(nvars * sizeof(fmpz_struct *)) - if tmp is NULL: - raise MemoryError("malloc returned a null pointer") - try: - for j in range(nvars): - tmp[j] = &(( (res[j])).val[0]) - fmpz_mpoly_get_term_exp_fmpz(tmp, self.val, i, self.ctx.val) - finally: - libc.stdlib.free(tmp) - return res + 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() def degrees(self): cdef: fmpz_struct ** tmp slong i, nvars = self.ctx.nvars() - res = tuple(fmpz() for _ in range(nvars)) - tmp = libc.stdlib.malloc(nvars * sizeof(fmpz_struct *)) - if tmp is NULL: - raise MemoryError("malloc returned a null pointer") - - for i in range(nvars): - tmp[i] = &(( res[i]).val[0]) - fmpz_mpoly_degrees_fmpz(tmp, self.val, self.ctx.val) - libc.stdlib.free(tmp) - return dict(zip(self.ctx.names(), res)) + res = fmpz_vec(nvars, double_indirect=True) + fmpz_mpoly_degrees_fmpz(res.double_indirect, self.val, self.ctx.val) + return dict(zip(self.ctx.names(), res.to_tuple())) def total_degree(self): cdef fmpz res = fmpz() @@ -716,7 +690,7 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly res fmpz_mpoly res2 fmpz_mpoly_ctx res_ctx - fmpz_struct ** V + fmpz_vec V fmpz vres fmpz_mpoly_struct ** C ulong *exponents @@ -755,18 +729,11 @@ cdef class fmpz_mpoly(flint_mpoly): if args and all_fmpz: # Normal evaluation - try: - V = libc.stdlib.malloc(nvars * sizeof(fmpz_struct *)) - if V is NULL: - raise MemoryError("malloc returned a null pointer") - for i in range(nvars): - V[i] = &(( args_fmpz[i]).val[0]) - vres = fmpz.__new__(fmpz) - if fmpz_mpoly_evaluate_all_fmpz(vres.val, self.val, V, self.ctx.val) == 0: - raise ValueError("unreasonably large polynomial") - return vres - finally: - libc.stdlib.free(V) + V = fmpz_vec.from_iterable(args_fmpz, double_indirect=True) + vres = fmpz.__new__(fmpz) + if fmpz_mpoly_evaluate_all_fmpz(vres.val, self.val, V.double_indirect, self.ctx.val) == 0: + raise ValueError("unreasonably large polynomial") + return vres elif kwargs and all_fmpz: # Partial application with args in Z. We evaluate the polynomial one variable at a time res = create_fmpz_mpoly(self.ctx) @@ -792,7 +759,7 @@ cdef class fmpz_mpoly(flint_mpoly): try: for i in range(nvars): C[i] = &(( args[i]).val[0]) - res = create_fmpz_mpoly(self.ctx) + res = create_fmpz_mpoly(self.ctx) if fmpz_mpoly_compose_fmpz_mpoly(res.val, self.val, C, self.ctx.val, res_ctx.val) == 0: raise ValueError("unreasonably large polynomial") return res diff --git a/src/flint/types/fmpz_vec.pxd b/src/flint/types/fmpz_vec.pxd new file mode 100644 index 00000000..6e4b1cf7 --- /dev/null +++ b/src/flint/types/fmpz_vec.pxd @@ -0,0 +1,7 @@ +from flint.flintlib.fmpz cimport fmpz_struct +from flint.flintlib.flint cimport slong + +cdef class fmpz_vec: + cdef fmpz_struct *val + cdef fmpz_struct **double_indirect + cdef slong length diff --git a/src/flint/types/fmpz_vec.pyx b/src/flint/types/fmpz_vec.pyx new file mode 100644 index 00000000..56962591 --- /dev/null +++ b/src/flint/types/fmpz_vec.pyx @@ -0,0 +1,80 @@ +from flint.flintlib.fmpz cimport fmpz_struct, fmpz_set, fmpz_init_set +from flint.flintlib.flint cimport slong +from flint.flintlib.fmpz_vec cimport _fmpz_vec_init, _fmpz_vec_clear + +from flint.types.fmpz cimport fmpz, any_as_fmpz + +cimport libc.stdlib + +cdef class fmpz_vec: + def __cinit__(self, slong length, bint double_indirect=False): + self.val = _fmpz_vec_init(length) + self.length = length + if double_indirect: + self.double_indirect = libc.stdlib.malloc(length * sizeof(fmpz_struct *)) + if self.double_indirect is NULL: + raise MemoryError("malloc returned a null pointer") + + for i in range(length): + self.double_indirect[i] = &self.val[i] + else: + self.double_indirect = NULL + + def __dealloc__(self): + libc.stdlib.free(self.double_indirect) + _fmpz_vec_clear(self.val, self.length) + + def __getitem__(self, x): + if not isinstance(x, int): + raise TypeError("index is not integer") + elif not 0 <= x < self.length: + raise IndexError("index out of range") + + cdef fmpz z = fmpz.__new__(fmpz) + fmpz_init_set(z.val, &self.val[x]) + return z + + def __setitem__(self, x, y): + if not isinstance(x, int): + raise TypeError("index is not integer") + elif not 0 <= x < self.length: + raise IndexError("index out of range") + + y = any_as_fmpz(y) + if y is NotImplemented: + raise TypeError("argument is not coercible to fmpz") + + fmpz_set(&self.val[x], (y).val) + + def __len__(self): + return self.length + + def str(self, *args): + s = [None] * self.length + for i in range(self.length): + x = fmpz.__new__(fmpz) + fmpz_init_set(x.val, &self.val[i]) + s[i] = x.str(*args) + return str(s) + + def repr(self, *args): + return f"fmpz_vec({self.str(*args)}, {self.length})" + + @staticmethod + def from_iterable(iterable, double_indirect: bool = False): + length = len(iterable) + + vec = fmpz_vec(length, double_indirect) + for i, x in enumerate(iterable): + x = any_as_fmpz(x) + if x is NotImplemented: + raise TypeError("argument is not coercible to fmpz") + + fmpz_set(&vec.val[i], (x).val) + return vec + + def to_tuple(self): + t = tuple(fmpz.__new__(fmpz) for _ in range(self.length)) + for i in range(self.length): + fmpz_init_set((t[i]).val, &self.val[i]) + return t diff --git a/src/flint/types/meson.build b/src/flint/types/meson.build index ddd382f0..03eb69a9 100644 --- a/src/flint/types/meson.build +++ b/src/flint/types/meson.build @@ -9,6 +9,7 @@ exts = [ 'fmpz_poly', 'fmpz_mat', 'fmpz_series', + 'fmpz_vec', 'fmpq', 'fmpq_poly', From 0878b38b834dc5e61e27216d8fc1e0c793ee8f49 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Fri, 3 May 2024 22:17:24 +1000 Subject: [PATCH 43/95] Mark char* obtained from python objects as const --- src/flint/flint_base/flint_base.pxd | 2 +- src/flint/flint_base/flint_base.pyx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/flint/flint_base/flint_base.pxd b/src/flint/flint_base/flint_base.pxd index 6cbb465b..c6cb5a63 100644 --- a/src/flint/flint_base/flint_base.pxd +++ b/src/flint/flint_base/flint_base.pxd @@ -9,7 +9,7 @@ cdef class flint_poly(flint_elem): cdef class flint_mpoly_context(flint_elem): cdef public object py_names - cdef char ** c_names + cdef const char ** c_names cdef bint _init cdef class flint_mpoly(flint_elem): diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index ccd27541..d4b5d35a 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -139,8 +139,8 @@ cdef class flint_mpoly_context(flint_elem): raise ValueError("cannot have a negative amount of variables") elif len(names) != nvars: raise ValueError("number of variables must match lens of variable names") - self.py_names = tuple(bytes(name, 'utf-8') if not isinstance(name, bytes) else name for name in names) - self.c_names = libc.stdlib.malloc(nvars * sizeof(char *)) + self.py_names = tuple(name.encode("ascii") if not isinstance(name, bytes) else name for name in names) + self.c_names = libc.stdlib.malloc(nvars * sizeof(const char *)) self._init = True for i in range(nvars): self.c_names[i] = self.py_names[i] From 98a0cb7981a369d6fdf27aca33d0f0a084ac0bd0 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Sat, 4 May 2024 22:28:56 +1000 Subject: [PATCH 44/95] Update flintlib/fmpz_mpoly.pxd --- src/flint/flintlib/fmpz_mpoly.pxd | 96 +++++++++++++++++++++++-------- 1 file changed, 72 insertions(+), 24 deletions(-) diff --git a/src/flint/flintlib/fmpz_mpoly.pxd b/src/flint/flintlib/fmpz_mpoly.pxd index 1d9ce207..6cc7d167 100644 --- a/src/flint/flintlib/fmpz_mpoly.pxd +++ b/src/flint/flintlib/fmpz_mpoly.pxd @@ -18,9 +18,24 @@ cdef extern from "flint/fmpz_mpoly.h": ctypedef fmpz_mpoly_struct fmpz_mpoly_t[1] + ctypedef struct fmpz_mpoly_univar_struct: + fmpz_mpoly_struct * coeffs + fmpz_struct * exps + slong alloc + slong length + + ctypedef fmpz_mpoly_univar_struct fmpz_mpoly_univar_t[1] + + ctypedef struct fmpz_mpoly_vec_struct: + fmpz_mpoly_struct * p + slong alloc + slong length + + ctypedef fmpz_mpoly_vec_struct fmpz_mpoly_vec_t[1] + # from here on is parsed void fmpz_mpoly_ctx_init(fmpz_mpoly_ctx_t ctx, slong nvars, const ordering_t ord) - slong fmpz_mpoly_ctx_nvars(fmpz_mpoly_ctx_t ctx) + slong fmpz_mpoly_ctx_nvars(const fmpz_mpoly_ctx_t ctx) ordering_t fmpz_mpoly_ctx_ord(const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_ctx_clear(fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_init(fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) @@ -31,12 +46,13 @@ cdef extern from "flint/fmpz_mpoly.h": void fmpz_mpoly_realloc(fmpz_mpoly_t A, slong alloc, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_clear(fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) char * fmpz_mpoly_get_str_pretty(const fmpz_mpoly_t A, const char ** x, const fmpz_mpoly_ctx_t ctx) + # int fmpz_mpoly_fprint_pretty(FILE * file, const fmpz_mpoly_t A, const char ** x, const fmpz_mpoly_ctx_t ctx) int fmpz_mpoly_print_pretty(const fmpz_mpoly_t A, const char ** x, const fmpz_mpoly_ctx_t ctx) int fmpz_mpoly_set_str_pretty(fmpz_mpoly_t A, const char * str, const char ** x, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_gen(fmpz_mpoly_t A, slong var, const fmpz_mpoly_ctx_t ctx) int fmpz_mpoly_is_gen(const fmpz_mpoly_t A, slong var, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_set(fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_mpoly_ctx_t ctx) - int fmpz_mpoly_equal(fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_mpoly_ctx_t ctx) + int fmpz_mpoly_equal(const fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_swap(fmpz_mpoly_t poly1, fmpz_mpoly_t poly2, const fmpz_mpoly_ctx_t ctx) int _fmpz_mpoly_fits_small(const fmpz_struct * poly, slong len) slong fmpz_mpoly_max_bits(const fmpz_mpoly_t A) @@ -47,7 +63,7 @@ cdef extern from "flint/fmpz_mpoly.h": void fmpz_mpoly_set_si(fmpz_mpoly_t A, slong c, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_zero(fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_one(fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) - int fmpz_mpoly_equal_fmpz(const fmpz_mpoly_t A, fmpz_t c, const fmpz_mpoly_ctx_t ctx) + int fmpz_mpoly_equal_fmpz(const fmpz_mpoly_t A, const fmpz_t c, const fmpz_mpoly_ctx_t ctx) int fmpz_mpoly_equal_ui(const fmpz_mpoly_t A, ulong c, const fmpz_mpoly_ctx_t ctx) int fmpz_mpoly_equal_si(const fmpz_mpoly_t A, slong c, const fmpz_mpoly_ctx_t ctx) int fmpz_mpoly_is_zero(const fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) @@ -69,14 +85,18 @@ cdef extern from "flint/fmpz_mpoly.h": void fmpz_mpoly_get_coeff_fmpz_ui(fmpz_t c, const fmpz_mpoly_t A, const ulong * exp, const fmpz_mpoly_ctx_t ctx) ulong fmpz_mpoly_get_coeff_ui_ui(const fmpz_mpoly_t A, const ulong * exp, const fmpz_mpoly_ctx_t ctx) slong fmpz_mpoly_get_coeff_si_ui(const fmpz_mpoly_t A, const ulong * exp, const fmpz_mpoly_ctx_t ctx) - void fmpz_mpoly_set_coeff_fmpz_fmpz(fmpz_mpoly_t A, const fmpz_t c, const fmpz_struct ** exp, fmpz_mpoly_ctx_t ctx) - void fmpz_mpoly_set_coeff_ui_fmpz(fmpz_mpoly_t A, ulong c, const fmpz_struct ** exp, const fmpz_mpoly_ctx_t ctx) - void fmpz_mpoly_set_coeff_si_fmpz(fmpz_mpoly_t A, slong c, const fmpz_struct ** exp, const fmpz_mpoly_ctx_t ctx) - void fmpz_mpoly_set_coeff_fmpz_ui(fmpz_mpoly_t A, const fmpz_t c, const ulong * exp, fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_set_coeff_fmpz_fmpz(fmpz_mpoly_t A, const fmpz_t c, fmpz_struct * const * exp, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_set_coeff_ui_fmpz(fmpz_mpoly_t A, ulong c, fmpz_struct * const * exp, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_set_coeff_si_fmpz(fmpz_mpoly_t A, slong c, fmpz_struct * const * exp, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_set_coeff_fmpz_ui(fmpz_mpoly_t A, const fmpz_t c, const ulong * exp, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_set_coeff_ui_ui(fmpz_mpoly_t A, ulong c, const ulong * exp, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_set_coeff_si_ui(fmpz_mpoly_t A, slong c, const ulong * exp, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_get_coeff_vars_ui(fmpz_mpoly_t C, const fmpz_mpoly_t A, const slong * vars, const ulong * exps, slong length, const fmpz_mpoly_ctx_t ctx) int fmpz_mpoly_cmp(const fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_mpoly_ctx_t ctx) + int fmpz_mpoly_is_fmpz_poly(const fmpz_mpoly_t A, slong var, const fmpz_mpoly_ctx_t ctx) + int fmpz_mpoly_get_fmpz_poly(fmpz_poly_t A, const fmpz_mpoly_t B, slong var, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_set_fmpz_poly(fmpz_mpoly_t A, const fmpz_poly_t B, slong var, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_set_gen_fmpz_poly(fmpz_mpoly_t A, slong var, const fmpz_poly_t B, const fmpz_mpoly_ctx_t ctx) fmpz_struct * fmpz_mpoly_term_coeff_ref(fmpz_mpoly_t A, slong i, const fmpz_mpoly_ctx_t ctx) int fmpz_mpoly_is_canonical(const fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) slong fmpz_mpoly_length(const fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) @@ -99,8 +119,11 @@ cdef extern from "flint/fmpz_mpoly.h": void fmpz_mpoly_get_term(fmpz_mpoly_t M, const fmpz_mpoly_t A, slong i, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_get_term_monomial(fmpz_mpoly_t M, const fmpz_mpoly_t A, slong i, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_push_term_fmpz_fmpz(fmpz_mpoly_t A, const fmpz_t c, fmpz_struct * const * exp, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_push_term_fmpz_ffmpz(fmpz_mpoly_t A, const fmpz_t c, const fmpz_struct * exp, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_push_term_ui_fmpz(fmpz_mpoly_t A, ulong c, fmpz_struct * const * exp, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_push_term_ui_ffmpz(fmpz_mpoly_t A, ulong c, const fmpz_struct * exp, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_push_term_si_fmpz(fmpz_mpoly_t A, slong c, fmpz_struct * const * exp, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_push_term_si_ffmpz(fmpz_mpoly_t A, slong c, const fmpz_struct * exp, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_push_term_fmpz_ui(fmpz_mpoly_t A, const fmpz_t c, const ulong * exp, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_push_term_ui_ui(fmpz_mpoly_t A, ulong c, const ulong * exp, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_push_term_si_ui(fmpz_mpoly_t A, slong c, const ulong * exp, const fmpz_mpoly_ctx_t ctx) @@ -110,10 +133,10 @@ cdef extern from "flint/fmpz_mpoly.h": void fmpz_mpoly_randtest_bound(fmpz_mpoly_t A, flint_rand_t state, slong length, mp_limb_t coeff_bits, ulong exp_bound, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_randtest_bounds(fmpz_mpoly_t A, flint_rand_t state, slong length, mp_limb_t coeff_bits, ulong * exp_bounds, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_randtest_bits(fmpz_mpoly_t A, flint_rand_t state, slong length, mp_limb_t coeff_bits, mp_limb_t exp_bits, const fmpz_mpoly_ctx_t ctx) - void fmpz_mpoly_add_fmpz(fmpz_mpoly_t A, const fmpz_mpoly_t B, fmpz_t c, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_add_fmpz(fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_t c, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_add_ui(fmpz_mpoly_t A, const fmpz_mpoly_t B, ulong c, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_add_si(fmpz_mpoly_t A, const fmpz_mpoly_t B, slong c, const fmpz_mpoly_ctx_t ctx) - void fmpz_mpoly_sub_fmpz(fmpz_mpoly_t A, const fmpz_mpoly_t B, fmpz_t c, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_sub_fmpz(fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_t c, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_sub_ui(fmpz_mpoly_t A, const fmpz_mpoly_t B, ulong c, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_sub_si(fmpz_mpoly_t A, const fmpz_mpoly_t B, slong c, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_add(fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_mpoly_t C, const fmpz_mpoly_ctx_t ctx) @@ -165,38 +188,63 @@ cdef extern from "flint/fmpz_mpoly.h": int fmpz_mpoly_gcd_zippel2(fmpz_mpoly_t G, const fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_mpoly_ctx_t ctx) int fmpz_mpoly_resultant(fmpz_mpoly_t R, const fmpz_mpoly_t A, const fmpz_mpoly_t B, slong var, const fmpz_mpoly_ctx_t ctx) int fmpz_mpoly_discriminant(fmpz_mpoly_t D, const fmpz_mpoly_t A, slong var, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_primitive_part(fmpz_mpoly_t res, const fmpz_mpoly_t f, const fmpz_mpoly_ctx_t ctx) int fmpz_mpoly_sqrt_heap(fmpz_mpoly_t Q, const fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx, int check) int fmpz_mpoly_sqrt(fmpz_mpoly_t q, const fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) int fmpz_mpoly_is_square(const fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) - # void fmpz_mpoly_univar_init(fmpz_mpoly_univar_t A, const fmpz_mpoly_ctx_t ctx) - # void fmpz_mpoly_univar_clear(fmpz_mpoly_univar_t A, const fmpz_mpoly_ctx_t ctx) - # void fmpz_mpoly_univar_swap(fmpz_mpoly_univar_t A, fmpz_mpoly_univar_t B, const fmpz_mpoly_ctx_t ctx) - # void fmpz_mpoly_to_univar(fmpz_mpoly_univar_t A, const fmpz_mpoly_t B, slong var, const fmpz_mpoly_ctx_t ctx) - # void fmpz_mpoly_from_univar(fmpz_mpoly_t A, const fmpz_mpoly_univar_t B, slong var, const fmpz_mpoly_ctx_t ctx) - # int fmpz_mpoly_univar_degree_fits_si(const fmpz_mpoly_univar_t A, const fmpz_mpoly_ctx_t ctx) - # slong fmpz_mpoly_univar_length(const fmpz_mpoly_univar_t A, const fmpz_mpoly_ctx_t ctx) - # slong fmpz_mpoly_univar_get_term_exp_si(fmpz_mpoly_univar_t A, slong i, const fmpz_mpoly_ctx_t ctx) - # void fmpz_mpoly_univar_get_term_coeff(fmpz_mpoly_t c, const fmpz_mpoly_univar_t A, slong i, const fmpz_mpoly_ctx_t ctx) - # void fmpz_mpoly_univar_swap_term_coeff(fmpz_mpoly_t c, fmpz_mpoly_univar_t A, slong i, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_univar_init(fmpz_mpoly_univar_t A, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_univar_clear(fmpz_mpoly_univar_t A, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_univar_swap(fmpz_mpoly_univar_t A, fmpz_mpoly_univar_t B, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_to_univar(fmpz_mpoly_univar_t A, const fmpz_mpoly_t B, slong var, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_from_univar(fmpz_mpoly_t A, const fmpz_mpoly_univar_t B, slong var, const fmpz_mpoly_ctx_t ctx) + int fmpz_mpoly_univar_degree_fits_si(const fmpz_mpoly_univar_t A, const fmpz_mpoly_ctx_t ctx) + slong fmpz_mpoly_univar_length(const fmpz_mpoly_univar_t A, const fmpz_mpoly_ctx_t ctx) + slong fmpz_mpoly_univar_get_term_exp_si(fmpz_mpoly_univar_t A, slong i, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_univar_get_term_coeff(fmpz_mpoly_t c, const fmpz_mpoly_univar_t A, slong i, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_univar_swap_term_coeff(fmpz_mpoly_t c, fmpz_mpoly_univar_t A, slong i, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_inflate(fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_struct * shift, const fmpz_struct * stride, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_deflate(fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_struct * shift, const fmpz_struct * stride, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_deflation(fmpz_struct * shift, fmpz_struct * stride, const fmpz_mpoly_t A, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_pow_fps(fmpz_mpoly_t A, const fmpz_mpoly_t B, ulong k, const fmpz_mpoly_ctx_t ctx) slong _fmpz_mpoly_divides_array(fmpz_struct ** poly1, ulong ** exp1, slong * alloc, const fmpz_struct * poly2, const ulong * exp2, slong len2, const fmpz_struct * poly3, const ulong * exp3, slong len3, slong * mults, slong num, slong bits) int fmpz_mpoly_divides_array(fmpz_mpoly_t poly1, const fmpz_mpoly_t poly2, const fmpz_mpoly_t poly3, const fmpz_mpoly_ctx_t ctx) - slong _fmpz_mpoly_divides_monagan_pearce(fmpz_struct ** poly1, ulong ** exp1, slong * alloc, const fmpz_struct * poly2, const ulong * exp2, slong len2, const fmpz_struct * poly3, const ulong * exp3, slong len3, slong bits, slong N) + slong _fmpz_mpoly_divides_monagan_pearce(fmpz_struct ** poly1, ulong ** exp1, slong * alloc, const fmpz_struct * poly2, const ulong * exp2, slong len2, const fmpz_struct * poly3, const ulong * exp3, slong len3, ulong bits, slong N, const mp_limb_t * cmpmask) int fmpz_mpoly_divides_monagan_pearce(fmpz_mpoly_t poly1, const fmpz_mpoly_t poly2, const fmpz_mpoly_t poly3, const fmpz_mpoly_ctx_t ctx) - int fmpz_mpoly_divides_heap_threaded(fmpz_mpoly_t Q, const fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_mpoly_ctx_t ctx, slong thread_limit) - slong _fmpz_mpoly_div_monagan_pearce(fmpz_struct ** polyq, ulong ** expq, slong * allocq, const fmpz_struct * poly2, const ulong * exp2, slong len2, const fmpz_struct * poly3, const ulong * exp3, slong len3, slong bits, slong N) + int fmpz_mpoly_divides_heap_threaded(fmpz_mpoly_t Q, const fmpz_mpoly_t A, const fmpz_mpoly_t B, const fmpz_mpoly_ctx_t ctx) + slong _fmpz_mpoly_div_monagan_pearce(fmpz_struct ** polyq, ulong ** expq, slong * allocq, const fmpz_struct * poly2, const ulong * exp2, slong len2, const fmpz_struct * poly3, const ulong * exp3, slong len3, slong bits, slong N, const mp_limb_t * cmpmask) void fmpz_mpoly_div_monagan_pearce(fmpz_mpoly_t polyq, const fmpz_mpoly_t poly2, const fmpz_mpoly_t poly3, const fmpz_mpoly_ctx_t ctx) - slong _fmpz_mpoly_divrem_monagan_pearce(slong * lenr, fmpz_struct ** polyq, ulong ** expq, slong * allocq, fmpz_struct ** polyr, ulong ** expr, slong * allocr, const fmpz_struct * poly2, const ulong * exp2, slong len2, const fmpz_struct * poly3, const ulong * exp3, slong len3, slong bits, slong N) + slong _fmpz_mpoly_divrem_monagan_pearce(slong * lenr, fmpz_struct ** polyq, ulong ** expq, slong * allocq, fmpz_struct ** polyr, ulong ** expr, slong * allocr, const fmpz_struct * poly2, const ulong * exp2, slong len2, const fmpz_struct * poly3, const ulong * exp3, slong len3, slong bits, slong N, const mp_limb_t * cmpmask) void fmpz_mpoly_divrem_monagan_pearce(fmpz_mpoly_t q, fmpz_mpoly_t r, const fmpz_mpoly_t poly2, const fmpz_mpoly_t poly3, const fmpz_mpoly_ctx_t ctx) slong _fmpz_mpoly_divrem_array(slong * lenr, fmpz_struct ** polyq, ulong ** expq, slong * allocq, fmpz_struct ** polyr, ulong ** expr, slong * allocr, const fmpz_struct * poly2, const ulong * exp2, slong len2, const fmpz_struct * poly3, const ulong * exp3, slong len3, slong * mults, slong num, slong bits) int fmpz_mpoly_divrem_array(fmpz_mpoly_t q, fmpz_mpoly_t r, const fmpz_mpoly_t poly2, const fmpz_mpoly_t poly3, const fmpz_mpoly_ctx_t ctx) void fmpz_mpoly_quasidivrem_heap(fmpz_t scale, fmpz_mpoly_t q, fmpz_mpoly_t r, const fmpz_mpoly_t poly2, const fmpz_mpoly_t poly3, const fmpz_mpoly_ctx_t ctx) - slong _fmpz_mpoly_divrem_ideal_monagan_pearce(fmpz_mpoly_struct ** polyq, fmpz_struct ** polyr, ulong ** expr, slong * allocr, const fmpz_struct * poly2, const ulong * exp2, slong len2, fmpz_mpoly_struct * const * poly3, ulong * const * exp3, slong len, slong N, slong bits, const fmpz_mpoly_ctx_t ctx) + slong _fmpz_mpoly_divrem_ideal_monagan_pearce(fmpz_mpoly_struct ** polyq, fmpz_struct ** polyr, ulong ** expr, slong * allocr, const fmpz_struct * poly2, const ulong * exp2, slong len2, fmpz_mpoly_struct * const * poly3, ulong * const * exp3, slong len, slong N, slong bits, const fmpz_mpoly_ctx_t ctx, const mp_limb_t * cmpmask) void fmpz_mpoly_divrem_ideal_monagan_pearce(fmpz_mpoly_struct ** q, fmpz_mpoly_t r, const fmpz_mpoly_t poly2, fmpz_mpoly_struct * const * poly3, slong len, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_vec_init(fmpz_mpoly_vec_t vec, slong len, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_vec_clear(fmpz_mpoly_vec_t vec, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_vec_print(const fmpz_mpoly_vec_t vec, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_vec_swap(fmpz_mpoly_vec_t x, fmpz_mpoly_vec_t y, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_vec_fit_length(fmpz_mpoly_vec_t vec, slong len, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_vec_set(fmpz_mpoly_vec_t dest, const fmpz_mpoly_vec_t src, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_vec_append(fmpz_mpoly_vec_t vec, const fmpz_mpoly_t f, const fmpz_mpoly_ctx_t ctx) + slong fmpz_mpoly_vec_insert_unique(fmpz_mpoly_vec_t vec, const fmpz_mpoly_t f, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_vec_set_length(fmpz_mpoly_vec_t vec, slong len, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_vec_randtest_not_zero(fmpz_mpoly_vec_t vec, flint_rand_t state, slong len, slong poly_len, slong bits, ulong exp_bound, fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_vec_set_primitive_unique(fmpz_mpoly_vec_t res, const fmpz_mpoly_vec_t src, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_spoly(fmpz_mpoly_t res, const fmpz_mpoly_t f, const fmpz_mpoly_t g, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_reduction_primitive_part(fmpz_mpoly_t res, const fmpz_mpoly_t f, const fmpz_mpoly_vec_t vec, const fmpz_mpoly_ctx_t ctx) + int fmpz_mpoly_vec_is_groebner(const fmpz_mpoly_vec_t G, const fmpz_mpoly_vec_t F, const fmpz_mpoly_ctx_t ctx) + int fmpz_mpoly_vec_is_autoreduced(const fmpz_mpoly_vec_t F, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_vec_autoreduction(fmpz_mpoly_vec_t H, const fmpz_mpoly_vec_t F, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_vec_autoreduction_groebner(fmpz_mpoly_vec_t H, const fmpz_mpoly_vec_t G, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_buchberger_naive(fmpz_mpoly_vec_t G, const fmpz_mpoly_vec_t F, const fmpz_mpoly_ctx_t ctx) + int fmpz_mpoly_buchberger_naive_with_limits(fmpz_mpoly_vec_t G, const fmpz_mpoly_vec_t F, slong ideal_len_limit, slong poly_len_limit, slong poly_bits_limit, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_symmetric_gens(fmpz_mpoly_t res, ulong k, slong * vars, slong n, const fmpz_mpoly_ctx_t ctx) + void fmpz_mpoly_symmetric(fmpz_mpoly_t res, ulong k, const fmpz_mpoly_ctx_t ctx) # undocumented functions void _fmpz_mpoly_push_exp_ffmpz(fmpz_mpoly_t A, const fmpz_struct * exp, const fmpz_mpoly_ctx_t ctx); void _fmpz_mpoly_push_exp_pfmpz(fmpz_mpoly_t A, fmpz_struct * const * exp, const fmpz_mpoly_ctx_t ctx); void _fmpz_mpoly_push_exp_ui(fmpz_mpoly_t A, const ulong * exp, const fmpz_mpoly_ctx_t ctx); + + # Macros + fmpz_mpoly_struct *fmpz_mpoly_vec_entry(fmpz_mpoly_vec_t vec, slong i) From 4b082992f2c2accdc0f2d34b9d7d3f70fc995f98 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Sat, 4 May 2024 22:29:42 +1000 Subject: [PATCH 45/95] Add fmpz_mpoly_vec and use it were appropriate --- src/flint/__init__.py | 2 +- src/flint/types/fmpz_mpoly.pxd | 11 ++- src/flint/types/fmpz_mpoly.pyx | 131 ++++++++++++++++++++++++--------- src/flint/types/fmpz_vec.pyx | 44 +++++------ 4 files changed, 125 insertions(+), 63 deletions(-) diff --git a/src/flint/__init__.py b/src/flint/__init__.py index e910ed3b..adb25a03 100644 --- a/src/flint/__init__.py +++ b/src/flint/__init__.py @@ -16,7 +16,7 @@ from .types.nmod_mat import * from .types.nmod_series import * -from .types.fmpz_mpoly import fmpz_mpoly_ctx, fmpz_mpoly +from .types.fmpz_mpoly import fmpz_mpoly_ctx, fmpz_mpoly, fmpz_mpoly_vec from .types.fmpz_mod import * from .types.fmpz_mod_poly import * from .types.fmpz_mod_mat import fmpz_mod_mat diff --git a/src/flint/types/fmpz_mpoly.pxd b/src/flint/types/fmpz_mpoly.pxd index 4b6520e5..97935ed7 100644 --- a/src/flint/types/fmpz_mpoly.pxd +++ b/src/flint/types/fmpz_mpoly.pxd @@ -1,8 +1,6 @@ -from flint.flint_base.flint_base cimport flint_mpoly -from flint.flint_base.flint_base cimport flint_mpoly_context +from flint.flint_base.flint_base cimport flint_mpoly, flint_mpoly_context -from flint.flintlib.fmpz_mpoly cimport fmpz_mpoly_ctx_t -from flint.flintlib.fmpz_mpoly cimport fmpz_mpoly_t, fmpz_mpoly_init +from flint.flintlib.fmpz_mpoly cimport fmpz_mpoly_ctx_t, fmpz_mpoly_vec_t, fmpz_mpoly_t, fmpz_mpoly_init, fmpz_mpoly_struct from flint.flintlib.flint cimport slong cdef inline init_fmpz_mpoly(fmpz_mpoly var, fmpz_mpoly_ctx ctx): @@ -27,3 +25,8 @@ cdef class fmpz_mpoly(flint_mpoly): cdef fmpz_mpoly_t val cdef fmpz_mpoly_ctx ctx cdef bint _init + +cdef class fmpz_mpoly_vec: + cdef fmpz_mpoly_vec_t val + cdef fmpz_mpoly_ctx ctx + cdef fmpz_mpoly_struct **double_indirect diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index d8d0e58d..2ab6fe01 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -308,7 +308,7 @@ cdef class fmpz_mpoly(flint_mpoly): raise ValueError("exponent vector provided does not match number of variables") try: res = fmpz() - exp_vec = fmpz_vec.from_iterable(x, double_indirect=True) + exp_vec = fmpz_vec(x, double_indirect=True) fmpz_mpoly_get_coeff_fmpz_fmpz((res).val, self.val, exp_vec.double_indirect, self.ctx.val) finally: libc.stdlib.free(tmp) @@ -335,7 +335,7 @@ cdef class fmpz_mpoly(flint_mpoly): try: res = fmpz() - exp_vec = fmpz_vec.from_iterable(x, double_indirect=True) + exp_vec = fmpz_vec(x, double_indirect=True) fmpz_mpoly_set_coeff_fmpz_fmpz(self.val, (coeff).val, exp_vec.double_indirect, self.ctx.val) finally: libc.stdlib.free(tmp) @@ -692,7 +692,7 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_ctx res_ctx fmpz_vec V fmpz vres - fmpz_mpoly_struct ** C + fmpz_mpoly_vec C ulong *exponents slong i, nvars = self.ctx.nvars(), nargs = len(args) @@ -729,7 +729,7 @@ cdef class fmpz_mpoly(flint_mpoly): if args and all_fmpz: # Normal evaluation - V = fmpz_vec.from_iterable(args_fmpz, double_indirect=True) + V = fmpz_vec(args_fmpz, double_indirect=True) vres = fmpz.__new__(fmpz) if fmpz_mpoly_evaluate_all_fmpz(vres.val, self.val, V.double_indirect, self.ctx.val) == 0: raise ValueError("unreasonably large polynomial") @@ -751,20 +751,14 @@ cdef class fmpz_mpoly(flint_mpoly): raise TypeError("all arguments must be fmpz_mpolys") res_ctx = ( args[0]).ctx if not all(( args[i]).ctx is res_ctx for i in range(1, len(args))): - raise ValueError("all arguments must share the same context") + raise IncompatibleContextError("all arguments must share the same context") - C = libc.stdlib.malloc(nvars * sizeof(fmpz_mpoly_struct *)) - if C is NULL: - raise MemoryError("malloc returned a null pointer") - try: - for i in range(nvars): - C[i] = &(( args[i]).val[0]) - res = create_fmpz_mpoly(self.ctx) - if fmpz_mpoly_compose_fmpz_mpoly(res.val, self.val, C, self.ctx.val, res_ctx.val) == 0: - raise ValueError("unreasonably large polynomial") - return res - finally: - libc.stdlib.free(C) + + C = fmpz_mpoly_vec(args, self.ctx, double_indirect=True) + res = create_fmpz_mpoly(self.ctx) + if fmpz_mpoly_compose_fmpz_mpoly(res.val, self.val, C.double_indirect, self.ctx.val, res_ctx.val) == 0: + raise ValueError("unreasonably large polynomial") + return res else: # Partial function composition. We do this by composing with all arguments, however the ones # that have not be provided are set to the trivial monomial. This is why we require all the @@ -773,7 +767,7 @@ cdef class fmpz_mpoly(flint_mpoly): if not all(typecheck(arg, fmpz_mpoly) for _, arg in partial_args): raise TypeError("all arguments must be fmpz_mpolys for partial composition") if not all(( arg).ctx is self.ctx for _, arg in partial_args): - raise ValueError("all arguments must share the same context") + raise IncompatibleContextError("all arguments must share the same context") polys = [None] * nvars for i, poly in partial_args: @@ -781,25 +775,15 @@ cdef class fmpz_mpoly(flint_mpoly): for i in range(nvars): if polys[i] is None: - res = create_fmpz_mpoly(self.ctx) - exponents = libc.stdlib.calloc(nvars, sizeof(ulong)) - exponents[i] = 1 - fmpz_mpoly_push_term_ui_ui(res.val, 1, exponents, self.ctx.val) + l = [0] * nvars + l[i] = 1 + polys[i] = self.ctx.from_dict({tuple(l): 1}) - polys[i] = res - - C = libc.stdlib.malloc(nvars * sizeof(fmpz_mpoly_struct *)) - if C is NULL: - raise MemoryError("malloc returned a null pointer") - try: - for i in range(len(polys)): - C[i] = &(( polys[i]).val[0]) - res = create_fmpz_mpoly(self.ctx) - if fmpz_mpoly_compose_fmpz_mpoly(res.val, self.val, C, self.ctx.val, self.ctx.val) == 0: - raise ValueError("unreasonably large polynomial") - return res - finally: - libc.stdlib.free(C) + C = fmpz_mpoly_vec(polys, self.ctx, double_indirect=True) + res = create_fmpz_mpoly(self.ctx) + if fmpz_mpoly_compose_fmpz_mpoly(res.val, self.val, C.double_indirect, self.ctx.val, self.ctx.val) == 0: + raise ValueError("unreasonably large polynomial") + return res def factor(self): """ @@ -947,3 +931,78 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_integral(res.val, scale.val, self.val, i, self.ctx.val) return scale, res + + +cdef class fmpz_mpoly_vec: + def __cinit__(self, iterable_or_len, fmpz_mpoly_ctx ctx, bint double_indirect=False): + if isinstance(iterable_or_len, int): + l = iterable_or_len + else: + l = len(iterable_or_len) + + self.ctx = ctx + fmpz_mpoly_vec_init(self.val, l, self.ctx.val) + + if double_indirect: + self.double_indirect = libc.stdlib.malloc(l * sizeof(fmpz_mpoly_struct *)) + if self.double_indirect is NULL: + raise MemoryError("malloc returned a null pointer") + + for i in range(l): + self.double_indirect[i] = fmpz_mpoly_vec_entry(self.val, i) + else: + self.double_indirect = NULL + + def __init__(self, iterable_or_len, _, double_indirect: bool =False): + if not isinstance(iterable_or_len, int): + for i, x in enumerate(iterable_or_len): + self[i] = x + + def __dealloc__(self): + libc.stdlib.free(self.double_indirect) + fmpz_mpoly_vec_clear(self.val, self.ctx.val) + + def __getitem__(self, x): + if not isinstance(x, int): + raise TypeError("index is not integer") + elif not 0 <= x < self.val.length: + raise IndexError("index out of range") + + cdef fmpz_mpoly z = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_set(z.val, fmpz_mpoly_vec_entry(self.val, x), self.ctx.val) + return z + + def __setitem__(self, x, y): + if not isinstance(x, int): + raise TypeError("index is not integer") + elif not 0 <= x < self.val.length: + raise IndexError("index out of range") + elif not typecheck(y, fmpz_mpoly): + raise TypeError("argument is not fmpz_mpoly") + elif (y).ctx is not self.ctx: + raise IncompatibleContextError(f"{(y).ctx} is not {self.ctx}") + + fmpz_mpoly_set(fmpz_mpoly_vec_entry(self.val, x), (y).val, self.ctx.val) + + def __len__(self): + return self.val.length + + def __str__(self): + return self.str() + + def __repr__(self): + return self.repr() + + def str(self, *args): + s = [None] * self.val.length + for i in range(self.val.length): + x = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_set(x.val, fmpz_mpoly_vec_entry(self.val, i), self.ctx.val) + s[i] = x.str(*args) + return str(s) + + def repr(self, *args): + return f"fmpz_mpoly_vec({self.str(*args)}, {self.val.length})" + + def to_tuple(self): + return tuple(self[i] for i in range(self.val.length)) diff --git a/src/flint/types/fmpz_vec.pyx b/src/flint/types/fmpz_vec.pyx index 56962591..1576a219 100644 --- a/src/flint/types/fmpz_vec.pyx +++ b/src/flint/types/fmpz_vec.pyx @@ -7,19 +7,29 @@ from flint.types.fmpz cimport fmpz, any_as_fmpz cimport libc.stdlib cdef class fmpz_vec: - def __cinit__(self, slong length, bint double_indirect=False): - self.val = _fmpz_vec_init(length) - self.length = length + def __cinit__(self, iterable_or_len, bint double_indirect=False): + if isinstance(iterable_or_len, int): + self.length = iterable_or_len + else: + self.length = len(iterable_or_len) + + self.val = _fmpz_vec_init(self.length) + if double_indirect: - self.double_indirect = libc.stdlib.malloc(length * sizeof(fmpz_struct *)) + self.double_indirect = libc.stdlib.malloc(self.length * sizeof(fmpz_struct *)) if self.double_indirect is NULL: raise MemoryError("malloc returned a null pointer") - for i in range(length): + for i in range(self.length): self.double_indirect[i] = &self.val[i] else: self.double_indirect = NULL + def __init__(self, iterable_or_len, double_indirect: bool = False): + if not isinstance(iterable_or_len, int): + for i, x in enumerate(iterable_or_len): + self[i] = x + def __dealloc__(self): libc.stdlib.free(self.double_indirect) _fmpz_vec_clear(self.val, self.length) @@ -49,6 +59,12 @@ cdef class fmpz_vec: def __len__(self): return self.length + def __str__(self): + return self.str() + + def __repr__(self): + return self.repr() + def str(self, *args): s = [None] * self.length for i in range(self.length): @@ -60,21 +76,5 @@ cdef class fmpz_vec: def repr(self, *args): return f"fmpz_vec({self.str(*args)}, {self.length})" - @staticmethod - def from_iterable(iterable, double_indirect: bool = False): - length = len(iterable) - - vec = fmpz_vec(length, double_indirect) - for i, x in enumerate(iterable): - x = any_as_fmpz(x) - if x is NotImplemented: - raise TypeError("argument is not coercible to fmpz") - - fmpz_set(&vec.val[i], (x).val) - return vec - def to_tuple(self): - t = tuple(fmpz.__new__(fmpz) for _ in range(self.length)) - for i in range(self.length): - fmpz_init_set((t[i]).val, &self.val[i]) - return t + return tuple(self[i] for i in range(self.length)) From 82885a4c69647d3099084051f631e66f3ffcd87a Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Sat, 4 May 2024 22:31:42 +1000 Subject: [PATCH 46/95] Add build-install to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 0f4570f3..075ec3ce 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ build/* +build-install/* dist/* src/flint/**/*.c src/flint/*.html From f0831013cf255310fc513ae5803b5b7516cea632 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Sun, 5 May 2024 16:32:49 +1000 Subject: [PATCH 47/95] Add coverage option to meson `spin build --clean -- -Dcoverage=true` --- meson.build | 11 +++++++++++ meson.options | 1 + src/flint/types/fmpz_mpoly.pyx | 1 - 3 files changed, 12 insertions(+), 1 deletion(-) create mode 100644 meson.options diff --git a/meson.build b/meson.build index b4fe7f83..8655c120 100644 --- a/meson.build +++ b/meson.build @@ -9,6 +9,17 @@ gmp_dep = dependency('gmp') mpfr_dep = dependency('mpfr') flint_dep = dependency('flint') +add_project_arguments( + '-X', 'embedsignature=True', + '-X', 'emit_code_comments=True', + language : 'cython' +) + +if get_option('coverage') + add_project_arguments('-X', 'linetrace=True', language : 'cython') + add_project_arguments('-DCYTHON_TRACE=1', language : 'c') +endif + # flint.pc was missing -lflint until Flint 3.1.0 if flint_dep.version().version_compare('<3.1') flint_dep = cc.find_library('flint') diff --git a/meson.options b/meson.options new file mode 100644 index 00000000..7115c50f --- /dev/null +++ b/meson.options @@ -0,0 +1 @@ +option('coverage', type : 'boolean', value : false, description : 'enable coverage build') diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 2ab6fe01..571ad18e 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -753,7 +753,6 @@ cdef class fmpz_mpoly(flint_mpoly): if not all(( args[i]).ctx is res_ctx for i in range(1, len(args))): raise IncompatibleContextError("all arguments must share the same context") - C = fmpz_mpoly_vec(args, self.ctx, double_indirect=True) res = create_fmpz_mpoly(self.ctx) if fmpz_mpoly_compose_fmpz_mpoly(res.val, self.val, C.double_indirect, self.ctx.val, res_ctx.val) == 0: From 8239992d52b72897909aa1199ed5d965a6bce57e Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Mon, 6 May 2024 00:08:01 +1000 Subject: [PATCH 48/95] Add doc strings, rewrite from_dict method to use the fmpz_vec --- src/flint/test/test_all.py | 2 +- src/flint/types/fmpz_mpoly.pyx | 258 ++++++++++++++++++++------------- 2 files changed, 161 insertions(+), 99 deletions(-) diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index e6973999..6be2961d 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -2659,7 +2659,7 @@ def test_mpolys(): assert raises(lambda: ctx.from_dict("bad"), ValueError) assert raises(lambda: ctx.from_dict({(0, 0): "bad"}), TypeError) assert raises(lambda: ctx.from_dict({(0, "bad"): 1}), TypeError) - assert raises(lambda: ctx.from_dict({(0,): 1}), TypeError) + assert raises(lambda: ctx.from_dict({(0,): 1}), ValueError) assert raises(lambda: P(val=C.get_context(nvars=1).constant(0), ctx=ctx), ValueError) assert raises(lambda: P(val={}, ctx=None), ValueError) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 571ad18e..7dadec53 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -161,59 +161,33 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): >>> ctx.from_dict({(1,0):2, (1,1):3, (0,1):1}) 3*x*y + 2*x + y """ - cdef long n - cdef fmpz_t coefficient - cdef fmpz_struct *exponents - cdef int xtype - cdef int nvars = self.nvars() - cdef int i,j - cdef int count - cdef fmpz_mpoly res + cdef: + fmpz_vec exp_vec + slong i, j, nvars = self.nvars() + fmpz_mpoly res - if not PyDict_Check(d): + if not isinstance(d, dict): raise ValueError("expected a dictionary") - n = PyDict_Size(d) - fmpz_init(coefficient) - exponents = libc.stdlib.calloc(nvars, sizeof(fmpz_struct)) - if exponents == NULL: - raise MemoryError("malloc returned a null pointer") - for i in range(nvars): - fmpz_init(exponents + i) - fmpz_init(coefficient) + + exp_vec = fmpz_vec(nvars) res = create_fmpz_mpoly(self) - count = 0 - for k, v in d.items(): - xtype = fmpz_set_any_ref(coefficient, v) - if xtype == FMPZ_UNKNOWN: - for i in range(nvars): - fmpz_clear(exponents + i) - libc.stdlib.free(exponents) - raise TypeError("invalid coefficient type %s" % type(v)) - if not PyTuple_Check(k): - for i in range(nvars): - fmpz_clear(exponents + i) - libc.stdlib.free(exponents) - raise TypeError("Expected tuple of ints as key not %s" % type(k)) - if PyTuple_GET_SIZE(k) != nvars: - for i in range(nvars): - fmpz_clear(exponents + i) - libc.stdlib.free(exponents) - raise TypeError("Expected exponent tuple of length %d" % nvars) - for i, tup in enumerate(k): - xtype = fmpz_set_any_ref(exponents + i, tup) - if xtype == FMPZ_UNKNOWN: - for i in range(nvars): - fmpz_clear(exponents + i) - libc.stdlib.free(exponents) - raise TypeError("Invalid exponent type %s" % type(tup)) - #Todo lobby for fmpz_mpoly_push_term_fmpz_ffmpz - if not fmpz_is_zero(coefficient): - _fmpz_mpoly_push_exp_ffmpz(res.val, exponents, self.val) - fmpz_mpoly_set_term_coeff_fmpz(res.val, count, coefficient, self.val) - count += 1 - for i in range(nvars): - fmpz_clear(exponents + i) - fmpz_clear(coefficient) + + for i, (k, v) in enumerate(d.items()): + v = any_as_fmpz(v) + if v is NotImplemented: + raise TypeError(f"cannot coerce coefficient '{v}' to fmpz") + elif len(k) != nvars: + raise ValueError(f"expected {nvars} exponents, got {len(k)}") + + exp_vec = fmpz_vec(nvars) + for j, exponent in enumerate(k): + exp_vec[j] = exponent + + # TODO: lobby for fmpz_mpoly_push_term_fmpz_ffmpz + if v: + _fmpz_mpoly_push_exp_ffmpz(res.val, exp_vec.val, self.val) + fmpz_mpoly_set_term_coeff_fmpz(res.val, i, ( v).val, self.val) + fmpz_mpoly_sort_terms(res.val, self.val) return res @@ -268,6 +242,14 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_set_fmpz(self.val, (v).val, self.ctx.val) def context(self): + """ + Return the context object for this polynomials. + + >>> ctx = fmpz_mpoly_ctx.get_context(2, "lex", 'x') + >>> p = ctx.from_dict({(0, 1): 2}) + >>> ctx is p.context() + True + """ return self.ctx def __bool__(self): @@ -293,8 +275,19 @@ cdef class fmpz_mpoly(flint_mpoly): return fmpz_mpoly_length(self.val, self.ctx.val) def __getitem__(self, x): + """ + Return the term at index `x` if `x` is an `int`, or the term with the exponent + vector `x` if `x` is a tuple of `int`s or `fmpz`s. + + >>> ctx = fmpz_mpoly_ctx.get_context(2, "lex", 'x') + >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) + >>> p[1] + 2*x1 + >>> p[1, 1] + 3*x0*x1 + + """ cdef: - fmpz_struct ** tmp = NULL slong nvars = self.ctx.nvars() if isinstance(x, int): @@ -306,19 +299,27 @@ cdef class fmpz_mpoly(flint_mpoly): elif isinstance(x, tuple): if len(x) != nvars: raise ValueError("exponent vector provided does not match number of variables") - try: - res = fmpz() - exp_vec = fmpz_vec(x, double_indirect=True) - fmpz_mpoly_get_coeff_fmpz_fmpz((res).val, self.val, exp_vec.double_indirect, self.ctx.val) - finally: - libc.stdlib.free(tmp) + res = fmpz() + exp_vec = fmpz_vec(x, double_indirect=True) + fmpz_mpoly_get_coeff_fmpz_fmpz((res).val, self.val, exp_vec.double_indirect, self.ctx.val) return res else: raise TypeError("index is not integer or tuple") def __setitem__(self, x, y): + """ + Set the coefficient at index `x` to `y` if `x` is an `int`, or the term with + the exponent vector `x` if `x` is a tuple of `int`s or `fmpz`s. + + >>> ctx = fmpz_mpoly_ctx.get_context(2, "lex", 'x') + >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) + >>> p[1] = 10 + >>> p[1, 1] = 20 + >>> p + 20*x0*x1 + 10*x1 + + """ cdef: - fmpz_struct ** tmp = NULL slong nvars = self.ctx.nvars() coeff = any_as_fmpz(y) @@ -332,17 +333,20 @@ cdef class fmpz_mpoly(flint_mpoly): elif isinstance(x, tuple): if len(x) != nvars: raise ValueError("exponent vector provided does not match number of variables") - - try: - res = fmpz() - exp_vec = fmpz_vec(x, double_indirect=True) - fmpz_mpoly_set_coeff_fmpz_fmpz(self.val, (coeff).val, exp_vec.double_indirect, self.ctx.val) - finally: - libc.stdlib.free(tmp) + exp_vec = fmpz_vec(x, double_indirect=True) + fmpz_mpoly_set_coeff_fmpz_fmpz(self.val, (coeff).val, exp_vec.double_indirect, self.ctx.val) else: raise TypeError("index is not integer or tuple") def coefficient(self, slong i): + """ + Return the coefficient at index `i`. + + >>> ctx = fmpz_mpoly_ctx.get_context(2, "lex", 'x') + >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) + >>> p.coefficient(1) + 2 + """ cdef fmpz v if i < 0 or i >= fmpz_mpoly_length(self.val, self.ctx.val): return fmpz(0) @@ -352,9 +356,16 @@ cdef class fmpz_mpoly(flint_mpoly): return v def exponent_tuple(self, slong i): + """ + Return the exponent vector at index `i` as a tuple. + + >>> ctx = fmpz_mpoly_ctx.get_context(2, "lex", 'x') + >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) + >>> p.exponent_tuple(1) + (0, 1) + """ cdef: - fmpz_struct ** tmp - slong j, nvars = self.ctx.nvars() + slong nvars = self.ctx.nvars() if i < 0 or i >= fmpz_mpoly_length(self.val, self.ctx.val): raise IndexError("term index out of range") @@ -363,15 +374,30 @@ cdef class fmpz_mpoly(flint_mpoly): return res.to_tuple() def degrees(self): + """ + Return a dictionary of variable name to degree. + + >>> ctx = fmpz_mpoly_ctx.get_context(4, "lex", 'x') + >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) + >>> p.degrees() + {'x0': 1, 'x1': 2, 'x2': 3, 'x3': 0} + """ cdef: - fmpz_struct ** tmp - slong i, nvars = self.ctx.nvars() + slong nvars = self.ctx.nvars() res = fmpz_vec(nvars, double_indirect=True) fmpz_mpoly_degrees_fmpz(res.double_indirect, self.val, self.ctx.val) return dict(zip(self.ctx.names(), res.to_tuple())) def total_degree(self): + """ + Return the total degree. + + >>> ctx = fmpz_mpoly_ctx.get_context(4, "lex", 'x') + >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) + >>> p.total_degree() + 3 + """ cdef fmpz res = fmpz() fmpz_mpoly_total_degree_fmpz(( res).val, self.val, self.ctx.val) return res @@ -601,7 +627,7 @@ cdef class fmpz_mpoly(flint_mpoly): return res return NotImplemented - def __rfloordiv__(self,other): + def __rfloordiv__(self, other): cdef fmpz_mpoly res if not self: raise ZeroDivisionError("fmpz_mpoly division by zero") @@ -693,7 +719,6 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_vec V fmpz vres fmpz_mpoly_vec C - ulong *exponents slong i, nvars = self.ctx.nvars(), nargs = len(args) if not args and not kwargs: @@ -774,9 +799,9 @@ cdef class fmpz_mpoly(flint_mpoly): for i in range(nvars): if polys[i] is None: - l = [0] * nvars - l[i] = 1 - polys[i] = self.ctx.from_dict({tuple(l): 1}) + vec = [0] * nvars + vec[i] = 1 + polys[i] = self.ctx.from_dict({tuple(vec): 1}) C = fmpz_mpoly_vec(polys, self.ctx, double_indirect=True) res = create_fmpz_mpoly(self.ctx) @@ -799,22 +824,24 @@ cdef class fmpz_mpoly(flint_mpoly): >>> (p2 * p1 * p2).factor() (18, [(z + 1, 2), (x + 2, 1), (x + 1, 2)]) """ - cdef fmpz_mpoly_factor_t fac - cdef int i - cdef fmpz c - cdef fmpz_mpoly u + cdef: + fmpz_mpoly_factor_t fac + fmpz c + fmpz_mpoly u + fmpz_mpoly_factor_init(fac, self.ctx.val) fmpz_mpoly_factor(fac, self.val, self.ctx.val) res = [0] * fac.num - for 0 <= i < fac.num: - u = fmpz_mpoly.__new__(fmpz_mpoly) - u.ctx = self.ctx - fmpz_mpoly_init(u.val, u.ctx.val) - u._init = True + + for i in range(fac.num): + u = create_fmpz_mpoly(self.ctx) fmpz_mpoly_set((u).val, &fac.poly[i], self.ctx.val) + c = fmpz.__new__(fmpz) fmpz_set((c).val, &fac.exp[i]) + res[i] = (u, c) + c = fmpz.__new__(fmpz) fmpz_set((c).val, fac.constant) fmpz_mpoly_factor_clear(fac, self.ctx.val) @@ -835,22 +862,25 @@ cdef class fmpz_mpoly(flint_mpoly): >>> (p1 * p2 * p1).factor_squarefree() (12, [(y + 1, 1), (x + 1, 1), (x + 2, 2)]) """ - cdef fmpz_mpoly_factor_t fac - cdef int i - cdef fmpz c - cdef fmpz_mpoly u + cdef: + fmpz_mpoly_factor_t fac + fmpz c + fmpz_mpoly u + fmpz_mpoly_factor_init(fac, self.ctx.val) fmpz_mpoly_factor_squarefree(fac, self.val, self.ctx.val) res = [0] * fac.num - for 0 <= i < fac.num: - u = fmpz_mpoly.__new__(fmpz_mpoly) - u.ctx = self.ctx + + for i in range(fac.num): + u = create_fmpz_mpoly(self.ctx) fmpz_mpoly_init(u.val, u.ctx.val) - u._init = True fmpz_mpoly_set((u).val, &fac.poly[i], self.ctx.val) + c = fmpz.__new__(fmpz) fmpz_set((c).val, &fac.exp[i]) + res[i] = (u, c) + c = fmpz.__new__(fmpz) fmpz_set((c).val, fac.constant) fmpz_mpoly_factor_clear(fac, self.ctx.val) @@ -883,6 +913,20 @@ cdef class fmpz_mpoly(flint_mpoly): return res def derivative(self, var): + """ + Return the derivative of this polynomial with respect to the provided variable. + The argument and either be the variable as a string, or the index of the + variable in the context. + + >>> ctx = fmpz_mpoly_ctx.get_context(2, "lex", 'x') + >>> p = ctx.from_dict({(0, 3): 2, (2, 1): 3}) + 3*x0^2*x1 + 2*x1^3 + >>> p.derivative("x0") + 6*x0*x1 + >>> p.derivative(1) + 3*x0^2 + 6*x1^2 + + """ cdef: fmpz_mpoly res slong i = 0 @@ -906,6 +950,20 @@ cdef class fmpz_mpoly(flint_mpoly): return res def integral(self, var): + """ + Return the integral of this polynomial*B with respect to the provided variable + where B is minimal. The argument and either be the variable as a string, or + the index of the variable in the context. + + >>> ctx = fmpz_mpoly_ctx.get_context(2, "lex", 'x') + >>> p = ctx.from_dict({(0, 3): 2, (2, 1): 3}) + 3*x0^2*x1 + 2*x1^3 + >>> p.integral("x0") + (1, x0^3*x1 + 2*x0*x1^3) + >>> p.integral(1) + (2, 3*x0^2*x1^2 + x1^4) + + """ cdef: fmpz_mpoly res fmpz scale @@ -933,26 +991,30 @@ cdef class fmpz_mpoly(flint_mpoly): cdef class fmpz_mpoly_vec: - def __cinit__(self, iterable_or_len, fmpz_mpoly_ctx ctx, bint double_indirect=False): + """ + A class representing a vector of fmpz_mpolys. + """ + + def __cinit__(self, iterable_or_len, fmpz_mpoly_ctx ctx, bint double_indirect = False): if isinstance(iterable_or_len, int): - l = iterable_or_len + length = iterable_or_len else: - l = len(iterable_or_len) + length = len(iterable_or_len) self.ctx = ctx - fmpz_mpoly_vec_init(self.val, l, self.ctx.val) + fmpz_mpoly_vec_init(self.val, length, self.ctx.val) if double_indirect: - self.double_indirect = libc.stdlib.malloc(l * sizeof(fmpz_mpoly_struct *)) + self.double_indirect = libc.stdlib.malloc(length * sizeof(fmpz_mpoly_struct *)) if self.double_indirect is NULL: raise MemoryError("malloc returned a null pointer") - for i in range(l): + for i in range(length): self.double_indirect[i] = fmpz_mpoly_vec_entry(self.val, i) else: self.double_indirect = NULL - def __init__(self, iterable_or_len, _, double_indirect: bool =False): + def __init__(self, iterable_or_len, _, double_indirect: bool = False): if not isinstance(iterable_or_len, int): for i, x in enumerate(iterable_or_len): self[i] = x From f35903e8e49c9d7a1792ab2c0be0efa0a74106b2 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Mon, 6 May 2024 00:21:10 +1000 Subject: [PATCH 49/95] Forgot about the interface I wrote, whoops --- src/flint/types/fmpz_mpoly.pyx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 7dadec53..2735f689 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -179,9 +179,7 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): elif len(k) != nvars: raise ValueError(f"expected {nvars} exponents, got {len(k)}") - exp_vec = fmpz_vec(nvars) - for j, exponent in enumerate(k): - exp_vec[j] = exponent + exp_vec = fmpz_vec(k) # TODO: lobby for fmpz_mpoly_push_term_fmpz_ffmpz if v: From a61b4e425535d4df3d5943ceeb0a204a9d87d065 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Mon, 6 May 2024 12:14:57 +1000 Subject: [PATCH 50/95] Fix doctests --- src/flint/types/fmpz_mpoly.pyx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 2735f689..bb493f63 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -282,7 +282,7 @@ cdef class fmpz_mpoly(flint_mpoly): >>> p[1] 2*x1 >>> p[1, 1] - 3*x0*x1 + 3 """ cdef: @@ -918,6 +918,7 @@ cdef class fmpz_mpoly(flint_mpoly): >>> ctx = fmpz_mpoly_ctx.get_context(2, "lex", 'x') >>> p = ctx.from_dict({(0, 3): 2, (2, 1): 3}) + >>> p 3*x0^2*x1 + 2*x1^3 >>> p.derivative("x0") 6*x0*x1 @@ -955,6 +956,7 @@ cdef class fmpz_mpoly(flint_mpoly): >>> ctx = fmpz_mpoly_ctx.get_context(2, "lex", 'x') >>> p = ctx.from_dict({(0, 3): 2, (2, 1): 3}) + >>> p 3*x0^2*x1 + 2*x1^3 >>> p.integral("x0") (1, x0^3*x1 + 2*x0*x1^3) From a1085223891d2319de15f4b6584bd3dff44d0a14 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Mon, 6 May 2024 12:16:09 +1000 Subject: [PATCH 51/95] Keep the setup.py up to date --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 71bf5079..507e98b0 100644 --- a/setup.py +++ b/setup.py @@ -81,6 +81,7 @@ ("flint.flint_base.flint_context", ["src/flint/flint_base/flint_context.pyx"]), ("flint.types.fmpz", ["src/flint/types/fmpz.pyx"]), + ("flint.types.fmpz_vec", ["src/flint/types/fmpz_vec.pyx"]), ("flint.types.fmpz_poly", ["src/flint/types/fmpz_poly.pyx"]), ("flint.types.fmpz_mpoly", ["src/flint/types/fmpz_mpoly.pyx"]), ("flint.types.fmpz_mat", ["src/flint/types/fmpz_mat.pyx"]), From 59a34ad798f6cef2dcd5eaf8cbc98bbb86f88fa1 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Mon, 6 May 2024 20:23:52 +1000 Subject: [PATCH 52/95] 100% fmpz_mpoly test coverage. Line 369 is being run I swear, coverage just isn't picking it up --- src/flint/flint_base/flint_base.pyx | 5 +- src/flint/test/test_all.py | 104 ++++++++++++++++--- src/flint/types/fmpz_mpoly.pyx | 151 +++++++++------------------- 3 files changed, 142 insertions(+), 118 deletions(-) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index d4b5d35a..6a679a1c 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -228,10 +228,7 @@ cdef class flint_mpoly(flint_elem): return hash(s) def to_dict(self): - d = {} - for i in range(len(self)): - d[self.exponent_tuple(i)] = self.coefficient(i) - return d + return {self.exponent_tuple(i): self.coefficient(i) for i in range(len(self))} cdef class flint_series(flint_elem): diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index 6be2961d..d2f526cf 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -2660,6 +2660,8 @@ def test_mpolys(): assert raises(lambda: ctx.from_dict({(0, 0): "bad"}), TypeError) assert raises(lambda: ctx.from_dict({(0, "bad"): 1}), TypeError) assert raises(lambda: ctx.from_dict({(0,): 1}), ValueError) + assert raises(lambda: ctx.gen(-1), IndexError) + assert raises(lambda: ctx.gen(10), IndexError) assert raises(lambda: P(val=C.get_context(nvars=1).constant(0), ctx=ctx), ValueError) assert raises(lambda: P(val={}, ctx=None), ValueError) @@ -2669,6 +2671,7 @@ def test_mpolys(): assert P(val={(0, 0): 1}, ctx=ctx) == ctx.from_dict({(0, 0): 1}) assert P(ctx=ctx).context() == ctx assert P(1, ctx=ctx).is_one() + assert ctx.gen(1) == ctx.from_dict({(0, 1): 1}) def mpoly(x): return ctx.from_dict(x) @@ -2711,7 +2714,11 @@ def quick_poly(): assert (P(1, ctx=ctx) == P(2, ctx=ctx)) is False assert (P(1, ctx=ctx) != P(2, ctx=ctx)) is True - ctx1 = C.get_context(1) + assert (P(1, ctx=ctx) == 1) is False + assert (P(1, ctx=ctx) != 1) is True + assert (1 == P(1, ctx=ctx)) is False + assert (1 != P(1, ctx=ctx)) is True + assert (P(1, ctx=ctx) == P(1, ctx=ctx1)) is False assert (P(1, ctx=ctx) != P(1, ctx=ctx1)) is True @@ -2720,6 +2727,9 @@ def quick_poly(): assert (None == P(1, ctx=ctx)) is False assert (None != P(1, ctx=ctx)) is True + assert P(ctx.from_dict({(0, 1): 3})) == ctx.from_dict({(0, 1): 3}) + assert P({(0, 1): 3}) == ctx.from_dict({(0, 1): 3}) + assert raises(lambda: P(ctx=ctx) < P(ctx=ctx), TypeError) assert raises(lambda: P(ctx=ctx) <= P(ctx=ctx), TypeError) assert raises(lambda: P(ctx=ctx) > P(ctx=ctx), TypeError) @@ -2767,9 +2777,9 @@ def quick_poly(): assert raises(lambda: p.__setitem__(2, None), TypeError) assert raises(lambda: p.__setitem__(-1, 1), IndexError) - assert repr(P(ctx=ctx)) == '0' - assert repr(P(1, ctx=ctx)) == '1' - assert repr(quick_poly()) == '4*x0^2*x1^2 + 3*x0 + 2*x1 + 1' + assert P(ctx=ctx).repr() == "fmpz_mpoly_ctx(2, 'lex', ('x0', 'x1')).from_dict({})" + assert P(1, ctx=ctx).repr() == "fmpz_mpoly_ctx(2, 'lex', ('x0', 'x1')).from_dict({(0, 0): 1})" + assert str(quick_poly()) == repr(quick_poly()) == '4*x0^2*x1^2 + 3*x0 + 2*x1 + 1' assert p.exponent_tuple(0) == (2, 2) assert p.exponent_tuple(3) == (0, 0) @@ -2791,7 +2801,18 @@ def quick_poly(): (0, 1): 2, (0, 0): 4 }) + assert p() == p + assert p(x0=ctx.from_dict({(1, 0): 1}), x1=ctx.from_dict({(0, 1): 1})) == p + assert raises(lambda: p(x0=None), TypeError) + assert raises(lambda: p(x0=None, x1=None), TypeError) + assert raises(lambda: p(1), ValueError) + assert raises(lambda: p(1, x1=1), ValueError) + assert raises(lambda: p(a=1), ValueError) + assert raises(lambda: p(0, 1, 2), ValueError) + assert raises(lambda: p(x0=0, x1=1, x2=2), ValueError) + assert raises(lambda: p(x0=p, x1=P(ctx=ctx1)), IncompatibleContextError) + assert raises(lambda: P(ctx=ctx1)(x0=p), IncompatibleContextError) assert bool(P(ctx=ctx)) is False assert bool(P(1, ctx=ctx)) is True @@ -2805,9 +2826,11 @@ def quick_poly(): + mpoly({(0, 0): 5, (0, 1): 6, (1, 0): 7, (2, 2): 8}) \ == mpoly({(0, 0): 6, (0, 1): 8, (1, 0): 10, (2, 2): 12}) - for T in [int, S, flint.fmpz]: + for T in [int, S, flint.fmpz, lambda x: P(x, ctx=ctx)]: + p = quick_poly() + p += T(1) assert quick_poly() + T(1) \ - == mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}) + == p == mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}) assert T(1) + quick_poly() \ == mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}) @@ -2815,18 +2838,22 @@ def quick_poly(): assert raises(lambda: None + mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}), TypeError) assert raises(lambda: quick_poly() + P(ctx=ctx1), IncompatibleContextError) assert raises(lambda: quick_poly().__iadd__(P(ctx=ctx1)), IncompatibleContextError) + assert quick_poly().__iadd__(None) is NotImplemented assert quick_poly() - mpoly({(0, 0): 5, (0, 1): 6, (1, 0): 7, (2, 2): 8}) \ == mpoly({(0, 0): -4, (0, 1): -4, (1, 0): -4, (2, 2): -4}) - for T in [int, S, flint.fmpz]: - assert quick_poly() - T(1) == mpoly({(0, 1): 2, (1, 0): 3, (2, 2): 4}) + for T in [int, S, flint.fmpz, lambda x: P(x, ctx=ctx)]: + p = quick_poly() + p -= T(1) + assert quick_poly() - T(1) == p == mpoly({(0, 1): 2, (1, 0): 3, (2, 2): 4}) assert T(1) - quick_poly() == mpoly({(0, 1): -2, (1, 0): -3, (2, 2): -4}) assert raises(lambda: quick_poly() - None, TypeError) assert raises(lambda: None - quick_poly(), TypeError) assert raises(lambda: quick_poly() - P(ctx=ctx1), IncompatibleContextError) assert raises(lambda: quick_poly().__isub__(P(ctx=ctx1)), IncompatibleContextError) + assert quick_poly().__isub__(None) is NotImplemented assert quick_poly() * mpoly({(1, 0): 5, (0, 1): 6}) \ == mpoly({ @@ -2839,14 +2866,17 @@ def quick_poly(): (0, 1): 6 }) - for T in [int, S, flint.fmpz]: - assert quick_poly() * T(2) == mpoly({(0, 0): 2, (0, 1): 4, (1, 0): 6, (2, 2): 8}) + for T in [int, S, flint.fmpz, lambda x: P(x, ctx=ctx)]: + p = quick_poly() + p *= T(2) + assert quick_poly() * T(2) == p == mpoly({(0, 0): 2, (0, 1): 4, (1, 0): 6, (2, 2): 8}) assert T(2) * quick_poly() == mpoly({(0, 0): 2, (0, 1): 4, (1, 0): 6, (2, 2): 8}) assert raises(lambda: quick_poly() * None, TypeError) assert raises(lambda: None * quick_poly(), TypeError) assert raises(lambda: quick_poly() * P(ctx=ctx1), IncompatibleContextError) assert raises(lambda: quick_poly().__imul__(P(ctx=ctx1)), IncompatibleContextError) + assert quick_poly().__imul__(None) is NotImplemented assert quick_poly() // mpoly({(1, 1): 1}) == mpoly({(1, 1): 4}) assert quick_poly() % mpoly({(1, 1): 1}) \ @@ -2854,10 +2884,13 @@ def quick_poly(): assert divmod(quick_poly(), mpoly({(1, 1): 1})) \ == (mpoly({(1, 1): 4}), mpoly({(1, 0): 3, (0, 1): 2, (0, 0): 1})) + assert quick_poly() / 1 == quick_poly() assert quick_poly() // 1 == quick_poly() assert quick_poly() % 1 == P(ctx=ctx) assert divmod(quick_poly(), 1) == (quick_poly(), P(ctx=ctx)) + assert raises(lambda: quick_poly() / 3, DomainError) + # TODO FIXME # if is_field: # assert P([1, 1]) // 2 == P([S(1)/2, S(1)/2]) @@ -2870,27 +2903,36 @@ def quick_poly(): assert 1 % quick_poly() == P(1, ctx=ctx) assert divmod(1, quick_poly()) == (P(ctx=ctx), P(1, ctx=ctx)) + assert raises(lambda: quick_poly() / None, TypeError) assert raises(lambda: quick_poly() // None, TypeError) assert raises(lambda: quick_poly() % None, TypeError) assert raises(lambda: divmod(quick_poly(), None), TypeError) + assert raises(lambda: None / quick_poly(), TypeError) assert raises(lambda: None // quick_poly(), TypeError) assert raises(lambda: None % quick_poly(), TypeError) assert raises(lambda: divmod(None, quick_poly()), TypeError) + assert raises(lambda: quick_poly() / 0, ZeroDivisionError) assert raises(lambda: quick_poly() // 0, ZeroDivisionError) assert raises(lambda: quick_poly() % 0, ZeroDivisionError) assert raises(lambda: divmod(quick_poly(), 0), ZeroDivisionError) + assert raises(lambda: 1 / P(ctx=ctx), ZeroDivisionError) + assert raises(lambda: 1 // P(ctx=ctx), ZeroDivisionError) + assert raises(lambda: 1 % P(ctx=ctx), ZeroDivisionError) + assert raises(lambda: divmod(1, P(ctx=ctx)), ZeroDivisionError) + + assert raises(lambda: quick_poly() / P(ctx=ctx), ZeroDivisionError) assert raises(lambda: quick_poly() // P(ctx=ctx), ZeroDivisionError) assert raises(lambda: quick_poly() % P(ctx=ctx), ZeroDivisionError) assert raises(lambda: divmod(quick_poly(), P(ctx=ctx)), ZeroDivisionError) + assert raises(lambda: quick_poly() / P(1, ctx=ctx1), IncompatibleContextError) assert raises(lambda: quick_poly() // P(1, ctx=ctx1), IncompatibleContextError) assert raises(lambda: quick_poly() % P(1, ctx=ctx1), IncompatibleContextError) assert raises(lambda: divmod(quick_poly(), P(1, ctx=ctx1)), IncompatibleContextError) - # TODO FIXME # # Exact/field scalar division # if is_field: @@ -2957,18 +2999,52 @@ def quick_poly(): # assert P([1, 2]).deflation() == (P([1, 2]), 1) # assert P([1, 0, 2]).deflation() == (P([1, 2]), 2) - assert quick_poly().derivative(0) == quick_poly().derivative("x0") == mpoly({(0, 0): 3, (1, 2): 8}) - assert quick_poly().derivative(1) == quick_poly().derivative("x1") == mpoly({(0, 0): 2, (2, 1): 8}) + p = quick_poly() + assert p.derivative(0) == p.derivative("x0") == mpoly({(0, 0): 3, (1, 2): 8}) + assert p.derivative(1) == p.derivative("x1") == mpoly({(0, 0): 2, (2, 1): 8}) + + assert raises(lambda: p.derivative("x3"), ValueError) + assert raises(lambda: p.derivative(3), IndexError) + assert raises(lambda: p.derivative(None), TypeError) assert quick_poly().integral(0) == quick_poly().integral("x0") == \ (6, mpoly({(3, 2): 8, (2, 0): 9, (1, 1): 12, (1, 0): 6})) assert quick_poly().integral(1) == quick_poly().integral("x1") == \ (3, mpoly({(2, 3): 4, (1, 1): 9, (0, 2): 3, (0, 1): 3})) + assert raises(lambda: p.integral("x3"), ValueError) + assert raises(lambda: p.integral(3), IndexError) + assert raises(lambda: p.integral(None), TypeError) + # if is_field: # assert P([1, 2, 1]).integral() == P([0, 1, 1, S(1)/3]) +def test_fmpz_mpoly_vec(): + ctx = flint.fmpz_mpoly_ctx.get_context(nvars=2) + ctx1 = flint.fmpz_mpoly_ctx.get_context(nvars=4) + + vec = flint.fmpz_mpoly_vec(3, ctx) + + assert vec[0] == ctx.from_dict({}) + assert vec[1] == ctx.from_dict({}) + assert vec[2] == ctx.from_dict({}) + + assert len(vec) == 3 + assert str(vec) == f"[{', '.join(str(ctx.from_dict({})) for _ in range(3))}]" + assert repr(vec) == f"fmpz_mpoly_vec([{', '.join(str(ctx.from_dict({})) for _ in range(3))}], ctx={str(ctx)})" + + assert raises(lambda: vec[None], TypeError) + assert raises(lambda: vec[-1], IndexError) + + vec[1] = ctx.from_dict({(1, 1): 1}) + assert vec.to_tuple() == flint.fmpz_mpoly_vec([ctx.from_dict({}), ctx.from_dict({(1, 1): 1}), ctx.from_dict({})], ctx).to_tuple() + assert raises(lambda: vec.__setitem__(None, 0), TypeError) + assert raises(lambda: vec.__setitem__(-1, 0), IndexError) + assert raises(lambda: vec.__setitem__(0, 0), TypeError) + assert raises(lambda: vec.__setitem__(0, ctx1.from_dict({})), IncompatibleContextError) + + def _all_matrices(): """Return a list of matrix types and scalar types.""" R163 = flint.fmpz_mod_ctx(163) @@ -3514,6 +3590,8 @@ def test_all_tests(): test_polys, test_mpolys, + test_fmpz_mpoly_vec, + test_matrices_eq, test_matrices_constructor, test_matrices_strrepr, diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index bb493f63..2725e454 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -1,72 +1,29 @@ -from cpython.dict cimport PyDict_Size, PyDict_Check -from cpython.tuple cimport PyTuple_Check, PyTuple_GET_SIZE -from cpython.object cimport Py_EQ, Py_NE -from flint.flintlib.fmpz cimport fmpz_init, fmpz_clear, fmpz_is_zero -from flint.flintlib.flint cimport * +from flint.flint_base.flint_base cimport flint_mpoly +from flint.flint_base.flint_base cimport flint_mpoly_context from flint.utils.conversion cimport str_from_chars from flint.utils.typecheck cimport typecheck -from flint.flint_base.flint_base cimport flint_mpoly -from flint.flint_base.flint_base cimport flint_mpoly_context +from flint.utils.flint_exceptions import DomainError, IncompatibleContextError + from flint.types.fmpz cimport any_as_fmpz from flint.types.fmpz cimport fmpz, fmpz_set_any_ref -from flint.types.fmpq_mpoly cimport fmpq_mpoly from flint.types.fmpz_vec cimport fmpz_vec -from flint.utils.flint_exceptions import DomainError, IncompatibleContextError - -cimport cython -cimport libc.stdlib +from flint.flintlib.flint cimport * from flint.flintlib.fmpz cimport fmpz_set from flint.flintlib.fmpz_mpoly cimport * from flint.flintlib.fmpz_mpoly_factor cimport * +from cpython.object cimport Py_EQ, Py_NE +cimport cython +cimport libc.stdlib + cdef extern from *: """ /* An ugly hack to get around the ugly hack of renaming fmpq to avoid a c/python name collision */ typedef fmpq fmpq_struct; """ - -cdef any_as_fmpz_mpoly(x): - cdef fmpz_mpoly res - """ - if typecheck(x, fmpz_poly): - return x - elif typecheck(x, fmpz): - res = fmpz_poly.__new__(fmpz_poly) - fmpz_poly_set_fmpz(res.val, (x).val) - return res - elif PY_MAJOR_VERSION < 3 and PyInt_Check(x): - res = fmpz_poly.__new__(fmpz_poly) - fmpz_poly_set_si(res.val, PyInt_AS_LONG(x)) - return res - elif PyLong_Check(x): - res = fmpz_poly.__new__(fmpz_poly) - t = fmpz(x) - fmpz_poly_set_fmpz(res.val, (t).val) - return res - """ - return NotImplemented - -""" -cdef fmpz_poly_set_list(fmpz_poly_t poly, list val): - cdef long i, n - cdef fmpz_t x - n = PyList_GET_SIZE(val) - fmpz_poly_fit_length(poly, n) - fmpz_init(x) - for i from 0 <= i < n: - if typecheck(val[i], fmpz): - fmpz_poly_set_coeff_fmpz(poly, i, ((val[i])).val) - elif fmpz_set_python(x, val[i]): - fmpz_poly_set_coeff_fmpz(poly, i, x) - else: - fmpz_clear(x) - raise TypeError("unsupported coefficient in list") - fmpz_clear(x) -""" - cdef dict _fmpz_mpoly_ctx_cache = {} @@ -121,7 +78,7 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): return "degrevlex" else: # This should be unreachable - raise ValueError("unknown ordering") + raise ValueError("unknown ordering") # pragma: no cover def gen(self, slong i): """ @@ -163,7 +120,7 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): """ cdef: fmpz_vec exp_vec - slong i, j, nvars = self.nvars() + slong i, nvars = self.nvars() fmpz_mpoly res if not isinstance(d, dict): @@ -220,7 +177,7 @@ cdef class fmpz_mpoly(flint_mpoly): raise ValueError("Dict should be keyed with tuples of integers") ctx = fmpz_mpoly_ctx.get_context(len(k)) x = ctx.from_dict(val) - #XXX this copy is silly, have a ctx function that assigns an fmpz_mpoly_t + # XXX: this copy is silly, have a ctx function that assigns an fmpz_mpoly_t init_fmpz_mpoly(self, ctx) fmpz_mpoly_set(self.val, (x).val, self.ctx.val) elif isinstance(val, str): @@ -263,7 +220,9 @@ cdef class fmpz_mpoly(flint_mpoly): return op == Py_NE elif typecheck(self, fmpz_mpoly) and typecheck(other, fmpz_mpoly): if (self).ctx is (other).ctx: - return (op == Py_NE) ^ bool(fmpz_mpoly_equal((self).val, (other).val, (self).ctx.val)) + return (op == Py_NE) ^ bool( + fmpz_mpoly_equal((self).val, (other).val, (self).ctx.val) + ) else: return op == Py_NE else: @@ -401,16 +360,11 @@ cdef class fmpz_mpoly(flint_mpoly): return res def repr(self): - return self.str() + " (nvars=%s, ordering=%s names=%s)" % (self.ctx.nvars(), self.ctx.ordering(), self.ctx.py_names) + return f"{self.ctx}.from_dict({self.to_dict()})" def str(self): - cdef char * s = fmpz_mpoly_get_str_pretty(self.val, self.ctx.c_names, self.ctx.val) - try: - res = str_from_chars(s) - finally: - libc.stdlib.free(s) - res = res.replace("+", " + ") - res = res.replace("-", " - ") + cdef bytes s = fmpz_mpoly_get_str_pretty(self.val, self.ctx.c_names, self.ctx.val) + res = s.decode().replace("+", " + ").replace("-", " - ") if res.startswith(" - "): res = "-" + res[3:] return res @@ -565,7 +519,7 @@ cdef class fmpz_mpoly(flint_mpoly): raise ValueError("cannot raise fmpz_mpoly to negative power") res = create_fmpz_mpoly(self.ctx) if fmpz_mpoly_pow_fmpz(res.val, (self).val, (other).val, res.ctx.val) == 0: - raise ValueError("unreasonably large polynomial") + raise ValueError("unreasonably large polynomial") # pragma: no cover return res def __divmod__(self, other): @@ -755,7 +709,7 @@ cdef class fmpz_mpoly(flint_mpoly): V = fmpz_vec(args_fmpz, double_indirect=True) vres = fmpz.__new__(fmpz) if fmpz_mpoly_evaluate_all_fmpz(vres.val, self.val, V.double_indirect, self.ctx.val) == 0: - raise ValueError("unreasonably large polynomial") + raise ValueError("unreasonably large polynomial") # pragma: no cover return vres elif kwargs and all_fmpz: # Partial application with args in Z. We evaluate the polynomial one variable at a time @@ -765,7 +719,7 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_set(res2.val, self.val, self.ctx.val) for (i, _), arg in zip(partial_args, args_fmpz): if fmpz_mpoly_evaluate_one_fmpz(res.val, res2.val, i, (arg).val, self.ctx.val) == 0: - raise ValueError("unreasonably large polynomial") + raise ValueError("unreasonably large polynomial") # pragma: no cover fmpz_mpoly_set(res2.val, res.val, self.ctx.val) return res elif args and not all_fmpz: @@ -779,7 +733,7 @@ cdef class fmpz_mpoly(flint_mpoly): C = fmpz_mpoly_vec(args, self.ctx, double_indirect=True) res = create_fmpz_mpoly(self.ctx) if fmpz_mpoly_compose_fmpz_mpoly(res.val, self.val, C.double_indirect, self.ctx.val, res_ctx.val) == 0: - raise ValueError("unreasonably large polynomial") + raise ValueError("unreasonably large polynomial") # pragma: no cover return res else: # Partial function composition. We do this by composing with all arguments, however the ones @@ -804,7 +758,7 @@ cdef class fmpz_mpoly(flint_mpoly): C = fmpz_mpoly_vec(polys, self.ctx, double_indirect=True) res = create_fmpz_mpoly(self.ctx) if fmpz_mpoly_compose_fmpz_mpoly(res.val, self.val, C.double_indirect, self.ctx.val, self.ctx.val) == 0: - raise ValueError("unreasonably large polynomial") + raise ValueError("unreasonably large polynomial") # pragma: no cover return res def factor(self): @@ -884,31 +838,32 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_factor_clear(fac, self.ctx.val) return c, res - def coerce_to_context(self, ctx): - cdef: - fmpz_mpoly res - slong *C - slong i + # TODO: Rethink context conversions, particularly the proposed methods in #132 + # def coerce_to_context(self, ctx): + # cdef: + # fmpz_mpoly res + # slong *C + # slong i - if not typecheck(ctx, fmpz_mpoly_ctx): - raise ValueError("provided context is not a fmpz_mpoly_ctx") + # if not typecheck(ctx, fmpz_mpoly_ctx): + # raise ValueError("provided context is not a fmpz_mpoly_ctx") - if self.ctx is ctx: - return self + # if self.ctx is ctx: + # return self - C = libc.stdlib.malloc(self.ctx.val.minfo.nvars * sizeof(slong *)) - if C is NULL: - raise MemoryError("malloc returned a null pointer") - res = create_fmpz_mpoly(self.ctx) + # C = libc.stdlib.malloc(self.ctx.val.minfo.nvars * sizeof(slong *)) + # if C is NULL: + # raise MemoryError("malloc returned a null pointer") + # res = create_fmpz_mpoly(self.ctx) - vars = {x: i for i, x in enumerate(ctx.py_names)} - for i, var in enumerate(self.ctx.py_names): - C[i] = vars[var] + # vars = {x: i for i, x in enumerate(ctx.py_names)} + # for i, var in enumerate(self.ctx.py_names): + # C[i] = vars[var] - fmpz_mpoly_compose_fmpz_mpoly_gen(res.val, self.val, C, self.ctx.val, (ctx).val) + # fmpz_mpoly_compose_fmpz_mpoly_gen(res.val, self.val, C, self.ctx.val, (ctx).val) - libc.stdlib.free(C) - return res + # libc.stdlib.free(C) + # return res def derivative(self, var): """ @@ -937,7 +892,7 @@ cdef class fmpz_mpoly(flint_mpoly): else: i = vars[var] elif isinstance(var, int): - if not 0 <= i < self.ctx.nvars(): + if not 0 <= var < self.ctx.nvars(): raise IndexError("generator index out of range") i = var else: @@ -976,7 +931,7 @@ cdef class fmpz_mpoly(flint_mpoly): else: i = vars[var] elif isinstance(var, int): - if not 0 <= i < self.ctx.nvars(): + if not 0 <= var < self.ctx.nvars(): raise IndexError("generator index out of range") i = var else: @@ -1007,7 +962,7 @@ cdef class fmpz_mpoly_vec: if double_indirect: self.double_indirect = libc.stdlib.malloc(length * sizeof(fmpz_mpoly_struct *)) if self.double_indirect is NULL: - raise MemoryError("malloc returned a null pointer") + raise MemoryError("malloc returned a null pointer") # pragma: no cover for i in range(length): self.double_indirect[i] = fmpz_mpoly_vec_entry(self.val, i) @@ -1049,21 +1004,15 @@ cdef class fmpz_mpoly_vec: return self.val.length def __str__(self): - return self.str() - - def __repr__(self): - return self.repr() - - def str(self, *args): s = [None] * self.val.length for i in range(self.val.length): x = create_fmpz_mpoly(self.ctx) fmpz_mpoly_set(x.val, fmpz_mpoly_vec_entry(self.val, i), self.ctx.val) - s[i] = x.str(*args) - return str(s) + s[i] = str(x) + return f"[{', '.join(s)}]" - def repr(self, *args): - return f"fmpz_mpoly_vec({self.str(*args)}, {self.val.length})" + def __repr__(self): + return f"fmpz_mpoly_vec({self}, ctx={self.ctx})" def to_tuple(self): return tuple(self[i] for i in range(self.val.length)) From a7eb6f62e5abe784473d331e4e29da196486e053 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Mon, 6 May 2024 21:07:15 +1000 Subject: [PATCH 53/95] Rearrange methods --- src/flint/types/fmpz_mpoly.pyx | 219 ++++++++++++++++----------------- 1 file changed, 109 insertions(+), 110 deletions(-) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 2725e454..61384d2e 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -126,7 +126,6 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): if not isinstance(d, dict): raise ValueError("expected a dictionary") - exp_vec = fmpz_vec(nvars) res = create_fmpz_mpoly(self) for i, (k, v) in enumerate(d.items()): @@ -149,7 +148,7 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): cdef class fmpz_mpoly(flint_mpoly): """ - The *fmpz_poly* type represents sparse multivariate polynomials over + The *fmpz_mpoly* type represents sparse multivariate polynomials over the integers. """ @@ -167,7 +166,7 @@ cdef class fmpz_mpoly(flint_mpoly): init_fmpz_mpoly(self, (val).ctx) fmpz_mpoly_set(self.val, (val).val, self.ctx.val) else: - raise ValueError("Cannot automatically coerce contexts") + raise IncompatibleContextError(f"{ctx} is not {val.ctx}") elif isinstance(val, dict): if ctx is None: if len(val) == 0: @@ -196,23 +195,9 @@ cdef class fmpz_mpoly(flint_mpoly): init_fmpz_mpoly(self, ctx) fmpz_mpoly_set_fmpz(self.val, (v).val, self.ctx.val) - def context(self): - """ - Return the context object for this polynomials. - - >>> ctx = fmpz_mpoly_ctx.get_context(2, "lex", 'x') - >>> p = ctx.from_dict({(0, 1): 2}) - >>> ctx is p.context() - True - """ - return self.ctx - def __bool__(self): return not fmpz_mpoly_is_zero(self.val, self.ctx.val) - def is_one(self): - return fmpz_mpoly_is_one(self.val, self.ctx.val) - def __richcmp__(self, other, int op): if not (op == Py_EQ or op == Py_NE): return NotImplemented @@ -295,80 +280,6 @@ cdef class fmpz_mpoly(flint_mpoly): else: raise TypeError("index is not integer or tuple") - def coefficient(self, slong i): - """ - Return the coefficient at index `i`. - - >>> ctx = fmpz_mpoly_ctx.get_context(2, "lex", 'x') - >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) - >>> p.coefficient(1) - 2 - """ - cdef fmpz v - if i < 0 or i >= fmpz_mpoly_length(self.val, self.ctx.val): - return fmpz(0) - else: - v = fmpz.__new__(fmpz) - fmpz_mpoly_get_term_coeff_fmpz(v.val, self.val, i, self.ctx.val) - return v - - def exponent_tuple(self, slong i): - """ - Return the exponent vector at index `i` as a tuple. - - >>> ctx = fmpz_mpoly_ctx.get_context(2, "lex", 'x') - >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) - >>> p.exponent_tuple(1) - (0, 1) - """ - cdef: - slong nvars = self.ctx.nvars() - - if i < 0 or i >= fmpz_mpoly_length(self.val, self.ctx.val): - 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() - - def degrees(self): - """ - Return a dictionary of variable name to degree. - - >>> ctx = fmpz_mpoly_ctx.get_context(4, "lex", 'x') - >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) - >>> p.degrees() - {'x0': 1, 'x1': 2, 'x2': 3, 'x3': 0} - """ - cdef: - slong nvars = self.ctx.nvars() - - res = fmpz_vec(nvars, double_indirect=True) - fmpz_mpoly_degrees_fmpz(res.double_indirect, self.val, self.ctx.val) - return dict(zip(self.ctx.names(), res.to_tuple())) - - def total_degree(self): - """ - Return the total degree. - - >>> ctx = fmpz_mpoly_ctx.get_context(4, "lex", 'x') - >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) - >>> p.total_degree() - 3 - """ - cdef fmpz res = fmpz() - fmpz_mpoly_total_degree_fmpz(( res).val, self.val, self.ctx.val) - return res - - def repr(self): - return f"{self.ctx}.from_dict({self.to_dict()})" - - def str(self): - cdef bytes s = fmpz_mpoly_get_str_pretty(self.val, self.ctx.c_names, self.ctx.val) - res = s.decode().replace("+", " + ").replace("-", " - ") - if res.startswith(" - "): - res = "-" + res[3:] - return res - def __pos__(self): return self @@ -644,25 +555,6 @@ cdef class fmpz_mpoly(flint_mpoly): def __rmod__(self, other): return divmod(other, self)[1] - def gcd(self, other): - cdef fmpz_mpoly res - if not typecheck(other, fmpz_mpoly): - raise TypeError("argument must be a fmpz_mpoly") - if (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - res = create_fmpz_mpoly(self.ctx) - fmpz_mpoly_gcd(res.val, (self).val, (other).val, res.ctx.val) - return res - - def sqrt(self, assume_perfect_square: bool = False): - cdef fmpz_mpoly res - res = create_fmpz_mpoly(self.ctx) - - if fmpz_mpoly_sqrt_heap(res.val, self.val, self.ctx.val, not assume_perfect_square): - return res - else: - raise ValueError("polynomial is not a perfect square") - def __call__(self, *args, **kwargs): cdef: fmpz_mpoly res @@ -761,6 +653,113 @@ cdef class fmpz_mpoly(flint_mpoly): raise ValueError("unreasonably large polynomial") # pragma: no cover return res + def context(self): + """ + Return the context object for this polynomials. + + >>> ctx = fmpz_mpoly_ctx.get_context(2, "lex", 'x') + >>> p = ctx.from_dict({(0, 1): 2}) + >>> ctx is p.context() + True + """ + return self.ctx + + def is_one(self): + return fmpz_mpoly_is_one(self.val, self.ctx.val) + + def coefficient(self, slong i): + """ + Return the coefficient at index `i`. + + >>> ctx = fmpz_mpoly_ctx.get_context(2, "lex", 'x') + >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) + >>> p.coefficient(1) + 2 + """ + cdef fmpz v + if i < 0 or i >= fmpz_mpoly_length(self.val, self.ctx.val): + return fmpz(0) + else: + v = fmpz.__new__(fmpz) + fmpz_mpoly_get_term_coeff_fmpz(v.val, self.val, i, self.ctx.val) + return v + + def exponent_tuple(self, slong i): + """ + Return the exponent vector at index `i` as a tuple. + + >>> ctx = fmpz_mpoly_ctx.get_context(2, "lex", 'x') + >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) + >>> p.exponent_tuple(1) + (0, 1) + """ + cdef: + slong nvars = self.ctx.nvars() + + if i < 0 or i >= fmpz_mpoly_length(self.val, self.ctx.val): + 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() + + def degrees(self): + """ + Return a dictionary of variable name to degree. + + >>> ctx = fmpz_mpoly_ctx.get_context(4, "lex", 'x') + >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) + >>> p.degrees() + {'x0': 1, 'x1': 2, 'x2': 3, 'x3': 0} + """ + cdef: + slong nvars = self.ctx.nvars() + + res = fmpz_vec(nvars, double_indirect=True) + fmpz_mpoly_degrees_fmpz(res.double_indirect, self.val, self.ctx.val) + return dict(zip(self.ctx.names(), res.to_tuple())) + + def total_degree(self): + """ + Return the total degree. + + >>> ctx = fmpz_mpoly_ctx.get_context(4, "lex", 'x') + >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) + >>> p.total_degree() + 3 + """ + cdef fmpz res = fmpz() + fmpz_mpoly_total_degree_fmpz(( res).val, self.val, self.ctx.val) + return res + + def repr(self): + return f"{self.ctx}.from_dict({self.to_dict()})" + + def str(self): + cdef bytes s = fmpz_mpoly_get_str_pretty(self.val, self.ctx.c_names, self.ctx.val) + res = s.decode().replace("+", " + ").replace("-", " - ") + if res.startswith(" - "): + res = "-" + res[3:] + return res + + def gcd(self, other): + cdef fmpz_mpoly res + if not typecheck(other, fmpz_mpoly): + raise TypeError("argument must be a fmpz_mpoly") + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + res = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_gcd(res.val, (self).val, (other).val, res.ctx.val) + return res + + def sqrt(self, assume_perfect_square: bool = False): + cdef fmpz_mpoly res + res = create_fmpz_mpoly(self.ctx) + + if fmpz_mpoly_sqrt_heap(res.val, self.val, self.ctx.val, not assume_perfect_square): + return res + else: + raise ValueError("polynomial is not a perfect square") + def factor(self): """ Factors self into irreducible factors, returning a tuple From aac76b42a08ff2fa148d56331c396f50a0375caa Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Tue, 7 May 2024 00:33:43 +1000 Subject: [PATCH 54/95] Update fmpq_mpoly, bring to parity with fmpz_mpoly, 100% coverage --- setup.py | 1 + src/flint/__init__.py | 3 +- src/flint/flintlib/fmpq.pxd | 45 +- src/flint/flintlib/fmpq_mpoly.pxd | 52 +- src/flint/test/test_all.py | 133 +++-- src/flint/types/fmpq.pyx | 6 - src/flint/types/fmpq_mpoly.pxd | 6 + src/flint/types/fmpq_mpoly.pyx | 784 ++++++++++++++++++++++-------- src/flint/types/fmpz_mpoly.pyx | 121 ++--- src/flint/types/meson.build | 1 + 10 files changed, 740 insertions(+), 412 deletions(-) diff --git a/setup.py b/setup.py index 507e98b0..d260834e 100644 --- a/setup.py +++ b/setup.py @@ -88,6 +88,7 @@ ("flint.types.fmpz_series", ["src/flint/types/fmpz_series.pyx"]), ("flint.types.fmpq", ["src/flint/types/fmpq.pyx"]), + ("flint.types.fmpq_vec", ["src/flint/types/fmpq_vec.pyx"]), ("flint.types.fmpq_poly", ["src/flint/types/fmpq_poly.pyx"]), ("flint.types.fmpq_mat", ["src/flint/types/fmpq_mat.pyx"]), ("flint.types.fmpq_series", ["src/flint/types/fmpq_series.pyx"]), diff --git a/src/flint/__init__.py b/src/flint/__init__.py index adb25a03..1a230ad7 100644 --- a/src/flint/__init__.py +++ b/src/flint/__init__.py @@ -10,6 +10,7 @@ from .types.fmpq_poly import * from .types.fmpq_mat import * from .types.fmpq_series import * +from .types.fmpq_vec import fmpq_vec from .types.nmod import * from .types.nmod_poly import * @@ -21,7 +22,7 @@ from .types.fmpz_mod_poly import * from .types.fmpz_mod_mat import fmpz_mod_mat -from .types.fmpq_mpoly import fmpq_mpoly_ctx, fmpq_mpoly +from .types.fmpq_mpoly import fmpq_mpoly_ctx, fmpq_mpoly, fmpq_mpoly_vec from .types.fmpz_mpoly_q import * from .types.arf import * diff --git a/src/flint/flintlib/fmpq.pxd b/src/flint/flintlib/fmpq.pxd index e03ddb1d..b9934e50 100644 --- a/src/flint/flintlib/fmpq.pxd +++ b/src/flint/flintlib/fmpq.pxd @@ -1,53 +1,22 @@ from flint.flintlib.flint cimport ulong, flint_rand_t, mp_bitcnt_t, slong, flint_bitcnt_t from flint.flintlib.fmpz cimport fmpz_struct, fmpz_t +cdef extern from *: + """ + /* An ugly hack to get around the ugly hack of renaming fmpq to avoid a c/python name collision */ + typedef fmpq fmpq_struct; + """ + cdef extern from "flint/fmpq.h": ctypedef struct fmpq_struct: fmpz_struct num fmpz_struct den ctypedef fmpq_struct fmpq_t[1] + fmpz_struct * fmpq_numref(fmpq_t x) fmpz_struct * fmpq_denref(fmpq_t x) -# from here on is parsed - void fmpq_init(fmpq_t x) - void fmpq_clear(fmpq_t x) - void fmpq_canonicalise(fmpq_t res) - void _fmpq_canonicalise(fmpz_t num, fmpz_t den) - int fmpq_is_canonical(const fmpq_t x) - int _fmpq_is_canonical(const fmpz_t num, const fmpz_t den) - void fmpq_set(fmpq_t dest, const fmpq_t src) - void fmpq_swap(fmpq_t op1, fmpq_t op2) - void fmpq_neg(fmpq_t dest, const fmpq_t src) - void fmpq_abs(fmpq_t dest, const fmpq_t src) - void fmpq_zero(fmpq_t res) - void fmpq_one(fmpq_t res) - int fmpq_is_zero(const fmpq_t res) - int fmpq_is_one(const fmpq_t res) - int fmpq_is_pm1(const fmpq_t res) - int fmpq_equal(const fmpq_t x, const fmpq_t y) - int fmpq_sgn(const fmpq_t x) - int fmpq_cmp(const fmpq_t x, const fmpq_t y) - int fmpq_cmp_fmpz(const fmpq_t x, const fmpz_t y) - int fmpq_cmp_ui(const fmpq_t x, ulong y) - int fmpq_cmp_si(const fmpq_t x, slong y) - int fmpq_equal_ui(const fmpq_t x, ulong y) - int fmpq_equal_si(const fmpq_t x, slong y) - void fmpq_height(fmpz_t height, const fmpq_t x) - flint_bitcnt_t fmpq_height_bits(const fmpq_t x) - void fmpq_set_fmpz_frac(fmpq_t res, const fmpz_t p, const fmpz_t q) - # void fmpq_get_mpz_frac(mpz_t a, mpz_t b, fmpq_t c) - void fmpq_set_si(fmpq_t res, slong p, ulong q) - void _fmpq_set_si(fmpz_t rnum, fmpz_t rden, slong p, ulong q) - void fmpq_set_ui(fmpq_t res, ulong p, ulong q) - void _fmpq_set_ui(fmpz_t rnum, fmpz_t rden, ulong p, ulong q) - # void fmpq_set_mpq(fmpq_t dest, const mpq_t src) - void fmpq_set_str(fmpq_t dest, const char * s, int base) - # void fmpq_init_set_mpz_frac_readonly(fmpq_t z, const mpz_t p, const mpz_t q) - double fmpq_get_d(const fmpq_t f) - # void fmpq_get_mpq(mpq_t dest, const fmpq_t src) - # int fmpq_get_mpfr(mpfr_t dest, const fmpq_t src, mpfr_rnd_t rnd) # from here on is parsed void fmpq_init(fmpq_t x) void fmpq_clear(fmpq_t x) diff --git a/src/flint/flintlib/fmpq_mpoly.pxd b/src/flint/flintlib/fmpq_mpoly.pxd index 766794de..ccaf77c1 100644 --- a/src/flint/flintlib/fmpq_mpoly.pxd +++ b/src/flint/flintlib/fmpq_mpoly.pxd @@ -1,16 +1,16 @@ -from flint.flintlib.flint cimport flint_rand_t, ulong, slong, mp_limb_t, flint_bitcnt_t, fmpz_struct -from flint.flintlib.fmpq_poly cimport fmpq_poly_struct, fmpq_poly_t +from flint.flintlib.fmpq_poly cimport fmpq_poly_t, fmpq_poly_struct +from flint.flintlib.flint cimport slong, mp_limb_t, ulong, flint_bitcnt_t, fmpz_struct, flint_rand_t from flint.flintlib.fmpz cimport fmpz_t -from flint.flintlib.fmpq cimport fmpq_t, fmpq_struct -from flint.flintlib.mpoly cimport ordering_t from flint.flintlib.fmpz_mpoly cimport fmpz_mpoly_ctx_t, fmpz_mpoly_t, fmpz_mpoly_struct +from flint.flintlib.fmpq cimport fmpq_struct, fmpq_t +from flint.flintlib.mpoly cimport ordering_t # unimported types {'fmpq_mpoly_univar_t'} cdef extern from "flint/fmpq_mpoly.h": ctypedef struct fmpq_mpoly_ctx_struct: - fmpz_mpoly_ctx_t zctx; + fmpz_mpoly_ctx_t zctx ctypedef fmpq_mpoly_ctx_struct fmpq_mpoly_ctx_t[1] @@ -20,6 +20,14 @@ cdef extern from "flint/fmpq_mpoly.h": ctypedef fmpq_mpoly_struct fmpq_mpoly_t[1] + ctypedef struct fmpq_mpoly_univar_struct: + fmpq_mpoly_struct *coeffs + fmpz_struct * exps + slong alloc + slong length + + ctypedef fmpq_mpoly_univar_struct fmpq_mpoly_univar_t[1] + void fmpq_mpoly_ctx_init(fmpq_mpoly_ctx_t ctx, slong nvars, const ordering_t ord) slong fmpq_mpoly_ctx_nvars(const fmpq_mpoly_ctx_t ctx) ordering_t fmpq_mpoly_ctx_ord(const fmpq_mpoly_ctx_t ctx) @@ -38,7 +46,7 @@ cdef extern from "flint/fmpq_mpoly.h": void fmpq_mpoly_gen(fmpq_mpoly_t A, slong var, const fmpq_mpoly_ctx_t ctx) int fmpq_mpoly_is_gen(const fmpq_mpoly_t A, slong var, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_set(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) - int fmpq_mpoly_equal(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_equal(const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_swap(fmpq_mpoly_t A, fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) int fmpq_mpoly_is_fmpq(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_get_fmpq(fmpq_t c, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) @@ -48,8 +56,8 @@ cdef extern from "flint/fmpq_mpoly.h": void fmpq_mpoly_set_si(fmpq_mpoly_t A, slong c, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_zero(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_one(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) - int fmpq_mpoly_equal_fmpq(const fmpq_mpoly_t A, fmpq_t c, const fmpq_mpoly_ctx_t ctx) - int fmpq_mpoly_equal_fmpz(const fmpq_mpoly_t A, fmpz_t c, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_equal_fmpq(const fmpq_mpoly_t A, const fmpq_t c, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_equal_fmpz(const fmpq_mpoly_t A, const fmpz_t c, const fmpq_mpoly_ctx_t ctx) int fmpq_mpoly_equal_ui(const fmpq_mpoly_t A, ulong c, const fmpq_mpoly_ctx_t ctx) int fmpq_mpoly_equal_si(const fmpq_mpoly_t A, slong c, const fmpq_mpoly_ctx_t ctx) int fmpq_mpoly_is_zero(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) @@ -67,9 +75,9 @@ cdef extern from "flint/fmpq_mpoly.h": void fmpq_mpoly_get_coeff_fmpq_monomial(fmpq_t c, const fmpq_mpoly_t A, const fmpq_mpoly_t M, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_set_coeff_fmpq_monomial(fmpq_mpoly_t A, const fmpq_t c, const fmpq_mpoly_t M, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_get_coeff_fmpq_fmpz(fmpq_t c, const fmpq_mpoly_t A, fmpz_struct * const * exp, const fmpq_mpoly_ctx_t ctx) - void fmpq_mpoly_get_coeff_fmpq_ui(fmpq_t c, const fmpq_mpoly_t A, ulong * exp, const fmpq_mpoly_ctx_t ctx) - void fmpq_mpoly_set_coeff_fmpq_fmpz(fmpq_mpoly_t A, const fmpq_t c, fmpz_struct * const * exp, fmpq_mpoly_ctx_t ctx) - void fmpq_mpoly_set_coeff_fmpq_ui(fmpq_mpoly_t A, const fmpq_t c, ulong * exp, fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_get_coeff_fmpq_ui(fmpq_t c, const fmpq_mpoly_t A, const ulong * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_set_coeff_fmpq_fmpz(fmpq_mpoly_t A, const fmpq_t c, fmpz_struct * const * exp, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_set_coeff_fmpq_ui(fmpq_mpoly_t A, const fmpq_t c, const ulong * exp, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_get_coeff_vars_ui(fmpq_mpoly_t C, const fmpq_mpoly_t A, const slong * vars, const ulong * exps, slong length, const fmpq_mpoly_ctx_t ctx) int fmpq_mpoly_cmp(const fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) fmpq_struct * fmpq_mpoly_content_ref(fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) @@ -129,7 +137,7 @@ cdef extern from "flint/fmpq_mpoly.h": void fmpq_mpoly_scalar_div_fmpz(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpz_t c, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_scalar_div_ui(fmpq_mpoly_t A, const fmpq_mpoly_t B, ulong c, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_scalar_div_si(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong c, const fmpq_mpoly_ctx_t ctx) - void fmpq_mpoly_make_monic(fmpq_mpoly_t A, fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_make_monic(fmpq_mpoly_t A, const fmpq_mpoly_t B, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_derivative(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong var, const fmpq_mpoly_ctx_t ctx) void fmpq_mpoly_integral(fmpq_mpoly_t A, const fmpq_mpoly_t B, slong var, const fmpq_mpoly_ctx_t ctx) int fmpq_mpoly_evaluate_all_fmpq(fmpq_t ev, const fmpq_mpoly_t A, fmpq_struct * const * vals, const fmpq_mpoly_ctx_t ctx) @@ -158,13 +166,13 @@ cdef extern from "flint/fmpq_mpoly.h": int fmpq_mpoly_discriminant(fmpq_mpoly_t D, const fmpq_mpoly_t A, slong var, const fmpq_mpoly_ctx_t ctx) int fmpq_mpoly_sqrt(fmpq_mpoly_t Q, const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) int fmpq_mpoly_is_square(const fmpq_mpoly_t A, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_univar_init(fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_univar_clear(fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_univar_swap(fmpq_mpoly_univar_t A, fmpq_mpoly_univar_t B, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_to_univar(fmpq_mpoly_univar_t A, const fmpq_mpoly_t B, slong var, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_from_univar(fmpq_mpoly_t A, const fmpq_mpoly_univar_t B, slong var, const fmpq_mpoly_ctx_t ctx) - # int fmpq_mpoly_univar_degree_fits_si(const fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) - # slong fmpq_mpoly_univar_length(const fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) - # slong fmpq_mpoly_univar_get_term_exp_si(fmpq_mpoly_univar_t A, slong i, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_univar_get_term_coeff(fmpq_mpoly_t c, const fmpq_mpoly_univar_t A, slong i, const fmpq_mpoly_ctx_t ctx) - # void fmpq_mpoly_univar_swap_term_coeff(fmpq_mpoly_t c, fmpq_mpoly_univar_t A, slong i, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_univar_init(fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_univar_clear(fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_univar_swap(fmpq_mpoly_univar_t A, fmpq_mpoly_univar_t B, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_to_univar(fmpq_mpoly_univar_t A, const fmpq_mpoly_t B, slong var, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_from_univar(fmpq_mpoly_t A, const fmpq_mpoly_univar_t B, slong var, const fmpq_mpoly_ctx_t ctx) + int fmpq_mpoly_univar_degree_fits_si(const fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) + slong fmpq_mpoly_univar_length(const fmpq_mpoly_univar_t A, const fmpq_mpoly_ctx_t ctx) + slong fmpq_mpoly_univar_get_term_exp_si(fmpq_mpoly_univar_t A, slong i, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_univar_get_term_coeff(fmpq_mpoly_t c, const fmpq_mpoly_univar_t A, slong i, const fmpq_mpoly_ctx_t ctx) + void fmpq_mpoly_univar_swap_term_coeff(fmpq_mpoly_t c, fmpq_mpoly_univar_t A, slong i, const fmpq_mpoly_ctx_t ctx) diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index d2f526cf..5b7d3d58 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -2643,7 +2643,7 @@ def setbad(obj, i, val): def _all_mpolys(): return [ (flint.fmpz_mpoly, flint.fmpz_mpoly_ctx, flint.fmpz, False), - # (flint.fmpq_mpoly, flint.fmpq_mpoly_ctx, flint.fmpq, True), + (flint.fmpq_mpoly, flint.fmpq_mpoly_ctx, flint.fmpq, True), ] @@ -2663,7 +2663,7 @@ def test_mpolys(): assert raises(lambda: ctx.gen(-1), IndexError) assert raises(lambda: ctx.gen(10), IndexError) - assert raises(lambda: P(val=C.get_context(nvars=1).constant(0), ctx=ctx), ValueError) + assert raises(lambda: P(val=C.get_context(nvars=1).constant(0), ctx=ctx), IncompatibleContextError) assert raises(lambda: P(val={}, ctx=None), ValueError) assert raises(lambda: P(val={"bad": 1}, ctx=None), ValueError) assert raises(lambda: P(val="1", ctx=None), ValueError) @@ -2702,7 +2702,7 @@ def quick_poly(): assert len(mpoly({(0, 0): 1, (0, 1): 1})) == 2 assert len(mpoly({(0, 0): 1, (0, 1): 1, (1, 0): 1})) == 3 - # degree is -1 when 0 poly + # degree is -1 when empty poly assert P(ctx=ctx).degrees() == mpoly({(0, 0): 0}).degrees() == {"x0": -1, "x1": -1} assert P(1, ctx=ctx).degrees() == mpoly({(0, 0): 1}).degrees() == {"x0": 0, "x1": 0} assert mpoly({(0, 0): 1, (0, 1): 1}).degrees() == {"x0": 0, "x1": 1} @@ -2730,6 +2730,11 @@ def quick_poly(): assert P(ctx.from_dict({(0, 1): 3})) == ctx.from_dict({(0, 1): 3}) assert P({(0, 1): 3}) == ctx.from_dict({(0, 1): 3}) + if P is flint.fmpq_mpoly: + ctx_z = flint.fmpz_mpoly_ctx.get_context(2) + assert quick_poly() == P(ctx_z.from_dict({(0, 0): 1, (0, 1): 2, (1, 0): 3, (2, 2): 4})) + assert raises(lambda: P(ctx_z.from_dict({(0, 0): 1}), ctx=ctx), TypeError) + assert raises(lambda: P(ctx=ctx) < P(ctx=ctx), TypeError) assert raises(lambda: P(ctx=ctx) <= P(ctx=ctx), TypeError) assert raises(lambda: P(ctx=ctx) > P(ctx=ctx), TypeError) @@ -2745,8 +2750,8 @@ def quick_poly(): p = quick_poly() assert p.coefficient(2) == S(2) - assert p.coefficient(-1) == S(0) - assert p.coefficient(10) == S(0) + assert raises(lambda: p.coefficient(-1), IndexError) + assert raises(lambda: p.coefficient(10), IndexError) assert p[0] == mpoly({(2, 2): 4}) assert p[3] == mpoly({(0, 0): 1}) @@ -2777,8 +2782,8 @@ def quick_poly(): assert raises(lambda: p.__setitem__(2, None), TypeError) assert raises(lambda: p.__setitem__(-1, 1), IndexError) - assert P(ctx=ctx).repr() == "fmpz_mpoly_ctx(2, 'lex', ('x0', 'x1')).from_dict({})" - assert P(1, ctx=ctx).repr() == "fmpz_mpoly_ctx(2, 'lex', ('x0', 'x1')).from_dict({(0, 0): 1})" + assert P(ctx=ctx).repr() == f"{ctx.__class__.__name__}(2, 'lex', ('x0', 'x1')).from_dict({{}})" + assert P(1, ctx=ctx).repr() == f"{ctx.__class__.__name__}(2, 'lex', ('x0', 'x1')).from_dict({{(0, 0): 1}})" assert str(quick_poly()) == repr(quick_poly()) == '4*x0^2*x1^2 + 3*x0 + 2*x1 + 1' assert p.exponent_tuple(0) == (2, 2) @@ -2884,20 +2889,16 @@ def quick_poly(): assert divmod(quick_poly(), mpoly({(1, 1): 1})) \ == (mpoly({(1, 1): 4}), mpoly({(1, 0): 3, (0, 1): 2, (0, 0): 1})) + assert 1 / P(1, ctx=ctx) == P(1, ctx=ctx) assert quick_poly() / 1 == quick_poly() assert quick_poly() // 1 == quick_poly() assert quick_poly() % 1 == P(ctx=ctx) assert divmod(quick_poly(), 1) == (quick_poly(), P(ctx=ctx)) - assert raises(lambda: quick_poly() / 3, DomainError) - - # TODO FIXME - # if is_field: - # assert P([1, 1]) // 2 == P([S(1)/2, S(1)/2]) - # assert P([1, 1]) % 2 == P([0]) - # else: - # assert P([1, 1]) // 2 == P([0, 0]) - # assert P([1, 1]) % 2 == P([1, 1]) + if is_field: + assert quick_poly() / 3 == mpoly({(0, 0): S(1, 3), (0, 1): S(2, 3), (1, 0): S(1), (2, 2): S(4, 3)}) + else: + assert raises(lambda: quick_poly() / 3, DomainError) assert 1 // quick_poly() == P(ctx=ctx) assert 1 % quick_poly() == P(1, ctx=ctx) @@ -2933,24 +2934,14 @@ def quick_poly(): assert raises(lambda: quick_poly() % P(1, ctx=ctx1), IncompatibleContextError) assert raises(lambda: divmod(quick_poly(), P(1, ctx=ctx1)), IncompatibleContextError) - # TODO FIXME - # # Exact/field scalar division - # if is_field: - # assert P([2, 2]) / 2 == P([1, 1]) - # assert P([1, 2]) / 2 == P([S(1)/2, 1]) - # else: - # assert P([2, 2]) / 2 == P([1, 1]) - # assert raises(lambda: P([1, 2]) / 2, DomainError) - # assert raises(lambda: P([1, 2]) / 0, ZeroDivisionError) - f = mpoly({(1, 1): 4, (0, 0): 1}) g = mpoly({(0, 1): 2, (1, 0): 2}) - assert f \ - * g \ - / mpoly({(0, 1): 1, (1, 0): 1}) \ + assert f * g / mpoly({(0, 1): 1, (1, 0): 1}) \ == mpoly({(1, 1): 8, (0, 0): 2}) - assert raises(lambda: 1 / quick_poly(), DomainError) - assert raises(lambda: quick_poly() / P(2, ctx=ctx), DomainError) + + if not is_field: + assert raises(lambda: 1 / quick_poly(), DomainError) + assert raises(lambda: quick_poly() / P(2, ctx=ctx), DomainError) assert quick_poly() ** 0 == P(1, ctx=ctx) assert quick_poly() ** 1 == quick_poly() @@ -2972,33 +2963,20 @@ def quick_poly(): # # XXX: Not sure what this should do in general: assert raises(lambda: pow(P(1, ctx=ctx), 2, 3), NotImplementedError) - assert (f * g).gcd(f) == f + if is_field: + assert (f * g).gcd(f) == f / 4 + else: + assert (f * g).gcd(f) == f assert raises(lambda: quick_poly().gcd(None), TypeError) assert raises(lambda: quick_poly().gcd(P(ctx=ctx1)), IncompatibleContextError) - # TODO FIXME - # if is_field: - # p1 = P([1, 0, 1]) - # p2 = P([2, 1]) - # g, s, t = P([1]), P([1])/5, P([2, -1])/5 - # assert p1.xgcd(p2) == (g, s, t) - # assert raises(lambda: p1.xgcd(None), TypeError) - assert (f * g).factor() == (S(2), [(mpoly({(0, 1): 1, (1, 0): 1}), 1), (f, 1)]) assert (f * f).sqrt() == f - assert (f * f).sqrt(assume_perfect_square=True) == f + if P is flint.fmpz_mpoly: + assert (f * f).sqrt(assume_perfect_square=True) == f assert raises(lambda: quick_poly().sqrt(), ValueError) - # TODO FIXME - # if P == flint.fmpq_mpoly: - # assert raises(lambda: P([1, 2, 1], 3).sqrt(), ValueError) - # assert P([1, 2, 1], 4).sqrt() == P([1, 1], 2) - - # assert P(ctx=C).deflation() == (P(ctx=C), 1) - # assert P([1, 2]).deflation() == (P([1, 2]), 1) - # assert P([1, 0, 2]).deflation() == (P([1, 2]), 2) - p = quick_poly() assert p.derivative(0) == p.derivative("x0") == mpoly({(0, 0): 3, (1, 2): 8}) assert p.derivative(1) == p.derivative("x1") == mpoly({(0, 0): 2, (2, 1): 8}) @@ -3007,42 +2985,51 @@ def quick_poly(): assert raises(lambda: p.derivative(3), IndexError) assert raises(lambda: p.derivative(None), TypeError) - assert quick_poly().integral(0) == quick_poly().integral("x0") == \ - (6, mpoly({(3, 2): 8, (2, 0): 9, (1, 1): 12, (1, 0): 6})) - assert quick_poly().integral(1) == quick_poly().integral("x1") == \ - (3, mpoly({(2, 3): 4, (1, 1): 9, (0, 2): 3, (0, 1): 3})) + if is_field: + assert quick_poly().integral(0) == quick_poly().integral("x0") == \ + mpoly({(3, 2): S(4, 3), (2, 0): S(3, 2), (1, 1): S(2), (1, 0): S(1)}) + assert quick_poly().integral(1) == quick_poly().integral("x1") == \ + mpoly({(2, 3): S(4, 3), (1, 1): S(3), (0, 2): S(1), (0, 1): S(1)}) + else: + assert quick_poly().integral(0) == quick_poly().integral("x0") == \ + (6, mpoly({(3, 2): 8, (2, 0): 9, (1, 1): 12, (1, 0): 6})) + assert quick_poly().integral(1) == quick_poly().integral("x1") == \ + (3, mpoly({(2, 3): 4, (1, 1): 9, (0, 2): 3, (0, 1): 3})) assert raises(lambda: p.integral("x3"), ValueError) assert raises(lambda: p.integral(3), IndexError) assert raises(lambda: p.integral(None), TypeError) - # if is_field: - # assert P([1, 2, 1]).integral() == P([0, 1, 1, S(1)/3]) + +def _all_mpoly_vecs(): + return [(flint.fmpz_mpoly_ctx, flint.fmpz_mpoly_vec), (flint.fmpq_mpoly_ctx, flint.fmpq_mpoly_vec)] def test_fmpz_mpoly_vec(): - ctx = flint.fmpz_mpoly_ctx.get_context(nvars=2) - ctx1 = flint.fmpz_mpoly_ctx.get_context(nvars=4) + for context, mpoly_vec in _all_mpoly_vecs(): + ctx = context.get_context(nvars=2) + ctx1 = context.get_context(nvars=4) + + vec = mpoly_vec(3, ctx) - vec = flint.fmpz_mpoly_vec(3, ctx) + assert vec[0] == ctx.from_dict({}) + assert vec[1] == ctx.from_dict({}) - assert vec[0] == ctx.from_dict({}) - assert vec[1] == ctx.from_dict({}) - assert vec[2] == ctx.from_dict({}) + assert vec[2] == ctx.from_dict({}) - assert len(vec) == 3 - assert str(vec) == f"[{', '.join(str(ctx.from_dict({})) for _ in range(3))}]" - assert repr(vec) == f"fmpz_mpoly_vec([{', '.join(str(ctx.from_dict({})) for _ in range(3))}], ctx={str(ctx)})" + assert len(vec) == 3 + assert str(vec) == f"[{', '.join(str(ctx.from_dict({})) for _ in range(3))}]" + assert repr(vec) == f"{mpoly_vec.__name__}([{', '.join(str(ctx.from_dict({})) for _ in range(3))}], ctx={str(ctx)})" - assert raises(lambda: vec[None], TypeError) - assert raises(lambda: vec[-1], IndexError) + assert raises(lambda: vec[None], TypeError) + assert raises(lambda: vec[-1], IndexError) - vec[1] = ctx.from_dict({(1, 1): 1}) - assert vec.to_tuple() == flint.fmpz_mpoly_vec([ctx.from_dict({}), ctx.from_dict({(1, 1): 1}), ctx.from_dict({})], ctx).to_tuple() - assert raises(lambda: vec.__setitem__(None, 0), TypeError) - assert raises(lambda: vec.__setitem__(-1, 0), IndexError) - assert raises(lambda: vec.__setitem__(0, 0), TypeError) - assert raises(lambda: vec.__setitem__(0, ctx1.from_dict({})), IncompatibleContextError) + vec[1] = ctx.from_dict({(1, 1): 1}) + assert vec.to_tuple() == mpoly_vec([ctx.from_dict({}), ctx.from_dict({(1, 1): 1}), ctx.from_dict({})], ctx).to_tuple() + assert raises(lambda: vec.__setitem__(None, 0), TypeError) + assert raises(lambda: vec.__setitem__(-1, 0), IndexError) + assert raises(lambda: vec.__setitem__(0, 0), TypeError) + assert raises(lambda: vec.__setitem__(0, ctx1.from_dict({})), IncompatibleContextError) def _all_matrices(): diff --git a/src/flint/types/fmpq.pyx b/src/flint/types/fmpq.pyx index 17ef7d91..49c008b5 100644 --- a/src/flint/types/fmpq.pyx +++ b/src/flint/types/fmpq.pyx @@ -14,12 +14,6 @@ from flint.flintlib.fmpz cimport fmpz_clear from flint.flintlib.fmpq cimport * from flint.flintlib.bernoulli cimport * -cdef extern from *: - """ - /* An ugly hack to get around the ugly hack of renaming fmpq to avoid a c/python name collision */ - typedef fmpq fmpq_struct; - """ - cdef int fmpq_set_any_ref(fmpq_t x, obj): cdef int status fmpq_init(x) diff --git a/src/flint/types/fmpq_mpoly.pxd b/src/flint/types/fmpq_mpoly.pxd index d01e8d7a..884646e1 100644 --- a/src/flint/types/fmpq_mpoly.pxd +++ b/src/flint/types/fmpq_mpoly.pxd @@ -28,3 +28,9 @@ cdef class fmpq_mpoly(flint_mpoly): cdef fmpq_mpoly_t val cdef fmpq_mpoly_ctx ctx cdef bint _init + +cdef class fmpq_mpoly_vec: + cdef fmpq_mpoly_struct *val + cdef slong length + cdef fmpq_mpoly_ctx ctx + cdef fmpq_mpoly_struct **double_indirect diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 6844793e..1e52dc59 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -1,30 +1,25 @@ -from cpython.dict cimport PyDict_Size, PyDict_Check, PyDict_Next -from cpython.tuple cimport PyTuple_Check, PyTuple_GET_SIZE - -from flint.types.fmpq cimport any_as_fmpq, fmpq, fmpq_set_any_ref -from flint.types.fmpz cimport fmpz, fmpz_set_any_ref, any_as_fmpz -from flint.types.fmpz_mpoly cimport fmpz_mpoly, fmpz_mpoly_ctx +from flint.flint_base.flint_base cimport flint_mpoly +from flint.flint_base.flint_base cimport flint_mpoly_context from flint.utils.typecheck cimport typecheck -from flint.utils.conversion cimport str_from_chars +from flint.utils.flint_exceptions import IncompatibleContextError -from flint.flint_base.flint_base cimport flint_mpoly_context +from flint.types.fmpq cimport fmpq, any_as_fmpq +from flint.types.fmpz_vec cimport fmpz_vec +from flint.types.fmpq_vec cimport fmpq_vec -from flint.flintlib.flint cimport FMPZ_UNKNOWN -from flint.flintlib.fmpq cimport fmpq_init, fmpq_clear, fmpq_is_zero, fmpq_set, fmpq_one -from flint.flintlib.fmpz cimport fmpz_clear, fmpz_init, fmpz_set -from flint.flintlib.fmpz_mpoly cimport fmpz_mpoly_set -from flint.flintlib.fmpq_mpoly_factor cimport fmpq_mpoly_factor_t, \ - fmpq_mpoly_factor_init, fmpq_mpoly_factor, fmpq_mpoly_factor_clear, \ - fmpq_mpoly_factor_squarefree +from flint.types.fmpz cimport fmpz, any_as_fmpz +from flint.types.fmpz_mpoly cimport fmpz_mpoly -cdef extern from *: - """ - /* An ugly hack to get around the ugly hack of renaming fmpq to avoid a c/python name collision */ - typedef fmpq fmpq_struct; - """ +from flint.flintlib.flint cimport * +from flint.flintlib.fmpq cimport fmpq_set, fmpq_one +from flint.flintlib.fmpq_mpoly cimport * +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 cpython.object cimport Py_EQ, Py_NE cimport cython cimport libc.stdlib @@ -77,10 +72,13 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): """ if self.val.zctx.minfo.ord == ordering_t.ORD_LEX: return "lex" - if self.val.zctx.minfo.ord == ordering_t.ORD_DEGLEX: + elif self.val.zctx.minfo.ord == ordering_t.ORD_DEGLEX: return "deglex" - if self.val.zctx.minfo.ord == ordering_t.ORD_DEGREVLEX: + elif self.val.zctx.minfo.ord == ordering_t.ORD_DEGREVLEX: return "degrevlex" + else: + # This should be unreachable + raise ValueError("unknown ordering") # pragma: no cover def gen(self, slong i): """ @@ -91,11 +89,9 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): z1 """ cdef fmpq_mpoly res - assert i >= 0 and i < self.val.zctx.minfo.nvars - res = fmpq_mpoly.__new__(fmpq_mpoly) - res.ctx = self - fmpq_mpoly_init(res.val, res.ctx.val) - res._init = True + if not 0 <= i < self.val.zctx.minfo.nvars: + raise IndexError("generator index out of range") + res = create_fmpq_mpoly(self) fmpq_mpoly_gen(res.val, i, res.ctx.val) return res @@ -106,95 +102,52 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): cdef fmpq_mpoly res z = any_as_fmpq(z) if z is NotImplemented: - raise ValueError("A constant fmpq_mpoly is a fmpq") - res = fmpq_mpoly.__new__(fmpq_mpoly) - res.ctx = self - fmpq_mpoly_init(res.val, res.ctx.val) - res._init = True + raise TypeError("cannot coerce argument to fmpq") + res = create_fmpq_mpoly(self) fmpq_mpoly_set_fmpq(res.val, (z).val, res.ctx.val) return res - def fmpq_mpoly_from_dict(self, d): + def from_dict(self, d): """ - Create a fmpz_mpoly from a dictionary. + Create a fmpq_mpoly from a dictionary. The dictionary's keys are tuples of ints (or anything that implicitly converts to fmpz) representing exponents, and corresponding coefficient values of fmpq. >>> ctx = fmpq_mpoly_ctx.get_context(2,'lex','x,y') - >>> ctx.fmpq_mpoly_from_dict({(1,0):2, (1,1):3, (0,1):1}) + >>> ctx.from_dict({(1,0):2, (1,1):3, (0,1):1}) 3*x*y + 2*x + y """ - cdef long n - cdef fmpq_t coefficient - cdef int xtype - cdef fmpz_struct *exponents - cdef fmpz_struct **exp_ptr - cdef int nvars = self.nvars() - cdef int i,j - cdef int count - cdef fmpq_mpoly res + cdef: + fmpz_vec exp_vec + slong i, nvars = self.nvars() + fmpq_mpoly res - if not PyDict_Check(d): + if not isinstance(d, dict): raise ValueError("expected a dictionary") - n = PyDict_Size(d) - exponents = libc.stdlib.calloc(nvars, sizeof(fmpz_struct)) - if exponents == NULL: - raise MemoryError() - exp_ptr = libc.stdlib.calloc(nvars, sizeof(fmpz_struct *)) - if exp_ptr == NULL: - libc.stdlib.free(exponents) - raise MemoryError() - for i in range(nvars): - fmpz_init(exponents + i) - exp_ptr[i] = exponents + i - # fmpq_init(coefficient) - res = fmpq_mpoly.__new__(fmpq_mpoly) - res.ctx = self - fmpq_mpoly_init(res.val, res.ctx.val) - res._init = True - count = 0 - for k, v in d.items(): - xtype = fmpq_set_any_ref(coefficient, v) - if xtype == FMPZ_UNKNOWN: - for i in range(nvars): - fmpz_clear(exponents + i) - libc.stdlib.free(exponents) - libc.stdlib.free(exp_ptr) - raise TypeError("invalid coefficient type %s" % type(v)) - if not PyTuple_Check(k): - for i in range(nvars): - fmpz_clear(exponents + i) - libc.stdlib.free(exponents) - raise TypeError("Expected tuple of ints as key not %s" % type(k)) - if PyTuple_GET_SIZE(k) != nvars: - for i in range(nvars): - fmpz_clear(exponents + i) - libc.stdlib.free(exponents) - raise TypeError("Expected exponent tuple of length %d" % nvars) - for i, tup in enumerate(k): - xtype = fmpz_set_any_ref(exponents + i, tup) - if xtype == FMPZ_UNKNOWN: - for i in range(nvars): - fmpz_clear(exponents + i) - libc.stdlib.free(exponents) - raise TypeError("Invalid exponent type %s" % type(tup)) - #Todo lobby for fmpz_mpoly_push_term_fmpz_ffmpz - if not fmpq_is_zero(coefficient): - fmpq_mpoly_push_term_fmpq_fmpz(res.val, coefficient, exp_ptr, self.val) - # _fmpq_mpoly_push_exp_ffmpz(res.val, exponents, self.val) - # fmpq_mpoly_set_term_coeff_fmpz(res.val, count, coefficient, self.val) - count += 1 - for i in range(nvars): - fmpz_clear(exponents + i) - fmpq_mpoly_sort_terms(res.val, self.val) - fmpq_mpoly_reduce(res.val, self.val) + + res = create_fmpq_mpoly(self) + + for i, (k, v) in enumerate(d.items()): + v = any_as_fmpq(v) + if v is NotImplemented: + raise TypeError(f"cannot coerce coefficient '{v}' to fmpq") + elif len(k) != nvars: + raise ValueError(f"expected {nvars} exponents, got {len(k)}") + + exp_vec = fmpz_vec(k) + + if v: + fmpq_mpoly_push_term_fmpq_ffmpz(res.val, (v).val, exp_vec.val, self.val) + + fmpq_mpoly_sort_terms(res.val, self.val) + fmpq_mpoly_reduce(res.val, self.val) return res cdef class fmpq_mpoly(flint_mpoly): """ - The *fmpz_poly* type represents sparse multivariate polynomials over + The *fmpq_mpoly* type represents sparse multivariate polynomials over the integers. """ @@ -212,12 +165,12 @@ cdef class fmpq_mpoly(flint_mpoly): init_fmpq_mpoly(self, (val).ctx) fmpq_mpoly_set(self.val, (val).val, self.ctx.val) else: - raise ValueError("Cannot automatically coerce contexts") - if typecheck(val, fmpz_mpoly): + raise IncompatibleContextError(f"{ctx} is not {(val).ctx}") + elif typecheck(val, fmpz_mpoly): if ctx is None: ctx = fmpq_mpoly_ctx.from_context((val).ctx) - elif ctx != fmpq_mpoly_ctx.from_context((val).ctx): - raise ValueError("Cannot automatically coerce contexts") + elif not typecheck(ctx, fmpz_mpoly_ctx): + raise TypeError(f"{ctx} is not a fmpz_mpoly_ctx") init_fmpq_mpoly(self, ctx) fmpz_mpoly_set(self.val.zpoly, (val).val, ( val).ctx.val) fmpq_one(self.val.content) @@ -230,14 +183,14 @@ cdef class fmpq_mpoly(flint_mpoly): if not isinstance(k, tuple): raise ValueError("Dict should be keyed with tuples of integers") ctx = fmpq_mpoly_ctx.get_context(len(k)) - x = ctx.fmpq_mpoly_from_dict(val) - #XXX this copy is silly, have a ctx function that assigns an fmpz_mpoly_t + x = ctx.from_dict(val) + # XXX: this copy is silly, have a ctx function that assigns an fmpz_mpoly_t init_fmpq_mpoly(self, ctx) fmpq_mpoly_set(self.val, (x).val, self.ctx.val) elif isinstance(val, str): if ctx is None: raise ValueError("Cannot parse a polynomial without context") - val = bytes(val, 'utf-8') + val = val.encode("ascii") init_fmpq_mpoly(self, ctx) fmpq_mpoly_set_str_pretty(self.val, val, self.ctx.c_names, self.ctx.val) fmpq_mpoly_sort_terms(self.val, self.ctx.val) @@ -250,77 +203,93 @@ cdef class fmpq_mpoly(flint_mpoly): init_fmpq_mpoly(self, ctx) fmpq_mpoly_set_fmpq(self.val, (v).val, self.ctx.val) - def __nonzero__(self): - return not fmpq_mpoly_is_zero(self.val, self.ctx.val) - def __bool__(self): return not fmpq_mpoly_is_zero(self.val, self.ctx.val) - def is_one(self): - return fmpq_mpoly_is_one(self.val, self.ctx.val) - def __richcmp__(self, other, int op): - if op != 2 and op != 3: + if not (op == Py_EQ or op == Py_NE): return NotImplemented - if typecheck(self, fmpq_mpoly) and typecheck(other, fmpq_mpoly): + elif other is None: + return op == Py_NE + elif typecheck(self, fmpq_mpoly) and typecheck(other, fmpq_mpoly): if (self).ctx is (other).ctx: - if op == 2: - return bool(fmpq_mpoly_equal((self).val, (other).val, (self).ctx.val)) - else: - return not bool(fmpq_mpoly_equal((self).val, (other).val, (self).ctx.val)) + return (op == Py_NE) ^ bool( + fmpq_mpoly_equal((self).val, (other).val, (self).ctx.val) + ) else: - if op == 2: - return False - else: - return True - if op == 2: - return not bool(self - other) + return op == Py_NE else: - return bool(self - other) - - def context(self): - return self.ctx + return NotImplemented def __len__(self): return fmpq_mpoly_length(self.val, self.ctx.val) - def coefficient(self, slong i): - cdef fmpq v - if i < 0 or i >= fmpq_mpoly_length(self.val, self.ctx.val): - return fmpq(0) + def __getitem__(self, x): + """ + Return the term at index `x` if `x` is an `int`, or the term with the exponent + vector `x` if `x` is a tuple of `int`s or `fmpq`s. + + >>> ctx = fmpq_mpoly_ctx.get_context(2, "lex", 'x') + >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) + >>> p[1] + 2*x1 + >>> p[1, 1] + 3 + + """ + cdef: + slong nvars = self.ctx.nvars() + + if isinstance(x, int): + if not 0 <= x < fmpq_mpoly_length(self.val, self.ctx.val): + raise IndexError("term index out of range") + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_get_term(( res).val, self.val, x, self.ctx.val) + return res + elif isinstance(x, tuple): + if len(x) != nvars: + raise ValueError("exponent vector provided does not match number of variables") + res = fmpq() + exp_vec = fmpz_vec(x, double_indirect=True) + fmpq_mpoly_get_coeff_fmpq_fmpz((res).val, self.val, exp_vec.double_indirect, self.ctx.val) + return res else: - v = fmpq.__new__(fmpz) - fmpq_mpoly_get_term_coeff_fmpq(v.val, self.val, i, self.ctx.val) - return v + raise TypeError("index is not integer or tuple") - def exponent_tuple(self, slong i): - cdef slong j, nvars - cdef fmpz_struct ** tmp - if i < 0 or i >= fmpq_mpoly_length(self.val, self.ctx.val): - raise ValueError - nvars = self.ctx.nvars() - res = tuple(fmpz() for j in range(nvars)) - tmp = libc.stdlib.malloc(nvars * sizeof(fmpz_struct **)) - try: - for j in range(nvars): - tmp[j] = &(( (res[j])).val[0]) - fmpq_mpoly_get_term_exp_fmpz(tmp, self.val, i, self.ctx.val) - finally: - libc.stdlib.free(tmp) - return res + def __setitem__(self, x, y): + """ + Set the coefficient at index `x` to `y` if `x` is an `int`, or the term with + the exponent vector `x` if `x` is a tuple of `int`s or `fmpq`s. - def repr(self): - return self.str() + " (nvars=%s, ordering=%s names=%s)" % (self.ctx.nvars(), self.ctx.ordering(), self.ctx.py_names) + >>> ctx = fmpq_mpoly_ctx.get_context(2, "lex", 'x') + >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) + >>> p[1] = 10 + >>> p[1, 1] = 20 + >>> p + 20*x0*x1 + 10*x1 - def str(self): - cdef char * s = fmpq_mpoly_get_str_pretty(self.val, self.ctx.c_names, self.ctx.val) - try: - res = str_from_chars(s) - finally: - libc.stdlib.free(s) - if res.startswith(" - "): - res = "-" + res[3:] - return res + """ + cdef: + slong nvars = self.ctx.nvars() + + coeff = any_as_fmpq(y) + if coeff is NotImplemented: + raise TypeError("provided coefficient not coercible to fmpq") + + if isinstance(x, int): + if not 0 <= x < fmpq_mpoly_length(self.val, self.ctx.val): + raise IndexError("term index out of range") + fmpq_mpoly_set_term_coeff_fmpq(self.val, x, (coeff).val, self.ctx.val) + elif isinstance(x, tuple): + if len(x) != nvars: + raise ValueError("exponent vector provided does not match number of variables") + exp_vec = fmpz_vec(x, double_indirect=True) + fmpq_mpoly_set_coeff_fmpq_fmpz(self.val, (coeff).val, exp_vec.double_indirect, self.ctx.val) + else: + raise TypeError("index is not integer or tuple") + + def __pos__(self): + return self def __neg__(self): cdef fmpq_mpoly res @@ -332,7 +301,7 @@ cdef class fmpq_mpoly(flint_mpoly): cdef fmpq_mpoly res if typecheck(other, fmpq_mpoly): if (self).ctx is not (other).ctx: - return NotImplemented + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") res = create_fmpq_mpoly(self.ctx) fmpq_mpoly_add(res.val, (self).val, (other).val, res.ctx.val) return res @@ -356,7 +325,7 @@ cdef class fmpq_mpoly(flint_mpoly): def __iadd__(self, other): if typecheck(other, fmpq_mpoly): if (self).ctx is not (other).ctx: - return NotImplemented + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") fmpq_mpoly_add((self).val, (self).val, (other).val, self.ctx.val) return self else: @@ -370,7 +339,7 @@ cdef class fmpq_mpoly(flint_mpoly): cdef fmpq_mpoly res if typecheck(other, fmpq_mpoly): if (self).ctx is not (other).ctx: - return NotImplemented + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") res = create_fmpq_mpoly(self.ctx) fmpq_mpoly_sub(res.val, (self).val, (other).val, res.ctx.val) return res @@ -394,7 +363,7 @@ cdef class fmpq_mpoly(flint_mpoly): def __isub__(self, other): if typecheck(other, fmpq_mpoly): if (self).ctx is not (other).ctx: - return NotImplemented + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") fmpq_mpoly_sub((self).val, (self).val, (other).val, self.ctx.val) return self else: @@ -408,7 +377,7 @@ cdef class fmpq_mpoly(flint_mpoly): cdef fmpq_mpoly res if typecheck(other, fmpq_mpoly): if (self).ctx is not (other).ctx: - return NotImplemented + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") res = create_fmpq_mpoly(self.ctx) fmpq_mpoly_mul(res.val, (self).val, (other).val, res.ctx.val) return res @@ -432,7 +401,7 @@ cdef class fmpq_mpoly(flint_mpoly): def __imul__(self, other): if typecheck(other, fmpq_mpoly): if (self).ctx is not (other).ctx: - return NotImplemented + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") fmpq_mpoly_mul((self).val, (self).val, (other).val, self.ctx.val) return self else: @@ -450,17 +419,19 @@ cdef class fmpq_mpoly(flint_mpoly): if other is NotImplemented: return other if other < 0: - raise ValueError("cannot raise fmpz_mpoly to negative power") + raise ValueError("cannot raise fmpq_mpoly to negative power") res = create_fmpq_mpoly(self.ctx) if fmpq_mpoly_pow_fmpz(res.val, (self).val, (other).val, res.ctx.val) == 0: - raise ValueError("unreasonably large polynomial") + raise ValueError("unreasonably large polynomial") # pragma: no cover return res def __divmod__(self, other): cdef fmpq_mpoly res, res2 if typecheck(other, fmpq_mpoly): - if (self).ctx is not (other).ctx: - return NotImplemented + if not other: + raise ZeroDivisionError("fmpq_mpoly division by zero") + elif (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") res = create_fmpq_mpoly(self.ctx) res2 = create_fmpq_mpoly(self.ctx) fmpq_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) @@ -468,7 +439,9 @@ cdef class fmpq_mpoly(flint_mpoly): else: other = any_as_fmpq(other) if other is not NotImplemented: - other= fmpq_mpoly(other, self.ctx) + other = fmpq_mpoly(other, self.ctx) + if not other: + raise ZeroDivisionError("fmpq_mpoly division by zero") res = create_fmpq_mpoly(self.ctx) res2 = create_fmpq_mpoly(self.ctx) fmpq_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) @@ -477,26 +450,32 @@ cdef class fmpq_mpoly(flint_mpoly): def __rdivmod__(self, other): cdef fmpq_mpoly res, res2 + if not self: + raise ZeroDivisionError("fmpq_mpoly division by zero") other = any_as_fmpq(other) if other is not NotImplemented: other = fmpq_mpoly(other, self.ctx) res = create_fmpq_mpoly(self.ctx) res2 = create_fmpq_mpoly(self.ctx) fmpq_mpoly_divrem(res.val, res2.val, (other).val, (self).val, res.ctx.val) - return res + return (res, res2) return NotImplemented def __floordiv__(self, other): cdef fmpq_mpoly res if typecheck(other, fmpq_mpoly): - if (self).ctx is not (other).ctx: - return NotImplemented + if not other: + raise ZeroDivisionError("fmpq_mpoly division by zero") + elif (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") res = create_fmpq_mpoly(self.ctx) fmpq_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) return res else: other = any_as_fmpq(other) if other is not NotImplemented: + if not other: + raise ZeroDivisionError("fmpq_mpoly division by zero") other = fmpq_mpoly(other, self.ctx) res = create_fmpq_mpoly(self.ctx) fmpq_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) @@ -505,6 +484,8 @@ cdef class fmpq_mpoly(flint_mpoly): def __rfloordiv__(self, other): cdef fmpq_mpoly res + if not self: + raise ZeroDivisionError("fmpq_mpoly division by zero") other = any_as_fmpq(other) if other is not NotImplemented: other = fmpq_mpoly(other, self.ctx) @@ -513,18 +494,264 @@ cdef class fmpq_mpoly(flint_mpoly): return res return NotImplemented + def __truediv__(self, other): + cdef: + fmpq_mpoly res + + if typecheck(other, fmpq_mpoly): + res, r = divmod(self, other) + return res + r + else: + o = any_as_fmpq(other) + if o is NotImplemented: + return NotImplemented + elif not o: + raise ZeroDivisionError("fmpq_mpoly division by zero") + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_scalar_div_fmpq(res.val, self.val, (o).val, self.ctx.val) + return res + + def __rtruediv__(self, other): + cdef fmpq_mpoly res + if not self: + raise ZeroDivisionError("fmpq_mpoly division by zero") + o = any_as_fmpq(other) + if o is NotImplemented: + return NotImplemented + res = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_set_fmpq(res.val, (o).val, self.ctx.val) + return res / self + def __mod__(self, other): return divmod(self, other)[1] + def __rmod__(self, other): + return divmod(other, self)[1] + + def __call__(self, *args, **kwargs): + cdef: + fmpq_mpoly res + fmpq_mpoly res2 + fmpq_mpoly_ctx res_ctx + fmpq_vec V + fmpq vres + fmpq_mpoly_vec C + slong i, nvars = self.ctx.nvars(), nargs = len(args) + + if not args and not kwargs: + return self + elif args and kwargs: + raise ValueError("only supply positional or keyword arguments") + elif len(args) < nvars and not kwargs: + raise ValueError("partial application requires keyword arguments") + + if kwargs: + # Sort and filter the provided keyword args to only valid ones + partial_args = tuple((i, kwargs[x]) for i, x in enumerate(self.ctx.names()) if x in kwargs) + nargs = len(partial_args) + + # If we've been provided with an invalid keyword arg then the length of our filter + # args will be less than what we've been provided with. + # If the length is equal to the number of variables then all arguments have been provided + # otherwise we need to do partial application + if nargs < len(kwargs): + raise ValueError("unknown keyword argument provided") + elif nargs == nvars: + args = tuple(arg for _, arg in partial_args) + kwargs = None + elif nargs > nvars: + raise ValueError("more arguments provided than variables") + + if kwargs: + args_fmpq = tuple(any_as_fmpq(v) for _, v in partial_args) + else: + args_fmpq = tuple(any_as_fmpq(v) for v in args) + + all_fmpq = NotImplemented not in args_fmpq + + if args and all_fmpq: + # Normal evaluation + V = fmpq_vec(args_fmpq, double_indirect=True) + vres = fmpq.__new__(fmpq) + if fmpq_mpoly_evaluate_all_fmpq(vres.val, self.val, V.double_indirect, self.ctx.val) == 0: + raise ValueError("unreasonably large polynomial") # pragma: no cover + return vres + elif kwargs and all_fmpq: + # Partial application with args in Z. We evaluate the polynomial one variable at a time + res = create_fmpq_mpoly(self.ctx) + res2 = create_fmpq_mpoly(self.ctx) + + fmpq_mpoly_set(res2.val, self.val, self.ctx.val) + for (i, _), arg in zip(partial_args, args_fmpq): + if fmpq_mpoly_evaluate_one_fmpq(res.val, res2.val, i, (arg).val, self.ctx.val) == 0: + raise ValueError("unreasonably large polynomial") # pragma: no cover + fmpq_mpoly_set(res2.val, res.val, self.ctx.val) + return res + elif args and not all_fmpq: + # Complete function composition + if not all(typecheck(arg, fmpq_mpoly) for arg in args): + raise TypeError("all arguments must be fmpq_mpolys") + res_ctx = ( args[0]).ctx + if not all(( args[i]).ctx is res_ctx for i in range(1, len(args))): + raise IncompatibleContextError("all arguments must share the same context") + + C = fmpq_mpoly_vec(args, self.ctx, double_indirect=True) + res = create_fmpq_mpoly(self.ctx) + if fmpq_mpoly_compose_fmpq_mpoly(res.val, self.val, C.double_indirect, self.ctx.val, res_ctx.val) == 0: + raise ValueError("unreasonably large polynomial") # pragma: no cover + return res + else: + # Partial function composition. We do this by composing with all arguments, however the ones + # that have not be provided are set to the trivial monomial. This is why we require all the + # polynomial itself, and all arguments exist in the same context, otherwise we have no way of + # finding the correct monomial to use. + if not all(typecheck(arg, fmpq_mpoly) for _, arg in partial_args): + raise TypeError("all arguments must be fmpq_mpolys for partial composition") + if not all(( arg).ctx is self.ctx for _, arg in partial_args): + raise IncompatibleContextError("all arguments must share the same context") + + polys = [None] * nvars + for i, poly in partial_args: + polys[i] = poly + + for i in range(nvars): + if polys[i] is None: + vec = [0] * nvars + vec[i] = 1 + polys[i] = self.ctx.from_dict({tuple(vec): 1}) + + C = fmpq_mpoly_vec(polys, self.ctx, double_indirect=True) + res = create_fmpq_mpoly(self.ctx) + if fmpq_mpoly_compose_fmpq_mpoly(res.val, self.val, C.double_indirect, self.ctx.val, self.ctx.val) == 0: + raise ValueError("unreasonably large polynomial") # pragma: no cover + return res + + def context(self): + """ + Return the context object for this polynomials. + + >>> ctx = fmpq_mpoly_ctx.get_context(2, "lex", 'x') + >>> p = ctx.from_dict({(0, 1): 2}) + >>> ctx is p.context() + True + """ + return self.ctx + + def is_one(self): + return fmpq_mpoly_is_one(self.val, self.ctx.val) + + def coefficient(self, slong i): + """ + Return the coefficient at index `i`. + + >>> ctx = fmpq_mpoly_ctx.get_context(2, "lex", 'x') + >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) + >>> p.coefficient(1) + 2 + """ + cdef fmpq v + if not 0 <= i < fmpq_mpoly_length(self.val, self.ctx.val): + raise IndexError("term index out of range") + else: + v = fmpq.__new__(fmpq) + fmpq_mpoly_get_term_coeff_fmpq(v.val, self.val, i, self.ctx.val) + return v + + def exponent_tuple(self, slong i): + """ + Return the exponent vector at index `i` as a tuple. + + >>> ctx = fmpq_mpoly_ctx.get_context(2, "lex", 'x') + >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) + >>> p.exponent_tuple(1) + (0, 1) + """ + cdef: + slong nvars = self.ctx.nvars() + + if not 0 <= i < fmpq_mpoly_length(self.val, self.ctx.val): + 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() + + def degrees(self): + """ + Return a dictionary of variable name to degree. + + >>> ctx = fmpq_mpoly_ctx.get_context(4, "lex", 'x') + >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) + >>> p.degrees() + {'x0': 1, 'x1': 2, 'x2': 3, 'x3': 0} + """ + cdef: + slong nvars = self.ctx.nvars() + + res = fmpz_vec(nvars, double_indirect=True) + fmpq_mpoly_degrees_fmpz(res.double_indirect, self.val, self.ctx.val) + return dict(zip(self.ctx.names(), res.to_tuple())) + + def total_degree(self): + """ + Return the total degree. + + >>> ctx = fmpq_mpoly_ctx.get_context(4, "lex", 'x') + >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) + >>> p.total_degree() + 3 + """ + cdef fmpz res = fmpz() + fmpq_mpoly_total_degree_fmpz(( res).val, self.val, self.ctx.val) + return res + + def repr(self): + return f"{self.ctx}.from_dict({self.to_dict()})" + + def str(self): + cdef bytes s = fmpq_mpoly_get_str_pretty(self.val, self.ctx.c_names, self.ctx.val) + # res = s.decode().replace("+", " + ").replace("-", " - ") + res = s.decode() + if res.startswith("- "): + res = "-" + res[3:] + return res + def gcd(self, other): + """ + Return the gcd of self and other. + + >>> ctx = fmpq_mpoly_ctx.get_context(2, "lex", 'x') + >>> f = ctx.from_dict({(1, 1): 4, (0, 0): 1}) + >>> g = ctx.from_dict({(0, 1): 2, (1, 0): 2}) + >>> (f * g).gcd(f) + x0*x1 + 1/4 + + """ cdef fmpq_mpoly res - assert isinstance(other, fmpq_mpoly) - if (self).ctx is not (other).ctx: - return NotImplemented + 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}") res = create_fmpq_mpoly(self.ctx) fmpq_mpoly_gcd(res.val, (self).val, (other).val, res.ctx.val) return res + def sqrt(self): + """ + Return the square root of self. + + >>> ctx = fmpq_mpoly_ctx.get_context(2, "lex", 'x') + >>> f = ctx.from_dict({(1, 1): 4, (0, 0): 1}) + >>> (f * f).sqrt() + 4*x0*x1 + 1 + """ + cdef fmpq_mpoly res + res = create_fmpq_mpoly(self.ctx) + + if fmpq_mpoly_sqrt(res.val, self.val, self.ctx.val): + return res + else: + raise ValueError("polynomial is not a perfect square") + def factor(self): """ Factors self into irreducible factors, returning a tuple @@ -540,23 +767,23 @@ cdef class fmpq_mpoly(flint_mpoly): >>> (p2 * p1 * p2).factor() (18, [(z + 1, 2), (x + 2, 1), (x + 1, 2)]) """ - cdef fmpq_mpoly_factor_t fac - cdef int i - cdef fmpq c - cdef fmpz exp - cdef fmpq_mpoly u + cdef: + fmpq_mpoly_factor_t fac + fmpq_mpoly u + fmpq_mpoly_factor_init(fac, self.ctx.val) fmpq_mpoly_factor(fac, self.val, self.ctx.val) res = [0] * fac.num + for i in range(fac.num): - u = fmpq_mpoly.__new__(fmpq_mpoly) - u.ctx = self.ctx - fmpq_mpoly_init(u.val, u.ctx.val) - u._init = True + u = create_fmpq_mpoly(self.ctx) fmpq_mpoly_set((u).val, &fac.poly[i], self.ctx.val) - exp = fmpz.__new__(fmpz) - fmpz_set((exp).val, fac.exp + i) - res[i] = (u, exp) + + c = fmpz.__new__(fmpz) + fmpz_init_set((c).val, &fac.exp[i]) + + res[i] = (u, c) + c = fmpq.__new__(fmpq) fmpq_set((c).val, fac.constant) fmpq_mpoly_factor_clear(fac, self.ctx.val) @@ -577,24 +804,179 @@ cdef class fmpq_mpoly(flint_mpoly): >>> (p1 * p2 * p1).factor_squarefree() (12, [(y + 1, 1), (x + 1, 1), (x + 2, 2)]) """ - cdef fmpq_mpoly_factor_t fac - cdef int i - cdef fmpq c - cdef fmpz exp - cdef fmpq_mpoly u + cdef: + fmpq_mpoly_factor_t fac + fmpq_mpoly u + fmpq_mpoly_factor_init(fac, self.ctx.val) fmpq_mpoly_factor_squarefree(fac, self.val, self.ctx.val) res = [0] * fac.num - for 0 <= i < fac.num: - u = fmpq_mpoly.__new__(fmpq_mpoly) - u.ctx = self.ctx + + for i in range(fac.num): + u = create_fmpq_mpoly(self.ctx) fmpq_mpoly_init(u.val, u.ctx.val) - u._init = True fmpq_mpoly_set((u).val, &fac.poly[i], self.ctx.val) - exp = fmpz.__new__(fmpz) - fmpz_set((exp).val, fac.exp + i) - res[i] = (u, exp) + + c = fmpz.__new__(fmpz) + fmpz_init_set((c).val, &fac.exp[i]) + + res[i] = (u, c) + c = fmpq.__new__(fmpq) fmpq_set((c).val, fac.constant) fmpq_mpoly_factor_clear(fac, self.ctx.val) return c, res + + def derivative(self, var): + """ + Return the derivative of this polynomial with respect to the provided variable. + The argument and either be the variable as a string, or the index of the + variable in the context. + + >>> ctx = fmpq_mpoly_ctx.get_context(2, "lex", 'x') + >>> p = ctx.from_dict({(0, 3): 2, (2, 1): 3}) + >>> p + 3*x0^2*x1 + 2*x1^3 + >>> p.derivative("x0") + 6*x0*x1 + >>> p.derivative(1) + 3*x0^2 + 6*x1^2 + + """ + cdef: + fmpq_mpoly res + slong i = 0 + + if isinstance(var, str): + vars = {x: i for i, x in enumerate(self.ctx.names())} + if var not in vars: + raise ValueError("variable not in context") + else: + i = vars[var] + elif isinstance(var, int): + if not 0 <= var < self.ctx.nvars(): + raise IndexError("generator index out of range") + i = var + else: + raise TypeError("invalid variable type") + + res = create_fmpq_mpoly(self.ctx) + + fmpq_mpoly_derivative(res.val, self.val, i, self.ctx.val) + return res + + def integral(self, var): + """ + Return the integral of this polynomial*B with respect to the provided variable + where B is minimal. The argument and either be the variable as a string, or + the index of the variable in the context. + + >>> ctx = fmpq_mpoly_ctx.get_context(2, "lex", 'x') + >>> p = ctx.from_dict({(0, 3): 2, (2, 1): 3}) + >>> p + 3*x0^2*x1 + 2*x1^3 + >>> p.integral("x0") + x0^3*x1 + 2*x0*x1^3 + >>> p.integral(1) + 3/2*x0^2*x1^2 + 1/2*x1^4 + + """ + cdef: + fmpq_mpoly res + slong i = 0 + + if isinstance(var, str): + vars = {x: i for i, x in enumerate(self.ctx.names())} + if var not in vars: + raise ValueError("variable not in context") + else: + i = vars[var] + elif isinstance(var, int): + if not 0 <= var < self.ctx.nvars(): + raise IndexError("generator index out of range") + i = var + else: + raise TypeError("invalid variable type") + + res = create_fmpq_mpoly(self.ctx) + + fmpq_mpoly_integral(res.val, self.val, i, self.ctx.val) + return res + + +cdef class fmpq_mpoly_vec: + """ + A class representing a vector of fmpq_mpolys. Not present in FLINT. + """ + + def __cinit__(self, iterable_or_len, fmpq_mpoly_ctx ctx, bint double_indirect = False): + if isinstance(iterable_or_len, int): + self.length = iterable_or_len + else: + self.length = len(iterable_or_len) + + self.ctx = ctx + + self.val = libc.stdlib.malloc(self.length * sizeof(fmpq_mpoly_struct)) + for i in range(self.length): + fmpq_mpoly_init(&self.val[i], self.ctx.val) + + if double_indirect: + self.double_indirect = libc.stdlib.malloc(self.length * sizeof(fmpq_mpoly_struct *)) + if self.double_indirect is NULL: + raise MemoryError("malloc returned a null pointer") # pragma: no cover + + for i in range(self.length): + self.double_indirect[i] = &self.val[i] + else: + self.double_indirect = NULL + + def __init__(self, iterable_or_len, _, double_indirect: bool = False): + if not isinstance(iterable_or_len, int): + for i, x in enumerate(iterable_or_len): + self[i] = x + + def __dealloc__(self): + libc.stdlib.free(self.double_indirect) + for i in range(self.length): + fmpq_mpoly_clear(&self.val[i], self.ctx.val) + libc.stdlib.free(self.val) + + def __getitem__(self, x): + if not isinstance(x, int): + raise TypeError("index is not integer") + elif not 0 <= x < self.length: + raise IndexError("index out of range") + + cdef fmpq_mpoly z = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_set(z.val, &self.val[x], self.ctx.val) + return z + + def __setitem__(self, x, y): + if not isinstance(x, int): + raise TypeError("index is not integer") + elif not 0 <= x < self.length: + raise IndexError("index out of range") + elif not typecheck(y, fmpq_mpoly): + raise TypeError("argument is not fmpq_mpoly") + elif (y).ctx is not self.ctx: + raise IncompatibleContextError(f"{(y).ctx} is not {self.ctx}") + + fmpq_mpoly_set(&self.val[x], (y).val, self.ctx.val) + + def __len__(self): + return self.length + + def __str__(self): + s = [None] * self.length + for i in range(self.length): + x = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_set(( x).val, &self.val[i], self.ctx.val) + s[i] = str(x) + return f"[{', '.join(s)}]" + + def __repr__(self): + return f"fmpq_mpoly_vec({self}, ctx={self.ctx})" + + def to_tuple(self): + return tuple(self[i] for i in range(self.length)) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 61384d2e..0b9384f7 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -1,12 +1,10 @@ from flint.flint_base.flint_base cimport flint_mpoly from flint.flint_base.flint_base cimport flint_mpoly_context -from flint.utils.conversion cimport str_from_chars from flint.utils.typecheck cimport typecheck from flint.utils.flint_exceptions import DomainError, IncompatibleContextError -from flint.types.fmpz cimport any_as_fmpz -from flint.types.fmpz cimport fmpz, fmpz_set_any_ref +from flint.types.fmpz cimport any_as_fmpz, fmpz from flint.types.fmpz_vec cimport fmpz_vec from flint.flintlib.flint cimport * @@ -18,12 +16,6 @@ from cpython.object cimport Py_EQ, Py_NE cimport cython cimport libc.stdlib -cdef extern from *: - """ - /* An ugly hack to get around the ugly hack of renaming fmpq to avoid a c/python name collision */ - typedef fmpq fmpq_struct; - """ - cdef dict _fmpz_mpoly_ctx_cache = {} @@ -137,10 +129,8 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): exp_vec = fmpz_vec(k) - # TODO: lobby for fmpz_mpoly_push_term_fmpz_ffmpz if v: - _fmpz_mpoly_push_exp_ffmpz(res.val, exp_vec.val, self.val) - fmpz_mpoly_set_term_coeff_fmpz(res.val, i, ( v).val, self.val) + fmpz_mpoly_push_term_fmpz_ffmpz(res.val, (v).val, exp_vec.val, self.val) fmpz_mpoly_sort_terms(res.val, self.val) return res @@ -166,7 +156,7 @@ cdef class fmpz_mpoly(flint_mpoly): init_fmpz_mpoly(self, (val).ctx) fmpz_mpoly_set(self.val, (val).val, self.ctx.val) else: - raise IncompatibleContextError(f"{ctx} is not {val.ctx}") + raise IncompatibleContextError(f"{ctx} is not {(val).ctx}") elif isinstance(val, dict): if ctx is None: if len(val) == 0: @@ -291,8 +281,6 @@ cdef class fmpz_mpoly(flint_mpoly): def __add__(self, other): cdef fmpz_mpoly res - cdef fmpz_t z_other - cdef int xtype if typecheck(other, fmpz_mpoly): if (self).ctx is not (other).ctx: raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") @@ -300,22 +288,19 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_add(res.val, (self).val, (other).val, res.ctx.val) return res else: - xtype = fmpz_set_any_ref(z_other, other) - if xtype != FMPZ_UNKNOWN: + other = any_as_fmpz(other) + if other is not NotImplemented: res = create_fmpz_mpoly(self.ctx) - fmpz_mpoly_add_fmpz(res.val, (self).val, z_other, res.ctx.val) + fmpz_mpoly_add_fmpz(res.val, (self).val, (other).val, self.ctx.val) return res return NotImplemented def __radd__(self, other): cdef fmpz_mpoly res - cdef fmpz_t z_other - cdef int xtype - - xtype = fmpz_set_any_ref(z_other, other) - if xtype != FMPZ_UNKNOWN: + other = any_as_fmpz(other) + if other is not NotImplemented: res = create_fmpz_mpoly(self.ctx) - fmpz_mpoly_add_fmpz(res.val, (self).val, z_other, res.ctx.val) + fmpz_mpoly_add_fmpz(res.val, (self).val, (other).val, self.ctx.val) return res return NotImplemented @@ -334,8 +319,6 @@ cdef class fmpz_mpoly(flint_mpoly): def __sub__(self, other): cdef fmpz_mpoly res - cdef fmpz_t z_other - cdef int xtype if typecheck(other, fmpz_mpoly): if (self).ctx is not (other).ctx: raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") @@ -343,22 +326,19 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_sub(res.val, (self).val, (other).val, res.ctx.val) return res else: - xtype = fmpz_set_any_ref(z_other, other) - if xtype != FMPZ_UNKNOWN: + other = any_as_fmpz(other) + if other is not NotImplemented: res = create_fmpz_mpoly(self.ctx) - fmpz_mpoly_sub_fmpz(res.val, (self).val, z_other, res.ctx.val) + fmpz_mpoly_sub_fmpz(res.val, (self).val, (other).val, self.ctx.val) return res return NotImplemented def __rsub__(self, other): cdef fmpz_mpoly res - cdef fmpz_t z_other - cdef int xtype - - xtype = fmpz_set_any_ref(z_other, other) - if xtype != FMPZ_UNKNOWN: + other = any_as_fmpz(other) + if other is not NotImplemented: res = create_fmpz_mpoly(self.ctx) - fmpz_mpoly_sub_fmpz(res.val, (self).val, z_other, res.ctx.val) + fmpz_mpoly_sub_fmpz(res.val, (self).val, (other).val, res.ctx.val) return -res return NotImplemented @@ -377,9 +357,6 @@ cdef class fmpz_mpoly(flint_mpoly): def __mul__(self, other): cdef fmpz_mpoly res - cdef fmpz_t z_other - cdef int xtype - if typecheck(other, fmpz_mpoly): if (self).ctx is not (other).ctx: raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") @@ -387,22 +364,19 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_mul(res.val, (self).val, (other).val, res.ctx.val) return res else: - xtype = fmpz_set_any_ref(z_other, other) - if xtype != FMPZ_UNKNOWN: + other = any_as_fmpz(other) + if other is not NotImplemented: res = create_fmpz_mpoly(self.ctx) - fmpz_mpoly_scalar_mul_fmpz(res.val, (self).val, z_other, res.ctx.val) + fmpz_mpoly_scalar_mul_fmpz(res.val, (self).val, (other).val, res.ctx.val) return res return NotImplemented def __rmul__(self, other): cdef fmpz_mpoly res - cdef fmpz_t z_other - cdef int xtype - - xtype = fmpz_set_any_ref(z_other, other) - if xtype != FMPZ_UNKNOWN: + other = any_as_fmpz(other) + if other is not NotImplemented: res = create_fmpz_mpoly(self.ctx) - fmpz_mpoly_scalar_mul_fmpz(res.val, (self).val, z_other, res.ctx.val) + fmpz_mpoly_scalar_mul_fmpz(res.val, (self).val, (other).val, res.ctx.val) return res return NotImplemented @@ -437,8 +411,8 @@ cdef class fmpz_mpoly(flint_mpoly): cdef fmpz_mpoly res, res2 if typecheck(other, fmpz_mpoly): if not other: - raise ZeroDivisionError("fmpz_mpoly_divison by zero") - if (self).ctx is not (other).ctx: + raise ZeroDivisionError("fmpz_mpoly division by zero") + elif (self).ctx is not (other).ctx: raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") res = create_fmpz_mpoly(self.ctx) res2 = create_fmpz_mpoly(self.ctx) @@ -447,9 +421,9 @@ cdef class fmpz_mpoly(flint_mpoly): else: other = any_as_fmpz(other) if other is not NotImplemented: - other= fmpz_mpoly(other, self.ctx) + other = fmpz_mpoly(other, self.ctx) if not other: - raise ZeroDivisionError("fmpz_mpoly divison by zero") + raise ZeroDivisionError("fmpz_mpoly division by zero") res = create_fmpz_mpoly(self.ctx) res2 = create_fmpz_mpoly(self.ctx) fmpz_mpoly_divrem(res.val, res2.val, (self).val, (other).val, res.ctx.val) @@ -459,7 +433,7 @@ cdef class fmpz_mpoly(flint_mpoly): def __rdivmod__(self, other): cdef fmpz_mpoly res, res2 if not self: - raise ZeroDivisionError("fmpz_mpoly divison by zero") + raise ZeroDivisionError("fmpz_mpoly division by zero") other = any_as_fmpz(other) if other is not NotImplemented: other = fmpz_mpoly(other, self.ctx) @@ -474,7 +448,7 @@ cdef class fmpz_mpoly(flint_mpoly): if typecheck(other, fmpz_mpoly): if not other: raise ZeroDivisionError("fmpz_mpoly division by zero") - if (self).ctx is not (other).ctx: + elif (self).ctx is not (other).ctx: raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_div(res.val, (self).val, (other).val, res.ctx.val) @@ -507,10 +481,6 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly res if typecheck(other, fmpz_mpoly): - if not other: - raise ZeroDivisionError("fmpz_mpoly division by zero") - if self.ctx is not ( other).ctx: - raise IncompatibleContextError(f"{self.ctx} is not {(other).ctx}") res, r = divmod(self, other) if not r: return res @@ -530,18 +500,8 @@ cdef class fmpz_mpoly(flint_mpoly): def __rtruediv__(self, other): cdef fmpz_mpoly res - - # If other was a fmpz_mpoly then __truediv__ would have already been called - # and it only reflects when other was not a fmpz_mpoly nor a fmpz. If other - # was not a fmpz_mpoly so our __truediv__ was not called then it should - # handle having a fmpz_mpoly as the divisor if it's part of python-flint and - # it's reasonable for the domain. Otherwise it is something outside of - # python-flint. We attempt to construct a fmpz out of it, then convert that to - # fmpz_mpoly - if not self: raise ZeroDivisionError("fmpz_mpoly division by zero") - o = any_as_fmpz(other) if o is NotImplemented: return NotImplemented @@ -677,8 +637,8 @@ cdef class fmpz_mpoly(flint_mpoly): 2 """ cdef fmpz v - if i < 0 or i >= fmpz_mpoly_length(self.val, self.ctx.val): - return fmpz(0) + if not 0 <= i < fmpz_mpoly_length(self.val, self.ctx.val): + raise IndexError("term index out of range") else: v = fmpz.__new__(fmpz) fmpz_mpoly_get_term_coeff_fmpz(v.val, self.val, i, self.ctx.val) @@ -696,7 +656,7 @@ cdef class fmpz_mpoly(flint_mpoly): cdef: slong nvars = self.ctx.nvars() - if i < 0 or i >= fmpz_mpoly_length(self.val, self.ctx.val): + if not 0 <= i < fmpz_mpoly_length(self.val, self.ctx.val): 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) @@ -742,16 +702,35 @@ cdef class fmpz_mpoly(flint_mpoly): return res def gcd(self, other): + """ + Return the gcd of self and other. + + >>> ctx = fmpz_mpoly_ctx.get_context(2, "lex", 'x') + >>> 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 + """ cdef fmpz_mpoly res if not typecheck(other, fmpz_mpoly): raise TypeError("argument must be a fmpz_mpoly") - if (self).ctx is not (other).ctx: + elif (self).ctx is not (other).ctx: raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_gcd(res.val, (self).val, (other).val, res.ctx.val) return res 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 + result. If self is not a square root the result is not guaranteed to be correct. + + >>> ctx = fmpz_mpoly_ctx.get_context(2, "lex", 'x') + >>> f = ctx.from_dict({(1, 1): 4, (0, 0): 1}) + >>> (f * f).sqrt() + 4*x0*x1 + 1 + """ cdef fmpz_mpoly res res = create_fmpz_mpoly(self.ctx) diff --git a/src/flint/types/meson.build b/src/flint/types/meson.build index 03eb69a9..b7e7c0d6 100644 --- a/src/flint/types/meson.build +++ b/src/flint/types/meson.build @@ -15,6 +15,7 @@ exts = [ 'fmpq_poly', 'fmpq_mat', 'fmpq_series', + 'fmpq_vec', 'nmod', 'nmod_poly', From a31d49c6548a083f86844ec24f72ef1cd3adfd20 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Tue, 7 May 2024 00:39:36 +1000 Subject: [PATCH 55/95] Forgot these files --- src/flint/flintlib/fmpq_vec.pxd | 18 ++++++++ src/flint/types/fmpq_vec.pxd | 7 +++ src/flint/types/fmpq_vec.pyx | 81 +++++++++++++++++++++++++++++++++ 3 files changed, 106 insertions(+) create mode 100644 src/flint/flintlib/fmpq_vec.pxd create mode 100644 src/flint/types/fmpq_vec.pxd create mode 100644 src/flint/types/fmpq_vec.pyx diff --git a/src/flint/flintlib/fmpq_vec.pxd b/src/flint/flintlib/fmpq_vec.pxd new file mode 100644 index 00000000..e8681822 --- /dev/null +++ b/src/flint/flintlib/fmpq_vec.pxd @@ -0,0 +1,18 @@ +from flint.flintlib.flint cimport flint_bitcnt_t, fmpz_struct, slong, flint_rand_t +from flint.flintlib.fmpq cimport fmpq_t, fmpq_struct +from flint.flintlib.fmpz cimport fmpz_t + + +# unimported types set() + +cdef extern from "flint/fmpq_vec.h": + fmpq_struct * _fmpq_vec_init(slong n) + void _fmpq_vec_clear(fmpq_struct * vec, slong n) + void _fmpq_vec_randtest(fmpq_struct * f, flint_rand_t state, slong len, flint_bitcnt_t bits) + void _fmpq_vec_randtest_uniq_sorted(fmpq_struct * vec, flint_rand_t state, slong len, flint_bitcnt_t bits) + void _fmpq_vec_sort(fmpq_struct * vec, slong len) + void _fmpq_vec_set_fmpz_vec(fmpq_struct * res, const fmpz_struct * vec, slong len) + void _fmpq_vec_get_fmpz_vec_fmpz(fmpz_struct * num, fmpz_t den, const fmpq_struct * a, slong len) + void _fmpq_vec_dot(fmpq_t res, const fmpq_struct * vec1, const fmpq_struct * vec2, slong len) + # int _fmpq_vec_fprint(FILE * file, const fmpq_struct * vec, slong len) + int _fmpq_vec_print(const fmpq_struct * vec, slong len) diff --git a/src/flint/types/fmpq_vec.pxd b/src/flint/types/fmpq_vec.pxd new file mode 100644 index 00000000..5ca2772c --- /dev/null +++ b/src/flint/types/fmpq_vec.pxd @@ -0,0 +1,7 @@ +from flint.flintlib.fmpq cimport fmpq_struct +from flint.flintlib.flint cimport slong + +cdef class fmpq_vec: + cdef fmpq_struct *val + cdef fmpq_struct **double_indirect + cdef slong length diff --git a/src/flint/types/fmpq_vec.pyx b/src/flint/types/fmpq_vec.pyx new file mode 100644 index 00000000..1cd17ca8 --- /dev/null +++ b/src/flint/types/fmpq_vec.pyx @@ -0,0 +1,81 @@ +from flint.flintlib.fmpq cimport fmpq_struct, fmpq_set, fmpq_init +from flint.flintlib.fmpq_vec cimport _fmpq_vec_init, _fmpq_vec_clear + +from flint.types.fmpq cimport fmpq, any_as_fmpq + +cimport libc.stdlib + +cdef class fmpq_vec: + def __cinit__(self, iterable_or_len, bint double_indirect=False): + if isinstance(iterable_or_len, int): + self.length = iterable_or_len + else: + self.length = len(iterable_or_len) + + self.val = _fmpq_vec_init(self.length) + + if double_indirect: + self.double_indirect = libc.stdlib.malloc(self.length * sizeof(fmpq_struct *)) + if self.double_indirect is NULL: + raise MemoryError("malloc returned a null pointer") + + for i in range(self.length): + self.double_indirect[i] = &self.val[i] + else: + self.double_indirect = NULL + + def __init__(self, iterable_or_len, double_indirect: bool = False): + if not isinstance(iterable_or_len, int): + for i, x in enumerate(iterable_or_len): + self[i] = x + + def __dealloc__(self): + libc.stdlib.free(self.double_indirect) + _fmpq_vec_clear(self.val, self.length) + + def __getitem__(self, x): + if not isinstance(x, int): + raise TypeError("index is not integer") + elif not 0 <= x < self.length: + raise IndexError("index out of range") + + cdef fmpq z = fmpq.__new__(fmpq) + fmpq_init(z.val) + fmpq_set(z.val, &self.val[x]) + return z + + def __setitem__(self, x, y): + if not isinstance(x, int): + raise TypeError("index is not integer") + elif not 0 <= x < self.length: + raise IndexError("index out of range") + + y = any_as_fmpq(y) + if y is NotImplemented: + raise TypeError("argument is not coercible to fmpq") + + fmpq_set(&self.val[x], (y).val) + + def __len__(self): + return self.length + + def __str__(self): + return self.str() + + def __repr__(self): + return self.repr() + + def str(self, *args): + s = [None] * self.length + for i in range(self.length): + x = fmpq.__new__(fmpq) + fmpq_init(x.val) + fmpq_set(x.val, &self.val[i]) + s[i] = x.str(*args) + return str(s) + + 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)) From 29c801568910b2b0a292b3c3ee38312329e3acbc Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Thu, 20 Jun 2024 19:31:10 +1000 Subject: [PATCH 56/95] Remove _init from flint_mpoly_context --- src/flint/flint_base/flint_base.pxd | 1 - src/flint/flint_base/flint_base.pyx | 9 ++------- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/src/flint/flint_base/flint_base.pxd b/src/flint/flint_base/flint_base.pxd index c6cb5a63..d701f8d1 100644 --- a/src/flint/flint_base/flint_base.pxd +++ b/src/flint/flint_base/flint_base.pxd @@ -10,7 +10,6 @@ cdef class flint_poly(flint_elem): cdef class flint_mpoly_context(flint_elem): cdef public object py_names cdef const char ** c_names - cdef bint _init cdef class flint_mpoly(flint_elem): pass diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index 2f0da23c..a92f99ab 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -131,9 +131,6 @@ cdef class flint_mpoly_context(flint_elem): _ctx_cache = None - def __cinit__(self): - self._init = False - def __init__(self, int nvars, names): if nvars < 0: raise ValueError("cannot have a negative amount of variables") @@ -141,14 +138,12 @@ cdef class flint_mpoly_context(flint_elem): raise ValueError("number of variables must match lens of variable names") self.py_names = tuple(name.encode("ascii") if not isinstance(name, bytes) else name for name in names) self.c_names = libc.stdlib.malloc(nvars * sizeof(const char *)) - self._init = True for i in range(nvars): self.c_names[i] = self.py_names[i] def __dealloc__(self): - if self._init: - libc.stdlib.free(self.c_names) - self._init = False + libc.stdlib.free(self.c_names) + self.c_names = NULL def __str__(self): return self.__repr__() From a9618891627930aa7f9a7fc637bb003c85cceeb0 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Thu, 20 Jun 2024 19:31:43 +1000 Subject: [PATCH 57/95] Refactor variable name creation and context retrieval --- src/flint/flint_base/flint_base.pyx | 39 ++++++++++++++++++----------- 1 file changed, 25 insertions(+), 14 deletions(-) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index a92f99ab..aded3934 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -133,9 +133,9 @@ cdef class flint_mpoly_context(flint_elem): def __init__(self, int nvars, names): if nvars < 0: - raise ValueError("cannot have a negative amount of variables") + raise ValueError("Cannot have a negative amount of variables") elif len(names) != nvars: - raise ValueError("number of variables must match lens of variable names") + raise ValueError("Number of variables must match number of variable names") self.py_names = tuple(name.encode("ascii") if not isinstance(name, bytes) else name for name in names) self.c_names = libc.stdlib.malloc(nvars * sizeof(const char *)) for i in range(nvars): @@ -163,25 +163,36 @@ cdef class flint_mpoly_context(flint_elem): return tuple(self.gen(i) for i in range(self.nvars())) @staticmethod - def create_cache_key(slong nvars, ordering: str, names: str): + def create_variable_names(slong nvars, names: str): + """ + 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. + + >>> create_variable_names(3, "x") + ("x0", "x1", "x2") + + """ nametup = tuple(name.strip() for name in names.split(',')) if len(nametup) != nvars: - if len(nametup) != 1: + if len(nametup) == 1: + nametup = tuple(nametup[0] + str(i) for i in range(nvars)) + else: raise ValueError("Number of variables does not equal number of names") - nametup = tuple(nametup[0] + str(i) for i in range(nvars)) - return nvars, ordering, nametup + return nametup @classmethod def get_context(cls, slong nvars=1, ordering: str = "lex", names: Optional[str] = "x", nametup: Optional[tuple] = None): - if nvars < 0: - raise ValueError("cannot have a negative amount of variables") - - if nametup is None and names is not None: - key = cls.create_cache_key(nvars, ordering, names) - elif len(nametup) != nvars: - raise ValueError("Number of variables does not equal number of names") + """ + 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`. + """ + 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: - key = (nvars, ordering, nametup) + raise ValueError("Must provide either `names` or `nametup`") ctx = cls._ctx_cache.get(key) if ctx is None: From d7e23fc4cdaed112564c30e4c071eef14f895ba7 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Thu, 20 Jun 2024 19:42:36 +1000 Subject: [PATCH 58/95] Remove joint_context --- src/flint/flint_base/flint_base.pyx | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index aded3934..6ecea0e4 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -209,17 +209,6 @@ cdef class flint_mpoly_context(flint_elem): nametup=tuple(str(s, 'utf-8') for s in ctx.py_names) ) - @classmethod - def joint_context(cls, *ctxs): - vars = set() - for ctx in ctxs: - if not typecheck(ctx, cls): - raise ValueError(f"{ctx} is not a {cls}") - else: - for var in ctx.py_names: - vars.add(var) - return cls.get_context(nvars=len(vars), nametup=tuple(vars)) - cdef class flint_mpoly(flint_elem): """ From d1632d2014ba051d2caaf2bdfa7532fa553042e4 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Thu, 20 Jun 2024 19:56:50 +1000 Subject: [PATCH 59/95] Remove pickle decorators --- src/flint/types/fmpq_mpoly.pyx | 1 - src/flint/types/fmpz_mpoly.pyx | 1 - 2 files changed, 2 deletions(-) diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 1e52dc59..6c3af995 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -26,7 +26,6 @@ cimport libc.stdlib cdef dict _fmpq_mpoly_ctx_cache = {} -@cython.auto_pickle(False) cdef class fmpq_mpoly_ctx(flint_mpoly_context): """ A class for storing the polynomial context diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 0b9384f7..a4a11b09 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -19,7 +19,6 @@ cimport libc.stdlib cdef dict _fmpz_mpoly_ctx_cache = {} -@cython.auto_pickle(False) cdef class fmpz_mpoly_ctx(flint_mpoly_context): """ A class for storing the polynomial context From a485ca39176682847e463255132a202251f3c453 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Thu, 20 Jun 2024 20:17:31 +1000 Subject: [PATCH 60/95] Remove star imports --- src/flint/types/fmpq_mpoly.pxd | 7 ++++- src/flint/types/fmpq_mpoly.pyx | 54 ++++++++++++++++++++++++++++++--- src/flint/types/fmpz_mpoly.pyx | 55 ++++++++++++++++++++++++++++++++-- 3 files changed, 108 insertions(+), 8 deletions(-) diff --git a/src/flint/types/fmpq_mpoly.pxd b/src/flint/types/fmpq_mpoly.pxd index 884646e1..d707fdff 100644 --- a/src/flint/types/fmpq_mpoly.pxd +++ b/src/flint/types/fmpq_mpoly.pxd @@ -1,7 +1,12 @@ from flint.flint_base.flint_base cimport flint_mpoly from flint.flint_base.flint_base cimport flint_mpoly_context -from flint.flintlib.fmpq_mpoly cimport * +from flint.flintlib.fmpq_mpoly cimport ( + fmpq_mpoly_ctx_t, + fmpq_mpoly_t, + fmpq_mpoly_struct, + fmpq_mpoly_init, +) from flint.flintlib.flint cimport slong from flint.types.fmpz_mpoly cimport fmpz_mpoly_ctx diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 6c3af995..938fa167 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -11,16 +11,62 @@ from flint.types.fmpq_vec cimport fmpq_vec from flint.types.fmpz cimport fmpz, any_as_fmpz from flint.types.fmpz_mpoly cimport fmpz_mpoly -from flint.flintlib.flint cimport * from flint.flintlib.fmpq cimport fmpq_set, fmpq_one -from flint.flintlib.fmpq_mpoly cimport * -from flint.flintlib.fmpq_mpoly_factor cimport * +from flint.flintlib.mpoly cimport ordering_t +from flint.flintlib.fmpq_mpoly cimport ( + fmpq_mpoly_add, + fmpq_mpoly_add_fmpq, + fmpq_mpoly_clear, + fmpq_mpoly_compose_fmpq_mpoly, + fmpq_mpoly_ctx_init, + fmpq_mpoly_degrees_fmpz, + fmpq_mpoly_derivative, + fmpq_mpoly_div, + fmpq_mpoly_divrem, + fmpq_mpoly_equal, + fmpq_mpoly_evaluate_all_fmpq, + fmpq_mpoly_evaluate_one_fmpq, + fmpq_mpoly_gcd, + fmpq_mpoly_gen, + fmpq_mpoly_get_coeff_fmpq_fmpz, + fmpq_mpoly_get_str_pretty, + fmpq_mpoly_get_term, + fmpq_mpoly_get_term_coeff_fmpq, + fmpq_mpoly_get_term_exp_fmpz, + fmpq_mpoly_integral, + fmpq_mpoly_is_one, + fmpq_mpoly_is_zero, + fmpq_mpoly_length, + fmpq_mpoly_mul, + fmpq_mpoly_neg, + fmpq_mpoly_pow_fmpz, + fmpq_mpoly_push_term_fmpq_ffmpz, + fmpq_mpoly_reduce, + fmpq_mpoly_scalar_div_fmpq, + fmpq_mpoly_scalar_mul_fmpq, + fmpq_mpoly_set, + fmpq_mpoly_set_coeff_fmpq_fmpz, + fmpq_mpoly_set_fmpq, + fmpq_mpoly_set_str_pretty, + fmpq_mpoly_set_term_coeff_fmpq, + fmpq_mpoly_sort_terms, + fmpq_mpoly_sqrt, + fmpq_mpoly_sub, + fmpq_mpoly_sub_fmpq, + fmpq_mpoly_total_degree_fmpz, +) +from flint.flintlib.fmpq_mpoly_factor cimport ( + fmpq_mpoly_factor, + fmpq_mpoly_factor_clear, + fmpq_mpoly_factor_init, + fmpq_mpoly_factor_squarefree, + fmpq_mpoly_factor_t, +) from flint.flintlib.fmpz cimport fmpz_init_set from flint.flintlib.fmpz_mpoly cimport fmpz_mpoly_set from cpython.object cimport Py_EQ, Py_NE -cimport cython cimport libc.stdlib cdef dict _fmpq_mpoly_ctx_cache = {} diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index a4a11b09..3853488b 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -7,10 +7,59 @@ from flint.utils.flint_exceptions import DomainError, IncompatibleContextError from flint.types.fmpz cimport any_as_fmpz, fmpz from flint.types.fmpz_vec cimport fmpz_vec -from flint.flintlib.flint cimport * from flint.flintlib.fmpz cimport fmpz_set -from flint.flintlib.fmpz_mpoly cimport * -from flint.flintlib.fmpz_mpoly_factor cimport * +from flint.flintlib.mpoly cimport ordering_t +from flint.flintlib.fmpz_mpoly cimport ( + fmpz_mpoly_add, + fmpz_mpoly_add_fmpz, + fmpz_mpoly_clear, + fmpz_mpoly_compose_fmpz_mpoly, + fmpz_mpoly_ctx_init, + fmpz_mpoly_degrees_fmpz, + fmpz_mpoly_derivative, + fmpz_mpoly_div, + fmpz_mpoly_divrem, + fmpz_mpoly_equal, + fmpz_mpoly_evaluate_all_fmpz, + fmpz_mpoly_evaluate_one_fmpz, + fmpz_mpoly_gcd, + fmpz_mpoly_gen, + fmpz_mpoly_get_coeff_fmpz_fmpz, + fmpz_mpoly_get_str_pretty, + fmpz_mpoly_get_term, + fmpz_mpoly_get_term_coeff_fmpz, + fmpz_mpoly_get_term_exp_fmpz, + fmpz_mpoly_integral, + fmpz_mpoly_is_one, + fmpz_mpoly_is_zero, + fmpz_mpoly_length, + fmpz_mpoly_mul, + fmpz_mpoly_neg, + fmpz_mpoly_pow_fmpz, + fmpz_mpoly_push_term_fmpz_ffmpz, + fmpz_mpoly_scalar_divides_fmpz, + fmpz_mpoly_scalar_mul_fmpz, + fmpz_mpoly_set, + fmpz_mpoly_set_coeff_fmpz_fmpz, + fmpz_mpoly_set_fmpz, + fmpz_mpoly_set_str_pretty, + fmpz_mpoly_set_term_coeff_fmpz, + fmpz_mpoly_sort_terms, + fmpz_mpoly_sqrt_heap, + fmpz_mpoly_sub, + fmpz_mpoly_sub_fmpz, + fmpz_mpoly_total_degree_fmpz, + fmpz_mpoly_vec_clear, + fmpz_mpoly_vec_entry, + fmpz_mpoly_vec_init, +) +from flint.flintlib.fmpz_mpoly_factor cimport ( + fmpz_mpoly_factor, + fmpz_mpoly_factor_clear, + fmpz_mpoly_factor_init, + fmpz_mpoly_factor_squarefree, + fmpz_mpoly_factor_t, +) from cpython.object cimport Py_EQ, Py_NE cimport cython From 17b3406758855ee3028c7f433c4d86442f872551 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Thu, 20 Jun 2024 23:23:06 +1000 Subject: [PATCH 61/95] Add Ordering enum class and remove cpdefs --- src/flint/__init__.py | 1 + src/flint/flint_base/flint_base.pxd | 8 +++ src/flint/flint_base/flint_base.pyx | 39 +++++++++++-- src/flint/test/test_all.py | 12 ++-- src/flint/types/fmpq_mpoly.pxd | 2 - src/flint/types/fmpq_mpoly.pyx | 86 +++++++++++++++-------------- src/flint/types/fmpz_mpoly.pxd | 2 - src/flint/types/fmpz_mpoly.pyx | 86 +++++++++++++++-------------- 8 files changed, 142 insertions(+), 94 deletions(-) diff --git a/src/flint/__init__.py b/src/flint/__init__.py index 1a230ad7..722724b6 100644 --- a/src/flint/__init__.py +++ b/src/flint/__init__.py @@ -41,6 +41,7 @@ from .flint_base.flint_base import ( FLINT_VERSION as __FLINT_VERSION__, FLINT_RELEASE as __FLINT_RELEASE__, + Ordering, ) __version__ = '0.6.0' diff --git a/src/flint/flint_base/flint_base.pxd b/src/flint/flint_base/flint_base.pxd index d701f8d1..edf9b25b 100644 --- a/src/flint/flint_base/flint_base.pxd +++ b/src/flint/flint_base/flint_base.pxd @@ -1,3 +1,5 @@ +from flint.flintlib.mpoly cimport ordering_t + cdef class flint_elem: pass @@ -22,3 +24,9 @@ cdef class flint_series(flint_elem): cdef class flint_rational_function(flint_elem): pass + +cpdef enum Ordering: + lex, deglex, degrevlex + +cdef ordering_t ordering_py_to_c(ordering: Ordering) +cdef ordering_c_to_py(ordering_t ordering) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index 6ecea0e4..99e2c704 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -4,7 +4,9 @@ from flint.flintlib.flint cimport ( __FLINT_RELEASE as _FLINT_RELEASE, slong ) +from flint.flintlib.mpoly cimport ordering_t from flint.flint_base.flint_context cimport thectx +from flint.flint_base.flint_base cimport Ordering from flint.utils.typecheck cimport typecheck cimport libc.stdlib @@ -149,7 +151,7 @@ cdef class flint_mpoly_context(flint_elem): return self.__repr__() def __repr__(self): - return f"{self.__class__.__name__}({self.nvars()}, '{self.ordering()}', {self.names()})" + return f"{self.__class__.__name__}({self.nvars()}, '{repr(self.ordering())}', {self.names()})" def name(self, long i): if not 0 <= i < len(self.py_names): @@ -169,8 +171,8 @@ cdef class flint_mpoly_context(flint_elem): If `names` contains a single value, and `nvars` > 1, then the variables are numbered, e.g. - >>> create_variable_names(3, "x") - ("x0", "x1", "x2") + >>> flint_mpoly_context.create_variable_names(3, "x") + ('x0', 'x1', 'x2') """ nametup = tuple(name.strip() for name in names.split(',')) @@ -182,11 +184,17 @@ cdef class flint_mpoly_context(flint_elem): return nametup @classmethod - def get_context(cls, slong nvars=1, ordering: str = "lex", names: Optional[str] = "x", nametup: Optional[tuple] = None): + def get_context(cls, slong nvars=1, ordering=Ordering.lex, names: Optional[str] = "x", nametup: Optional[tuple] = None): """ 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`. """ + + # 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 it's type ourselves + if not isinstance(ordering, 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: @@ -295,3 +303,26 @@ cdef class flint_mat(flint_elem): cdef class flint_rational_function(flint_elem): pass + + +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") + + if ordering == Ordering.lex: + return ordering_t.ORD_LEX + elif ordering == Ordering.deglex: + return ordering_t.ORD_DEGLEX + elif ordering == Ordering.degrevlex: + return ordering_t.ORD_DEGREVLEX + + +cdef ordering_c_to_py(ordering_t ordering): + if ordering == ordering_t.ORD_LEX: + return Ordering.lex + elif ordering == ordering_t.ORD_DEGLEX: + return Ordering.deglex + elif ordering == ordering_t.ORD_DEGREVLEX: + return Ordering.degrevlex + else: + raise ValueError("Unimplemented term order %d" % ordering) diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index db477618..44bd254c 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -2645,9 +2645,9 @@ def test_mpolys(): ctx = C.get_context(nvars=2) - assert raises(lambda: C.get_context(nvars=2, ordering="bad"), ValueError) + assert raises(lambda: C.get_context(nvars=2, ordering="bad"), TypeError) assert raises(lambda: C.get_context(nvars=-1), ValueError) - assert raises(lambda: C(-1, "lex", []), ValueError) + assert raises(lambda: C(-1, flint.Ordering.lex, []), ValueError) assert raises(lambda: ctx.constant("bad"), TypeError) assert raises(lambda: ctx.from_dict("bad"), ValueError) assert raises(lambda: ctx.from_dict({(0, 0): "bad"}), TypeError) @@ -2673,11 +2673,11 @@ def quick_poly(): return mpoly({(0, 0): 1, (0, 1): 2, (1, 0): 3, (2, 2): 4}) assert ctx.nvars() == 2 - assert ctx.ordering() == "lex" + assert ctx.ordering() == flint.Ordering.lex ctx1 = C.get_context(4) assert [ctx1.name(i) for i in range(4)] == ['x0', 'x1', 'x2', 'x3'] - for order in ['lex', 'deglex', 'degrevlex']: + for order in list(flint.Ordering): ctx1 = C.get_context(4, order) assert ctx1.ordering() == order @@ -2775,8 +2775,8 @@ def quick_poly(): assert raises(lambda: p.__setitem__(2, None), TypeError) assert raises(lambda: p.__setitem__(-1, 1), IndexError) - assert P(ctx=ctx).repr() == f"{ctx.__class__.__name__}(2, 'lex', ('x0', 'x1')).from_dict({{}})" - assert P(1, ctx=ctx).repr() == f"{ctx.__class__.__name__}(2, 'lex', ('x0', 'x1')).from_dict({{(0, 0): 1}})" + assert P(ctx=ctx).repr() == f"{ctx.__class__.__name__}(2, '', ('x0', 'x1')).from_dict({{}})" + assert P(1, ctx=ctx).repr() == f"{ctx.__class__.__name__}(2, '', ('x0', 'x1')).from_dict({{(0, 0): 1}})" assert str(quick_poly()) == repr(quick_poly()) == '4*x0^2*x1^2 + 3*x0 + 2*x1 + 1' assert p.exponent_tuple(0) == (2, 2) diff --git a/src/flint/types/fmpq_mpoly.pxd b/src/flint/types/fmpq_mpoly.pxd index d707fdff..166f8479 100644 --- a/src/flint/types/fmpq_mpoly.pxd +++ b/src/flint/types/fmpq_mpoly.pxd @@ -26,8 +26,6 @@ cdef inline create_fmpq_mpoly(fmpq_mpoly_ctx ctx): cdef class fmpq_mpoly_ctx(flint_mpoly_context): cdef fmpq_mpoly_ctx_t val - cpdef slong nvars(self) - cpdef ordering(self) cdef class fmpq_mpoly(flint_mpoly): cdef fmpq_mpoly_t val diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 938fa167..3f8a8e62 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -1,5 +1,10 @@ -from flint.flint_base.flint_base cimport flint_mpoly -from flint.flint_base.flint_base cimport flint_mpoly_context +from flint.flint_base.flint_base cimport ( + flint_mpoly, + flint_mpoly_context, + Ordering, + ordering_py_to_c, + ordering_c_to_py, +) from flint.utils.typecheck cimport typecheck from flint.utils.flint_exceptions import IncompatibleContextError @@ -86,50 +91,37 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): _ctx_cache = _fmpq_mpoly_ctx_cache def __init__(self, slong nvars, ordering, names): - if ordering == "lex": - fmpq_mpoly_ctx_init(self.val, nvars, ordering_t.ORD_LEX) - elif ordering == "deglex": - fmpq_mpoly_ctx_init(self.val, nvars, ordering_t.ORD_DEGLEX) - elif ordering == "degrevlex": - fmpq_mpoly_ctx_init(self.val, nvars, ordering_t.ORD_DEGREVLEX) - else: - raise ValueError("Unimplemented term order %s" % ordering) - + fmpq_mpoly_ctx_init(self.val, nvars, ordering_py_to_c(ordering)) super().__init__(nvars, names) - cpdef slong nvars(self): + def nvars(self): """ Return the number of variables in the context - >>> ctx = fmpq_mpoly_ctx.get_context(4, "lex", 'x') + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(4, Ordering.lex, 'x') >>> ctx.nvars() 4 """ return self.val.zctx.minfo.nvars - cpdef ordering(self): + def ordering(self): """ Return the term order of the context object. - >>> ctx = fmpq_mpoly_ctx.get_context(4, "deglex", 'w') + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(4, Ordering.deglex, 'w') >>> ctx.ordering() - 'deglex' + """ - if self.val.zctx.minfo.ord == ordering_t.ORD_LEX: - return "lex" - elif self.val.zctx.minfo.ord == ordering_t.ORD_DEGLEX: - return "deglex" - elif self.val.zctx.minfo.ord == ordering_t.ORD_DEGREVLEX: - return "degrevlex" - else: - # This should be unreachable - raise ValueError("unknown ordering") # pragma: no cover + return ordering_c_to_py(self.val.zctx.minfo.ord) def gen(self, slong i): """ Return the `i`th generator of the polynomial ring - >>> ctx = fmpq_mpoly_ctx.get_context(3, 'degrevlex', 'z') + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(3, Ordering.degrevlex, 'z') >>> ctx.gen(1) z1 """ @@ -159,7 +151,8 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): The dictionary's keys are tuples of ints (or anything that implicitly converts to fmpz) representing exponents, and corresponding coefficient values of fmpq. - >>> ctx = fmpq_mpoly_ctx.get_context(2,'lex','x,y') + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x,y') >>> ctx.from_dict({(1,0):2, (1,1):3, (0,1):1}) 3*x*y + 2*x + y """ @@ -274,7 +267,8 @@ cdef class fmpq_mpoly(flint_mpoly): Return the term at index `x` if `x` is an `int`, or the term with the exponent vector `x` if `x` is a tuple of `int`s or `fmpq`s. - >>> ctx = fmpq_mpoly_ctx.get_context(2, "lex", 'x') + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p[1] 2*x1 @@ -306,7 +300,8 @@ cdef class fmpq_mpoly(flint_mpoly): Set the coefficient at index `x` to `y` if `x` is an `int`, or the term with the exponent vector `x` if `x` is a tuple of `int`s or `fmpq`s. - >>> ctx = fmpq_mpoly_ctx.get_context(2, "lex", 'x') + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p[1] = 10 >>> p[1, 1] = 20 @@ -675,7 +670,8 @@ cdef class fmpq_mpoly(flint_mpoly): """ Return the context object for this polynomials. - >>> ctx = fmpq_mpoly_ctx.get_context(2, "lex", 'x') + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> p = ctx.from_dict({(0, 1): 2}) >>> ctx is p.context() True @@ -689,7 +685,8 @@ cdef class fmpq_mpoly(flint_mpoly): """ Return the coefficient at index `i`. - >>> ctx = fmpq_mpoly_ctx.get_context(2, "lex", 'x') + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p.coefficient(1) 2 @@ -706,7 +703,8 @@ cdef class fmpq_mpoly(flint_mpoly): """ Return the exponent vector at index `i` as a tuple. - >>> ctx = fmpq_mpoly_ctx.get_context(2, "lex", 'x') + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p.exponent_tuple(1) (0, 1) @@ -724,7 +722,8 @@ cdef class fmpq_mpoly(flint_mpoly): """ Return a dictionary of variable name to degree. - >>> ctx = fmpq_mpoly_ctx.get_context(4, "lex", 'x') + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(4, Ordering.lex, 'x') >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) >>> p.degrees() {'x0': 1, 'x1': 2, 'x2': 3, 'x3': 0} @@ -740,7 +739,8 @@ cdef class fmpq_mpoly(flint_mpoly): """ Return the total degree. - >>> ctx = fmpq_mpoly_ctx.get_context(4, "lex", 'x') + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(4, Ordering.lex, 'x') >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) >>> p.total_degree() 3 @@ -764,7 +764,8 @@ cdef class fmpq_mpoly(flint_mpoly): """ Return the gcd of self and other. - >>> ctx = fmpq_mpoly_ctx.get_context(2, "lex", 'x') + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> f = ctx.from_dict({(1, 1): 4, (0, 0): 1}) >>> g = ctx.from_dict({(0, 1): 2, (1, 0): 2}) >>> (f * g).gcd(f) @@ -784,7 +785,8 @@ cdef class fmpq_mpoly(flint_mpoly): """ Return the square root of self. - >>> ctx = fmpq_mpoly_ctx.get_context(2, "lex", 'x') + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> f = ctx.from_dict({(1, 1): 4, (0, 0): 1}) >>> (f * f).sqrt() 4*x0*x1 + 1 @@ -803,8 +805,9 @@ cdef class fmpq_mpoly(flint_mpoly): (c, factors) where c is the content of the coefficients and factors is a list of (poly, exp) pairs. + >>> from flint import Ordering >>> Zm = fmpq_mpoly - >>> ctx = fmpq_mpoly_ctx.get_context(3, 'lex', 'x,y,z') + >>> ctx = fmpq_mpoly_ctx.get_context(3, Ordering.lex, 'x,y,z') >>> p1 = Zm("2*x + 4", ctx) >>> p2 = Zm("3*x*z + + 3*x + 3*z + 3", ctx) >>> (p1 * p2).factor() @@ -840,8 +843,9 @@ cdef class fmpq_mpoly(flint_mpoly): (c, factors) where c is the content of the coefficients and factors is a list of (poly, exp) pairs. + >>> from flint import Ordering >>> Zm = fmpq_mpoly - >>> ctx = fmpq_mpoly_ctx.get_context(3, 'lex', 'x,y,z') + >>> ctx = fmpq_mpoly_ctx.get_context(3, Ordering.lex, 'x,y,z') >>> p1 = Zm("2*x + 4", ctx) >>> p2 = Zm("3*x*y + 3*x + 3*y + 3", ctx) >>> (p1 * p2).factor_squarefree() @@ -878,7 +882,8 @@ cdef class fmpq_mpoly(flint_mpoly): The argument and either be the variable as a string, or the index of the variable in the context. - >>> ctx = fmpq_mpoly_ctx.get_context(2, "lex", 'x') + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> p = ctx.from_dict({(0, 3): 2, (2, 1): 3}) >>> p 3*x0^2*x1 + 2*x1^3 @@ -916,7 +921,8 @@ cdef class fmpq_mpoly(flint_mpoly): where B is minimal. The argument and either be the variable as a string, or the index of the variable in the context. - >>> ctx = fmpq_mpoly_ctx.get_context(2, "lex", 'x') + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> p = ctx.from_dict({(0, 3): 2, (2, 1): 3}) >>> p 3*x0^2*x1 + 2*x1^3 diff --git a/src/flint/types/fmpz_mpoly.pxd b/src/flint/types/fmpz_mpoly.pxd index 97935ed7..953bb82e 100644 --- a/src/flint/types/fmpz_mpoly.pxd +++ b/src/flint/types/fmpz_mpoly.pxd @@ -18,8 +18,6 @@ cdef inline fmpz_mpoly create_fmpz_mpoly(fmpz_mpoly_ctx ctx): cdef class fmpz_mpoly_ctx(flint_mpoly_context): cdef fmpz_mpoly_ctx_t val - cpdef slong nvars(self) - cpdef ordering(self) cdef class fmpz_mpoly(flint_mpoly): cdef fmpz_mpoly_t val diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 3853488b..7cf76a71 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -1,5 +1,10 @@ -from flint.flint_base.flint_base cimport flint_mpoly -from flint.flint_base.flint_base cimport flint_mpoly_context +from flint.flint_base.flint_base cimport ( + flint_mpoly, + flint_mpoly_context, + Ordering, + ordering_py_to_c, + ordering_c_to_py, +) from flint.utils.typecheck cimport typecheck from flint.utils.flint_exceptions import DomainError, IncompatibleContextError @@ -8,7 +13,6 @@ from flint.types.fmpz cimport any_as_fmpz, fmpz from flint.types.fmpz_vec cimport fmpz_vec from flint.flintlib.fmpz cimport fmpz_set -from flint.flintlib.mpoly cimport ordering_t from flint.flintlib.fmpz_mpoly cimport ( fmpz_mpoly_add, fmpz_mpoly_add_fmpz, @@ -82,49 +86,37 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): _ctx_cache = _fmpz_mpoly_ctx_cache def __init__(self, slong nvars, ordering, names): - if ordering == "lex": - fmpz_mpoly_ctx_init(self.val, nvars, ordering_t.ORD_LEX) - elif ordering == "deglex": - fmpz_mpoly_ctx_init(self.val, nvars, ordering_t.ORD_DEGLEX) - elif ordering == "degrevlex": - fmpz_mpoly_ctx_init(self.val, nvars, ordering_t.ORD_DEGREVLEX) - else: - raise ValueError("Unimplemented term order %s" % ordering) + fmpz_mpoly_ctx_init(self.val, nvars, ordering_py_to_c(ordering)) super().__init__(nvars, names) - cpdef slong nvars(self): + def nvars(self): """ Return the number of variables in the context - >>> ctx = fmpz_mpoly_ctx.get_context(4, "lex", 'x') + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(4, Ordering.lex, 'x') >>> ctx.nvars() 4 """ return self.val.minfo.nvars - cpdef ordering(self): + def ordering(self): """ Return the term order of the context object. - >>> ctx = fmpz_mpoly_ctx.get_context(4, "deglex", 'w') + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(4, Ordering.deglex, 'w') >>> ctx.ordering() - 'deglex' + """ - if self.val.minfo.ord == ordering_t.ORD_LEX: - return "lex" - elif self.val.minfo.ord == ordering_t.ORD_DEGLEX: - return "deglex" - elif self.val.minfo.ord == ordering_t.ORD_DEGREVLEX: - return "degrevlex" - else: - # This should be unreachable - raise ValueError("unknown ordering") # pragma: no cover + return ordering_c_to_py(self.val.minfo.ord) def gen(self, slong i): """ Return the `i`th generator of the polynomial ring - >>> ctx = fmpz_mpoly_ctx.get_context(3, 'degrevlex', 'z') + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(3, Ordering.degrevlex, 'z') >>> ctx.gen(1) z1 """ @@ -154,7 +146,8 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): The dictionary's keys are tuples of ints (or anything that implicitly converts to fmpz) representing exponents, and corresponding values of fmpz. - >>> ctx = fmpz_mpoly_ctx.get_context(2,'lex','x,y') + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x,y') >>> ctx.from_dict({(1,0):2, (1,1):3, (0,1):1}) 3*x*y + 2*x + y """ @@ -259,7 +252,8 @@ cdef class fmpz_mpoly(flint_mpoly): Return the term at index `x` if `x` is an `int`, or the term with the exponent vector `x` if `x` is a tuple of `int`s or `fmpz`s. - >>> ctx = fmpz_mpoly_ctx.get_context(2, "lex", 'x') + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p[1] 2*x1 @@ -291,7 +285,8 @@ cdef class fmpz_mpoly(flint_mpoly): Set the coefficient at index `x` to `y` if `x` is an `int`, or the term with the exponent vector `x` if `x` is a tuple of `int`s or `fmpz`s. - >>> ctx = fmpz_mpoly_ctx.get_context(2, "lex", 'x') + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p[1] = 10 >>> p[1, 1] = 20 @@ -665,7 +660,8 @@ cdef class fmpz_mpoly(flint_mpoly): """ Return the context object for this polynomials. - >>> ctx = fmpz_mpoly_ctx.get_context(2, "lex", 'x') + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> p = ctx.from_dict({(0, 1): 2}) >>> ctx is p.context() True @@ -679,7 +675,8 @@ cdef class fmpz_mpoly(flint_mpoly): """ Return the coefficient at index `i`. - >>> ctx = fmpz_mpoly_ctx.get_context(2, "lex", 'x') + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p.coefficient(1) 2 @@ -696,7 +693,8 @@ cdef class fmpz_mpoly(flint_mpoly): """ Return the exponent vector at index `i` as a tuple. - >>> ctx = fmpz_mpoly_ctx.get_context(2, "lex", 'x') + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p.exponent_tuple(1) (0, 1) @@ -714,7 +712,8 @@ cdef class fmpz_mpoly(flint_mpoly): """ Return a dictionary of variable name to degree. - >>> ctx = fmpz_mpoly_ctx.get_context(4, "lex", 'x') + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(4, Ordering.lex, 'x') >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) >>> p.degrees() {'x0': 1, 'x1': 2, 'x2': 3, 'x3': 0} @@ -730,7 +729,8 @@ cdef class fmpz_mpoly(flint_mpoly): """ Return the total degree. - >>> ctx = fmpz_mpoly_ctx.get_context(4, "lex", 'x') + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(4, Ordering.lex, 'x') >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) >>> p.total_degree() 3 @@ -753,7 +753,8 @@ cdef class fmpz_mpoly(flint_mpoly): """ Return the gcd of self and other. - >>> ctx = fmpz_mpoly_ctx.get_context(2, "lex", 'x') + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> f = ctx.from_dict({(1, 1): 4, (0, 0): 1}) >>> g = ctx.from_dict({(0, 1): 2, (1, 0): 2}) >>> (f * g).gcd(f) @@ -774,7 +775,8 @@ cdef class fmpz_mpoly(flint_mpoly): 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. - >>> ctx = fmpz_mpoly_ctx.get_context(2, "lex", 'x') + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> f = ctx.from_dict({(1, 1): 4, (0, 0): 1}) >>> (f * f).sqrt() 4*x0*x1 + 1 @@ -793,8 +795,9 @@ cdef class fmpz_mpoly(flint_mpoly): (c, factors) where c is the content of the coefficients and factors is a list of (poly, exp) pairs. + >>> from flint import Ordering >>> Zm = fmpz_mpoly - >>> ctx = fmpz_mpoly_ctx.get_context(3, 'lex', 'x,y,z') + >>> ctx = fmpz_mpoly_ctx.get_context(3, Ordering.lex, 'x,y,z') >>> p1 = Zm("2*x + 4", ctx) >>> p2 = Zm("3*x*z + + 3*x + 3*z + 3", ctx) >>> (p1 * p2).factor() @@ -831,8 +834,9 @@ cdef class fmpz_mpoly(flint_mpoly): (c, factors) where c is the content of the coefficients and factors is a list of (poly, exp) pairs. + >>> from flint import Ordering >>> Zm = fmpz_mpoly - >>> ctx = fmpz_mpoly_ctx.get_context(3, 'lex', 'x,y,z') + >>> ctx = fmpz_mpoly_ctx.get_context(3, Ordering.lex, 'x,y,z') >>> p1 = Zm("2*x + 4", ctx) >>> p2 = Zm("3*x*y + 3*x + 3*y + 3", ctx) >>> (p1 * p2).factor_squarefree() @@ -897,7 +901,8 @@ cdef class fmpz_mpoly(flint_mpoly): The argument and either be the variable as a string, or the index of the variable in the context. - >>> ctx = fmpz_mpoly_ctx.get_context(2, "lex", 'x') + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> p = ctx.from_dict({(0, 3): 2, (2, 1): 3}) >>> p 3*x0^2*x1 + 2*x1^3 @@ -935,7 +940,8 @@ cdef class fmpz_mpoly(flint_mpoly): where B is minimal. The argument and either be the variable as a string, or the index of the variable in the context. - >>> ctx = fmpz_mpoly_ctx.get_context(2, "lex", 'x') + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> p = ctx.from_dict({(0, 3): 2, (2, 1): 3}) >>> p 3*x0^2*x1 + 2*x1^3 From 4cf40f9f1c8a47ceac831df1ad71a2be71e77d1d Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Fri, 21 Jun 2024 00:39:30 +1000 Subject: [PATCH 62/95] Remove two places where contexts were created on the fly --- src/flint/test/test_all.py | 4 ++-- src/flint/types/fmpq_mpoly.pyx | 15 +++++++-------- src/flint/types/fmpz_mpoly.pyx | 9 ++------- 3 files changed, 11 insertions(+), 17 deletions(-) diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index 44bd254c..1ae98f46 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -2721,12 +2721,12 @@ def quick_poly(): assert (None != P(1, ctx=ctx)) is True assert P(ctx.from_dict({(0, 1): 3})) == ctx.from_dict({(0, 1): 3}) - assert P({(0, 1): 3}) == ctx.from_dict({(0, 1): 3}) + assert P({(0, 1): 3}, ctx=ctx) == ctx.from_dict({(0, 1): 3}) if P is flint.fmpq_mpoly: ctx_z = flint.fmpz_mpoly_ctx.get_context(2) assert quick_poly() == P(ctx_z.from_dict({(0, 0): 1, (0, 1): 2, (1, 0): 3, (2, 2): 4})) - assert raises(lambda: P(ctx_z.from_dict({(0, 0): 1}), ctx=ctx), TypeError) + assert P(ctx_z.from_dict({(0, 0): 1}), ctx=ctx) == P({(0, 0): 1}, ctx=ctx) assert raises(lambda: P(ctx=ctx) < P(ctx=ctx), TypeError) assert raises(lambda: P(ctx=ctx) <= P(ctx=ctx), TypeError) diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 3f8a8e62..4706dba9 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -207,20 +207,19 @@ cdef class fmpq_mpoly(flint_mpoly): elif typecheck(val, fmpz_mpoly): if ctx is None: ctx = fmpq_mpoly_ctx.from_context((val).ctx) - elif not typecheck(ctx, fmpz_mpoly_ctx): - raise TypeError(f"{ctx} is not a fmpz_mpoly_ctx") + elif not typecheck(ctx, fmpq_mpoly_ctx) and not typecheck(ctx, fmpz_mpoly_ctx): + raise TypeError(f"{ctx} is not a fmpq_mpoly_ctx or fmpz_mpoly_ctx") + elif ctx.nvars() != val.context().nvars(): + raise ValueError( + f"Provided context ('{ctx}') and provided fmpz_mpoly ('{val}') don't share the same number of variables" + ) init_fmpq_mpoly(self, ctx) fmpz_mpoly_set(self.val.zpoly, (val).val, ( val).ctx.val) fmpq_one(self.val.content) fmpq_mpoly_reduce(self.val, self.ctx.val) elif isinstance(val, dict): if ctx is None: - if len(val) == 0: - raise ValueError("Need context for zero polynomial") - k = list(val.keys())[0] - if not isinstance(k, tuple): - raise ValueError("Dict should be keyed with tuples of integers") - ctx = fmpq_mpoly_ctx.get_context(len(k)) + raise ValueError("A context is required to create a fmpq_mpoly from a dict") x = ctx.from_dict(val) # XXX: this copy is silly, have a ctx function that assigns an fmpz_mpoly_t init_fmpq_mpoly(self, ctx) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 7cf76a71..90e5eb28 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -200,12 +200,7 @@ cdef class fmpz_mpoly(flint_mpoly): raise IncompatibleContextError(f"{ctx} is not {(val).ctx}") elif isinstance(val, dict): if ctx is None: - if len(val) == 0: - raise ValueError("Need context for zero polynomial") - k = list(val.keys())[0] - if not isinstance(k, tuple): - raise ValueError("Dict should be keyed with tuples of integers") - ctx = fmpz_mpoly_ctx.get_context(len(k)) + raise ValueError("A context is required to create a fmpz_mpoly from a dict") x = ctx.from_dict(val) # XXX: this copy is silly, have a ctx function that assigns an fmpz_mpoly_t init_fmpz_mpoly(self, ctx) @@ -220,7 +215,7 @@ cdef class fmpz_mpoly(flint_mpoly): else: v = any_as_fmpz(val) if v is NotImplemented: - raise TypeError("cannot create fmpz_mpoly from type %s" % type(val)) + raise TypeError("Cannot create fmpz_mpoly from type %s" % type(val)) if ctx is None: raise ValueError("Need context to convert fmpz to fmpz_mpoly") init_fmpz_mpoly(self, ctx) From 4e22e1476e7f06cf6911da99002ee9dcca510b4a Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Fri, 21 Jun 2024 01:09:09 +1000 Subject: [PATCH 63/95] Prefer fmp[zq]_mpoly_divides to divmod --- src/flint/types/fmpq_mpoly.pyx | 15 ++++++++++++--- src/flint/types/fmpz_mpoly.pyx | 10 ++++++++-- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 4706dba9..c8d11f6a 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -7,7 +7,7 @@ from flint.flint_base.flint_base cimport ( ) from flint.utils.typecheck cimport typecheck -from flint.utils.flint_exceptions import IncompatibleContextError +from flint.utils.flint_exceptions import DomainError, IncompatibleContextError from flint.types.fmpq cimport fmpq, any_as_fmpq from flint.types.fmpz_vec cimport fmpz_vec @@ -27,6 +27,7 @@ from flint.flintlib.fmpq_mpoly cimport ( fmpq_mpoly_degrees_fmpz, fmpq_mpoly_derivative, fmpq_mpoly_div, + fmpq_mpoly_divides, fmpq_mpoly_divrem, fmpq_mpoly_equal, fmpq_mpoly_evaluate_all_fmpq, @@ -538,8 +539,16 @@ cdef class fmpq_mpoly(flint_mpoly): fmpq_mpoly res if typecheck(other, fmpq_mpoly): - res, r = divmod(self, other) - return res + r + if not other: + raise ZeroDivisionError("fmpq_mpoly division by zero") + elif self.ctx is not (other).ctx: + raise IncompatibleContextError(f"{self.ctx} is not {(other).ctx}") + + res = create_fmpq_mpoly(self.ctx) + if fmpq_mpoly_divides(res.val, self.val, (other).val, self.ctx.val): + return res + else: + raise DomainError("fmpq_mpoly division is not exact") else: o = any_as_fmpq(other) if o is NotImplemented: diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 90e5eb28..4fb9273b 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -22,6 +22,7 @@ from flint.flintlib.fmpz_mpoly cimport ( fmpz_mpoly_degrees_fmpz, fmpz_mpoly_derivative, fmpz_mpoly_div, + fmpz_mpoly_divides, fmpz_mpoly_divrem, fmpz_mpoly_equal, fmpz_mpoly_evaluate_all_fmpz, @@ -519,8 +520,13 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly res if typecheck(other, fmpz_mpoly): - res, r = divmod(self, other) - if not r: + if not other: + raise ZeroDivisionError("fmpz_mpoly division by zero") + elif self.ctx is not (other).ctx: + raise IncompatibleContextError(f"{self.ctx} is not {(other).ctx}") + + res = create_fmpz_mpoly(self.ctx) + if fmpz_mpoly_divides(res.val, self.val, (other).val, self.ctx.val): return res else: raise DomainError("fmpz_mpoly division is not exact") From 6af74337f51bc41cc4943ffd4d6acb94da25de98 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Fri, 21 Jun 2024 01:14:36 +1000 Subject: [PATCH 64/95] Typo in doc string --- src/flint/types/fmpq_mpoly.pyx | 2 +- src/flint/types/fmpz_mpoly.pyx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index c8d11f6a..da19b63d 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -887,7 +887,7 @@ cdef class fmpq_mpoly(flint_mpoly): def derivative(self, var): """ Return the derivative of this polynomial with respect to the provided variable. - The argument and either be the variable as a string, or the index of the + The argument can either be the variable as a string, or the index of the variable in the context. >>> from flint import Ordering diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 4fb9273b..cc89c55f 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -899,7 +899,7 @@ cdef class fmpz_mpoly(flint_mpoly): def derivative(self, var): """ Return the derivative of this polynomial with respect to the provided variable. - The argument and either be the variable as a string, or the index of the + The argument can either be the variable as a string, or the index of the variable in the context. >>> from flint import Ordering From aef740ca037d7fe2e38f4556109fe4bd29931abc Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Fri, 21 Jun 2024 01:17:43 +1000 Subject: [PATCH 65/95] Remove fmpz_mpoly_q --- src/flint/__init__.py | 1 - src/flint/test/__main__.py | 1 - src/flint/types/fmpz_mpoly_q.pxd | 9 -- src/flint/types/fmpz_mpoly_q.pyx | 138 ------------------------------- src/flint/types/meson.build | 1 - 5 files changed, 150 deletions(-) delete mode 100644 src/flint/types/fmpz_mpoly_q.pxd delete mode 100644 src/flint/types/fmpz_mpoly_q.pyx diff --git a/src/flint/__init__.py b/src/flint/__init__.py index 722724b6..aa1e1ff6 100644 --- a/src/flint/__init__.py +++ b/src/flint/__init__.py @@ -23,7 +23,6 @@ from .types.fmpz_mod_mat import fmpz_mod_mat from .types.fmpq_mpoly import fmpq_mpoly_ctx, fmpq_mpoly, fmpq_mpoly_vec -from .types.fmpz_mpoly_q import * from .types.arf import * from .types.arb import * diff --git a/src/flint/test/__main__.py b/src/flint/test/__main__.py index 34c7a414..f837f63a 100644 --- a/src/flint/test/__main__.py +++ b/src/flint/test/__main__.py @@ -58,7 +58,6 @@ def run_doctests(verbose=None): flint.types.fmpz_poly, flint.types.fmpz_mat, flint.types.fmpz_mpoly, - flint.types.fmpz_mpoly_q, flint.types.fmpz_series, flint.types.fmpz_mod, flint.types.fmpz_mod_poly, diff --git a/src/flint/types/fmpz_mpoly_q.pxd b/src/flint/types/fmpz_mpoly_q.pxd deleted file mode 100644 index c80a7fde..00000000 --- a/src/flint/types/fmpz_mpoly_q.pxd +++ /dev/null @@ -1,9 +0,0 @@ -from flint.flint_base.flint_base cimport flint_rational_function -from flint.flintlib.fmpz_mpoly_q cimport fmpz_mpoly_q_t - -from flint.types.fmpz_mpoly cimport fmpz_mpoly, fmpz_mpoly_ctx - -cdef class fmpz_mpoly_q(flint_rational_function): - cdef fmpz_mpoly_q_t fraction - cdef fmpz_mpoly_ctx ctx - cdef bint _init diff --git a/src/flint/types/fmpz_mpoly_q.pyx b/src/flint/types/fmpz_mpoly_q.pyx deleted file mode 100644 index 640d9029..00000000 --- a/src/flint/types/fmpz_mpoly_q.pyx +++ /dev/null @@ -1,138 +0,0 @@ -from flint.flint_base.flint_base cimport flint_rational_function -from flint.utils.typecheck cimport typecheck -from flint.flintlib.fmpz_mpoly cimport fmpz_mpoly_set, fmpz_mpoly_get_str_pretty -from flint.flintlib.fmpz_mpoly_q cimport * -from flint.types.fmpz_mpoly cimport fmpz_mpoly, fmpz_mpoly_ctx, create_fmpz_mpoly - - -cdef inline init_fmpz_mpoly_q(fmpz_mpoly_q var, fmpz_mpoly_ctx ctx): - var.ctx = ctx - fmpz_mpoly_q_init(var.fraction, ctx.val) - var._init = True - -cdef inline create_fmpz_mpoly_q(fmpz_mpoly_ctx ctx): - cdef fmpz_mpoly_q var - var = fmpz_mpoly_q.__new__(fmpz_mpoly_q) - var.ctx = ctx - fmpz_mpoly_q_init(var.fraction, ctx.val) - var._init = True - return var - - -cdef class fmpz_mpoly_q(flint_rational_function): - """ - The `fmpz_mpoly_q` represents multivariate rational functions - over the integers - """ - def __cinit__(self): - self._init = False - - def __dealloc__(self): - if self._init: - fmpz_mpoly_q_clear(self.fraction, self.ctx.val) - self._init = False - - def __init__(self, num, den): - if typecheck(num, fmpz_mpoly) and typecheck(den, fmpz_mpoly): - if (num).ctx == (den).ctx: - self.ctx = (num).ctx - fmpz_mpoly_q_init(self.fraction, self.ctx.val) - fmpz_mpoly_set(fmpz_mpoly_q_numref(self.fraction), - (num).val, self.ctx.val) - fmpz_mpoly_set(fmpz_mpoly_q_denref(self.fraction), - (den).val, self.ctx.val) - self._init = True - else: - raise ValueError("numerator and denominator must have identical contexts") - else: - raise TypeError("fmpz_mpoly_q is a fraction of two fmpz_mpolys fs") - - - def __nonzero__(self): - return not fmpz_mpoly_q_is_zero(self.fraction, self.ctx.val) - - def __bool__(self): - return not fmpz_mpoly_q_is_zero(self.fraction, self.ctx.val) - - def is_one(self): - return fmpz_mpoly_q_is_one(self.fraction, self.ctx.val) - - def __richcmp__(self, other, int op): - if op != 2 and op != 3: - return NotImplemented - if typecheck(self, fmpz_mpoly_q) and typecheck(other, fmpz_mpoly_q): - if ( self).ctx is ( other).ctx: - if op == 2: - return bool(fmpz_mpoly_q_equal((self).fraction, (other).fraction, self.ctx.val)) - else: - return not bool(fmpz_mpoly_q_equal((self).fraction, (other).fraction, self.ctx.val)) - else: - if op == 2: - return False - else: - return True - if op == 2: - return not bool(self - other) - else: - return bool(self - other) - - def repr(self): - return self.str() + " (nvars=%s, ordering=%s names=%s)" % (self.ctx.nvars(), self.ctx.ordering(), self.ctx.py_names) - - def str(self): - cdef bytes numerator = fmpz_mpoly_get_str_pretty(&(self.fraction.num), self.ctx.c_names, self.ctx.val) - cdef bytes denominator = fmpz_mpoly_get_str_pretty(&(self.fraction.den), self.ctx.c_names, self.ctx.val) - res = str(b"(" + numerator + b")/(" + denominator + b")", encoding='utf-8') - res = res.replace("+", " + ") - res = res.replace("-", " - ") - return res - - def numer(self): - """ - Returns the numerator of *self* as an *fmpz_mpoly* - """ - cdef fmpz_mpoly num = create_fmpz_mpoly(self.ctx) - fmpz_mpoly_set(num.val, &(self.fraction.num), self.ctx.val) - return num - - def denom(self): - """ - Returns the denominator of *self* as an *fmpz_mpoly*. - """ - cdef fmpz_mpoly num = create_fmpz_mpoly(self.ctx) - fmpz_mpoly_set(num.val, &(self.fraction.den), self.ctx.val) - return num - - def __neg__(self): - cdef fmpz_mpoly_q res - res = create_fmpz_mpoly_q(self.ctx) - fmpz_mpoly_q_neg(res.fraction, self.fraction, res.ctx.val) - return res - - def __add__(s, t): - cdef fmpz_mpoly_q res - if typecheck(t, fmpz_mpoly_q) and typecheck(s, fmpz_mpoly_q): - if (s).ctx is (t).ctx: - res = create_fmpz_mpoly_q(s.ctx) - fmpz_mpoly_q_add(res.fraction, (s).fraction, (t).fraction, ( s).ctx.val) - return res - return NotImplemented - - def __sub__(s,t): - cdef fmpz_mpoly_q res - if typecheck(t, fmpz_mpoly_q) and typecheck(s, fmpz_mpoly_q): - if (s).ctx is (t).ctx: - res = create_fmpz_mpoly_q(s.ctx) - fmpz_mpoly_q_sub(res.fraction, (s).fraction, (t).fraction, ( s).ctx.val) - return res - return NotImplemented - - def __mul__(s,t): - cdef fmpz_mpoly_q res - if typecheck(t, fmpz_mpoly_q) and typecheck(s, fmpz_mpoly_q): - if (s).ctx is (t).ctx: - res = create_fmpz_mpoly_q(s.ctx) - fmpz_mpoly_q_mul(res.fraction, (s).fraction, (t).fraction, ( s).ctx.val) - return res - return NotImplemented - diff --git a/src/flint/types/meson.build b/src/flint/types/meson.build index 063d624e..4dbfcd35 100644 --- a/src/flint/types/meson.build +++ b/src/flint/types/meson.build @@ -42,7 +42,6 @@ exts = [ 'fmpz_mpoly', 'fmpq_mpoly', - 'fmpz_mpoly_q', ] if have_acb_theta From 3ebd8ff0c9ea4bf2eb2febb12c7d41e83c4f1d12 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Fri, 21 Jun 2024 01:25:27 +1000 Subject: [PATCH 66/95] Prevent flint clear of null pointer --- src/flint/types/fmpq_vec.pyx | 4 +++- src/flint/types/fmpz_vec.pyx | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/flint/types/fmpq_vec.pyx b/src/flint/types/fmpq_vec.pyx index 1cd17ca8..a4d01d79 100644 --- a/src/flint/types/fmpq_vec.pyx +++ b/src/flint/types/fmpq_vec.pyx @@ -31,7 +31,9 @@ cdef class fmpq_vec: def __dealloc__(self): libc.stdlib.free(self.double_indirect) - _fmpq_vec_clear(self.val, self.length) + if self.val is not NULL: + _fmpq_vec_clear(self.val, self.length) + self.val = NULL def __getitem__(self, x): if not isinstance(x, int): diff --git a/src/flint/types/fmpz_vec.pyx b/src/flint/types/fmpz_vec.pyx index 1576a219..d5d7e27e 100644 --- a/src/flint/types/fmpz_vec.pyx +++ b/src/flint/types/fmpz_vec.pyx @@ -32,7 +32,9 @@ cdef class fmpz_vec: def __dealloc__(self): libc.stdlib.free(self.double_indirect) - _fmpz_vec_clear(self.val, self.length) + if self.val is not NULL: + _fmpz_vec_clear(self.val, self.length) + self.val = NULL def __getitem__(self, x): if not isinstance(x, int): From 6e12555629afd42ea8f53a1e59789efb710e2259 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Fri, 21 Jun 2024 01:37:56 +1000 Subject: [PATCH 67/95] Fix poor error reporting --- src/flint/types/fmpq_mpoly.pyx | 8 ++++---- src/flint/types/fmpz_mpoly.pyx | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index da19b63d..e66f6899 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -168,16 +168,16 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): res = create_fmpq_mpoly(self) for i, (k, v) in enumerate(d.items()): - v = any_as_fmpq(v) - if v is NotImplemented: + o = any_as_fmpq(v) + if o is NotImplemented: raise TypeError(f"cannot coerce coefficient '{v}' to fmpq") elif len(k) != nvars: raise ValueError(f"expected {nvars} exponents, got {len(k)}") exp_vec = fmpz_vec(k) - if v: - fmpq_mpoly_push_term_fmpq_ffmpz(res.val, (v).val, exp_vec.val, self.val) + if o: + fmpq_mpoly_push_term_fmpq_ffmpz(res.val, (o).val, exp_vec.val, self.val) fmpq_mpoly_sort_terms(res.val, self.val) fmpq_mpoly_reduce(res.val, self.val) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index cc89c55f..cb1b323b 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -163,16 +163,16 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): res = create_fmpz_mpoly(self) for i, (k, v) in enumerate(d.items()): - v = any_as_fmpz(v) - if v is NotImplemented: + o = any_as_fmpz(v) + if o is NotImplemented: raise TypeError(f"cannot coerce coefficient '{v}' to fmpz") elif len(k) != nvars: raise ValueError(f"expected {nvars} exponents, got {len(k)}") exp_vec = fmpz_vec(k) - if v: - fmpz_mpoly_push_term_fmpz_ffmpz(res.val, (v).val, exp_vec.val, self.val) + if o: + fmpz_mpoly_push_term_fmpz_ffmpz(res.val, (o).val, exp_vec.val, self.val) fmpz_mpoly_sort_terms(res.val, self.val) return res From 317189a84b5876e25d93ce05f21998978331183a Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Fri, 21 Jun 2024 01:49:13 +1000 Subject: [PATCH 68/95] Add variable_to_index method --- src/flint/flint_base/flint_base.pyx | 17 ++++++++++++++++ src/flint/types/fmpq_mpoly.pyx | 30 ++--------------------------- src/flint/types/fmpz_mpoly.pyx | 30 ++--------------------------- 3 files changed, 21 insertions(+), 56 deletions(-) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index 99e2c704..6a0856d0 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -164,6 +164,23 @@ cdef class flint_mpoly_context(flint_elem): def gens(self): return tuple(self.gen(i) for i in range(self.nvars())) + def variable_to_index(self, var: Union[int, str]): + """Convert a variable name string or possible index to it's index in the context.""" + if isinstance(var, str): + vars = {x: i for i, x in enumerate(self.names())} + if var not in vars: + raise ValueError("Variable not in context") + else: + i = vars[var] + elif isinstance(var, int): + if not 0 <= var < self.nvars(): + raise IndexError("Generator index out of range") + i = var + else: + raise TypeError("Invalid variable type") + + return i + @staticmethod def create_variable_names(slong nvars, names: str): """ diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index e66f6899..b8310997 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -903,20 +903,7 @@ cdef class fmpq_mpoly(flint_mpoly): """ cdef: fmpq_mpoly res - slong i = 0 - - if isinstance(var, str): - vars = {x: i for i, x in enumerate(self.ctx.names())} - if var not in vars: - raise ValueError("variable not in context") - else: - i = vars[var] - elif isinstance(var, int): - if not 0 <= var < self.ctx.nvars(): - raise IndexError("generator index out of range") - i = var - else: - raise TypeError("invalid variable type") + slong i = self.ctx.variable_to_index(var) res = create_fmpq_mpoly(self.ctx) @@ -942,20 +929,7 @@ cdef class fmpq_mpoly(flint_mpoly): """ cdef: fmpq_mpoly res - slong i = 0 - - if isinstance(var, str): - vars = {x: i for i, x in enumerate(self.ctx.names())} - if var not in vars: - raise ValueError("variable not in context") - else: - i = vars[var] - elif isinstance(var, int): - if not 0 <= var < self.ctx.nvars(): - raise IndexError("generator index out of range") - i = var - else: - raise TypeError("invalid variable type") + slong i = self.ctx.variable_to_index(var) res = create_fmpq_mpoly(self.ctx) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index cb1b323b..2038355f 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -915,20 +915,7 @@ cdef class fmpz_mpoly(flint_mpoly): """ cdef: fmpz_mpoly res - slong i = 0 - - if isinstance(var, str): - vars = {x: i for i, x in enumerate(self.ctx.names())} - if var not in vars: - raise ValueError("variable not in context") - else: - i = vars[var] - elif isinstance(var, int): - if not 0 <= var < self.ctx.nvars(): - raise IndexError("generator index out of range") - i = var - else: - raise TypeError("invalid variable type") + slong i = self.ctx.variable_to_index(var) res = create_fmpz_mpoly(self.ctx) @@ -955,20 +942,7 @@ cdef class fmpz_mpoly(flint_mpoly): cdef: fmpz_mpoly res fmpz scale - slong i = 0 - - if isinstance(var, str): - vars = {x: i for i, x in enumerate(self.ctx.names())} - if var not in vars: - raise ValueError("variable not in context") - else: - i = vars[var] - elif isinstance(var, int): - if not 0 <= var < self.ctx.nvars(): - raise IndexError("generator index out of range") - i = var - else: - raise TypeError("invalid variable type") + slong i = self.ctx.variable_to_index(var) res = create_fmpz_mpoly(self.ctx) From 250861faf63ca56a0fa373567690b29aafe0864c Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Fri, 21 Jun 2024 14:20:19 +1000 Subject: [PATCH 69/95] Use setdefault over normal dict insertion --- src/flint/flint_base/flint_base.pyx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index 6a0856d0..297734dd 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -221,8 +221,7 @@ cdef class flint_mpoly_context(flint_elem): ctx = cls._ctx_cache.get(key) if ctx is None: - ctx = cls(*key) - cls._ctx_cache[key] = ctx + ctx = cls._ctx_cache.setdefault(key, cls(*key)) return ctx @classmethod From bc61ca94c85a0c760c700f573253226353ee3353 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Fri, 21 Jun 2024 14:28:28 +1000 Subject: [PATCH 70/95] Use ascii over utf-8 decoding for c strings --- src/flint/flint_base/flint_base.pyx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index 297734dd..6eea2a28 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -155,11 +155,11 @@ cdef class flint_mpoly_context(flint_elem): def name(self, long i): if not 0 <= i < len(self.py_names): - raise IndexError("variable name index out of range") - return self.py_names[i].decode('utf-8') + raise IndexError("Variable name index out of range") + return self.py_names[i].decode("ascii") def names(self): - return tuple(name.decode('utf-8') for name in self.py_names) + return tuple(name.decode("ascii") for name in self.py_names) def gens(self): return tuple(self.gen(i) for i in range(self.nvars())) @@ -230,7 +230,7 @@ cdef class flint_mpoly_context(flint_elem): nvars=ctx.nvars(), ordering=ctx.ordering(), names=None, - nametup=tuple(str(s, 'utf-8') for s in ctx.py_names) + nametup=ctx.names() ) From e1758546deff5ff8407e67889eb42ce85951d3b3 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Fri, 21 Jun 2024 14:30:24 +1000 Subject: [PATCH 71/95] Typos --- src/flint/flint_base/flint_base.pyx | 2 +- src/flint/types/fmpq_mpoly.pyx | 2 +- src/flint/types/fmpz_mpoly.pyx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index 6eea2a28..161432f8 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -165,7 +165,7 @@ cdef class flint_mpoly_context(flint_elem): return tuple(self.gen(i) for i in range(self.nvars())) def variable_to_index(self, var: Union[int, str]): - """Convert a variable name string or possible index to it's index in the context.""" + """Convert a variable name string or possible index to its index in the context.""" if isinstance(var, str): vars = {x: i for i, x in enumerate(self.names())} if var not in vars: diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index b8310997..b398ed0b 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -913,7 +913,7 @@ cdef class fmpq_mpoly(flint_mpoly): def integral(self, var): """ Return the integral of this polynomial*B with respect to the provided variable - where B is minimal. The argument and either be the variable as a string, or + where B is minimal. The argument can either be the variable as a string, or the index of the variable in the context. >>> from flint import Ordering diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 2038355f..ee7aa1ce 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -925,7 +925,7 @@ cdef class fmpz_mpoly(flint_mpoly): def integral(self, var): """ Return the integral of this polynomial*B with respect to the provided variable - where B is minimal. The argument and either be the variable as a string, or + where B is minimal. The argument can either be the variable as a string, or the index of the variable in the context. >>> from flint import Ordering From 7ec3112cbc98fa5bcdeae1e057d8988d661fc8a3 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Fri, 21 Jun 2024 14:31:42 +1000 Subject: [PATCH 72/95] Remove unused var --- src/flint/types/fmpq_mpoly.pyx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index b398ed0b..f784420f 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -159,20 +159,20 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): """ cdef: fmpz_vec exp_vec - slong i, nvars = self.nvars() + slong nvars = self.nvars() fmpq_mpoly res if not isinstance(d, dict): - raise ValueError("expected a dictionary") + raise ValueError("Expected a dictionary") res = create_fmpq_mpoly(self) - for i, (k, v) in enumerate(d.items()): + for k, v in d.items(): o = any_as_fmpq(v) if o is NotImplemented: - raise TypeError(f"cannot coerce coefficient '{v}' to fmpq") + raise TypeError(f"Cannot coerce coefficient '{v}' to fmpq") elif len(k) != nvars: - raise ValueError(f"expected {nvars} exponents, got {len(k)}") + raise ValueError(f"Expected {nvars} exponents, got {len(k)}") exp_vec = fmpz_vec(k) From 96fa8e649ea94e60fe2ea979c0a148266ce06e50 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Fri, 21 Jun 2024 14:36:04 +1000 Subject: [PATCH 73/95] Prefer .index over dict, raise nicer exception --- src/flint/flint_base/flint_base.pyx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index 161432f8..21a57d54 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -167,11 +167,10 @@ cdef class flint_mpoly_context(flint_elem): def variable_to_index(self, var: Union[int, str]): """Convert a variable name string or possible index to its index in the context.""" if isinstance(var, str): - vars = {x: i for i, x in enumerate(self.names())} - if var not in vars: + try: + i = self.names().index(var) + except ValueError: raise ValueError("Variable not in context") - else: - i = vars[var] elif isinstance(var, int): if not 0 <= var < self.nvars(): raise IndexError("Generator index out of range") From 59bce437c3a1103be9cf698d3234ab299e173636 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Fri, 21 Jun 2024 16:40:24 +1000 Subject: [PATCH 74/95] fixup! Remove fmpz_mpoly_q --- src/flint/flint_base/flint_base.pxd | 3 --- src/flint/flint_base/flint_base.pyx | 4 ---- 2 files changed, 7 deletions(-) diff --git a/src/flint/flint_base/flint_base.pxd b/src/flint/flint_base/flint_base.pxd index edf9b25b..239cab54 100644 --- a/src/flint/flint_base/flint_base.pxd +++ b/src/flint/flint_base/flint_base.pxd @@ -22,9 +22,6 @@ cdef class flint_mat(flint_elem): cdef class flint_series(flint_elem): pass -cdef class flint_rational_function(flint_elem): - pass - cpdef enum Ordering: lex, deglex, degrevlex diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index 21a57d54..c47dc6eb 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -316,10 +316,6 @@ cdef class flint_mat(flint_elem): tolist = table -cdef class flint_rational_function(flint_elem): - pass - - 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") From 492f9629bb8cae3e5b213b85f6af84c31c09bb2a Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Mon, 24 Jun 2024 12:58:29 +1000 Subject: [PATCH 75/95] Typo --- src/flint/types/fmpq_mpoly.pyx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index f784420f..66257e6a 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -912,9 +912,8 @@ cdef class fmpq_mpoly(flint_mpoly): def integral(self, var): """ - Return the integral of this polynomial*B with respect to the provided variable - where B is minimal. The argument can either be the variable as a string, or - the index of the variable in the context. + Return the integral of this polynomial with respect to the provided variable The argument can either be the + variable as a string, or the index of the variable in the context. >>> from flint import Ordering >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') From fcd92944fbe7ac83415ec5da6fdd128cdc2e46a5 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Mon, 24 Jun 2024 13:57:57 +1000 Subject: [PATCH 76/95] Remove hash --- src/flint/flint_base/flint_base.pyx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index c47dc6eb..515267e6 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -241,10 +241,6 @@ cdef class flint_mpoly(flint_elem): def leading_coefficient(self): return self.coefficient(0) - def __hash__(self): - s = repr(self) - return hash(s) - def to_dict(self): return {self.exponent_tuple(i): self.coefficient(i) for i in range(len(self))} From efb2c27aaa41f509a4edc9c62beaa7a726efec1b Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Mon, 24 Jun 2024 14:00:27 +1000 Subject: [PATCH 77/95] Clarify integral doc string --- src/flint/types/fmpz_mpoly.pyx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index ee7aa1ce..cefbf7a1 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -924,9 +924,10 @@ cdef class fmpz_mpoly(flint_mpoly): def integral(self, var): """ - Return the integral of this polynomial*B with respect to the provided variable - where B is minimal. The argument can either be the variable as a string, or - the index of the variable in the context. + Return the integral of this polynomial*B with respect to the provided variable where B is minimal. That is, + given p this method returns (B, P) such that P' = B*p or P/B is the formal integral of p. + + The argument can either be the variable as a string, or the index of the variable in the context. >>> from flint import Ordering >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') From 4bef17755ceca55a8502ed0f07b3d13217cd3d14 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Mon, 24 Jun 2024 16:00:00 +1000 Subject: [PATCH 78/95] Split __call__ methods --- src/flint/test/test_all.py | 26 ++--- src/flint/types/fmpq_mpoly.pyx | 160 +++++++++++++--------------- src/flint/types/fmpz_mpoly.pyx | 186 +++++++++++++++++---------------- 3 files changed, 183 insertions(+), 189 deletions(-) diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index 1ae98f46..cf5ed4d1 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -2789,9 +2789,11 @@ def quick_poly(): assert P(1, ctx=ctx).total_degree() == 0 p = quick_poly() - assert p(0, 0) == p(0, S(0)) == p(S(0), S(0)) == p(x1=S(0), x0=S(0)) == S(1) == 1 - assert p(1, 1) == p(x1=S(1), x0=S(1)) == S(10) == 10 - assert p(x0=p(x1=0)) == mpoly({ + assert p(0, 0) == p(0, S(0)) == p(S(0), S(0)) == S(1) == 1 + assert p(1, 1) == S(10) == 10 + + assert p.subs({"x1": S(0), "x0": S(0)}) == ctx.from_dict({(0, 0): 1}) + assert p.compose(p.subs({"x1": 0}), ctx.from_dict({(0, 1): 1})) == mpoly({ (2, 2): 36, (1, 2): 24, (1, 0): 9, @@ -2799,18 +2801,18 @@ def quick_poly(): (0, 1): 2, (0, 0): 4 }) - assert p() == p - assert p(x0=ctx.from_dict({(1, 0): 1}), x1=ctx.from_dict({(0, 1): 1})) == p + assert p.compose(ctx.from_dict({(1, 0): 1}), ctx.from_dict({(0, 1): 1})) == p - assert raises(lambda: p(x0=None), TypeError) - assert raises(lambda: p(x0=None, x1=None), TypeError) + assert raises(lambda: p(None, None), TypeError) assert raises(lambda: p(1), ValueError) - assert raises(lambda: p(1, x1=1), ValueError) - assert raises(lambda: p(a=1), ValueError) assert raises(lambda: p(0, 1, 2), ValueError) - assert raises(lambda: p(x0=0, x1=1, x2=2), ValueError) - assert raises(lambda: p(x0=p, x1=P(ctx=ctx1)), IncompatibleContextError) - assert raises(lambda: P(ctx=ctx1)(x0=p), IncompatibleContextError) + + assert raises(lambda: p.subs({"x0": None}), TypeError) + assert raises(lambda: p.subs({"x0": None, "x1": None}), TypeError) + assert raises(lambda: p.subs({"a": 1}), ValueError) + assert raises(lambda: p.subs({"x0": 0, "x1": 1, "x2": 2}), ValueError) + + assert raises(lambda: p.compose(p, P(ctx=ctx1)), IncompatibleContextError) assert bool(P(ctx=ctx)) is False assert bool(P(1, ctx=ctx)) is True diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 66257e6a..5308b8cc 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -576,103 +576,89 @@ cdef class fmpq_mpoly(flint_mpoly): def __rmod__(self, other): return divmod(other, self)[1] - def __call__(self, *args, **kwargs): + def __call__(self, *args) -> fmpq: + cdef: + fmpq_vec V + fmpq vres + slong nvars = self.ctx.nvars(), nargs = len(args) + + if nargs < nvars: + raise ValueError("Not enough arguments provided") + elif nargs > nvars: + raise ValueError("More arguments provided than variables") + + args_fmpq = tuple(any_as_fmpq(v) for v in args) + for arg in args_fmpq: + if arg is NotImplemented: + raise TypeError(f"Cannot coerce argument ('{arg}') to fmpq") + + V = fmpq_vec(args_fmpq, double_indirect=True) + vres = fmpq.__new__(fmpq) + if fmpq_mpoly_evaluate_all_fmpq(vres.val, self.val, V.double_indirect, self.ctx.val) == 0: + raise ValueError("Unreasonably large polynomial") # pragma: no cover + return vres + + def subs(self, dict_args) -> fmpq_mpoly: + """ + Partial evaluate this polynomial. + """ cdef: fmpq_mpoly res fmpq_mpoly res2 + slong i, nargs + + partial_args = tuple((i, dict_args[x]) for i, x in enumerate(self.ctx.names()) if x in dict_args) + nargs = len(partial_args) + + # If we've been provided with an invalid keyword arg then the length of our filter + # args will be less than what we've been provided with. + # If the length is equal to the number of variables then all arguments have been provided + # otherwise we need to do partial application + if nargs < len(dict_args): + raise ValueError("Unknown keyword argument provided") + + args_fmpq = tuple(any_as_fmpq(v) for _, v in partial_args) + for arg in args_fmpq: + if arg is NotImplemented: + raise TypeError(f"Cannot coerce argument ('{arg}') to fmpq") + + # Partial application with args in Z. We evaluate the polynomial one variable at a time + res = create_fmpq_mpoly(self.ctx) + res2 = create_fmpq_mpoly(self.ctx) + + fmpq_mpoly_set(res2.val, self.val, self.ctx.val) + for (i, _), arg in zip(partial_args, args_fmpq): + if fmpq_mpoly_evaluate_one_fmpq(res.val, res2.val, i, (arg).val, self.ctx.val) == 0: + raise ValueError("Unreasonably large polynomial") # pragma: no cover + fmpq_mpoly_set(res2.val, res.val, self.ctx.val) + return res + + def compose(self, *args) -> fmpq_mpoly: + """ + Compose this polynomial with other fmpq_mpolys. Takes positional arguments. + """ + cdef: + fmpq_mpoly res fmpq_mpoly_ctx res_ctx - fmpq_vec V - fmpq vres fmpq_mpoly_vec C slong i, nvars = self.ctx.nvars(), nargs = len(args) - if not args and not kwargs: - return self - elif args and kwargs: - raise ValueError("only supply positional or keyword arguments") - elif len(args) < nvars and not kwargs: - raise ValueError("partial application requires keyword arguments") - - if kwargs: - # Sort and filter the provided keyword args to only valid ones - partial_args = tuple((i, kwargs[x]) for i, x in enumerate(self.ctx.names()) if x in kwargs) - nargs = len(partial_args) - - # If we've been provided with an invalid keyword arg then the length of our filter - # args will be less than what we've been provided with. - # If the length is equal to the number of variables then all arguments have been provided - # otherwise we need to do partial application - if nargs < len(kwargs): - raise ValueError("unknown keyword argument provided") - elif nargs == nvars: - args = tuple(arg for _, arg in partial_args) - kwargs = None + if nargs < nvars: + raise ValueError("Not enough arguments provided") elif nargs > nvars: - raise ValueError("more arguments provided than variables") + raise ValueError("More arguments provided than variables") + elif not all(typecheck(arg, fmpq_mpoly) for arg in args): + raise TypeError("All arguments must be fmpq_mpolys") - if kwargs: - args_fmpq = tuple(any_as_fmpq(v) for _, v in partial_args) - else: - args_fmpq = tuple(any_as_fmpq(v) for v in args) - - all_fmpq = NotImplemented not in args_fmpq - - if args and all_fmpq: - # Normal evaluation - V = fmpq_vec(args_fmpq, double_indirect=True) - vres = fmpq.__new__(fmpq) - if fmpq_mpoly_evaluate_all_fmpq(vres.val, self.val, V.double_indirect, self.ctx.val) == 0: - raise ValueError("unreasonably large polynomial") # pragma: no cover - return vres - elif kwargs and all_fmpq: - # Partial application with args in Z. We evaluate the polynomial one variable at a time - res = create_fmpq_mpoly(self.ctx) - res2 = create_fmpq_mpoly(self.ctx) + res_ctx = ( args[0]).ctx + if not all(( args[i]).ctx is res_ctx for i in range(1, len(args))): + raise IncompatibleContextError("All arguments must share the same context") - fmpq_mpoly_set(res2.val, self.val, self.ctx.val) - for (i, _), arg in zip(partial_args, args_fmpq): - if fmpq_mpoly_evaluate_one_fmpq(res.val, res2.val, i, (arg).val, self.ctx.val) == 0: - raise ValueError("unreasonably large polynomial") # pragma: no cover - fmpq_mpoly_set(res2.val, res.val, self.ctx.val) - return res - elif args and not all_fmpq: - # Complete function composition - if not all(typecheck(arg, fmpq_mpoly) for arg in args): - raise TypeError("all arguments must be fmpq_mpolys") - res_ctx = ( args[0]).ctx - if not all(( args[i]).ctx is res_ctx for i in range(1, len(args))): - raise IncompatibleContextError("all arguments must share the same context") - - C = fmpq_mpoly_vec(args, self.ctx, double_indirect=True) - res = create_fmpq_mpoly(self.ctx) - if fmpq_mpoly_compose_fmpq_mpoly(res.val, self.val, C.double_indirect, self.ctx.val, res_ctx.val) == 0: - raise ValueError("unreasonably large polynomial") # pragma: no cover - return res - else: - # Partial function composition. We do this by composing with all arguments, however the ones - # that have not be provided are set to the trivial monomial. This is why we require all the - # polynomial itself, and all arguments exist in the same context, otherwise we have no way of - # finding the correct monomial to use. - if not all(typecheck(arg, fmpq_mpoly) for _, arg in partial_args): - raise TypeError("all arguments must be fmpq_mpolys for partial composition") - if not all(( arg).ctx is self.ctx for _, arg in partial_args): - raise IncompatibleContextError("all arguments must share the same context") - - polys = [None] * nvars - for i, poly in partial_args: - polys[i] = poly - - for i in range(nvars): - if polys[i] is None: - vec = [0] * nvars - vec[i] = 1 - polys[i] = self.ctx.from_dict({tuple(vec): 1}) - - C = fmpq_mpoly_vec(polys, self.ctx, double_indirect=True) - res = create_fmpq_mpoly(self.ctx) - if fmpq_mpoly_compose_fmpq_mpoly(res.val, self.val, C.double_indirect, self.ctx.val, self.ctx.val) == 0: - raise ValueError("unreasonably large polynomial") # pragma: no cover - return res + C = fmpq_mpoly_vec(args, self.ctx, double_indirect=True) + res = create_fmpq_mpoly(self.ctx) + if fmpq_mpoly_compose_fmpq_mpoly(res.val, self.val, C.double_indirect, self.ctx.val, res_ctx.val) == 0: + raise ValueError("Unreasonably large polynomial") # pragma: no cover + return res def context(self): """ diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index cefbf7a1..ff70872a 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -67,7 +67,6 @@ from flint.flintlib.fmpz_mpoly_factor cimport ( ) from cpython.object cimport Py_EQ, Py_NE -cimport cython cimport libc.stdlib cdef dict _fmpz_mpoly_ctx_cache = {} @@ -559,103 +558,110 @@ cdef class fmpz_mpoly(flint_mpoly): def __rmod__(self, other): return divmod(other, self)[1] - def __call__(self, *args, **kwargs): + def __call__(self, *args) -> fmpz: + cdef: + fmpz_vec V + fmpz vres + slong nvars = self.ctx.nvars(), nargs = len(args) + + if nargs < nvars: + raise ValueError("Not enough arguments provided") + elif nargs > nvars: + raise ValueError("More arguments provided than variables") + + args_fmpz = tuple(any_as_fmpz(v) for v in args) + for arg in args_fmpz: + if arg is NotImplemented: + raise TypeError(f"Cannot coerce argument ('{arg}') to fmpz") + + V = fmpz_vec(args_fmpz, double_indirect=True) + vres = fmpz.__new__(fmpz) + if fmpz_mpoly_evaluate_all_fmpz(vres.val, self.val, V.double_indirect, self.ctx.val) == 0: + raise ValueError("Unreasonably large polynomial") # pragma: no cover + return vres + + def subs(self, dict_args) -> fmpz_mpoly: + """ + Partial evaluate this polynomial with select constants. All arguments must be fmpz. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f.subs({"x1": 0}) + 2*x0 + 1 + + """ cdef: fmpz_mpoly res fmpz_mpoly res2 + slong i, nargs + + partial_args = tuple((i, dict_args[x]) for i, x in enumerate(self.ctx.names()) if x in dict_args) + nargs = len(partial_args) + + # If we've been provided with an invalid keyword arg then the length of our filter + # args will be less than what we've been provided with. + # If the length is equal to the number of variables then all arguments have been provided + # otherwise we need to do partial application + if nargs < len(dict_args): + raise ValueError("Unknown keyword argument provided") + + args_fmpz = tuple(any_as_fmpz(v) for _, v in partial_args) + for arg in args_fmpz: + if arg is NotImplemented: + raise TypeError(f"Cannot coerce argument ('{arg}') to fmpz") + + # Partial application with args in Z. We evaluate the polynomial one variable at a time + res = create_fmpz_mpoly(self.ctx) + res2 = create_fmpz_mpoly(self.ctx) + + fmpz_mpoly_set(res2.val, self.val, self.ctx.val) + for (i, _), arg in zip(partial_args, args_fmpz): + if fmpz_mpoly_evaluate_one_fmpz(res.val, res2.val, i, (arg).val, self.ctx.val) == 0: + raise ValueError("Unreasonably large polynomial") # pragma: no cover + fmpz_mpoly_set(res2.val, res.val, self.ctx.val) + return res + + def compose(self, *args) -> fmpz_mpoly: + """ + Compose this polynomial with other fmpz_mpolys. All arguments must share the same context, it may different + from this polynomials context. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(1, Ordering.lex, 'x') + >>> ctx1 = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'y') + >>> f = ctx.from_dict({(2,): 1}) + >>> g = ctx1.from_dict({(1, 0): 1, (0, 1): 1}) + >>> f + x^2 + >>> g + y0 + y1 + >>> f.compose(g) + y0^2 + 2*y0*y1 + y1^2 + + """ + cdef: + fmpz_mpoly res fmpz_mpoly_ctx res_ctx - fmpz_vec V - fmpz vres fmpz_mpoly_vec C slong i, nvars = self.ctx.nvars(), nargs = len(args) - if not args and not kwargs: - return self - elif args and kwargs: - raise ValueError("only supply positional or keyword arguments") - elif len(args) < nvars and not kwargs: - raise ValueError("partial application requires keyword arguments") - - if kwargs: - # Sort and filter the provided keyword args to only valid ones - partial_args = tuple((i, kwargs[x]) for i, x in enumerate(self.ctx.names()) if x in kwargs) - nargs = len(partial_args) - - # If we've been provided with an invalid keyword arg then the length of our filter - # args will be less than what we've been provided with. - # If the length is equal to the number of variables then all arguments have been provided - # otherwise we need to do partial application - if nargs < len(kwargs): - raise ValueError("unknown keyword argument provided") - elif nargs == nvars: - args = tuple(arg for _, arg in partial_args) - kwargs = None + if nargs < nvars: + raise ValueError("Not enough arguments provided") elif nargs > nvars: - raise ValueError("more arguments provided than variables") - - if kwargs: - args_fmpz = tuple(any_as_fmpz(v) for _, v in partial_args) - else: - args_fmpz = tuple(any_as_fmpz(v) for v in args) - - all_fmpz = NotImplemented not in args_fmpz - - if args and all_fmpz: - # Normal evaluation - V = fmpz_vec(args_fmpz, double_indirect=True) - vres = fmpz.__new__(fmpz) - if fmpz_mpoly_evaluate_all_fmpz(vres.val, self.val, V.double_indirect, self.ctx.val) == 0: - raise ValueError("unreasonably large polynomial") # pragma: no cover - return vres - elif kwargs and all_fmpz: - # Partial application with args in Z. We evaluate the polynomial one variable at a time - res = create_fmpz_mpoly(self.ctx) - res2 = create_fmpz_mpoly(self.ctx) - - fmpz_mpoly_set(res2.val, self.val, self.ctx.val) - for (i, _), arg in zip(partial_args, args_fmpz): - if fmpz_mpoly_evaluate_one_fmpz(res.val, res2.val, i, (arg).val, self.ctx.val) == 0: - raise ValueError("unreasonably large polynomial") # pragma: no cover - fmpz_mpoly_set(res2.val, res.val, self.ctx.val) - return res - elif args and not all_fmpz: - # Complete function composition - if not all(typecheck(arg, fmpz_mpoly) for arg in args): - raise TypeError("all arguments must be fmpz_mpolys") - res_ctx = ( args[0]).ctx - if not all(( args[i]).ctx is res_ctx for i in range(1, len(args))): - raise IncompatibleContextError("all arguments must share the same context") - - C = fmpz_mpoly_vec(args, self.ctx, double_indirect=True) - res = create_fmpz_mpoly(self.ctx) - if fmpz_mpoly_compose_fmpz_mpoly(res.val, self.val, C.double_indirect, self.ctx.val, res_ctx.val) == 0: - raise ValueError("unreasonably large polynomial") # pragma: no cover - return res - else: - # Partial function composition. We do this by composing with all arguments, however the ones - # that have not be provided are set to the trivial monomial. This is why we require all the - # polynomial itself, and all arguments exist in the same context, otherwise we have no way of - # finding the correct monomial to use. - if not all(typecheck(arg, fmpz_mpoly) for _, arg in partial_args): - raise TypeError("all arguments must be fmpz_mpolys for partial composition") - if not all(( arg).ctx is self.ctx for _, arg in partial_args): - raise IncompatibleContextError("all arguments must share the same context") - - polys = [None] * nvars - for i, poly in partial_args: - polys[i] = poly - - for i in range(nvars): - if polys[i] is None: - vec = [0] * nvars - vec[i] = 1 - polys[i] = self.ctx.from_dict({tuple(vec): 1}) - - C = fmpz_mpoly_vec(polys, self.ctx, double_indirect=True) - res = create_fmpz_mpoly(self.ctx) - if fmpz_mpoly_compose_fmpz_mpoly(res.val, self.val, C.double_indirect, self.ctx.val, self.ctx.val) == 0: - raise ValueError("unreasonably large polynomial") # pragma: no cover - return res + raise ValueError("More arguments provided than variables") + elif not all(typecheck(arg, fmpz_mpoly) for arg in args): + raise TypeError("All arguments must be fmpz_mpolys") + + res_ctx = ( args[0]).ctx + if not all(( args[i]).ctx is res_ctx for i in range(1, len(args))): + raise IncompatibleContextError("All arguments must share the same context") + + C = fmpz_mpoly_vec(args, res_ctx, double_indirect=True) + res = create_fmpz_mpoly(res_ctx) + if fmpz_mpoly_compose_fmpz_mpoly(res.val, self.val, C.double_indirect, self.ctx.val, res_ctx.val) == 0: + raise ValueError("Unreasonably large polynomial") # pragma: no cover + return res def context(self): """ From ada4b2f62abf816ebe49b9189ce2eb506064a38e Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Mon, 24 Jun 2024 16:40:32 +1000 Subject: [PATCH 79/95] Raise exception on parsing failure --- src/flint/types/fmpq_mpoly.pyx | 3 ++- src/flint/types/fmpz_mpoly.pyx | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 5308b8cc..851053a0 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -230,7 +230,8 @@ cdef class fmpq_mpoly(flint_mpoly): raise ValueError("Cannot parse a polynomial without context") val = val.encode("ascii") init_fmpq_mpoly(self, ctx) - fmpq_mpoly_set_str_pretty(self.val, val, self.ctx.c_names, self.ctx.val) + if fmpq_mpoly_set_str_pretty(self.val, val, self.ctx.c_names, self.ctx.val) == -1: + raise ValueError("Unable to parse fmpq_mpoly from string") fmpq_mpoly_sort_terms(self.val, self.ctx.val) else: v = any_as_fmpq(val) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index ff70872a..0445fbdc 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -210,7 +210,8 @@ cdef class fmpz_mpoly(flint_mpoly): raise ValueError("Cannot parse a polynomial without context") val = bytes(val, 'utf-8') init_fmpz_mpoly(self, ctx) - fmpz_mpoly_set_str_pretty(self.val, val, self.ctx.c_names, self.ctx.val) + if fmpz_mpoly_set_str_pretty(self.val, val, self.ctx.c_names, self.ctx.val) == -1: + raise ValueError("Unable to parse fmpz_mpoly from string") fmpz_mpoly_sort_terms(self.val, self.ctx.val) else: v = any_as_fmpz(val) From e093ac1866976f636a1e19354598e1680c616eaf Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Mon, 1 Jul 2024 14:33:21 +1000 Subject: [PATCH 80/95] Remove unnecessary secondary results --- src/flint/types/fmpq_mpoly.pyx | 7 ++----- src/flint/types/fmpz_mpoly.pyx | 7 ++----- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 851053a0..f82734eb 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -605,7 +605,6 @@ cdef class fmpq_mpoly(flint_mpoly): """ cdef: fmpq_mpoly res - fmpq_mpoly res2 slong i, nargs partial_args = tuple((i, dict_args[x]) for i, x in enumerate(self.ctx.names()) if x in dict_args) @@ -625,13 +624,11 @@ cdef class fmpq_mpoly(flint_mpoly): # Partial application with args in Z. We evaluate the polynomial one variable at a time res = create_fmpq_mpoly(self.ctx) - res2 = create_fmpq_mpoly(self.ctx) - fmpq_mpoly_set(res2.val, self.val, self.ctx.val) + fmpq_mpoly_set(res.val, self.val, self.ctx.val) for (i, _), arg in zip(partial_args, args_fmpq): - if fmpq_mpoly_evaluate_one_fmpq(res.val, res2.val, i, (arg).val, self.ctx.val) == 0: + if fmpq_mpoly_evaluate_one_fmpq(res.val, res.val, i, (arg).val, self.ctx.val) == 0: raise ValueError("Unreasonably large polynomial") # pragma: no cover - fmpq_mpoly_set(res2.val, res.val, self.ctx.val) return res def compose(self, *args) -> fmpq_mpoly: diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 0445fbdc..6e7596ec 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -594,7 +594,6 @@ cdef class fmpz_mpoly(flint_mpoly): """ cdef: fmpz_mpoly res - fmpz_mpoly res2 slong i, nargs partial_args = tuple((i, dict_args[x]) for i, x in enumerate(self.ctx.names()) if x in dict_args) @@ -614,13 +613,11 @@ cdef class fmpz_mpoly(flint_mpoly): # Partial application with args in Z. We evaluate the polynomial one variable at a time res = create_fmpz_mpoly(self.ctx) - res2 = create_fmpz_mpoly(self.ctx) - fmpz_mpoly_set(res2.val, self.val, self.ctx.val) + fmpz_mpoly_set(res.val, self.val, self.ctx.val) for (i, _), arg in zip(partial_args, args_fmpz): - if fmpz_mpoly_evaluate_one_fmpz(res.val, res2.val, i, (arg).val, self.ctx.val) == 0: + if fmpz_mpoly_evaluate_one_fmpz(res.val, res.val, i, (arg).val, self.ctx.val) == 0: raise ValueError("Unreasonably large polynomial") # pragma: no cover - fmpz_mpoly_set(res2.val, res.val, self.ctx.val) return res def compose(self, *args) -> fmpz_mpoly: From faa1fb21cfbbdad16bc21c659c1e1fbc454a240c Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Mon, 1 Jul 2024 20:39:24 +1000 Subject: [PATCH 81/95] Drop sequence-like indexing --- src/flint/flint_base/flint_base.pyx | 15 ++++++++ src/flint/test/test_all.py | 22 +++++------ src/flint/types/fmpq_mpoly.pyx | 60 ++++++++++++----------------- src/flint/types/fmpz_mpoly.pyx | 56 +++++++++++---------------- 4 files changed, 71 insertions(+), 82 deletions(-) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index 515267e6..e2a42d8c 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -244,6 +244,21 @@ cdef class flint_mpoly(flint_elem): def to_dict(self): return {self.exponent_tuple(i): self.coefficient(i) for i in range(len(self))} + def __contains__(self, x): + """ + Returns True if `self` contains a term with exponent vector `x` and a non-zero coefficient. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) + >>> (1, 1) in p + True + >>> (5, 1) in p + False + + """ + return bool(self[x]) + cdef class flint_series(flint_elem): """ diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index cf5ed4d1..e9e961ff 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -2746,10 +2746,8 @@ def quick_poly(): assert raises(lambda: p.coefficient(-1), IndexError) assert raises(lambda: p.coefficient(10), IndexError) - assert p[0] == mpoly({(2, 2): 4}) - assert p[3] == mpoly({(0, 0): 1}) - assert raises(lambda: p[-1], IndexError) - assert raises(lambda: p[4], IndexError) + assert raises(lambda: p[-1], TypeError) + assert raises(lambda: p[4], TypeError) assert p[(2, 2)] == 4 assert p[(0, 0)] == 1 @@ -2758,22 +2756,22 @@ def quick_poly(): assert raises(lambda: p["bad"], TypeError) p = quick_poly() - p[1] = S(10) + p[(1, 0)] = S(10) assert p == mpoly({(0, 0): 1, (0, 1): 2, (1, 0): 10, (2, 2): 4}) p = quick_poly() - p[(1, 0)] = S(10) - assert p == mpoly({(0, 0): 1, (0, 1): 2, (1, 0): 10, (2, 2): 4}) + p[(1, 0)] = p[(1, 0)] + assert p == quick_poly() + assert (1, 0) in p + assert (100, 100) not in p - assert raises(lambda: p.__setitem__(-1, 1), IndexError) - assert raises(lambda: p.__setitem__(4, 1), IndexError) + assert raises(lambda: p.__setitem__((4,), 1), ValueError) assert raises(lambda: p.__setitem__((1,), 1), ValueError) assert raises(lambda: p.__setitem__((1, "bad"), 1), TypeError) - assert raises(lambda: p.__setitem__("bad", 1), TypeError) + assert raises(lambda: p.__setitem__(("bad", 1), 1), TypeError) - assert raises(lambda: p.__setitem__(2, None), TypeError) - assert raises(lambda: p.__setitem__(-1, 1), IndexError) + assert raises(lambda: p.__setitem__((2, 1), None), TypeError) assert P(ctx=ctx).repr() == f"{ctx.__class__.__name__}(2, '', ('x0', 'x1')).from_dict({{}})" assert P(1, ctx=ctx).repr() == f"{ctx.__class__.__name__}(2, '', ('x0', 'x1')).from_dict({{(0, 0): 1}})" diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index f82734eb..023f40d0 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -265,14 +265,13 @@ cdef class fmpq_mpoly(flint_mpoly): def __getitem__(self, x): """ - Return the term at index `x` if `x` is an `int`, or the term with the exponent - vector `x` if `x` is a tuple of `int`s or `fmpq`s. + 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 >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) - >>> p[1] - 2*x1 >>> p[1, 1] 3 @@ -280,34 +279,28 @@ cdef class fmpq_mpoly(flint_mpoly): cdef: slong nvars = self.ctx.nvars() - if isinstance(x, int): - if not 0 <= x < fmpq_mpoly_length(self.val, self.ctx.val): - raise IndexError("term index out of range") - res = create_fmpq_mpoly(self.ctx) - fmpq_mpoly_get_term(( res).val, self.val, x, self.ctx.val) - return res - elif isinstance(x, tuple): - if len(x) != nvars: - raise ValueError("exponent vector provided does not match number of variables") - res = fmpq() - exp_vec = fmpz_vec(x, double_indirect=True) - fmpq_mpoly_get_coeff_fmpq_fmpz((res).val, self.val, exp_vec.double_indirect, self.ctx.val) - return res - else: - raise TypeError("index is not integer or tuple") + if not isinstance(x, tuple): + raise TypeError("Exponent vector index is not a tuple") + elif len(x) != nvars: + raise ValueError("Exponent vector provided does not match number of variables") + + res = fmpq() + exp_vec = fmpz_vec(x, double_indirect=True) + fmpq_mpoly_get_coeff_fmpq_fmpz((res).val, self.val, exp_vec.double_indirect, self.ctx.val) + return res def __setitem__(self, x, y): """ - Set the coefficient at index `x` to `y` if `x` is an `int`, or the term with - the exponent vector `x` if `x` is a tuple of `int`s or `fmpq`s. + 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. >>> from flint import Ordering >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) - >>> p[1] = 10 >>> p[1, 1] = 20 >>> p - 20*x0*x1 + 10*x1 + 20*x0*x1 + 2*x1 """ cdef: @@ -315,19 +308,14 @@ cdef class fmpq_mpoly(flint_mpoly): coeff = any_as_fmpq(y) if coeff is NotImplemented: - raise TypeError("provided coefficient not coercible to fmpq") - - if isinstance(x, int): - if not 0 <= x < fmpq_mpoly_length(self.val, self.ctx.val): - raise IndexError("term index out of range") - fmpq_mpoly_set_term_coeff_fmpq(self.val, x, (coeff).val, self.ctx.val) - elif isinstance(x, tuple): - if len(x) != nvars: - raise ValueError("exponent vector provided does not match number of variables") - exp_vec = fmpz_vec(x, double_indirect=True) - fmpq_mpoly_set_coeff_fmpq_fmpz(self.val, (coeff).val, exp_vec.double_indirect, self.ctx.val) - else: - raise TypeError("index is not integer or tuple") + raise TypeError("Provided coefficient not coercible to fmpq") + elif not isinstance(x, tuple): + raise TypeError("Exponent vector index is not a tuple") + elif len(x) != nvars: + raise ValueError("Exponent vector provided does not match number of variables") + + exp_vec = fmpz_vec(x, double_indirect=True) + fmpq_mpoly_set_coeff_fmpq_fmpz(self.val, (coeff).val, exp_vec.double_indirect, self.ctx.val) def __pos__(self): return self diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 6e7596ec..3abea61f 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -245,14 +245,13 @@ cdef class fmpz_mpoly(flint_mpoly): def __getitem__(self, x): """ - Return the term at index `x` if `x` is an `int`, or the term with the exponent - vector `x` if `x` is a tuple of `int`s or `fmpz`s. + 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 >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) - >>> p[1] - 2*x1 >>> p[1, 1] 3 @@ -260,34 +259,28 @@ cdef class fmpz_mpoly(flint_mpoly): cdef: slong nvars = self.ctx.nvars() - if isinstance(x, int): - if not 0 <= x < fmpz_mpoly_length(self.val, self.ctx.val): - raise IndexError("term index out of range") - res = create_fmpz_mpoly(self.ctx) - fmpz_mpoly_get_term(( res).val, self.val, x, self.ctx.val) - return res - elif isinstance(x, tuple): - if len(x) != nvars: - raise ValueError("exponent vector provided does not match number of variables") - res = fmpz() - exp_vec = fmpz_vec(x, double_indirect=True) - fmpz_mpoly_get_coeff_fmpz_fmpz((res).val, self.val, exp_vec.double_indirect, self.ctx.val) - return res - else: - raise TypeError("index is not integer or tuple") + if not isinstance(x, tuple): + raise TypeError("exponent vector index is not a tuple") + elif len(x) != nvars: + raise ValueError("exponent vector provided does not match number of variables") + + res = fmpz() + exp_vec = fmpz_vec(x, double_indirect=True) + fmpz_mpoly_get_coeff_fmpz_fmpz((res).val, self.val, exp_vec.double_indirect, self.ctx.val) + return res def __setitem__(self, x, y): """ - Set the coefficient at index `x` to `y` if `x` is an `int`, or the term with - the exponent vector `x` if `x` is a tuple of `int`s or `fmpz`s. + 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. >>> from flint import Ordering >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) - >>> p[1] = 10 >>> p[1, 1] = 20 >>> p - 20*x0*x1 + 10*x1 + 20*x0*x1 + 2*x1 """ cdef: @@ -296,18 +289,13 @@ cdef class fmpz_mpoly(flint_mpoly): coeff = any_as_fmpz(y) if coeff is NotImplemented: raise TypeError("provided coefficient not coercible to fmpz") + elif not isinstance(x, tuple): + raise TypeError("exponent vector index is not a tuple") + elif len(x) != nvars: + raise ValueError("exponent vector provided does not match number of variables") - if isinstance(x, int): - if not 0 <= x < fmpz_mpoly_length(self.val, self.ctx.val): - raise IndexError("term index out of range") - fmpz_mpoly_set_term_coeff_fmpz(self.val, x, (coeff).val, self.ctx.val) - elif isinstance(x, tuple): - if len(x) != nvars: - raise ValueError("exponent vector provided does not match number of variables") - exp_vec = fmpz_vec(x, double_indirect=True) - fmpz_mpoly_set_coeff_fmpz_fmpz(self.val, (coeff).val, exp_vec.double_indirect, self.ctx.val) - else: - raise TypeError("index is not integer or tuple") + exp_vec = fmpz_vec(x, double_indirect=True) + fmpz_mpoly_set_coeff_fmpz_fmpz(self.val, (coeff).val, exp_vec.double_indirect, self.ctx.val) def __pos__(self): return self From fbc685486a8c769d1b7fd82766d037833263dbbb Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Mon, 1 Jul 2024 21:23:09 +1000 Subject: [PATCH 82/95] Make fmpq_mpoly and fmpz_mpoly more dict-like --- src/flint/flint_base/flint_base.pyx | 21 ++++++++++++- src/flint/test/test_all.py | 5 +++ src/flint/types/fmpq_mpoly.pyx | 48 +++++++++++++++++++++++++++-- src/flint/types/fmpz_mpoly.pyx | 48 +++++++++++++++++++++++++++-- 4 files changed, 115 insertions(+), 7 deletions(-) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index e2a42d8c..929c02a2 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -248,7 +248,7 @@ cdef class flint_mpoly(flint_elem): """ Returns True if `self` contains a term with exponent vector `x` and a non-zero coefficient. - >>> from flint import Ordering + >>> from flint import fmpq_mpoly_ctx, Ordering >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> (1, 1) in p @@ -259,6 +259,25 @@ cdef class flint_mpoly(flint_elem): """ return bool(self[x]) + def __iter__(self): + return iter(self.keys()) + + def __pos__(self): + return self + + def items(self): + """ + Return the exponent vectors and coefficient of each term. + + >>> from flint import fmpq_mpoly_ctx, Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> list(f.items()) + [((1, 1), 4), ((1, 0), 2), ((0, 1), 3), ((0, 0), 1)] + + """ + return zip(self.keys(), self.values()) + cdef class flint_series(flint_elem): """ diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index e9e961ff..214622f4 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -2790,6 +2790,11 @@ def quick_poly(): assert p(0, 0) == p(0, S(0)) == p(S(0), S(0)) == S(1) == 1 assert p(1, 1) == S(10) == 10 + p = quick_poly() + assert p.keys() == [(2, 2), (1, 0), (0, 1), (0, 0)] + assert p.values() == [4, 3, 2, 1] + assert list(p.items()) == list(zip([(2, 2), (1, 0), (0, 1), (0, 0)], [4, 3, 2, 1])) + assert p.subs({"x1": S(0), "x0": S(0)}) == ctx.from_dict({(0, 0): 1}) assert p.compose(p.subs({"x1": 0}), ctx.from_dict({(0, 1): 1})) == mpoly({ (2, 2): 36, diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 023f40d0..deb67afc 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -317,9 +317,6 @@ cdef class fmpq_mpoly(flint_mpoly): exp_vec = fmpz_vec(x, double_indirect=True) fmpq_mpoly_set_coeff_fmpq_fmpz(self.val, (coeff).val, exp_vec.double_indirect, self.ctx.val) - def __pos__(self): - return self - def __neg__(self): cdef fmpq_mpoly res res = create_fmpq_mpoly(self.ctx) @@ -587,6 +584,51 @@ cdef class fmpq_mpoly(flint_mpoly): raise ValueError("Unreasonably large polynomial") # pragma: no cover return vres + def keys(self): + """ + Return the exponent vectors of each term as a tuple of fmpz. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f.keys() + [(1, 1), (1, 0), (0, 1), (0, 0)] + + """ + cdef: + slong i, nvars = self.ctx.nvars() + fmpz_vec vec = fmpz_vec(nvars, double_indirect=True) + + 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()) + + return res + + def values(self): + """ + Return the coefficients of each term as a fmpq. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f.values() + [4, 2, 3, 1] + + """ + cdef: + fmpq coeff + slong i + + res = [] + for i in range(len(self)): + coeff = fmpq.__new__(fmpq) + fmpq_mpoly_get_term_coeff_fmpq(coeff.val, self.val, i, self.ctx.val) + res.append(coeff) + + return res + def subs(self, dict_args) -> fmpq_mpoly: """ Partial evaluate this polynomial. diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 3abea61f..16830200 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -297,9 +297,6 @@ cdef class fmpz_mpoly(flint_mpoly): exp_vec = fmpz_vec(x, double_indirect=True) fmpz_mpoly_set_coeff_fmpz_fmpz(self.val, (coeff).val, exp_vec.double_indirect, self.ctx.val) - def __pos__(self): - return self - def __neg__(self): cdef fmpz_mpoly res res = create_fmpz_mpoly(self.ctx) @@ -569,6 +566,51 @@ cdef class fmpz_mpoly(flint_mpoly): raise ValueError("Unreasonably large polynomial") # pragma: no cover return vres + def keys(self): + """ + Return the exponent vectors of each term as a tuple of fmpz. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f.keys() + [(1, 1), (1, 0), (0, 1), (0, 0)] + + """ + cdef: + slong i, nvars = self.ctx.nvars() + fmpz_vec vec = fmpz_vec(nvars, double_indirect=True) + + 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()) + + return res + + def values(self): + """ + Return the coefficients of each term as a fmpz + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f.values() + [4, 2, 3, 1] + + """ + cdef: + fmpz coeff + slong i + + res = [] + for i in range(len(self)): + coeff = fmpz.__new__(fmpz) + fmpz_mpoly_get_term_coeff_fmpz(coeff.val, self.val, i, self.ctx.val) + res.append(coeff) + + return res + def subs(self, dict_args) -> fmpz_mpoly: """ Partial evaluate this polynomial with select constants. All arguments must be fmpz. From 10c19be5c39274b6357373101fc9051c6b40d061 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Tue, 2 Jul 2024 20:24:00 +1000 Subject: [PATCH 83/95] Add alias for keys/value to monoms/coeffs, add terms function --- src/flint/flint_base/flint_base.pyx | 6 ++++++ src/flint/test/test_all.py | 2 ++ src/flint/types/fmpq_mpoly.pyx | 27 +++++++++++++++++++++++++-- src/flint/types/fmpz_mpoly.pyx | 27 +++++++++++++++++++++++++-- 4 files changed, 58 insertions(+), 4 deletions(-) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index 929c02a2..ceff2cb4 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -265,6 +265,12 @@ cdef class flint_mpoly(flint_elem): def __pos__(self): return self + def keys(self): + return self.monoms() + + def values(self): + return self.coeffs() + def items(self): """ Return the exponent vectors and coefficient of each term. diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index 214622f4..fa9fef98 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -2794,6 +2794,8 @@ def quick_poly(): assert p.keys() == [(2, 2), (1, 0), (0, 1), (0, 0)] assert p.values() == [4, 3, 2, 1] assert list(p.items()) == list(zip([(2, 2), (1, 0), (0, 1), (0, 0)], [4, 3, 2, 1])) + assert sum(ctx.from_dict({exp_vec: coeff}) for exp_vec, coeff in p.items()) == p + assert sum(p.terms()) == p assert p.subs({"x1": S(0), "x0": S(0)}) == ctx.from_dict({(0, 0): 1}) assert p.compose(p.subs({"x1": 0}), ctx.from_dict({(0, 1): 1})) == mpoly({ diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index deb67afc..3955ad0a 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -584,7 +584,7 @@ cdef class fmpq_mpoly(flint_mpoly): raise ValueError("Unreasonably large polynomial") # pragma: no cover return vres - def keys(self): + def monoms(self): """ Return the exponent vectors of each term as a tuple of fmpz. @@ -606,7 +606,7 @@ cdef class fmpq_mpoly(flint_mpoly): return res - def values(self): + def coeffs(self): """ Return the coefficients of each term as a fmpq. @@ -629,6 +629,29 @@ cdef class fmpq_mpoly(flint_mpoly): return res + def terms(self): + """ + Return the terms of this polynomial as a list of fmpq_mpolys. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f.terms() + [4*x0*x1, 2*x0, 3*x1, 1] + + """ + cdef: + fmpq_mpoly term + slong i + + res = [] + for i in range(len(self)): + term = create_fmpq_mpoly(self.ctx) + fmpq_mpoly_get_term(term.val, self.val, i, self.ctx.val) + res.append(term) + + return res + def subs(self, dict_args) -> fmpq_mpoly: """ Partial evaluate this polynomial. diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 16830200..9d01ba96 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -566,7 +566,7 @@ cdef class fmpz_mpoly(flint_mpoly): raise ValueError("Unreasonably large polynomial") # pragma: no cover return vres - def keys(self): + def monoms(self): """ Return the exponent vectors of each term as a tuple of fmpz. @@ -588,7 +588,7 @@ cdef class fmpz_mpoly(flint_mpoly): return res - def values(self): + def coeffs(self): """ Return the coefficients of each term as a fmpz @@ -611,6 +611,29 @@ cdef class fmpz_mpoly(flint_mpoly): return res + def terms(self): + """ + Return the terms of this polynomial as a list of fmpz_mpolys. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f.terms() + [4*x0*x1, 2*x0, 3*x1, 1] + + """ + cdef: + fmpz_mpoly term + slong i + + res = [] + for i in range(len(self)): + term = create_fmpz_mpoly(self.ctx) + fmpz_mpoly_get_term(term.val, self.val, i, self.ctx.val) + res.append(term) + + return res + def subs(self, dict_args) -> fmpz_mpoly: """ Partial evaluate this polynomial with select constants. All arguments must be fmpz. From d70c2f94fe5cdfe4dfe090b37cdee2ceab0d64f2 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Thu, 4 Jul 2024 18:22:40 +1000 Subject: [PATCH 84/95] Remove keys/values/exponent_tuple functions, rename items -> terms --- src/flint/flint_base/flint_base.pyx | 14 +++------ src/flint/test/test_all.py | 16 +++++------ src/flint/types/fmpq_mpoly.pyx | 44 ++++++++++++++--------------- src/flint/types/fmpz_mpoly.pyx | 44 ++++++++++++++--------------- 4 files changed, 55 insertions(+), 63 deletions(-) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index ceff2cb4..2e92e6d6 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -242,7 +242,7 @@ cdef class flint_mpoly(flint_elem): return self.coefficient(0) def to_dict(self): - return {self.exponent_tuple(i): self.coefficient(i) for i in range(len(self))} + return {self.monomial(i): self.coefficient(i) for i in range(len(self))} def __contains__(self, x): """ @@ -265,24 +265,18 @@ cdef class flint_mpoly(flint_elem): def __pos__(self): return self - def keys(self): - return self.monoms() - - def values(self): - return self.coeffs() - - def items(self): + def terms(self): """ Return the exponent vectors and coefficient of each term. >>> from flint import fmpq_mpoly_ctx, Ordering >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) - >>> list(f.items()) + >>> list(f.terms()) [((1, 1), 4), ((1, 0), 2), ((0, 1), 3), ((0, 0), 1)] """ - return zip(self.keys(), self.values()) + return zip(self.monoms(), self.coeffs()) cdef class flint_series(flint_elem): diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index fa9fef98..9b31c253 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -2777,10 +2777,10 @@ def quick_poly(): assert P(1, ctx=ctx).repr() == f"{ctx.__class__.__name__}(2, '', ('x0', 'x1')).from_dict({{(0, 0): 1}})" assert str(quick_poly()) == repr(quick_poly()) == '4*x0^2*x1^2 + 3*x0 + 2*x1 + 1' - assert p.exponent_tuple(0) == (2, 2) - assert p.exponent_tuple(3) == (0, 0) - assert raises(lambda: p.exponent_tuple(-1), IndexError) - assert raises(lambda: p.exponent_tuple(4), IndexError) + assert p.monomial(0) == (2, 2) + assert p.monomial(3) == (0, 0) + assert raises(lambda: p.monomial(-1), IndexError) + assert raises(lambda: p.monomial(4), IndexError) assert p.total_degree() == 4 assert P(ctx=ctx).total_degree() == -1 @@ -2791,11 +2791,9 @@ def quick_poly(): assert p(1, 1) == S(10) == 10 p = quick_poly() - assert p.keys() == [(2, 2), (1, 0), (0, 1), (0, 0)] - assert p.values() == [4, 3, 2, 1] - assert list(p.items()) == list(zip([(2, 2), (1, 0), (0, 1), (0, 0)], [4, 3, 2, 1])) - assert sum(ctx.from_dict({exp_vec: coeff}) for exp_vec, coeff in p.items()) == p - assert sum(p.terms()) == p + assert p.monoms() == [(2, 2), (1, 0), (0, 1), (0, 0)] + assert p.coeffs() == [4, 3, 2, 1] + assert list(p.terms()) == list(zip([(2, 2), (1, 0), (0, 1), (0, 0)], [4, 3, 2, 1])) assert p.subs({"x1": S(0), "x0": S(0)}) == ctx.from_dict({(0, 0): 1}) assert p.compose(p.subs({"x1": 0}), ctx.from_dict({(0, 1): 1})) == mpoly({ diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 3955ad0a..79efd9d9 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -591,7 +591,7 @@ cdef class fmpq_mpoly(flint_mpoly): >>> from flint import Ordering >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) - >>> f.keys() + >>> f.monoms() [(1, 1), (1, 0), (0, 1), (0, 0)] """ @@ -613,7 +613,7 @@ cdef class fmpq_mpoly(flint_mpoly): >>> from flint import Ordering >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) - >>> f.values() + >>> f.coeffs() [4, 2, 3, 1] """ @@ -629,28 +629,28 @@ cdef class fmpq_mpoly(flint_mpoly): return res - def terms(self): - """ - Return the terms of this polynomial as a list of fmpq_mpolys. + # def terms(self): + # """ + # Return the terms of this polynomial as a list of fmpq_mpolys. - >>> from flint import Ordering - >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') - >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) - >>> f.terms() - [4*x0*x1, 2*x0, 3*x1, 1] + # >>> from flint import Ordering + # >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + # >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) + # >>> f.terms() + # [4*x0*x1, 2*x0, 3*x1, 1] - """ - cdef: - fmpq_mpoly term - slong i + # """ + # cdef: + # fmpq_mpoly term + # slong i - res = [] - for i in range(len(self)): - term = create_fmpq_mpoly(self.ctx) - fmpq_mpoly_get_term(term.val, self.val, i, self.ctx.val) - res.append(term) + # res = [] + # for i in range(len(self)): + # term = create_fmpq_mpoly(self.ctx) + # fmpq_mpoly_get_term(term.val, self.val, i, self.ctx.val) + # res.append(term) - return res + # return res def subs(self, dict_args) -> fmpq_mpoly: """ @@ -744,14 +744,14 @@ cdef class fmpq_mpoly(flint_mpoly): fmpq_mpoly_get_term_coeff_fmpq(v.val, self.val, i, self.ctx.val) return v - def exponent_tuple(self, slong i): + def monomial(self, slong i): """ Return the exponent vector at index `i` as a tuple. >>> from flint import Ordering >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) - >>> p.exponent_tuple(1) + >>> p.monomial(1) (0, 1) """ cdef: diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 9d01ba96..4ff0c938 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -573,7 +573,7 @@ cdef class fmpz_mpoly(flint_mpoly): >>> from flint import Ordering >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) - >>> f.keys() + >>> f.monoms() [(1, 1), (1, 0), (0, 1), (0, 0)] """ @@ -595,7 +595,7 @@ cdef class fmpz_mpoly(flint_mpoly): >>> from flint import Ordering >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) - >>> f.values() + >>> f.coeffs() [4, 2, 3, 1] """ @@ -611,28 +611,28 @@ cdef class fmpz_mpoly(flint_mpoly): return res - def terms(self): - """ - Return the terms of this polynomial as a list of fmpz_mpolys. + # def terms(self): + # """ + # Return the terms of this polynomial as a list of fmpz_mpolys. - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') - >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) - >>> f.terms() - [4*x0*x1, 2*x0, 3*x1, 1] + # >>> from flint import Ordering + # >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + # >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) + # >>> f.terms() + # [4*x0*x1, 2*x0, 3*x1, 1] - """ - cdef: - fmpz_mpoly term - slong i + # """ + # cdef: + # fmpz_mpoly term + # slong i - res = [] - for i in range(len(self)): - term = create_fmpz_mpoly(self.ctx) - fmpz_mpoly_get_term(term.val, self.val, i, self.ctx.val) - res.append(term) + # res = [] + # for i in range(len(self)): + # term = create_fmpz_mpoly(self.ctx) + # fmpz_mpoly_get_term(term.val, self.val, i, self.ctx.val) + # res.append(term) - return res + # return res def subs(self, dict_args) -> fmpz_mpoly: """ @@ -747,14 +747,14 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly_get_term_coeff_fmpz(v.val, self.val, i, self.ctx.val) return v - def exponent_tuple(self, slong i): + def monomial(self, slong i): """ Return the exponent vector at index `i` as a tuple. >>> from flint import Ordering >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) - >>> p.exponent_tuple(1) + >>> p.monomial(1) (0, 1) """ cdef: From 8f76c6a212a5f1e293983890ec0db0b29b1edb30 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Thu, 4 Jul 2024 19:17:59 +1000 Subject: [PATCH 85/95] Remove in-place add, sub, and mul --- src/flint/test/test_all.py | 6 ------ src/flint/types/fmpq_mpoly.pyx | 39 ---------------------------------- src/flint/types/fmpz_mpoly.pyx | 39 ---------------------------------- 3 files changed, 84 deletions(-) diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index 9b31c253..9dbf021c 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -2840,8 +2840,6 @@ def quick_poly(): assert raises(lambda: mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}) + None, TypeError) assert raises(lambda: None + mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}), TypeError) assert raises(lambda: quick_poly() + P(ctx=ctx1), IncompatibleContextError) - assert raises(lambda: quick_poly().__iadd__(P(ctx=ctx1)), IncompatibleContextError) - assert quick_poly().__iadd__(None) is NotImplemented assert quick_poly() - mpoly({(0, 0): 5, (0, 1): 6, (1, 0): 7, (2, 2): 8}) \ == mpoly({(0, 0): -4, (0, 1): -4, (1, 0): -4, (2, 2): -4}) @@ -2855,8 +2853,6 @@ def quick_poly(): assert raises(lambda: quick_poly() - None, TypeError) assert raises(lambda: None - quick_poly(), TypeError) assert raises(lambda: quick_poly() - P(ctx=ctx1), IncompatibleContextError) - assert raises(lambda: quick_poly().__isub__(P(ctx=ctx1)), IncompatibleContextError) - assert quick_poly().__isub__(None) is NotImplemented assert quick_poly() * mpoly({(1, 0): 5, (0, 1): 6}) \ == mpoly({ @@ -2878,8 +2874,6 @@ def quick_poly(): assert raises(lambda: quick_poly() * None, TypeError) assert raises(lambda: None * quick_poly(), TypeError) assert raises(lambda: quick_poly() * P(ctx=ctx1), IncompatibleContextError) - assert raises(lambda: quick_poly().__imul__(P(ctx=ctx1)), IncompatibleContextError) - assert quick_poly().__imul__(None) is NotImplemented assert quick_poly() // mpoly({(1, 1): 1}) == mpoly({(1, 1): 4}) assert quick_poly() % mpoly({(1, 1): 1}) \ diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 79efd9d9..185ade76 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -348,19 +348,6 @@ cdef class fmpq_mpoly(flint_mpoly): return res return NotImplemented - def __iadd__(self, other): - if typecheck(other, fmpq_mpoly): - if (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - fmpq_mpoly_add((self).val, (self).val, (other).val, self.ctx.val) - return self - else: - other = any_as_fmpq(other) - if other is not NotImplemented: - fmpq_mpoly_add_fmpq((self).val, (self).val, (other).val, self.ctx.val) - return self - return NotImplemented - def __sub__(self, other): cdef fmpq_mpoly res if typecheck(other, fmpq_mpoly): @@ -386,19 +373,6 @@ cdef class fmpq_mpoly(flint_mpoly): return -res return NotImplemented - def __isub__(self, other): - if typecheck(other, fmpq_mpoly): - if (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - fmpq_mpoly_sub((self).val, (self).val, (other).val, self.ctx.val) - return self - else: - other = any_as_fmpq(other) - if other is not NotImplemented: - fmpq_mpoly_sub_fmpq((self).val, (self).val, (other).val, self.ctx.val) - return self - return NotImplemented - def __mul__(self, other): cdef fmpq_mpoly res if typecheck(other, fmpq_mpoly): @@ -424,19 +398,6 @@ cdef class fmpq_mpoly(flint_mpoly): return res return NotImplemented - def __imul__(self, other): - if typecheck(other, fmpq_mpoly): - if (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - fmpq_mpoly_mul((self).val, (self).val, (other).val, self.ctx.val) - return self - else: - other = any_as_fmpq(other) - if other is not NotImplemented: - fmpq_mpoly_scalar_mul_fmpq(self.val, (self).val, (other).val, self.ctx.val) - return self - return NotImplemented - def __pow__(self, other, modulus): cdef fmpq_mpoly res if modulus is not None: diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 4ff0c938..f7446542 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -328,19 +328,6 @@ cdef class fmpz_mpoly(flint_mpoly): return res return NotImplemented - def __iadd__(self, other): - if typecheck(other, fmpz_mpoly): - if (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - fmpz_mpoly_add((self).val, (self).val, (other).val, self.ctx.val) - return self - else: - zval = any_as_fmpz(other) - if zval is not NotImplemented: - fmpz_mpoly_add_fmpz((self).val, (self).val, (zval).val, self.ctx.val) - return self - return NotImplemented - def __sub__(self, other): cdef fmpz_mpoly res if typecheck(other, fmpz_mpoly): @@ -366,19 +353,6 @@ cdef class fmpz_mpoly(flint_mpoly): return -res return NotImplemented - def __isub__(self, other): - if typecheck(other, fmpz_mpoly): - if (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - fmpz_mpoly_sub((self).val, (self).val, (other).val, self.ctx.val) - return self - else: - other = any_as_fmpz(other) - if other is not NotImplemented: - fmpz_mpoly_sub_fmpz((self).val, (self).val, (other).val, self.ctx.val) - return self - return NotImplemented - def __mul__(self, other): cdef fmpz_mpoly res if typecheck(other, fmpz_mpoly): @@ -404,19 +378,6 @@ cdef class fmpz_mpoly(flint_mpoly): return res return NotImplemented - def __imul__(self, other): - if typecheck(other, fmpz_mpoly): - if (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - fmpz_mpoly_mul((self).val, (self).val, (other).val, self.ctx.val) - return self - else: - other = any_as_fmpz(other) - if other is not NotImplemented: - fmpz_mpoly_scalar_mul_fmpz(self.val, (self).val, (other).val, self.ctx.val) - return self - return NotImplemented - def __pow__(self, other, modulus): cdef fmpz_mpoly res if modulus is not None: From dc7c787f086506648079c104d705898d546bbe2d Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Thu, 4 Jul 2024 19:43:43 +1000 Subject: [PATCH 86/95] Add missing subs and compose docs, fix old error message and comment --- src/flint/types/fmpq_mpoly.pyx | 50 ++++++++++++++++++++++++---------- src/flint/types/fmpz_mpoly.pyx | 19 ++++++------- 2 files changed, 44 insertions(+), 25 deletions(-) diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 185ade76..f9f126cb 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -615,39 +615,59 @@ cdef class fmpq_mpoly(flint_mpoly): def subs(self, dict_args) -> fmpq_mpoly: """ - Partial evaluate this polynomial. + Partial evaluate this polynomial with select constants. Keys must be generator names, + all values must be fmpq. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f.subs({"x1": 0}) + 2*x0 + 1 + """ cdef: fmpq_mpoly res slong i, nargs - partial_args = tuple((i, dict_args[x]) for i, x in enumerate(self.ctx.names()) if x in dict_args) - nargs = len(partial_args) + args = tuple((i, dict_args[x]) for i, x in enumerate(self.ctx.names()) if x in dict_args) + nargs = len(args) - # If we've been provided with an invalid keyword arg then the length of our filter + # If we've been provided with an invalid generator name then the length of our filter # args will be less than what we've been provided with. - # If the length is equal to the number of variables then all arguments have been provided - # otherwise we need to do partial application if nargs < len(dict_args): - raise ValueError("Unknown keyword argument provided") + raise ValueError("unknown generator name provided") - args_fmpq = tuple(any_as_fmpq(v) for _, v in partial_args) + args_fmpq = tuple(any_as_fmpq(v) for _, v in args) for arg in args_fmpq: if arg is NotImplemented: - raise TypeError(f"Cannot coerce argument ('{arg}') to fmpq") + raise TypeError(f"cannot coerce argument ('{arg}') to fmpq") - # Partial application with args in Z. We evaluate the polynomial one variable at a time + # Partial application with args in Q. We evaluate the polynomial one variable at a time res = create_fmpq_mpoly(self.ctx) fmpq_mpoly_set(res.val, self.val, self.ctx.val) - for (i, _), arg in zip(partial_args, args_fmpq): + for (i, _), arg in zip(args, args_fmpq): if fmpq_mpoly_evaluate_one_fmpq(res.val, res.val, i, (arg).val, self.ctx.val) == 0: - raise ValueError("Unreasonably large polynomial") # pragma: no cover + raise ValueError("unreasonably large polynomial") # pragma: no cover return res def compose(self, *args) -> fmpq_mpoly: """ - Compose this polynomial with other fmpq_mpolys. Takes positional arguments. + Compose this polynomial with other fmpq_mpolys. All arguments must share the same context, it may different + from this polynomials context. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(1, Ordering.lex, 'x') + >>> ctx1 = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'y') + >>> f = ctx.from_dict({(2,): 1}) + >>> g = ctx1.from_dict({(1, 0): 1, (0, 1): 1}) + >>> f + x^2 + >>> g + y0 + y1 + >>> f.compose(g) + y0^2 + 2*y0*y1 + y1^2 + """ cdef: fmpq_mpoly res @@ -666,8 +686,8 @@ cdef class fmpq_mpoly(flint_mpoly): if not all(( args[i]).ctx is res_ctx for i in range(1, len(args))): raise IncompatibleContextError("All arguments must share the same context") - C = fmpq_mpoly_vec(args, self.ctx, double_indirect=True) - res = create_fmpq_mpoly(self.ctx) + C = fmpq_mpoly_vec(args, res_ctx, double_indirect=True) + res = create_fmpq_mpoly(res_ctx) if fmpq_mpoly_compose_fmpq_mpoly(res.val, self.val, C.double_indirect, self.ctx.val, res_ctx.val) == 0: raise ValueError("Unreasonably large polynomial") # pragma: no cover return res diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index f7446542..7bed304e 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -597,7 +597,8 @@ cdef class fmpz_mpoly(flint_mpoly): def subs(self, dict_args) -> fmpz_mpoly: """ - Partial evaluate this polynomial with select constants. All arguments must be fmpz. + Partial evaluate this polynomial with select constants. Keys must be generator names, + all values must be fmpz. >>> from flint import Ordering >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') @@ -610,28 +611,26 @@ cdef class fmpz_mpoly(flint_mpoly): fmpz_mpoly res slong i, nargs - partial_args = tuple((i, dict_args[x]) for i, x in enumerate(self.ctx.names()) if x in dict_args) - nargs = len(partial_args) + args = tuple((i, dict_args[x]) for i, x in enumerate(self.ctx.names()) if x in dict_args) + nargs = len(args) # If we've been provided with an invalid keyword arg then the length of our filter # args will be less than what we've been provided with. - # If the length is equal to the number of variables then all arguments have been provided - # otherwise we need to do partial application if nargs < len(dict_args): - raise ValueError("Unknown keyword argument provided") + raise ValueError("unknown generator name provided") - args_fmpz = tuple(any_as_fmpz(v) for _, v in partial_args) + args_fmpz = tuple(any_as_fmpz(v) for _, v in args) for arg in args_fmpz: if arg is NotImplemented: - raise TypeError(f"Cannot coerce argument ('{arg}') to fmpz") + raise TypeError(f"cannot coerce argument ('{arg}') to fmpz") # Partial application with args in Z. We evaluate the polynomial one variable at a time res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_set(res.val, self.val, self.ctx.val) - for (i, _), arg in zip(partial_args, args_fmpz): + for (i, _), arg in zip(args, args_fmpz): if fmpz_mpoly_evaluate_one_fmpz(res.val, res.val, i, (arg).val, self.ctx.val) == 0: - raise ValueError("Unreasonably large polynomial") # pragma: no cover + raise ValueError("unreasonably large polynomial") # pragma: no cover return res def compose(self, *args) -> fmpz_mpoly: From e042933ebb9c85503e9a0a423812d9e36a2378ce Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Thu, 4 Jul 2024 20:08:44 +1000 Subject: [PATCH 87/95] Change .degrees to return a tuple over a dict --- src/flint/test/test_all.py | 10 +++++----- src/flint/types/fmpq_mpoly.pyx | 4 ++-- src/flint/types/fmpz_mpoly.pyx | 4 ++-- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index 9dbf021c..c7d2ff97 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -2696,11 +2696,11 @@ def quick_poly(): assert len(mpoly({(0, 0): 1, (0, 1): 1, (1, 0): 1})) == 3 # degree is -1 when empty poly - assert P(ctx=ctx).degrees() == mpoly({(0, 0): 0}).degrees() == {"x0": -1, "x1": -1} - assert P(1, ctx=ctx).degrees() == mpoly({(0, 0): 1}).degrees() == {"x0": 0, "x1": 0} - assert mpoly({(0, 0): 1, (0, 1): 1}).degrees() == {"x0": 0, "x1": 1} - assert mpoly({(0, 0): 1, (0, 1): 1, (1, 0): 1}).degrees() == {"x0": 1, "x1": 1} - assert mpoly({(0, 0): 1, (0, 1): 1, (1, 0): 1, (2, 2): 2}).degrees() == {"x0": 2, "x1": 2} + assert P(ctx=ctx).degrees() == mpoly({(0, 0): 0}).degrees() == (-1, -1) + assert P(1, ctx=ctx).degrees() == mpoly({(0, 0): 1}).degrees() == (0, 0) + assert mpoly({(0, 0): 1, (0, 1): 1}).degrees() == (0, 1) + assert mpoly({(0, 0): 1, (0, 1): 1, (1, 0): 1}).degrees() == (1, 1) + assert mpoly({(0, 0): 1, (0, 1): 1, (1, 0): 1, (2, 2): 2}).degrees() == (2, 2) assert (P(1, ctx=ctx) == P(1, ctx=ctx)) is True assert (P(1, ctx=ctx) != P(1, ctx=ctx)) is False diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index f9f126cb..3184124d 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -752,14 +752,14 @@ cdef class fmpq_mpoly(flint_mpoly): >>> ctx = fmpq_mpoly_ctx.get_context(4, Ordering.lex, 'x') >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) >>> p.degrees() - {'x0': 1, 'x1': 2, 'x2': 3, 'x3': 0} + (1, 2, 3, 0) """ cdef: slong nvars = self.ctx.nvars() res = fmpz_vec(nvars, double_indirect=True) fmpq_mpoly_degrees_fmpz(res.double_indirect, self.val, self.ctx.val) - return dict(zip(self.ctx.names(), res.to_tuple())) + return res.to_tuple() def total_degree(self): """ diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 7bed304e..a46f0f4e 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -734,14 +734,14 @@ cdef class fmpz_mpoly(flint_mpoly): >>> ctx = fmpz_mpoly_ctx.get_context(4, Ordering.lex, 'x') >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) >>> p.degrees() - {'x0': 1, 'x1': 2, 'x2': 3, 'x3': 0} + (1, 2, 3, 0) """ cdef: slong nvars = self.ctx.nvars() res = fmpz_vec(nvars, double_indirect=True) fmpz_mpoly_degrees_fmpz(res.double_indirect, self.val, self.ctx.val) - return dict(zip(self.ctx.names(), res.to_tuple())) + return res.to_tuple() def total_degree(self): """ From 128ae01140e0c3aae3a1aa83428b440d0229f136 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Thu, 4 Jul 2024 20:25:26 +1000 Subject: [PATCH 88/95] Remove unused imports, use lower case to start exception message --- src/flint/types/fmpq_mpoly.pyx | 48 ++++++++++++++++------------------ src/flint/types/fmpz_mpoly.pyx | 30 ++++++++++----------- 2 files changed, 37 insertions(+), 41 deletions(-) diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 3184124d..8436cd6a 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -1,7 +1,6 @@ from flint.flint_base.flint_base cimport ( flint_mpoly, flint_mpoly_context, - Ordering, ordering_py_to_c, ordering_c_to_py, ) @@ -17,7 +16,6 @@ from flint.types.fmpz cimport fmpz, any_as_fmpz from flint.types.fmpz_mpoly cimport fmpz_mpoly from flint.flintlib.fmpq cimport fmpq_set, fmpq_one -from flint.flintlib.mpoly cimport ordering_t from flint.flintlib.fmpq_mpoly cimport ( fmpq_mpoly_add, fmpq_mpoly_add_fmpq, @@ -54,7 +52,6 @@ from flint.flintlib.fmpq_mpoly cimport ( fmpq_mpoly_set_coeff_fmpq_fmpz, fmpq_mpoly_set_fmpq, fmpq_mpoly_set_str_pretty, - fmpq_mpoly_set_term_coeff_fmpq, fmpq_mpoly_sort_terms, fmpq_mpoly_sqrt, fmpq_mpoly_sub, @@ -163,16 +160,16 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): fmpq_mpoly res if not isinstance(d, dict): - raise ValueError("Expected a dictionary") + raise ValueError("expected a dictionary") res = create_fmpq_mpoly(self) for k, v in d.items(): o = any_as_fmpq(v) if o is NotImplemented: - raise TypeError(f"Cannot coerce coefficient '{v}' to fmpq") + raise TypeError(f"cannot coerce coefficient '{v}' to fmpq") elif len(k) != nvars: - raise ValueError(f"Expected {nvars} exponents, got {len(k)}") + raise ValueError(f"expected {nvars} exponents, got {len(k)}") exp_vec = fmpz_vec(k) @@ -212,7 +209,8 @@ cdef class fmpq_mpoly(flint_mpoly): raise TypeError(f"{ctx} is not a fmpq_mpoly_ctx or fmpz_mpoly_ctx") elif ctx.nvars() != val.context().nvars(): raise ValueError( - f"Provided context ('{ctx}') and provided fmpz_mpoly ('{val}') don't share the same number of variables" + f"Provided context ('{ctx}') and provided fmpz_mpoly ('{val}') don't share the same number of " + "variables" ) init_fmpq_mpoly(self, ctx) fmpz_mpoly_set(self.val.zpoly, (val).val, ( val).ctx.val) @@ -220,25 +218,25 @@ cdef class fmpq_mpoly(flint_mpoly): fmpq_mpoly_reduce(self.val, self.ctx.val) elif isinstance(val, dict): if ctx is None: - raise ValueError("A context is required to create a fmpq_mpoly from a dict") + raise ValueError("a context is required to create a fmpq_mpoly from a dict") x = ctx.from_dict(val) # XXX: this copy is silly, have a ctx function that assigns an fmpz_mpoly_t init_fmpq_mpoly(self, ctx) fmpq_mpoly_set(self.val, (x).val, self.ctx.val) elif isinstance(val, str): if ctx is None: - raise ValueError("Cannot parse a polynomial without context") + raise ValueError("cannot parse a polynomial without context") val = val.encode("ascii") init_fmpq_mpoly(self, ctx) if fmpq_mpoly_set_str_pretty(self.val, val, self.ctx.c_names, self.ctx.val) == -1: - raise ValueError("Unable to parse fmpq_mpoly from string") + raise ValueError("unable to parse fmpq_mpoly from string") fmpq_mpoly_sort_terms(self.val, self.ctx.val) else: v = any_as_fmpq(val) if v is NotImplemented: raise TypeError("cannot create fmpz_mpoly from type %s" % type(val)) if ctx is None: - raise ValueError("Need context to convert fmpz to fmpq_mpoly") + raise ValueError("need context to convert fmpz to fmpq_mpoly") init_fmpq_mpoly(self, ctx) fmpq_mpoly_set_fmpq(self.val, (v).val, self.ctx.val) @@ -280,9 +278,9 @@ cdef class fmpq_mpoly(flint_mpoly): slong nvars = self.ctx.nvars() if not isinstance(x, tuple): - raise TypeError("Exponent vector index is not a tuple") + raise TypeError("exponent vector index is not a tuple") elif len(x) != nvars: - raise ValueError("Exponent vector provided does not match number of variables") + raise ValueError("exponent vector provided does not match number of variables") res = fmpq() exp_vec = fmpz_vec(x, double_indirect=True) @@ -308,11 +306,11 @@ cdef class fmpq_mpoly(flint_mpoly): coeff = any_as_fmpq(y) if coeff is NotImplemented: - raise TypeError("Provided coefficient not coercible to fmpq") + raise TypeError("provided coefficient not coercible to fmpq") elif not isinstance(x, tuple): - raise TypeError("Exponent vector index is not a tuple") + raise TypeError("exponent vector index is not a tuple") elif len(x) != nvars: - raise ValueError("Exponent vector provided does not match number of variables") + raise ValueError("exponent vector provided does not match number of variables") exp_vec = fmpz_vec(x, double_indirect=True) fmpq_mpoly_set_coeff_fmpq_fmpz(self.val, (coeff).val, exp_vec.double_indirect, self.ctx.val) @@ -530,19 +528,19 @@ cdef class fmpq_mpoly(flint_mpoly): slong nvars = self.ctx.nvars(), nargs = len(args) if nargs < nvars: - raise ValueError("Not enough arguments provided") + raise ValueError("not enough arguments provided") elif nargs > nvars: - raise ValueError("More arguments provided than variables") + raise ValueError("more arguments provided than variables") args_fmpq = tuple(any_as_fmpq(v) for v in args) for arg in args_fmpq: if arg is NotImplemented: - raise TypeError(f"Cannot coerce argument ('{arg}') to fmpq") + raise TypeError(f"cannot coerce argument ('{arg}') to fmpq") V = fmpq_vec(args_fmpq, double_indirect=True) vres = fmpq.__new__(fmpq) if fmpq_mpoly_evaluate_all_fmpq(vres.val, self.val, V.double_indirect, self.ctx.val) == 0: - raise ValueError("Unreasonably large polynomial") # pragma: no cover + raise ValueError("unreasonably large polynomial") # pragma: no cover return vres def monoms(self): @@ -676,20 +674,20 @@ cdef class fmpq_mpoly(flint_mpoly): slong i, nvars = self.ctx.nvars(), nargs = len(args) if nargs < nvars: - raise ValueError("Not enough arguments provided") + raise ValueError("not enough arguments provided") elif nargs > nvars: - raise ValueError("More arguments provided than variables") + raise ValueError("more arguments provided than variables") elif not all(typecheck(arg, fmpq_mpoly) for arg in args): - raise TypeError("All arguments must be fmpq_mpolys") + raise TypeError("all arguments must be fmpq_mpolys") res_ctx = ( args[0]).ctx if not all(( args[i]).ctx is res_ctx for i in range(1, len(args))): - raise IncompatibleContextError("All arguments must share the same context") + raise IncompatibleContextError("all arguments must share the same context") C = fmpq_mpoly_vec(args, res_ctx, double_indirect=True) res = create_fmpq_mpoly(res_ctx) if fmpq_mpoly_compose_fmpq_mpoly(res.val, self.val, C.double_indirect, self.ctx.val, res_ctx.val) == 0: - raise ValueError("Unreasonably large polynomial") # pragma: no cover + raise ValueError("unreasonably large polynomial") # pragma: no cover return res def context(self): diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index a46f0f4e..bf211595 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -1,7 +1,6 @@ from flint.flint_base.flint_base cimport ( flint_mpoly, flint_mpoly_context, - Ordering, ordering_py_to_c, ordering_c_to_py, ) @@ -48,7 +47,6 @@ from flint.flintlib.fmpz_mpoly cimport ( fmpz_mpoly_set_coeff_fmpz_fmpz, fmpz_mpoly_set_fmpz, fmpz_mpoly_set_str_pretty, - fmpz_mpoly_set_term_coeff_fmpz, fmpz_mpoly_sort_terms, fmpz_mpoly_sqrt_heap, fmpz_mpoly_sub, @@ -200,25 +198,25 @@ cdef class fmpz_mpoly(flint_mpoly): raise IncompatibleContextError(f"{ctx} is not {(val).ctx}") elif isinstance(val, dict): if ctx is None: - raise ValueError("A context is required to create a fmpz_mpoly from a dict") + raise ValueError("a context is required to create a fmpz_mpoly from a dict") x = ctx.from_dict(val) # XXX: this copy is silly, have a ctx function that assigns an fmpz_mpoly_t init_fmpz_mpoly(self, ctx) fmpz_mpoly_set(self.val, (x).val, self.ctx.val) elif isinstance(val, str): if ctx is None: - raise ValueError("Cannot parse a polynomial without context") + raise ValueError("cannot parse a polynomial without context") val = bytes(val, 'utf-8') init_fmpz_mpoly(self, ctx) if fmpz_mpoly_set_str_pretty(self.val, val, self.ctx.c_names, self.ctx.val) == -1: - raise ValueError("Unable to parse fmpz_mpoly from string") + raise ValueError("unable to parse fmpz_mpoly from string") fmpz_mpoly_sort_terms(self.val, self.ctx.val) else: v = any_as_fmpz(val) if v is NotImplemented: - raise TypeError("Cannot create fmpz_mpoly from type %s" % type(val)) + raise TypeError("cannot create fmpz_mpoly from type %s" % type(val)) if ctx is None: - raise ValueError("Need context to convert fmpz to fmpz_mpoly") + raise ValueError("need context to convert fmpz to fmpz_mpoly") init_fmpz_mpoly(self, ctx) fmpz_mpoly_set_fmpz(self.val, (v).val, self.ctx.val) @@ -512,19 +510,19 @@ cdef class fmpz_mpoly(flint_mpoly): slong nvars = self.ctx.nvars(), nargs = len(args) if nargs < nvars: - raise ValueError("Not enough arguments provided") + raise ValueError("not enough arguments provided") elif nargs > nvars: - raise ValueError("More arguments provided than variables") + raise ValueError("more arguments provided than variables") args_fmpz = tuple(any_as_fmpz(v) for v in args) for arg in args_fmpz: if arg is NotImplemented: - raise TypeError(f"Cannot coerce argument ('{arg}') to fmpz") + raise TypeError(f"cannot coerce argument ('{arg}') to fmpz") V = fmpz_vec(args_fmpz, double_indirect=True) vres = fmpz.__new__(fmpz) if fmpz_mpoly_evaluate_all_fmpz(vres.val, self.val, V.double_indirect, self.ctx.val) == 0: - raise ValueError("Unreasonably large polynomial") # pragma: no cover + raise ValueError("unreasonably large polynomial") # pragma: no cover return vres def monoms(self): @@ -658,20 +656,20 @@ cdef class fmpz_mpoly(flint_mpoly): slong i, nvars = self.ctx.nvars(), nargs = len(args) if nargs < nvars: - raise ValueError("Not enough arguments provided") + raise ValueError("not enough arguments provided") elif nargs > nvars: - raise ValueError("More arguments provided than variables") + raise ValueError("more arguments provided than variables") elif not all(typecheck(arg, fmpz_mpoly) for arg in args): - raise TypeError("All arguments must be fmpz_mpolys") + raise TypeError("all arguments must be fmpz_mpolys") res_ctx = ( args[0]).ctx if not all(( args[i]).ctx is res_ctx for i in range(1, len(args))): - raise IncompatibleContextError("All arguments must share the same context") + raise IncompatibleContextError("all arguments must share the same context") C = fmpz_mpoly_vec(args, res_ctx, double_indirect=True) res = create_fmpz_mpoly(res_ctx) if fmpz_mpoly_compose_fmpz_mpoly(res.val, self.val, C.double_indirect, self.ctx.val, res_ctx.val) == 0: - raise ValueError("Unreasonably large polynomial") # pragma: no cover + raise ValueError("unreasonably large polynomial") # pragma: no cover return res def context(self): From 1317bb097b8f7cd361c38049489479d021ea4a54 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Mon, 8 Jul 2024 17:14:24 +1000 Subject: [PATCH 89/95] Fix composition with no-generator polynomial --- src/flint/test/test_all.py | 5 +++++ src/flint/types/fmpq_mpoly.pyx | 18 ++++++++++++++---- src/flint/types/fmpz_mpoly.pyx | 18 ++++++++++++++---- 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index c7d2ff97..68a14d1f 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -2815,6 +2815,11 @@ def quick_poly(): assert raises(lambda: p.subs({"a": 1}), ValueError) assert raises(lambda: p.subs({"x0": 0, "x1": 1, "x2": 2}), ValueError) + no_gens_ctx = C.get_context(0) + no_gens_p = P("2", no_gens_ctx) + assert no_gens_p.compose(ctx=ctx1).context() is ctx1 + assert raises(lambda: no_gens_p.compose(), ValueError) + assert raises(lambda: p.compose(p, P(ctx=ctx1)), IncompatibleContextError) assert bool(P(ctx=ctx)) is False diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 8436cd6a..7fa7a776 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -649,7 +649,7 @@ cdef class fmpq_mpoly(flint_mpoly): raise ValueError("unreasonably large polynomial") # pragma: no cover return res - def compose(self, *args) -> fmpq_mpoly: + def compose(self, *args, ctx=None) -> fmpq_mpoly: """ Compose this polynomial with other fmpq_mpolys. All arguments must share the same context, it may different from this polynomials context. @@ -677,12 +677,22 @@ cdef class fmpq_mpoly(flint_mpoly): raise ValueError("not enough arguments provided") elif nargs > nvars: raise ValueError("more arguments provided than variables") + elif self.ctx.nvars() == 0 and ctx is None: + raise ValueError("a context must be provided when composing a polynomial with no generators") elif not all(typecheck(arg, fmpq_mpoly) for arg in args): raise TypeError("all arguments must be fmpq_mpolys") - res_ctx = ( args[0]).ctx - if not all(( args[i]).ctx is res_ctx for i in range(1, len(args))): - raise IncompatibleContextError("all arguments must share the same context") + if ctx is None: + res_ctx = ( args[0]).ctx + elif typecheck(ctx, fmpq_mpoly_ctx): + res_ctx = ctx + else: + raise TypeError(f"provided context ({ctx}) is not a fmpq_mpoly_ctx") + + if not all(( arg).ctx is res_ctx for arg in args): + raise IncompatibleContextError( + "all arguments must share the " + ("same" if ctx is not None else "provided") + " context" + ) C = fmpq_mpoly_vec(args, res_ctx, double_indirect=True) res = create_fmpq_mpoly(res_ctx) diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index bf211595..968069a0 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -631,7 +631,7 @@ cdef class fmpz_mpoly(flint_mpoly): raise ValueError("unreasonably large polynomial") # pragma: no cover return res - def compose(self, *args) -> fmpz_mpoly: + def compose(self, *args, ctx=None) -> fmpz_mpoly: """ Compose this polynomial with other fmpz_mpolys. All arguments must share the same context, it may different from this polynomials context. @@ -659,12 +659,22 @@ cdef class fmpz_mpoly(flint_mpoly): raise ValueError("not enough arguments provided") elif nargs > nvars: raise ValueError("more arguments provided than variables") + elif self.ctx.nvars() == 0 and ctx is None: + raise ValueError("a context must be provided when composing a polynomial with no generators") elif not all(typecheck(arg, fmpz_mpoly) for arg in args): raise TypeError("all arguments must be fmpz_mpolys") - res_ctx = ( args[0]).ctx - if not all(( args[i]).ctx is res_ctx for i in range(1, len(args))): - raise IncompatibleContextError("all arguments must share the same context") + if ctx is None: + res_ctx = ( args[0]).ctx + elif typecheck(ctx, fmpz_mpoly_ctx): + res_ctx = ctx + else: + raise TypeError(f"provided context ({ctx}) is not a fmpz_mpoly_ctx") + + if not all(( arg).ctx is res_ctx for arg in args): + raise IncompatibleContextError( + "all arguments must share the " + ("same" if ctx is not None else "provided") + " context" + ) C = fmpz_mpoly_vec(args, res_ctx, double_indirect=True) res = create_fmpz_mpoly(res_ctx) From 424dc4f14f972b0410d246aab46b5ba876d59949 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Mon, 8 Jul 2024 20:29:01 +1000 Subject: [PATCH 90/95] fixup! Add alias for keys/value to monoms/coeffs, add terms function --- src/flint/flint_base/flint_base.pyx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index 2e92e6d6..98699e94 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -260,7 +260,7 @@ cdef class flint_mpoly(flint_elem): return bool(self[x]) def __iter__(self): - return iter(self.keys()) + return iter(self.monoms()) def __pos__(self): return self From 1c88ec3fd5419a8d4b743afd4038e26000db3c7e Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Mon, 8 Jul 2024 20:29:13 +1000 Subject: [PATCH 91/95] Revert "Remove in-place add, sub, and mul" This reverts commit 8f76c6a212a5f1e293983890ec0db0b29b1edb30. --- src/flint/test/test_all.py | 6 ++++++ src/flint/types/fmpq_mpoly.pyx | 39 ++++++++++++++++++++++++++++++++++ src/flint/types/fmpz_mpoly.pyx | 39 ++++++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+) diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index 68a14d1f..360d7288 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -2845,6 +2845,8 @@ def quick_poly(): assert raises(lambda: mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}) + None, TypeError) assert raises(lambda: None + mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}), TypeError) assert raises(lambda: quick_poly() + P(ctx=ctx1), IncompatibleContextError) + assert raises(lambda: quick_poly().__iadd__(P(ctx=ctx1)), IncompatibleContextError) + assert quick_poly().__iadd__(None) is NotImplemented assert quick_poly() - mpoly({(0, 0): 5, (0, 1): 6, (1, 0): 7, (2, 2): 8}) \ == mpoly({(0, 0): -4, (0, 1): -4, (1, 0): -4, (2, 2): -4}) @@ -2858,6 +2860,8 @@ def quick_poly(): assert raises(lambda: quick_poly() - None, TypeError) assert raises(lambda: None - quick_poly(), TypeError) assert raises(lambda: quick_poly() - P(ctx=ctx1), IncompatibleContextError) + assert raises(lambda: quick_poly().__isub__(P(ctx=ctx1)), IncompatibleContextError) + assert quick_poly().__isub__(None) is NotImplemented assert quick_poly() * mpoly({(1, 0): 5, (0, 1): 6}) \ == mpoly({ @@ -2879,6 +2883,8 @@ def quick_poly(): assert raises(lambda: quick_poly() * None, TypeError) assert raises(lambda: None * quick_poly(), TypeError) assert raises(lambda: quick_poly() * P(ctx=ctx1), IncompatibleContextError) + assert raises(lambda: quick_poly().__imul__(P(ctx=ctx1)), IncompatibleContextError) + assert quick_poly().__imul__(None) is NotImplemented assert quick_poly() // mpoly({(1, 1): 1}) == mpoly({(1, 1): 4}) assert quick_poly() % mpoly({(1, 1): 1}) \ diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 7fa7a776..aa8a67ee 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -346,6 +346,19 @@ cdef class fmpq_mpoly(flint_mpoly): return res return NotImplemented + def __iadd__(self, other): + if typecheck(other, fmpq_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + fmpq_mpoly_add((self).val, (self).val, (other).val, self.ctx.val) + return self + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + fmpq_mpoly_add_fmpq((self).val, (self).val, (other).val, self.ctx.val) + return self + return NotImplemented + def __sub__(self, other): cdef fmpq_mpoly res if typecheck(other, fmpq_mpoly): @@ -371,6 +384,19 @@ cdef class fmpq_mpoly(flint_mpoly): return -res return NotImplemented + def __isub__(self, other): + if typecheck(other, fmpq_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + fmpq_mpoly_sub((self).val, (self).val, (other).val, self.ctx.val) + return self + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + fmpq_mpoly_sub_fmpq((self).val, (self).val, (other).val, self.ctx.val) + return self + return NotImplemented + def __mul__(self, other): cdef fmpq_mpoly res if typecheck(other, fmpq_mpoly): @@ -396,6 +422,19 @@ cdef class fmpq_mpoly(flint_mpoly): return res return NotImplemented + def __imul__(self, other): + if typecheck(other, fmpq_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + fmpq_mpoly_mul((self).val, (self).val, (other).val, self.ctx.val) + return self + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + fmpq_mpoly_scalar_mul_fmpq(self.val, (self).val, (other).val, self.ctx.val) + return self + return NotImplemented + def __pow__(self, other, modulus): cdef fmpq_mpoly res if modulus is not None: diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index 968069a0..c76311d6 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -326,6 +326,19 @@ cdef class fmpz_mpoly(flint_mpoly): return res return NotImplemented + def __iadd__(self, other): + if typecheck(other, fmpz_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + fmpz_mpoly_add((self).val, (self).val, (other).val, self.ctx.val) + return self + else: + zval = any_as_fmpz(other) + if zval is not NotImplemented: + fmpz_mpoly_add_fmpz((self).val, (self).val, (zval).val, self.ctx.val) + return self + return NotImplemented + def __sub__(self, other): cdef fmpz_mpoly res if typecheck(other, fmpz_mpoly): @@ -351,6 +364,19 @@ cdef class fmpz_mpoly(flint_mpoly): return -res return NotImplemented + def __isub__(self, other): + if typecheck(other, fmpz_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + fmpz_mpoly_sub((self).val, (self).val, (other).val, self.ctx.val) + return self + else: + other = any_as_fmpz(other) + if other is not NotImplemented: + fmpz_mpoly_sub_fmpz((self).val, (self).val, (other).val, self.ctx.val) + return self + return NotImplemented + def __mul__(self, other): cdef fmpz_mpoly res if typecheck(other, fmpz_mpoly): @@ -376,6 +402,19 @@ cdef class fmpz_mpoly(flint_mpoly): return res return NotImplemented + def __imul__(self, other): + if typecheck(other, fmpz_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + fmpz_mpoly_mul((self).val, (self).val, (other).val, self.ctx.val) + return self + else: + other = any_as_fmpz(other) + if other is not NotImplemented: + fmpz_mpoly_scalar_mul_fmpz(self.val, (self).val, (other).val, self.ctx.val) + return self + return NotImplemented + def __pow__(self, other, modulus): cdef fmpz_mpoly res if modulus is not None: From fb107850bc0491d63761d139e2fc916ece8ef54c Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Mon, 8 Jul 2024 20:50:53 +1000 Subject: [PATCH 92/95] Make in-place operators functions, and explict --- src/flint/test/test_all.py | 25 ++++--- src/flint/types/fmpq_mpoly.pyx | 117 ++++++++++++++++++++++----------- src/flint/types/fmpz_mpoly.pyx | 117 ++++++++++++++++++++++----------- 3 files changed, 172 insertions(+), 87 deletions(-) diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index 360d7288..b5c7605f 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -2837,16 +2837,18 @@ def quick_poly(): for T in [int, S, flint.fmpz, lambda x: P(x, ctx=ctx)]: p = quick_poly() p += T(1) + q = quick_poly() + assert q.iadd(T(1)) is None assert quick_poly() + T(1) \ - == p == mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}) + == p == q == mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}) assert T(1) + quick_poly() \ == mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}) assert raises(lambda: mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}) + None, TypeError) assert raises(lambda: None + mpoly({(0, 0): 2, (0, 1): 2, (1, 0): 3, (2, 2): 4}), TypeError) assert raises(lambda: quick_poly() + P(ctx=ctx1), IncompatibleContextError) - assert raises(lambda: quick_poly().__iadd__(P(ctx=ctx1)), IncompatibleContextError) - assert quick_poly().__iadd__(None) is NotImplemented + assert raises(lambda: quick_poly().iadd(P(ctx=ctx1)), IncompatibleContextError) + assert raises(lambda: quick_poly().iadd(None), NotImplementedError) assert quick_poly() - mpoly({(0, 0): 5, (0, 1): 6, (1, 0): 7, (2, 2): 8}) \ == mpoly({(0, 0): -4, (0, 1): -4, (1, 0): -4, (2, 2): -4}) @@ -2854,14 +2856,17 @@ def quick_poly(): for T in [int, S, flint.fmpz, lambda x: P(x, ctx=ctx)]: p = quick_poly() p -= T(1) - assert quick_poly() - T(1) == p == mpoly({(0, 1): 2, (1, 0): 3, (2, 2): 4}) + q = quick_poly() + assert q.isub(T(1)) is None + breakpoint() + assert quick_poly() - T(1) == p == q == mpoly({(0, 1): 2, (1, 0): 3, (2, 2): 4}) assert T(1) - quick_poly() == mpoly({(0, 1): -2, (1, 0): -3, (2, 2): -4}) assert raises(lambda: quick_poly() - None, TypeError) assert raises(lambda: None - quick_poly(), TypeError) assert raises(lambda: quick_poly() - P(ctx=ctx1), IncompatibleContextError) - assert raises(lambda: quick_poly().__isub__(P(ctx=ctx1)), IncompatibleContextError) - assert quick_poly().__isub__(None) is NotImplemented + assert raises(lambda: quick_poly().isub(P(ctx=ctx1)), IncompatibleContextError) + assert raises(lambda: quick_poly().isub(None), NotImplementedError) assert quick_poly() * mpoly({(1, 0): 5, (0, 1): 6}) \ == mpoly({ @@ -2877,14 +2882,16 @@ def quick_poly(): for T in [int, S, flint.fmpz, lambda x: P(x, ctx=ctx)]: p = quick_poly() p *= T(2) - assert quick_poly() * T(2) == p == mpoly({(0, 0): 2, (0, 1): 4, (1, 0): 6, (2, 2): 8}) + q = quick_poly() + assert q.imul(T(2)) is None + assert quick_poly() * T(2) == p == q == mpoly({(0, 0): 2, (0, 1): 4, (1, 0): 6, (2, 2): 8}) assert T(2) * quick_poly() == mpoly({(0, 0): 2, (0, 1): 4, (1, 0): 6, (2, 2): 8}) assert raises(lambda: quick_poly() * None, TypeError) assert raises(lambda: None * quick_poly(), TypeError) assert raises(lambda: quick_poly() * P(ctx=ctx1), IncompatibleContextError) - assert raises(lambda: quick_poly().__imul__(P(ctx=ctx1)), IncompatibleContextError) - assert quick_poly().__imul__(None) is NotImplemented + assert raises(lambda: quick_poly().imul(P(ctx=ctx1)), IncompatibleContextError) + assert raises(lambda: quick_poly().imul(None), NotImplementedError) assert quick_poly() // mpoly({(1, 1): 1}) == mpoly({(1, 1): 4}) assert quick_poly() % mpoly({(1, 1): 1}) \ diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index aa8a67ee..613e7c16 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -346,19 +346,6 @@ cdef class fmpq_mpoly(flint_mpoly): return res return NotImplemented - def __iadd__(self, other): - if typecheck(other, fmpq_mpoly): - if (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - fmpq_mpoly_add((self).val, (self).val, (other).val, self.ctx.val) - return self - else: - other = any_as_fmpq(other) - if other is not NotImplemented: - fmpq_mpoly_add_fmpq((self).val, (self).val, (other).val, self.ctx.val) - return self - return NotImplemented - def __sub__(self, other): cdef fmpq_mpoly res if typecheck(other, fmpq_mpoly): @@ -384,19 +371,6 @@ cdef class fmpq_mpoly(flint_mpoly): return -res return NotImplemented - def __isub__(self, other): - if typecheck(other, fmpq_mpoly): - if (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - fmpq_mpoly_sub((self).val, (self).val, (other).val, self.ctx.val) - return self - else: - other = any_as_fmpq(other) - if other is not NotImplemented: - fmpq_mpoly_sub_fmpq((self).val, (self).val, (other).val, self.ctx.val) - return self - return NotImplemented - def __mul__(self, other): cdef fmpq_mpoly res if typecheck(other, fmpq_mpoly): @@ -422,19 +396,6 @@ cdef class fmpq_mpoly(flint_mpoly): return res return NotImplemented - def __imul__(self, other): - if typecheck(other, fmpq_mpoly): - if (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - fmpq_mpoly_mul((self).val, (self).val, (other).val, self.ctx.val) - return self - else: - other = any_as_fmpq(other) - if other is not NotImplemented: - fmpq_mpoly_scalar_mul_fmpq(self.val, (self).val, (other).val, self.ctx.val) - return self - return NotImplemented - def __pow__(self, other, modulus): cdef fmpq_mpoly res if modulus is not None: @@ -582,6 +543,84 @@ cdef class fmpq_mpoly(flint_mpoly): raise ValueError("unreasonably large polynomial") # pragma: no cover return vres + def iadd(self, other): + """ + In-place addition, mutates self. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 + >>> f.iadd(5) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 + 5 + + """ + if typecheck(other, fmpq_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + fmpq_mpoly_add((self).val, (self).val, (other).val, self.ctx.val) + return + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + fmpq_mpoly_add_fmpq((self).val, (self).val, (other).val, self.ctx.val) + return + raise NotImplementedError(f"in-place addition not implemented between {type(self)} and {type(other)}") + + def isub(self, other): + """ + In-place subtraction, mutates self. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 + >>> f.isub(5) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 - 5 + + """ + if typecheck(other, fmpq_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + fmpq_mpoly_sub((self).val, (self).val, (other).val, self.ctx.val) + return + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + fmpq_mpoly_sub_fmpq((self).val, (self).val, (other).val, self.ctx.val) + return + raise NotImplementedError(f"in-place subtraction not implemented between {type(self)} and {type(other)}") + + def imul(self, other): + """ + In-place multiplication, mutates self. + + >>> from flint import Ordering + >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 + >>> f.imul(2) + >>> f + 8*x0*x1 + 4*x0 + 6*x1 + + """ + if typecheck(other, fmpq_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + fmpq_mpoly_mul((self).val, (self).val, (other).val, self.ctx.val) + return + else: + other = any_as_fmpq(other) + if other is not NotImplemented: + fmpq_mpoly_scalar_mul_fmpq(self.val, (self).val, (other).val, self.ctx.val) + return + raise NotImplementedError(f"in-place multiplication not implemented between {type(self)} and {type(other)}") + def monoms(self): """ Return the exponent vectors of each term as a tuple of fmpz. diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index c76311d6..e1842bc7 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -326,19 +326,6 @@ cdef class fmpz_mpoly(flint_mpoly): return res return NotImplemented - def __iadd__(self, other): - if typecheck(other, fmpz_mpoly): - if (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - fmpz_mpoly_add((self).val, (self).val, (other).val, self.ctx.val) - return self - else: - zval = any_as_fmpz(other) - if zval is not NotImplemented: - fmpz_mpoly_add_fmpz((self).val, (self).val, (zval).val, self.ctx.val) - return self - return NotImplemented - def __sub__(self, other): cdef fmpz_mpoly res if typecheck(other, fmpz_mpoly): @@ -364,19 +351,6 @@ cdef class fmpz_mpoly(flint_mpoly): return -res return NotImplemented - def __isub__(self, other): - if typecheck(other, fmpz_mpoly): - if (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - fmpz_mpoly_sub((self).val, (self).val, (other).val, self.ctx.val) - return self - else: - other = any_as_fmpz(other) - if other is not NotImplemented: - fmpz_mpoly_sub_fmpz((self).val, (self).val, (other).val, self.ctx.val) - return self - return NotImplemented - def __mul__(self, other): cdef fmpz_mpoly res if typecheck(other, fmpz_mpoly): @@ -402,19 +376,6 @@ cdef class fmpz_mpoly(flint_mpoly): return res return NotImplemented - def __imul__(self, other): - if typecheck(other, fmpz_mpoly): - if (self).ctx is not (other).ctx: - raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") - fmpz_mpoly_mul((self).val, (self).val, (other).val, self.ctx.val) - return self - else: - other = any_as_fmpz(other) - if other is not NotImplemented: - fmpz_mpoly_scalar_mul_fmpz(self.val, (self).val, (other).val, self.ctx.val) - return self - return NotImplemented - def __pow__(self, other, modulus): cdef fmpz_mpoly res if modulus is not None: @@ -564,6 +525,84 @@ cdef class fmpz_mpoly(flint_mpoly): raise ValueError("unreasonably large polynomial") # pragma: no cover return vres + def iadd(self, other): + """ + In-place addition, mutates self. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 + >>> f.iadd(5) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 + 5 + + """ + if typecheck(other, fmpz_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + fmpz_mpoly_add((self).val, (self).val, (other).val, self.ctx.val) + return + else: + zval = any_as_fmpz(other) + if zval is not NotImplemented: + fmpz_mpoly_add_fmpz((self).val, (self).val, (zval).val, self.ctx.val) + return + raise NotImplementedError() + + def isub(self, other): + """ + In-place subtraction, mutates self. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 + >>> f.isub(5) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 - 5 + + """ + if typecheck(other, fmpz_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + fmpz_mpoly_sub((self).val, (self).val, (other).val, self.ctx.val) + return + else: + other = any_as_fmpz(other) + if other is not NotImplemented: + fmpz_mpoly_sub_fmpz((self).val, (self).val, (other).val, self.ctx.val) + return + raise NotImplementedError() + + def imul(self, other): + """ + In-place multiplication, mutates self. + + >>> from flint import Ordering + >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) + >>> f + 4*x0*x1 + 2*x0 + 3*x1 + >>> f.imul(2) + >>> f + 8*x0*x1 + 4*x0 + 6*x1 + + """ + if typecheck(other, fmpz_mpoly): + if (self).ctx is not (other).ctx: + raise IncompatibleContextError(f"{(self).ctx} is not {(other).ctx}") + fmpz_mpoly_mul((self).val, (self).val, (other).val, self.ctx.val) + return + else: + other = any_as_fmpz(other) + if other is not NotImplemented: + fmpz_mpoly_scalar_mul_fmpz(self.val, (self).val, (other).val, self.ctx.val) + return + raise NotImplementedError() + def monoms(self): """ Return the exponent vectors of each term as a tuple of fmpz. From c8782c591b38cb85f04fb7a528c3fcee1fd83aaa Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Mon, 8 Jul 2024 21:03:59 +1000 Subject: [PATCH 93/95] Simplify subs method, allow generator index --- src/flint/types/fmpq_mpoly.pyx | 22 +++++++--------------- src/flint/types/fmpz_mpoly.pyx | 22 +++++++--------------- 2 files changed, 14 insertions(+), 30 deletions(-) diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 613e7c16..234d7f63 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -691,7 +691,7 @@ cdef class fmpq_mpoly(flint_mpoly): def subs(self, dict_args) -> fmpq_mpoly: """ - Partial evaluate this polynomial with select constants. Keys must be generator names, + Partial evaluate this polynomial with select constants. Keys must be generator names or generator indices, all values must be fmpq. >>> from flint import Ordering @@ -703,26 +703,18 @@ cdef class fmpq_mpoly(flint_mpoly): """ cdef: fmpq_mpoly res - slong i, nargs - - args = tuple((i, dict_args[x]) for i, x in enumerate(self.ctx.names()) if x in dict_args) - nargs = len(args) - - # If we've been provided with an invalid generator name then the length of our filter - # args will be less than what we've been provided with. - if nargs < len(dict_args): - raise ValueError("unknown generator name provided") + slong i - args_fmpq = tuple(any_as_fmpq(v) for _, v in args) - for arg in args_fmpq: - if arg is NotImplemented: - raise TypeError(f"cannot coerce argument ('{arg}') to fmpq") + args = tuple((self.ctx.variable_to_index(k), any_as_fmpq(v)) for k, v in dict_args.items()) + for _, v in args: + if v is NotImplemented: + raise TypeError(f"cannot coerce argument ('{v}') to fmpq") # Partial application with args in Q. We evaluate the polynomial one variable at a time res = create_fmpq_mpoly(self.ctx) fmpq_mpoly_set(res.val, self.val, self.ctx.val) - for (i, _), arg in zip(args, args_fmpq): + for i, arg in args: if fmpq_mpoly_evaluate_one_fmpq(res.val, res.val, i, (arg).val, self.ctx.val) == 0: raise ValueError("unreasonably large polynomial") # pragma: no cover return res diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index e1842bc7..28c6bf76 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -673,7 +673,7 @@ cdef class fmpz_mpoly(flint_mpoly): def subs(self, dict_args) -> fmpz_mpoly: """ - Partial evaluate this polynomial with select constants. Keys must be generator names, + Partial evaluate this polynomial with select constants. Keys must be generator names or generator indices, all values must be fmpz. >>> from flint import Ordering @@ -685,26 +685,18 @@ cdef class fmpz_mpoly(flint_mpoly): """ cdef: fmpz_mpoly res - slong i, nargs - - args = tuple((i, dict_args[x]) for i, x in enumerate(self.ctx.names()) if x in dict_args) - nargs = len(args) - - # If we've been provided with an invalid keyword arg then the length of our filter - # args will be less than what we've been provided with. - if nargs < len(dict_args): - raise ValueError("unknown generator name provided") + slong i - args_fmpz = tuple(any_as_fmpz(v) for _, v in args) - for arg in args_fmpz: - if arg is NotImplemented: - raise TypeError(f"cannot coerce argument ('{arg}') to fmpz") + args = tuple((self.ctx.variable_to_index(k), any_as_fmpz(v)) for k, v in dict_args.items()) + for _, v in args: + if v is NotImplemented: + raise TypeError(f"cannot coerce argument ('{v}') to fmpz") # Partial application with args in Z. We evaluate the polynomial one variable at a time res = create_fmpz_mpoly(self.ctx) fmpz_mpoly_set(res.val, self.val, self.ctx.val) - for (i, _), arg in zip(args, args_fmpz): + for i, arg in args: if fmpz_mpoly_evaluate_one_fmpz(res.val, res.val, i, (arg).val, self.ctx.val) == 0: raise ValueError("unreasonably large polynomial") # pragma: no cover return res From c0c0517a86a24563bc331b7799fb56261ae60a06 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Mon, 8 Jul 2024 21:04:33 +1000 Subject: [PATCH 94/95] fixup! Remove unused imports, use lower case to start exception message --- src/flint/flint_base/flint_base.pyx | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/flint/flint_base/flint_base.pyx b/src/flint/flint_base/flint_base.pyx index 98699e94..481cac7f 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -135,9 +135,9 @@ cdef class flint_mpoly_context(flint_elem): def __init__(self, int nvars, names): if nvars < 0: - raise ValueError("Cannot have a negative amount of variables") + raise ValueError("cannot have a negative amount of variables") elif len(names) != nvars: - raise ValueError("Number of variables must match number of variable names") + raise ValueError("number of variables must match number of variable names") self.py_names = tuple(name.encode("ascii") if not isinstance(name, bytes) else name for name in names) self.c_names = libc.stdlib.malloc(nvars * sizeof(const char *)) for i in range(nvars): @@ -155,7 +155,7 @@ cdef class flint_mpoly_context(flint_elem): def name(self, long i): if not 0 <= i < len(self.py_names): - raise IndexError("Variable name index out of range") + raise IndexError("variable name index out of range") return self.py_names[i].decode("ascii") def names(self): @@ -170,13 +170,13 @@ cdef class flint_mpoly_context(flint_elem): try: i = self.names().index(var) except ValueError: - raise ValueError("Variable not in context") + raise ValueError("variable not in context") elif isinstance(var, int): if not 0 <= var < self.nvars(): - raise IndexError("Generator index out of range") + raise IndexError("generator index out of range") i = var else: - raise TypeError("Invalid variable type") + raise TypeError("invalid variable type") return i @@ -196,7 +196,7 @@ cdef class flint_mpoly_context(flint_elem): if len(nametup) == 1: nametup = tuple(nametup[0] + str(i) for i in range(nvars)) else: - raise ValueError("Number of variables does not equal number of names") + raise ValueError("number of variables does not equal number of names") return nametup @classmethod @@ -216,7 +216,7 @@ cdef class flint_mpoly_context(flint_elem): 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`") ctx = cls._ctx_cache.get(key) if ctx is None: @@ -366,4 +366,4 @@ cdef ordering_c_to_py(ordering_t ordering): elif ordering == ordering_t.ORD_DEGREVLEX: return Ordering.degrevlex else: - raise ValueError("Unimplemented term order %d" % ordering) + raise ValueError("unimplemented term order %d" % ordering) From d4e6589e630d0aab680c614be5480bd286e80e80 Mon Sep 17 00:00:00 2001 From: Jake-Moss Date: Mon, 8 Jul 2024 21:05:43 +1000 Subject: [PATCH 95/95] fixup! fixup! Remove unused imports, use lower case to start exception message --- src/flint/test/test_all.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index b5c7605f..f44c5e92 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -2795,7 +2795,7 @@ def quick_poly(): assert p.coeffs() == [4, 3, 2, 1] assert list(p.terms()) == list(zip([(2, 2), (1, 0), (0, 1), (0, 0)], [4, 3, 2, 1])) - assert p.subs({"x1": S(0), "x0": S(0)}) == ctx.from_dict({(0, 0): 1}) + assert p.subs({"x1": S(0), 0: S(0)}) == ctx.from_dict({(0, 0): 1}) assert p.compose(p.subs({"x1": 0}), ctx.from_dict({(0, 1): 1})) == mpoly({ (2, 2): 36, (1, 2): 24, @@ -2858,7 +2858,6 @@ def quick_poly(): p -= T(1) q = quick_poly() assert q.isub(T(1)) is None - breakpoint() assert quick_poly() - T(1) == p == q == mpoly({(0, 1): 2, (1, 0): 3, (2, 2): 4}) assert T(1) - quick_poly() == mpoly({(0, 1): -2, (1, 0): -3, (2, 2): -4})