123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346 |
- """Finite extensions of ring domains."""
- from sympy.polys.domains.domain import Domain
- from sympy.polys.domains.domainelement import DomainElement
- from sympy.polys.polyerrors import (CoercionFailed, NotInvertible,
- GeneratorsError)
- from sympy.polys.polytools import Poly
- from sympy.printing.defaults import DefaultPrinting
- class ExtensionElement(DomainElement, DefaultPrinting):
- """
- Element of a finite extension.
- A class of univariate polynomials modulo the ``modulus``
- of the extension ``ext``. It is represented by the
- unique polynomial ``rep`` of lowest degree. Both
- ``rep`` and the representation ``mod`` of ``modulus``
- are of class DMP.
- """
- __slots__ = ('rep', 'ext')
- def __init__(self, rep, ext):
- self.rep = rep
- self.ext = ext
- def parent(f):
- return f.ext
- def __bool__(f):
- return bool(f.rep)
- def __pos__(f):
- return f
- def __neg__(f):
- return ExtElem(-f.rep, f.ext)
- def _get_rep(f, g):
- if isinstance(g, ExtElem):
- if g.ext == f.ext:
- return g.rep
- else:
- return None
- else:
- try:
- g = f.ext.convert(g)
- return g.rep
- except CoercionFailed:
- return None
- def __add__(f, g):
- rep = f._get_rep(g)
- if rep is not None:
- return ExtElem(f.rep + rep, f.ext)
- else:
- return NotImplemented
- __radd__ = __add__
- def __sub__(f, g):
- rep = f._get_rep(g)
- if rep is not None:
- return ExtElem(f.rep - rep, f.ext)
- else:
- return NotImplemented
- def __rsub__(f, g):
- rep = f._get_rep(g)
- if rep is not None:
- return ExtElem(rep - f.rep, f.ext)
- else:
- return NotImplemented
- def __mul__(f, g):
- rep = f._get_rep(g)
- if rep is not None:
- return ExtElem((f.rep * rep) % f.ext.mod, f.ext)
- else:
- return NotImplemented
- __rmul__ = __mul__
- def _divcheck(f):
- """Raise if division is not implemented for this divisor"""
- if not f:
- raise NotInvertible('Zero divisor')
- elif f.ext.is_Field:
- return True
- elif f.rep.is_ground and f.ext.domain.is_unit(f.rep.rep[0]):
- return True
- else:
- # Some cases like (2*x + 2)/2 over ZZ will fail here. It is
- # unclear how to implement division in general if the ground
- # domain is not a field so for now it was decided to restrict the
- # implementation to division by invertible constants.
- msg = (f"Can not invert {f} in {f.ext}. "
- "Only division by invertible constants is implemented.")
- raise NotImplementedError(msg)
- def inverse(f):
- """Multiplicative inverse.
- Raises
- ======
- NotInvertible
- If the element is a zero divisor.
- """
- f._divcheck()
- if f.ext.is_Field:
- invrep = f.rep.invert(f.ext.mod)
- else:
- R = f.ext.ring
- invrep = R.exquo(R.one, f.rep)
- return ExtElem(invrep, f.ext)
- def __truediv__(f, g):
- rep = f._get_rep(g)
- if rep is None:
- return NotImplemented
- g = ExtElem(rep, f.ext)
- try:
- ginv = g.inverse()
- except NotInvertible:
- raise ZeroDivisionError(f"{f} / {g}")
- return f * ginv
- __floordiv__ = __truediv__
- def __rtruediv__(f, g):
- try:
- g = f.ext.convert(g)
- except CoercionFailed:
- return NotImplemented
- return g / f
- __rfloordiv__ = __rtruediv__
- def __mod__(f, g):
- rep = f._get_rep(g)
- if rep is None:
- return NotImplemented
- g = ExtElem(rep, f.ext)
- try:
- g._divcheck()
- except NotInvertible:
- raise ZeroDivisionError(f"{f} % {g}")
- # Division where defined is always exact so there is no remainder
- return f.ext.zero
- def __rmod__(f, g):
- try:
- g = f.ext.convert(g)
- except CoercionFailed:
- return NotImplemented
- return g % f
- def __pow__(f, n):
- if not isinstance(n, int):
- raise TypeError("exponent of type 'int' expected")
- if n < 0:
- try:
- f, n = f.inverse(), -n
- except NotImplementedError:
- raise ValueError("negative powers are not defined")
- b = f.rep
- m = f.ext.mod
- r = f.ext.one.rep
- while n > 0:
- if n % 2:
- r = (r*b) % m
- b = (b*b) % m
- n //= 2
- return ExtElem(r, f.ext)
- def __eq__(f, g):
- if isinstance(g, ExtElem):
- return f.rep == g.rep and f.ext == g.ext
- else:
- return NotImplemented
- def __ne__(f, g):
- return not f == g
- def __hash__(f):
- return hash((f.rep, f.ext))
- def __str__(f):
- from sympy.printing.str import sstr
- return sstr(f.rep)
- __repr__ = __str__
- @property
- def is_ground(f):
- return f.rep.is_ground
- def to_ground(f):
- [c] = f.rep.to_list()
- return c
- ExtElem = ExtensionElement
- class MonogenicFiniteExtension(Domain):
- r"""
- Finite extension generated by an integral element.
- The generator is defined by a monic univariate
- polynomial derived from the argument ``mod``.
- A shorter alias is ``FiniteExtension``.
- Examples
- ========
- Quadratic integer ring $\mathbb{Z}[\sqrt2]$:
- >>> from sympy import Symbol, Poly
- >>> from sympy.polys.agca.extensions import FiniteExtension
- >>> x = Symbol('x')
- >>> R = FiniteExtension(Poly(x**2 - 2)); R
- ZZ[x]/(x**2 - 2)
- >>> R.rank
- 2
- >>> R(1 + x)*(3 - 2*x)
- x - 1
- Finite field $GF(5^3)$ defined by the primitive
- polynomial $x^3 + x^2 + 2$ (over $\mathbb{Z}_5$).
- >>> F = FiniteExtension(Poly(x**3 + x**2 + 2, modulus=5)); F
- GF(5)[x]/(x**3 + x**2 + 2)
- >>> F.basis
- (1, x, x**2)
- >>> F(x + 3)/(x**2 + 2)
- -2*x**2 + x + 2
- Function field of an elliptic curve:
- >>> t = Symbol('t')
- >>> FiniteExtension(Poly(t**2 - x**3 - x + 1, t, field=True))
- ZZ(x)[t]/(t**2 - x**3 - x + 1)
- """
- is_FiniteExtension = True
- dtype = ExtensionElement
- def __init__(self, mod):
- if not (isinstance(mod, Poly) and mod.is_univariate):
- raise TypeError("modulus must be a univariate Poly")
- # Using auto=True (default) potentially changes the ground domain to a
- # field whereas auto=False raises if division is not exact. We'll let
- # the caller decide whether or not they want to put the ground domain
- # over a field. In most uses mod is already monic.
- mod = mod.monic(auto=False)
- self.rank = mod.degree()
- self.modulus = mod
- self.mod = mod.rep # DMP representation
- self.domain = dom = mod.domain
- self.ring = mod.rep.ring or dom.old_poly_ring(*mod.gens)
- self.zero = self.convert(self.ring.zero)
- self.one = self.convert(self.ring.one)
- gen = self.ring.gens[0]
- self.symbol = self.ring.symbols[0]
- self.generator = self.convert(gen)
- self.basis = tuple(self.convert(gen**i) for i in range(self.rank))
- # XXX: It might be necessary to check mod.is_irreducible here
- self.is_Field = self.domain.is_Field
- def new(self, arg):
- rep = self.ring.convert(arg)
- return ExtElem(rep % self.mod, self)
- def __eq__(self, other):
- if not isinstance(other, FiniteExtension):
- return False
- return self.modulus == other.modulus
- def __hash__(self):
- return hash((self.__class__.__name__, self.modulus))
- def __str__(self):
- return "%s/(%s)" % (self.ring, self.modulus.as_expr())
- __repr__ = __str__
- def convert(self, f, base=None):
- rep = self.ring.convert(f, base)
- return ExtElem(rep % self.mod, self)
- def convert_from(self, f, base):
- rep = self.ring.convert(f, base)
- return ExtElem(rep % self.mod, self)
- def to_sympy(self, f):
- return self.ring.to_sympy(f.rep)
- def from_sympy(self, f):
- return self.convert(f)
- def set_domain(self, K):
- mod = self.modulus.set_domain(K)
- return self.__class__(mod)
- def drop(self, *symbols):
- if self.symbol in symbols:
- raise GeneratorsError('Can not drop generator from FiniteExtension')
- K = self.domain.drop(*symbols)
- return self.set_domain(K)
- def quo(self, f, g):
- return self.exquo(f, g)
- def exquo(self, f, g):
- rep = self.ring.exquo(f.rep, g.rep)
- return ExtElem(rep % self.mod, self)
- def is_negative(self, a):
- return False
- def is_unit(self, a):
- if self.is_Field:
- return bool(a)
- elif a.is_ground:
- return self.domain.is_unit(a.to_ground())
- FiniteExtension = MonogenicFiniteExtension
|