Ask Your Question

Revision history [back]

click to hide/show revision 1
initial version

The code below (following this tutorial) seems to do the trick. I might turn it into an addition to the Sage codebase at some point (it needs documentation and tests for that, at the very least), but I don't have the time. If you feel like doing that, be my guest!

Just save to product_ring.py and, in Sage do sage: %runfile product_ring.py # from command line

or

load product_ring.py # from notebook.

import sage
from sage.rings.ring import Ring
from sage.rings.all import ZZ
from sage.structure.element import RingElement
from sage.categories.category import Category
from sage.structure.unique_representation import UniqueRepresentation
from sage.categories.rings import Rings
from sage.categories.pushout import ConstructionFunctor

class ProductRingElement(RingElement):
    def __init__(self, data, parent=None):
        if parent is None:
            raise ValueError, "The parent must be provided"
        self._data = tuple(data)
        RingElement.__init__(self,parent)

    def _repr_(self):
        return "(" + ", ".join(x._repr_() for x in self._data) + ")"

    def __cmp__(self, other):
        return cmp(self._data, other._data)

    def _add_(self, other):
        C = self.__class__
        z = zip(self._data, other._data)
        return C(tuple(zz[0]._add_(zz[1]) for zz in z), parent=self.parent())

    def _sub_(self, other):
        C = self.__class__
        z = zip(self._data, other._data)
        return C(tuple(zz[0]._sub_(zz[1]) for zz in z), parent=self.parent())

    def _mul_(self, other):
        C = self.__class__
        z = zip(self._data, other._data)
        return C(tuple(zz[0]._mul_(zz[1]) for zz in z), parent=self.parent())

    def _div_(self, other):
        C = self.__class__
        z = zip(self._data, other._data)
        return C(tuple(zz[0]._div_(zz[1]) for zz in z), parent=self.parent())

    def __iter__(self):
        r"""
        Returns an iterator of the entries of ``self``.
        """
        for x in self._data:
            yield x


class ProductRing(UniqueRepresentation, Ring):
    r"""
    The product ring of a finite number of rings, with elementwise addition
    and multiplication.
    """
    Element = ProductRingElement

    def __init__(self,rings, base=ZZ, category=None):
        r"""
        INPUT:

        - ``rings`` -- a tuple of rings.
        """
        from sage.categories.rings import Rings

        if not all(R.is_ring() for R in rings):
            raise TypeError("Expected a tuple of rings as input.")
        self._rings = tuple(rings)
        Ring.__init__(self, base=base, category=category or Rings())

    def _repr_(self):
        return "Product ring: (" + ", ".join(R._repr_() for R in self._rings) + ")"

    def _element_constructor_(self, *args, **kwds):
        if len(args) == len(self._rings):
            z = zip(self._rings, args)
        elif len(args) == 1:
            try:
                if len(args[0]) != len(self._rings):
                    raise TypeError  # also if args[0] has no len()
                z = zip(self._rings, args[0])
            except TypeError:
                z = zip(self._rings, [args[0] for x in self._rings])
        else:
            raise TypeError("Wrong number of ring elements")
        return self.element_class(tuple(zz[0](zz[1]) for zz in z), parent=self, **kwds)

    def _coerce_map_from_(self, S):
        if all(R.has_coerce_map_from(S) for R in self._rings):
            return True

    def __pow__(self,n):
        r"""
        Returns the ``n``-th power of self as a vector space.
        """
        from sage.modules.free_module import FreeModule
        return FreeModule(self,n)

    def is_finite(self):
        return all(R.is_finite() for R in self._rings)

    def is_field(self):
        return False