switching to high quality piper tts and added label translations
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
"""Module for algebraic geometry and commutative algebra."""
|
||||
|
||||
from .homomorphisms import homomorphism
|
||||
|
||||
__all__ = ['homomorphism']
|
||||
@@ -0,0 +1,356 @@
|
||||
"""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 as_expr(f):
|
||||
return f.ext.to_sympy(f)
|
||||
|
||||
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.LC()):
|
||||
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.as_expr())
|
||||
|
||||
__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 = 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__
|
||||
|
||||
@property
|
||||
def has_CharacteristicZero(self):
|
||||
return self.domain.has_CharacteristicZero
|
||||
|
||||
def characteristic(self):
|
||||
return self.domain.characteristic()
|
||||
|
||||
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
|
||||
@@ -0,0 +1,691 @@
|
||||
"""
|
||||
Computations with homomorphisms of modules and rings.
|
||||
|
||||
This module implements classes for representing homomorphisms of rings and
|
||||
their modules. Instead of instantiating the classes directly, you should use
|
||||
the function ``homomorphism(from, to, matrix)`` to create homomorphism objects.
|
||||
"""
|
||||
|
||||
|
||||
from sympy.polys.agca.modules import (Module, FreeModule, QuotientModule,
|
||||
SubModule, SubQuotientModule)
|
||||
from sympy.polys.polyerrors import CoercionFailed
|
||||
|
||||
# The main computational task for module homomorphisms is kernels.
|
||||
# For this reason, the concrete classes are organised by domain module type.
|
||||
|
||||
|
||||
class ModuleHomomorphism:
|
||||
"""
|
||||
Abstract base class for module homomoprhisms. Do not instantiate.
|
||||
|
||||
Instead, use the ``homomorphism`` function:
|
||||
|
||||
>>> from sympy import QQ
|
||||
>>> from sympy.abc import x
|
||||
>>> from sympy.polys.agca import homomorphism
|
||||
|
||||
>>> F = QQ.old_poly_ring(x).free_module(2)
|
||||
>>> homomorphism(F, F, [[1, 0], [0, 1]])
|
||||
Matrix([
|
||||
[1, 0], : QQ[x]**2 -> QQ[x]**2
|
||||
[0, 1]])
|
||||
|
||||
Attributes:
|
||||
|
||||
- ring - the ring over which we are considering modules
|
||||
- domain - the domain module
|
||||
- codomain - the codomain module
|
||||
- _ker - cached kernel
|
||||
- _img - cached image
|
||||
|
||||
Non-implemented methods:
|
||||
|
||||
- _kernel
|
||||
- _image
|
||||
- _restrict_domain
|
||||
- _restrict_codomain
|
||||
- _quotient_domain
|
||||
- _quotient_codomain
|
||||
- _apply
|
||||
- _mul_scalar
|
||||
- _compose
|
||||
- _add
|
||||
"""
|
||||
|
||||
def __init__(self, domain, codomain):
|
||||
if not isinstance(domain, Module):
|
||||
raise TypeError('Source must be a module, got %s' % domain)
|
||||
if not isinstance(codomain, Module):
|
||||
raise TypeError('Target must be a module, got %s' % codomain)
|
||||
if domain.ring != codomain.ring:
|
||||
raise ValueError('Source and codomain must be over same ring, '
|
||||
'got %s != %s' % (domain, codomain))
|
||||
self.domain = domain
|
||||
self.codomain = codomain
|
||||
self.ring = domain.ring
|
||||
self._ker = None
|
||||
self._img = None
|
||||
|
||||
def kernel(self):
|
||||
r"""
|
||||
Compute the kernel of ``self``.
|
||||
|
||||
That is, if ``self`` is the homomorphism `\phi: M \to N`, then compute
|
||||
`ker(\phi) = \{x \in M | \phi(x) = 0\}`. This is a submodule of `M`.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import QQ
|
||||
>>> from sympy.abc import x
|
||||
>>> from sympy.polys.agca import homomorphism
|
||||
|
||||
>>> F = QQ.old_poly_ring(x).free_module(2)
|
||||
>>> homomorphism(F, F, [[1, 0], [x, 0]]).kernel()
|
||||
<[x, -1]>
|
||||
"""
|
||||
if self._ker is None:
|
||||
self._ker = self._kernel()
|
||||
return self._ker
|
||||
|
||||
def image(self):
|
||||
r"""
|
||||
Compute the image of ``self``.
|
||||
|
||||
That is, if ``self`` is the homomorphism `\phi: M \to N`, then compute
|
||||
`im(\phi) = \{\phi(x) | x \in M \}`. This is a submodule of `N`.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import QQ
|
||||
>>> from sympy.abc import x
|
||||
>>> from sympy.polys.agca import homomorphism
|
||||
|
||||
>>> F = QQ.old_poly_ring(x).free_module(2)
|
||||
>>> homomorphism(F, F, [[1, 0], [x, 0]]).image() == F.submodule([1, 0])
|
||||
True
|
||||
"""
|
||||
if self._img is None:
|
||||
self._img = self._image()
|
||||
return self._img
|
||||
|
||||
def _kernel(self):
|
||||
"""Compute the kernel of ``self``."""
|
||||
raise NotImplementedError
|
||||
|
||||
def _image(self):
|
||||
"""Compute the image of ``self``."""
|
||||
raise NotImplementedError
|
||||
|
||||
def _restrict_domain(self, sm):
|
||||
"""Implementation of domain restriction."""
|
||||
raise NotImplementedError
|
||||
|
||||
def _restrict_codomain(self, sm):
|
||||
"""Implementation of codomain restriction."""
|
||||
raise NotImplementedError
|
||||
|
||||
def _quotient_domain(self, sm):
|
||||
"""Implementation of domain quotient."""
|
||||
raise NotImplementedError
|
||||
|
||||
def _quotient_codomain(self, sm):
|
||||
"""Implementation of codomain quotient."""
|
||||
raise NotImplementedError
|
||||
|
||||
def restrict_domain(self, sm):
|
||||
"""
|
||||
Return ``self``, with the domain restricted to ``sm``.
|
||||
|
||||
Here ``sm`` has to be a submodule of ``self.domain``.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import QQ
|
||||
>>> from sympy.abc import x
|
||||
>>> from sympy.polys.agca import homomorphism
|
||||
|
||||
>>> F = QQ.old_poly_ring(x).free_module(2)
|
||||
>>> h = homomorphism(F, F, [[1, 0], [x, 0]])
|
||||
>>> h
|
||||
Matrix([
|
||||
[1, x], : QQ[x]**2 -> QQ[x]**2
|
||||
[0, 0]])
|
||||
>>> h.restrict_domain(F.submodule([1, 0]))
|
||||
Matrix([
|
||||
[1, x], : <[1, 0]> -> QQ[x]**2
|
||||
[0, 0]])
|
||||
|
||||
This is the same as just composing on the right with the submodule
|
||||
inclusion:
|
||||
|
||||
>>> h * F.submodule([1, 0]).inclusion_hom()
|
||||
Matrix([
|
||||
[1, x], : <[1, 0]> -> QQ[x]**2
|
||||
[0, 0]])
|
||||
"""
|
||||
if not self.domain.is_submodule(sm):
|
||||
raise ValueError('sm must be a submodule of %s, got %s'
|
||||
% (self.domain, sm))
|
||||
if sm == self.domain:
|
||||
return self
|
||||
return self._restrict_domain(sm)
|
||||
|
||||
def restrict_codomain(self, sm):
|
||||
"""
|
||||
Return ``self``, with codomain restricted to to ``sm``.
|
||||
|
||||
Here ``sm`` has to be a submodule of ``self.codomain`` containing the
|
||||
image.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import QQ
|
||||
>>> from sympy.abc import x
|
||||
>>> from sympy.polys.agca import homomorphism
|
||||
|
||||
>>> F = QQ.old_poly_ring(x).free_module(2)
|
||||
>>> h = homomorphism(F, F, [[1, 0], [x, 0]])
|
||||
>>> h
|
||||
Matrix([
|
||||
[1, x], : QQ[x]**2 -> QQ[x]**2
|
||||
[0, 0]])
|
||||
>>> h.restrict_codomain(F.submodule([1, 0]))
|
||||
Matrix([
|
||||
[1, x], : QQ[x]**2 -> <[1, 0]>
|
||||
[0, 0]])
|
||||
"""
|
||||
if not sm.is_submodule(self.image()):
|
||||
raise ValueError('the image %s must contain sm, got %s'
|
||||
% (self.image(), sm))
|
||||
if sm == self.codomain:
|
||||
return self
|
||||
return self._restrict_codomain(sm)
|
||||
|
||||
def quotient_domain(self, sm):
|
||||
"""
|
||||
Return ``self`` with domain replaced by ``domain/sm``.
|
||||
|
||||
Here ``sm`` must be a submodule of ``self.kernel()``.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import QQ
|
||||
>>> from sympy.abc import x
|
||||
>>> from sympy.polys.agca import homomorphism
|
||||
|
||||
>>> F = QQ.old_poly_ring(x).free_module(2)
|
||||
>>> h = homomorphism(F, F, [[1, 0], [x, 0]])
|
||||
>>> h
|
||||
Matrix([
|
||||
[1, x], : QQ[x]**2 -> QQ[x]**2
|
||||
[0, 0]])
|
||||
>>> h.quotient_domain(F.submodule([-x, 1]))
|
||||
Matrix([
|
||||
[1, x], : QQ[x]**2/<[-x, 1]> -> QQ[x]**2
|
||||
[0, 0]])
|
||||
"""
|
||||
if not self.kernel().is_submodule(sm):
|
||||
raise ValueError('kernel %s must contain sm, got %s' %
|
||||
(self.kernel(), sm))
|
||||
if sm.is_zero():
|
||||
return self
|
||||
return self._quotient_domain(sm)
|
||||
|
||||
def quotient_codomain(self, sm):
|
||||
"""
|
||||
Return ``self`` with codomain replaced by ``codomain/sm``.
|
||||
|
||||
Here ``sm`` must be a submodule of ``self.codomain``.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import QQ
|
||||
>>> from sympy.abc import x
|
||||
>>> from sympy.polys.agca import homomorphism
|
||||
|
||||
>>> F = QQ.old_poly_ring(x).free_module(2)
|
||||
>>> h = homomorphism(F, F, [[1, 0], [x, 0]])
|
||||
>>> h
|
||||
Matrix([
|
||||
[1, x], : QQ[x]**2 -> QQ[x]**2
|
||||
[0, 0]])
|
||||
>>> h.quotient_codomain(F.submodule([1, 1]))
|
||||
Matrix([
|
||||
[1, x], : QQ[x]**2 -> QQ[x]**2/<[1, 1]>
|
||||
[0, 0]])
|
||||
|
||||
This is the same as composing with the quotient map on the left:
|
||||
|
||||
>>> (F/[(1, 1)]).quotient_hom() * h
|
||||
Matrix([
|
||||
[1, x], : QQ[x]**2 -> QQ[x]**2/<[1, 1]>
|
||||
[0, 0]])
|
||||
"""
|
||||
if not self.codomain.is_submodule(sm):
|
||||
raise ValueError('sm must be a submodule of codomain %s, got %s'
|
||||
% (self.codomain, sm))
|
||||
if sm.is_zero():
|
||||
return self
|
||||
return self._quotient_codomain(sm)
|
||||
|
||||
def _apply(self, elem):
|
||||
"""Apply ``self`` to ``elem``."""
|
||||
raise NotImplementedError
|
||||
|
||||
def __call__(self, elem):
|
||||
return self.codomain.convert(self._apply(self.domain.convert(elem)))
|
||||
|
||||
def _compose(self, oth):
|
||||
"""
|
||||
Compose ``self`` with ``oth``, that is, return the homomorphism
|
||||
obtained by first applying then ``self``, then ``oth``.
|
||||
|
||||
(This method is private since in this syntax, it is non-obvious which
|
||||
homomorphism is executed first.)
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def _mul_scalar(self, c):
|
||||
"""Scalar multiplication. ``c`` is guaranteed in self.ring."""
|
||||
raise NotImplementedError
|
||||
|
||||
def _add(self, oth):
|
||||
"""
|
||||
Homomorphism addition.
|
||||
``oth`` is guaranteed to be a homomorphism with same domain/codomain.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
|
||||
def _check_hom(self, oth):
|
||||
"""Helper to check that oth is a homomorphism with same domain/codomain."""
|
||||
if not isinstance(oth, ModuleHomomorphism):
|
||||
return False
|
||||
return oth.domain == self.domain and oth.codomain == self.codomain
|
||||
|
||||
def __mul__(self, oth):
|
||||
if isinstance(oth, ModuleHomomorphism) and self.domain == oth.codomain:
|
||||
return oth._compose(self)
|
||||
try:
|
||||
return self._mul_scalar(self.ring.convert(oth))
|
||||
except CoercionFailed:
|
||||
return NotImplemented
|
||||
|
||||
# NOTE: _compose will never be called from rmul
|
||||
__rmul__ = __mul__
|
||||
|
||||
def __truediv__(self, oth):
|
||||
try:
|
||||
return self._mul_scalar(1/self.ring.convert(oth))
|
||||
except CoercionFailed:
|
||||
return NotImplemented
|
||||
|
||||
def __add__(self, oth):
|
||||
if self._check_hom(oth):
|
||||
return self._add(oth)
|
||||
return NotImplemented
|
||||
|
||||
def __sub__(self, oth):
|
||||
if self._check_hom(oth):
|
||||
return self._add(oth._mul_scalar(self.ring.convert(-1)))
|
||||
return NotImplemented
|
||||
|
||||
def is_injective(self):
|
||||
"""
|
||||
Return True if ``self`` is injective.
|
||||
|
||||
That is, check if the elements of the domain are mapped to the same
|
||||
codomain element.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import QQ
|
||||
>>> from sympy.abc import x
|
||||
>>> from sympy.polys.agca import homomorphism
|
||||
|
||||
>>> F = QQ.old_poly_ring(x).free_module(2)
|
||||
>>> h = homomorphism(F, F, [[1, 0], [x, 0]])
|
||||
>>> h.is_injective()
|
||||
False
|
||||
>>> h.quotient_domain(h.kernel()).is_injective()
|
||||
True
|
||||
"""
|
||||
return self.kernel().is_zero()
|
||||
|
||||
def is_surjective(self):
|
||||
"""
|
||||
Return True if ``self`` is surjective.
|
||||
|
||||
That is, check if every element of the codomain has at least one
|
||||
preimage.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import QQ
|
||||
>>> from sympy.abc import x
|
||||
>>> from sympy.polys.agca import homomorphism
|
||||
|
||||
>>> F = QQ.old_poly_ring(x).free_module(2)
|
||||
>>> h = homomorphism(F, F, [[1, 0], [x, 0]])
|
||||
>>> h.is_surjective()
|
||||
False
|
||||
>>> h.restrict_codomain(h.image()).is_surjective()
|
||||
True
|
||||
"""
|
||||
return self.image() == self.codomain
|
||||
|
||||
def is_isomorphism(self):
|
||||
"""
|
||||
Return True if ``self`` is an isomorphism.
|
||||
|
||||
That is, check if every element of the codomain has precisely one
|
||||
preimage. Equivalently, ``self`` is both injective and surjective.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import QQ
|
||||
>>> from sympy.abc import x
|
||||
>>> from sympy.polys.agca import homomorphism
|
||||
|
||||
>>> F = QQ.old_poly_ring(x).free_module(2)
|
||||
>>> h = homomorphism(F, F, [[1, 0], [x, 0]])
|
||||
>>> h = h.restrict_codomain(h.image())
|
||||
>>> h.is_isomorphism()
|
||||
False
|
||||
>>> h.quotient_domain(h.kernel()).is_isomorphism()
|
||||
True
|
||||
"""
|
||||
return self.is_injective() and self.is_surjective()
|
||||
|
||||
def is_zero(self):
|
||||
"""
|
||||
Return True if ``self`` is a zero morphism.
|
||||
|
||||
That is, check if every element of the domain is mapped to zero
|
||||
under self.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import QQ
|
||||
>>> from sympy.abc import x
|
||||
>>> from sympy.polys.agca import homomorphism
|
||||
|
||||
>>> F = QQ.old_poly_ring(x).free_module(2)
|
||||
>>> h = homomorphism(F, F, [[1, 0], [x, 0]])
|
||||
>>> h.is_zero()
|
||||
False
|
||||
>>> h.restrict_domain(F.submodule()).is_zero()
|
||||
True
|
||||
>>> h.quotient_codomain(h.image()).is_zero()
|
||||
True
|
||||
"""
|
||||
return self.image().is_zero()
|
||||
|
||||
def __eq__(self, oth):
|
||||
try:
|
||||
return (self - oth).is_zero()
|
||||
except TypeError:
|
||||
return False
|
||||
|
||||
def __ne__(self, oth):
|
||||
return not (self == oth)
|
||||
|
||||
|
||||
class MatrixHomomorphism(ModuleHomomorphism):
|
||||
r"""
|
||||
Helper class for all homomoprhisms which are expressed via a matrix.
|
||||
|
||||
That is, for such homomorphisms ``domain`` is contained in a module
|
||||
generated by finitely many elements `e_1, \ldots, e_n`, so that the
|
||||
homomorphism is determined uniquely by its action on the `e_i`. It
|
||||
can thus be represented as a vector of elements of the codomain module,
|
||||
or potentially a supermodule of the codomain module
|
||||
(and hence conventionally as a matrix, if there is a similar interpretation
|
||||
for elements of the codomain module).
|
||||
|
||||
Note that this class does *not* assume that the `e_i` freely generate a
|
||||
submodule, nor that ``domain`` is even all of this submodule. It exists
|
||||
only to unify the interface.
|
||||
|
||||
Do not instantiate.
|
||||
|
||||
Attributes:
|
||||
|
||||
- matrix - the list of images determining the homomorphism.
|
||||
NOTE: the elements of matrix belong to either self.codomain or
|
||||
self.codomain.container
|
||||
|
||||
Still non-implemented methods:
|
||||
|
||||
- kernel
|
||||
- _apply
|
||||
"""
|
||||
|
||||
def __init__(self, domain, codomain, matrix):
|
||||
ModuleHomomorphism.__init__(self, domain, codomain)
|
||||
if len(matrix) != domain.rank:
|
||||
raise ValueError('Need to provide %s elements, got %s'
|
||||
% (domain.rank, len(matrix)))
|
||||
|
||||
converter = self.codomain.convert
|
||||
if isinstance(self.codomain, (SubModule, SubQuotientModule)):
|
||||
converter = self.codomain.container.convert
|
||||
self.matrix = tuple(converter(x) for x in matrix)
|
||||
|
||||
def _sympy_matrix(self):
|
||||
"""Helper function which returns a SymPy matrix ``self.matrix``."""
|
||||
from sympy.matrices import Matrix
|
||||
c = lambda x: x
|
||||
if isinstance(self.codomain, (QuotientModule, SubQuotientModule)):
|
||||
c = lambda x: x.data
|
||||
return Matrix([[self.ring.to_sympy(y) for y in c(x)] for x in self.matrix]).T
|
||||
|
||||
def __repr__(self):
|
||||
lines = repr(self._sympy_matrix()).split('\n')
|
||||
t = " : %s -> %s" % (self.domain, self.codomain)
|
||||
s = ' '*len(t)
|
||||
n = len(lines)
|
||||
for i in range(n // 2):
|
||||
lines[i] += s
|
||||
lines[n // 2] += t
|
||||
for i in range(n//2 + 1, n):
|
||||
lines[i] += s
|
||||
return '\n'.join(lines)
|
||||
|
||||
def _restrict_domain(self, sm):
|
||||
"""Implementation of domain restriction."""
|
||||
return SubModuleHomomorphism(sm, self.codomain, self.matrix)
|
||||
|
||||
def _restrict_codomain(self, sm):
|
||||
"""Implementation of codomain restriction."""
|
||||
return self.__class__(self.domain, sm, self.matrix)
|
||||
|
||||
def _quotient_domain(self, sm):
|
||||
"""Implementation of domain quotient."""
|
||||
return self.__class__(self.domain/sm, self.codomain, self.matrix)
|
||||
|
||||
def _quotient_codomain(self, sm):
|
||||
"""Implementation of codomain quotient."""
|
||||
Q = self.codomain/sm
|
||||
converter = Q.convert
|
||||
if isinstance(self.codomain, SubModule):
|
||||
converter = Q.container.convert
|
||||
return self.__class__(self.domain, self.codomain/sm,
|
||||
[converter(x) for x in self.matrix])
|
||||
|
||||
def _add(self, oth):
|
||||
return self.__class__(self.domain, self.codomain,
|
||||
[x + y for x, y in zip(self.matrix, oth.matrix)])
|
||||
|
||||
def _mul_scalar(self, c):
|
||||
return self.__class__(self.domain, self.codomain, [c*x for x in self.matrix])
|
||||
|
||||
def _compose(self, oth):
|
||||
return self.__class__(self.domain, oth.codomain, [oth(x) for x in self.matrix])
|
||||
|
||||
|
||||
class FreeModuleHomomorphism(MatrixHomomorphism):
|
||||
"""
|
||||
Concrete class for homomorphisms with domain a free module or a quotient
|
||||
thereof.
|
||||
|
||||
Do not instantiate; the constructor does not check that your data is well
|
||||
defined. Use the ``homomorphism`` function instead:
|
||||
|
||||
>>> from sympy import QQ
|
||||
>>> from sympy.abc import x
|
||||
>>> from sympy.polys.agca import homomorphism
|
||||
|
||||
>>> F = QQ.old_poly_ring(x).free_module(2)
|
||||
>>> homomorphism(F, F, [[1, 0], [0, 1]])
|
||||
Matrix([
|
||||
[1, 0], : QQ[x]**2 -> QQ[x]**2
|
||||
[0, 1]])
|
||||
"""
|
||||
|
||||
def _apply(self, elem):
|
||||
if isinstance(self.domain, QuotientModule):
|
||||
elem = elem.data
|
||||
return sum(x * e for x, e in zip(elem, self.matrix))
|
||||
|
||||
def _image(self):
|
||||
return self.codomain.submodule(*self.matrix)
|
||||
|
||||
def _kernel(self):
|
||||
# The domain is either a free module or a quotient thereof.
|
||||
# It does not matter if it is a quotient, because that won't increase
|
||||
# the kernel.
|
||||
# Our generators {e_i} are sent to the matrix entries {b_i}.
|
||||
# The kernel is essentially the syzygy module of these {b_i}.
|
||||
syz = self.image().syzygy_module()
|
||||
return self.domain.submodule(*syz.gens)
|
||||
|
||||
|
||||
class SubModuleHomomorphism(MatrixHomomorphism):
|
||||
"""
|
||||
Concrete class for homomorphism with domain a submodule of a free module
|
||||
or a quotient thereof.
|
||||
|
||||
Do not instantiate; the constructor does not check that your data is well
|
||||
defined. Use the ``homomorphism`` function instead:
|
||||
|
||||
>>> from sympy import QQ
|
||||
>>> from sympy.abc import x
|
||||
>>> from sympy.polys.agca import homomorphism
|
||||
|
||||
>>> M = QQ.old_poly_ring(x).free_module(2)*x
|
||||
>>> homomorphism(M, M, [[1, 0], [0, 1]])
|
||||
Matrix([
|
||||
[1, 0], : <[x, 0], [0, x]> -> <[x, 0], [0, x]>
|
||||
[0, 1]])
|
||||
"""
|
||||
|
||||
def _apply(self, elem):
|
||||
if isinstance(self.domain, SubQuotientModule):
|
||||
elem = elem.data
|
||||
return sum(x * e for x, e in zip(elem, self.matrix))
|
||||
|
||||
def _image(self):
|
||||
return self.codomain.submodule(*[self(x) for x in self.domain.gens])
|
||||
|
||||
def _kernel(self):
|
||||
syz = self.image().syzygy_module()
|
||||
return self.domain.submodule(
|
||||
*[sum(xi*gi for xi, gi in zip(s, self.domain.gens))
|
||||
for s in syz.gens])
|
||||
|
||||
|
||||
def homomorphism(domain, codomain, matrix):
|
||||
r"""
|
||||
Create a homomorphism object.
|
||||
|
||||
This function tries to build a homomorphism from ``domain`` to ``codomain``
|
||||
via the matrix ``matrix``.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import QQ
|
||||
>>> from sympy.abc import x
|
||||
>>> from sympy.polys.agca import homomorphism
|
||||
|
||||
>>> R = QQ.old_poly_ring(x)
|
||||
>>> T = R.free_module(2)
|
||||
|
||||
If ``domain`` is a free module generated by `e_1, \ldots, e_n`, then
|
||||
``matrix`` should be an n-element iterable `(b_1, \ldots, b_n)` where
|
||||
the `b_i` are elements of ``codomain``. The constructed homomorphism is the
|
||||
unique homomorphism sending `e_i` to `b_i`.
|
||||
|
||||
>>> F = R.free_module(2)
|
||||
>>> h = homomorphism(F, T, [[1, x], [x**2, 0]])
|
||||
>>> h
|
||||
Matrix([
|
||||
[1, x**2], : QQ[x]**2 -> QQ[x]**2
|
||||
[x, 0]])
|
||||
>>> h([1, 0])
|
||||
[1, x]
|
||||
>>> h([0, 1])
|
||||
[x**2, 0]
|
||||
>>> h([1, 1])
|
||||
[x**2 + 1, x]
|
||||
|
||||
If ``domain`` is a submodule of a free module, them ``matrix`` determines
|
||||
a homomoprhism from the containing free module to ``codomain``, and the
|
||||
homomorphism returned is obtained by restriction to ``domain``.
|
||||
|
||||
>>> S = F.submodule([1, 0], [0, x])
|
||||
>>> homomorphism(S, T, [[1, x], [x**2, 0]])
|
||||
Matrix([
|
||||
[1, x**2], : <[1, 0], [0, x]> -> QQ[x]**2
|
||||
[x, 0]])
|
||||
|
||||
If ``domain`` is a (sub)quotient `N/K`, then ``matrix`` determines a
|
||||
homomorphism from `N` to ``codomain``. If the kernel contains `K`, this
|
||||
homomorphism descends to ``domain`` and is returned; otherwise an exception
|
||||
is raised.
|
||||
|
||||
>>> homomorphism(S/[(1, 0)], T, [0, [x**2, 0]])
|
||||
Matrix([
|
||||
[0, x**2], : <[1, 0] + <[1, 0]>, [0, x] + <[1, 0]>, [1, 0] + <[1, 0]>> -> QQ[x]**2
|
||||
[0, 0]])
|
||||
>>> homomorphism(S/[(0, x)], T, [0, [x**2, 0]])
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
ValueError: kernel <[1, 0], [0, 0]> must contain sm, got <[0,x]>
|
||||
|
||||
"""
|
||||
def freepres(module):
|
||||
"""
|
||||
Return a tuple ``(F, S, Q, c)`` where ``F`` is a free module, ``S`` is a
|
||||
submodule of ``F``, and ``Q`` a submodule of ``S``, such that
|
||||
``module = S/Q``, and ``c`` is a conversion function.
|
||||
"""
|
||||
if isinstance(module, FreeModule):
|
||||
return module, module, module.submodule(), lambda x: module.convert(x)
|
||||
if isinstance(module, QuotientModule):
|
||||
return (module.base, module.base, module.killed_module,
|
||||
lambda x: module.convert(x).data)
|
||||
if isinstance(module, SubQuotientModule):
|
||||
return (module.base.container, module.base, module.killed_module,
|
||||
lambda x: module.container.convert(x).data)
|
||||
# an ordinary submodule
|
||||
return (module.container, module, module.submodule(),
|
||||
lambda x: module.container.convert(x))
|
||||
|
||||
SF, SS, SQ, _ = freepres(domain)
|
||||
TF, TS, TQ, c = freepres(codomain)
|
||||
# NOTE this is probably a bit inefficient (redundant checks)
|
||||
return FreeModuleHomomorphism(SF, TF, [c(x) for x in matrix]
|
||||
).restrict_domain(SS).restrict_codomain(TS
|
||||
).quotient_codomain(TQ).quotient_domain(SQ)
|
||||
@@ -0,0 +1,395 @@
|
||||
"""Computations with ideals of polynomial rings."""
|
||||
|
||||
from sympy.polys.polyerrors import CoercionFailed
|
||||
from sympy.polys.polyutils import IntegerPowerable
|
||||
|
||||
|
||||
class Ideal(IntegerPowerable):
|
||||
"""
|
||||
Abstract base class for ideals.
|
||||
|
||||
Do not instantiate - use explicit constructors in the ring class instead:
|
||||
|
||||
>>> from sympy import QQ
|
||||
>>> from sympy.abc import x
|
||||
>>> QQ.old_poly_ring(x).ideal(x+1)
|
||||
<x + 1>
|
||||
|
||||
Attributes
|
||||
|
||||
- ring - the ring this ideal belongs to
|
||||
|
||||
Non-implemented methods:
|
||||
|
||||
- _contains_elem
|
||||
- _contains_ideal
|
||||
- _quotient
|
||||
- _intersect
|
||||
- _union
|
||||
- _product
|
||||
- is_whole_ring
|
||||
- is_zero
|
||||
- is_prime, is_maximal, is_primary, is_radical
|
||||
- is_principal
|
||||
- height, depth
|
||||
- radical
|
||||
|
||||
Methods that likely should be overridden in subclasses:
|
||||
|
||||
- reduce_element
|
||||
"""
|
||||
|
||||
def _contains_elem(self, x):
|
||||
"""Implementation of element containment."""
|
||||
raise NotImplementedError
|
||||
|
||||
def _contains_ideal(self, I):
|
||||
"""Implementation of ideal containment."""
|
||||
raise NotImplementedError
|
||||
|
||||
def _quotient(self, J):
|
||||
"""Implementation of ideal quotient."""
|
||||
raise NotImplementedError
|
||||
|
||||
def _intersect(self, J):
|
||||
"""Implementation of ideal intersection."""
|
||||
raise NotImplementedError
|
||||
|
||||
def is_whole_ring(self):
|
||||
"""Return True if ``self`` is the whole ring."""
|
||||
raise NotImplementedError
|
||||
|
||||
def is_zero(self):
|
||||
"""Return True if ``self`` is the zero ideal."""
|
||||
raise NotImplementedError
|
||||
|
||||
def _equals(self, J):
|
||||
"""Implementation of ideal equality."""
|
||||
return self._contains_ideal(J) and J._contains_ideal(self)
|
||||
|
||||
def is_prime(self):
|
||||
"""Return True if ``self`` is a prime ideal."""
|
||||
raise NotImplementedError
|
||||
|
||||
def is_maximal(self):
|
||||
"""Return True if ``self`` is a maximal ideal."""
|
||||
raise NotImplementedError
|
||||
|
||||
def is_radical(self):
|
||||
"""Return True if ``self`` is a radical ideal."""
|
||||
raise NotImplementedError
|
||||
|
||||
def is_primary(self):
|
||||
"""Return True if ``self`` is a primary ideal."""
|
||||
raise NotImplementedError
|
||||
|
||||
def is_principal(self):
|
||||
"""Return True if ``self`` is a principal ideal."""
|
||||
raise NotImplementedError
|
||||
|
||||
def radical(self):
|
||||
"""Compute the radical of ``self``."""
|
||||
raise NotImplementedError
|
||||
|
||||
def depth(self):
|
||||
"""Compute the depth of ``self``."""
|
||||
raise NotImplementedError
|
||||
|
||||
def height(self):
|
||||
"""Compute the height of ``self``."""
|
||||
raise NotImplementedError
|
||||
|
||||
# TODO more
|
||||
|
||||
# non-implemented methods end here
|
||||
|
||||
def __init__(self, ring):
|
||||
self.ring = ring
|
||||
|
||||
def _check_ideal(self, J):
|
||||
"""Helper to check ``J`` is an ideal of our ring."""
|
||||
if not isinstance(J, Ideal) or J.ring != self.ring:
|
||||
raise ValueError(
|
||||
'J must be an ideal of %s, got %s' % (self.ring, J))
|
||||
|
||||
def contains(self, elem):
|
||||
"""
|
||||
Return True if ``elem`` is an element of this ideal.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.abc import x
|
||||
>>> from sympy import QQ
|
||||
>>> QQ.old_poly_ring(x).ideal(x+1, x-1).contains(3)
|
||||
True
|
||||
>>> QQ.old_poly_ring(x).ideal(x**2, x**3).contains(x)
|
||||
False
|
||||
"""
|
||||
return self._contains_elem(self.ring.convert(elem))
|
||||
|
||||
def subset(self, other):
|
||||
"""
|
||||
Returns True if ``other`` is is a subset of ``self``.
|
||||
|
||||
Here ``other`` may be an ideal.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.abc import x
|
||||
>>> from sympy import QQ
|
||||
>>> I = QQ.old_poly_ring(x).ideal(x+1)
|
||||
>>> I.subset([x**2 - 1, x**2 + 2*x + 1])
|
||||
True
|
||||
>>> I.subset([x**2 + 1, x + 1])
|
||||
False
|
||||
>>> I.subset(QQ.old_poly_ring(x).ideal(x**2 - 1))
|
||||
True
|
||||
"""
|
||||
if isinstance(other, Ideal):
|
||||
return self._contains_ideal(other)
|
||||
return all(self._contains_elem(x) for x in other)
|
||||
|
||||
def quotient(self, J, **opts):
|
||||
r"""
|
||||
Compute the ideal quotient of ``self`` by ``J``.
|
||||
|
||||
That is, if ``self`` is the ideal `I`, compute the set
|
||||
`I : J = \{x \in R | xJ \subset I \}`.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.abc import x, y
|
||||
>>> from sympy import QQ
|
||||
>>> R = QQ.old_poly_ring(x, y)
|
||||
>>> R.ideal(x*y).quotient(R.ideal(x))
|
||||
<y>
|
||||
"""
|
||||
self._check_ideal(J)
|
||||
return self._quotient(J, **opts)
|
||||
|
||||
def intersect(self, J):
|
||||
"""
|
||||
Compute the intersection of self with ideal J.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.abc import x, y
|
||||
>>> from sympy import QQ
|
||||
>>> R = QQ.old_poly_ring(x, y)
|
||||
>>> R.ideal(x).intersect(R.ideal(y))
|
||||
<x*y>
|
||||
"""
|
||||
self._check_ideal(J)
|
||||
return self._intersect(J)
|
||||
|
||||
def saturate(self, J):
|
||||
r"""
|
||||
Compute the ideal saturation of ``self`` by ``J``.
|
||||
|
||||
That is, if ``self`` is the ideal `I`, compute the set
|
||||
`I : J^\infty = \{x \in R | xJ^n \subset I \text{ for some } n\}`.
|
||||
"""
|
||||
raise NotImplementedError
|
||||
# Note this can be implemented using repeated quotient
|
||||
|
||||
def union(self, J):
|
||||
"""
|
||||
Compute the ideal generated by the union of ``self`` and ``J``.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.abc import x
|
||||
>>> from sympy import QQ
|
||||
>>> QQ.old_poly_ring(x).ideal(x**2 - 1).union(QQ.old_poly_ring(x).ideal((x+1)**2)) == QQ.old_poly_ring(x).ideal(x+1)
|
||||
True
|
||||
"""
|
||||
self._check_ideal(J)
|
||||
return self._union(J)
|
||||
|
||||
def product(self, J):
|
||||
r"""
|
||||
Compute the ideal product of ``self`` and ``J``.
|
||||
|
||||
That is, compute the ideal generated by products `xy`, for `x` an element
|
||||
of ``self`` and `y \in J`.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.abc import x, y
|
||||
>>> from sympy import QQ
|
||||
>>> QQ.old_poly_ring(x, y).ideal(x).product(QQ.old_poly_ring(x, y).ideal(y))
|
||||
<x*y>
|
||||
"""
|
||||
self._check_ideal(J)
|
||||
return self._product(J)
|
||||
|
||||
def reduce_element(self, x):
|
||||
"""
|
||||
Reduce the element ``x`` of our ring modulo the ideal ``self``.
|
||||
|
||||
Here "reduce" has no specific meaning: it could return a unique normal
|
||||
form, simplify the expression a bit, or just do nothing.
|
||||
"""
|
||||
return x
|
||||
|
||||
def __add__(self, e):
|
||||
if not isinstance(e, Ideal):
|
||||
R = self.ring.quotient_ring(self)
|
||||
if isinstance(e, R.dtype):
|
||||
return e
|
||||
if isinstance(e, R.ring.dtype):
|
||||
return R(e)
|
||||
return R.convert(e)
|
||||
self._check_ideal(e)
|
||||
return self.union(e)
|
||||
|
||||
__radd__ = __add__
|
||||
|
||||
def __mul__(self, e):
|
||||
if not isinstance(e, Ideal):
|
||||
try:
|
||||
e = self.ring.ideal(e)
|
||||
except CoercionFailed:
|
||||
return NotImplemented
|
||||
self._check_ideal(e)
|
||||
return self.product(e)
|
||||
|
||||
__rmul__ = __mul__
|
||||
|
||||
def _zeroth_power(self):
|
||||
return self.ring.ideal(1)
|
||||
|
||||
def _first_power(self):
|
||||
# Raising to any power but 1 returns a new instance. So we mult by 1
|
||||
# here so that the first power is no exception.
|
||||
return self * 1
|
||||
|
||||
def __eq__(self, e):
|
||||
if not isinstance(e, Ideal) or e.ring != self.ring:
|
||||
return False
|
||||
return self._equals(e)
|
||||
|
||||
def __ne__(self, e):
|
||||
return not (self == e)
|
||||
|
||||
|
||||
class ModuleImplementedIdeal(Ideal):
|
||||
"""
|
||||
Ideal implementation relying on the modules code.
|
||||
|
||||
Attributes:
|
||||
|
||||
- _module - the underlying module
|
||||
"""
|
||||
|
||||
def __init__(self, ring, module):
|
||||
Ideal.__init__(self, ring)
|
||||
self._module = module
|
||||
|
||||
def _contains_elem(self, x):
|
||||
return self._module.contains([x])
|
||||
|
||||
def _contains_ideal(self, J):
|
||||
if not isinstance(J, ModuleImplementedIdeal):
|
||||
raise NotImplementedError
|
||||
return self._module.is_submodule(J._module)
|
||||
|
||||
def _intersect(self, J):
|
||||
if not isinstance(J, ModuleImplementedIdeal):
|
||||
raise NotImplementedError
|
||||
return self.__class__(self.ring, self._module.intersect(J._module))
|
||||
|
||||
def _quotient(self, J, **opts):
|
||||
if not isinstance(J, ModuleImplementedIdeal):
|
||||
raise NotImplementedError
|
||||
return self._module.module_quotient(J._module, **opts)
|
||||
|
||||
def _union(self, J):
|
||||
if not isinstance(J, ModuleImplementedIdeal):
|
||||
raise NotImplementedError
|
||||
return self.__class__(self.ring, self._module.union(J._module))
|
||||
|
||||
@property
|
||||
def gens(self):
|
||||
"""
|
||||
Return generators for ``self``.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy import QQ
|
||||
>>> from sympy.abc import x, y
|
||||
>>> list(QQ.old_poly_ring(x, y).ideal(x, y, x**2 + y).gens)
|
||||
[DMP_Python([[1], []], QQ), DMP_Python([[1, 0]], QQ), DMP_Python([[1], [], [1, 0]], QQ)]
|
||||
"""
|
||||
return (x[0] for x in self._module.gens)
|
||||
|
||||
def is_zero(self):
|
||||
"""
|
||||
Return True if ``self`` is the zero ideal.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.abc import x
|
||||
>>> from sympy import QQ
|
||||
>>> QQ.old_poly_ring(x).ideal(x).is_zero()
|
||||
False
|
||||
>>> QQ.old_poly_ring(x).ideal().is_zero()
|
||||
True
|
||||
"""
|
||||
return self._module.is_zero()
|
||||
|
||||
def is_whole_ring(self):
|
||||
"""
|
||||
Return True if ``self`` is the whole ring, i.e. one generator is a unit.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.abc import x
|
||||
>>> from sympy import QQ, ilex
|
||||
>>> QQ.old_poly_ring(x).ideal(x).is_whole_ring()
|
||||
False
|
||||
>>> QQ.old_poly_ring(x).ideal(3).is_whole_ring()
|
||||
True
|
||||
>>> QQ.old_poly_ring(x, order=ilex).ideal(2 + x).is_whole_ring()
|
||||
True
|
||||
"""
|
||||
return self._module.is_full_module()
|
||||
|
||||
def __repr__(self):
|
||||
from sympy.printing.str import sstr
|
||||
gens = [self.ring.to_sympy(x) for [x] in self._module.gens]
|
||||
return '<' + ','.join(sstr(g) for g in gens) + '>'
|
||||
|
||||
# NOTE this is the only method using the fact that the module is a SubModule
|
||||
def _product(self, J):
|
||||
if not isinstance(J, ModuleImplementedIdeal):
|
||||
raise NotImplementedError
|
||||
return self.__class__(self.ring, self._module.submodule(
|
||||
*[[x*y] for [x] in self._module.gens for [y] in J._module.gens]))
|
||||
|
||||
def in_terms_of_generators(self, e):
|
||||
"""
|
||||
Express ``e`` in terms of the generators of ``self``.
|
||||
|
||||
Examples
|
||||
========
|
||||
|
||||
>>> from sympy.abc import x
|
||||
>>> from sympy import QQ
|
||||
>>> I = QQ.old_poly_ring(x).ideal(x**2 + 1, x)
|
||||
>>> I.in_terms_of_generators(1) # doctest: +SKIP
|
||||
[DMP_Python([1], QQ), DMP_Python([-1, 0], QQ)]
|
||||
"""
|
||||
return self._module.in_terms_of_generators([e])
|
||||
|
||||
def reduce_element(self, x, **options):
|
||||
return self._module.reduce_element([x], **options)[0]
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,196 @@
|
||||
from sympy.core.symbol import symbols
|
||||
from sympy.functions.elementary.trigonometric import (cos, sin)
|
||||
from sympy.polys import QQ, ZZ
|
||||
from sympy.polys.polytools import Poly
|
||||
from sympy.polys.polyerrors import NotInvertible
|
||||
from sympy.polys.agca.extensions import FiniteExtension
|
||||
from sympy.polys.domainmatrix import DomainMatrix
|
||||
|
||||
from sympy.testing.pytest import raises
|
||||
|
||||
from sympy.abc import x, y, t
|
||||
|
||||
|
||||
def test_FiniteExtension():
|
||||
# Gaussian integers
|
||||
A = FiniteExtension(Poly(x**2 + 1, x))
|
||||
assert A.rank == 2
|
||||
assert str(A) == 'ZZ[x]/(x**2 + 1)'
|
||||
i = A.generator
|
||||
assert i.parent() is A
|
||||
|
||||
assert i*i == A(-1)
|
||||
raises(TypeError, lambda: i*())
|
||||
|
||||
assert A.basis == (A.one, i)
|
||||
assert A(1) == A.one
|
||||
assert i**2 == A(-1)
|
||||
assert i**2 != -1 # no coercion
|
||||
assert (2 + i)*(1 - i) == 3 - i
|
||||
assert (1 + i)**8 == A(16)
|
||||
assert A(1).inverse() == A(1)
|
||||
raises(NotImplementedError, lambda: A(2).inverse())
|
||||
|
||||
# Finite field of order 27
|
||||
F = FiniteExtension(Poly(x**3 - x + 1, x, modulus=3))
|
||||
assert F.rank == 3
|
||||
a = F.generator # also generates the cyclic group F - {0}
|
||||
assert F.basis == (F(1), a, a**2)
|
||||
assert a**27 == a
|
||||
assert a**26 == F(1)
|
||||
assert a**13 == F(-1)
|
||||
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))
|
||||
assert K.rank == 2
|
||||
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
|
||||
|
||||
|
||||
def test_FiniteExtension_eq_hash():
|
||||
# Test eq and hash
|
||||
p1 = Poly(x**2 - 2, x, domain=ZZ)
|
||||
p2 = Poly(x**2 - 2, x, domain=QQ)
|
||||
K1 = FiniteExtension(p1)
|
||||
K2 = FiniteExtension(p2)
|
||||
assert K1 == FiniteExtension(Poly(x**2 - 2))
|
||||
assert K2 != FiniteExtension(Poly(x**2 - 2))
|
||||
assert len({K1, K2, FiniteExtension(p1)}) == 2
|
||||
|
||||
|
||||
def test_FiniteExtension_mod():
|
||||
# Test mod
|
||||
K = FiniteExtension(Poly(x**3 + 1, x, domain=QQ))
|
||||
xf = K(x)
|
||||
assert (xf**2 - 1) % 1 == K.zero
|
||||
assert 1 % (xf**2 - 1) == K.zero
|
||||
assert (xf**2 - 1) / (xf - 1) == xf + 1
|
||||
assert (xf**2 - 1) // (xf - 1) == xf + 1
|
||||
assert (xf**2 - 1) % (xf - 1) == K.zero
|
||||
raises(ZeroDivisionError, lambda: (xf**2 - 1) % 0)
|
||||
raises(TypeError, lambda: xf % [])
|
||||
raises(TypeError, lambda: [] % xf)
|
||||
|
||||
# Test mod over ring
|
||||
K = FiniteExtension(Poly(x**3 + 1, x, domain=ZZ))
|
||||
xf = K(x)
|
||||
assert (xf**2 - 1) % 1 == K.zero
|
||||
raises(NotImplementedError, lambda: (xf**2 - 1) % (xf - 1))
|
||||
|
||||
|
||||
def test_FiniteExtension_from_sympy():
|
||||
# Test to_sympy/from_sympy
|
||||
K = FiniteExtension(Poly(x**3 + 1, x, domain=ZZ))
|
||||
xf = K(x)
|
||||
assert K.from_sympy(x) == xf
|
||||
assert K.to_sympy(xf) == x
|
||||
|
||||
|
||||
def test_FiniteExtension_set_domain():
|
||||
KZ = FiniteExtension(Poly(x**2 + 1, x, domain='ZZ'))
|
||||
KQ = FiniteExtension(Poly(x**2 + 1, x, domain='QQ'))
|
||||
assert KZ.set_domain(QQ) == KQ
|
||||
|
||||
|
||||
def test_FiniteExtension_exquo():
|
||||
# Test exquo
|
||||
K = FiniteExtension(Poly(x**4 + 1))
|
||||
xf = K(x)
|
||||
assert K.exquo(xf**2 - 1, xf - 1) == xf + 1
|
||||
|
||||
|
||||
def test_FiniteExtension_convert():
|
||||
# Test from_MonogenicFiniteExtension
|
||||
K1 = FiniteExtension(Poly(x**2 + 1))
|
||||
K2 = QQ[x]
|
||||
x1, x2 = K1(x), K2(x)
|
||||
assert K1.convert(x2) == x1
|
||||
assert K2.convert(x1) == x2
|
||||
|
||||
K = FiniteExtension(Poly(x**2 - 1, domain=QQ))
|
||||
assert K.convert_from(QQ(1, 2), QQ) == K.one/2
|
||||
|
||||
|
||||
def test_FiniteExtension_division_ring():
|
||||
# Test division in FiniteExtension over a ring
|
||||
KQ = FiniteExtension(Poly(x**2 - 1, x, domain=QQ))
|
||||
KZ = FiniteExtension(Poly(x**2 - 1, x, domain=ZZ))
|
||||
KQt = FiniteExtension(Poly(x**2 - 1, x, domain=QQ[t]))
|
||||
KQtf = FiniteExtension(Poly(x**2 - 1, x, domain=QQ.frac_field(t)))
|
||||
assert KQ.is_Field is True
|
||||
assert KZ.is_Field is False
|
||||
assert KQt.is_Field is False
|
||||
assert KQtf.is_Field is True
|
||||
for K in KQ, KZ, KQt, KQtf:
|
||||
xK = K.convert(x)
|
||||
assert xK / K.one == xK
|
||||
assert xK // K.one == xK
|
||||
assert xK % K.one == K.zero
|
||||
raises(ZeroDivisionError, lambda: xK / K.zero)
|
||||
raises(ZeroDivisionError, lambda: xK // K.zero)
|
||||
raises(ZeroDivisionError, lambda: xK % K.zero)
|
||||
if K.is_Field:
|
||||
assert xK / xK == K.one
|
||||
assert xK // xK == K.one
|
||||
assert xK % xK == K.zero
|
||||
else:
|
||||
raises(NotImplementedError, lambda: xK / xK)
|
||||
raises(NotImplementedError, lambda: xK // xK)
|
||||
raises(NotImplementedError, lambda: xK % xK)
|
||||
|
||||
|
||||
def test_FiniteExtension_Poly():
|
||||
K = FiniteExtension(Poly(x**2 - 2))
|
||||
p = Poly(x, y, domain=K)
|
||||
assert p.domain == K
|
||||
assert p.as_expr() == x
|
||||
assert (p**2).as_expr() == 2
|
||||
|
||||
K = FiniteExtension(Poly(x**2 - 2, x, domain=QQ))
|
||||
K2 = FiniteExtension(Poly(t**2 - 2, t, domain=K))
|
||||
assert str(K2) == 'QQ[x]/(x**2 - 2)[t]/(t**2 - 2)'
|
||||
|
||||
eK = K2.convert(x + t)
|
||||
assert K2.to_sympy(eK) == x + t
|
||||
assert K2.to_sympy(eK ** 2) == 4 + 2*x*t
|
||||
p = Poly(x + t, y, domain=K2)
|
||||
assert p**2 == Poly(4 + 2*x*t, y, domain=K2)
|
||||
|
||||
|
||||
def test_FiniteExtension_sincos_jacobian():
|
||||
# Use FiniteExtensino to compute the Jacobian of a matrix involving sin
|
||||
# and cos of different symbols.
|
||||
r, p, t = symbols('rho, phi, theta')
|
||||
elements = [
|
||||
[sin(p)*cos(t), r*cos(p)*cos(t), -r*sin(p)*sin(t)],
|
||||
[sin(p)*sin(t), r*cos(p)*sin(t), r*sin(p)*cos(t)],
|
||||
[ cos(p), -r*sin(p), 0],
|
||||
]
|
||||
|
||||
def make_extension(K):
|
||||
K = FiniteExtension(Poly(sin(p)**2+cos(p)**2-1, sin(p), domain=K[cos(p)]))
|
||||
K = FiniteExtension(Poly(sin(t)**2+cos(t)**2-1, sin(t), domain=K[cos(t)]))
|
||||
return K
|
||||
|
||||
Ksc1 = make_extension(ZZ[r])
|
||||
Ksc2 = make_extension(ZZ)[r]
|
||||
|
||||
for K in [Ksc1, Ksc2]:
|
||||
elements_K = [[K.convert(e) for e in row] for row in elements]
|
||||
J = DomainMatrix(elements_K, (3, 3), K)
|
||||
det = J.charpoly()[-1] * (-K.one)**3
|
||||
assert det == K.convert(r**2*sin(p))
|
||||
@@ -0,0 +1,113 @@
|
||||
"""Tests for homomorphisms."""
|
||||
|
||||
from sympy.core.singleton import S
|
||||
from sympy.polys.domains.rationalfield import QQ
|
||||
from sympy.abc import x, y
|
||||
from sympy.polys.agca import homomorphism
|
||||
from sympy.testing.pytest import raises
|
||||
|
||||
|
||||
def test_printing():
|
||||
R = QQ.old_poly_ring(x)
|
||||
|
||||
assert str(homomorphism(R.free_module(1), R.free_module(1), [0])) == \
|
||||
'Matrix([[0]]) : QQ[x]**1 -> QQ[x]**1'
|
||||
assert str(homomorphism(R.free_module(2), R.free_module(2), [0, 0])) == \
|
||||
'Matrix([ \n[0, 0], : QQ[x]**2 -> QQ[x]**2\n[0, 0]]) '
|
||||
assert str(homomorphism(R.free_module(1), R.free_module(1) / [[x]], [0])) == \
|
||||
'Matrix([[0]]) : QQ[x]**1 -> QQ[x]**1/<[x]>'
|
||||
assert str(R.free_module(0).identity_hom()) == 'Matrix(0, 0, []) : QQ[x]**0 -> QQ[x]**0'
|
||||
|
||||
def test_operations():
|
||||
F = QQ.old_poly_ring(x).free_module(2)
|
||||
G = QQ.old_poly_ring(x).free_module(3)
|
||||
f = F.identity_hom()
|
||||
g = homomorphism(F, F, [0, [1, x]])
|
||||
h = homomorphism(F, F, [[1, 0], 0])
|
||||
i = homomorphism(F, G, [[1, 0, 0], [0, 1, 0]])
|
||||
|
||||
assert f == f
|
||||
assert f != g
|
||||
assert f != i
|
||||
assert (f != F.identity_hom()) is False
|
||||
assert 2*f == f*2 == homomorphism(F, F, [[2, 0], [0, 2]])
|
||||
assert f/2 == homomorphism(F, F, [[S.Half, 0], [0, S.Half]])
|
||||
assert f + g == homomorphism(F, F, [[1, 0], [1, x + 1]])
|
||||
assert f - g == homomorphism(F, F, [[1, 0], [-1, 1 - x]])
|
||||
assert f*g == g == g*f
|
||||
assert h*g == homomorphism(F, F, [0, [1, 0]])
|
||||
assert g*h == homomorphism(F, F, [0, 0])
|
||||
assert i*f == i
|
||||
assert f([1, 2]) == [1, 2]
|
||||
assert g([1, 2]) == [2, 2*x]
|
||||
|
||||
assert i.restrict_domain(F.submodule([x, x]))([x, x]) == i([x, x])
|
||||
h1 = h.quotient_domain(F.submodule([0, 1]))
|
||||
assert h1([1, 0]) == h([1, 0])
|
||||
assert h1.restrict_domain(h1.domain.submodule([x, 0]))([x, 0]) == h([x, 0])
|
||||
|
||||
raises(TypeError, lambda: f/g)
|
||||
raises(TypeError, lambda: f + 1)
|
||||
raises(TypeError, lambda: f + i)
|
||||
raises(TypeError, lambda: f - 1)
|
||||
raises(TypeError, lambda: f*i)
|
||||
|
||||
|
||||
def test_creation():
|
||||
F = QQ.old_poly_ring(x).free_module(3)
|
||||
G = QQ.old_poly_ring(x).free_module(2)
|
||||
SM = F.submodule([1, 1, 1])
|
||||
Q = F / SM
|
||||
SQ = Q.submodule([1, 0, 0])
|
||||
|
||||
matrix = [[1, 0], [0, 1], [-1, -1]]
|
||||
h = homomorphism(F, G, matrix)
|
||||
h2 = homomorphism(Q, G, matrix)
|
||||
assert h.quotient_domain(SM) == h2
|
||||
raises(ValueError, lambda: h.quotient_domain(F.submodule([1, 0, 0])))
|
||||
assert h2.restrict_domain(SQ) == homomorphism(SQ, G, matrix)
|
||||
raises(ValueError, lambda: h.restrict_domain(G))
|
||||
raises(ValueError, lambda: h.restrict_codomain(G.submodule([1, 0])))
|
||||
raises(ValueError, lambda: h.quotient_codomain(F))
|
||||
|
||||
im = [[1, 0, 0], [0, 1, 0], [0, 0, 1]]
|
||||
for M in [F, SM, Q, SQ]:
|
||||
assert M.identity_hom() == homomorphism(M, M, im)
|
||||
assert SM.inclusion_hom() == homomorphism(SM, F, im)
|
||||
assert SQ.inclusion_hom() == homomorphism(SQ, Q, im)
|
||||
assert Q.quotient_hom() == homomorphism(F, Q, im)
|
||||
assert SQ.quotient_hom() == homomorphism(SQ.base, SQ, im)
|
||||
|
||||
class conv:
|
||||
def convert(x, y=None):
|
||||
return x
|
||||
|
||||
class dummy:
|
||||
container = conv()
|
||||
|
||||
def submodule(*args):
|
||||
return None
|
||||
raises(TypeError, lambda: homomorphism(dummy(), G, matrix))
|
||||
raises(TypeError, lambda: homomorphism(F, dummy(), matrix))
|
||||
raises(
|
||||
ValueError, lambda: homomorphism(QQ.old_poly_ring(x, y).free_module(3), G, matrix))
|
||||
raises(ValueError, lambda: homomorphism(F, G, [0, 0]))
|
||||
|
||||
|
||||
def test_properties():
|
||||
R = QQ.old_poly_ring(x, y)
|
||||
F = R.free_module(2)
|
||||
h = homomorphism(F, F, [[x, 0], [y, 0]])
|
||||
assert h.kernel() == F.submodule([-y, x])
|
||||
assert h.image() == F.submodule([x, 0], [y, 0])
|
||||
assert not h.is_injective()
|
||||
assert not h.is_surjective()
|
||||
assert h.restrict_codomain(h.image()).is_surjective()
|
||||
assert h.restrict_domain(F.submodule([1, 0])).is_injective()
|
||||
assert h.quotient_domain(
|
||||
h.kernel()).restrict_codomain(h.image()).is_isomorphism()
|
||||
|
||||
R2 = QQ.old_poly_ring(x, y, order=(("lex", x), ("ilex", y))) / [x**2 + 1]
|
||||
F = R2.free_module(2)
|
||||
h = homomorphism(F, F, [[x, 0], [y, y + 1]])
|
||||
assert h.is_isomorphism()
|
||||
@@ -0,0 +1,131 @@
|
||||
"""Test ideals.py code."""
|
||||
|
||||
from sympy.polys import QQ, ilex
|
||||
from sympy.abc import x, y, z
|
||||
from sympy.testing.pytest import raises
|
||||
|
||||
|
||||
def test_ideal_operations():
|
||||
R = QQ.old_poly_ring(x, y)
|
||||
I = R.ideal(x)
|
||||
J = R.ideal(y)
|
||||
S = R.ideal(x*y)
|
||||
T = R.ideal(x, y)
|
||||
|
||||
assert not (I == J)
|
||||
assert I == I
|
||||
|
||||
assert I.union(J) == T
|
||||
assert I + J == T
|
||||
assert I + T == T
|
||||
|
||||
assert not I.subset(T)
|
||||
assert T.subset(I)
|
||||
|
||||
assert I.product(J) == S
|
||||
assert I*J == S
|
||||
assert x*J == S
|
||||
assert I*y == S
|
||||
assert R.convert(x)*J == S
|
||||
assert I*R.convert(y) == S
|
||||
|
||||
assert not I.is_zero()
|
||||
assert not J.is_whole_ring()
|
||||
|
||||
assert R.ideal(x**2 + 1, x).is_whole_ring()
|
||||
assert R.ideal() == R.ideal(0)
|
||||
assert R.ideal().is_zero()
|
||||
|
||||
assert T.contains(x*y)
|
||||
assert T.subset([x, y])
|
||||
|
||||
assert T.in_terms_of_generators(x) == [R(1), R(0)]
|
||||
|
||||
assert T**0 == R.ideal(1)
|
||||
assert T**1 == T
|
||||
assert T**2 == R.ideal(x**2, y**2, x*y)
|
||||
assert I**5 == R.ideal(x**5)
|
||||
|
||||
|
||||
def test_exceptions():
|
||||
I = QQ.old_poly_ring(x).ideal(x)
|
||||
J = QQ.old_poly_ring(y).ideal(1)
|
||||
raises(ValueError, lambda: I.union(x))
|
||||
raises(ValueError, lambda: I + J)
|
||||
raises(ValueError, lambda: I * J)
|
||||
raises(ValueError, lambda: I.union(J))
|
||||
assert (I == J) is False
|
||||
assert I != J
|
||||
|
||||
|
||||
def test_nontriv_global():
|
||||
R = QQ.old_poly_ring(x, y, z)
|
||||
|
||||
def contains(I, f):
|
||||
return R.ideal(*I).contains(f)
|
||||
|
||||
assert contains([x, y], x)
|
||||
assert contains([x, y], x + y)
|
||||
assert not contains([x, y], 1)
|
||||
assert not contains([x, y], z)
|
||||
assert contains([x**2 + y, x**2 + x], x - y)
|
||||
assert not contains([x + y + z, x*y + x*z + y*z, x*y*z], x**2)
|
||||
assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x**3)
|
||||
assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x**4)
|
||||
assert not contains([x + y + z, x*y + x*z + y*z, x*y*z], x*y**2)
|
||||
assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x**4 + y**3 + 2*z*y*x)
|
||||
assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x*y*z)
|
||||
assert contains([x, 1 + x + y, 5 - 7*y], 1)
|
||||
assert contains(
|
||||
[x**3 + y**3, y**3 + z**3, z**3 + x**3, x**2*y + x**2*z + y**2*z],
|
||||
x**3)
|
||||
assert not contains(
|
||||
[x**3 + y**3, y**3 + z**3, z**3 + x**3, x**2*y + x**2*z + y**2*z],
|
||||
x**2 + y**2)
|
||||
|
||||
# compare local order
|
||||
assert not contains([x*(1 + x + y), y*(1 + z)], x)
|
||||
assert not contains([x*(1 + x + y), y*(1 + z)], x + y)
|
||||
|
||||
|
||||
def test_nontriv_local():
|
||||
R = QQ.old_poly_ring(x, y, z, order=ilex)
|
||||
|
||||
def contains(I, f):
|
||||
return R.ideal(*I).contains(f)
|
||||
|
||||
assert contains([x, y], x)
|
||||
assert contains([x, y], x + y)
|
||||
assert not contains([x, y], 1)
|
||||
assert not contains([x, y], z)
|
||||
assert contains([x**2 + y, x**2 + x], x - y)
|
||||
assert not contains([x + y + z, x*y + x*z + y*z, x*y*z], x**2)
|
||||
assert contains([x*(1 + x + y), y*(1 + z)], x)
|
||||
assert contains([x*(1 + x + y), y*(1 + z)], x + y)
|
||||
|
||||
|
||||
def test_intersection():
|
||||
R = QQ.old_poly_ring(x, y, z)
|
||||
# SCA, example 1.8.11
|
||||
assert R.ideal(x, y).intersect(R.ideal(y**2, z)) == R.ideal(y**2, y*z, x*z)
|
||||
|
||||
assert R.ideal(x, y).intersect(R.ideal()).is_zero()
|
||||
|
||||
R = QQ.old_poly_ring(x, y, z, order="ilex")
|
||||
assert R.ideal(x, y).intersect(R.ideal(y**2 + y**2*z, z + z*x**3*y)) == \
|
||||
R.ideal(y**2, y*z, x*z)
|
||||
|
||||
|
||||
def test_quotient():
|
||||
# SCA, example 1.8.13
|
||||
R = QQ.old_poly_ring(x, y, z)
|
||||
assert R.ideal(x, y).quotient(R.ideal(y**2, z)) == R.ideal(x, y)
|
||||
|
||||
|
||||
def test_reduction():
|
||||
from sympy.polys.distributedmodules import sdm_nf_buchberger_reduced
|
||||
R = QQ.old_poly_ring(x, y)
|
||||
I = R.ideal(x**5, y)
|
||||
e = R.convert(x**3 + y**2)
|
||||
assert I.reduce_element(e) == e
|
||||
assert I.reduce_element(e, NF=sdm_nf_buchberger_reduced) == R.convert(x**3)
|
||||
@@ -0,0 +1,408 @@
|
||||
"""Test modules.py code."""
|
||||
|
||||
from sympy.polys.agca.modules import FreeModule, ModuleOrder, FreeModulePolyRing
|
||||
from sympy.polys import CoercionFailed, QQ, lex, grlex, ilex, ZZ
|
||||
from sympy.abc import x, y, z
|
||||
from sympy.testing.pytest import raises
|
||||
from sympy.core.numbers import Rational
|
||||
|
||||
|
||||
def test_FreeModuleElement():
|
||||
M = QQ.old_poly_ring(x).free_module(3)
|
||||
e = M.convert([1, x, x**2])
|
||||
f = [QQ.old_poly_ring(x).convert(1), QQ.old_poly_ring(x).convert(x), QQ.old_poly_ring(x).convert(x**2)]
|
||||
assert list(e) == f
|
||||
assert f[0] == e[0]
|
||||
assert f[1] == e[1]
|
||||
assert f[2] == e[2]
|
||||
raises(IndexError, lambda: e[3])
|
||||
|
||||
g = M.convert([x, 0, 0])
|
||||
assert e + g == M.convert([x + 1, x, x**2])
|
||||
assert f + g == M.convert([x + 1, x, x**2])
|
||||
assert -e == M.convert([-1, -x, -x**2])
|
||||
assert e - g == M.convert([1 - x, x, x**2])
|
||||
assert e != g
|
||||
|
||||
assert M.convert([x, x, x]) / QQ.old_poly_ring(x).convert(x) == [1, 1, 1]
|
||||
R = QQ.old_poly_ring(x, order="ilex")
|
||||
assert R.free_module(1).convert([x]) / R.convert(x) == [1]
|
||||
|
||||
|
||||
def test_FreeModule():
|
||||
M1 = FreeModule(QQ.old_poly_ring(x), 2)
|
||||
assert M1 == FreeModule(QQ.old_poly_ring(x), 2)
|
||||
assert M1 != FreeModule(QQ.old_poly_ring(y), 2)
|
||||
assert M1 != FreeModule(QQ.old_poly_ring(x), 3)
|
||||
M2 = FreeModule(QQ.old_poly_ring(x, order="ilex"), 2)
|
||||
|
||||
assert [x, 1] in M1
|
||||
assert [x] not in M1
|
||||
assert [2, y] not in M1
|
||||
assert [1/(x + 1), 2] not in M1
|
||||
|
||||
e = M1.convert([x, x**2 + 1])
|
||||
X = QQ.old_poly_ring(x).convert(x)
|
||||
assert e == [X, X**2 + 1]
|
||||
assert e == [x, x**2 + 1]
|
||||
assert 2*e == [2*x, 2*x**2 + 2]
|
||||
assert e*2 == [2*x, 2*x**2 + 2]
|
||||
assert e/2 == [x/2, (x**2 + 1)/2]
|
||||
assert x*e == [x**2, x**3 + x]
|
||||
assert e*x == [x**2, x**3 + x]
|
||||
assert X*e == [x**2, x**3 + x]
|
||||
assert e*X == [x**2, x**3 + x]
|
||||
|
||||
assert [x, 1] in M2
|
||||
assert [x] not in M2
|
||||
assert [2, y] not in M2
|
||||
assert [1/(x + 1), 2] in M2
|
||||
|
||||
e = M2.convert([x, x**2 + 1])
|
||||
X = QQ.old_poly_ring(x, order="ilex").convert(x)
|
||||
assert e == [X, X**2 + 1]
|
||||
assert e == [x, x**2 + 1]
|
||||
assert 2*e == [2*x, 2*x**2 + 2]
|
||||
assert e*2 == [2*x, 2*x**2 + 2]
|
||||
assert e/2 == [x/2, (x**2 + 1)/2]
|
||||
assert x*e == [x**2, x**3 + x]
|
||||
assert e*x == [x**2, x**3 + x]
|
||||
assert e/(1 + x) == [x/(1 + x), (x**2 + 1)/(1 + x)]
|
||||
assert X*e == [x**2, x**3 + x]
|
||||
assert e*X == [x**2, x**3 + x]
|
||||
|
||||
M3 = FreeModule(QQ.old_poly_ring(x, y), 2)
|
||||
assert M3.convert(e) == M3.convert([x, x**2 + 1])
|
||||
|
||||
assert not M3.is_submodule(0)
|
||||
assert not M3.is_zero()
|
||||
|
||||
raises(NotImplementedError, lambda: ZZ.old_poly_ring(x).free_module(2))
|
||||
raises(NotImplementedError, lambda: FreeModulePolyRing(ZZ, 2))
|
||||
raises(CoercionFailed, lambda: M1.convert(QQ.old_poly_ring(x).free_module(3)
|
||||
.convert([1, 2, 3])))
|
||||
raises(CoercionFailed, lambda: M3.convert(1))
|
||||
|
||||
|
||||
def test_ModuleOrder():
|
||||
o1 = ModuleOrder(lex, grlex, False)
|
||||
o2 = ModuleOrder(ilex, lex, False)
|
||||
|
||||
assert o1 == ModuleOrder(lex, grlex, False)
|
||||
assert (o1 != ModuleOrder(lex, grlex, False)) is False
|
||||
assert o1 != o2
|
||||
|
||||
assert o1((1, 2, 3)) == (1, (5, (2, 3)))
|
||||
assert o2((1, 2, 3)) == (-1, (2, 3))
|
||||
|
||||
|
||||
def test_SubModulePolyRing_global():
|
||||
R = QQ.old_poly_ring(x, y)
|
||||
F = R.free_module(3)
|
||||
Fd = F.submodule([1, 0, 0], [1, 2, 0], [1, 2, 3])
|
||||
M = F.submodule([x**2 + y**2, 1, 0], [x, y, 1])
|
||||
|
||||
assert F == Fd
|
||||
assert Fd == F
|
||||
assert F != M
|
||||
assert M != F
|
||||
assert Fd != M
|
||||
assert M != Fd
|
||||
assert Fd == F.submodule(*F.basis())
|
||||
|
||||
assert Fd.is_full_module()
|
||||
assert not M.is_full_module()
|
||||
assert not Fd.is_zero()
|
||||
assert not M.is_zero()
|
||||
assert Fd.submodule().is_zero()
|
||||
|
||||
assert M.contains([x**2 + y**2 + x, 1 + y, 1])
|
||||
assert not M.contains([x**2 + y**2 + x, 1 + y, 2])
|
||||
assert M.contains([y**2, 1 - x*y, -x])
|
||||
|
||||
assert not F.submodule([1 + x, 0, 0]) == F.submodule([1, 0, 0])
|
||||
assert F.submodule([1, 0, 0], [0, 1, 0]).union(F.submodule([0, 0, 1])) == F
|
||||
assert not M.is_submodule(0)
|
||||
|
||||
m = F.convert([x**2 + y**2, 1, 0])
|
||||
n = M.convert(m)
|
||||
assert m.module is F
|
||||
assert n.module is M
|
||||
|
||||
raises(ValueError, lambda: M.submodule([1, 0, 0]))
|
||||
raises(TypeError, lambda: M.union(1))
|
||||
raises(ValueError, lambda: M.union(R.free_module(1).submodule([x])))
|
||||
|
||||
assert F.submodule([x, x, x]) != F.submodule([x, x, x], order="ilex")
|
||||
|
||||
|
||||
def test_SubModulePolyRing_local():
|
||||
R = QQ.old_poly_ring(x, y, order=ilex)
|
||||
F = R.free_module(3)
|
||||
Fd = F.submodule([1 + x, 0, 0], [1 + y, 2 + 2*y, 0], [1, 2, 3])
|
||||
M = F.submodule([x**2 + y**2, 1, 0], [x, y, 1])
|
||||
|
||||
assert F == Fd
|
||||
assert Fd == F
|
||||
assert F != M
|
||||
assert M != F
|
||||
assert Fd != M
|
||||
assert M != Fd
|
||||
assert Fd == F.submodule(*F.basis())
|
||||
|
||||
assert Fd.is_full_module()
|
||||
assert not M.is_full_module()
|
||||
assert not Fd.is_zero()
|
||||
assert not M.is_zero()
|
||||
assert Fd.submodule().is_zero()
|
||||
|
||||
assert M.contains([x**2 + y**2 + x, 1 + y, 1])
|
||||
assert not M.contains([x**2 + y**2 + x, 1 + y, 2])
|
||||
assert M.contains([y**2, 1 - x*y, -x])
|
||||
|
||||
assert F.submodule([1 + x, 0, 0]) == F.submodule([1, 0, 0])
|
||||
assert F.submodule(
|
||||
[1, 0, 0], [0, 1, 0]).union(F.submodule([0, 0, 1 + x*y])) == F
|
||||
|
||||
raises(ValueError, lambda: M.submodule([1, 0, 0]))
|
||||
|
||||
|
||||
def test_SubModulePolyRing_nontriv_global():
|
||||
R = QQ.old_poly_ring(x, y, z)
|
||||
F = R.free_module(1)
|
||||
|
||||
def contains(I, f):
|
||||
return F.submodule(*[[g] for g in I]).contains([f])
|
||||
|
||||
assert contains([x, y], x)
|
||||
assert contains([x, y], x + y)
|
||||
assert not contains([x, y], 1)
|
||||
assert not contains([x, y], z)
|
||||
assert contains([x**2 + y, x**2 + x], x - y)
|
||||
assert not contains([x + y + z, x*y + x*z + y*z, x*y*z], x**2)
|
||||
assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x**3)
|
||||
assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x**4)
|
||||
assert not contains([x + y + z, x*y + x*z + y*z, x*y*z], x*y**2)
|
||||
assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x**4 + y**3 + 2*z*y*x)
|
||||
assert contains([x + y + z, x*y + x*z + y*z, x*y*z], x*y*z)
|
||||
assert contains([x, 1 + x + y, 5 - 7*y], 1)
|
||||
assert contains(
|
||||
[x**3 + y**3, y**3 + z**3, z**3 + x**3, x**2*y + x**2*z + y**2*z],
|
||||
x**3)
|
||||
assert not contains(
|
||||
[x**3 + y**3, y**3 + z**3, z**3 + x**3, x**2*y + x**2*z + y**2*z],
|
||||
x**2 + y**2)
|
||||
|
||||
# compare local order
|
||||
assert not contains([x*(1 + x + y), y*(1 + z)], x)
|
||||
assert not contains([x*(1 + x + y), y*(1 + z)], x + y)
|
||||
|
||||
|
||||
def test_SubModulePolyRing_nontriv_local():
|
||||
R = QQ.old_poly_ring(x, y, z, order=ilex)
|
||||
F = R.free_module(1)
|
||||
|
||||
def contains(I, f):
|
||||
return F.submodule(*[[g] for g in I]).contains([f])
|
||||
|
||||
assert contains([x, y], x)
|
||||
assert contains([x, y], x + y)
|
||||
assert not contains([x, y], 1)
|
||||
assert not contains([x, y], z)
|
||||
assert contains([x**2 + y, x**2 + x], x - y)
|
||||
assert not contains([x + y + z, x*y + x*z + y*z, x*y*z], x**2)
|
||||
assert contains([x*(1 + x + y), y*(1 + z)], x)
|
||||
assert contains([x*(1 + x + y), y*(1 + z)], x + y)
|
||||
|
||||
|
||||
def test_syzygy():
|
||||
R = QQ.old_poly_ring(x, y, z)
|
||||
M = R.free_module(1).submodule([x*y], [y*z], [x*z])
|
||||
S = R.free_module(3).submodule([0, x, -y], [z, -x, 0])
|
||||
assert M.syzygy_module() == S
|
||||
|
||||
M2 = M / ([x*y*z],)
|
||||
S2 = R.free_module(3).submodule([z, 0, 0], [0, x, 0], [0, 0, y])
|
||||
assert M2.syzygy_module() == S2
|
||||
|
||||
F = R.free_module(3)
|
||||
assert F.submodule(*F.basis()).syzygy_module() == F.submodule()
|
||||
|
||||
R2 = QQ.old_poly_ring(x, y, z) / [x*y*z]
|
||||
M3 = R2.free_module(1).submodule([x*y], [y*z], [x*z])
|
||||
S3 = R2.free_module(3).submodule([z, 0, 0], [0, x, 0], [0, 0, y])
|
||||
assert M3.syzygy_module() == S3
|
||||
|
||||
|
||||
def test_in_terms_of_generators():
|
||||
R = QQ.old_poly_ring(x, order="ilex")
|
||||
M = R.free_module(2).submodule([2*x, 0], [1, 2])
|
||||
assert M.in_terms_of_generators(
|
||||
[x, x]) == [R.convert(Rational(1, 4)), R.convert(x/2)]
|
||||
raises(ValueError, lambda: M.in_terms_of_generators([1, 0]))
|
||||
|
||||
M = R.free_module(2) / ([x, 0], [1, 1])
|
||||
SM = M.submodule([1, x])
|
||||
assert SM.in_terms_of_generators([2, 0]) == [R.convert(-2/(x - 1))]
|
||||
|
||||
R = QQ.old_poly_ring(x, y) / [x**2 - y**2]
|
||||
M = R.free_module(2)
|
||||
SM = M.submodule([x, 0], [0, y])
|
||||
assert SM.in_terms_of_generators(
|
||||
[x**2, x**2]) == [R.convert(x), R.convert(y)]
|
||||
|
||||
|
||||
def test_QuotientModuleElement():
|
||||
R = QQ.old_poly_ring(x)
|
||||
F = R.free_module(3)
|
||||
N = F.submodule([1, x, x**2])
|
||||
M = F/N
|
||||
e = M.convert([x**2, 2, 0])
|
||||
|
||||
assert M.convert([x + 1, x**2 + x, x**3 + x**2]) == 0
|
||||
assert e == [x**2, 2, 0] + N == F.convert([x**2, 2, 0]) + N == \
|
||||
M.convert(F.convert([x**2, 2, 0]))
|
||||
|
||||
assert M.convert([x**2 + 1, 2*x + 2, x**2]) == e + [0, x, 0] == \
|
||||
e + M.convert([0, x, 0]) == e + F.convert([0, x, 0])
|
||||
assert M.convert([x**2 + 1, 2, x**2]) == e - [0, x, 0] == \
|
||||
e - M.convert([0, x, 0]) == e - F.convert([0, x, 0])
|
||||
assert M.convert([0, 2, 0]) == M.convert([x**2, 4, 0]) - e == \
|
||||
[x**2, 4, 0] - e == F.convert([x**2, 4, 0]) - e
|
||||
assert M.convert([x**3 + x**2, 2*x + 2, 0]) == (1 + x)*e == \
|
||||
R.convert(1 + x)*e == e*(1 + x) == e*R.convert(1 + x)
|
||||
assert -e == [-x**2, -2, 0]
|
||||
|
||||
f = [x, x, 0] + N
|
||||
assert M.convert([1, 1, 0]) == f / x == f / R.convert(x)
|
||||
|
||||
M2 = F/[(2, 2*x, 2*x**2), (0, 0, 1)]
|
||||
G = R.free_module(2)
|
||||
M3 = G/[[1, x]]
|
||||
M4 = F.submodule([1, x, x**2], [1, 0, 0]) / N
|
||||
raises(CoercionFailed, lambda: M.convert(G.convert([1, x])))
|
||||
raises(CoercionFailed, lambda: M.convert(M3.convert([1, x])))
|
||||
raises(CoercionFailed, lambda: M.convert(M2.convert([1, x, x])))
|
||||
assert M2.convert(M.convert([2, x, x**2])) == [2, x, 0]
|
||||
assert M.convert(M4.convert([2, 0, 0])) == [2, 0, 0]
|
||||
|
||||
|
||||
def test_QuotientModule():
|
||||
R = QQ.old_poly_ring(x)
|
||||
F = R.free_module(3)
|
||||
N = F.submodule([1, x, x**2])
|
||||
M = F/N
|
||||
|
||||
assert M != F
|
||||
assert M != N
|
||||
assert M == F / [(1, x, x**2)]
|
||||
assert not M.is_zero()
|
||||
assert (F / F.basis()).is_zero()
|
||||
|
||||
SQ = F.submodule([1, x, x**2], [2, 0, 0]) / N
|
||||
assert SQ == M.submodule([2, x, x**2])
|
||||
assert SQ != M.submodule([2, 1, 0])
|
||||
assert SQ != M
|
||||
assert M.is_submodule(SQ)
|
||||
assert not SQ.is_full_module()
|
||||
|
||||
raises(ValueError, lambda: N/F)
|
||||
raises(ValueError, lambda: F.submodule([2, 0, 0]) / N)
|
||||
raises(ValueError, lambda: R.free_module(2)/F)
|
||||
raises(CoercionFailed, lambda: F.convert(M.convert([1, x, x**2])))
|
||||
|
||||
M1 = F / [[1, 1, 1]]
|
||||
M2 = M1.submodule([1, 0, 0], [0, 1, 0])
|
||||
assert M1 == M2
|
||||
|
||||
|
||||
def test_ModulesQuotientRing():
|
||||
R = QQ.old_poly_ring(x, y, order=(("lex", x), ("ilex", y))) / [x**2 + 1]
|
||||
M1 = R.free_module(2)
|
||||
assert M1 == R.free_module(2)
|
||||
assert M1 != QQ.old_poly_ring(x).free_module(2)
|
||||
assert M1 != R.free_module(3)
|
||||
|
||||
assert [x, 1] in M1
|
||||
assert [x] not in M1
|
||||
assert [1/(R.convert(x) + 1), 2] in M1
|
||||
assert [1, 2/(1 + y)] in M1
|
||||
assert [1, 2/y] not in M1
|
||||
|
||||
assert M1.convert([x**2, y]) == [-1, y]
|
||||
|
||||
F = R.free_module(3)
|
||||
Fd = F.submodule([x**2, 0, 0], [1, 2, 0], [1, 2, 3])
|
||||
M = F.submodule([x**2 + y**2, 1, 0], [x, y, 1])
|
||||
|
||||
assert F == Fd
|
||||
assert Fd == F
|
||||
assert F != M
|
||||
assert M != F
|
||||
assert Fd != M
|
||||
assert M != Fd
|
||||
assert Fd == F.submodule(*F.basis())
|
||||
|
||||
assert Fd.is_full_module()
|
||||
assert not M.is_full_module()
|
||||
assert not Fd.is_zero()
|
||||
assert not M.is_zero()
|
||||
assert Fd.submodule().is_zero()
|
||||
|
||||
assert M.contains([x**2 + y**2 + x, -x**2 + y, 1])
|
||||
assert not M.contains([x**2 + y**2 + x, 1 + y, 2])
|
||||
assert M.contains([y**2, 1 - x*y, -x])
|
||||
|
||||
assert F.submodule([x, 0, 0]) == F.submodule([1, 0, 0])
|
||||
assert not F.submodule([y, 0, 0]) == F.submodule([1, 0, 0])
|
||||
assert F.submodule([1, 0, 0], [0, 1, 0]).union(F.submodule([0, 0, 1])) == F
|
||||
assert not M.is_submodule(0)
|
||||
|
||||
|
||||
def test_module_mul():
|
||||
R = QQ.old_poly_ring(x)
|
||||
M = R.free_module(2)
|
||||
S1 = M.submodule([x, 0], [0, x])
|
||||
S2 = M.submodule([x**2, 0], [0, x**2])
|
||||
I = R.ideal(x)
|
||||
|
||||
assert I*M == M*I == S1 == x*M == M*x
|
||||
assert I*S1 == S2 == x*S1
|
||||
|
||||
|
||||
def test_intersection():
|
||||
# SCA, example 2.8.5
|
||||
F = QQ.old_poly_ring(x, y).free_module(2)
|
||||
M1 = F.submodule([x, y], [y, 1])
|
||||
M2 = F.submodule([0, y - 1], [x, 1], [y, x])
|
||||
I = F.submodule([x, y], [y**2 - y, y - 1], [x*y + y, x + 1])
|
||||
I1, rel1, rel2 = M1.intersect(M2, relations=True)
|
||||
assert I1 == M2.intersect(M1) == I
|
||||
for i, g in enumerate(I1.gens):
|
||||
assert g == sum(c*x for c, x in zip(rel1[i], M1.gens)) \
|
||||
== sum(d*y for d, y in zip(rel2[i], M2.gens))
|
||||
|
||||
assert F.submodule([x, y]).intersect(F.submodule([y, x])).is_zero()
|
||||
|
||||
|
||||
def test_quotient():
|
||||
# SCA, example 2.8.6
|
||||
R = QQ.old_poly_ring(x, y, z)
|
||||
F = R.free_module(2)
|
||||
assert F.submodule([x*y, x*z], [y*z, x*y]).module_quotient(
|
||||
F.submodule([y, z], [z, y])) == QQ.old_poly_ring(x, y, z).ideal(x**2*y**2 - x*y*z**2)
|
||||
assert F.submodule([x, y]).module_quotient(F.submodule()).is_whole_ring()
|
||||
|
||||
M = F.submodule([x**2, x**2], [y**2, y**2])
|
||||
N = F.submodule([x + y, x + y])
|
||||
q, rel = M.module_quotient(N, relations=True)
|
||||
assert q == R.ideal(y**2, x - y)
|
||||
for i, g in enumerate(q.gens):
|
||||
assert g*N.gens[0] == sum(c*x for c, x in zip(rel[i], M.gens))
|
||||
|
||||
|
||||
def test_groebner_extendend():
|
||||
M = QQ.old_poly_ring(x, y, z).free_module(3).submodule([x + 1, y, 1], [x*y, z, z**2])
|
||||
G, R = M._groebner_vec(extended=True)
|
||||
for i, g in enumerate(G):
|
||||
assert g == sum(c*gen for c, gen in zip(R[i], M.gens))
|
||||
Reference in New Issue
Block a user