Skip to content

Commit 6cdc1bc

Browse files
Merge pull request #216 from Jake-Moss/master
Add mpoly docs, functions for discriminant, resultant, term_content, and deflation, and run mod_mpoly doctests.
2 parents da51f7b + 4d22d95 commit 6cdc1bc

25 files changed

+1438
-186
lines changed

doc/source/fmpq_mpoly.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
**fmpq_mpoly** -- multivariate polynomials over the rational numbers
2+
===============================================================================
3+
4+
.. autoclass :: flint.fmpq_mpoly_ctx
5+
:members:
6+
:inherited-members:
7+
:undoc-members:
8+
9+
.. autoclass :: flint.fmpq_mpoly
10+
:members:
11+
:inherited-members:
12+
:undoc-members:
13+
14+
.. autoclass :: flint.fmpq_mpoly_vec
15+
:members:
16+
:inherited-members:
17+
:undoc-members:
18+

doc/source/fmpz_mod_mpoly.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
**fmpz_mod_mpoly** -- multivariate polynomials over the integers mod n
2+
===============================================================================
3+
4+
.. autoclass :: flint.fmpz_mod_mpoly_ctx
5+
:members:
6+
:inherited-members:
7+
:undoc-members:
8+
9+
.. autoclass :: flint.fmpz_mod_mpoly
10+
:members:
11+
:inherited-members:
12+
:undoc-members:
13+
14+
.. autoclass :: flint.fmpz_mod_mpoly_vec
15+
:members:
16+
:inherited-members:
17+
:undoc-members:
18+

doc/source/fmpz_mpoly.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
**fmpz_mpoly** -- multivariate polynomials over the integers
2+
===============================================================================
3+
4+
.. autoclass :: flint.fmpz_mpoly_ctx
5+
:members:
6+
:inherited-members:
7+
:undoc-members:
8+
9+
.. autoclass :: flint.fmpz_mpoly
10+
:members:
11+
:inherited-members:
12+
:undoc-members:
13+
14+
.. autoclass :: flint.fmpz_mpoly_vec
15+
:members:
16+
:inherited-members:
17+
:undoc-members:
18+

doc/source/index.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,9 +73,13 @@ Polynomial types
7373
:maxdepth: 1
7474

7575
fmpz_poly.rst
76+
fmpz_mpoly.rst
7677
fmpq_poly.rst
78+
fmpq_mpoly.rst
7779
nmod_poly.rst
80+
nmod_mpoly.rst
7881
fmpz_mod_poly.rst
82+
fmpz_mod_mpoly.rst
7983
fq_default_poly.rst
8084
arb_poly.rst
8185
acb_poly.rst

doc/source/nmod_mpoly.rst

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
**nmod_mpoly** -- multivariate polynomials over the integers mod n (word-size n)
2+
===============================================================================
3+
4+
.. autoclass :: flint.nmod_mpoly_ctx
5+
:members:
6+
:inherited-members:
7+
:undoc-members:
8+
9+
.. autoclass :: flint.nmod_mpoly
10+
:members:
11+
:inherited-members:
12+
:undoc-members:
13+
14+
.. autoclass :: flint.nmod_mpoly_vec
15+
:members:
16+
:inherited-members:
17+
:undoc-members:
18+

src/flint/flint_base/flint_base.pxd

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ cdef class flint_mpoly(flint_elem):
2323
cdef _mul_mpoly_(self, other)
2424

2525
cdef _divmod_mpoly_(self, other)
26+
cdef _truediv_scalar_(self, other)
27+
cdef _divexact_scalar_(self, other)
2628
cdef _floordiv_mpoly_(self, other)
2729
cdef _truediv_mpoly_(self, other)
2830
cdef _mod_mpoly_(self, other)

