diff --git a/doc/src/modules/polys/agca.rst b/doc/src/modules/polys/agca.rst index 9800fa0e0fa1..032416d26405 100644 --- a/doc/src/modules/polys/agca.rst +++ b/doc/src/modules/polys/agca.rst @@ -309,3 +309,28 @@ Finally, here is the detailed reference of the actual homomorphism class: .. autoclass:: ModuleHomomorphism :members: + +Finite Extensions +----------------- + +Let $A$ be a (commutative) ring and $B$ an extension ring of $A$. +An element $t$ of $B$ is a generator of $B$ (over $A$) if all elements +of $B$ can be represented as polynomials in $t$ with coefficients +in $A$. The representation is unique if and only if $t$ satisfies no +non-trivial polynomial relation, in which case $B$ can be identified +with a (univariate) polynomial ring over $A$. + +The polynomials having $t$ as a root form a non-zero ideal in general. +The most important case in practice is that of an ideal generated by +a single monic polynomial. If $t$ satisfies such a polynomial relation, +then its highest power $t^n$ can be written as linear combination of +lower powers. It follows, inductively, that all higher powers of $t$ +also have such a representation. Hence the lower powers $t^i$ +($i = 0, \dots, n-1$) form a basis of $B$, which is then called a finite +extension of $A$, or, more precisely, a monogenic finite extension +as it is generated by a single element $t$. + +.. currentmodule:: sympy.polys.agca.extensions + +.. autoclass:: MonogenicFiniteExtension + :members: diff --git a/sympy/polys/agca/extensions.py b/sympy/polys/agca/extensions.py index fe9c4b83df67..b991829afd98 100644 --- a/sympy/polys/agca/extensions.py +++ b/sympy/polys/agca/extensions.py @@ -1,8 +1,6 @@ """Finite extensions of ring domains.""" -from __future__ import print_function, division - -from sympy.polys.polyerrors import CoercionFailed +from sympy.polys.polyerrors import CoercionFailed, NotInvertible from sympy.polys.polytools import Poly from sympy.printing.defaults import DefaultPrinting @@ -72,11 +70,57 @@ def __mul__(f, g): __rmul__ = __mul__ + def inverse(f): + """Multiplicative inverse. + + Raises + ====== + NotInvertible + If the element is a zero divisor. + + """ + if not f.ext.domain.is_Field: + raise NotImplementedError("base field expected") + return ExtElem(f.rep.invert(f.ext.mod), f.ext) + + def _invrep(f, g): + rep = f._get_rep(g) + if rep is not None: + return rep.invert(f.ext.mod) + else: + return None + + def __truediv__(f, g): + if not f.ext.domain.is_Field: + return NotImplemented + try: + rep = f._invrep(g) + except NotInvertible: + raise ZeroDivisionError + + if rep is not None: + return f*ExtElem(rep, f.ext) + else: + return NotImplemented + + __floordiv__ = __truediv__ + + def __rtruediv__(f, g): + try: + return f.ext.convert(g)/f + except CoercionFailed: + return NotImplemented + + __rfloordiv__ = __rtruediv__ + def __pow__(f, n): if not isinstance(n, int): raise TypeError("exponent of type 'int' expected") if n < 0: - raise ValueError("negative powers are not defined") + try: + f, n = f.inverse(), -n + except NotImplementedError: + raise ValueError("negative powers are not defined") b = f.rep m = f.ext.mod @@ -111,12 +155,52 @@ def __str__(f): class MonogenicFiniteExtension(object): - """ + 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) + + Notes + ===== + + ``FiniteExtension`` is not a subclass of :class:`~.Domain`. Consequently, + a ``FiniteExtension`` can't currently be used as ``domain`` for the + :class:`~.Poly` class. + """ def __init__(self, mod): if not (isinstance(mod, Poly) and mod.is_univariate): diff --git a/sympy/polys/agca/tests/test_extensions.py b/sympy/polys/agca/tests/test_extensions.py index a995f701ca4a..432687e2cf20 100644 --- a/sympy/polys/agca/tests/test_extensions.py +++ b/sympy/polys/agca/tests/test_extensions.py @@ -1,5 +1,7 @@ from sympy.polys.polytools import Poly +from sympy.polys.polyerrors import NotInvertible from sympy.polys.agca.extensions import FiniteExtension +from sympy.testing.pytest import raises from sympy.abc import x, t def test_FiniteExtension(): @@ -14,6 +16,7 @@ def test_FiniteExtension(): assert i**2 != -1 # no coercion assert (2 + i)*(1 - i) == 3 - i assert (1 + i)**8 == A(16) + raises(NotImplementedError, lambda: A(1).inverse()) # Finite field of order 27 F = FiniteExtension(Poly(x**3 - x + 1, x, modulus=3)) @@ -26,6 +29,14 @@ def test_FiniteExtension(): assert a**9 == a + 1 assert a**3 == a - 1 assert a**6 == a**2 + a + 1 + assert F(x**2 + x).inverse() == 1 - a + assert F(x + 2)**(-1) == F(x + 2).inverse() + assert a**19 * a**(-19) == F(1) + assert (a - 1) / (2*a**2 - 1) == a**2 + 1 + assert (a - 1) // (2*a**2 - 1) == a**2 + 1 + assert 2/(a**2 + 1) == a**2 - a + 1 + assert (a**2 + 1)/2 == -a**2 - 1 + raises(NotInvertible, lambda: F(0).inverse()) # Function field of an elliptic curve K = FiniteExtension(Poly(t**2 - x**3 - x + 1, t, field=True)) @@ -33,4 +44,5 @@ def test_FiniteExtension(): assert str(K) == 'ZZ(x)[t]/(t**2 - x**3 - x + 1)' y = K.generator c = 1/(x**3 - x**2 + x - 1) + assert ((y + x)*(y - x)).inverse() == K(c) assert (y + x)*(y - x)*c == K(1) # explicit inverse of y + x