Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add mpoly docs, functions for discriminant, resultant, term_content, and deflation, and run mod_mpoly doctests. #216

Merged
merged 28 commits into from
Sep 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
91f498f
Run mod_mpoly and vec doc tests, sort module imports
Sep 4, 2024
e84d0e9
Add mpoly discriminant, resultant, term_content, and deflation
Sep 4, 2024
67e7aa5
Deflation docs
Jake-Moss Sep 4, 2024
86254f2
Add mpoly docs, clean up verbatim text
Jake-Moss Sep 4, 2024
f07bdc9
Add tests
Jake-Moss Sep 4, 2024
ac4c3e4
Add content method for fmpz_mpoly and fmpz_poly
Jake-Moss Sep 5, 2024
a5f4d2c
Add missing truediv_scalar methods to fmpz_mpoly and fmpq_mpoly
Jake-Moss Sep 5, 2024
9e7bcb5
Remove to_tuple, replace with __iter__ generator
Jake-Moss Sep 5, 2024
3db235b
Improve primitive function for fmpz_mpoly
Jake-Moss Sep 5, 2024
6d3c5de
Linting
Jake-Moss Sep 5, 2024
46d39cf
Rework fmpz_[m]poly inflation and deflation, wip
Jake-Moss Sep 6, 2024
206d7b0
POC doctest discovery
Jake-Moss Sep 7, 2024
2f9c24f
Add term helper method
Jake-Moss Sep 7, 2024
662e06d
New deflation functions
Jake-Moss Sep 7, 2024
9c06381
Integrate doctests with pytest
Jake-Moss Sep 8, 2024
9dc4f4c
Don't say "Failed:" twice
Jake-Moss Sep 8, 2024
170f212
Add invariant tests and correct doc strings
Jake-Moss Sep 8, 2024
6b6ca2c
Merge remote-tracking branch 'origin/main'
Jake-Moss Sep 8, 2024
e001635
Conditionally define the pytest doctest target
Jake-Moss Sep 8, 2024
f99158d
fixup! Conditionally define the pytest doctest target
Jake-Moss Sep 8, 2024
2e0ebc7
Fix doctest exceptions not being reported as pytest failures
Jake-Moss Sep 8, 2024
1b31a67
Fix mpoly.deflation, fix invariant tests
Jake-Moss Sep 8, 2024
b2d2694
Make verbose tests identifible
Jake-Moss Sep 8, 2024
f34e228
Replace back ticks with quotes in exception strings
Sep 9, 2024
f482540
Clarify doc strings
Sep 9, 2024
5fc1d74
Create uncocumented.pxd, move undocumented function uses
Sep 9, 2024
703e0dc
Split `_truediv_scalar_` into two functions
Sep 9, 2024
4d22d95
Skip arb.hypgeom_2f1 non-convergence doctest
Sep 9, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions doc/source/fmpq_mpoly.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
**fmpq_mpoly** -- multivariate polynomials over the rational numbers
===============================================================================

.. autoclass :: flint.fmpq_mpoly_ctx
:members:
:inherited-members:
:undoc-members:

.. autoclass :: flint.fmpq_mpoly
:members:
:inherited-members:
:undoc-members:

.. autoclass :: flint.fmpq_mpoly_vec
:members:
:inherited-members:
:undoc-members:

18 changes: 18 additions & 0 deletions doc/source/fmpz_mod_mpoly.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
**fmpz_mod_mpoly** -- multivariate polynomials over the integers mod n
===============================================================================

.. autoclass :: flint.fmpz_mod_mpoly_ctx
:members:
:inherited-members:
:undoc-members:

.. autoclass :: flint.fmpz_mod_mpoly
:members:
:inherited-members:
:undoc-members:

.. autoclass :: flint.fmpz_mod_mpoly_vec
:members:
:inherited-members:
:undoc-members:

18 changes: 18 additions & 0 deletions doc/source/fmpz_mpoly.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
**fmpz_mpoly** -- multivariate polynomials over the integers
===============================================================================

.. autoclass :: flint.fmpz_mpoly_ctx
:members:
:inherited-members:
:undoc-members:

.. autoclass :: flint.fmpz_mpoly
:members:
:inherited-members:
:undoc-members:

.. autoclass :: flint.fmpz_mpoly_vec
:members:
:inherited-members:
:undoc-members:

4 changes: 4 additions & 0 deletions doc/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,13 @@ Polynomial types
:maxdepth: 1