src/flint/flint_base/flint_base.pyx

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -229,7 +229,7 @@ cdef class flint_poly(flint_elem):
229229
integer root and *m* is the multiplicity of the root.
230230
231231
To compute complex roots of a polynomial, instead use
232-
the `.complex_roots()` method, which is available on
232+
the ``.complex_roots()`` method, which is available on
233233
certain polynomial rings.
234234
235235
>>> from flint import fmpz_poly
@@ -322,9 +322,9 @@ cdef class flint_mpoly_context(flint_elem):
322322
@staticmethod
323323
def create_variable_names(slong nvars, names: str):
324324
"""
325-
Create a tuple of variable names based on the comma separated `names` string.
325+
Create a tuple of variable names based on the comma separated ``names`` string.
326326
327-
If `names` contains a single value, and `nvars` > 1, then the variables are numbered, e.g.
327+
If ``names`` contains a single value, and ``nvars`` > 1, then the variables are numbered, e.g.
328328
329329
>>> flint_mpoly_context.create_variable_names(3, "x")
330330
('x0', 'x1', 'x2')
@@ -344,24 +344,24 @@ cdef class flint_mpoly_context(flint_elem):
344344
Create a key for the context cache via the number of variables, the ordering, and
345345
either a variable name string, or a tuple of variable names.
346346
"""
347-
# A type hint of `ordering: Ordering` results in the error "TypeError: an integer is required" if a Ordering
347+
# A type hint of ``ordering: Ordering`` results in the error "TypeError: an integer is required" if a Ordering
348348
# object is not provided. This is pretty obtuse so we check its type ourselves
349349
if not isinstance(ordering, Ordering):
350-
raise TypeError(f"`ordering` ('{ordering}') is not an instance of flint.Ordering")
350+
raise TypeError(f"'ordering' ('{ordering}') is not an instance of flint.Ordering")
351351

352352
if nametup is not None:
353353
key = nvars, ordering, nametup
354354
elif nametup is None and names is not None:
355355
key = nvars, ordering, cls.create_variable_names(nvars, names)
356356
else:
357-
raise ValueError("must provide either `names` or `nametup`")
357+
raise ValueError("must provide either 'names' or 'nametup'")
358358
return key
359359

360360
@classmethod
361361
def get_context(cls, *args, **kwargs):
362362
"""
363-
Retrieve a context via the number of variables, `nvars`, the ordering, `ordering`, and either a variable
364-
name string, `names`, or a tuple of variable names, `nametup`.
363+
Retrieve a context via the number of variables, ``nvars``, the ordering, ``ordering``, and either a variable
364+
name string, ``names``, or a tuple of variable names, ``nametup``.
365365
"""
366366
key = cls.create_context_key(*args, **kwargs)
367367

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

394+
def term(self, coeff = None, exp_vec = None):
395+
"""
396+
Create a monomial from a coefficient and exponent vector. ``coeff`` defaults
397+
to ``1``. ``exp_vec``` defaults to ``(0,) * self.nvars()```.
398+
399+
>>> from flint import fmpz_mpoly_ctx, Ordering
400+
>>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x')
401+
>>> ctx.term(coeff=5, exp_vec=(2, 3))
402+
5*x0^2*x1^3
403+
>>> ctx.term()
404+
1
405+
"""
406+
if coeff is None:
407+
coeff = 1
408+
if exp_vec is None:
409+
exp_vec = (0,) * self.nvars()
410+
return self.from_dict({tuple(exp_vec): coeff})
411+
394412

395413
cdef class flint_mpoly(flint_elem):
396414
"""
@@ -405,7 +423,7 @@ cdef class flint_mpoly(flint_elem):
405423

406424
def _division_check(self, other):
407425
if not other:
408-
raise ZeroDivisionError("nmod_mpoly division by zero")
426+
raise ZeroDivisionError(f"{self.__class__.__name__} division by zero")
409427

410428
cdef _add_scalar_(self, other):
411429
return NotImplemented
@@ -431,6 +449,12 @@ cdef class flint_mpoly(flint_elem):
431449
cdef _floordiv_mpoly_(self, other):
432450
return NotImplemented
433451

452+
cdef _truediv_scalar_(self, other):
453+
return NotImplemented
454+
455+
cdef _divexact_scalar_(self, other):
456+
return NotImplemented
457+
434458
cdef _truediv_mpoly_(self, other):
435459
return NotImplemented
436460

@@ -589,8 +613,12 @@ cdef class flint_mpoly(flint_elem):
589613
if other is NotImplemented:
590614
return NotImplemented
591615

592-
other = self.context().scalar_as_mpoly(other)
593616
self._division_check(other)
617+
res = self._truediv_scalar_(other)
618+
if res is not NotImplemented:
619+
return res
620+
621+
other = self.context().scalar_as_mpoly(other)
594622
return self._truediv_mpoly_(other)
595623

596624
def __rtruediv__(self, other):
@@ -725,7 +753,7 @@ cdef class flint_mpoly(flint_elem):
725753

726754
def __contains__(self, x):
727755
"""
728-
Returns True if `self` contains a term with exponent vector `x` and a non-zero coefficient.
756+
Returns True if ``self`` contains a term with exponent vector ``x`` and a non-zero coefficient.
729757
730758
>>> from flint import fmpq_mpoly_ctx, Ordering
731759
>>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x')
@@ -829,7 +857,7 @@ cdef class flint_mat(flint_elem):
829857

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

