diff --git a/src/flint/flint_base/flint_base.pxd b/src/flint/flint_base/flint_base.pxd index 1f1d2371..daf22a96 100644 --- a/src/flint/flint_base/flint_base.pxd +++ b/src/flint/flint_base/flint_base.pxd @@ -13,6 +13,9 @@ cdef class flint_mpoly_context(flint_elem): cdef public object py_names cdef const char ** c_names +cdef class flint_mod_mpoly_context(flint_mpoly_context): + cdef readonly bint __prime_modulus + cdef class flint_mpoly(flint_elem): cdef _add_scalar_(self, other) cdef _sub_scalar_(self, other) @@ -54,8 +57,5 @@ cdef class flint_mat(flint_elem): cdef class flint_series(flint_elem): pass -cpdef enum Ordering: - lex, deglex, degrevlex - -cdef ordering_t ordering_py_to_c(ordering: Ordering) +cdef ordering_t ordering_py_to_c(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 18ac3e0c..cfe57fc9 100644 --- a/src/flint/flint_base/flint_base.pyx +++ b/src/flint/flint_base/flint_base.pyx @@ -2,20 +2,19 @@ from flint.flintlib.types.flint cimport ( FLINT_BITS as _FLINT_BITS, FLINT_VERSION as _FLINT_VERSION, __FLINT_RELEASE as _FLINT_RELEASE, - slong ) from flint.utils.flint_exceptions import DomainError from flint.flintlib.types.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 -from typing import Optional +from collections.abc import Iterable from flint.utils.flint_exceptions import IncompatibleContextError from flint.types.fmpz cimport fmpz, any_as_fmpz +import enum FLINT_BITS = _FLINT_BITS FLINT_VERSION = _FLINT_VERSION.decode("ascii") @@ -265,6 +264,31 @@ cdef class flint_poly(flint_elem): raise NotImplementedError("Complex roots are not supported for this polynomial") +class Ordering(enum.Enum): + lex = "lex" + deglex = "deglex" + degrevlex = "degrevlex" + + +cdef ordering_t ordering_py_to_c(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) + + cdef class flint_mpoly_context(flint_elem): """ Base class for multivariate ring contexts @@ -272,15 +296,31 @@ cdef class flint_mpoly_context(flint_elem): _ctx_cache = None - 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 number of variable names") + def __init__(self, *_, **_2): + raise RuntimeError( + f"{self.__class__.__name__} should not be constructed directly. " + f"Use '{self.__class__.__name__}.get' instead." + ) + + @classmethod + def _new_(_, flint_mpoly_context self, names: Iterable[str]): + """ + Constructor for all mpoly context types. This method is not intended for + user-face use. See ``get`` instead. + + Construction via ``__init__`` is disabled to prevent the accidental creation of + new mpoly contexts. By ensuring each context is unique they can be compared via + pointer comparisons. + + Each concrete subclass should maintain their own context cache in + ``_ctx_cache``, and the ``get`` method should insert newly created contexts into + the cache. + """ 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): + self.c_names = libc.stdlib.malloc(len(names) * sizeof(const char *)) + for i in range(len(names)): self.c_names[i] = self.py_names[i] + return self def __dealloc__(self): libc.stdlib.free(self.c_names) @@ -292,18 +332,18 @@ cdef class flint_mpoly_context(flint_elem): def __repr__(self): return f"{self.__class__.__name__}({self.nvars()}, '{repr(self.ordering())}', {self.names()})" - def name(self, long i): + def name(self, i: int): if not 0 <= i < len(self.py_names): raise IndexError("variable name index out of range") return self.py_names[i].decode("ascii") - def names(self): + def names(self) -> tuple[str]: 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())) - def variable_to_index(self, var: Union[int, str]): + def variable_to_index(self, var: Union[int, str]) -> int: """Convert a variable name string or possible index to its index in the context.""" if isinstance(var, str): try: @@ -320,69 +360,71 @@ cdef class flint_mpoly_context(flint_elem): return i @staticmethod - def create_variable_names(slong nvars, names: str): + def create_variable_names(names: str | Iterable[str | tuple[str, int]]) -> tuple[str]: """ - Create a tuple of variable names based on the comma separated ``names`` string. + Create a tuple of variable names based off either ``str``, ``Iterable[str]``, + ``tuple[str, int]``, or ``Iterable[tuple[str, int]]``. - If ``names`` contains a single value, and ``nvars`` > 1, then the variables are numbered, e.g. - - >>> flint_mpoly_context.create_variable_names(3, "x") + >>> flint_mpoly_context.create_variable_names('x') + ('x',) + >>> flint_mpoly_context.create_variable_names(('x', 3)) ('x0', 'x1', 'x2') - + >>> flint_mpoly_context.create_variable_names([('x', 3), 'y']) + ('x0', 'x1', 'x2', 'y') """ - nametup = tuple(name.strip() for name in names.split(',')) - if len(nametup) != nvars: - if len(nametup) == 1: - nametup = tuple(nametup[0] + str(i) for i in range(nvars)) + res: list[str] = [] + + # To avoid having to pass a nested tuple we allow a tuple[str, int] + if len(names) == 2 and isinstance(names[0], str) and isinstance(names[1], int): + names = (names,) + + for name in names: + if isinstance(name, str): + res.append(name) else: - raise ValueError("number of variables does not equal number of names") - return nametup + base, num = name + if num < 0: + raise ValueError("cannot create a negative number of variables") + res.extend(base + str(i) for i in range(num)) + + return tuple(res) @classmethod - def create_context_key(cls, slong nvars=1, ordering=Ordering.lex, names: Optional[str] = "x", nametup: Optional[tuple] = None): + def create_context_key( + cls, + names: str | Iterable[str | tuple[str, int]], + ordering: Ordering | str = Ordering.lex + ): """ - 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. + Create a key for the context cache via the variable names and the 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") - - 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'") - return key + return cls.create_variable_names(names), Ordering(ordering) if not isinstance(ordering, Ordering) else ordering @classmethod - def get_context(cls, *args, **kwargs): + def get(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 or create a context via generator names, ``names`` and the ordering, ``ordering``. + + See ``create_variable_names`` for naming schemes. """ key = cls.create_context_key(*args, **kwargs) ctx = cls._ctx_cache.get(key) if ctx is None: - ctx = cls._ctx_cache.setdefault(key, cls(*key)) + ctx = cls._ctx_cache.setdefault(key, cls._new_(*key)) return ctx @classmethod def from_context(cls, ctx: flint_mpoly_context): - return cls.get_context( - nvars=ctx.nvars(), + return cls.get( ordering=ctx.ordering(), - names=None, - nametup=ctx.names() + names=ctx.names(), ) - def any_as_scalar(self, other): + def _any_as_scalar(self, other): raise NotImplementedError("abstract method") - def scalar_as_mpoly(self, other): + def _scalar_as_mpoly(self, other): raise NotImplementedError("abstract method") def compatible_context_check(self, other): @@ -396,8 +438,8 @@ cdef class flint_mpoly_context(flint_elem): 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') + >>> from flint import fmpz_mpoly_ctx + >>> ctx = fmpz_mpoly_ctx.get(('x', 2), 'lex') >>> ctx.term(coeff=5, exp_vec=(2, 3)) 5*x0^2*x1^3 >>> ctx.term() @@ -409,6 +451,48 @@ cdef class flint_mpoly_context(flint_elem): exp_vec = (0,) * self.nvars() return self.from_dict({tuple(exp_vec): coeff}) +cdef class flint_mod_mpoly_context(flint_mpoly_context): + @classmethod + def _new_(_, flint_mod_mpoly_context self, names, prime_modulus): + super()._new_(self, names) + self.__prime_modulus = prime_modulus + + return self + + @classmethod + def create_context_key( + cls, + names: Iterable[str | tuple[str, int]], + modulus, + ordering: Ordering | str = Ordering.lex + ): + """ + Create a key for the context cache via the variable names, modulus, and the ordering. + """ + return *super().create_context_key(names, ordering), modulus + + @classmethod + def from_context(cls, ctx: flint_mod_mpoly_context): + return cls.get( + names=ctx.names(), + modulus=ctx.modulus(), + ordering=ctx.ordering(), + ) + + def is_prime(self): + """ + Return whether the modulus is prime + + >>> from flint import fmpz_mod_mpoly_ctx + >>> ctx = fmpz_mod_mpoly_ctx.get(('z',), 2**127, 'degrevlex') + >>> ctx.is_prime() + False + >>> ctx = fmpz_mod_mpoly_ctx.get(('z',), 2**127 - 1, 'degrevlex') + >>> ctx.is_prime() + True + """ + return self.__prime_modulus + cdef class flint_mpoly(flint_elem): """ @@ -505,7 +589,7 @@ cdef class flint_mpoly(flint_elem): self.context().compatible_context_check(other.context()) return self._add_mpoly_(other) - other = self.context().any_as_scalar(other) + other = self.context()._any_as_scalar(other) if other is NotImplemented: return NotImplemented @@ -516,7 +600,7 @@ cdef class flint_mpoly(flint_elem): self.context().compatible_context_check(other.context()) return self._add_mpoly_(other) - other = self.context().any_as_scalar(other) + other = self.context()._any_as_scalar(other) if other is NotImplemented: return NotImplemented @@ -527,7 +611,7 @@ cdef class flint_mpoly(flint_elem): self.context().compatible_context_check(other.context()) return self._sub_mpoly_(other) - other = self.context().any_as_scalar(other) + other = self.context()._any_as_scalar(other) if other is NotImplemented: return NotImplemented @@ -538,7 +622,7 @@ cdef class flint_mpoly(flint_elem): self.context().compatible_context_check(other.context()) return self._rsub_mpoly_(other) - other = self.context().any_as_scalar(other) + other = self.context()._any_as_scalar(other) if other is NotImplemented: return NotImplemented @@ -549,7 +633,7 @@ cdef class flint_mpoly(flint_elem): self.context().compatible_context_check(other.context()) return self._mul_mpoly_(other) - other = self.context().any_as_scalar(other) + other = self.context()._any_as_scalar(other) if other is NotImplemented: return NotImplemented @@ -560,7 +644,7 @@ cdef class flint_mpoly(flint_elem): self.context().compatible_context_check(other.context()) return self._mul_mpoly_(other) - other = self.context().any_as_scalar(other) + other = self.context()._any_as_scalar(other) if other is NotImplemented: return NotImplemented @@ -586,20 +670,20 @@ cdef class flint_mpoly(flint_elem): self._division_check(other) return self._divmod_mpoly_(other) - other = self.context().any_as_scalar(other) + other = self.context()._any_as_scalar(other) if other is NotImplemented: return NotImplemented - other = self.context().scalar_as_mpoly(other) + other = self.context()._scalar_as_mpoly(other) self._division_check(other) return self._divmod_mpoly_(other) def __rdivmod__(self, other): - other = self.context().any_as_scalar(other) + other = self.context()._any_as_scalar(other) if other is NotImplemented: return NotImplemented - other = self.context().scalar_as_mpoly(other) + other = self.context()._scalar_as_mpoly(other) other._division_check(self) return self._rdivmod_mpoly_(other) @@ -609,7 +693,7 @@ cdef class flint_mpoly(flint_elem): self._division_check(other) return self._truediv_mpoly_(other) - other = self.context().any_as_scalar(other) + other = self.context()._any_as_scalar(other) if other is NotImplemented: return NotImplemented @@ -618,15 +702,15 @@ cdef class flint_mpoly(flint_elem): if res is not NotImplemented: return res - other = self.context().scalar_as_mpoly(other) + other = self.context()._scalar_as_mpoly(other) return self._truediv_mpoly_(other) def __rtruediv__(self, other): - other = self.context().any_as_scalar(other) + other = self.context()._any_as_scalar(other) if other is NotImplemented: return NotImplemented - other = self.context().scalar_as_mpoly(other) + other = self.context()._scalar_as_mpoly(other) other._division_check(self) return self._rtruediv_mpoly_(other) @@ -636,20 +720,20 @@ cdef class flint_mpoly(flint_elem): self._division_check(other) return self._floordiv_mpoly_(other) - other = self.context().any_as_scalar(other) + other = self.context()._any_as_scalar(other) if other is NotImplemented: return NotImplemented - other = self.context().scalar_as_mpoly(other) + other = self.context()._scalar_as_mpoly(other) self._division_check(other) return self._floordiv_mpoly_(other) def __rfloordiv__(self, other): - other = self.context().any_as_scalar(other) + other = self.context()._any_as_scalar(other) if other is NotImplemented: return NotImplemented - other = self.context().scalar_as_mpoly(other) + other = self.context()._scalar_as_mpoly(other) other._division_check(self) return self._rfloordiv_mpoly_(other) @@ -659,20 +743,20 @@ cdef class flint_mpoly(flint_elem): self._division_check(other) return self._mod_mpoly_(other) - other = self.context().any_as_scalar(other) + other = self.context()._any_as_scalar(other) if other is NotImplemented: return NotImplemented - other = self.context().scalar_as_mpoly(other) + other = self.context()._scalar_as_mpoly(other) self._division_check(other) return self._mod_mpoly_(other) def __rmod__(self, other): - other = self.context().any_as_scalar(other) + other = self.context()._any_as_scalar(other) if other is NotImplemented: return NotImplemented - other = self.context().scalar_as_mpoly(other) + other = self.context()._scalar_as_mpoly(other) other._division_check(self) return self._rmod_mpoly_(other) @@ -680,8 +764,8 @@ cdef class flint_mpoly(flint_elem): """ In-place addition, mutates self. - >>> from flint import Ordering, fmpz_mpoly_ctx - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> from flint import fmpz_mpoly_ctx + >>> ctx = fmpz_mpoly_ctx.get(('x', 2), 'lex') >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) >>> f 4*x0*x1 + 2*x0 + 3*x1 @@ -695,7 +779,7 @@ cdef class flint_mpoly(flint_elem): self._iadd_mpoly_(other) return - other_scalar = self.context().any_as_scalar(other) + other_scalar = self.context()._any_as_scalar(other) if other_scalar is NotImplemented: raise NotImplementedError(f"cannot add {type(self)} and {type(other)}") @@ -705,8 +789,8 @@ cdef class flint_mpoly(flint_elem): """ In-place subtraction, mutates self. - >>> from flint import Ordering, fmpz_mpoly_ctx - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> from flint import fmpz_mpoly_ctx + >>> ctx = fmpz_mpoly_ctx.get(('x', 2), 'lex') >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) >>> f 4*x0*x1 + 2*x0 + 3*x1 @@ -720,7 +804,7 @@ cdef class flint_mpoly(flint_elem): self._isub_mpoly_(other) return - other_scalar = self.context().any_as_scalar(other) + other_scalar = self.context()._any_as_scalar(other) if other_scalar is NotImplemented: raise NotImplementedError(f"cannot subtract {type(self)} and {type(other)}") @@ -730,8 +814,8 @@ cdef class flint_mpoly(flint_elem): """ In-place multiplication, mutates self. - >>> from flint import Ordering, fmpz_mpoly_ctx - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> from flint import fmpz_mpoly_ctx + >>> ctx = fmpz_mpoly_ctx.get(('x', 2), 'lex') >>> f = ctx.from_dict({(1, 0): 2, (0, 1): 3, (1, 1): 4}) >>> f 4*x0*x1 + 2*x0 + 3*x1 @@ -745,7 +829,7 @@ cdef class flint_mpoly(flint_elem): self._imul_mpoly_(other) return - other_scalar = self.context().any_as_scalar(other) + other_scalar = self.context()._any_as_scalar(other) if other_scalar is NotImplemented: raise NotImplementedError(f"cannot multiply {type(self)} and {type(other)}") @@ -755,8 +839,8 @@ 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 fmpq_mpoly_ctx, Ordering - >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> from flint import fmpq_mpoly_ctx + >>> ctx = fmpq_mpoly_ctx.get(('x', 2), 'lex') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> (1, 1) in p True @@ -776,8 +860,8 @@ cdef class flint_mpoly(flint_elem): """ 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') + >>> from flint import fmpq_mpoly_ctx + >>> ctx = fmpq_mpoly_ctx.get(('x', 2), 'lex') >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) >>> list(f.terms()) [((1, 1), 4), ((1, 0), 2), ((0, 1), 3), ((0, 0), 1)] @@ -853,26 +937,3 @@ cdef class flint_mat(flint_elem): # supports mpmath conversions tolist = table - - -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 68102541..00f7006c 100644 --- a/src/flint/test/test_all.py +++ b/src/flint/test/test_all.py @@ -2104,7 +2104,7 @@ def test_fmpz_mod_poly(): assert pow(f, 2**60, g) == pow(pow(f, 2**30, g), 2**30, g) assert pow(R_gen, 2**60, g) == pow(pow(R_gen, 2**30, g), 2**30, g) - # Check other typechecks for pow_mod + # Check other typechecks for pow_mod assert raises(lambda: pow(f, -2, g), ValueError) assert raises(lambda: pow(f, 1, "A"), TypeError) assert raises(lambda: pow(f, "A", g), TypeError) @@ -2756,7 +2756,7 @@ def setbad(obj, i, val): assert P([1, 1]) ** 2 == P([1, 2, 1]) assert raises(lambda: P([1, 1]) ** -1, ValueError) assert raises(lambda: P([1, 1]) ** None, TypeError) - + # XXX: Not sure what this should do in general: p = P([1, 1]) mod = P([1, 1]) @@ -2816,32 +2816,32 @@ def setbad(obj, i, val): def _all_mpolys(): return [ - (flint.fmpz_mpoly, flint.fmpz_mpoly_ctx.get_context, flint.fmpz, False, flint.fmpz(0)), - (flint.fmpq_mpoly, flint.fmpq_mpoly_ctx.get_context, flint.fmpq, True, flint.fmpz(0)), + (flint.fmpz_mpoly, flint.fmpz_mpoly_ctx.get, flint.fmpz, False, flint.fmpz(0)), + (flint.fmpq_mpoly, flint.fmpq_mpoly_ctx.get, flint.fmpq, True, flint.fmpz(0)), ( flint.fmpz_mod_mpoly, - lambda *args, **kwargs: flint.fmpz_mod_mpoly_ctx.get_context(*args, **kwargs, modulus=101), + lambda *args, **kwargs: flint.fmpz_mod_mpoly_ctx.get(*args, **kwargs, modulus=101), lambda x: flint.fmpz_mod(x, flint.fmpz_mod_ctx(101)), True, flint.fmpz(101), ), ( flint.fmpz_mod_mpoly, - lambda *args, **kwargs: flint.fmpz_mod_mpoly_ctx.get_context(*args, **kwargs, modulus=100), + lambda *args, **kwargs: flint.fmpz_mod_mpoly_ctx.get(*args, **kwargs, modulus=100), lambda x: flint.fmpz_mod(x, flint.fmpz_mod_ctx(100)), False, flint.fmpz(100), ), ( flint.nmod_mpoly, - lambda *args, **kwargs: flint.nmod_mpoly_ctx.get_context(*args, **kwargs, modulus=101), + lambda *args, **kwargs: flint.nmod_mpoly_ctx.get(*args, **kwargs, modulus=101), lambda x: flint.nmod(x, 101), True, flint.fmpz(101), ), ( flint.nmod_mpoly, - lambda *args, **kwargs: flint.nmod_mpoly_ctx.get_context(*args, **kwargs, modulus=100), + lambda *args, **kwargs: flint.nmod_mpoly_ctx.get(*args, **kwargs, modulus=100), lambda x: flint.nmod(x, 100), False, flint.fmpz(100), @@ -2859,14 +2859,11 @@ def test_mpolys(): # division is exact or not. composite_characteristic = characteristic != 0 and not characteristic.is_prime() - ctx = get_context(nvars=2) + ctx = get_context(("x", 2)) - assert raises(lambda: get_context(nvars=2, ordering="bad"), TypeError) - assert raises(lambda: get_context(nvars=-1), ValueError) - if ctx.__class__ is flint.fmpz_mod_mpoly_ctx or ctx.__class__ is flint.nmod_mpoly_ctx: - assert raises(lambda: ctx.__class__(-1, flint.Ordering.lex, [], 4), ValueError) - else: - assert raises(lambda: ctx.__class__(-1, flint.Ordering.lex, []), ValueError) + assert raises(lambda : ctx.__class__("x", flint.Ordering.lex), RuntimeError) + assert raises(lambda: get_context(("x", 2), ordering="bad"), ValueError) + assert raises(lambda: get_context(("x", -1)), 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) @@ -2875,7 +2872,7 @@ def test_mpolys(): assert raises(lambda: ctx.gen(-1), IndexError) assert raises(lambda: ctx.gen(10), IndexError) - assert raises(lambda: P(val=get_context(nvars=1).constant(0), ctx=ctx), IncompatibleContextError) + assert raises(lambda: P(val=get_context(("x",)).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) @@ -2894,10 +2891,10 @@ def quick_poly(): assert ctx.nvars() == 2 assert ctx.ordering() == flint.Ordering.lex - ctx1 = get_context(4) + ctx1 = get_context(("x", 4)) assert [ctx1.name(i) for i in range(4)] == ['x0', 'x1', 'x2', 'x3'] for order in list(flint.Ordering): - ctx1 = get_context(4, order) + ctx1 = get_context(("x", 4), ordering=order) assert ctx1.ordering() == order assert ctx.constant(1) == mpoly({(0, 0): 1}) == P(1, ctx=ctx) @@ -2948,7 +2945,7 @@ def quick_poly(): 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) + ctx_z = flint.fmpz_mpoly_ctx.get((("x", 2),)) assert quick_poly() == P(ctx_z.from_dict({(0, 0): 1, (0, 1): 2, (1, 0): 3, (2, 2): 4})) assert P(ctx_z.from_dict({(0, 0): 1}), ctx=ctx) == P({(0, 0): 1}, ctx=ctx) @@ -2997,8 +2994,8 @@ def quick_poly(): 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}})" + 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.monomial(0) == (2, 2) @@ -3039,7 +3036,7 @@ 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 = get_context(0) + no_gens_ctx = get_context(tuple()) 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) @@ -3318,8 +3315,8 @@ def test_fmpz_mpoly_vec(): for context, mpoly_vec in _all_mpoly_vecs(): has_groebner_functions = mpoly_vec is flint.fmpz_mpoly_vec - ctx = context.get_context(nvars=2) - ctx1 = context.get_context(nvars=4) + ctx = context.get((("x", 2),)) + ctx1 = context.get((("x", 4),)) x, y = ctx.gens() vec = mpoly_vec(3, ctx) @@ -3348,7 +3345,7 @@ def test_fmpz_mpoly_vec(): assert raises(lambda: vec.__setitem__(0, ctx1.from_dict({})), IncompatibleContextError) if has_groebner_functions: - ctx = context.get_context(3, flint.Ordering.lex, nametup=('x', 'y', 'z')) + ctx = context.get(("x", "y", "z")) # Examples here cannibalised from # https://en.wikipedia.org/wiki/Gr%C3%B6bner_basis#Example_and_counterexample @@ -3411,8 +3408,8 @@ def _all_polys_mpolys(): )) yield P, S, [x, y], is_field, characteristic - for P, get_context, S, is_field, characteristic in _all_mpolys(): - ctx = get_context(2, flint.Ordering.lex, nametup=("x", "y")) + for P, get, S, is_field, characteristic in _all_mpolys(): + ctx = get(("x", "y")) x, y = ctx.gens() assert isinstance(x, ( flint.fmpz_mpoly, @@ -4163,7 +4160,7 @@ def test_fq_default(): # p must be prime assert raises(lambda: flint.fq_default_ctx(10), ValueError) - + # degree must be positive assert raises(lambda: flint.fq_default_ctx(11, -1), ValueError) diff --git a/src/flint/types/fmpq_mpoly.pyx b/src/flint/types/fmpq_mpoly.pyx index fa4ae5e0..0e86a3c8 100644 --- a/src/flint/types/fmpq_mpoly.pyx +++ b/src/flint/types/fmpq_mpoly.pyx @@ -89,20 +89,23 @@ 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. + :param ordering: The term order for the ring. - Do not construct one of these directly, use ``fmpz_mpoly_ctx.get_context``. + Do not construct one of these directly, use ``fmpz_mpoly_ctx.get``. """ _ctx_cache = _fmpq_mpoly_ctx_cache - def __init__(self, slong nvars, ordering, names): - fmpq_mpoly_ctx_init(self.val, nvars, ordering_py_to_c(ordering)) - super().__init__(nvars, names) + @classmethod + def _new_(cls, names, ordering): + cdef fmpq_mpoly_ctx self = cls.__new__(cls) + super()._new_(self, names) + fmpq_mpoly_ctx_init(self.val, len(names), ordering_py_to_c(ordering)) - def any_as_scalar(self, other): + return self + + def _any_as_scalar(self, other): if isinstance(other, int): return any_as_fmpq(other) elif typecheck(other, fmpz): @@ -114,16 +117,15 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): else: return NotImplemented - def scalar_as_mpoly(self, other: fmpq): - # non-fmpq scalars should first be converted via self.any_as_scalar + def _scalar_as_mpoly(self, other: fmpq): + # non-fmpq scalars should first be converted via self._any_as_scalar return self.constant(other) def nvars(self): """ Return the number of variables in the context - >>> from flint import Ordering - >>> ctx = fmpq_mpoly_ctx.get_context(4, Ordering.lex, 'x') + >>> ctx = fmpq_mpoly_ctx.get(('x', 4), 'lex') >>> ctx.nvars() 4 """ @@ -133,10 +135,9 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): """ Return the term order of the context object. - >>> from flint import Ordering - >>> ctx = fmpq_mpoly_ctx.get_context(4, Ordering.deglex, 'w') + >>> ctx = fmpq_mpoly_ctx.get(('w', 4), 'deglex') >>> ctx.ordering() - + """ return ordering_c_to_py(self.val.zctx.minfo.ord) @@ -144,8 +145,7 @@ cdef class fmpq_mpoly_ctx(flint_mpoly_context): """ Return the ``i`` th generator of the polynomial ring - >>> from flint import Ordering - >>> ctx = fmpq_mpoly_ctx.get_context(3, Ordering.degrevlex, 'z') + >>> ctx = fmpq_mpoly_ctx.get(('z', 3), 'degrevlex') >>> ctx.gen(1) z1 """ @@ -175,8 +175,7 @@ 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. - >>> from flint import Ordering - >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x,y') + >>> ctx = fmpq_mpoly_ctx.get(('x', 'y'), 'lex') >>> ctx.from_dict({(1,0):2, (1,1):3, (0,1):1}) 3*x*y + 2*x + y """ @@ -306,8 +305,7 @@ cdef class fmpq_mpoly(flint_mpoly): 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') + >>> ctx = fmpq_mpoly_ctx.get(('x', 2), 'lex') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p[1, 1] 3 @@ -332,8 +330,7 @@ cdef class fmpq_mpoly(flint_mpoly): 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') + >>> ctx = fmpq_mpoly_ctx.get(('x', 2), 'lex') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p[1, 1] = 20 >>> p @@ -511,8 +508,7 @@ cdef class fmpq_mpoly(flint_mpoly): """ 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') + >>> ctx = fmpq_mpoly_ctx.get(('x', 2), 'lex') >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) >>> f.monoms() [(1, 1), (1, 0), (0, 1), (0, 0)] @@ -533,8 +529,7 @@ cdef class fmpq_mpoly(flint_mpoly): """ Return the coefficients of each term as a fmpq. - >>> from flint import Ordering - >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpq_mpoly_ctx.get(('x', 2), 'lex') >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) >>> f.coeffs() [4, 2, 3, 1] @@ -556,8 +551,7 @@ cdef class fmpq_mpoly(flint_mpoly): # """ # 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') + # >>> ctx = fmpq_mpoly_ctx.get(('x', 2), 'lex') # >>> 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] @@ -580,8 +574,7 @@ cdef class fmpq_mpoly(flint_mpoly): Partial evaluate this polynomial with select constants. Keys must be generator names or generator indices, all values must be fmpq. - >>> from flint import Ordering - >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpq_mpoly_ctx.get(('x', 2), 'lex') >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) >>> f.subs({"x1": 0}) 2*x0 + 1 @@ -610,9 +603,8 @@ cdef class fmpq_mpoly(flint_mpoly): 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') + >>> ctx = fmpq_mpoly_ctx.get(('x',), 'lex') + >>> ctx1 = fmpq_mpoly_ctx.get(('y', 2), 'lex') >>> f = ctx.from_dict({(2,): 1}) >>> g = ctx1.from_dict({(1, 0): 1, (0, 1): 1}) >>> f @@ -660,8 +652,7 @@ cdef class fmpq_mpoly(flint_mpoly): """ Return the context object for this polynomials. - >>> from flint import Ordering - >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpq_mpoly_ctx.get(('x', 2), 'lex') >>> p = ctx.from_dict({(0, 1): 2}) >>> ctx is p.context() True @@ -672,8 +663,7 @@ cdef class fmpq_mpoly(flint_mpoly): """ Return the coefficient at index ``i``. - >>> from flint import Ordering - >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpq_mpoly_ctx.get(('x', 2), 'lex') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p.coefficient(1) 2 @@ -690,8 +680,7 @@ cdef class fmpq_mpoly(flint_mpoly): """ Return the exponent vector at index ``i`` as a tuple. - >>> from flint import Ordering - >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpq_mpoly_ctx.get(('x', 2), 'lex') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p.monomial(1) (0, 1) @@ -709,8 +698,7 @@ cdef class fmpq_mpoly(flint_mpoly): """ Return a dictionary of variable name to degree. - >>> from flint import Ordering - >>> ctx = fmpq_mpoly_ctx.get_context(4, Ordering.lex, 'x') + >>> ctx = fmpq_mpoly_ctx.get(('x', 4), 'lex') >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) >>> p.degrees() (1, 2, 3, 0) @@ -726,8 +714,7 @@ cdef class fmpq_mpoly(flint_mpoly): """ Return the total degree. - >>> from flint import Ordering - >>> ctx = fmpq_mpoly_ctx.get_context(4, Ordering.lex, 'x') + >>> ctx = fmpq_mpoly_ctx.get(('x', 4), 'lex') >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) >>> p.total_degree() 3 @@ -740,8 +727,7 @@ cdef class fmpq_mpoly(flint_mpoly): """ Leading coefficient in the monomial ordering. - >>> from flint import Ordering - >>> ctx = fmpq_mpoly_ctx(2, Ordering.lex, ['x', 'y']) + >>> ctx = fmpq_mpoly_ctx.get(('x', 'y'), 'lex') >>> x, y = ctx.gens() >>> p = 2*x*y + 3*x + 4*y**2 + 5 >>> p @@ -770,8 +756,7 @@ cdef class fmpq_mpoly(flint_mpoly): """ Return the gcd of self and other. - >>> from flint import Ordering - >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpq_mpoly_ctx.get(('x', 2), 'lex') >>> f = ctx.from_dict({(1, 1): 4, (0, 0): 1}) >>> g = ctx.from_dict({(0, 1): 2, (1, 0): 2}) >>> (f * g).gcd(f) @@ -792,8 +777,7 @@ cdef class fmpq_mpoly(flint_mpoly): Return the GCD of the terms of ``self``. If ``self`` is zero, then the result will be zero, otherwise it will be a monomial with positive coefficient. - >>> from flint import Ordering - >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpq_mpoly_ctx.get(('x', 2), 'lex') >>> x0, x1 = ctx.gens() >>> f = 3 * x0**2 * x1 + 6 * x0 * x1 >>> f.term_content() @@ -807,8 +791,7 @@ cdef class fmpq_mpoly(flint_mpoly): """ Return the resultant of ``self`` and ``other`` with respect to variable ``var``. - >>> from flint import Ordering - >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpq_mpoly_ctx.get(('x', 2), 'lex') >>> x0, x1 = ctx.gens() >>> f = x0**2 * x1 + x0 * x1 >>> g = x0 + x1 @@ -834,8 +817,7 @@ cdef class fmpq_mpoly(flint_mpoly): """ Return the discriminant of ``self`` with respect to variable ``var``. - >>> from flint import Ordering - >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpq_mpoly_ctx.get(('x', 2), 'lex') >>> x0, x1 = ctx.gens() >>> f = (x0 + x1)**2 + 1 >>> f.discriminant('x1') @@ -856,8 +838,7 @@ cdef class fmpq_mpoly(flint_mpoly): """ Return the square root of self. - >>> from flint import Ordering - >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpq_mpoly_ctx.get(('x', 2), 'lex') >>> f = ctx.from_dict({(1, 1): 4, (0, 0): 1}) >>> (f * f).sqrt() 4*x0*x1 + 1 @@ -876,9 +857,8 @@ 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, Ordering.lex, 'x,y,z') + >>> ctx = fmpq_mpoly_ctx.get(('x', 'y', 'z'), 'lex') >>> p1 = Zm("2*x + 4", ctx) >>> p2 = Zm("3*x*z + 3*x + 3*z + 3", ctx) >>> (p1 * p2).factor() @@ -915,9 +895,8 @@ 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, Ordering.lex, 'x,y,z') + >>> ctx = fmpq_mpoly_ctx.get(('x', 'y', 'z'), 'lex') >>> p1 = Zm("2*x + 4", ctx) >>> p2 = Zm("3*x*y + 3*x + 3*y + 3", ctx) >>> (p1 * p2).factor_squarefree() @@ -954,8 +933,7 @@ cdef class fmpq_mpoly(flint_mpoly): 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') + >>> ctx = fmpq_mpoly_ctx.get(('x', 2), 'lex') >>> p = ctx.from_dict({(0, 3): 2, (2, 1): 3}) >>> p 3*x0^2*x1 + 2*x1^3 @@ -979,8 +957,7 @@ cdef class fmpq_mpoly(flint_mpoly): 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') + >>> ctx = fmpq_mpoly_ctx.get(('x', 2), 'lex') >>> p = ctx.from_dict({(0, 3): 2, (2, 1): 3}) >>> p 3*x0^2*x1 + 2*x1^3 @@ -1004,8 +981,7 @@ cdef class fmpq_mpoly(flint_mpoly): Compute the inflation of ``self`` for a provided ``N``, that is return ``q`` such that ``q(X) = p(X^N)``. - >>> from flint import Ordering - >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) + >>> ctx = fmpq_mpoly_ctx.get(('x', 'y'), 'lex') >>> x, y = ctx.gens() >>> f = x + y + 1 >>> f.inflate([2, 3]) @@ -1033,8 +1009,7 @@ cdef class fmpq_mpoly(flint_mpoly): Compute the deflation of ``self`` for a provided ``N``, that is return ``q`` such that ``q(X) = p(X^(1/N))``. - >>> from flint import Ordering - >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) + >>> ctx = fmpq_mpoly_ctx.get(('x', 'y'), 'lex') >>> x, y = ctx.gens() >>> f = x**3 * y + x * y**4 + x * y >>> f.deflate([2, 3]) @@ -1058,8 +1033,7 @@ cdef class fmpq_mpoly(flint_mpoly): """ Compute the deflation of ``self``, that is ``p(X^(1/N))`` for maximal N. - >>> from flint import Ordering - >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) + >>> ctx = fmpq_mpoly_ctx.get(('x', 'y'), 'lex') >>> x, y = ctx.gens() >>> f = x**2 * y**2 + x * y**2 >>> q, N = f.deflation() @@ -1091,8 +1065,7 @@ cdef class fmpq_mpoly(flint_mpoly): = m * q(X^N)`` for maximal N. The returned monomial allows the undo-ing of the deflation. - >>> from flint import Ordering - >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) + >>> ctx = fmpq_mpoly_ctx.get(('x', 'y'), 'lex') >>> x, y = ctx.gens() >>> f = x**3 * y + x * y**4 + x * y >>> fd, N, m = f.deflation_monom() @@ -1123,8 +1096,7 @@ cdef class fmpq_mpoly(flint_mpoly): exponents. It is the exponent vector of the monomial returned by ``deflation_monom``. - >>> from flint import Ordering - >>> ctx = fmpq_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) + >>> ctx = fmpq_mpoly_ctx.get(('x', 'y'), 'lex') >>> x, y = ctx.gens() >>> f = x**3 * y + x * y**4 + x * y >>> N, I = f.deflation_index() diff --git a/src/flint/types/fmpz_mod_mpoly.pxd b/src/flint/types/fmpz_mod_mpoly.pxd index 50058243..8c473595 100644 --- a/src/flint/types/fmpz_mod_mpoly.pxd +++ b/src/flint/types/fmpz_mod_mpoly.pxd @@ -1,4 +1,4 @@ -from flint.flint_base.flint_base cimport flint_mpoly, flint_mpoly_context +from flint.flint_base.flint_base cimport flint_mpoly, flint_mod_mpoly_context from flint.flintlib.functions.fmpz_mod_mpoly cimport ( fmpz_mod_mpoly_ctx_t, @@ -21,9 +21,8 @@ cdef inline fmpz_mod_mpoly create_fmpz_mod_mpoly(fmpz_mod_mpoly_ctx ctx): var._init = True return var -cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context): +cdef class fmpz_mod_mpoly_ctx(flint_mod_mpoly_context): cdef fmpz_mod_mpoly_ctx_t val - cdef readonly object __prime_modulus cdef class fmpz_mod_mpoly(flint_mpoly): cdef fmpz_mod_mpoly_t val diff --git a/src/flint/types/fmpz_mod_mpoly.pyx b/src/flint/types/fmpz_mod_mpoly.pyx index 0e091244..de20cad9 100644 --- a/src/flint/types/fmpz_mod_mpoly.pyx +++ b/src/flint/types/fmpz_mod_mpoly.pyx @@ -1,10 +1,9 @@ from flint.flint_base.flint_base cimport ( flint_mpoly, - flint_mpoly_context, + flint_mod_mpoly_context, ordering_py_to_c, ordering_c_to_py, ) -from flint.flint_base.flint_base import Ordering from flint.utils.typecheck cimport typecheck from flint.utils.flint_exceptions import DomainError, IncompatibleContextError @@ -76,20 +75,22 @@ cimport libc.stdlib cdef dict _fmpz_mod_mpoly_ctx_cache = {} -cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context): +cdef class fmpz_mod_mpoly_ctx(flint_mod_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. + :param ordering: The term order for the ring. + :param modulus: The modulus for the ring. - Do not construct one of these directly, use ``fmpz_mod_mpoly_ctx.get_context``. + Do not construct one of these directly, use ``fmpz_mod_mpoly_ctx.get``. """ _ctx_cache = _fmpz_mod_mpoly_ctx_cache - def __init__(self, slong nvars, ordering, names, modulus): + @classmethod + def _new_(cls, names, ordering, modulus): + cdef fmpz_mod_mpoly_ctx self = cls.__new__(cls) cdef fmpz m if not typecheck(modulus, fmpz): m = any_as_fmpz(modulus) @@ -97,76 +98,36 @@ cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context): raise TypeError(f"modulus ({modulus}) is not coercible to fmpz") else: m = modulus - fmpz_mod_mpoly_ctx_init(self.val, nvars, ordering_py_to_c(ordering), m.val) - self.__prime_modulus = None - super().__init__(nvars, names) - @classmethod - def create_context_key( - cls, - slong nvars=1, - ordering=Ordering.lex, - modulus = None, - names: Optional[str] = "x", - nametup: Optional[tuple] = None, - ): - """ - Create a key for the context cache via the number of variables, the ordering, the modulus, and either a - variable name string, or a tuple of variable names. - """ - # A type hint of ``ordering: Ordering`` results in the error "TypeError: an integer is required" if a Ordering - # 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") - elif not typecheck(modulus, fmpz): - m = any_as_fmpz(modulus) - if m is NotImplemented: - raise TypeError(f"'modulus' ('{modulus}') is not coercible to fmpz") - else: - modulus = m + super()._new_(self, names, m.is_prime()) + fmpz_mod_mpoly_ctx_init(self.val, len(names), ordering_py_to_c(ordering), m.val) + return self - if nametup is not None: - key = nvars, ordering, nametup, modulus - elif nametup is None and names is not None: - key = nvars, ordering, cls.create_variable_names(nvars, names), modulus - else: - raise ValueError("must provide either 'names' or 'nametup'") - return key - - def any_as_scalar(self, other): + def _any_as_scalar(self, other): if isinstance(other, int): return any_as_fmpz(other) elif typecheck(other, nmod): - if (other).modulus() != self.modulus(): - raise DomainError( - f"modulus does not match, got {(other).modulus()}, expected {self.modulus()}" - ) return any_as_fmpz((other).val) elif typecheck(other, fmpz): res = fmpz.__new__(fmpz) fmpz_set((res).val, (other).val) return res elif typecheck(other, fmpz_mod): - if (other).ctx.modulus() != self.modulus(): - raise DomainError( - f"modulus does not match, got {(other).ctx.modulus()}, expected {self.modulus()}" - ) res = fmpz.__new__(fmpz) fmpz_set((res).val, (other).val) return res else: return NotImplemented - def scalar_as_mpoly(self, other: fmpz): - # non-fmpz scalars should first be converted via self.any_as_scalar + def _scalar_as_mpoly(self, other: fmpz): + # non-fmpz scalars should first be converted via self._any_as_scalar return self.constant(other) def nvars(self): """ Return the number of variables in the context - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(4, Ordering.lex, 11, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get(('x', 4), 11, 'lex') >>> ctx.nvars() 4 """ @@ -176,10 +137,9 @@ cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context): """ Return the term order of the context object. - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(4, Ordering.deglex, 11, 'w') + >>> ctx = fmpz_mod_mpoly_ctx.get(('w', 4), 11, 'deglex') >>> ctx.ordering() - + """ return ordering_c_to_py(self.val.minfo.ord) @@ -187,8 +147,7 @@ cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context): """ Return the modulus of the context object. - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(4, Ordering.deglex, 2, 'w') + >>> ctx = fmpz_mod_mpoly_ctx.get(('w', 4), 2, 'deglex') >>> ctx.modulus() 2 @@ -197,28 +156,11 @@ cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context): fmpz_mod_mpoly_ctx_get_modulus(m.val, self.val) return m - def is_prime(self): - """ - Return whether the modulus is prime - - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(4, Ordering.degrevlex, 2**127, 'z') - >>> ctx.is_prime() - False - >>> ctx = fmpz_mod_mpoly_ctx.get_context(4, Ordering.degrevlex, 2**127 - 1, 'z') - >>> ctx.is_prime() - True - """ - if self.__prime_modulus is None: - self.__prime_modulus = self.modulus().is_prime() - return self.__prime_modulus - def gen(self, slong i): """ Return the ``i`` th generator of the polynomial ring - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(3, Ordering.degrevlex, 11, 'z') + >>> ctx = fmpz_mod_mpoly_ctx.get(('z', 3), 11, 'degrevlex') >>> ctx.gen(1) z1 """ @@ -248,8 +190,7 @@ cdef class fmpz_mod_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. - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x,y') + >>> ctx = fmpz_mod_mpoly_ctx.get(('x', 'y'), 11, 'lex') >>> ctx.from_dict({(1,0):2, (1,1):3, (0,1):1}) 3*x*y + 2*x + y """ @@ -270,7 +211,7 @@ cdef class fmpz_mod_mpoly_ctx(flint_mpoly_context): continue exp_vec = fmpz_vec(exps) - coeff_scalar = self.any_as_scalar(coeff) + coeff_scalar = self._any_as_scalar(coeff) if coeff_scalar is NotImplemented: raise TypeError(f"cannot coerce {repr(coeff)} to nmod_mpoly coefficient") @@ -382,8 +323,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): Always returns a value, missing keys will return ``0``. Negative exponents are made positive. - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get(('x', 2), 11, 'lex') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p[1, 1] 3 @@ -408,8 +348,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): Will always set a value, missing keys will create a new term. Negative exponents are made positive. - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get(('x', 2), 11, 'lex') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p[1, 1] = 20 >>> p @@ -425,7 +364,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): raise ValueError("exponent vector provided does not match number of variables") exp_vec = fmpz_vec(x, double_indirect=True) - coeff = self.ctx.any_as_scalar(y) + coeff = self.ctx._any_as_scalar(y) if coeff is NotImplemented: raise TypeError("provided coefficient not coercible to fmpz") fmpz_mod_mpoly_set_coeff_fmpz_fmpz(self.val, (coeff).val, exp_vec.double_indirect, self.ctx.val) @@ -566,7 +505,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): if nargs != nvars: raise ValueError("number of generators does not match number of arguments") - args = [self.ctx.any_as_scalar(x) for x in args] + args = [self.ctx._any_as_scalar(x) for x in args] V = fmpz_vec(args, double_indirect=True) vres = fmpz.__new__(fmpz) fmpz_mod_mpoly_evaluate_all_fmpz(vres.val, self.val, V.double_indirect, self.ctx.val) @@ -576,8 +515,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): """ Return the exponent vectors of each term as a tuple of fmpz. - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get(('x', 2), 11, 'lex') >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) >>> f.monoms() [(1, 1), (1, 0), (0, 1), (0, 0)] @@ -598,8 +536,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): """ Return the coefficients of each term as a fmpz - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get(('x', 2), 11, 'lex') >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) >>> f.coeffs() [4, 2, 3, 1] @@ -621,8 +558,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): # """ # Return the terms of this polynomial as a list of fmpz_mod_mpolys. - # >>> from flint import Ordering - # >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + # >>> ctx = fmpz_mod_mpoly_ctx.get(('x', 2), 11, 'lex') # >>> 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] @@ -645,8 +581,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): Partial evaluate this polynomial with select constants. Keys must be generator names or generator indices, all values must be fmpz. - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get(('x', 2), 11, 'lex') >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) >>> f.subs({"x1": 0}) 2*x0 + 1 @@ -656,7 +591,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): fmpz_mod_mpoly res slong i - args = tuple((self.ctx.variable_to_index(k), self.ctx.any_as_scalar(v)) for k, v in dict_args.items()) + args = tuple((self.ctx.variable_to_index(k), self.ctx._any_as_scalar(v)) for k, v in dict_args.items()) for (_, v), old in zip(args, dict_args.values()): if v is NotImplemented: raise TypeError(f"cannot coerce {type(old)} to fmpz") @@ -674,9 +609,8 @@ cdef class fmpz_mod_mpoly(flint_mpoly): Compose this polynomial with other fmpz_mod_mpolys. All arguments must share the same context, it may different from this polynomials context. - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(1, Ordering.lex, 11, 'x') - >>> ctx1 = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'y') + >>> ctx = fmpz_mod_mpoly_ctx.get(('x',), 11, 'lex') + >>> ctx1 = fmpz_mod_mpoly_ctx.get(('y', 2), 11, 'lex') >>> f = ctx.from_dict({(2,): 1}) >>> g = ctx1.from_dict({(1, 0): 1, (0, 1): 1}) >>> f @@ -722,8 +656,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): """ Return the context object for this polynomials. - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get(('x', 2), 11, 'lex') >>> p = ctx.from_dict({(0, 1): 2}) >>> ctx is p.context() True @@ -734,8 +667,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): """ Return the coefficient at index ``i``. - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get(('x', 2), 11, 'lex') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p.coefficient(1) 2 @@ -752,8 +684,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): """ Return the exponent vector at index ``i`` as a tuple. - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get(('x', 2), 11, 'lex') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p.monomial(1) (0, 1) @@ -771,8 +702,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): """ Return a dictionary of variable name to degree. - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(4, Ordering.lex, 11, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get(('x', 4), 11, 'lex') >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) >>> p.degrees() (1, 2, 3, 0) @@ -788,8 +718,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): """ Return the total degree. - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(4, Ordering.lex, 11, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get(('x', 4), 11, 'lex') >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) >>> p.total_degree() 3 @@ -802,8 +731,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): """ Leading coefficient in the monomial ordering. - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx(2, Ordering.lex, ['x', 'y'], 11) + >>> ctx = fmpz_mod_mpoly_ctx.get(('x', 'y'), 11, 'lex') >>> x, y = ctx.gens() >>> p = 2*x*y + 3*x + 4*y**2 + 5 >>> p @@ -831,8 +759,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): """ Return the gcd of self and other. - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get(('x', 2), 11, 'lex') >>> f = ctx.from_dict({(1, 1): 4, (0, 0): 1}) >>> g = ctx.from_dict({(0, 1): 2, (1, 0): 2}) >>> (f * g).gcd(f) @@ -854,8 +781,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): Return the GCD of the terms of ``self``. If ``self`` is zero, then the result will be zero, otherwise it will be a monomial with positive coefficient. - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get(('x', 2), 11, 'lex') >>> x0, x1 = ctx.gens() >>> f = 3 * x0**2 * x1 + 6 * x0 * x1 >>> f.term_content() @@ -870,8 +796,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): """ Return the resultant of ``self`` and ``other`` with respect to variable ``var``. - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get(('x', 2), 11, 'lex') >>> x0, x1 = ctx.gens() >>> f = x0**2 * x1 + x0 * x1 >>> g = x0 + x1 @@ -897,8 +822,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): """ Return the discriminant of ``self`` with respect to variable ``var``. - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get(('x', 2), 11, 'lex') >>> x0, x1 = ctx.gens() >>> f = (x0 + x1)**2 + 1 >>> f.discriminant('x1') @@ -919,8 +843,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): """ Return the square root of self. - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get(('x', 2), 11, 'lex') >>> f = ctx.from_dict({(1, 1): 4, (0, 0): 1}) >>> (f * f).sqrt() 4*x0*x1 + 1 @@ -942,9 +865,8 @@ cdef class fmpz_mod_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_mod_mpoly - >>> ctx = fmpz_mod_mpoly_ctx.get_context(3, Ordering.lex, 11, 'x,y,z') + >>> ctx = fmpz_mod_mpoly_ctx.get(('x', 'y', 'z'), 11, 'lex') >>> p1 = Zm("2*x + 4", ctx) >>> p2 = Zm("3*x*z + 3*x + 3*z + 3", ctx) >>> (p1 * p2).factor() @@ -985,9 +907,8 @@ cdef class fmpz_mod_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_mod_mpoly - >>> ctx = fmpz_mod_mpoly_ctx.get_context(3, Ordering.lex, 11, 'x,y,z') + >>> ctx = fmpz_mod_mpoly_ctx.get(('x', 'y', 'z'), 11, 'lex') >>> p1 = Zm("2*x + 4", ctx) >>> p2 = Zm("3*x*y + 3*x + 3*y + 3", ctx) >>> (p1 * p2).factor_squarefree() @@ -1056,8 +977,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): 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_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> ctx = fmpz_mod_mpoly_ctx.get(('x', 2), 11, 'lex') >>> p = ctx.from_dict({(0, 3): 2, (2, 1): 3}) >>> p 3*x0^2*x1 + 2*x1^3 @@ -1081,8 +1001,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): Compute the inflation of ``self`` for a provided ``N``, that is return ``q`` such that ``q(X) = p(X^N)``. - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, nametup=('x', 'y')) + >>> ctx = fmpz_mod_mpoly_ctx.get(('x', 'y'), 11, 'lex') >>> x, y = ctx.gens() >>> f = x + y + 1 >>> f.inflate([2, 3]) @@ -1109,8 +1028,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): Compute the deflation of ``self`` for a provided ``N``, that is return ``q`` such that ``q(X) = p(X^(1/N))``. - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, nametup=('x', 'y')) + >>> ctx = fmpz_mod_mpoly_ctx.get(('x', 'y'), 11, 'lex') >>> x, y = ctx.gens() >>> f = x**3 * y + x * y**4 + x * y >>> f.deflate([2, 3]) @@ -1134,8 +1052,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): Compute the deflation of ``self``, that is ``p(X^(1/N))`` for maximal N. Returns ``q, N`` such that ``self == q.inflate(N)``. - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, nametup=('x', 'y')) + >>> ctx = fmpz_mod_mpoly_ctx.get(('x', 'y'), 11, 'lex') >>> x, y = ctx.gens() >>> f = x**2 * y**2 + x * y**2 >>> q, N = f.deflation() @@ -1166,8 +1083,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): = m * q(X^N)`` for maximal N. The returned monomial allows the undo-ing of the deflation. - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, nametup=('x', 'y')) + >>> ctx = fmpz_mod_mpoly_ctx.get(('x', 'y'), 11, 'lex') >>> x, y = ctx.gens() >>> f = x**3 * y + x * y**4 + x * y >>> fd, N, m = f.deflation_monom() @@ -1197,8 +1113,7 @@ cdef class fmpz_mod_mpoly(flint_mpoly): exponents. It is the exponent vector of the monomial returned by ``deflation_monom``. - >>> from flint import Ordering - >>> ctx = fmpz_mod_mpoly_ctx.get_context(2, Ordering.lex, 11, nametup=('x', 'y')) + >>> ctx = fmpz_mod_mpoly_ctx.get(('x', 'y'), 11, 'lex') >>> x, y = ctx.gens() >>> f = x**3 * y + x * y**4 + x * y >>> N, I = f.deflation_index() diff --git a/src/flint/types/fmpz_mpoly.pyx b/src/flint/types/fmpz_mpoly.pyx index b351127f..e5cb1962 100644 --- a/src/flint/types/fmpz_mpoly.pyx +++ b/src/flint/types/fmpz_mpoly.pyx @@ -93,20 +93,22 @@ cdef class fmpz_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. + :param ordering: The term order for the ring. - Do not construct one of these directly, use ``fmpz_mpoly_ctx.get_context``. + Do not construct one of these directly, use ``fmpz_mpoly_ctx.get``. """ _ctx_cache = _fmpz_mpoly_ctx_cache - def __init__(self, slong nvars, ordering, names): - fmpz_mpoly_ctx_init(self.val, nvars, ordering_py_to_c(ordering)) - super().__init__(nvars, names) + @classmethod + def _new_(cls, names, ordering): + cdef fmpz_mpoly_ctx self = cls.__new__(cls) + super()._new_(self, names) + fmpz_mpoly_ctx_init(self.val, len(names), ordering_py_to_c(ordering)) + return self - def any_as_scalar(self, other): + def _any_as_scalar(self, other): if isinstance(other, int): return any_as_fmpz(other) elif typecheck(other, fmpz): @@ -116,16 +118,15 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): else: return NotImplemented - def scalar_as_mpoly(self, other: fmpz): - # non-fmpz scalars should first be converted via self.any_as_scalar + def _scalar_as_mpoly(self, other: fmpz): + # non-fmpz scalars should first be converted via self._any_as_scalar return self.constant(other) def nvars(self): """ Return the number of variables in the context - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(4, Ordering.lex, 'x') + >>> ctx = fmpz_mpoly_ctx.get(('x', 4), 'lex') >>> ctx.nvars() 4 """ @@ -135,10 +136,9 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): """ Return the term order of the context object. - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(4, Ordering.deglex, 'w') + >>> ctx = fmpz_mpoly_ctx.get(('w', 4), 'deglex') >>> ctx.ordering() - + """ return ordering_c_to_py(self.val.minfo.ord) @@ -146,8 +146,7 @@ cdef class fmpz_mpoly_ctx(flint_mpoly_context): """ Return the ``i`` th generator of the polynomial ring - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(3, Ordering.degrevlex, 'z') + >>> ctx = fmpz_mpoly_ctx.get(('z', 3), 'degrevlex') >>> ctx.gen(1) z1 """ @@ -177,8 +176,7 @@ 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. - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x,y') + >>> ctx = fmpz_mpoly_ctx.get(('x', 'y'), 'lex') >>> ctx.from_dict({(1,0):2, (1,1):3, (0,1):1}) 3*x*y + 2*x + y """ @@ -291,8 +289,7 @@ cdef class fmpz_mpoly(flint_mpoly): 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') + >>> ctx = fmpz_mpoly_ctx.get(('x', 2), 'lex') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p[1, 1] 3 @@ -317,8 +314,7 @@ cdef class fmpz_mpoly(flint_mpoly): 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') + >>> ctx = fmpz_mpoly_ctx.get(('x', 2), 'lex') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p[1, 1] = 20 >>> p @@ -502,8 +498,7 @@ cdef class fmpz_mpoly(flint_mpoly): """ 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') + >>> ctx = fmpz_mpoly_ctx.get(('x', 2), 'lex') >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) >>> f.monoms() [(1, 1), (1, 0), (0, 1), (0, 0)] @@ -524,8 +519,7 @@ cdef class fmpz_mpoly(flint_mpoly): """ Return the coefficients of each term as a fmpz - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpz_mpoly_ctx.get(('x', 2), 'lex') >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) >>> f.coeffs() [4, 2, 3, 1] @@ -547,8 +541,7 @@ cdef class fmpz_mpoly(flint_mpoly): # """ # 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') + # >>> ctx = fmpz_mpoly_ctx.get(('x', 2), 'lex') # >>> 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] @@ -571,8 +564,7 @@ cdef class fmpz_mpoly(flint_mpoly): Partial evaluate this polynomial with select constants. Keys must be generator names or generator indices, all values must be fmpz. - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpz_mpoly_ctx.get(('x', 2), 'lex') >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) >>> f.subs({"x1": 0}) 2*x0 + 1 @@ -601,9 +593,8 @@ cdef class fmpz_mpoly(flint_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') + >>> ctx = fmpz_mpoly_ctx.get(('x',), 'lex') + >>> ctx1 = fmpz_mpoly_ctx.get(('y', 2), 'lex') >>> f = ctx.from_dict({(2,): 1}) >>> g = ctx1.from_dict({(1, 0): 1, (0, 1): 1}) >>> f @@ -651,8 +642,7 @@ cdef class fmpz_mpoly(flint_mpoly): """ Return the context object for this polynomials. - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpz_mpoly_ctx.get(('x', 2), 'lex') >>> p = ctx.from_dict({(0, 1): 2}) >>> ctx is p.context() True @@ -663,8 +653,7 @@ cdef class fmpz_mpoly(flint_mpoly): """ Return the coefficient at index ``i``. - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpz_mpoly_ctx.get(('x', 2), 'lex') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p.coefficient(1) 2 @@ -681,8 +670,7 @@ cdef class fmpz_mpoly(flint_mpoly): """ Return the exponent vector at index ``i`` as a tuple. - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpz_mpoly_ctx.get(('x', 2), 'lex') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p.monomial(1) (0, 1) @@ -700,8 +688,7 @@ cdef class fmpz_mpoly(flint_mpoly): """ Return a dictionary of variable name to degree. - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(4, Ordering.lex, 'x') + >>> ctx = fmpz_mpoly_ctx.get(('x', 4), 'lex') >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) >>> p.degrees() (1, 2, 3, 0) @@ -717,8 +704,7 @@ cdef class fmpz_mpoly(flint_mpoly): """ Return the total degree. - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(4, Ordering.lex, 'x') + >>> ctx = fmpz_mpoly_ctx.get(('x', 4), 'lex') >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) >>> p.total_degree() 3 @@ -731,8 +717,7 @@ cdef class fmpz_mpoly(flint_mpoly): """ Leading coefficient in the monomial ordering. - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx(2, Ordering.lex, ['x', 'y']) + >>> ctx = fmpz_mpoly_ctx.get(('x', 'y'), 'lex') >>> x, y = ctx.gens() >>> p = 2*x*y + 3*x + 4*y**2 + 5 >>> p @@ -760,8 +745,7 @@ cdef class fmpz_mpoly(flint_mpoly): """ Return the gcd of self and other. - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpz_mpoly_ctx.get(('x', 2), 'lex') >>> f = ctx.from_dict({(1, 1): 4, (0, 0): 1}) >>> g = ctx.from_dict({(0, 1): 2, (1, 0): 2}) >>> (f * g).gcd(f) @@ -780,8 +764,7 @@ cdef class fmpz_mpoly(flint_mpoly): """ Return the GCD of the coefficients of ``self``. - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpz_mpoly_ctx.get(('x', 2), 'lex') >>> x0, x1 = ctx.gens() >>> f = 3 * x0**2 * x1 + 6 * x0 * x1 >>> f.content() @@ -796,8 +779,7 @@ cdef class fmpz_mpoly(flint_mpoly): Return the GCD of the terms of ``self``. If ``self`` is zero, then the result will be zero, otherwise it will be a monomial with positive coefficient. - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpz_mpoly_ctx.get(('x', 2), 'lex') >>> x0, x1 = ctx.gens() >>> f = 3 * x0**2 * x1 + 6 * x0 * x1 >>> f.term_content() @@ -811,8 +793,7 @@ cdef class fmpz_mpoly(flint_mpoly): """ Return the resultant of ``self`` and ``other`` with respect to variable ``var``. - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpz_mpoly_ctx.get(('x', 2), 'lex') >>> x0, x1 = ctx.gens() >>> f = x0**2 * x1 + x0 * x1 >>> g = x0 + x1 @@ -838,8 +819,7 @@ cdef class fmpz_mpoly(flint_mpoly): """ Return the discriminant of ``self`` with respect to variable ``var``. - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpz_mpoly_ctx.get(('x', 2), 'lex') >>> x0, x1 = ctx.gens() >>> f = (x0 + x1)**2 + 1 >>> f.discriminant('x1') @@ -860,8 +840,7 @@ cdef class fmpz_mpoly(flint_mpoly): """ Return the content and primitive of ``self``. - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpz_mpoly_ctx.get(('x', 2), 'lex') >>> x, y = ctx.gens() >>> f = 4*x + 2*x*y >>> f.primitive() @@ -876,8 +855,7 @@ 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. - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, 'x') + >>> ctx = fmpz_mpoly_ctx.get(('x', 2), 'lex') >>> f = ctx.from_dict({(1, 1): 4, (0, 0): 1}) >>> (f * f).sqrt() 4*x0*x1 + 1 @@ -896,9 +874,8 @@ 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, Ordering.lex, 'x,y,z') + >>> ctx = fmpz_mpoly_ctx.get(('x', 'y', 'z'), 'lex') >>> p1 = Zm("2*x + 4", ctx) >>> p2 = Zm("3*x*z + 3*x + 3*z + 3", ctx) >>> (p1 * p2).factor() @@ -936,9 +913,8 @@ 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, Ordering.lex, 'x,y,z') + >>> ctx = fmpz_mpoly_ctx.get(('x', 'y', 'z'), 'lex') >>> p1 = Zm("2*x + 4", ctx) >>> p2 = Zm("3*x*y + 3*x + 3*y + 3", ctx) >>> (p1 * p2).factor_squarefree() @@ -1003,8 +979,7 @@ cdef class fmpz_mpoly(flint_mpoly): 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') + >>> ctx = fmpz_mpoly_ctx.get(('x', 2), 'lex') >>> p = ctx.from_dict({(0, 3): 2, (2, 1): 3}) >>> p 3*x0^2*x1 + 2*x1^3 @@ -1030,8 +1005,7 @@ cdef class fmpz_mpoly(flint_mpoly): 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') + >>> ctx = fmpz_mpoly_ctx.get(('x', 2), 'lex') >>> p = ctx.from_dict({(0, 3): 2, (2, 1): 3}) >>> p 3*x0^2*x1 + 2*x1^3 @@ -1058,8 +1032,7 @@ cdef class fmpz_mpoly(flint_mpoly): Compute the S-polynomial of ``self`` and ``g``, scaled to an integer polynomial by computing the LCM of the leading coefficients. - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) + >>> ctx = fmpz_mpoly_ctx.get(('x', 'y'), 'lex') >>> f = ctx.from_dict({(2, 0): 1, (0, 1): -1}) >>> g = ctx.from_dict({(3, 0): 1, (1, 0): -1}) >>> f.spoly(g) @@ -1079,15 +1052,14 @@ cdef class fmpz_mpoly(flint_mpoly): Compute the the primitive part of the reduction (remainder of multivariate quasi-division with remainder) with respect to the polynomials ``vec``. - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) + >>> ctx = fmpz_mpoly_ctx.get(('x', 'y'), 'lex') >>> x, y = ctx.gens() >>> f = 2 * x**3 -x**2 * y + y**3 + 3 * y >>> g1 = x**2 + y**2 + 1 >>> g2 = x * y - 2 >>> vec = fmpz_mpoly_vec([g1, g2], ctx) >>> vec - fmpz_mpoly_vec([x^2 + y^2 + 1, x*y - 2], ctx=fmpz_mpoly_ctx(2, '', ('x', 'y'))) + fmpz_mpoly_vec([x^2 + y^2 + 1, x*y - 2], ctx=fmpz_mpoly_ctx(2, '', ('x', 'y'))) >>> f.reduction_primitive_part(vec) x - y^3 """ @@ -1104,8 +1076,7 @@ cdef class fmpz_mpoly(flint_mpoly): Compute the inflation of ``self`` for a provided ``N``, that is return ``q`` such that ``q(X) = p(X^N)``. - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) + >>> ctx = fmpz_mpoly_ctx.get(('x', 'y'), 'lex') >>> x, y = ctx.gens() >>> f = x + y + 1 >>> f.inflate([2, 3]) @@ -1132,8 +1103,7 @@ cdef class fmpz_mpoly(flint_mpoly): Compute the deflation of ``self`` for a provided ``N``, that is return ``q`` such that ``q(X) = p(X^(1/N))``. - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) + >>> ctx = fmpz_mpoly_ctx.get(('x', 'y'), 'lex') >>> x, y = ctx.gens() >>> f = x**3 * y + x * y**4 + x * y >>> f.deflate([2, 3]) @@ -1156,8 +1126,7 @@ cdef class fmpz_mpoly(flint_mpoly): """ Compute the deflation of ``self``, that is ``p(X^(1/N))`` for maximal N. - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) + >>> ctx = fmpz_mpoly_ctx.get(('x', 'y'), 'lex') >>> x, y = ctx.gens() >>> f = x**2 * y**2 + x * y**2 >>> q, N = f.deflation() @@ -1188,8 +1157,7 @@ cdef class fmpz_mpoly(flint_mpoly): = m * q(X^N)`` for maximal N. The returned monomial allows the undo-ing of the deflation. - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) + >>> ctx = fmpz_mpoly_ctx.get(('x', 'y'), 'lex') >>> x, y = ctx.gens() >>> f = x**3 * y + x * y**4 + x * y >>> fd, N, m = f.deflation_monom() @@ -1219,8 +1187,7 @@ cdef class fmpz_mpoly(flint_mpoly): exponents. It is the exponent vector of the monomial returned by ``deflation_monom``. - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) + >>> ctx = fmpz_mpoly_ctx.get(('x', 'y'), 'lex') >>> x, y = ctx.gens() >>> f = x**3 * y + x * y**4 + x * y >>> N, I = f.deflation_index() @@ -1345,8 +1312,7 @@ cdef class fmpz_mpoly_vec: Check if self is a Gröbner basis. If ``other`` is not None then check if self is a Gröbner basis for ``other``. - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) + >>> ctx = fmpz_mpoly_ctx.get(('x', 'y'), 'lex') >>> x, y = ctx.gens() >>> f = x**2 - y >>> g = x**3 - x @@ -1376,8 +1342,7 @@ cdef class fmpz_mpoly_vec: """ Check if self is auto-reduced (or inter-reduced). - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) + >>> ctx = fmpz_mpoly_ctx.get(('x', 'y'), 'lex') >>> x, y = ctx.gens() >>> f2 = 3*x**2 - y >>> k = x*y - x @@ -1399,8 +1364,7 @@ cdef class fmpz_mpoly_vec: Gröbner basis, compute the reduced reduced Gröbner basis of ``self``, throws an ``RuntimeError`` otherwise. - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) + >>> ctx = fmpz_mpoly_ctx.get(('x', 'y'), 'lex') >>> x, y = ctx.gens() >>> f2 = 3*x**2 - y >>> k2 = 3*x*y - 3*x @@ -1412,7 +1376,7 @@ cdef class fmpz_mpoly_vec: >>> vec2.is_autoreduced() True >>> vec2 - fmpz_mpoly_vec([3*x^2 - y, x*y - x, y^2 - y], ctx=fmpz_mpoly_ctx(2, '', ('x', 'y'))) + fmpz_mpoly_vec([3*x^2 - y, x*y - x, y^2 - y], ctx=fmpz_mpoly_ctx(2, '', ('x', 'y'))) """ cdef fmpz_mpoly_vec h = fmpz_mpoly_vec(0, self.ctx) @@ -1447,8 +1411,7 @@ cdef class fmpz_mpoly_vec: implementation and does not compute a reduced basis. To construct a reduced basis use ``autoreduce``. - >>> from flint import Ordering - >>> ctx = fmpz_mpoly_ctx.get_context(2, Ordering.lex, nametup=('x', 'y')) + >>> ctx = fmpz_mpoly_ctx.get(('x', 'y'), 'lex') >>> x, y = ctx.gens() >>> f = x**2 - y >>> g = x**3*y - x @@ -1457,13 +1420,13 @@ cdef class fmpz_mpoly_vec: False >>> vec2 = vec.buchberger_naive() >>> vec2 - fmpz_mpoly_vec([x^2 - y, x^3*y - x, x*y^2 - x, y^3 - y], ctx=fmpz_mpoly_ctx(2, '', ('x', 'y'))) + fmpz_mpoly_vec([x^2 - y, x^3*y - x, x*y^2 - x, y^3 - y], ctx=fmpz_mpoly_ctx(2, '', ('x', 'y'))) >>> vec.buchberger_naive(limits=(2, 2, 512)) - (fmpz_mpoly_vec([x^2 - y, x^3*y - x], ctx=fmpz_mpoly_ctx(2, '', ('x', 'y'))), False) + (fmpz_mpoly_vec([x^2 - y, x^3*y - x], ctx=fmpz_mpoly_ctx(2, '', ('x', 'y'))), False) >>> vec2.is_autoreduced() False >>> vec2.autoreduction() - fmpz_mpoly_vec([x^2 - y, y^3 - y, x*y^2 - x], ctx=fmpz_mpoly_ctx(2, '', ('x', 'y'))) + fmpz_mpoly_vec([x^2 - y, y^3 - y, x*y^2 - x], ctx=fmpz_mpoly_ctx(2, '', ('x', 'y'))) """ cdef: diff --git a/src/flint/types/nmod_mpoly.pxd b/src/flint/types/nmod_mpoly.pxd index 3626208f..8a44d87d 100644 --- a/src/flint/types/nmod_mpoly.pxd +++ b/src/flint/types/nmod_mpoly.pxd @@ -1,4 +1,4 @@ -from flint.flint_base.flint_base cimport flint_mpoly, flint_mpoly_context +from flint.flint_base.flint_base cimport flint_mpoly, flint_mod_mpoly_context from flint.flintlib.functions.nmod_mpoly cimport ( nmod_mpoly_ctx_t, @@ -21,9 +21,8 @@ cdef inline nmod_mpoly create_nmod_mpoly(nmod_mpoly_ctx ctx): var._init = True return var -cdef class nmod_mpoly_ctx(flint_mpoly_context): +cdef class nmod_mpoly_ctx(flint_mod_mpoly_context): cdef nmod_mpoly_ctx_t val - cdef readonly object __prime_modulus cdef class nmod_mpoly(flint_mpoly): cdef nmod_mpoly_t val diff --git a/src/flint/types/nmod_mpoly.pyx b/src/flint/types/nmod_mpoly.pyx index f383deb7..d9de8791 100644 --- a/src/flint/types/nmod_mpoly.pyx +++ b/src/flint/types/nmod_mpoly.pyx @@ -1,10 +1,9 @@ from flint.flint_base.flint_base cimport ( flint_mpoly, - flint_mpoly_context, + flint_mod_mpoly_context, ordering_py_to_c, ordering_c_to_py, ) -from flint.flint_base.flint_base import Ordering from flint.utils.typecheck cimport typecheck from flint.utils.flint_exceptions import DomainError, IncompatibleContextError @@ -81,86 +80,54 @@ cimport libc.stdlib cdef dict _nmod_mpoly_ctx_cache = {} -cdef class nmod_mpoly_ctx(flint_mpoly_context): +cdef class nmod_mpoly_ctx(flint_mod_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. + :param ordering: The term order for the ring. + :param modulus: The modulus for the ring. - Do not construct one of these directly, use ``nmod_mpoly_ctx.get_context``. + Do not construct one of these directly, use ``nmod_mpoly_ctx.get``. """ _ctx_cache = _nmod_mpoly_ctx_cache - def __init__(self, slong nvars, ordering, names, modulus: int): + @classmethod + def _new_(cls, names, ordering, modulus: int): + cdef nmod_mpoly_ctx self = cls.__new__(cls) + if modulus <= 0: raise ValueError("modulus must be positive") - nmod_mpoly_ctx_init(self.val, nvars, ordering_py_to_c(ordering), modulus) - self.__prime_modulus = None - super().__init__(nvars, names) + super()._new_(self, names, n_is_prime(modulus)) + nmod_mpoly_ctx_init(self.val, len(names), ordering_py_to_c(ordering), modulus) + return self - @classmethod - def create_context_key( - cls, - slong nvars=1, - ordering=Ordering.lex, - modulus = None, - names: Optional[str] = "x", - nametup: Optional[tuple] = None, - ): - """ - Create a key for the context cache via the number of variables, the ordering, the modulus, and either a - variable name string, or a tuple of variable names. - """ - # A type hint of ``ordering: Ordering`` results in the error "TypeError: an integer is required" if a Ordering - # 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") - - if nametup is not None: - key = nvars, ordering, nametup, modulus - elif nametup is None and names is not None: - key = nvars, ordering, cls.create_variable_names(nvars, names), modulus - else: - raise ValueError("must provide either 'names' or 'nametup'") - return key - - def any_as_scalar(self, other): + def _any_as_scalar(self, other): if isinstance(other, int): try: return other except OverflowError: return (other % self.modulus()) elif typecheck(other, nmod): - if (other).modulus() != self.modulus(): - raise DomainError( - f"modulus does not match, got {(other).modulus()}, expected {self.modulus()}" - ) return (other).val elif typecheck(other, fmpz): return fmpz_get_nmod((other).val, self.val.mod) elif typecheck(other, fmpz_mod): - if (other).ctx.modulus() != self.modulus(): - raise DomainError( - f"modulus does not match, got {(other).ctx.modulus()}, expected {self.modulus()}" - ) return fmpz_get_nmod((other).val, self.val.mod) else: return NotImplemented - def scalar_as_mpoly(self, other: ulong): - # non-ulong scalars should first be converted via self.any_as_scalar + def _scalar_as_mpoly(self, other: ulong): + # non-ulong scalars should first be converted via self._any_as_scalar return self.constant(other) def nvars(self): """ Return the number of variables in the context - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(4, Ordering.lex, 11, 'x') + >>> ctx = nmod_mpoly_ctx.get(('x', 4), 11, 'lex') >>> ctx.nvars() 4 """ @@ -170,10 +137,9 @@ cdef class nmod_mpoly_ctx(flint_mpoly_context): """ Return the term order of the context object. - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(4, Ordering.deglex, 11, 'w') + >>> ctx = nmod_mpoly_ctx.get(('w', 4), 11, 'deglex') >>> ctx.ordering() - + """ return ordering_c_to_py(self.val.minfo.ord) @@ -181,36 +147,18 @@ cdef class nmod_mpoly_ctx(flint_mpoly_context): """ Return the modulus of the context object. - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(4, Ordering.deglex, 2, 'w') + >>> ctx = nmod_mpoly_ctx.get(('w', 4), 2, 'deglex') >>> ctx.modulus() 2 """ return nmod_mpoly_ctx_modulus(self.val) - def is_prime(self): - """ - Return whether the modulus is prime - - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(4, Ordering.degrevlex, 2**32, 'z') - >>> ctx.is_prime() - False - >>> ctx = nmod_mpoly_ctx.get_context(4, Ordering.degrevlex, 2**32 - 17, 'z') - >>> ctx.is_prime() - True - """ - if self.__prime_modulus is None: - self.__prime_modulus = n_is_prime(self.modulus()) - return self.__prime_modulus - def gen(self, slong i): """ Return the ``i`` th generator of the polynomial ring - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(3, Ordering.degrevlex, 11, 'z') + >>> ctx = nmod_mpoly_ctx.get(('z', 3), 11, 'degrevlex') >>> ctx.gen(1) z1 """ @@ -237,8 +185,7 @@ cdef class nmod_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. - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x,y') + >>> ctx = nmod_mpoly_ctx.get(('x', 'y'), 11, 'lex') >>> ctx.from_dict({(1,0):2, (1,1):3, (0,1):1}) 3*x*y + 2*x + y """ @@ -259,7 +206,7 @@ cdef class nmod_mpoly_ctx(flint_mpoly_context): continue exp_vec = fmpz_vec(exps) - coeff_scalar = self.any_as_scalar(coeff) + coeff_scalar = self._any_as_scalar(coeff) if coeff_scalar is NotImplemented: raise TypeError(f"cannot coerce {repr(coeff)} to nmod_mpoly coefficient") @@ -359,8 +306,7 @@ cdef class nmod_mpoly(flint_mpoly): Always returns a value, missing keys will return ``0``. Negative exponents are made positive. - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> ctx = nmod_mpoly_ctx.get(('x', 2), 11, 'lex') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p[1, 1] 3 @@ -383,8 +329,7 @@ cdef class nmod_mpoly(flint_mpoly): Will always set a value, missing keys will create a new term. Negative exponents are made positive. - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> ctx = nmod_mpoly_ctx.get(('x', 2), 11, 'lex') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p[1, 1] = 20 >>> p @@ -402,7 +347,7 @@ cdef class nmod_mpoly(flint_mpoly): exp_vec = fmpz_vec(x, double_indirect=True) - coeff = self.ctx.any_as_scalar(y) + coeff = self.ctx._any_as_scalar(y) if coeff is NotImplemented: raise TypeError("provided coefficient not coercible to ulong") nmod_mpoly_set_coeff_ui_fmpz(self.val, coeff, exp_vec.double_indirect, self.ctx.val) @@ -542,7 +487,7 @@ cdef class nmod_mpoly(flint_mpoly): if nargs != nvars: raise ValueError("number of generators does not match number of arguments") - args = [self.ctx.any_as_scalar(x) for x in args] + args = [self.ctx._any_as_scalar(x) for x in args] cdef: # Using sizeof(ulong) here breaks on 64 windows machines because of the ``ctypedef unsigned long ulong`` in # flintlib/flint.pxd. Cython will inline this definition and then allocate the wrong amount of memory. @@ -564,8 +509,7 @@ cdef class nmod_mpoly(flint_mpoly): """ Return the exponent vectors of each term as a tuple of fmpz. - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> ctx = nmod_mpoly_ctx.get(('x', 2), 11, 'lex') >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) >>> f.monoms() [(1, 1), (1, 0), (0, 1), (0, 0)] @@ -586,8 +530,7 @@ cdef class nmod_mpoly(flint_mpoly): """ Return the coefficients of each term as a fmpz - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> ctx = nmod_mpoly_ctx.get(('x', 2), 11, 'lex') >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) >>> f.coeffs() [4, 2, 3, 1] @@ -599,8 +542,7 @@ cdef class nmod_mpoly(flint_mpoly): # """ # Return the terms of this polynomial as a list of nmod_mpolys. - # >>> from flint import Ordering - # >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + # >>> ctx = nmod_mpoly_ctx.get(('x', 2), 11, 'lex') # >>> 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] @@ -623,8 +565,7 @@ cdef class nmod_mpoly(flint_mpoly): Partial evaluate this polynomial with select constants. Keys must be generator names or generator indices, all values must be fmpz. - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> ctx = nmod_mpoly_ctx.get(('x', 2), 11, 'lex') >>> f = ctx.from_dict({(0, 0): 1, (1, 0): 2, (0, 1): 3, (1, 1): 4}) >>> f.subs({"x1": 0}) 2*x0 + 1 @@ -634,7 +575,7 @@ cdef class nmod_mpoly(flint_mpoly): nmod_mpoly res slong i - args = tuple((self.ctx.variable_to_index(k), self.ctx.any_as_scalar(v)) for k, v in dict_args.items()) + args = tuple((self.ctx.variable_to_index(k), self.ctx._any_as_scalar(v)) for k, v in dict_args.items()) for (_, v), old in zip(args, dict_args.values()): if v is NotImplemented: raise TypeError(f"cannot coerce {type(old)} to ulong") @@ -652,9 +593,8 @@ cdef class nmod_mpoly(flint_mpoly): Compose this polynomial with other nmod_mpolys. All arguments must share the same context, it may different from this polynomials context. - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(1, Ordering.lex, 11, 'x') - >>> ctx1 = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'y') + >>> ctx = nmod_mpoly_ctx.get(('x',), 11, 'lex') + >>> ctx1 = nmod_mpoly_ctx.get(('y', 2), 11, 'lex') >>> f = ctx.from_dict({(2,): 1}) >>> g = ctx1.from_dict({(1, 0): 1, (0, 1): 1}) >>> f @@ -700,8 +640,7 @@ cdef class nmod_mpoly(flint_mpoly): """ Return the context object for this polynomials. - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> ctx = nmod_mpoly_ctx.get(('x', 2), 11, 'lex') >>> p = ctx.from_dict({(0, 1): 2}) >>> ctx is p.context() True @@ -712,8 +651,7 @@ cdef class nmod_mpoly(flint_mpoly): """ Return the coefficient at index ``i``. - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> ctx = nmod_mpoly_ctx.get(('x', 2), 11, 'lex') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p.coefficient(1) 2 @@ -727,8 +665,7 @@ cdef class nmod_mpoly(flint_mpoly): """ Return the exponent vector at index ``i`` as a tuple. - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> ctx = nmod_mpoly_ctx.get(('x', 2), 11, 'lex') >>> p = ctx.from_dict({(0, 1): 2, (1, 1): 3}) >>> p.monomial(1) (0, 1) @@ -746,8 +683,7 @@ cdef class nmod_mpoly(flint_mpoly): """ Return a dictionary of variable name to degree. - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(4, Ordering.lex, 11, 'x') + >>> ctx = nmod_mpoly_ctx.get(('x', 4), 11, 'lex') >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) >>> p.degrees() (1, 2, 3, 0) @@ -763,8 +699,7 @@ cdef class nmod_mpoly(flint_mpoly): """ Return the total degree. - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(4, Ordering.lex, 11, 'x') + >>> ctx = nmod_mpoly_ctx.get(('x', 4), 11, 'lex') >>> p = ctx.from_dict({(1, 0, 0, 0): 1, (0, 2, 0, 0): 2, (0, 0, 3, 0): 3}) >>> p.total_degree() 3 @@ -777,8 +712,7 @@ cdef class nmod_mpoly(flint_mpoly): """ Leading coefficient in the monomial ordering. - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx(2, Ordering.lex, ['x', 'y'], 11) + >>> ctx = nmod_mpoly_ctx.get(('x', 'y'), 11, 'lex') >>> x, y = ctx.gens() >>> p = 2*x*y + 3*x + 4*y**2 + 5 >>> p @@ -806,8 +740,7 @@ cdef class nmod_mpoly(flint_mpoly): """ Return the gcd of self and other. - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> ctx = nmod_mpoly_ctx.get(('x', 2), 11, 'lex') >>> f = ctx.from_dict({(1, 1): 4, (0, 0): 1}) >>> g = ctx.from_dict({(0, 1): 2, (1, 0): 2}) >>> (f * g).gcd(f) @@ -829,8 +762,7 @@ cdef class nmod_mpoly(flint_mpoly): Return the GCD of the terms of ``self``. If ``self`` is zero, then the result will be zero, otherwise it will be a monomial with positive coefficient. - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> ctx = nmod_mpoly_ctx.get(('x', 2), 11, 'lex') >>> x0, x1 = ctx.gens() >>> f = 3 * x0**2 * x1 + 6 * x0 * x1 >>> f.term_content() @@ -845,8 +777,7 @@ cdef class nmod_mpoly(flint_mpoly): """ Return the resultant of ``self`` and ``other`` with respect to variable ``var``. - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> ctx = nmod_mpoly_ctx.get(('x', 2), 11, 'lex') >>> x0, x1 = ctx.gens() >>> f = x0**2 * x1 + x0 * x1 >>> g = x0 + x1 @@ -872,8 +803,7 @@ cdef class nmod_mpoly(flint_mpoly): """ Return the discriminant of ``self`` with respect to variable ``var``. - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> ctx = nmod_mpoly_ctx.get(('x', 2), 11, 'lex') >>> x0, x1 = ctx.gens() >>> f = (x0 + x1)**2 + 1 >>> f.discriminant('x1') @@ -894,8 +824,7 @@ cdef class nmod_mpoly(flint_mpoly): """ Return the square root of self. - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> ctx = nmod_mpoly_ctx.get(('x', 2), 11, 'lex') >>> f = ctx.from_dict({(1, 1): 4, (0, 0): 1}) >>> (f * f).sqrt() 4*x0*x1 + 1 @@ -917,9 +846,8 @@ cdef class nmod_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 = nmod_mpoly - >>> ctx = nmod_mpoly_ctx.get_context(3, Ordering.lex, 11, 'x,y,z') + >>> ctx = nmod_mpoly_ctx.get(('x', 'y', 'z'), 11, 'lex') >>> p1 = Zm("2*x + 4", ctx) >>> p2 = Zm("3*x*z + 3*x + 3*z + 3", ctx) >>> (p1 * p2).factor() @@ -959,9 +887,8 @@ cdef class nmod_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 = nmod_mpoly - >>> ctx = nmod_mpoly_ctx.get_context(3, Ordering.lex, 11, 'x,y,z') + >>> ctx = nmod_mpoly_ctx.get(('x', 'y', 'z'), 11, 'lex') >>> p1 = Zm("2*x + 4", ctx) >>> p2 = Zm("3*x*y + 3*x + 3*y + 3", ctx) >>> (p1 * p2).factor_squarefree() @@ -1029,8 +956,7 @@ cdef class nmod_mpoly(flint_mpoly): The argument can either be the variable as a string, or the index of the variable in the context. - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, 'x') + >>> ctx = nmod_mpoly_ctx.get(('x', 2), 11, 'lex') >>> p = ctx.from_dict({(0, 3): 2, (2, 1): 3}) >>> p 3*x0^2*x1 + 2*x1^3 @@ -1054,8 +980,7 @@ cdef class nmod_mpoly(flint_mpoly): Compute the inflation of ``self`` for a provided ``N``, that is return ``q`` such that ``q(X) = p(X^N)``. - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, nametup=('x', 'y')) + >>> ctx = nmod_mpoly_ctx.get(('x', 'y'), 11, 'lex') >>> x, y = ctx.gens() >>> f = x + y + 1 >>> f.inflate([2, 3]) @@ -1082,8 +1007,7 @@ cdef class nmod_mpoly(flint_mpoly): Compute the deflation of ``self`` for a provided ``N``, that is return ``q`` such that ``q(X) = p(X^(1/N))``. - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, nametup=('x', 'y')) + >>> ctx = nmod_mpoly_ctx.get(('x', 'y'), 11, 'lex') >>> x, y = ctx.gens() >>> f = x**3 * y + x * y**4 + x * y >>> f.deflate([2, 3]) @@ -1106,8 +1030,7 @@ cdef class nmod_mpoly(flint_mpoly): """ Compute the deflation of ``self``, that is ``p(X^(1/N))`` for maximal N. - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, nametup=('x', 'y')) + >>> ctx = nmod_mpoly_ctx.get(('x', 'y'), 11, 'lex') >>> x, y = ctx.gens() >>> f = x**2 * y**2 + x * y**2 >>> q, N = f.deflation() @@ -1138,8 +1061,7 @@ cdef class nmod_mpoly(flint_mpoly): = m * q(X^N)`` for maximal N. The returned monomial allows the undo-ing of the deflation. - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, nametup=('x', 'y')) + >>> ctx = nmod_mpoly_ctx.get(('x', 'y'), 11, 'lex') >>> x, y = ctx.gens() >>> f = x**3 * y + x * y**4 + x * y >>> fd, N, m = f.deflation_monom() @@ -1169,8 +1091,7 @@ cdef class nmod_mpoly(flint_mpoly): exponents. It is the exponent vector of the monomial returned by ``deflation_monom``. - >>> from flint import Ordering - >>> ctx = nmod_mpoly_ctx.get_context(2, Ordering.lex, 11, nametup=('x', 'y')) + >>> ctx = nmod_mpoly_ctx.get(('x', 'y'), 11, 'lex') >>> x, y = ctx.gens() >>> f = x**3 * y + x * y**4 + x * y >>> N, I = f.deflation_index()