fmpz_poly.rst
fmpz_mpoly.rst
fmpq_poly.rst
fmpq_mpoly.rst
nmod_poly.rst
nmod_mpoly.rst
fmpz_mod_poly.rst
fmpz_mod_mpoly.rst
fq_default_poly.rst
arb_poly.rst
acb_poly.rst
Expand Down
18 changes: 18 additions & 0 deletions doc/source/nmod_mpoly.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
**nmod_mpoly** -- multivariate polynomials over the integers mod n (word-size n)
===============================================================================

.. autoclass :: flint.nmod_mpoly_ctx
:members:
:inherited-members:
:undoc-members:

.. autoclass :: flint.nmod_mpoly
:members:
:inherited-members:
:undoc-members:

.. autoclass :: flint.nmod_mpoly_vec
:members:
:inherited-members:
:undoc-members:

2 changes: 2 additions & 0 deletions src/flint/flint_base/flint_base.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ cdef class flint_mpoly(flint_elem):
cdef _mul_mpoly_(self, other)

cdef _divmod_mpoly_(self, other)
cdef _truediv_scalar_(self, other)
cdef _divexact_scalar_(self, other)
cdef _floordiv_mpoly_(self, other)
cdef _truediv_mpoly_(self, other)
cdef _mod_mpoly_(self, other)
Expand Down
52 changes: 40 additions & 12 deletions src/flint/flint_base/flint_base.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -229,7 +229,7 @@ cdef class flint_poly(flint_elem):
integer root and *m* is the multiplicity of the root.

To compute complex roots of a polynomial, instead use
the `.complex_roots()` method, which is available on
the ``.complex_roots()`` method, which is available on
certain polynomial rings.

>>> from flint import fmpz_poly
Expand Down Expand Up @@ -322,9 +322,9 @@ cdef class flint_mpoly_context(flint_elem):
@staticmethod
def create_variable_names(slong nvars, names: str):
"""
Create a tuple of variable names based on the comma separated `names` string.
Create a tuple of variable names based on the comma separated ``names`` string.

If `names` contains a single value, and `nvars` > 1, then the variables are numbered, e.g.
If ``names`` contains a single value, and ``nvars`` > 1, then the variables are numbered, e.g.

>>> flint_mpoly_context.create_variable_names(3, "x")
('x0', 'x1', 'x2')
Expand All @@ -344,24 +344,24 @@ cdef class flint_mpoly_context(flint_elem):
Create a key for the context cache via the number of variables, the ordering, and
either a variable name string, or a tuple of variable names.
"""
# A type hint of `ordering: Ordering` results in the error "TypeError: an integer is required" if a Ordering
# A type hint of ``ordering: Ordering`` results in the error "TypeError: an integer is required" if a Ordering
# object is not provided. This is pretty obtuse so we check its type ourselves
if not isinstance(ordering, Ordering):
raise TypeError(f"`ordering` ('{ordering}') is not an instance of flint.Ordering")
raise TypeError(f"'ordering' ('{ordering}') is not an instance of flint.Ordering")

if nametup is not None:
key = nvars, ordering, nametup
elif nametup is None and names is not None:
key = nvars, ordering, cls.create_variable_names(nvars, names)
else:
raise ValueError("must provide either `names` or `nametup`")
raise ValueError("must provide either 'names' or 'nametup'")
return key

@classmethod
def get_context(cls, *args, **kwargs):
"""
Retrieve a context via the number of variables, `nvars`, the ordering, `ordering`, and either a variable
name string, `names`, or a tuple of variable names, `nametup`.
Retrieve a context via the number of variables, ``nvars``, the ordering, ``ordering``, and either a variable
name string, ``names``, or a tuple of variable names, ``nametup``.
"""
key = cls.create_context_key(*args, **kwargs)

Expand Down Expand Up @@ -391,6 +391,24 @@ cdef class flint_mpoly_context(flint_elem):
elif other is not self:
raise IncompatibleContextError(f"{other} is not {self}")

def term(self, coeff = None, exp_vec = None):
"""
Create a monomial from a coefficient and exponent vector. ``coeff`` defaults
to ``1``. ``exp_vec``` defaults to ``(0,) * self.nvars()```.

>>> from flint import fmpz_mpoly_ctx, Ordering
>>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x')
>>> ctx.term(coeff=5, exp_vec=(2, 3))
5*x0^2*x1^3
>>> ctx.term()
1
"""
if coeff is None:
coeff = 1
if exp_vec is None:
exp_vec = (0,) * self.nvars()
return self.from_dict({tuple(exp_vec): coeff})


