| """ |
| Computations with modules over polynomial rings. |
| |
| This module implements various classes that encapsulate groebner basis |
| computations for modules. Most of them should not be instantiated by hand. |
| Instead, use the constructing routines on objects you already have. |
| |
| For example, to construct a free module over ``QQ[x, y]``, call |
| ``QQ[x, y].free_module(rank)`` instead of the ``FreeModule`` constructor. |
| In fact ``FreeModule`` is an abstract base class that should not be |
| instantiated, the ``free_module`` method instead returns the implementing class |
| ``FreeModulePolyRing``. |
| |
| In general, the abstract base classes implement most functionality in terms of |
| a few non-implemented methods. The concrete base classes supply only these |
| non-implemented methods. They may also supply new implementations of the |
| convenience methods, for example if there are faster algorithms available. |
| """ |
|
|
|
|
| from copy import copy |
| from functools import reduce |
|
|
| from sympy.polys.agca.ideals import Ideal |
| from sympy.polys.domains.field import Field |
| from sympy.polys.orderings import ProductOrder, monomial_key |
| from sympy.polys.polyclasses import DMP |
| from sympy.polys.polyerrors import CoercionFailed |
| from sympy.core.basic import _aresame |
| from sympy.utilities.iterables import iterable |
|
|
| |
| |
| |
| |
| |
| |
|
|
| |
| |
| |
|
|
|
|
| class Module: |
| """ |
| Abstract base class for modules. |
| |
| Do not instantiate - use ring explicit constructors instead: |
| |
| >>> from sympy import QQ |
| >>> from sympy.abc import x |
| >>> QQ.old_poly_ring(x).free_module(2) |
| QQ[x]**2 |
| |
| Attributes: |
| |
| - dtype - type of elements |
| - ring - containing ring |
| |
| Non-implemented methods: |
| |
| - submodule |
| - quotient_module |
| - is_zero |
| - is_submodule |
| - multiply_ideal |
| |
| The method convert likely needs to be changed in subclasses. |
| """ |
|
|
| def __init__(self, ring): |
| self.ring = ring |
|
|
| def convert(self, elem, M=None): |
| """ |
| Convert ``elem`` into internal representation of this module. |
| |
| If ``M`` is not None, it should be a module containing it. |
| """ |
| if not isinstance(elem, self.dtype): |
| raise CoercionFailed |
| return elem |
|
|
| def submodule(self, *gens): |
| """Generate a submodule.""" |
| raise NotImplementedError |
|
|
| def quotient_module(self, other): |
| """Generate a quotient module.""" |
| raise NotImplementedError |
|
|
| def __truediv__(self, e): |
| if not isinstance(e, Module): |
| e = self.submodule(*e) |
| return self.quotient_module(e) |
|
|
| def contains(self, elem): |
| """Return True if ``elem`` is an element of this module.""" |
| try: |
| self.convert(elem) |
| return True |
| except CoercionFailed: |
| return False |
|
|
| def __contains__(self, elem): |
| return self.contains(elem) |
|
|
| def subset(self, other): |
| """ |
| Returns True if ``other`` is is a subset of ``self``. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> F = QQ.old_poly_ring(x).free_module(2) |
| >>> F.subset([(1, x), (x, 2)]) |
| True |
| >>> F.subset([(1/x, x), (x, 2)]) |
| False |
| """ |
| return all(self.contains(x) for x in other) |
|
|
| def __eq__(self, other): |
| return self.is_submodule(other) and other.is_submodule(self) |
|
|
| def __ne__(self, other): |
| return not (self == other) |
|
|
| def is_zero(self): |
| """Returns True if ``self`` is a zero module.""" |
| raise NotImplementedError |
|
|
| def is_submodule(self, other): |
| """Returns True if ``other`` is a submodule of ``self``.""" |
| raise NotImplementedError |
|
|
| def multiply_ideal(self, other): |
| """ |
| Multiply ``self`` by the ideal ``other``. |
| """ |
| raise NotImplementedError |
|
|
| def __mul__(self, e): |
| if not isinstance(e, Ideal): |
| try: |
| e = self.ring.ideal(e) |
| except (CoercionFailed, NotImplementedError): |
| return NotImplemented |
| return self.multiply_ideal(e) |
|
|
| __rmul__ = __mul__ |
|
|
| def identity_hom(self): |
| """Return the identity homomorphism on ``self``.""" |
| raise NotImplementedError |
|
|
|
|
| class ModuleElement: |
| """ |
| Base class for module element wrappers. |
| |
| Use this class to wrap primitive data types as module elements. It stores |
| a reference to the containing module, and implements all the arithmetic |
| operators. |
| |
| Attributes: |
| |
| - module - containing module |
| - data - internal data |
| |
| Methods that likely need change in subclasses: |
| |
| - add |
| - mul |
| - div |
| - eq |
| """ |
|
|
| def __init__(self, module, data): |
| self.module = module |
| self.data = data |
|
|
| def add(self, d1, d2): |
| """Add data ``d1`` and ``d2``.""" |
| return d1 + d2 |
|
|
| def mul(self, m, d): |
| """Multiply module data ``m`` by coefficient d.""" |
| return m * d |
|
|
| def div(self, m, d): |
| """Divide module data ``m`` by coefficient d.""" |
| return m / d |
|
|
| def eq(self, d1, d2): |
| """Return true if d1 and d2 represent the same element.""" |
| return d1 == d2 |
|
|
| def __add__(self, om): |
| if not isinstance(om, self.__class__) or om.module != self.module: |
| try: |
| om = self.module.convert(om) |
| except CoercionFailed: |
| return NotImplemented |
| return self.__class__(self.module, self.add(self.data, om.data)) |
|
|
| __radd__ = __add__ |
|
|
| def __neg__(self): |
| return self.__class__(self.module, self.mul(self.data, |
| self.module.ring.convert(-1))) |
|
|
| def __sub__(self, om): |
| if not isinstance(om, self.__class__) or om.module != self.module: |
| try: |
| om = self.module.convert(om) |
| except CoercionFailed: |
| return NotImplemented |
| return self.__add__(-om) |
|
|
| def __rsub__(self, om): |
| return (-self).__add__(om) |
|
|
| def __mul__(self, o): |
| if not isinstance(o, self.module.ring.dtype): |
| try: |
| o = self.module.ring.convert(o) |
| except CoercionFailed: |
| return NotImplemented |
| return self.__class__(self.module, self.mul(self.data, o)) |
|
|
| __rmul__ = __mul__ |
|
|
| def __truediv__(self, o): |
| if not isinstance(o, self.module.ring.dtype): |
| try: |
| o = self.module.ring.convert(o) |
| except CoercionFailed: |
| return NotImplemented |
| return self.__class__(self.module, self.div(self.data, o)) |
|
|
| def __eq__(self, om): |
| if not isinstance(om, self.__class__) or om.module != self.module: |
| try: |
| om = self.module.convert(om) |
| except CoercionFailed: |
| return False |
| return self.eq(self.data, om.data) |
|
|
| def __ne__(self, om): |
| return not self == om |
|
|
| |
| |
| |
|
|
|
|
| class FreeModuleElement(ModuleElement): |
| """Element of a free module. Data stored as a tuple.""" |
|
|
| def add(self, d1, d2): |
| return tuple(x + y for x, y in zip(d1, d2)) |
|
|
| def mul(self, d, p): |
| return tuple(x * p for x in d) |
|
|
| def div(self, d, p): |
| return tuple(x / p for x in d) |
|
|
| def __repr__(self): |
| from sympy.printing.str import sstr |
| data = self.data |
| if any(isinstance(x, DMP) for x in data): |
| data = [self.module.ring.to_sympy(x) for x in data] |
| return '[' + ', '.join(sstr(x) for x in data) + ']' |
|
|
| def __iter__(self): |
| return self.data.__iter__() |
|
|
| def __getitem__(self, idx): |
| return self.data[idx] |
|
|
|
|
| class FreeModule(Module): |
| """ |
| Abstract base class for free modules. |
| |
| Additional attributes: |
| |
| - rank - rank of the free module |
| |
| Non-implemented methods: |
| |
| - submodule |
| """ |
|
|
| dtype = FreeModuleElement |
|
|
| def __init__(self, ring, rank): |
| Module.__init__(self, ring) |
| self.rank = rank |
|
|
| def __repr__(self): |
| return repr(self.ring) + "**" + repr(self.rank) |
|
|
| def is_submodule(self, other): |
| """ |
| Returns True if ``other`` is a submodule of ``self``. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> F = QQ.old_poly_ring(x).free_module(2) |
| >>> M = F.submodule([2, x]) |
| >>> F.is_submodule(F) |
| True |
| >>> F.is_submodule(M) |
| True |
| >>> M.is_submodule(F) |
| False |
| """ |
| if isinstance(other, SubModule): |
| return other.container == self |
| if isinstance(other, FreeModule): |
| return other.ring == self.ring and other.rank == self.rank |
| return False |
|
|
| def convert(self, elem, M=None): |
| """ |
| Convert ``elem`` into the internal representation. |
| |
| This method is called implicitly whenever computations involve elements |
| not in the internal representation. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> F = QQ.old_poly_ring(x).free_module(2) |
| >>> F.convert([1, 0]) |
| [1, 0] |
| """ |
| if isinstance(elem, FreeModuleElement): |
| if elem.module is self: |
| return elem |
| if elem.module.rank != self.rank: |
| raise CoercionFailed |
| return FreeModuleElement(self, |
| tuple(self.ring.convert(x, elem.module.ring) for x in elem.data)) |
| elif iterable(elem): |
| tpl = tuple(self.ring.convert(x) for x in elem) |
| if len(tpl) != self.rank: |
| raise CoercionFailed |
| return FreeModuleElement(self, tpl) |
| elif _aresame(elem, 0): |
| return FreeModuleElement(self, (self.ring.convert(0),)*self.rank) |
| else: |
| raise CoercionFailed |
|
|
| def is_zero(self): |
| """ |
| Returns True if ``self`` is a zero module. |
| |
| (If, as this implementation assumes, the coefficient ring is not the |
| zero ring, then this is equivalent to the rank being zero.) |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> QQ.old_poly_ring(x).free_module(0).is_zero() |
| True |
| >>> QQ.old_poly_ring(x).free_module(1).is_zero() |
| False |
| """ |
| return self.rank == 0 |
|
|
| def basis(self): |
| """ |
| Return a set of basis elements. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> QQ.old_poly_ring(x).free_module(3).basis() |
| ([1, 0, 0], [0, 1, 0], [0, 0, 1]) |
| """ |
| from sympy.matrices import eye |
| M = eye(self.rank) |
| return tuple(self.convert(M.row(i)) for i in range(self.rank)) |
|
|
| def quotient_module(self, submodule): |
| """ |
| Return a quotient module. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> M = QQ.old_poly_ring(x).free_module(2) |
| >>> M.quotient_module(M.submodule([1, x], [x, 2])) |
| QQ[x]**2/<[1, x], [x, 2]> |
| |
| Or more conicisely, using the overloaded division operator: |
| |
| >>> QQ.old_poly_ring(x).free_module(2) / [[1, x], [x, 2]] |
| QQ[x]**2/<[1, x], [x, 2]> |
| """ |
| return QuotientModule(self.ring, self, submodule) |
|
|
| def multiply_ideal(self, other): |
| """ |
| Multiply ``self`` by the ideal ``other``. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> I = QQ.old_poly_ring(x).ideal(x) |
| >>> F = QQ.old_poly_ring(x).free_module(2) |
| >>> F.multiply_ideal(I) |
| <[x, 0], [0, x]> |
| """ |
| return self.submodule(*self.basis()).multiply_ideal(other) |
|
|
| def identity_hom(self): |
| """ |
| Return the identity homomorphism on ``self``. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> QQ.old_poly_ring(x).free_module(2).identity_hom() |
| Matrix([ |
| [1, 0], : QQ[x]**2 -> QQ[x]**2 |
| [0, 1]]) |
| """ |
| from sympy.polys.agca.homomorphisms import homomorphism |
| return homomorphism(self, self, self.basis()) |
|
|
|
|
| class FreeModulePolyRing(FreeModule): |
| """ |
| Free module over a generalized polynomial ring. |
| |
| Do not instantiate this, use the constructor method of the ring instead: |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> F = QQ.old_poly_ring(x).free_module(3) |
| >>> F |
| QQ[x]**3 |
| >>> F.contains([x, 1, 0]) |
| True |
| >>> F.contains([1/x, 0, 1]) |
| False |
| """ |
|
|
| def __init__(self, ring, rank): |
| from sympy.polys.domains.old_polynomialring import PolynomialRingBase |
| FreeModule.__init__(self, ring, rank) |
| if not isinstance(ring, PolynomialRingBase): |
| raise NotImplementedError('This implementation only works over ' |
| + 'polynomial rings, got %s' % ring) |
| if not isinstance(ring.dom, Field): |
| raise NotImplementedError('Ground domain must be a field, ' |
| + 'got %s' % ring.dom) |
|
|
| def submodule(self, *gens, **opts): |
| """ |
| Generate a submodule. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x, y |
| >>> from sympy import QQ |
| >>> M = QQ.old_poly_ring(x, y).free_module(2).submodule([x, x + y]) |
| >>> M |
| <[x, x + y]> |
| >>> M.contains([2*x, 2*x + 2*y]) |
| True |
| >>> M.contains([x, y]) |
| False |
| """ |
| return SubModulePolyRing(gens, self, **opts) |
|
|
|
|
| class FreeModuleQuotientRing(FreeModule): |
| """ |
| Free module over a quotient ring. |
| |
| Do not instantiate this, use the constructor method of the ring instead: |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> F = (QQ.old_poly_ring(x)/[x**2 + 1]).free_module(3) |
| >>> F |
| (QQ[x]/<x**2 + 1>)**3 |
| |
| Attributes |
| |
| - quot - the quotient module `R^n / IR^n`, where `R/I` is our ring |
| """ |
|
|
| def __init__(self, ring, rank): |
| from sympy.polys.domains.quotientring import QuotientRing |
| FreeModule.__init__(self, ring, rank) |
| if not isinstance(ring, QuotientRing): |
| raise NotImplementedError('This implementation only works over ' |
| + 'quotient rings, got %s' % ring) |
| F = self.ring.ring.free_module(self.rank) |
| self.quot = F / (self.ring.base_ideal*F) |
|
|
| def __repr__(self): |
| return "(" + repr(self.ring) + ")" + "**" + repr(self.rank) |
|
|
| def submodule(self, *gens, **opts): |
| """ |
| Generate a submodule. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x, y |
| >>> from sympy import QQ |
| >>> M = (QQ.old_poly_ring(x, y)/[x**2 - y**2]).free_module(2).submodule([x, x + y]) |
| >>> M |
| <[x + <x**2 - y**2>, x + y + <x**2 - y**2>]> |
| >>> M.contains([y**2, x**2 + x*y]) |
| True |
| >>> M.contains([x, y]) |
| False |
| """ |
| return SubModuleQuotientRing(gens, self, **opts) |
|
|
| def lift(self, elem): |
| """ |
| Lift the element ``elem`` of self to the module self.quot. |
| |
| Note that self.quot is the same set as self, just as an R-module |
| and not as an R/I-module, so this makes sense. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> F = (QQ.old_poly_ring(x)/[x**2 + 1]).free_module(2) |
| >>> e = F.convert([1, 0]) |
| >>> e |
| [1 + <x**2 + 1>, 0 + <x**2 + 1>] |
| >>> L = F.quot |
| >>> l = F.lift(e) |
| >>> l |
| [1, 0] + <[x**2 + 1, 0], [0, x**2 + 1]> |
| >>> L.contains(l) |
| True |
| """ |
| return self.quot.convert([x.data for x in elem]) |
|
|
| def unlift(self, elem): |
| """ |
| Push down an element of self.quot to self. |
| |
| This undoes ``lift``. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> F = (QQ.old_poly_ring(x)/[x**2 + 1]).free_module(2) |
| >>> e = F.convert([1, 0]) |
| >>> l = F.lift(e) |
| >>> e == l |
| False |
| >>> e == F.unlift(l) |
| True |
| """ |
| return self.convert(elem.data) |
|
|
| |
| |
| |
|
|
|
|
| class SubModule(Module): |
| """ |
| Base class for submodules. |
| |
| Attributes: |
| |
| - container - containing module |
| - gens - generators (subset of containing module) |
| - rank - rank of containing module |
| |
| Non-implemented methods: |
| |
| - _contains |
| - _syzygies |
| - _in_terms_of_generators |
| - _intersect |
| - _module_quotient |
| |
| Methods that likely need change in subclasses: |
| |
| - reduce_element |
| """ |
|
|
| def __init__(self, gens, container): |
| Module.__init__(self, container.ring) |
| self.gens = tuple(container.convert(x) for x in gens) |
| self.container = container |
| self.rank = container.rank |
| self.ring = container.ring |
| self.dtype = container.dtype |
|
|
| def __repr__(self): |
| return "<" + ", ".join(repr(x) for x in self.gens) + ">" |
|
|
| def _contains(self, other): |
| """Implementation of containment. |
| Other is guaranteed to be FreeModuleElement.""" |
| raise NotImplementedError |
|
|
| def _syzygies(self): |
| """Implementation of syzygy computation wrt self generators.""" |
| raise NotImplementedError |
|
|
| def _in_terms_of_generators(self, e): |
| """Implementation of expression in terms of generators.""" |
| raise NotImplementedError |
|
|
| def convert(self, elem, M=None): |
| """ |
| Convert ``elem`` into the internal represantition. |
| |
| Mostly called implicitly. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> M = QQ.old_poly_ring(x).free_module(2).submodule([1, x]) |
| >>> M.convert([2, 2*x]) |
| [2, 2*x] |
| """ |
| if isinstance(elem, self.container.dtype) and elem.module is self: |
| return elem |
| r = copy(self.container.convert(elem, M)) |
| r.module = self |
| if not self._contains(r): |
| raise CoercionFailed |
| return r |
|
|
| def _intersect(self, other): |
| """Implementation of intersection. |
| Other is guaranteed to be a submodule of same free module.""" |
| raise NotImplementedError |
|
|
| def _module_quotient(self, other): |
| """Implementation of quotient. |
| Other is guaranteed to be a submodule of same free module.""" |
| raise NotImplementedError |
|
|
| def intersect(self, other, **options): |
| """ |
| Returns the intersection of ``self`` with submodule ``other``. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x, y |
| >>> from sympy import QQ |
| >>> F = QQ.old_poly_ring(x, y).free_module(2) |
| >>> F.submodule([x, x]).intersect(F.submodule([y, y])) |
| <[x*y, x*y]> |
| |
| Some implementation allow further options to be passed. Currently, to |
| only one implemented is ``relations=True``, in which case the function |
| will return a triple ``(res, rela, relb)``, where ``res`` is the |
| intersection module, and ``rela`` and ``relb`` are lists of coefficient |
| vectors, expressing the generators of ``res`` in terms of the |
| generators of ``self`` (``rela``) and ``other`` (``relb``). |
| |
| >>> F.submodule([x, x]).intersect(F.submodule([y, y]), relations=True) |
| (<[x*y, x*y]>, [(DMP_Python([[1, 0]], QQ),)], [(DMP_Python([[1], []], QQ),)]) |
| |
| The above result says: the intersection module is generated by the |
| single element `(-xy, -xy) = -y (x, x) = -x (y, y)`, where |
| `(x, x)` and `(y, y)` respectively are the unique generators of |
| the two modules being intersected. |
| """ |
| if not isinstance(other, SubModule): |
| raise TypeError('%s is not a SubModule' % other) |
| if other.container != self.container: |
| raise ValueError( |
| '%s is contained in a different free module' % other) |
| return self._intersect(other, **options) |
|
|
| def module_quotient(self, other, **options): |
| r""" |
| Returns the module quotient of ``self`` by submodule ``other``. |
| |
| That is, if ``self`` is the module `M` and ``other`` is `N`, then |
| return the ideal `\{f \in R | fN \subset M\}`. |
| |
| Examples |
| ======== |
| |
| >>> from sympy import QQ |
| >>> from sympy.abc import x, y |
| >>> F = QQ.old_poly_ring(x, y).free_module(2) |
| >>> S = F.submodule([x*y, x*y]) |
| >>> T = F.submodule([x, x]) |
| >>> S.module_quotient(T) |
| <y> |
| |
| Some implementations allow further options to be passed. Currently, the |
| only one implemented is ``relations=True``, which may only be passed |
| if ``other`` is principal. In this case the function |
| will return a pair ``(res, rel)`` where ``res`` is the ideal, and |
| ``rel`` is a list of coefficient vectors, expressing the generators of |
| the ideal, multiplied by the generator of ``other`` in terms of |
| generators of ``self``. |
| |
| >>> S.module_quotient(T, relations=True) |
| (<y>, [[DMP_Python([[1]], QQ)]]) |
| |
| This means that the quotient ideal is generated by the single element |
| `y`, and that `y (x, x) = 1 (xy, xy)`, `(x, x)` and `(xy, xy)` being |
| the generators of `T` and `S`, respectively. |
| """ |
| if not isinstance(other, SubModule): |
| raise TypeError('%s is not a SubModule' % other) |
| if other.container != self.container: |
| raise ValueError( |
| '%s is contained in a different free module' % other) |
| return self._module_quotient(other, **options) |
|
|
| def union(self, other): |
| """ |
| Returns the module generated by the union of ``self`` and ``other``. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> F = QQ.old_poly_ring(x).free_module(1) |
| >>> M = F.submodule([x**2 + x]) # <x(x+1)> |
| >>> N = F.submodule([x**2 - 1]) # <(x-1)(x+1)> |
| >>> M.union(N) == F.submodule([x+1]) |
| True |
| """ |
| if not isinstance(other, SubModule): |
| raise TypeError('%s is not a SubModule' % other) |
| if other.container != self.container: |
| raise ValueError( |
| '%s is contained in a different free module' % other) |
| return self.__class__(self.gens + other.gens, self.container) |
|
|
| def is_zero(self): |
| """ |
| Return True if ``self`` is a zero module. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> F = QQ.old_poly_ring(x).free_module(2) |
| >>> F.submodule([x, 1]).is_zero() |
| False |
| >>> F.submodule([0, 0]).is_zero() |
| True |
| """ |
| return all(x == 0 for x in self.gens) |
|
|
| def submodule(self, *gens): |
| """ |
| Generate a submodule. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> M = QQ.old_poly_ring(x).free_module(2).submodule([x, 1]) |
| >>> M.submodule([x**2, x]) |
| <[x**2, x]> |
| """ |
| if not self.subset(gens): |
| raise ValueError('%s not a subset of %s' % (gens, self)) |
| return self.__class__(gens, self.container) |
|
|
| def is_full_module(self): |
| """ |
| Return True if ``self`` is the entire free module. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> F = QQ.old_poly_ring(x).free_module(2) |
| >>> F.submodule([x, 1]).is_full_module() |
| False |
| >>> F.submodule([1, 1], [1, 2]).is_full_module() |
| True |
| """ |
| return all(self.contains(x) for x in self.container.basis()) |
|
|
| def is_submodule(self, other): |
| """ |
| Returns True if ``other`` is a submodule of ``self``. |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> F = QQ.old_poly_ring(x).free_module(2) |
| >>> M = F.submodule([2, x]) |
| >>> N = M.submodule([2*x, x**2]) |
| >>> M.is_submodule(M) |
| True |
| >>> M.is_submodule(N) |
| True |
| >>> N.is_submodule(M) |
| False |
| """ |
| if isinstance(other, SubModule): |
| return self.container == other.container and \ |
| all(self.contains(x) for x in other.gens) |
| if isinstance(other, (FreeModule, QuotientModule)): |
| return self.container == other and self.is_full_module() |
| return False |
|
|
| def syzygy_module(self, **opts): |
| r""" |
| Compute the syzygy module of the generators of ``self``. |
| |
| Suppose `M` is generated by `f_1, \ldots, f_n` over the ring |
| `R`. Consider the homomorphism `\phi: R^n \to M`, given by |
| sending `(r_1, \ldots, r_n) \to r_1 f_1 + \cdots + r_n f_n`. |
| The syzygy module is defined to be the kernel of `\phi`. |
| |
| Examples |
| ======== |
| |
| The syzygy module is zero iff the generators generate freely a free |
| submodule: |
| |
| >>> from sympy.abc import x, y |
| >>> from sympy import QQ |
| >>> QQ.old_poly_ring(x).free_module(2).submodule([1, 0], [1, 1]).syzygy_module().is_zero() |
| True |
| |
| A slightly more interesting example: |
| |
| >>> M = QQ.old_poly_ring(x, y).free_module(2).submodule([x, 2*x], [y, 2*y]) |
| >>> S = QQ.old_poly_ring(x, y).free_module(2).submodule([y, -x]) |
| >>> M.syzygy_module() == S |
| True |
| """ |
| F = self.ring.free_module(len(self.gens)) |
| |
| |
| |
| return F.submodule(*[x for x in self._syzygies() if F.convert(x) != 0], |
| **opts) |
|
|
| def in_terms_of_generators(self, e): |
| """ |
| Express element ``e`` of ``self`` in terms of the generators. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> F = QQ.old_poly_ring(x).free_module(2) |
| >>> M = F.submodule([1, 0], [1, 1]) |
| >>> M.in_terms_of_generators([x, x**2]) # doctest: +SKIP |
| [DMP_Python([-1, 1, 0], QQ), DMP_Python([1, 0, 0], QQ)] |
| """ |
| try: |
| e = self.convert(e) |
| except CoercionFailed: |
| raise ValueError('%s is not an element of %s' % (e, self)) |
| return self._in_terms_of_generators(e) |
|
|
| 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 quotient_module(self, other, **opts): |
| """ |
| Return a quotient module. |
| |
| This is the same as taking a submodule of a quotient of the containing |
| module. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> F = QQ.old_poly_ring(x).free_module(2) |
| >>> S1 = F.submodule([x, 1]) |
| >>> S2 = F.submodule([x**2, x]) |
| >>> S1.quotient_module(S2) |
| <[x, 1] + <[x**2, x]>> |
| |
| Or more coincisely, using the overloaded division operator: |
| |
| >>> F.submodule([x, 1]) / [(x**2, x)] |
| <[x, 1] + <[x**2, x]>> |
| """ |
| if not self.is_submodule(other): |
| raise ValueError('%s not a submodule of %s' % (other, self)) |
| return SubQuotientModule(self.gens, |
| self.container.quotient_module(other), **opts) |
|
|
| def __add__(self, oth): |
| return self.container.quotient_module(self).convert(oth) |
|
|
| __radd__ = __add__ |
|
|
| def multiply_ideal(self, I): |
| """ |
| Multiply ``self`` by the ideal ``I``. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> I = QQ.old_poly_ring(x).ideal(x**2) |
| >>> M = QQ.old_poly_ring(x).free_module(2).submodule([1, 1]) |
| >>> I*M |
| <[x**2, x**2]> |
| """ |
| return self.submodule(*[x*g for [x] in I._module.gens for g in self.gens]) |
|
|
| def inclusion_hom(self): |
| """ |
| Return a homomorphism representing the inclusion map of ``self``. |
| |
| That is, the natural map from ``self`` to ``self.container``. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> QQ.old_poly_ring(x).free_module(2).submodule([x, x]).inclusion_hom() |
| Matrix([ |
| [1, 0], : <[x, x]> -> QQ[x]**2 |
| [0, 1]]) |
| """ |
| return self.container.identity_hom().restrict_domain(self) |
|
|
| def identity_hom(self): |
| """ |
| Return the identity homomorphism on ``self``. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> QQ.old_poly_ring(x).free_module(2).submodule([x, x]).identity_hom() |
| Matrix([ |
| [1, 0], : <[x, x]> -> <[x, x]> |
| [0, 1]]) |
| """ |
| return self.container.identity_hom().restrict_domain( |
| self).restrict_codomain(self) |
|
|
|
|
| class SubQuotientModule(SubModule): |
| """ |
| Submodule of a quotient module. |
| |
| Equivalently, quotient module of a submodule. |
| |
| Do not instantiate this, instead use the submodule or quotient_module |
| constructing methods: |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> F = QQ.old_poly_ring(x).free_module(2) |
| >>> S = F.submodule([1, 0], [1, x]) |
| >>> Q = F/[(1, 0)] |
| >>> S/[(1, 0)] == Q.submodule([5, x]) |
| True |
| |
| Attributes: |
| |
| - base - base module we are quotient of |
| - killed_module - submodule used to form the quotient |
| """ |
| def __init__(self, gens, container, **opts): |
| SubModule.__init__(self, gens, container) |
| self.killed_module = self.container.killed_module |
| |
| |
| self.base = self.container.base.submodule( |
| *[x.data for x in self.gens], **opts).union(self.killed_module) |
|
|
| def _contains(self, elem): |
| return self.base.contains(elem.data) |
|
|
| def _syzygies(self): |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| return [X[:len(self.gens)] for X in self.base._syzygies()] |
|
|
| def _in_terms_of_generators(self, e): |
| return self.base._in_terms_of_generators(e.data)[:len(self.gens)] |
|
|
| def is_full_module(self): |
| """ |
| Return True if ``self`` is the entire free module. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> F = QQ.old_poly_ring(x).free_module(2) |
| >>> F.submodule([x, 1]).is_full_module() |
| False |
| >>> F.submodule([1, 1], [1, 2]).is_full_module() |
| True |
| """ |
| return self.base.is_full_module() |
|
|
| def quotient_hom(self): |
| """ |
| Return the quotient homomorphism to self. |
| |
| That is, return the natural map from ``self.base`` to ``self``. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> M = (QQ.old_poly_ring(x).free_module(2) / [(1, x)]).submodule([1, 0]) |
| >>> M.quotient_hom() |
| Matrix([ |
| [1, 0], : <[1, 0], [1, x]> -> <[1, 0] + <[1, x]>, [1, x] + <[1, x]>> |
| [0, 1]]) |
| """ |
| return self.base.identity_hom().quotient_codomain(self.killed_module) |
|
|
|
|
| _subs0 = lambda x: x[0] |
| _subs1 = lambda x: x[1:] |
|
|
|
|
| class ModuleOrder(ProductOrder): |
| """A product monomial order with a zeroth term as module index.""" |
|
|
| def __init__(self, o1, o2, TOP): |
| if TOP: |
| ProductOrder.__init__(self, (o2, _subs1), (o1, _subs0)) |
| else: |
| ProductOrder.__init__(self, (o1, _subs0), (o2, _subs1)) |
|
|
|
|
| class SubModulePolyRing(SubModule): |
| """ |
| Submodule of a free module over a generalized polynomial ring. |
| |
| Do not instantiate this, use the constructor method of FreeModule instead: |
| |
| >>> from sympy.abc import x, y |
| >>> from sympy import QQ |
| >>> F = QQ.old_poly_ring(x, y).free_module(2) |
| >>> F.submodule([x, y], [1, 0]) |
| <[x, y], [1, 0]> |
| |
| Attributes: |
| |
| - order - monomial order used |
| """ |
|
|
| |
| |
|
|
| def __init__(self, gens, container, order="lex", TOP=True): |
| SubModule.__init__(self, gens, container) |
| if not isinstance(container, FreeModulePolyRing): |
| raise NotImplementedError('This implementation is for submodules of ' |
| + 'FreeModulePolyRing, got %s' % container) |
| self.order = ModuleOrder(monomial_key(order), self.ring.order, TOP) |
| self._gb = None |
| self._gbe = None |
|
|
| def __eq__(self, other): |
| if isinstance(other, SubModulePolyRing) and self.order != other.order: |
| return False |
| return SubModule.__eq__(self, other) |
|
|
| def _groebner(self, extended=False): |
| """Returns a standard basis in sdm form.""" |
| from sympy.polys.distributedmodules import sdm_groebner, sdm_nf_mora |
| if self._gbe is None and extended: |
| gb, gbe = sdm_groebner( |
| [self.ring._vector_to_sdm(x, self.order) for x in self.gens], |
| sdm_nf_mora, self.order, self.ring.dom, extended=True) |
| self._gb, self._gbe = tuple(gb), tuple(gbe) |
| if self._gb is None: |
| self._gb = tuple(sdm_groebner( |
| [self.ring._vector_to_sdm(x, self.order) for x in self.gens], |
| sdm_nf_mora, self.order, self.ring.dom)) |
| if extended: |
| return self._gb, self._gbe |
| else: |
| return self._gb |
|
|
| def _groebner_vec(self, extended=False): |
| """Returns a standard basis in element form.""" |
| if not extended: |
| return [FreeModuleElement(self, |
| tuple(self.ring._sdm_to_vector(x, self.rank))) |
| for x in self._groebner()] |
| gb, gbe = self._groebner(extended=True) |
| return ([self.convert(self.ring._sdm_to_vector(x, self.rank)) |
| for x in gb], |
| [self.ring._sdm_to_vector(x, len(self.gens)) for x in gbe]) |
|
|
| def _contains(self, x): |
| from sympy.polys.distributedmodules import sdm_zero, sdm_nf_mora |
| return sdm_nf_mora(self.ring._vector_to_sdm(x, self.order), |
| self._groebner(), self.order, self.ring.dom) == \ |
| sdm_zero() |
|
|
| def _syzygies(self): |
| """Compute syzygies. See [SCA, algorithm 2.5.4].""" |
| |
| |
|
|
| |
| k = len(self.gens) |
| r = self.rank |
| zero = self.ring.convert(0) |
| one = self.ring.convert(1) |
| Rkr = self.ring.free_module(r + k) |
| newgens = [] |
| for j, f in enumerate(self.gens): |
| m = [0]*(r + k) |
| for i, v in enumerate(f): |
| m[i] = v |
| for i in range(k): |
| m[r + i] = one if j == i else zero |
| m = FreeModuleElement(Rkr, tuple(m)) |
| newgens.append(m) |
| |
| |
| F = Rkr.submodule(*newgens, order='ilex', TOP=False) |
|
|
| |
| G = F._groebner_vec() |
|
|
| |
| G0 = [x[r:] for x in G if all(y == zero for y in x[:r])] |
|
|
| |
| return G0 |
|
|
| def _in_terms_of_generators(self, e): |
| """Expression in terms of generators. See [SCA, 2.8.1].""" |
| |
| M = self.ring.free_module(self.rank).submodule(*((e,) + self.gens)) |
| S = M.syzygy_module( |
| order="ilex", TOP=False) |
| G = S._groebner_vec() |
| |
| e = [x for x in G if self.ring.is_unit(x[0])][0] |
| return [-x/e[0] for x in e[1:]] |
|
|
| def reduce_element(self, x, NF=None): |
| """ |
| Reduce the element ``x`` of our container modulo ``self``. |
| |
| This applies the normal form ``NF`` to ``x``. If ``NF`` is passed |
| as none, the default Mora normal form is used (which is not unique!). |
| """ |
| from sympy.polys.distributedmodules import sdm_nf_mora |
| if NF is None: |
| NF = sdm_nf_mora |
| return self.container.convert(self.ring._sdm_to_vector(NF( |
| self.ring._vector_to_sdm(x, self.order), self._groebner(), |
| self.order, self.ring.dom), |
| self.rank)) |
|
|
| def _intersect(self, other, relations=False): |
| |
| fi = self.gens |
| hi = other.gens |
| r = self.rank |
| ci = [[0]*(2*r) for _ in range(r)] |
| for k in range(r): |
| ci[k][k] = 1 |
| ci[k][r + k] = 1 |
| di = [list(f) + [0]*r for f in fi] |
| ei = [[0]*r + list(h) for h in hi] |
| syz = self.ring.free_module(2*r).submodule(*(ci + di + ei))._syzygies() |
| nonzero = [x for x in syz if any(y != self.ring.zero for y in x[:r])] |
| res = self.container.submodule(*([-y for y in x[:r]] for x in nonzero)) |
| reln1 = [x[r:r + len(fi)] for x in nonzero] |
| reln2 = [x[r + len(fi):] for x in nonzero] |
| if relations: |
| return res, reln1, reln2 |
| return res |
|
|
| def _module_quotient(self, other, relations=False): |
| |
| if relations and len(other.gens) != 1: |
| raise NotImplementedError |
| if len(other.gens) == 0: |
| return self.ring.ideal(1) |
| elif len(other.gens) == 1: |
| |
| |
| |
| |
| |
| g1 = list(other.gens[0]) + [1] |
| gi = [list(x) + [0] for x in self.gens] |
| |
| M = self.ring.free_module(self.rank + 1).submodule(*([g1] + gi), |
| order='ilex', TOP=False) |
| if not relations: |
| return self.ring.ideal(*[x[-1] for x in M._groebner_vec() if |
| all(y == self.ring.zero for y in x[:-1])]) |
| else: |
| G, R = M._groebner_vec(extended=True) |
| indices = [i for i, x in enumerate(G) if |
| all(y == self.ring.zero for y in x[:-1])] |
| return (self.ring.ideal(*[G[i][-1] for i in indices]), |
| [[-x for x in R[i][1:]] for i in indices]) |
| |
| |
| |
| return reduce(lambda x, y: x.intersect(y), |
| (self._module_quotient(self.container.submodule(x)) for x in other.gens)) |
|
|
|
|
| class SubModuleQuotientRing(SubModule): |
| """ |
| Class for submodules of free modules over quotient rings. |
| |
| Do not instantiate this. Instead use the submodule methods. |
| |
| >>> from sympy.abc import x, y |
| >>> from sympy import QQ |
| >>> M = (QQ.old_poly_ring(x, y)/[x**2 - y**2]).free_module(2).submodule([x, x + y]) |
| >>> M |
| <[x + <x**2 - y**2>, x + y + <x**2 - y**2>]> |
| >>> M.contains([y**2, x**2 + x*y]) |
| True |
| >>> M.contains([x, y]) |
| False |
| |
| Attributes: |
| |
| - quot - the subquotient of `R^n/IR^n` generated by lifts of our generators |
| """ |
|
|
| def __init__(self, gens, container): |
| SubModule.__init__(self, gens, container) |
| self.quot = self.container.quot.submodule( |
| *[self.container.lift(x) for x in self.gens]) |
|
|
| def _contains(self, elem): |
| return self.quot._contains(self.container.lift(elem)) |
|
|
| def _syzygies(self): |
| return [tuple(self.ring.convert(y, self.quot.ring) for y in x) |
| for x in self.quot._syzygies()] |
|
|
| def _in_terms_of_generators(self, elem): |
| return [self.ring.convert(x, self.quot.ring) for x in |
| self.quot._in_terms_of_generators(self.container.lift(elem))] |
|
|
| |
| |
| |
|
|
|
|
| class QuotientModuleElement(ModuleElement): |
| """Element of a quotient module.""" |
|
|
| def eq(self, d1, d2): |
| """Equality comparison.""" |
| return self.module.killed_module.contains(d1 - d2) |
|
|
| def __repr__(self): |
| return repr(self.data) + " + " + repr(self.module.killed_module) |
|
|
|
|
| class QuotientModule(Module): |
| """ |
| Class for quotient modules. |
| |
| Do not instantiate this directly. For subquotients, see the |
| SubQuotientModule class. |
| |
| Attributes: |
| |
| - base - the base module we are a quotient of |
| - killed_module - the submodule used to form the quotient |
| - rank of the base |
| """ |
|
|
| dtype = QuotientModuleElement |
|
|
| def __init__(self, ring, base, submodule): |
| Module.__init__(self, ring) |
| if not base.is_submodule(submodule): |
| raise ValueError('%s is not a submodule of %s' % (submodule, base)) |
| self.base = base |
| self.killed_module = submodule |
| self.rank = base.rank |
|
|
| def __repr__(self): |
| return repr(self.base) + "/" + repr(self.killed_module) |
|
|
| def is_zero(self): |
| """ |
| Return True if ``self`` is a zero module. |
| |
| This happens if and only if the base module is the same as the |
| submodule being killed. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> F = QQ.old_poly_ring(x).free_module(2) |
| >>> (F/[(1, 0)]).is_zero() |
| False |
| >>> (F/[(1, 0), (0, 1)]).is_zero() |
| True |
| """ |
| return self.base == self.killed_module |
|
|
| def is_submodule(self, other): |
| """ |
| Return True if ``other`` is a submodule of ``self``. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> Q = QQ.old_poly_ring(x).free_module(2) / [(x, x)] |
| >>> S = Q.submodule([1, 0]) |
| >>> Q.is_submodule(S) |
| True |
| >>> S.is_submodule(Q) |
| False |
| """ |
| if isinstance(other, QuotientModule): |
| return self.killed_module == other.killed_module and \ |
| self.base.is_submodule(other.base) |
| if isinstance(other, SubQuotientModule): |
| return other.container == self |
| return False |
|
|
| def submodule(self, *gens, **opts): |
| """ |
| Generate a submodule. |
| |
| This is the same as taking a quotient of a submodule of the base |
| module. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> Q = QQ.old_poly_ring(x).free_module(2) / [(x, x)] |
| >>> Q.submodule([x, 0]) |
| <[x, 0] + <[x, x]>> |
| """ |
| return SubQuotientModule(gens, self, **opts) |
|
|
| def convert(self, elem, M=None): |
| """ |
| Convert ``elem`` into the internal representation. |
| |
| This method is called implicitly whenever computations involve elements |
| not in the internal representation. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> F = QQ.old_poly_ring(x).free_module(2) / [(1, 2), (1, x)] |
| >>> F.convert([1, 0]) |
| [1, 0] + <[1, 2], [1, x]> |
| """ |
| if isinstance(elem, QuotientModuleElement): |
| if elem.module is self: |
| return elem |
| if self.killed_module.is_submodule(elem.module.killed_module): |
| return QuotientModuleElement(self, self.base.convert(elem.data)) |
| raise CoercionFailed |
| return QuotientModuleElement(self, self.base.convert(elem)) |
|
|
| def identity_hom(self): |
| """ |
| Return the identity homomorphism on ``self``. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> M = QQ.old_poly_ring(x).free_module(2) / [(1, 2), (1, x)] |
| >>> M.identity_hom() |
| Matrix([ |
| [1, 0], : QQ[x]**2/<[1, 2], [1, x]> -> QQ[x]**2/<[1, 2], [1, x]> |
| [0, 1]]) |
| """ |
| return self.base.identity_hom().quotient_codomain( |
| self.killed_module).quotient_domain(self.killed_module) |
|
|
| def quotient_hom(self): |
| """ |
| Return the quotient homomorphism to ``self``. |
| |
| That is, return a homomorphism representing the natural map from |
| ``self.base`` to ``self``. |
| |
| Examples |
| ======== |
| |
| >>> from sympy.abc import x |
| >>> from sympy import QQ |
| >>> M = QQ.old_poly_ring(x).free_module(2) / [(1, 2), (1, x)] |
| >>> M.quotient_hom() |
| Matrix([ |
| [1, 0], : QQ[x]**2 -> QQ[x]**2/<[1, 2], [1, x]> |
| [0, 1]]) |
| """ |
| return self.base.identity_hom().quotient_codomain( |
| self.killed_module) |
|
|