Differences between revisions 1 and 28 (spanning 27 versions)
Revision 1 as of 2009-02-08 09:09:12
Size: 9609
Editor: robertwb
Comment: first draft
Revision 28 as of 2022-03-31 01:40:34
Size: 0
Editor: mkoeppe
Comment: This was just a copy of https://doc.sagemath.org/html/en/reference/coercion/index.html
Deletions are marked like this. Additions are marked like this.
Line 1: Line 1:
=== What is coercion all about? ===

''The primary goal of the coercion is to be able to transparently do arithmetic, comparisons, etc. between elements of distinct sets.''

As a concrete example, when one writes $1 + 1/2$ one wants to perform arithmetic on the operands as rational numbers, despite the left being an integer. This makes sense given the obvious and natural inclusion of the integers into the rational numbers. The goal of the coercion system is to facilitate this (and more complicated arithmetic) without having to explicitly map everything over into the same domain, and at the same time being strict enough to not resolve ambiguity or accept nonsense. Here are some examples

{{{
sage: 1 + 1/2
3/2
sage: R.<x,y> = ZZ[]
sage: R
Multivariate Polynomial Ring in x, y over Integer Ring
sage: parent(x)
Multivariate Polynomial Ring in x, y over Integer Ring
sage: parent(1/3)
Rational Field
sage: x+1/3
x + 1/3
sage: parent(x+1/3)
Multivariate Polynomial Ring in x, y over Rational Field
Assume we want to do $a + b$ with $a \in R$ and $b \in S$

sage: GF(5)(1) + CC(I)
Traceback (most recent call last):
...
TypeError: unsupported operand parent(s) for '+': 'Finite Field of size 5' and 'Complex Field with 53 bits of precision'
}}}

=== Parents and Elements ===

Parents objects in concrete categories, and Elements are their members. Parents are first-class objects. Most things Sage either are parents or have a parent. Whenever one sees the word ''Parent'' one can think ''Set''. Here are some examples:

{{{
sage: parent(1)
Integer Ring
sage: parent(1) is ZZ
True
sage: ZZ
Integer Ring
sage: parent(1.50000000000000000000000000000000000)
Real Field with 123 bits of precision
sage: parent(x)
Symbolic Ring
sage: x^sin(x)
x^sin(x)
sage: R.<t> = Qp(5)[]
sage: f = t^3-5; f
(1 + O(5^20))*t^3 + (4*5 + 4*5^2 + 4*5^3 + 4*5^4 + 4*5^5 + 4*5^6 + 4*5^7 + 4*5^8 + 4*5^9 + 4*5^10 + 4*5^11 + 4*5^12 + 4*5^13 + 4*5^14 + 4*5^15 + 4*5^16 + 4*5^17 + 4*5^18 + 4*5^19 + 4*5^20 + O(5^21))
sage: parent(f)
Univariate Polynomial Ring in t over 5-adic Field with capped relative precision 20
sage: f = EllipticCurve('37a').lseries().taylor_series(10); f
0.997997869801216 + 0.00140712894524925*z - 0.000498127610960097*z^2 + 0.000118835596665956*z^3 - 0.0000215906522442707*z^4 + (3.20363155418419e-6)*z^5 + O(z^6)
sage: parent(f)
Power Series Ring in z over Complex Field with 53 bits of precision
}}}

There is an important distinction between Parents and types

{{{
sage: a = GF(5).random_element()
sage: b = GF(7).random_element()
sage: type(a)
<type 'sage.rings.integer_mod.IntegerMod_int'>
sage: type(b)
<type 'sage.rings.integer_mod.IntegerMod_int'>
sage: type(a) == type(b)
True
sage: parent(a)
Finite Field of size 5
sage: parent(a) == parent(b)
False
}}}

However, non-sage objects don't really have parents, but we still want to be able to reason with them, so their type is used instead.

{{{
sage: a = int(10)
sage: parent(a)
<type 'int'>
}}}

In fact, under the hood, a special parent "The set of all Python objects of type T" is used in these cases.

Note that parents are '''not''' always as tight as possible.

{{{
sage: parent(1/2)
Rational Field
sage: parent(2/1)
Rational Field
}}}

=== Maps between Parents ===

Many parents come with maps to and from other parents.

Sage makes a distinction between being able to '''convert''' between various parents, and '''coerce''' between them. Conversion is explicit and tries to make sense of an object in the target domain if at all possible. It is invoked by calling

{{{
sage: ZZ(5)
5
sage: ZZ(10/5)
2
sage: QQ(10)
10
sage: parent(QQ(10))
Rational Field
sage: a = GF(5)(2); a
2
sage: parent(a)
Finite Field of size 5
sage: parent(ZZ(a))
Integer Ring
}}}

Conversions need not be canonical (they may for example involve a choice of lift) or even make sense mathematically (e.g. constructions of some kind).

{{{
sage: ZZ("123")
123
sage: ZZ['x']([4,3,2,1])
x^3 + 2*x^2 + 3*x + 4
sage: a = Qp(5, 10)(1/3); a
2 + 3*5 + 5^2 + 3*5^3 + 5^4 + 3*5^5 + 5^6 + 3*5^7 + 5^8 + 3*5^9 + O(5^10)
sage: ZZ(a)
6510417
}}}

On the other hand, Sage has the notion of a '''coercion''' which is a canonical morphism (occasionally up to a conventional choice made by developers) between parents. A coercion from one parent to another '''must''' be defined on the whole domain, and always succeeds. As it may be invoked implicitly, is should be obvious and natural (in both the mathematically rigorous and colloquial sense of the word). Up to inescapable rounding issues that arise with inexact representations, these coercion morphisms should all commute.