cdef class flint_mpoly(flint_elem):
"""
Expand All @@ -405,7 +423,7 @@ cdef class flint_mpoly(flint_elem):

def _division_check(self, other):
if not other:
raise ZeroDivisionError("nmod_mpoly division by zero")
raise ZeroDivisionError(f"{self.__class__.__name__} division by zero")

cdef _add_scalar_(self, other):
return NotImplemented
Expand All @@ -431,6 +449,12 @@ cdef class flint_mpoly(flint_elem):
cdef _floordiv_mpoly_(self, other):
return NotImplemented

cdef _truediv_scalar_(self, other):
return NotImplemented

cdef _divexact_scalar_(self, other):
return NotImplemented

cdef _truediv_mpoly_(self, other):
return NotImplemented

Expand Down Expand Up @@ -589,8 +613,12 @@ cdef class flint_mpoly(flint_elem):
if other is NotImplemented:
return NotImplemented

other = self.context().scalar_as_mpoly(other)
self._division_check(other)
res = self._truediv_scalar_(other)
if res is not NotImplemented:
return res

other = self.context().scalar_as_mpoly(other)
return self._truediv_mpoly_(other)

def __rtruediv__(self, other):
Expand Down Expand Up @@ -725,7 +753,7 @@ cdef class flint_mpoly(flint_elem):

def __contains__(self, x):
"""
Returns True if `self` contains a term with exponent vector `x` and a non-zero coefficient.
Returns True if ``self`` contains a term with exponent vector ``x`` and a non-zero coefficient.

>>> from flint import fmpq_mpoly_ctx, Ordering
>>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x')
Expand Down Expand Up @@ -829,7 +857,7 @@ cdef class flint_mat(flint_elem):

cdef ordering_t ordering_py_to_c(ordering): # Cython does not like an "Ordering" type hint here
if not isinstance(ordering, Ordering):
raise TypeError(f"`ordering` ('{ordering}') is not an instance of flint.Ordering")
raise TypeError(f"'ordering' ('{ordering}') is not an instance of flint.Ordering")

if ordering == Ordering.lex:
return ordering_t.ORD_LEX
Expand Down
3 changes: 0 additions & 3 deletions src/flint/flintlib/types/nmod.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,6 @@ cdef extern from "flint/nmod_types.h":

ctypedef nmod_mat_struct nmod_mat_t[1]

# XXX: Undocumented function:
int nmod_mat_is_square(const nmod_mat_t mat)

# Macros:
ulong nmod_mat_entry(nmod_mat_t mat, slong i, slong j)

Expand Down
12 changes: 12 additions & 0 deletions src/flint/flintlib/types/undocumented.pxd
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from flint.flintlib.types.nmod cimport nmod_mat_t

from flint.flintlib.types.flint cimport fmpz_struct
from flint.flintlib.types.nmod cimport nmod_mpoly_ctx_t, nmod_mpoly_t

cdef extern from "flint/nmod_types.h":
int nmod_mat_is_square(const nmod_mat_t mat)

cdef extern from "flint/nmod_mpoly.h":
void nmod_mpoly_deflation(fmpz_struct * shift, fmpz_struct * stride, const nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx)
void nmod_mpoly_deflate(nmod_mpoly_t A, const nmod_mpoly_t B, const fmpz_struct * shift, const fmpz_struct * stride, const nmod_mpoly_ctx_t ctx)
void nmod_mpoly_inflate(nmod_mpoly_t A, const nmod_mpoly_t B, const fmpz_struct * shift, const fmpz_struct * stride, const nmod_mpoly_ctx_t ctx)
63 changes: 14 additions & 49 deletions src/flint/test/__main__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

import flint
from flint.test.test_all import all_tests
from flint.test.test_docstrings import find_doctests


def run_tests(verbose=None):
Expand Down Expand Up @@ -48,54 +49,18 @@ def run_tests(verbose=None):
return failed, total


def run_doctests(verbose=None):
"""Run the python-flint doctests"""
# Here verbose=True shows a lot of output.
modules = [flint.pyflint,
flint.flint_base.flint_base,
flint.flint_base.flint_context,
flint.types.fmpz,
flint.types.fmpz_poly,
flint.types.fmpz_mat,
flint.types.fmpz_mpoly,
flint.types.fmpz_series,
flint.types.fmpz_mod,
flint.types.fmpz_mod_poly,
flint.types.fmpz_mod_mat,
flint.types.fmpq,
flint.types.fmpq_poly,
flint.types.fmpq_mat,
flint.types.fmpq_mpoly,
flint.types.fmpq_series,
flint.types.nmod,
flint.types.nmod_poly,
flint.types.nmod_mat,
flint.types.nmod_series,
flint.types.fq_default,
flint.types.fq_default_poly,
flint.types.arf,
flint.types.arb,
flint.types.arb_poly,
flint.types.arb_mat,
flint.types.arb_series,
flint.types.acb,
flint.types.acb_poly,
flint.types.acb_mat,
flint.types.acb_series,
flint.types.dirichlet,
flint.functions.showgood]
try:
from flint.types import acb_theta
modules.append(acb_theta)
except ImportError:
pass

