add read me
This commit is contained in:
@@ -0,0 +1,5 @@
|
||||
"""Module for algebraic geometry and commutative algebra."""
|
||||
|
||||
from .homomorphisms import homomorphism
|
||||
|
||||
__all__ = ['homomorphism']
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
356
venv/lib/python3.12/site-packages/sympy/polys/agca/extensions.py
Normal file
356
venv/lib/python3.12/site-packages/sympy/polys/agca/extensions.py
Normal file
@@ -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)
|
||||
395
venv/lib/python3.12/site-packages/sympy/polys/agca/ideals.py
Normal file
395
venv/lib/python3.12/site-packages/sympy/polys/agca/ideals.py
Normal file
@@ -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]
|
||||
1488
venv/lib/python3.12/site-packages/sympy/polys/agca/modules.py
Normal file
1488
venv/lib/python3.12/site-packages/sympy/polys/agca/modules.py
Normal file
File diff suppressed because it is too large
Load Diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -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