The coercion model - Coercion

Skip to content
Back to top
View this page
Edit this page

# The coercion model

The coercion model manages how elements of one parent get related to elements of another. For example, the integer 2 can canonically be viewed as an element of the rational numbers. (The parent of a non-element is its Python type.)

Sage

    sage: ZZ(2).parent()
    Integer Ring
    sage: QQ(2).parent()
    Rational Field

Python

    >>> from sage.all import *
    >>> ZZ(Integer(2)).parent()
    Integer Ring
    >>> QQ(Integer(2)).parent()
    Rational Field

The most prominent role of the coercion model is to make sense of binary operations between elements that have distinct parents. It does this by finding a parent where both elements make sense, and doing the operation there. For example:

Sage

    sage: a = 1/2; a.parent()
    Rational Field
    sage: b = ZZ['x'].gen(); b.parent()
    Univariate Polynomial Ring in x over Integer Ring
    sage: a + b
    x + 1/2
    sage: (a + b).parent()
    Univariate Polynomial Ring in x over Rational Field

[Python]

    >>> from sage.all import *
    >>> a = Integer(1)/Integer(2); a.parent()
    Rational Field
    >>> b = ZZ['x'].gen(); b.parent()
    Univariate Polynomial Ring in x over Integer Ring
    >>> a + b
    x + 1/2
    >>> (a + b).parent()
    Univariate Polynomial Ring in x over Rational Field

If there is a coercion (see below) from one of the parents to the other, the operation is always performed in the codomain of that coercion. Otherwise a reasonable attempt to create a new parent with coercion maps from both original parents is made. The results of these discoveries are cached. On failure, a `TypeError` is always raised.

Some arithmetic operations (such as multiplication) can indicate an action rather than arithmetic in a common parent. For example:

Sage

    sage: E = EllipticCurve('37a')                                                      # needs sage.schemes
    sage: P = E(0,0)                                                                    # needs sage.schemes
    sage: 5*P                                                                           # needs sage.schemes
    (1/4 : -5/8 : 1)

Python

    >>> from sage.all import *
    >>> E = EllipticCurve('37a')                                                      # needs sage.schemes
    >>> P = E(Integer(0),Integer(0))                                                                    # needs sage.schemes
    >>> Integer(5)*P                                                                           # needs sage.schemes
    (1/4 : -5/8 : 1)

where there is action of \(\ZZ\) on the points of \(E\) given by the additive group law. Parents can specify how they act on or are acted upon by other parents.

There are two kinds of ways to get from one parent to another, coercions and conversions.

Coercions are canonical (possibly modulo a finite number of deterministic choices) morphisms, and the set of all coercions between all parents forms a commuting diagram (modulo possibly rounding issues). \(\ZZ \rightarrow \QQ\) is an example of a coercion. These are invoked implicitly by the coercion model.

Conversions try to construct an element out of their input if at all possible. Examples include sections of coercions, creating an element from a string or list, etc. and may fail on some inputs of a given type while succeeding on others (i.e. they may not be defined on the whole domain). Conversions are always explicitly invoked, and never used by the coercion model to resolve binary operations.

For more information on how to specify coercions, conversions, and actions, see the documentation for `Parent`.

