Traktor/myenv/Lib/site-packages/sympy/polys/agca/extensions.py

347 lines
9.0 KiB
Python
Raw Permalink Normal View History

2024-05-26 05:12:46 +02:00
"""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