They is can be discovered via the {{{has_coerce_map_from}}} method, and if needed explicitly invoked with the {{{coerce}}} method.

{{{
sage: QQ.has_coerce_map_from(ZZ)
True
sage: QQ.has_coerce_map_from(RR)
False
sage: ZZ['x'].has_coerce_map_from(QQ)
False
sage: ZZ['x'].has_coerce_map_from(ZZ)
True
sage: ZZ['x'].coerce(5)
5
sage: ZZ['x'].coerce(5).parent()
Univariate Polynomial Ring in x over Integer Ring
sage: ZZ['x'].coerce(5/1)
Traceback (most recent call last):
...
TypeError: no cannonical coercion from Rational Field to Univariate Polynomial Ring in x over Integer Ring
}}}

=== Basic Rules ===

Suppose we want to add two element, a and b, whose parents are R and S respectively. When we type {{{a+b}}} then

 * If R {{{is}}} S, call a._add_(b)

 * If there is a coercion $\phi: S \rightarrow R$, call a._add_($\phi$(b))

 * If there is a coercion $\phi: R \rightarrow S$, call $\phi$(a)._add_(b)

 * Look for $T$ such that there is a coercion $\phi_S: S \rightarrow Z$ and $\phi_R: R \rightarrow Z$, call $\phi$(a)._add_($\phi$(b))

{{{
sage: parent(1 + 1/2)
Rational Field
sage: cm = sage.structure.element.get_coercion_model()
sage: cm.bin_op(77, 9, gcd)
1
sage: cm.explain(ZZ, QQ)
Coercion on left operand via
    Natural morphism:
      From: Integer Ring
      To: Rational Field
Arithmetic performed after coercions.
Result lives in Rational Field
Rational Field
sage: cm.explain(ZZ['x','y'], QQ['x'])
Coercion on left operand via
    Call morphism:
      From: Multivariate Polynomial Ring in x, y over Integer Ring
      To: Multivariate Polynomial Ring in x, y over Rational Field
Coercion on right operand via
    Call morphism:
      From: Univariate Polynomial Ring in x over Rational Field
      To: Multivariate Polynomial Ring in x, y over Rational Field
Arithmetic performed after coercions.
Result lives in Multivariate Polynomial Ring in x, y over Rational Field
Multivariate Polynomial Ring in x, y over Rational Field
}}}

There are also '''actions'''.

{{{
sage: cm.explain(ZZ['x'], ZZ, operator.mul)
Action discovered.
    Right scalar multiplication by Integer Ring on Univariate Polynomial Ring in x over Integer Ring
Result lives in Univariate Polynomial Ring in x over Integer Ring
Univariate Polynomial Ring in x over Integer Ring
sage: cm.explain(ZZ['x'], ZZ, operator.div)
Action discovered.
    Right inverse action by Rational Field on Univariate Polynomial Ring in x over Integer Ring
    with precomposition on right by Natural morphism:
      From: Integer Ring
      To: Rational Field
Result lives in Univariate Polynomial Ring in x over Rational Field
Univariate Polynomial Ring in x over Rational Field
sage: f = QQ.coerce_map_from(ZZ)
sage: f(3).parent()
Rational Field
sage: QQ.coerce_map_from(int)
Native morphism:
  From: Set of Python objects of type 'int'
  To: Rational Field
sage: QQ.has_coerce_map_from(RR)
False
sage: QQ['x'].get_action(QQ)
Right scalar multiplication by Rational Field on Univariate Polynomial Ring in x over Rational Field
sage: (QQ^2).get_action(QQ)
Right scalar multiplication by Rational Field on Vector space of dimension 2 over Rational Field
sage: QQ['x'].get_action(RR)
Right scalar multiplication by Real Field with 53 bits of precision on Univariate Polynomial Ring in x over Rational Field
}}}

=== How to implement ===

Special methods to implement:

 * Arithmetic on elements{{{_add_, _sub_, _mul_, _div_}}}

 * Coercion for Parents {{{_coerce_map_from_}}} (Return True, False, or a morphism object)

 * Actions for Parents {{{_get_action_}}} or {{{_rmul_, _lmul_, _r_action_, _l_action_}}}

 * Element conversion/construction for Parents {{{_element_constructor_}}} ''not'' {{{__call__}}}

See documentation for {{{Parent._populate_coercion_lists_}}}

=== What is provided ===

 * {{{__call__}}}
    Let $x$ be in R, and consider S(x). First, a coercion R -> S is searched for. Then a coercion S -> R is looked for (and the inverse attempted). Last of all _element_constructor_ is called.

 * {{{coerce_map_from, convert_map_from}}} (returns an actual morphism), {{{has_coerce_map_from}}} (bool)

 * ...
    



=== Discovering new parents ===

New parents are discovered using a ''heuristic'' algorithm in sage/category/pushout.py.

TODO

{{{
sage: CC.construction()
(AlgebraicClosureFunctor, Real Field with 53 bits of precision)
sage: RR.construction()
(CompletionFunctor, Rational Field)
sage: Qp(5).construction()
(CompletionFunctor, Rational Field)
sage: c, R = RR.construction()
sage: QQ.completion(5, 100)
5-adic Field with capped relative precision 100
sage: a = CC.construction()[0]
sage: a.commutes(c)
False
sage: RR == c(QQ)
True
sage: QQ.construction()
(FractionField, Integer Ring)
sage: ZZ.construction()
}}}