class sage.structure.coerce.CoercionModel

  Bases: `object`

  See also `sage.categories.pushout`

  EXAMPLES:

  Sage

      sage: f = ZZ['t', 'x'].0 + QQ['x'].0 + CyclotomicField(13).gen(); f             # needs sage.rings.number_field
      t + x + zeta13
      sage: f.parent()                                                                # needs sage.rings.number_field
      Multivariate Polynomial Ring in t, x
       over Cyclotomic Field of order 13 and degree 12
      sage: ZZ['x','y'].0 + ~Frac(QQ['y']).0
      (x*y + 1)/y
      sage: MatrixSpace(ZZ['x'], 2, 2)(2) + ~Frac(QQ['x']).0                          # needs sage.modules
      [(2*x + 1)/x           0]
      [          0 (2*x + 1)/x]
      sage: f = ZZ['x,y,z'].0 + QQ['w,x,z,a'].0; f
      w + x
      sage: f.parent()
      Multivariate Polynomial Ring in w, x, y, z, a over Rational Field
      sage: ZZ['x,y,z'].0 + ZZ['w,x,z,a'].1
      2*x

  Python

      >>> from sage.all import *
      >>> f = ZZ['t', 'x'].gen(0) + QQ['x'].gen(0) + CyclotomicField(Integer(13)).gen(); f             # needs sage.rings.number_field
      t + x + zeta13
      >>> f.parent()                                                                # needs sage.rings.number_field
      Multivariate Polynomial Ring in t, x
       over Cyclotomic Field of order 13 and degree 12
      >>> ZZ['x','y'].gen(0) + ~Frac(QQ['y']).gen(0)
      (x*y + 1)/y
      >>> MatrixSpace(ZZ['x'], Integer(2), Integer(2))(Integer(2)) + ~Frac(QQ['x']).gen(0)                          # needs sage.modules
      [(2*x + 1)/x           0]
      [          0 (2*x + 1)/x]
      >>> f = ZZ['x,y,z'].gen(0) + QQ['w,x,z,a'].gen(0); f
      w + x
      >>> f.parent()
      Multivariate Polynomial Ring in w, x, y, z, a over Rational Field
      >>> ZZ['x,y,z'].gen(0) + ZZ['w,x,z,a'].gen(1)
      2*x

  AUTHOR:

  - Robert Bradshaw

  analyse(xp, yp, op='mul')

    Emulate the process of doing arithmetic between xp and yp, returning a list of steps and the parent that the result will live in.

    The `explain()` method is easier to use, but if one wants access to the actual morphism and action objects (rather than their string representations), then this is the function to use.

    For programmatic usages, use `canonical_coercion()` and `common_parent()` instead.

    EXAMPLES:

    Sage

        sage: cm = sage.structure.element.get_coercion_model()
        sage: GF7 = GF(7)
        sage: steps, res = cm.analyse(GF7, ZZ)
        sage: steps
        ['Coercion on right operand via',
         Natural morphism:
          From: Integer Ring
          To:   Finite Field of size 7,
         'Arithmetic performed after coercions.']
        sage: res
        Finite Field of size 7
        sage: f = steps[1]; type(f)
        <class 'sage.rings.finite_rings.integer_mod.Integer_to_IntegerMod'>
        sage: f(100)
        2

    Python

        >>> from sage.all import *
        >>> cm = sage.structure.element.get_coercion_model()
        >>> GF7 = GF(Integer(7))
        >>> steps, res = cm.analyse(GF7, ZZ)
        >>> steps
        ['Coercion on right operand via',
         Natural morphism:
          From: Integer Ring
          To:   Finite Field of size 7,
         'Arithmetic performed after coercions.']
        >>> res
        Finite Field of size 7
        >>> f = steps[Integer(1)]; type(f)
        <class 'sage.rings.finite_rings.integer_mod.Integer_to_IntegerMod'>
        >>> f(Integer(100))
        2

  bin_op(x, y, op)

    Execute the operation `op` on \(x\) and \(y\).

    It first looks for an action corresponding to `op`, and failing that, it tries to coerce \(x\) and \(y\) into a common parent and calls `op` on them.

    If it cannot make sense of the operation, a `TypeError` is raised.

    INPUT:

    - `x` – the left operand

    - `y` – the right operand

    - `op` – a python function taking 2 arguments

      Note

      `op` is often an arithmetic operation, but need not be so.

    EXAMPLES:

    Sage

        sage: cm = sage.structure.element.get_coercion_model()
        sage: cm.bin_op(1/2, 5, operator.mul)
        5/2

    Python

        >>> from sage.all import *
        >>> cm = sage.structure.element.get_coercion_model()
        >>> cm.bin_op(Integer(1)/Integer(2), Integer(5), operator.mul)
        5/2

    The operator can be any callable:

    Sage

        sage: R.<x> = ZZ['x']
        sage: cm.bin_op(x^2 - 1, x + 1, gcd)
        x + 1

    [Python]

        >>> from sage.all import *
        >>> R = ZZ['x']; (x,) = R._first_ngens(1)
        >>> cm.bin_op(x**Integer(2) - Integer(1), x + Integer(1), gcd)
        x + 1

    Actions are detected and performed:

    Sage

        sage: M = matrix(ZZ, 2, 2, range(4))                                        # needs sage.modules
        sage: V = vector(ZZ, [5,7])                                                 # needs sage.modules
        sage: cm.bin_op(M, V, operator.mul)                                         # needs sage.modules
        (7, 31)

    Python

        >>> from sage.all import *
        >>> M = matrix(ZZ, Integer(2), Integer(2), range(Integer(4)))                                        # needs sage.modules
        >>> V = vector(ZZ, [Integer(5),Integer(7)])                                                 # needs sage.modules
        >>> cm.bin_op(M, V, operator.mul)                                         # needs sage.modules
        (7, 31)

  canonical_coercion(x, y)

    Given two elements \(x\) and \(y\), with parents \(S\) and \(R\) respectively, find a common parent \(Z\) such that there are coercions \(f: S \to Z\) and \(g: R \to Z\) and return \(f(x), g(y)\), which will have the same parent.

    Raises a type error if no such \(Z\) can be found.

    EXAMPLES:

    Sage

        sage: cm = sage.structure.element.get_coercion_model()
        sage: cm.canonical_coercion(mod(2, 10), 17)
        (2, 7)

        sage: # needs sage.modules
        sage: x, y = cm.canonical_coercion(1/2, matrix(ZZ, 2, 2, range(4)))
        sage: x
        [1/2   0]
        [  0 1/2]
        sage: y
        [0 1]
        [2 3]
        sage: parent(x) is parent(y)
        True

    Python

        >>> from sage.all import *
        >>> cm = sage.structure.element.get_coercion_model()
        >>> cm.canonical_coercion(mod(Integer(2), Integer(10)), Integer(17))
        (2, 7)

        >>> # needs sage.modules
        >>> x, y = cm.canonical_coercion(Integer(1)/Integer(2), matrix(ZZ, Integer(2), Integer(2), range(Integer(4))))
        >>> x
        [1/2   0]
        [  0 1/2]
        >>> y
        [0 1]
        [2 3]
        >>> parent(x) is parent(y)
        True

    There is some support for non-Sage datatypes as well:

    Sage

        sage: x, y = cm.canonical_coercion(int(5), 10)
        sage: type(x), type(y)
        (<class 'sage.rings.integer.Integer'>, <class 'sage.rings.integer.Integer'>)

        sage: x, y = cm.canonical_coercion(int(5), complex(3))
        sage: type(x), type(y)
        (<class 'complex'>, <class 'complex'>)

        sage: class MyClass:
        ....:     def _sage_(self):
        ....:         return 13
        sage: a, b = cm.canonical_coercion(MyClass(), 1/3)
        sage: a, b
        (13, 1/3)
        sage: type(a)
        <class 'sage.rings.rational.Rational'>

    [Python]

        >>> from sage.all import *
        >>> x, y = cm.canonical_coercion(int(Integer(5)), Integer(10))
        >>> type(x), type(y)
        (<class 'sage.rings.integer.Integer'>, <class 'sage.rings.integer.Integer'>)

        >>> x, y = cm.canonical_coercion(int(Integer(5)), complex(Integer(3)))
        >>> type(x), type(y)
        (<class 'complex'>, <class 'complex'>)

        >>> class MyClass:
        ...     def _sage_(self):
        ...         return Integer(13)
        >>> a, b = cm.canonical_coercion(MyClass(), Integer(1)/Integer(3))
        >>> a, b
        (13, 1/3)
        >>> type(a)
        <class 'sage.rings.rational.Rational'>

    We also make an exception for 0, even if \(\ZZ\) does not map in:

    Sage

        sage: canonical_coercion(vector([1, 2, 3]), 0)                              # needs sage.modules
        ((1, 2, 3), (0, 0, 0))
        sage: canonical_coercion(GF(5)(0), float(0))
        (0, 0)

    Python

        >>> from sage.all import *
        >>> canonical_coercion(vector([Integer(1), Integer(2), Integer(3)]), Integer(0))                              # needs sage.modules
        ((1, 2, 3), (0, 0, 0))
        >>> canonical_coercion(GF(Integer(5))(Integer(0)), float(Integer(0)))
        (0, 0)

  coercion_maps(R, S)

    Give two parents \(R\) and \(S\), return a pair of coercion maps \(f: R \rightarrow Z\) and \(g: S \rightarrow Z\) , if such a \(Z\) can be found.

    In the (common) case that \(R=Z\) or \(S=Z\) then `None` is returned for \(f\) or \(g\) respectively rather than constructing (and subsequently calling) the identity morphism.

    If no suitable \(f, g\) can be found, a single `None` is returned. This result is cached.

    Note

    By Issue #14711, coerce maps should be copied when using them outside of the coercion system, because they may become defunct by garbage collection.

    EXAMPLES:

    Sage

        sage: cm = sage.structure.element.get_coercion_model()
        sage: f, g = cm.coercion_maps(ZZ, QQ)
        sage: print(copy(f))
        Natural morphism:
          From: Integer Ring
          To:   Rational Field
        sage: print(g)
        None

        sage: ZZx = ZZ['x']
        sage: f, g = cm.coercion_maps(ZZx, QQ)
        sage: print(f)
        (map internal to coercion system -- copy before use)
        Ring morphism:
          From: Univariate Polynomial Ring in x over Integer Ring
          To:   Univariate Polynomial Ring in x over Rational Field
        sage: print(g)
        (map internal to coercion system -- copy before use)
        Polynomial base injection morphism:
          From: Rational Field
          To:   Univariate Polynomial Ring in x over Rational Field

        sage: K = GF(7)
        sage: cm.coercion_maps(QQ, K) is None
        True

    Python

        >>> from sage.all import *
        >>> cm = sage.structure.element.get_coercion_model()
        >>> f, g = cm.coercion_maps(ZZ, QQ)
        >>> print(copy(f))
        Natural morphism:
          From: Integer Ring
          To:   Rational Field
        >>> print(g)
        None

        >>> ZZx = ZZ['x']
        >>> f, g = cm.coercion_maps(ZZx, QQ)
        >>> print(f)
        (map internal to coercion system -- copy before use)
        Ring morphism:
          From: Univariate Polynomial Ring in x over Integer Ring
          To:   Univariate Polynomial Ring in x over Rational Field
        >>> print(g)
        (map internal to coercion system -- copy before use)
        Polynomial base injection morphism:
          From: Rational Field
          To:   Univariate Polynomial Ring in x over Rational Field

        >>> K = GF(Integer(7))
        >>> cm.coercion_maps(QQ, K) is None
        True

    Note that to break symmetry, if there is a coercion map in both directions, the parent on the left is used:

    Sage

        sage: # needs sage.modules
        sage: V = QQ^3
        sage: W = V.__class__(QQ, 3)
        sage: V == W
        True
        sage: V is W
        False
        sage: cm = sage.structure.element.get_coercion_model()
        sage: cm.coercion_maps(V, W)
        (None,
         (map internal to coercion system -- copy before use)
         Coercion map:
           From: Vector space of dimension 3 over Rational Field
           To:   Vector space of dimension 3 over Rational Field)
        sage: cm.coercion_maps(W, V)
        (None,
         (map internal to coercion system -- copy before use)
         Coercion map:
           From: Vector space of dimension 3 over Rational Field
           To:   Vector space of dimension 3 over Rational Field)
        sage: v = V([1,2,3])
        sage: w = W([1,2,3])
        sage: parent(v + w) is V
        True
        sage: parent(w + v) is W
        True

    [Python]

        >>> from sage.all import *
        >>> # needs sage.modules
        >>> V = QQ**Integer(3)
        >>> W = V.__class__(QQ, Integer(3))
        >>> V == W
        True
        >>> V is W
        False
        >>> cm = sage.structure.element.get_coercion_model()
        >>> cm.coercion_maps(V, W)
        (None,
         (map internal to coercion system -- copy before use)
         Coercion map:
           From: Vector space of dimension 3 over Rational Field
           To:   Vector space of dimension 3 over Rational Field)
        >>> cm.coercion_maps(W, V)
        (None,
         (map internal to coercion system -- copy before use)
         Coercion map:
           From: Vector space of dimension 3 over Rational Field
           To:   Vector space of dimension 3 over Rational Field)
        >>> v = V([Integer(1),Integer(2),Integer(3)])
        >>> w = W([Integer(1),Integer(2),Integer(3)])
        >>> parent(v + w) is V
        True
        >>> parent(w + v) is W
        True

  common_parent(*args)

    Compute a common parent for all the inputs.

    It’s essentially an \(n\)-ary canonical coercion except it can operate on parents rather than just elements.

    INPUT:

    - `args` – set of elements and/or parents

    OUTPUT:

    A `Parent` into which each input should coerce, or raises a `TypeError` if no such `Parent` can be found.

    EXAMPLES:

    Sage

        sage: cm = sage.structure.element.get_coercion_model()
        sage: cm.common_parent(ZZ, QQ)
        Rational Field
        sage: cm.common_parent(ZZ, QQ, RR)                                          # needs sage.rings.real_mpfr
        Real Field with 53 bits of precision
        sage: ZZT = ZZ[['T']]
        sage: QQT = QQ['T']
        sage: cm.common_parent(ZZT, QQT, RDF)
        Power Series Ring in T over Real Double Field
        sage: cm.common_parent(4r, 5r)
        <class 'int'>
        sage: cm.common_parent(int, float, ZZ)
        <class 'float'>
        sage: real_fields = [RealField(prec) for prec in [10,20..100]]              # needs sage.rings.real_mpfr
        sage: cm.common_parent(*real_fields)                                        # needs sage.rings.real_mpfr
        Real Field with 10 bits of precision

    Python

        >>> from sage.all import *
        >>> cm = sage.structure.element.get_coercion_model()
        >>> cm.common_parent(ZZ, QQ)
        Rational Field
        >>> cm.common_parent(ZZ, QQ, RR)                                          # needs sage.rings.real_mpfr
        Real Field with 53 bits of precision
        >>> ZZT = ZZ[['T']]
        >>> QQT = QQ['T']
        >>> cm.common_parent(ZZT, QQT, RDF)
        Power Series Ring in T over Real Double Field
        >>> cm.common_parent(4, 5)
        <class 'int'>
        >>> cm.common_parent(int, float, ZZ)
        <class 'float'>
        >>> real_fields = [RealField(prec) for prec in (ellipsis_range(Integer(10),Integer(20),Ellipsis,Integer(100)))]              # needs sage.rings.real_mpfr
        >>> cm.common_parent(*real_fields)                                        # needs sage.rings.real_mpfr
        Real Field with 10 bits of precision

    There are some cases where the ordering does matter, but if a parent can be found it is always the same:

    Sage

        sage: QQxy = QQ['x,y']
        sage: QQyz = QQ['y,z']
        sage: cm.common_parent(QQxy, QQyz) == cm.common_parent(QQyz, QQxy)
        True
        sage: QQzt = QQ['z,t']
        sage: cm.common_parent(QQxy, QQyz, QQzt)
        Multivariate Polynomial Ring in x, y, z, t over Rational Field
        sage: cm.common_parent(QQxy, QQzt, QQyz)
        Traceback (most recent call last):
        ...
        TypeError: no common canonical parent for objects with parents:
        'Multivariate Polynomial Ring in x, y over Rational Field' and
        'Multivariate Polynomial Ring in z, t over Rational Field'

    [Python]

        >>> from sage.all import *
        >>> QQxy = QQ['x,y']
        >>> QQyz = QQ['y,z']
        >>> cm.common_parent(QQxy, QQyz) == cm.common_parent(QQyz, QQxy)
        True
        >>> QQzt = QQ['z,t']
        >>> cm.common_parent(QQxy, QQyz, QQzt)
        Multivariate Polynomial Ring in x, y, z, t over Rational Field
        >>> cm.common_parent(QQxy, QQzt, QQyz)
        Traceback (most recent call last):
        ...
        TypeError: no common canonical parent for objects with parents:
        'Multivariate Polynomial Ring in x, y over Rational Field' and
        'Multivariate Polynomial Ring in z, t over Rational Field'

  discover_action(R, S, op, r=None, s=None)

    INPUT:

    - `R` – the left `Parent` (or type)

    - `S` – the right `Parent` (or type)

    - `op` – the operand, typically an element of the `operator` module

    - `r` – (optional) element of \(R\)

    - `s` – (optional) element of \(S\)

    OUTPUT: an action \(A\) such that \(s\) `op` \(r\) is given by \(A(s,r)\)

    The steps taken are illustrated below.

    EXAMPLES:

    Sage

        sage: P.<x> = ZZ['x']
        sage: P.get_action(ZZ)
        Right scalar multiplication by Integer Ring on
         Univariate Polynomial Ring in x over Integer Ring
        sage: ZZ.get_action(P) is None
        True
        sage: cm = sage.structure.element.get_coercion_model()

    Python

        >>> from sage.all import *
        >>> P = ZZ['x']; (x,) = P._first_ngens(1)
        >>> P.get_action(ZZ)
        Right scalar multiplication by Integer Ring on
         Univariate Polynomial Ring in x over Integer Ring
        >>> ZZ.get_action(P) is None
        True
        >>> cm = sage.structure.element.get_coercion_model()

    If \(R\) or \(S\) is a `Parent`, ask it for an action by/on \(R\):

    Sage

        sage: cm.discover_action(ZZ, P, operator.mul)
        Left scalar multiplication by Integer Ring on
         Univariate Polynomial Ring in x over Integer Ring

    [Python]

        >>> from sage.all import *
        >>> cm.discover_action(ZZ, P, operator.mul)
        Left scalar multiplication by Integer Ring on
         Univariate Polynomial Ring in x over Integer Ring

    If \(R\) or \(S\) a type, recursively call `get_action()` with the Sage versions of \(R\) and/or \(S\):

    Sage

        sage: cm.discover_action(P, int, operator.mul)
        Right scalar multiplication by Integer Ring on
         Univariate Polynomial Ring in x over Integer Ring
         with precomposition on right by Native morphism:
          From: Set of Python objects of class 'int'
          To:   Integer Ring

    Python

        >>> from sage.all import *
        >>> cm.discover_action(P, int, operator.mul)
        Right scalar multiplication by Integer Ring on
         Univariate Polynomial Ring in x over Integer Ring
         with precomposition on right by Native morphism:
          From: Set of Python objects of class 'int'
          To:   Integer Ring

    If `op` is division, look for action on `right` by inverse:

    Sage

        sage: cm.discover_action(P, ZZ, operator.truediv)
        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

    [Python]

        >>> from sage.all import *
        >>> cm.discover_action(P, ZZ, operator.truediv)
        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

    Check that Issue #17740 is fixed:

    Sage

        sage: R = GF(5)['x']
        sage: cm.discover_action(R, ZZ, operator.truediv)
        Right inverse action by Finite Field of size 5
         on Univariate Polynomial Ring in x over Finite Field of size 5
        with precomposition on right by Natural morphism:
          From: Integer Ring
          To:   Finite Field of size 5
        sage: cm.bin_op(R.gen(), 7, operator.truediv).parent()
        Univariate Polynomial Ring in x over Finite Field of size 5

    Python

        >>> from sage.all import *
        >>> R = GF(Integer(5))['x']
        >>> cm.discover_action(R, ZZ, operator.truediv)
        Right inverse action by Finite Field of size 5
         on Univariate Polynomial Ring in x over Finite Field of size 5
        with precomposition on right by Natural morphism:
          From: Integer Ring
          To:   Finite Field of size 5
        >>> cm.bin_op(R.gen(), Integer(7), operator.truediv).parent()
        Univariate Polynomial Ring in x over Finite Field of size 5

    Check that Issue #18221 is fixed:

    Sage

        sage: # needs sage.combinat sage.modules
        sage: F.<x> = FreeAlgebra(QQ)
        sage: x / 2
        1/2*x
        sage: cm.discover_action(F, ZZ, operator.truediv)
        Right inverse action by Rational Field on
         Free Algebra on 1 generator (x,) over Rational Field
         with precomposition on right by Natural morphism:
          From: Integer Ring
          To:   Rational Field

    [Python]

        >>> from sage.all import *
        >>> # needs sage.combinat sage.modules
        >>> F = FreeAlgebra(QQ, names=('x',)); (x,) = F._first_ngens(1)
        >>> x / Integer(2)
        1/2*x
        >>> cm.discover_action(F, ZZ, operator.truediv)
        Right inverse action by Rational Field on
         Free Algebra on 1 generator (x,) over Rational Field
         with precomposition on right by Natural morphism:
          From: Integer Ring
          To:   Rational Field

  discover_coercion(R, S)

    This actually implements the finding of coercion maps as described in the `coercion_maps()` method.

    EXAMPLES:

    Sage

        sage: cm = sage.structure.element.get_coercion_model()

    Python

        >>> from sage.all import *
        >>> cm = sage.structure.element.get_coercion_model()

    If R is S, then two identity morphisms suffice:

    Sage

        sage: cm.discover_coercion(SR, SR)                                          # needs sage.symbolic
        (None, None)

    [Python]

        >>> from sage.all import *
        >>> cm.discover_coercion(SR, SR)                                          # needs sage.symbolic
        (None, None)

    If there is a coercion map either direction, use that:

    Sage

        sage: cm.discover_coercion(ZZ, QQ)
        ((map internal to coercion system -- copy before use)
        Natural morphism:
          From: Integer Ring
          To:   Rational Field, None)
        sage: cm.discover_coercion(RR, QQ)                                          # needs sage.rings.real_mpfr
        (None, (map internal to coercion system -- copy before use)
         Generic map:
          From: Rational Field
          To:   Real Field with 53 bits of precision)

    Python

        >>> from sage.all import *
        >>> cm.discover_coercion(ZZ, QQ)
        ((map internal to coercion system -- copy before use)
        Natural morphism:
          From: Integer Ring
          To:   Rational Field, None)
        >>> cm.discover_coercion(RR, QQ)                                          # needs sage.rings.real_mpfr
        (None, (map internal to coercion system -- copy before use)
         Generic map:
          From: Rational Field
          To:   Real Field with 53 bits of precision)

    Otherwise, try and compute an appropriate cover:

    Sage

        sage: ZZxy = ZZ['x,y']
        sage: cm.discover_coercion(ZZxy, RDF)
        ((map internal to coercion system -- copy before use)
         Coercion map:
           From: Multivariate Polynomial Ring in x, y over Integer Ring
           To:   Multivariate Polynomial Ring in x, y over Real Double Field,
         (map internal to coercion system -- copy before use)
         Polynomial base injection morphism:
           From: Real Double Field
           To:   Multivariate Polynomial Ring in x, y over Real Double Field)

    [Python]

        >>> from sage.all import *
        >>> ZZxy = ZZ['x,y']
        >>> cm.discover_coercion(ZZxy, RDF)
        ((map internal to coercion system -- copy before use)
         Coercion map:
           From: Multivariate Polynomial Ring in x, y over Integer Ring
           To:   Multivariate Polynomial Ring in x, y over Real Double Field,
         (map internal to coercion system -- copy before use)
         Polynomial base injection morphism:
           From: Real Double Field
           To:   Multivariate Polynomial Ring in x, y over Real Double Field)

    Sometimes there is a reasonable “cover,” but no canonical coercion:

    Sage

        sage: sage.categories.pushout.pushout(QQ, QQ^3)                             # needs sage.modules
        Vector space of dimension 3 over Rational Field
        sage: print(cm.discover_coercion(QQ, QQ^3))                                 # needs sage.modules
        None

    Python

        >>> from sage.all import *
        >>> sage.categories.pushout.pushout(QQ, QQ**Integer(3))                             # needs sage.modules
        Vector space of dimension 3 over Rational Field
        >>> print(cm.discover_coercion(QQ, QQ**Integer(3)))                                 # needs sage.modules
        None

  division_parent(P)

    Deduces where the result of division in `P` lies by calculating the inverse of `P.one()` or `P.an_element()`.

    The result is cached.

    EXAMPLES:

    Sage

        sage: cm = sage.structure.element.get_coercion_model()
        sage: cm.division_parent(ZZ)
        Rational Field
        sage: cm.division_parent(QQ)
        Rational Field
        sage: ZZx = ZZ['x']
        sage: cm.division_parent(ZZx)
        Fraction Field of Univariate Polynomial Ring in x over Integer Ring
        sage: K = GF(41)
        sage: cm.division_parent(K)
        Finite Field of size 41
        sage: Zmod100 = Integers(100)
        sage: cm.division_parent(Zmod100)
        Ring of integers modulo 100
        sage: S5 = SymmetricGroup(5)                                                # needs sage.groups
        sage: cm.division_parent(S5)                                                # needs sage.groups
        Symmetric group of order 5! as a permutation group

    Python

        >>> from sage.all import *
        >>> cm = sage.structure.element.get_coercion_model()
        >>> cm.division_parent(ZZ)
        Rational Field
        >>> cm.division_parent(QQ)
        Rational Field
        >>> ZZx = ZZ['x']
        >>> cm.division_parent(ZZx)
        Fraction Field of Univariate Polynomial Ring in x over Integer Ring
        >>> K = GF(Integer(41))
        >>> cm.division_parent(K)
        Finite Field of size 41
        >>> Zmod100 = Integers(Integer(100))
        >>> cm.division_parent(Zmod100)
        Ring of integers modulo 100
        >>> S5 = SymmetricGroup(Integer(5))                                                # needs sage.groups
        >>> cm.division_parent(S5)                                                # needs sage.groups
        Symmetric group of order 5! as a permutation group

  exception_stack()

    Return the list of exceptions that were caught in the course of executing the last binary operation. Useful for diagnosis when user-defined maps or actions raise exceptions that are caught in the course of coercion detection.

    If all went well, this should be the empty list. If things aren’t happening as you expect, this is a good place to check. See also `coercion_traceback()`.

    EXAMPLES:

    Sage

        sage: cm = sage.structure.element.get_coercion_model()
        sage: cm.record_exceptions()
        sage: 1/2 + 2
        5/2
        sage: cm.exception_stack()
        []
        sage: 1/2 + GF(3)(2)
        Traceback (most recent call last):
        ...
        TypeError: unsupported operand parent(s) for +:
        'Rational Field' and 'Finite Field of size 3'

    Python

        >>> from sage.all import *
        >>> cm = sage.structure.element.get_coercion_model()
        >>> cm.record_exceptions()
        >>> Integer(1)/Integer(2) + Integer(2)
        5/2
        >>> cm.exception_stack()
        []
        >>> Integer(1)/Integer(2) + GF(Integer(3))(Integer(2))
        Traceback (most recent call last):
        ...
        TypeError: unsupported operand parent(s) for +:
        'Rational Field' and 'Finite Field of size 3'

    Now see what the actual problem was:

    Sage

        sage: import traceback
        sage: cm.exception_stack()
        ['Traceback (most recent call last):...', 'Traceback (most recent call last):...']
        sage: print(cm.exception_stack()[-1])
        Traceback (most recent call last):
        ...
        TypeError: no common canonical parent for objects with parents:
        'Rational Field' and 'Finite Field of size 3'

    [Python]

        >>> from sage.all import *
        >>> import traceback
        >>> cm.exception_stack()
        ['Traceback (most recent call last):...', 'Traceback (most recent call last):...']
        >>> print(cm.exception_stack()[-Integer(1)])
        Traceback (most recent call last):
        ...
        TypeError: no common canonical parent for objects with parents:
        'Rational Field' and 'Finite Field of size 3'

    This is typically accessed via the `coercion_traceback()` function.

    Sage

        sage: coercion_traceback()
        Traceback (most recent call last):
        ...
        TypeError: no common canonical parent for objects with parents:
        'Rational Field' and 'Finite Field of size 3'

    Python

        >>> from sage.all import *
        >>> coercion_traceback()
        Traceback (most recent call last):
        ...
        TypeError: no common canonical parent for objects with parents:
        'Rational Field' and 'Finite Field of size 3'

  explain(xp, yp, op='mul', verbosity=2)

    This function can be used to understand what coercions will happen for an arithmetic operation between xp and yp (which may be either elements or parents). If the parent of the result can be determined then it will be returned.

    For programmatic usages, use `canonical_coercion()` and `common_parent()` instead.

    EXAMPLES:

    Sage

        sage: cm = sage.structure.element.get_coercion_model()

        sage: cm.explain(ZZ, ZZ)
        Identical parents, arithmetic performed immediately.
        Result lives in Integer Ring
        Integer Ring

        sage: cm.explain(QQ, int)
        Coercion on right operand via
            Native morphism:
              From: Set of Python objects of class 'int'
              To:   Rational Field
        Arithmetic performed after coercions.
        Result lives in Rational Field
        Rational Field

        sage: R = ZZ['x']
        sage: cm.explain(R, QQ)
        Action discovered.
            Right scalar multiplication by Rational Field
             on Univariate Polynomial Ring in x over Integer Ring
        Result lives in Univariate Polynomial Ring in x over Rational Field
        Univariate Polynomial Ring in x over Rational Field

        sage: cm.explain(ZZ['x'], QQ, operator.add)
        Coercion on left operand via
            Ring morphism:
              From: Univariate Polynomial Ring in x over Integer Ring
              To:   Univariate Polynomial Ring in x over Rational Field
              Defn: Induced from base ring by
                    Natural morphism:
                      From: Integer Ring
                      To:   Rational Field
        Coercion on right operand via
            Polynomial base injection morphism:
              From: Rational Field
              To:   Univariate Polynomial Ring in x over Rational Field
        Arithmetic performed after coercions.
        Result lives in Univariate Polynomial Ring in x over Rational Field
        Univariate Polynomial Ring in x over Rational Field

    Python

        >>> from sage.all import *
        >>> cm = sage.structure.element.get_coercion_model()

        >>> cm.explain(ZZ, ZZ)
        Identical parents, arithmetic performed immediately.
        Result lives in Integer Ring
        Integer Ring

        >>> cm.explain(QQ, int)
        Coercion on right operand via
            Native morphism:
              From: Set of Python objects of class 'int'
              To:   Rational Field
        Arithmetic performed after coercions.
        Result lives in Rational Field
        Rational Field

        >>> R = ZZ['x']
        >>> cm.explain(R, QQ)
        Action discovered.
            Right scalar multiplication by Rational Field
             on Univariate Polynomial Ring in x over Integer Ring
        Result lives in Univariate Polynomial Ring in x over Rational Field
        Univariate Polynomial Ring in x over Rational Field

        >>> cm.explain(ZZ['x'], QQ, operator.add)
        Coercion on left operand via
            Ring morphism:
              From: Univariate Polynomial Ring in x over Integer Ring
              To:   Univariate Polynomial Ring in x over Rational Field
              Defn: Induced from base ring by
                    Natural morphism:
                      From: Integer Ring
                      To:   Rational Field
        Coercion on right operand via
            Polynomial base injection morphism:
              From: Rational Field
              To:   Univariate Polynomial Ring in x over Rational Field
        Arithmetic performed after coercions.
        Result lives in Univariate Polynomial Ring in x over Rational Field
        Univariate Polynomial Ring in x over Rational Field

    Sometimes with non-sage types there is not enough information to deduce what will actually happen:

    Sage

        sage: R100 = RealField(100)                                                 # needs sage.rings.real_mpfr
        sage: cm.explain(R100, float, operator.add)                                 # needs sage.rings.real_mpfr
        Right operand is numeric, will attempt coercion in both directions.
        Unknown result parent.
        sage: parent(R100(1) + float(1))                                            # needs sage.rings.real_mpfr
        <class 'float'>
        sage: cm.explain(QQ, float, operator.add)
        Right operand is numeric, will attempt coercion in both directions.
        Unknown result parent.
        sage: parent(QQ(1) + float(1))
        <class 'float'>

    [Python]

        >>> from sage.all import *
        >>> R100 = RealField(Integer(100))                                                 # needs sage.rings.real_mpfr
        >>> cm.explain(R100, float, operator.add)                                 # needs sage.rings.real_mpfr
        Right operand is numeric, will attempt coercion in both directions.
        Unknown result parent.
        >>> parent(R100(Integer(1)) + float(Integer(1)))                                            # needs sage.rings.real_mpfr
        <class 'float'>
        >>> cm.explain(QQ, float, operator.add)
        Right operand is numeric, will attempt coercion in both directions.
        Unknown result parent.
        >>> parent(QQ(Integer(1)) + float(Integer(1)))
        <class 'float'>

    Special care is taken to deal with division:

    Sage

        sage: cm.explain(ZZ, ZZ, operator.truediv)
        Identical parents, arithmetic performed immediately.
        Result lives in Rational Field
        Rational Field

        sage: ZZx = ZZ['x']
        sage: QQx = QQ['x']
        sage: cm.explain(ZZx, QQx, operator.truediv)
        Coercion on left operand via
            Ring morphism:
              From: Univariate Polynomial Ring in x over Integer Ring
              To:   Univariate Polynomial Ring in x over Rational Field
              Defn: Induced from base ring by
                    Natural morphism:
                      From: Integer Ring
                      To:   Rational Field
        Arithmetic performed after coercions.
        Result lives in Fraction Field of Univariate Polynomial Ring in x over Rational Field
        Fraction Field of Univariate Polynomial Ring in x over Rational Field

        sage: cm.explain(int, ZZ, operator.truediv)
        Coercion on left operand via
            Native morphism:
              From: Set of Python objects of class 'int'
              To:   Integer Ring
        Arithmetic performed after coercions.
        Result lives in Rational Field
        Rational Field

        sage: cm.explain(ZZx, ZZ, operator.truediv)
        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

    Python

        >>> from sage.all import *
        >>> cm.explain(ZZ, ZZ, operator.truediv)
        Identical parents, arithmetic performed immediately.
        Result lives in Rational Field
        Rational Field

        >>> ZZx = ZZ['x']
        >>> QQx = QQ['x']
        >>> cm.explain(ZZx, QQx, operator.truediv)
        Coercion on left operand via
            Ring morphism:
              From: Univariate Polynomial Ring in x over Integer Ring
              To:   Univariate Polynomial Ring in x over Rational Field
              Defn: Induced from base ring by
                    Natural morphism:
                      From: Integer Ring
                      To:   Rational Field
        Arithmetic performed after coercions.
        Result lives in Fraction Field of Univariate Polynomial Ring in x over Rational Field
        Fraction Field of Univariate Polynomial Ring in x over Rational Field

        >>> cm.explain(int, ZZ, operator.truediv)
        Coercion on left operand via
            Native morphism:
              From: Set of Python objects of class 'int'
              To:   Integer Ring
        Arithmetic performed after coercions.
        Result lives in Rational Field
        Rational Field

        >>> cm.explain(ZZx, ZZ, operator.truediv)
        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

    Note

    This function is accurate only in so far as `analyse()` is kept in sync with the `bin_op()` and `canonical_coercion()` which are kept separate for maximal efficiency.

  get_action(R, S, op='mul', r=None, s=None)

    Get the action of R on S or S on R associated to the operation op.

    EXAMPLES:

    Sage

        sage: cm = sage.structure.element.get_coercion_model()
        sage: ZZx = ZZ['x']
        sage: cm.get_action(ZZx, ZZ, operator.mul)
        Right scalar multiplication by Integer Ring
         on Univariate Polynomial Ring in x over Integer Ring
        sage: cm.get_action(ZZx, QQ, operator.mul)
        Right scalar multiplication by Rational Field
         on Univariate Polynomial Ring in x over Integer Ring
        sage: QQx = QQ['x']
        sage: cm.get_action(QQx, int, operator.mul)
        Right scalar multiplication by Integer Ring
         on Univariate Polynomial Ring in x over Rational Field
        with precomposition on right by Native morphism:
          From: Set of Python objects of class 'int'
          To:   Integer Ring

        sage: A = cm.get_action(QQx, ZZ, operator.truediv); A
        Right inverse action by Rational Field
         on Univariate Polynomial Ring in x over Rational Field
        with precomposition on right by Natural morphism:
          From: Integer Ring
          To:   Rational Field
        sage: x = QQx.gen()
        sage: A(x+10, 5)
        1/5*x + 2

    Python

        >>> from sage.all import *
        >>> cm = sage.structure.element.get_coercion_model()
        >>> ZZx = ZZ['x']
        >>> cm.get_action(ZZx, ZZ, operator.mul)
        Right scalar multiplication by Integer Ring
         on Univariate Polynomial Ring in x over Integer Ring
        >>> cm.get_action(ZZx, QQ, operator.mul)
        Right scalar multiplication by Rational Field
         on Univariate Polynomial Ring in x over Integer Ring
        >>> QQx = QQ['x']
        >>> cm.get_action(QQx, int, operator.mul)
        Right scalar multiplication by Integer Ring
         on Univariate Polynomial Ring in x over Rational Field
        with precomposition on right by Native morphism:
          From: Set of Python objects of class 'int'
          To:   Integer Ring

        >>> A = cm.get_action(QQx, ZZ, operator.truediv); A
        Right inverse action by Rational Field
         on Univariate Polynomial Ring in x over Rational Field
        with precomposition on right by Natural morphism:
          From: Integer Ring
          To:   Rational Field
        >>> x = QQx.gen()
        >>> A(x+Integer(10), Integer(5))
        1/5*x + 2

  get_cache()

    This returns the current cache of coercion maps and actions, primarily useful for debugging and introspection.

    EXAMPLES:

    Sage

        sage: cm = sage.structure.element.get_coercion_model()
        sage: cm.canonical_coercion(1, 2/3)
        (1, 2/3)
        sage: maps, actions = cm.get_cache()

    Python

        >>> from sage.all import *
        >>> cm = sage.structure.element.get_coercion_model()
        >>> cm.canonical_coercion(Integer(1), Integer(2)/Integer(3))
        (1, 2/3)
        >>> maps, actions = cm.get_cache()

    Now let us see what happens when we do a binary operations with an integer and a rational:

    Sage

        sage: left_morphism_ref, right_morphism_ref = maps[ZZ, QQ]

    [Python]

        >>> from sage.all import *
        >>> left_morphism_ref, right_morphism_ref = maps[ZZ, QQ]

    Note that by Issue #14058 the coercion model only stores a weak reference to the coercion maps in this case:

    Sage

        sage: left_morphism_ref
        <weakref at ...; to 'sage.rings.rational.Z_to_Q' at ...>

    Python

        >>> from sage.all import *
        >>> left_morphism_ref
        <weakref at ...; to 'sage.rings.rational.Z_to_Q' at ...>

    Moreover, the weakly referenced coercion map uses only a weak reference to the codomain:

    Sage

        sage: left_morphism_ref()
        (map internal to coercion system -- copy before use)
        Natural morphism:
          From: Integer Ring
          To:   Rational Field

    [Python]

        >>> from sage.all import *
        >>> left_morphism_ref()
        (map internal to coercion system -- copy before use)
        Natural morphism:
          From: Integer Ring
          To:   Rational Field

    To get an actual valid map, we simply copy the weakly referenced coercion map:

    Sage

        sage: print(copy(left_morphism_ref()))
        Natural morphism:
          From: Integer Ring
          To:   Rational Field
        sage: print(right_morphism_ref)
        None

    Python

        >>> from sage.all import *
        >>> print(copy(left_morphism_ref()))
        Natural morphism:
          From: Integer Ring
          To:   Rational Field
        >>> print(right_morphism_ref)
        None

    We can see that it coerces the left operand from an integer to a rational, and doesn’t do anything to the right.

    Now for some actions:

    Sage

        sage: R.<x> = ZZ['x']
        sage: 1/2 * x
        1/2*x
        sage: maps, actions = cm.get_cache()
        sage: act = actions[QQ, R, operator.mul]; act
        Left scalar multiplication by Rational Field
         on Univariate Polynomial Ring in x over Integer Ring
        sage: act.actor()
        Rational Field
        sage: act.domain()
        Univariate Polynomial Ring in x over Integer Ring
        sage: act.codomain()
        Univariate Polynomial Ring in x over Rational Field
        sage: act(1/5, x+10)
        1/5*x + 2

    [Python]

        >>> from sage.all import *
        >>> R = ZZ['x']; (x,) = R._first_ngens(1)
        >>> Integer(1)/Integer(2) * x
        1/2*x
        >>> maps, actions = cm.get_cache()
        >>> act = actions[QQ, R, operator.mul]; act
        Left scalar multiplication by Rational Field
         on Univariate Polynomial Ring in x over Integer Ring
        >>> act.actor()
        Rational Field
        >>> act.domain()
        Univariate Polynomial Ring in x over Integer Ring
        >>> act.codomain()
        Univariate Polynomial Ring in x over Rational Field
        >>> act(Integer(1)/Integer(5), x+Integer(10))
        1/5*x + 2

  record_exceptions(value=True)

    Enables (or disables) recording of the exceptions suppressed during arithmetic.

    Each time that record_exceptions is called (either enabling or disabling the record), the exception_stack is cleared.

  reset_cache()

    Clear the coercion cache.

    This should have no impact on the result of arithmetic operations, as the exact same coercions and actions will be re-discovered when needed.

    It may be useful for debugging, and may also free some memory.

    EXAMPLES:

    Sage

        sage: cm = sage.structure.element.get_coercion_model()
        sage: len(cm.get_cache()[0])    # random
        42
        sage: cm.reset_cache()
        sage: cm.get_cache()
        ({}, {})

    Python

        >>> from sage.all import *
        >>> cm = sage.structure.element.get_coercion_model()
        >>> len(cm.get_cache()[Integer(0)])    # random
        42
        >>> cm.reset_cache()
        >>> cm.get_cache()
        ({}, {})

  richcmp(x, y, op)

    Given two arbitrary objects `x` and `y`, coerce them to a common parent and compare them using rich comparison operator `op`.

    EXAMPLES:

    Sage

        sage: from sage.structure.element import get_coercion_model
        sage: from sage.structure.richcmp import op_LT, op_LE, op_EQ, op_NE, op_GT, op_GE
        sage: richcmp = get_coercion_model().richcmp
        sage: richcmp(None, None, op_EQ)
        True
        sage: richcmp(None, 1, op_LT)
        True
        sage: richcmp("hello", None, op_LE)
        False
        sage: richcmp(-1, 1, op_GE)
        False
        sage: richcmp(int(1), float(2), op_GE)
        False

    Python

        >>> from sage.all import *
        >>> from sage.structure.element import get_coercion_model
        >>> from sage.structure.richcmp import op_LT, op_LE, op_EQ, op_NE, op_GT, op_GE
        >>> richcmp = get_coercion_model().richcmp
        >>> richcmp(None, None, op_EQ)
        True
        >>> richcmp(None, Integer(1), op_LT)
        True
        >>> richcmp("hello", None, op_LE)
        False
        >>> richcmp(-Integer(1), Integer(1), op_GE)
        False
        >>> richcmp(int(Integer(1)), float(Integer(2)), op_GE)
        False

    If there is no coercion, we only support `==` and `!=`:

    Sage

        sage: x = QQ.one(); y = GF(2).one()
        sage: richcmp(x, y, op_EQ)
        False
        sage: richcmp(x, y, op_NE)
        True
        sage: richcmp(x, y, op_GT)
        Traceback (most recent call last):
        ...
        TypeError: unsupported operand parent(s) for >:
        'Rational Field' and 'Finite Field of size 2'

    [Python]

        >>> from sage.all import *
        >>> x = QQ.one(); y = GF(Integer(2)).one()
        >>> richcmp(x, y, op_EQ)
        False
        >>> richcmp(x, y, op_NE)
        True
        >>> richcmp(x, y, op_GT)
        Traceback (most recent call last):
        ...
        TypeError: unsupported operand parent(s) for >:
        'Rational Field' and 'Finite Field of size 2'

    We support non-Sage types with the usual Python convention:

    Sage

        sage: class AlwaysEqual():
        ....:     def __eq__(self, other):
        ....:         return True
        sage: x = AlwaysEqual()
        sage: x == 1
        True
        sage: 1 == x
        True

    Python

        >>> from sage.all import *
        >>> class AlwaysEqual():
        ...     def __eq__(self, other):
        ...         return True
        >>> x = AlwaysEqual()
        >>> x == Integer(1)
        True
        >>> Integer(1) == x
        True

  verify_action(action, R, S, op, fix=True)

    Verify that `action` takes an element of R on the left and S on the right, raising an error if not.

    This is used for consistency checking in the coercion model.

    EXAMPLES:

    Sage

        sage: R.<x> = ZZ['x']
        sage: cm = sage.structure.element.get_coercion_model()
        sage: cm.verify_action(R.get_action(QQ), R, QQ, operator.mul)
        Right scalar multiplication by Rational Field
         on Univariate Polynomial Ring in x over Integer Ring
        sage: cm.verify_action(R.get_action(QQ), RDF, R, operator.mul)
        Traceback (most recent call last):
        ...
        RuntimeError: There is a BUG in the coercion model:
            Action found for R <built-in function mul> S does not have the correct domains
            R = Real Double Field
            S = Univariate Polynomial Ring in x over Integer Ring
            (should be Univariate Polynomial Ring in x over Integer Ring, Rational Field)
            action = Right scalar multiplication by Rational Field
                     on Univariate Polynomial Ring in x over Integer Ring
            (<class 'sage.structure.coerce_actions.RightModuleAction'>)

    Python

        >>> from sage.all import *
        >>> R = ZZ['x']; (x,) = R._first_ngens(1)
        >>> cm = sage.structure.element.get_coercion_model()
        >>> cm.verify_action(R.get_action(QQ), R, QQ, operator.mul)
        Right scalar multiplication by Rational Field
         on Univariate Polynomial Ring in x over Integer Ring
        >>> cm.verify_action(R.get_action(QQ), RDF, R, operator.mul)
        Traceback (most recent call last):
        ...
        RuntimeError: There is a BUG in the coercion model:
            Action found for R <built-in function mul> S does not have the correct domains
            R = Real Double Field
            S = Univariate Polynomial Ring in x over Integer Ring
            (should be Univariate Polynomial Ring in x over Integer Ring, Rational Field)
            action = Right scalar multiplication by Rational Field
                     on Univariate Polynomial Ring in x over Integer Ring
            (<class 'sage.structure.coerce_actions.RightModuleAction'>)

  verify_coercion_maps(R, S, homs, fix=False)

    Make sure this is a valid pair of homomorphisms from \(R\) and \(S\) to a common parent. This function is used to protect the user against buggy parents.

    EXAMPLES:

    Sage

        sage: cm = sage.structure.element.get_coercion_model()
        sage: homs = QQ.coerce_map_from(ZZ), None
        sage: cm.verify_coercion_maps(ZZ, QQ, homs) == homs
        True
        sage: homs = QQ.coerce_map_from(ZZ), RR.coerce_map_from(QQ)
        sage: cm.verify_coercion_maps(ZZ, QQ, homs) == homs
        Traceback (most recent call last):
        ...
        RuntimeError: ('BUG in coercion model, codomains must be identical',
        Natural morphism:
          From: Integer Ring
          To:   Rational Field,
        Generic map:
          From: Rational Field
          To:   Real Field with 53 bits of precision)

    Python

        >>> from sage.all import *
        >>> cm = sage.structure.element.get_coercion_model()
        >>> homs = QQ.coerce_map_from(ZZ), None
        >>> cm.verify_coercion_maps(ZZ, QQ, homs) == homs
        True
        >>> homs = QQ.coerce_map_from(ZZ), RR.coerce_map_from(QQ)
        >>> cm.verify_coercion_maps(ZZ, QQ, homs) == homs
        Traceback (most recent call last):
        ...
        RuntimeError: ('BUG in coercion model, codomains must be identical',
        Natural morphism:
          From: Integer Ring
          To:   Rational Field,
        Generic map:
          From: Rational Field
          To:   Real Field with 53 bits of precision)