results = []
for x in modules:
if verbose:
print(f" {x.__name__}")
results.append(doctest.testmod(x))
return tuple(sum(res) for res in zip(*results))
def run_doctests(tests, verbose=False):
runner = doctest.DocTestRunner()
for module, test_set in tests:
if test_set:
print(f"{module}...", end="" if not verbose else "\n", flush=True)
for test in test_set:
if verbose:
print("\tTesting:", test.name)
runner.run(test)
print("OK")

return runner.summarize()


def run_all_tests(tests=True, doctests=True, verbose=None):
Expand All @@ -108,7 +73,7 @@ def run_all_tests(tests=True, doctests=True, verbose=None):

if doctests:
print("Running doctests...")
d_failed, d_total = run_doctests(verbose=verbose)
d_failed, d_total = run_doctests(find_doctests(flint), verbose=verbose)

if tests:
if t_failed:
Expand Down
1 change: 1 addition & 0 deletions src/flint/test/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pyfiles = [
'__init__.py',
'__main__.py',
'test_all.py',
'test_docstrings.py',
]

py.install_sources(
Expand Down
39 changes: 38 additions & 1 deletion src/flint/test/test_all.py
Original file line number Diff line number Diff line change
Expand Up @@ -3229,6 +3229,43 @@ def quick_poly():
assert raises(lambda: quick_poly().gcd(None), TypeError)
assert raises(lambda: quick_poly().gcd(P(ctx=ctx1)), IncompatibleContextError)

x0, x1 = ctx.gens()
assert (2*x0**3 + 4*x0**2 + 6*x0).term_content() == x0 if is_field else 2*x0
assert (3*x0**2*x1 + 6*x0*x1**2 + 9*x1**3).term_content() == x1 if is_field else 3*x1

assert (x0**2 - 1).resultant(x0**2 - 2*x0 + 1, 'x0') == 0
assert (x0**2 + x1**2 - 1).resultant(x0 - x1, 'x0') == 2*x1**2 - 1

assert (x0**3 - 6*x0**2 + 11*x0 - 6).discriminant('x0') == 4
assert (x0**2 + 4*x0*x1 + 4*x1**2 - 1).discriminant('x0') == 4

f1 = 3*x0**2*x1**2 + 6*x0*x1**2 + 9*x1**2
res, stride = f1.deflation()
assert res == 3*x0**2*x1 + 6*x0*x1 + 9*x1
assert tuple(stride) == (1, 2)

g1 = ((x0**2 + x1**2)**3 + (x0**2 + x1**2)**2 + 1)
res, stride = g1.deflation()
assert res == x0**3 + 3*x0**2*x1 + x0**2 + 3*x0*x1**2 + 2*x0*x1 + x1**3 + x1**2 + 1
assert tuple(stride) == (2, 2)

for p in [f1, g1]:
pd, n = p.deflation()
assert pd.inflate(n) == p
assert p.deflate(n).inflate(n) == p

pd, n, m = p.deflation_monom()
assert m * pd.inflate(n) == p

if not composite_characteristic:
n, i = p.deflation_index()
m = ctx.term(exp_vec=i)
assert (p / m).deflate(n).inflate(n) * m == p

if P is flint.fmpz_mpoly:
assert (x0**2 * x1 + x0 * x1).primitive() == (1, x0**2*x1 + x0*x1)
assert (4 * x0 + 2 * x0 * x1).primitive() == (2, x0 * x1 + 2 * x0)

if composite_characteristic:
# Factorisation not allowed over Z/nZ for n not prime.
# Flint would abort so we raise an exception instead:
Expand Down Expand Up @@ -3304,7 +3341,7 @@ def test_fmpz_mpoly_vec():
assert vec != mpoly_vec([x, x * y, ctx.from_dict({})], ctx)
assert vec != mpoly_vec([ctx.from_dict({})], ctx)
assert vec != mpoly_vec([ctx1.from_dict({})], ctx1)
assert vec.to_tuple() == mpoly_vec([ctx.from_dict({}), x * y, ctx.from_dict({})], ctx).to_tuple()
assert tuple(vec) == tuple(mpoly_vec([ctx.from_dict({}), x * y, ctx.from_dict({})], ctx))
assert raises(lambda: vec.__setitem__(None, 0), TypeError)
assert raises(lambda: vec.__setitem__(-1, 0), IndexError)
assert raises(lambda: vec.__setitem__(0, 0), TypeError)
Expand Down
Loading
Loading