Here is a minimal example of setting up a simple Ring that handles coercion. (It is easy to imagine much more sophisticated and powerful localizations, but that would obscure the main points being made here.)

class Localization(Ring):
    def __init__(self, primes):
        """
        Localization of $\Z$ away from primes.
        """
        Ring.__init__(self, base=ZZ)
        self._primes = primes
        self._populate_coercion_lists_()

    def _repr_(self):
        """
        How to print self.
        """
        return "%s localized at %s" % (self.base(), self._primes)

    def _element_constructor_(self, x):
        """
        Make sure x is a valid member of self, and return the constructed element. 
        """
        if isinstance(x, LocalizationElement):
            x = x._x
        else:
            x = QQ(x)
        for p, e in x.denominator().factor():
            if p not in self._primes:
                raise ValueError, "Not integral at %s" % p
        return LocalizationElement(self, x)

    def _coerce_map_from_(self, S):
        """
        The only things that coerce into this ring are:
            - the integer ring
            - other localizations away from fewer primes
        """
        if S is ZZ:
            return True
        elif isinstance(S, Localization):
            return all(p in self._primes for p in S._primes)


class LocalizationElement(RingElement):

    def __init__(self, parent, x):
        RingElement.__init__(self, parent)
        self._value = x


    # We're just printing out this way to make it easy to see what's going on in the examples. 

    def _repr_(self):
        return "LocalElt(%s)" % self._value

    
    # Now define addition, subtraction, and multiplication of elements.
    # Note that left and right always have the same parent.

    def _add_(left, right):
        return LocalizationElement(left.parent(), left._value + right._value)
    
    def _sub_(left, right):
        return LocalizationElement(left.parent(), left._value - right._value)
    
    def _mul_(left, right):
        return LocalizationElement(left.parent(), left._value * right._value)


    # The basering was set to ZZ, so c is guaranteed to be in ZZ

    def _rmul_(self, c):
        return LocalizationElement(self.parent(), c * self._value)

    def _lmul_(self, c):
        return LocalizationElement(self.parent(), self._value * c)

That's all there is to it. Now we can test it out:

sage: R = Localization([2]); R
Integer Ring localized at [2]
sage: R(1)
LocalElt(1)
sage: R(1/2)
LocalElt(1/2)
sage: R(1/3)
Traceback (most recent call last):
...
ValueError: Not integral at 3

sage: R.coerce(1)
LocalElt(1)
sage: R.coerce(1/4)
Traceback (click to the left for traceback)
...
TypeError: no cannonical coercion from Rational Field to Integer Ring localized at [2]

sage: R(1/2) + R(3/4)
LocalElt(5/4)
sage: R(1/2) + 5
LocalElt(11/2)
sage: 5 + R(1/2)
LocalElt(11/2)
sage: R(1/2) + 1/7
Traceback (most recent call last):
...
TypeError: unsupported operand parent(s) for '+': 'Integer Ring localized at [2]' and 'Rational Field'
sage: R(3/4) * 7
LocalElt(21/4)

sage: R.get_action(ZZ)
Right scalar multiplication by Integer Ring on Integer Ring localized at [2]
sage: cm = sage.structure.element.get_coercion_model()
sage: cm.explain(R, ZZ, operator.add)
Coercion on right operand via
    Conversion map:
      From: Integer Ring
      To:   Integer Ring localized at [2]
Arithmetic performed after coercions.
Result lives in Integer Ring localized at [2]
Integer Ring localized at [2]

sage: cm.explain(R, ZZ, operator.mul)
Action discovered.
    Right scalar multiplication by Integer Ring on Integer Ring localized at [2]
Result lives in Integer Ring localized at [2]
Integer Ring localized at [2]

sage: R6 = Localization([2,3]); R6
Integer Ring localized at [2, 3]
sage: R6(1/3) - R(1/2)
LocalElt(-1/6)
sage: parent(R6(1/3) - R(1/2))
Integer Ring localized at [2, 3]

sage: R.has_coerce_map_from(ZZ)
True
sage: R.coerce_map_from(ZZ)
Conversion map:
  From: Integer Ring
  To:   Integer Ring localized at [2]

sage: R6.coerce_map_from(R)
Conversion map:
  From: Integer Ring localized at [2]
  To:   Integer Ring localized at [2, 3]

sage: R6.coerce(R(1/2))
LocalElt(1/2)

sage: cm.explain(R, R6, operator.mul)
Coercion on left operand via
    Conversion map:
      From: Integer Ring localized at [2]
      To:   Integer Ring localized at [2, 3]
Arithmetic performed after coercions.
Result lives in Integer Ring localized at [2, 3]
Integer Ring localized at [2, 3]

coercion/example (last edited 2009-02-13 07:15:16 by robertwb)