sage.structure.coerce.is_mpmath_type(t)

  Check whether the type `t` is a type whose name starts with `mpmath.`

  EXAMPLES:

  Sage

      sage: from sage.structure.coerce import is_mpmath_type
      sage: is_mpmath_type(int)
      False
      sage: import mpmath
      sage: is_mpmath_type(mpmath.mpc(2))
      False
      sage: is_mpmath_type(type(mpmath.mpc(2)))
      True
      sage: is_mpmath_type(type(mpmath.mpf(2)))
      True

  Python

      >>> from sage.all import *
      >>> from sage.structure.coerce import is_mpmath_type
      >>> is_mpmath_type(int)
      False
      >>> import mpmath
      >>> is_mpmath_type(mpmath.mpc(Integer(2)))
      False
      >>> is_mpmath_type(type(mpmath.mpc(Integer(2))))
      True
      >>> is_mpmath_type(type(mpmath.mpf(Integer(2))))
      True

sage.structure.coerce.is_numpy_type(t)

  Return `True` if and only if \(t\) is a type whose name starts with `numpy.`

  EXAMPLES:

  Sage

      sage: from sage.structure.coerce import is_numpy_type

      sage: # needs numpy
      sage: import numpy
      sage: is_numpy_type(numpy.int16)
      True
      sage: is_numpy_type(numpy.floating)
      True
      sage: is_numpy_type(numpy.ndarray)
      True
      sage: is_numpy_type(numpy.matrix)
      True

      sage: is_numpy_type(int)
      False
      sage: is_numpy_type(Integer)
      False
      sage: is_numpy_type(Sudoku)                                                     # needs sage.combinat
      False
      sage: is_numpy_type(None)
      False

  Python

      >>> from sage.all import *
      >>> from sage.structure.coerce import is_numpy_type

      >>> # needs numpy
      >>> import numpy
      >>> is_numpy_type(numpy.int16)
      True
      >>> is_numpy_type(numpy.floating)
      True
      >>> is_numpy_type(numpy.ndarray)
      True
      >>> is_numpy_type(numpy.matrix)
      True

      >>> is_numpy_type(int)
      False
      >>> is_numpy_type(Integer)
      False
      >>> is_numpy_type(Sudoku)                                                     # needs sage.combinat
      False
      >>> is_numpy_type(None)
      False

