From 43dc15dd9f65e02e2b013faac654d08bf91e48c1 Mon Sep 17 00:00:00 2001 From: Vitaly Magerya Date: Wed, 26 Aug 2026 15:12:58 +0200 Subject: [PATCH 1/7] Add fmpq_mpoly.zpoly and fmpq_mpoly.zcontent --- src/flint/types/fmpq_mpoly.pyi | 3 +++ src/flint/types/fmpq_mpoly.pyx | 32 +++++++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/flint/types/fmpq_mpoly.pyi b/src/flint/types/fmpq_mpoly.pyi index 6dec549b..a8ed897e 100644 --- a/src/flint/types/fmpq_mpoly.pyi +++ b/src/flint/types/fmpq_mpoly.pyi @@ -102,6 +102,9 @@ class fmpq_mpoly(flint_mpoly[fmpq_mpoly_ctx, fmpq, ifmpq]): def xgcd(self, other: fmpq_mpoly, /) -> fmpq: ... def term_content(self) -> fmpq_mpoly: ... + def zpoly(self) -> fmpz_mpoly: ... + def zcontent(self) -> fmpq: ... + def factor(self) -> tuple[fmpq, list[tuple[fmpq_mpoly, int]]]: ... def factor_squarefree(self) -> tuple[fmpq, list[tuple[fmpq_mpoly, int]]]: ... diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index c00160b8..7089661d 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -17,7 +17,7 @@ from flint.types.fmpz_vec cimport fmpz_vec 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.types.fmpz_mpoly cimport fmpz_mpoly, fmpz_mpoly_ctx, create_fmpz_mpoly from flint.flintlib.functions.fmpq cimport fmpq_set, fmpq_one from flint.flintlib.functions.fmpq_mpoly cimport ( @@ -785,6 +785,36 @@ cdef class fmpq_mpoly(flint_mpoly): fmpq_mpoly_term_content(res.val, self.val, self.ctx.val) return res + def zpoly(self): + """ + Return the integer polynomial of ``self``. The product + of this and ``self.zcontent()``, represents the whole + polynomial. + + >>> ctx = fmpq_mpoly_ctx.get(("x","y")) + >>> x, y = ctx.gens() + >>> (fmpq(2,3)*x + fmpq(2,5)*y).zpoly() + 5*x + 3*y + """ + cdef fmpz_mpoly_ctx zctx = fmpz_mpoly_ctx.from_context(self.ctx) + cdef fmpz_mpoly res = create_fmpz_mpoly(zctx) + fmpz_mpoly_set(res.val, self.val.zpoly, zctx.val) + return res + + def zcontent(self): + """ + Return the content of ``self``. The product of this and + ``self.zpoly()``, represents the whole polynomial. + + >>> ctx = fmpq_mpoly_ctx.get(("x","y")) + >>> x, y = ctx.gens() + >>> (fmpq(2,3)*x + fmpq(2,5)*y).zcontent() + 2/15 + """ + cdef fmpq res = fmpq.__new__(fmpq) + fmpq_set(res.val, self.val.content) + return res + def resultant(self, other, var): """ Return the resultant of ``self`` and ``other`` with respect to variable ``var``. From 725a1940d2ee3b51795e65475534b3da735055ec Mon Sep 17 00:00:00 2001 From: Oscar Benjamin Date: Wed, 26 Aug 2026 14:43:23 +0100 Subject: [PATCH 2/7] Add 0.10.0 release note section in README --- README.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/README.md b/README.md index 45ef3a39..71d903fa 100644 --- a/README.md +++ b/README.md @@ -163,6 +163,20 @@ experimental. CHANGELOG ========= +0.10.0 (Not yet released) +------------------------- + +Contributors (0.10.0): + +- Insert Name (IN) + +Changes (0.10.0): + +- [gh-322](https://github.com/flintlib/python-flint/pull/322), Add `zcontent` + and `zpoly` methods to `fmpq_mpoly`, to get the primitive integer polynomial + of self and the `fmpq` content (the internal representation of the + `fmpq_mpoly`). (IN) + 0.9.0 ----- From 1f3c4a5b69853fe3e057c93e61e9d023c5d72061 Mon Sep 17 00:00:00 2001 From: Vitaly Magerya Date: Wed, 26 Aug 2026 16:26:51 +0200 Subject: [PATCH 3/7] Rename fmpq_mpoly.zpoly into zprimitive Also fix their docstring tests. --- README.md | 10 +++++----- src/flint/types/fmpq_mpoly.pyi | 2 +- src/flint/types/fmpq_mpoly.pyx | 8 ++++---- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 71d903fa..ee9475a3 100644 --- a/README.md +++ b/README.md @@ -168,14 +168,14 @@ CHANGELOG Contributors (0.10.0): -- Insert Name (IN) +- Vitaly Magerya (VM) Changes (0.10.0): -- [gh-322](https://github.com/flintlib/python-flint/pull/322), Add `zcontent` - and `zpoly` methods to `fmpq_mpoly`, to get the primitive integer polynomial - of self and the `fmpq` content (the internal representation of the - `fmpq_mpoly`). (IN) +- [gh-421](https://github.com/flintlib/python-flint/pull/421), + Add `zprimitive` and `zcontent` methods to `fmpq_mpoly`, to get + the primitive integer polynomial and the `fmpq` content, which + form the internal representation of the `fmpq_mpoly`. (VM) 0.9.0 ----- diff --git a/src/flint/types/fmpq_mpoly.pyi b/src/flint/types/fmpq_mpoly.pyi index a8ed897e..a8f3b606 100644 --- a/src/flint/types/fmpq_mpoly.pyi +++ b/src/flint/types/fmpq_mpoly.pyi @@ -102,7 +102,7 @@ class fmpq_mpoly(flint_mpoly[fmpq_mpoly_ctx, fmpq, ifmpq]): def xgcd(self, other: fmpq_mpoly, /) -> fmpq: ... def term_content(self) -> fmpq_mpoly: ... - def zpoly(self) -> fmpz_mpoly: ... + def zprimitive(self) -> fmpz_mpoly: ... def zcontent(self) -> fmpq: ... def factor(self) -> tuple[fmpq, list[tuple[fmpq_mpoly, int]]]: ... diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 7089661d..313efd12 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -785,7 +785,7 @@ cdef class fmpq_mpoly(flint_mpoly): fmpq_mpoly_term_content(res.val, self.val, self.ctx.val) return res - def zpoly(self): + def zprimitive(self): """ Return the integer polynomial of ``self``. The product of this and ``self.zcontent()``, represents the whole @@ -793,7 +793,7 @@ cdef class fmpq_mpoly(flint_mpoly): >>> ctx = fmpq_mpoly_ctx.get(("x","y")) >>> x, y = ctx.gens() - >>> (fmpq(2,3)*x + fmpq(2,5)*y).zpoly() + >>> (x*2/3 + y*2/5).zprimitive() 5*x + 3*y """ cdef fmpz_mpoly_ctx zctx = fmpz_mpoly_ctx.from_context(self.ctx) @@ -804,11 +804,11 @@ cdef class fmpq_mpoly(flint_mpoly): def zcontent(self): """ Return the content of ``self``. The product of this and - ``self.zpoly()``, represents the whole polynomial. + ``self.zprimitive()``, represents the whole polynomial. >>> ctx = fmpq_mpoly_ctx.get(("x","y")) >>> x, y = ctx.gens() - >>> (fmpq(2,3)*x + fmpq(2,5)*y).zcontent() + >>> (x*2/3 + y*2/5).zcontent() 2/15 """ cdef fmpq res = fmpq.__new__(fmpq) From b8927a2fa0c5a877b6c544f3c5a2deca8ef837f9 Mon Sep 17 00:00:00 2001 From: Oscar Benjamin Date: Wed, 26 Aug 2026 18:21:59 +0100 Subject: [PATCH 4/7] Remove FLINT 3.1 from CI test matrix The CI job would sporadically fail because FLINT 3.1 has a bug that makes it fail to build on newer GitHub Actions runners (with e.g. znver3 as the CPU name). --- .github/workflows/buildwheel.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/buildwheel.yml b/.github/workflows/buildwheel.yml index e60fc9dd..7c07eb8a 100644 --- a/.github/workflows/buildwheel.yml +++ b/.github/workflows/buildwheel.yml @@ -363,7 +363,6 @@ jobs: # Supported Flint versions: flint-tag: [ 'v3.0.1', - 'v3.1.3-p1', 'v3.2.2', 'v3.3.1', 'v3.4.0', From 7ea428655aa6c70ed23b1ece94603f8202c4f09f Mon Sep 17 00:00:00 2001 From: Oscar Benjamin Date: Wed, 26 Aug 2026 18:34:23 +0100 Subject: [PATCH 5/7] Allow building with Cython 3.3 --- README.md | 5 ++++- doc/source/build.rst | 5 +++++ meson.build | 2 +- pyproject.toml | 2 +- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ee9475a3..d57a20da 100644 --- a/README.md +++ b/README.md @@ -144,6 +144,7 @@ Compatible versions: | python-flint | Release date | CPython | FLINT | Cython | |--------------|---------------|-------------|------------|------------------| +| `0.10.0` | Not released | `3.10-3.15` | `3.0-3.6` | `3.1-3.3` | | `0.9.0` | 3rd Jul 2026 | `3.10-3.14` | `3.0-3.6` | `3.1-3.2` | | `0.8.0` | 29th Aug 2025 | `3.11-3.14` | `3.0-3.3` | `3.1` only | | `0.7.0` | 16th Mar 2025 | `3.11-3.13` | `3.0-3.2` | `3.0.11-3.1.0a1` | @@ -169,13 +170,15 @@ CHANGELOG Contributors (0.10.0): - Vitaly Magerya (VM) +- Oscar Benjamin (OB) Changes (0.10.0): - [gh-421](https://github.com/flintlib/python-flint/pull/421), Add `zprimitive` and `zcontent` methods to `fmpq_mpoly`, to get the primitive integer polynomial and the `fmpq` content, which - form the internal representation of the `fmpq_mpoly`. (VM) + form the internal representation of the `fmpq_mpoly`. Also add + support for building with Cython 3.3. (VM, OB) 0.9.0 ----- diff --git a/doc/source/build.rst b/doc/source/build.rst index 4fb9e3c9..11556216 100644 --- a/doc/source/build.rst +++ b/doc/source/build.rst @@ -84,6 +84,11 @@ Compatible versions: - CPython - FLINT - Cython + * - 0.10.0 + - Not released + - 3.10-3.15 + - 3.0-3.6 + - 3.1-3.3 * - 0.9.0 - 3rd Jul 2026 - 3.10-3.14 diff --git a/meson.build b/meson.build index 3392cdf7..ab150a67 100644 --- a/meson.build +++ b/meson.build @@ -18,7 +18,7 @@ project( flint_lower = '>=3.0' flint_upper = '<3.7' cython_lower = '>=3.0.11' -cython_upper = '<3.3' +cython_upper = '<3.4' py = import('python').find_installation(pure: false) diff --git a/pyproject.toml b/pyproject.toml index b940562c..44220d1e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,7 +41,7 @@ content-type = "text/markdown" # fine. It is not possible to have a separate version constraint here for the # freethreading build only though. # -requires = ["meson-python >= 0.18", "cython >=3.1,<3.3"] +requires = ["meson-python >= 0.18", "cython >=3.1,<3.4"] build-backend = "mesonpy" [tool.meson-python] From efd85a77a03e99bbbc8c4730ce4789291463ab7b Mon Sep 17 00:00:00 2001 From: Oscar Benjamin Date: Wed, 26 Aug 2026 19:03:51 +0100 Subject: [PATCH 6/7] Test negative and zero content in fmpq_mpoly.zcontent() --- src/flint/test/test_all.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index 2b10111e..8bb18857 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -3934,6 +3934,27 @@ def quick_poly(): assert False # New _mpoly types? +def test_fmpq_mpoly_zcontent_zprimitive(): + ctx = flint.fmpq_mpoly_ctx.get(("x", "y")) + x, y = ctx.gens() + zctx = flint.fmpz_mpoly_ctx.from_context(ctx) + zx, zy = zctx.gens() + + f = -2*x/3 - 2*y/5 + assert f.zcontent() == flint.fmpq(-2, 15) + assert f.zprimitive() == 5*zx + 3*zy + assert isinstance(f.zcontent(), flint.fmpq) + assert isinstance(f.zprimitive(), flint.fmpz_mpoly) + # Can't mix fmpq_mpoly with fmpz_mpoly... + p = flint.fmpq_mpoly(f.zprimitive(), ctx=ctx) + c = f.zcontent() + assert p * c == f + + f = 0*x + assert f.zcontent() == 0 + assert f.zprimitive() == 0 + + def _all_mpoly_vecs(): return [(flint.fmpz_mpoly_ctx, flint.fmpz_mpoly_vec), (flint.fmpq_mpoly_ctx, flint.fmpq_mpoly_vec)] From 7edef2a6f9f462b868f52f02faae5040d7af8b18 Mon Sep 17 00:00:00 2001 From: Oscar Benjamin Date: Wed, 26 Aug 2026 22:10:41 +0100 Subject: [PATCH 7/7] Rename zprimitive to zpoly --- README.md | 2 +- src/flint/test/test_all.py | 10 +++++----- src/flint/types/fmpq_mpoly.pyi | 2 +- src/flint/types/fmpq_mpoly.pyx | 25 +++++++++++++++++++++---- 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index d57a20da..8cbb492e 100644 --- a/README.md +++ b/README.md @@ -175,7 +175,7 @@ Contributors (0.10.0): Changes (0.10.0): - [gh-421](https://github.com/flintlib/python-flint/pull/421), - Add `zprimitive` and `zcontent` methods to `fmpq_mpoly`, to get + Add `zpoly` and `zcontent` methods to `fmpq_mpoly`, to get the primitive integer polynomial and the `fmpq` content, which form the internal representation of the `fmpq_mpoly`. Also add support for building with Cython 3.3. (VM, OB) diff --git a/src/flint/test/test_all.py b/src/flint/test/test_all.py index 8bb18857..d7216a2f 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -3934,7 +3934,7 @@ def quick_poly(): assert False # New _mpoly types? -def test_fmpq_mpoly_zcontent_zprimitive(): +def test_fmpq_mpoly_zcontent_zpoly(): ctx = flint.fmpq_mpoly_ctx.get(("x", "y")) x, y = ctx.gens() zctx = flint.fmpz_mpoly_ctx.from_context(ctx) @@ -3942,17 +3942,17 @@ def test_fmpq_mpoly_zcontent_zprimitive(): f = -2*x/3 - 2*y/5 assert f.zcontent() == flint.fmpq(-2, 15) - assert f.zprimitive() == 5*zx + 3*zy + assert f.zpoly() == 5*zx + 3*zy assert isinstance(f.zcontent(), flint.fmpq) - assert isinstance(f.zprimitive(), flint.fmpz_mpoly) + assert isinstance(f.zpoly(), flint.fmpz_mpoly) # Can't mix fmpq_mpoly with fmpz_mpoly... - p = flint.fmpq_mpoly(f.zprimitive(), ctx=ctx) + p = flint.fmpq_mpoly(f.zpoly(), ctx=ctx) c = f.zcontent() assert p * c == f f = 0*x assert f.zcontent() == 0 - assert f.zprimitive() == 0 + assert f.zpoly() == 0 def _all_mpoly_vecs(): diff --git a/src/flint/types/fmpq_mpoly.pyi b/src/flint/types/fmpq_mpoly.pyi index a8f3b606..a8ed897e 100644 --- a/src/flint/types/fmpq_mpoly.pyi +++ b/src/flint/types/fmpq_mpoly.pyi @@ -102,7 +102,7 @@ class fmpq_mpoly(flint_mpoly[fmpq_mpoly_ctx, fmpq, ifmpq]): def xgcd(self, other: fmpq_mpoly, /) -> fmpq: ... def term_content(self) -> fmpq_mpoly: ... - def zprimitive(self) -> fmpz_mpoly: ... + def zpoly(self) -> fmpz_mpoly: ... def zcontent(self) -> fmpq: ... def factor(self) -> tuple[fmpq, list[tuple[fmpq_mpoly, int]]]: ... diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index 313efd12..cbc0a9d7 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -215,6 +215,23 @@ cdef class fmpq_mpoly(flint_mpoly): """ The *fmpq_mpoly* type represents sparse multivariate polynomials over the rationals. + + Internally, an ``fmpq_mpoly`` is represented as the product of a signed + rational content and a primitive integer multivariate polynomial with + positive leading coefficient. The :meth:`zcontent` and :meth:`zpoly` + methods return copies of these two components, respectively. + + The original polynomial can be reconstructed from these components by + converting the integer polynomial back to an ``fmpq_mpoly`` in the + original context and multiplying by the content: + + >>> ctx = fmpq_mpoly_ctx.get(("x", "y")) + >>> x, y = ctx.gens() + >>> f = -2*x/3 - 2*y/5 + >>> zpoly = f.zpoly() + >>> content = f.zcontent() + >>> fmpq_mpoly(zpoly, ctx=f.context()) * content == f + True """ def __cinit__(self): @@ -785,15 +802,15 @@ cdef class fmpq_mpoly(flint_mpoly): fmpq_mpoly_term_content(res.val, self.val, self.ctx.val) return res - def zprimitive(self): + def zpoly(self): """ Return the integer polynomial of ``self``. The product - of this and ``self.zcontent()``, represents the whole + of this and ``self.zcontent()`` represents the whole polynomial. >>> ctx = fmpq_mpoly_ctx.get(("x","y")) >>> x, y = ctx.gens() - >>> (x*2/3 + y*2/5).zprimitive() + >>> (x*2/3 + y*2/5).zpoly() 5*x + 3*y """ cdef fmpz_mpoly_ctx zctx = fmpz_mpoly_ctx.from_context(self.ctx) @@ -804,7 +821,7 @@ cdef class fmpq_mpoly(flint_mpoly): def zcontent(self): """ Return the content of ``self``. The product of this and - ``self.zprimitive()``, represents the whole polynomial. + ``self.zpoly()`` represents the whole polynomial. >>> ctx = fmpq_mpoly_ctx.get(("x","y")) >>> x, y = ctx.gens()