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]