sage.structure.coerce.parent_is_integers(P)

  Check whether the type or parent represents the ring of integers.

  EXAMPLES:

  Sage

      sage: from sage.structure.coerce import parent_is_integers
      sage: parent_is_integers(int)
      True
      sage: parent_is_integers(float)
      False
      sage: parent_is_integers(bool)
      True
      sage: parent_is_integers(dict)
      False

      sage: import numpy                                                              # needs numpy
      sage: parent_is_integers(numpy.int16)                                           # needs numpy
      True
      sage: parent_is_integers(numpy.uint64)                                          # needs numpy
      True
      sage: parent_is_integers(float)
      False

      sage: import gmpy2
      sage: parent_is_integers(gmpy2.mpz)
      True
      sage: parent_is_integers(gmpy2.mpq)
      False

  Python

      >>> from sage.all import *
      >>> from sage.structure.coerce import parent_is_integers
      >>> parent_is_integers(int)
      True
      >>> parent_is_integers(float)
      False
      >>> parent_is_integers(bool)
      True
      >>> parent_is_integers(dict)
      False

      >>> import numpy                                                              # needs numpy
      >>> parent_is_integers(numpy.int16)                                           # needs numpy
      True
      >>> parent_is_integers(numpy.uint64)                                          # needs numpy
      True
      >>> parent_is_integers(float)
      False

      >>> import gmpy2
      >>> parent_is_integers(gmpy2.mpz)
      True
      >>> parent_is_integers(gmpy2.mpq)
      False

  Ensure (Issue #27893) is fixed:

  Sage

      sage: K.<f> = QQ[]
      sage: gmpy2.mpz(2) * f
      2*f

  [Python]

      >>> from sage.all import *
      >>> K = QQ['f']; (f,) = K._first_ngens(1)
      >>> gmpy2.mpz(Integer(2)) * f
      2*f

sage.structure.coerce.parent_is_numerical(P)

  Test if elements of the parent or type `P` can be numerically evaluated as complex numbers (in a canonical way).

  EXAMPLES:

  Sage

      sage: from sage.structure.coerce import parent_is_numerical
      sage: import gmpy2
      sage: [parent_is_numerical(R) for R in [QQ, int, complex, gmpy2.mpc]]           # needs sage.rings.complex_double
      [True, True, True, True]
      sage: [parent_is_numerical(R) for R in [RR, CC]]                                # needs sage.rings.real_mpfr
      [True, True]
      sage: parent_is_numerical(QuadraticField(-1))                                   # needs sage.rings.number_field
      True
      sage: import numpy; parent_is_numerical(numpy.complexfloating)                  # needs numpy
      True
      sage: parent_is_numerical(SR)                                                   # needs sage.symbolic
      False
      sage: [parent_is_numerical(R) for R in [QQ['x'], QQ[['x']], str]]
      [False, False, False]
      sage: [parent_is_numerical(R) for R in [RIF, RBF, CIF, CBF]]                    # needs sage.libs.flint
      [False, False, False, False]

  Python

      >>> from sage.all import *
      >>> from sage.structure.coerce import parent_is_numerical
      >>> import gmpy2
      >>> [parent_is_numerical(R) for R in [QQ, int, complex, gmpy2.mpc]]           # needs sage.rings.complex_double
      [True, True, True, True]
      >>> [parent_is_numerical(R) for R in [RR, CC]]                                # needs sage.rings.real_mpfr
      [True, True]
      >>> parent_is_numerical(QuadraticField(-Integer(1)))                                   # needs sage.rings.number_field
      True
      >>> import numpy; parent_is_numerical(numpy.complexfloating)                  # needs numpy
      True
      >>> parent_is_numerical(SR)                                                   # needs sage.symbolic
      False
      >>> [parent_is_numerical(R) for R in [QQ['x'], QQ[['x']], str]]
      [False, False, False]
      >>> [parent_is_numerical(R) for R in [RIF, RBF, CIF, CBF]]                    # needs sage.libs.flint
      [False, False, False, False]

sage.structure.coerce.parent_is_real_numerical(P)

  Test if elements of the parent or type `P` can be numerically evaluated as real numbers (in a canonical way).

  EXAMPLES:

  Sage

      sage: from sage.structure.coerce import parent_is_real_numerical
      sage: import gmpy2
      sage: [parent_is_real_numerical(R) for R in [RR, QQ, ZZ, RLF, int, float, gmpy2.mpq]]
      [True, True, True, True, True, True, True]
      sage: parent_is_real_numerical(QuadraticField(2))                               # needs sage.rings.number_field
      True
      sage: import numpy; parent_is_real_numerical(numpy.integer)                     # needs numpy
      True
      sage: parent_is_real_numerical(QuadraticField(-1))                              # needs sage.rings.number_field
      False
      sage: [parent_is_real_numerical(R)                                              # needs numpy
      ....:  for R in [CC, complex, gmpy2.mpc, numpy.complexfloating]]
      [False, False, False, False]
      sage: [parent_is_real_numerical(R) for R in [QQ['x'], QQ[['x']], str]]
      [False, False, False]
      sage: parent_is_real_numerical(SR)                                              # needs sage.symbolic
      False
      sage: [parent_is_real_numerical(R) for R in [RIF, RBF, CIF, CBF]]               # needs sage.libs.flint
      [False, False, False, False]

  Python

      >>> from sage.all import *
      >>> from sage.structure.coerce import parent_is_real_numerical
      >>> import gmpy2
      >>> [parent_is_real_numerical(R) for R in [RR, QQ, ZZ, RLF, int, float, gmpy2.mpq]]
      [True, True, True, True, True, True, True]
      >>> parent_is_real_numerical(QuadraticField(Integer(2)))                               # needs sage.rings.number_field
      True
      >>> import numpy; parent_is_real_numerical(numpy.integer)                     # needs numpy
      True
      >>> parent_is_real_numerical(QuadraticField(-Integer(1)))                              # needs sage.rings.number_field
      False
      >>> [parent_is_real_numerical(R)                                              # needs numpy
      ...  for R in [CC, complex, gmpy2.mpc, numpy.complexfloating]]
      [False, False, False, False]
      >>> [parent_is_real_numerical(R) for R in [QQ['x'], QQ[['x']], str]]
      [False, False, False]
      >>> parent_is_real_numerical(SR)                                              # needs sage.symbolic
      False
      >>> [parent_is_real_numerical(R) for R in [RIF, RBF, CIF, CBF]]               # needs sage.libs.flint
      [False, False, False, False]

sage.structure.coerce.py_scalar_parent(py_type)

  Return the Sage equivalent of the given python type, if one exists. If there is no equivalent, return `None`.

  EXAMPLES:

  Sage

      sage: from sage.structure.coerce import py_scalar_parent
      sage: py_scalar_parent(int)
      Integer Ring
      sage: py_scalar_parent(float)
      Real Double Field
      sage: py_scalar_parent(complex)                                                 # needs sage.rings.complex_double
      Complex Double Field
      sage: py_scalar_parent(bool)
      Integer Ring
      sage: py_scalar_parent(dict),
      (None,)

      sage: import fractions
      sage: py_scalar_parent(fractions.Fraction)
      Rational Field

      sage: # needs numpy
      sage: import numpy
      sage: py_scalar_parent(numpy.int16)
      Integer Ring
      sage: py_scalar_parent(numpy.int32)
      Integer Ring
      sage: py_scalar_parent(numpy.uint64)
      Integer Ring
      sage: py_scalar_parent(numpy.double)
      Real Double Field

      sage: import gmpy2
      sage: py_scalar_parent(gmpy2.mpz)
      Integer Ring
      sage: py_scalar_parent(gmpy2.mpq)
      Rational Field
      sage: py_scalar_parent(gmpy2.mpfr)
      Real Double Field
      sage: py_scalar_parent(gmpy2.mpc)                                               # needs sage.rings.complex_double
      Complex Double Field

      sage: import mpmath
      sage: py_scalar_parent(mpmath.mpf)
      Real Double Field
      sage: py_scalar_parent(mpmath.mpc)                                              # needs sage.rings.complex_double
      Complex Double Field

  Python

      >>> from sage.all import *
      >>> from sage.structure.coerce import py_scalar_parent
      >>> py_scalar_parent(int)
      Integer Ring
      >>> py_scalar_parent(float)
      Real Double Field
      >>> py_scalar_parent(complex)                                                 # needs sage.rings.complex_double
      Complex Double Field
      >>> py_scalar_parent(bool)
      Integer Ring
      >>> py_scalar_parent(dict),
      (None,)

      >>> import fractions
      >>> py_scalar_parent(fractions.Fraction)
      Rational Field

      >>> # needs numpy
      >>> import numpy
      >>> py_scalar_parent(numpy.int16)
      Integer Ring
      >>> py_scalar_parent(numpy.int32)
      Integer Ring
      >>> py_scalar_parent(numpy.uint64)
      Integer Ring
      >>> py_scalar_parent(numpy.double)
      Real Double Field

      >>> import gmpy2
      >>> py_scalar_parent(gmpy2.mpz)
      Integer Ring
      >>> py_scalar_parent(gmpy2.mpq)
      Rational Field
      >>> py_scalar_parent(gmpy2.mpfr)
      Real Double Field
      >>> py_scalar_parent(gmpy2.mpc)                                               # needs sage.rings.complex_double
      Complex Double Field

      >>> import mpmath
      >>> py_scalar_parent(mpmath.mpf)
      Real Double Field
      >>> py_scalar_parent(mpmath.mpc)                                              # needs sage.rings.complex_double
      Complex Double Field

sage.structure.coerce.py_scalar_to_element(x)

  Convert `x` to a Sage `Element` if possible.

  If `x` was already an `Element` or if there is no obvious conversion possible, just return `x` itself.

  EXAMPLES:

  Sage

      sage: from sage.structure.coerce import py_scalar_to_element
      sage: x = py_scalar_to_element(42)
      sage: x, parent(x)
      (42, Integer Ring)
      sage: x = py_scalar_to_element(int(42))
      sage: x, parent(x)
      (42, Integer Ring)
      sage: x = py_scalar_to_element(float(42))
      sage: x, parent(x)
      (42.0, Real Double Field)
      sage: x = py_scalar_to_element(complex(42))                                     # needs sage.rings.complex_double
      sage: x, parent(x)                                                              # needs sage.rings.complex_double
      (42.0, Complex Double Field)
      sage: py_scalar_to_element('hello')
      'hello'

      sage: from fractions import Fraction
      sage: f = Fraction(int(2^100), int(3^100))
      sage: py_scalar_to_element(f)
      1267650600228229401496703205376/515377520732011331036461129765621272702107522001

  Python

      >>> from sage.all import *
      >>> from sage.structure.coerce import py_scalar_to_element
      >>> x = py_scalar_to_element(Integer(42))
      >>> x, parent(x)
      (42, Integer Ring)
      >>> x = py_scalar_to_element(int(Integer(42)))
      >>> x, parent(x)
      (42, Integer Ring)
      >>> x = py_scalar_to_element(float(Integer(42)))
      >>> x, parent(x)
      (42.0, Real Double Field)
      >>> x = py_scalar_to_element(complex(Integer(42)))                                     # needs sage.rings.complex_double
      >>> x, parent(x)                                                              # needs sage.rings.complex_double
      (42.0, Complex Double Field)
      >>> py_scalar_to_element('hello')
      'hello'

      >>> from fractions import Fraction
      >>> f = Fraction(int(Integer(2)**Integer(100)), int(Integer(3)**Integer(100)))
      >>> py_scalar_to_element(f)
      1267650600228229401496703205376/515377520732011331036461129765621272702107522001

  Note that bools are converted to 0 or 1:

  Sage

      sage: py_scalar_to_element(False), py_scalar_to_element(True)
      (0, 1)

  [Python]

      >>> from sage.all import *
      >>> py_scalar_to_element(False), py_scalar_to_element(True)
      (0, 1)

  Test gmpy2’s types:

  Sage

      sage: import gmpy2
      sage: x = py_scalar_to_element(gmpy2.mpz(42))
      sage: x, parent(x)
      (42, Integer Ring)
      sage: x = py_scalar_to_element(gmpy2.mpq('3/4'))
      sage: x, parent(x)
      (3/4, Rational Field)
      sage: x = py_scalar_to_element(gmpy2.mpfr(42.57))
      sage: x, parent(x)
      (42.57, Real Double Field)
      sage: x = py_scalar_to_element(gmpy2.mpc(int(42), int(42)))                     # needs sage.rings.complex_double
      sage: x, parent(x)                                                              # needs sage.rings.complex_double
      (42.0 + 42.0*I, Complex Double Field)

  Python

      >>> from sage.all import *
      >>> import gmpy2
      >>> x = py_scalar_to_element(gmpy2.mpz(Integer(42)))
      >>> x, parent(x)
      (42, Integer Ring)
      >>> x = py_scalar_to_element(gmpy2.mpq('3/4'))
      >>> x, parent(x)
      (3/4, Rational Field)
      >>> x = py_scalar_to_element(gmpy2.mpfr(RealNumber('42.57')))
      >>> x, parent(x)
      (42.57, Real Double Field)
      >>> x = py_scalar_to_element(gmpy2.mpc(int(Integer(42)), int(Integer(42))))                     # needs sage.rings.complex_double
      >>> x, parent(x)                                                              # needs sage.rings.complex_double
      (42.0 + 42.0*I, Complex Double Field)

  Test compatibility with `py_scalar_parent()`:

  Sage

      sage: from sage.structure.coerce import py_scalar_parent
      sage: elt = [True, int(42), float(42), complex(42)]
      sage: for x in elt:                                                             # needs sage.rings.complex_double
      ....:     assert py_scalar_parent(type(x)) == py_scalar_to_element(x).parent()

      sage: import numpy                                                              # needs numpy
      sage: elt = [numpy.int8('-12'),  numpy.uint8('143'),                            # needs numpy
      ....:        numpy.int16('-33'), numpy.uint16('122'),
      ....:        numpy.int32('-19'), numpy.uint32('44'),
      ....:        numpy.int64('-3'),  numpy.uint64('552'),
      ....:        numpy.float16('-1.23'), numpy.float32('-2.22'),
      ....:        numpy.float64('-3.412'), numpy.complex64(1.2+I),
      ....:        numpy.complex128(-2+I)]
      sage: for x in elt:                                                             # needs numpy
      ....:     assert py_scalar_parent(type(x)) == py_scalar_to_element(x).parent()

      sage: elt = [gmpy2.mpz(42), gmpy2.mpq('3/4'),
      ....:        gmpy2.mpfr(42.57), gmpy2.mpc(int(42), int(42))]
      sage: for x in elt:                                                             # needs sage.rings.complex_double
      ....:     assert py_scalar_parent(type(x)) == py_scalar_to_element(x).parent()

  [Python]

      >>> from sage.all import *
      >>> from sage.structure.coerce import py_scalar_parent
      >>> elt = [True, int(Integer(42)), float(Integer(42)), complex(Integer(42))]
      >>> for x in elt:                                                             # needs sage.rings.complex_double
      ...     assert py_scalar_parent(type(x)) == py_scalar_to_element(x).parent()

      >>> import numpy                                                              # needs numpy
      >>> elt = [numpy.int8('-12'),  numpy.uint8('143'),                            # needs numpy
      ...        numpy.int16('-33'), numpy.uint16('122'),
      ...        numpy.int32('-19'), numpy.uint32('44'),
      ...        numpy.int64('-3'),  numpy.uint64('552'),
      ...        numpy.float16('-1.23'), numpy.float32('-2.22'),
      ...        numpy.float64('-3.412'), numpy.complex64(RealNumber('1.2')+I),
      ...        numpy.complex128(-Integer(2)+I)]
      >>> for x in elt:                                                             # needs numpy
      ...     assert py_scalar_parent(type(x)) == py_scalar_to_element(x).parent()

      >>> elt = [gmpy2.mpz(Integer(42)), gmpy2.mpq('3/4'),
      ...        gmpy2.mpfr(RealNumber('42.57')), gmpy2.mpc(int(Integer(42)), int(Integer(42)))]
      >>> for x in elt:                                                             # needs sage.rings.complex_double
      ...     assert py_scalar_parent(type(x)) == py_scalar_to_element(x).parent()