834862
if ordering == Ordering.lex:
835863
return ordering_t.ORD_LEX

src/flint/flintlib/types/nmod.pxd

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ cdef extern from "flint/nmod_types.h":
1313

1414
ctypedef nmod_mat_struct nmod_mat_t[1]
1515

16-
# XXX: Undocumented function:
17-
int nmod_mat_is_square(const nmod_mat_t mat)
18-
1916
# Macros:
2017
ulong nmod_mat_entry(nmod_mat_t mat, slong i, slong j)
2118

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
from flint.flintlib.types.nmod cimport nmod_mat_t
2+
3+
from flint.flintlib.types.flint cimport fmpz_struct
4+
from flint.flintlib.types.nmod cimport nmod_mpoly_ctx_t, nmod_mpoly_t
5+
6+
cdef extern from "flint/nmod_types.h":
7+
int nmod_mat_is_square(const nmod_mat_t mat)
8+
9+
cdef extern from "flint/nmod_mpoly.h":
10+
void nmod_mpoly_deflation(fmpz_struct * shift, fmpz_struct * stride, const nmod_mpoly_t A, const nmod_mpoly_ctx_t ctx)
11+
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)
12+
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)

src/flint/test/__main__.py

Lines changed: 14 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
import flint
1313
from flint.test.test_all import all_tests
14+
from flint.test.test_docstrings import find_doctests
1415

1516

1617
def run_tests(verbose=None):
@@ -48,54 +49,18 @@ def run_tests(verbose=None):
4849
return failed, total
4950

5051

51-
def run_doctests(verbose=None):
52-
"""Run the python-flint doctests"""
53-
# Here verbose=True shows a lot of output.
54-
modules = [flint.pyflint,
55-
flint.flint_base.flint_base,
56-
flint.flint_base.flint_context,
57-
flint.types.fmpz,
58-
flint.types.fmpz_poly,
59-
flint.types.fmpz_mat,
60-
flint.types.fmpz_mpoly,
61-
flint.types.fmpz_series,
62-
flint.types.fmpz_mod,
63-
flint.types.fmpz_mod_poly,
64-
flint.types.fmpz_mod_mat,
65-
flint.types.fmpq,
66-
flint.types.fmpq_poly,
67-
flint.types.fmpq_mat,
68-
flint.types.fmpq_mpoly,
69-
flint.types.fmpq_series,
70-
flint.types.nmod,
71-
flint.types.nmod_poly,
72-
flint.types.nmod_mat,
73-
flint.types.nmod_series,
74-
flint.types.fq_default,
75-
flint.types.fq_default_poly,
76-
flint.types.arf,
77-
flint.types.arb,
78-
flint.types.arb_poly,
79-
flint.types.arb_mat,
80-
flint.types.arb_series,
81-
flint.types.acb,
82-
flint.types.acb_poly,
83-
flint.types.acb_mat,
84-
flint.types.acb_series,
85-
flint.types.dirichlet,
86-
flint.functions.showgood]
87-
try:
88-
from flint.types import acb_theta
89-
modules.append(acb_theta)
90-
except ImportError:
91-
pass
92-
93-
results = []
94-
for x in modules:
95-
if verbose:
96-
print(f" {x.__name__}")
97-
results.append(doctest.testmod(x))
98-
return tuple(sum(res) for res in zip(*results))
52+
def run_doctests(tests, verbose=False):
53+
runner = doctest.DocTestRunner()
54+
for module, test_set in tests:
55+
if test_set:
56+
print(f"{module}...", end="" if not verbose else "\n", flush=True)
57+
for test in test_set:
58+
if verbose:
59+
print("\tTesting:", test.name)
60+
runner.run(test)
61+
print("OK")
62+
63+
return runner.summarize()
9964

10065

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

10974
if doctests:
11075
print("Running doctests...")
111-
d_failed, d_total = run_doctests(verbose=verbose)
76+
d_failed, d_total = run_doctests(find_doctests(flint), verbose=verbose)
11277

11378
if tests:
11479
if t_failed:

0 commit comments

Comments
 (0)