Base class for parent objects

CLASS HIERARCHY:

SageObject
    CategoryObject
        Parent

TESTS: This came up in some subtle bug once.

sage: gp(2) + gap(3)
5

A simple example of registering coercions:

sage: class A_class(Parent):
...     def __init__(self, name):
...         Parent.__init__(self, name=name)
...         self._populate_coercion_lists_()
...         self.rename(name)
...     #
...     def category(self):
...         return Sets()
...     #
...     def _element_constructor_(self, i):
...         assert(isinstance(i, (int, Integer)))
...         return ElementWrapper(i, parent = self)
...
...
sage: A = A_class("A")
sage: B = A_class("B")
sage: C = A_class("C")

sage: def f(a):
...     return B(a.value+1)
...
sage: class MyMorphism(Morphism):
...     def __init__(self, domain, codomain):
...         Morphism.__init__(self, Hom(domain, codomain))
...     #
...     def _call_(self, x):
...         return self.codomain()(x.value)
...
sage: f = MyMorphism(A,B)
sage: f
    Generic morphism:
      From: A
      To:   B
sage: B.register_coercion(f)
sage: C.register_coercion(MyMorphism(B,C))
sage: A(A(1)) == A(1)
True
sage: B(A(1)) == B(1)
True
sage: C(A(1)) == C(1)
True

sage: A(B(1))
Traceback (most recent call last):
...
AssertionError
class sage.structure.parent.A

Bases: object

class sage.structure.parent.EltPair

Bases: object

short_repr()
class sage.structure.parent.Parent

Bases: sage.structure.category_object.CategoryObject

Parents are the Sage/mathematical analogues of container objects in computer science.

Internal invariants:
  • self._element_init_pass_parent == guess_pass_parent(self, self._element_constructor) Ensures that self.__call__ passes down the parent properly to self._element_constructor. See #5979.
Hom(codomain, category=None)

Return the homspace Hom(self, codomain, cat) of all homomorphisms from self to codomain in the category cat. The default category is category`().

EXAMPLES:

sage: R.<x,y> = PolynomialRing(QQ, 2)
sage: R.Hom(QQ)
Set of Homomorphisms from Multivariate Polynomial Ring in x, y over Rational Field to Rational Field

Homspaces are defined for very general Sage objects, even elements of familiar rings.

sage: n = 5; Hom(n,7)
Set of Morphisms from 5 to 7 in Category of elements of Integer Ring
sage: z=(2/3); Hom(z,8/1)
Set of Morphisms from 2/3 to 8 in Category of elements of Rational Field

This example illustrates the optional third argument:

sage: QQ.Hom(ZZ, Sets())
Set of Morphisms from Rational Field to Integer Ring in Category of sets

A parent may specify how to construct certain homsets by implementing a method _Hom_`(codomain, category). This method should either construct the requested homset or raise a ``TypeError`().

an_element()

Returns a (preferably typical) element of this parent.

This is used both for illustration and testing purposes. If the set self is empty, an_element() raises the exception EmptySetError.

This calls _an_element_() (which see), and caches the result. Parent are thus encouraged to override _an_element_().

EXAMPLES:

sage: CDF.an_element()
1.0*I
sage: ZZ[['t']].an_element()
t

In case the set is empty, an EmptySetError is raised:

sage: Set([]).an_element()
Traceback (most recent call last):
...
EmptySetError
category()

EXAMPLES:

sage: P = Parent()
sage: P.category()
Category of sets
sage: class MyParent(Parent):
...       def __init__(self): pass
sage: MyParent().category()
Category of sets
coerce(x)

Return x as an element of self, if and only if there is a canonical coercion from the parent of x to self.

EXAMPLES:

sage: QQ.coerce(ZZ(2))
2
sage: ZZ.coerce(QQ(2))
Traceback (most recent call last):
...
TypeError: no canonical coercion from Rational Field to Integer Ring

We make an exception for zero:

sage: V = GF(7)^7
sage: V.coerce(0)
(0, 0, 0, 0, 0, 0, 0)
coerce_embedding()

Returns the embedding of self into some other parent, if such a parent exists.

This does not mean that there are no coercion maps from self into other fields, this is simply a specific morphism specified out of self and usually denotes a special relationship (e.g. sub-objects, choice of completion, etc.)

EXAMPLES:

sage: K.<a>=NumberField(x^3+x^2+1,embedding=1)
sage: K.coerce_embedding()
Generic morphism:
  From: Number Field in a with defining polynomial x^3 + x^2 + 1
  To:   Real Lazy Field
  Defn: a -> -1.465571231876768?
sage: K.<a>=NumberField(x^3+x^2+1,embedding=CC.gen())
sage: K.coerce_embedding()
Generic morphism:
  From: Number Field in a with defining polynomial x^3 + x^2 + 1
  To:   Complex Lazy Field
  Defn: a -> 0.2327856159383841? + 0.7925519925154479?*I
coerce_map_from(S)

This returns a Map object to coerce from S to self if one exists, or None if no such coercion exists.

EXAMPLES:

sage: ZZ.coerce_map_from(int)
Native morphism:
  From: Set of Python objects of type 'int'
  To:   Integer Ring
sage: QQ.coerce_map_from(ZZ)
Natural morphism:
  From: Integer Ring
  To:   Rational Field
construction()

Returns a pair (functor, parent) such that functor(parent) return self. If this ring does not have a functorial construction, return None.

EXAMPLES:

sage: QQ.construction()
(FractionField, Integer Ring)
sage: f, R = QQ['x'].construction()
sage: f
Poly[x]
sage: R
Rational Field
sage: f(R)
Univariate Polynomial Ring in x over Rational Field
convert_map_from(S)

This function returns a Map from S to self, which may or may not succeed on all inputs. If a coercion map from S to self exists, then the it will be returned. If a coercion from self to S exists, then it will attempt to return a section of that map.

Under the new coercion model, this is the fastest way to convert elements of S to elements of self (short of manually constructing the elements) and is used by __call__.

EXAMPLES:

sage: m = ZZ.convert_map_from(QQ)
sage: m(-35/7)
-5
sage: parent(m(-35/7))
Integer Ring
element_class()

The (default) class for the elements of this parent

FIXME’s and design issues:
  • If self.Element is “trivial enough”, should we optimize it away with: self.element_class = dynamic_class(“%s.element_class”%self.__class__.__name__, (category.element_class,), self.Element)
  • This should lookup for Element classes in all super classes
get_action(S, op='operator.mul', self_on_left=True)

Returns an action of self on S or S on self.

To provide additional actions, override _get_action_().

TESTS:

sage: M = QQ['y']^3
sage: M.get_action(ZZ['x']['y'])
Right scalar multiplication by Univariate Polynomial Ring in y over Univariate Polynomial Ring in x over Integer Ring on Ambient free module of rank 3 over the principal ideal domain Univariate Polynomial Ring in y over Rational Field
sage: M.get_action(ZZ['x']) # should be None
has_coerce_map_from(S)

Return True if there is a natural map from S to self. Otherwise, return False.

EXAMPLES:

sage: RDF.has_coerce_map_from(QQ)
True
sage: RDF.has_coerce_map_from(QQ['x'])
False
sage: RDF['x'].has_coerce_map_from(QQ['x'])
True
sage: RDF['x,y'].has_coerce_map_from(QQ['x'])
True
hom(im_gens, codomain=None, check=None)

Return the unique homomorphism from self to codomain that sends self.gens() to the entries of im_gens. Raises a TypeError if there is no such homomorphism.

INPUT:

  • im_gens - the images in the codomain of the generators of this object under the homomorphism
  • codomain - the codomain of the homomorphism
  • check - whether to verify that the images of generators extend to define a map (using only canonical coercions).

OUTPUT:

  • a homomorphism self –> codomain

Note

As a shortcut, one can also give an object X instead of im_gens, in which case return the (if it exists) natural map to X.

EXAMPLE: Polynomial Ring We first illustrate construction of a few homomorphisms involving a polynomial ring.

sage: R.<x> = PolynomialRing(ZZ)
sage: f = R.hom([5], QQ)
sage: f(x^2 - 19)
6

sage: R.<x> = PolynomialRing(QQ)
sage: f = R.hom([5], GF(7))
Traceback (most recent call last):
...
TypeError: images do not define a valid homomorphism

sage: R.<x> = PolynomialRing(GF(7))
sage: f = R.hom([3], GF(49,'a'))
sage: f
Ring morphism:
  From: Univariate Polynomial Ring in x over Finite Field of size 7
  To:   Finite Field in a of size 7^2
  Defn: x |--> 3
sage: f(x+6)
2
sage: f(x^2+1)
3

EXAMPLE: Natural morphism

sage: f = ZZ.hom(GF(5))
sage: f(7)
2
sage: f
Ring Coercion morphism:
  From: Integer Ring
  To:   Finite Field of size 5

There might not be a natural morphism, in which case a TypeError exception is raised.

sage: QQ.hom(ZZ)
Traceback (most recent call last):
...
TypeError: Natural coercion morphism from Rational Field to Integer Ring not defined.
is_exact()

Return True if elements of this ring are represented exactly, i.e., there is no precision loss when doing arithmetic.

NOTE: This defaults to true, so even if it does return True you have no guarantee (unless the ring has properly overloaded this).

EXAMPLES:
sage: QQ.is_exact() True sage: ZZ.is_exact() True sage: Qp(7).is_exact() False sage: Zp(7, type=’capped-abs’).is_exact() False
register_action(action)

Update the coercion model to use action to act on self.

action should be of type sage.categories.action.Action.

EXAMPLES:

sage: import sage.categories.action
sage: import operator

sage: class SymmetricGroupAction(sage.categories.action.Action):
...       "Act on a multivariate polynomial ring by permuting the generators."
...       def __init__(self, G, M, is_left=True):
...           sage.categories.action.Action.__init__(self, G, M, is_left, operator.mul)
...   
...       def _call_(self, g, a):
...           if not self.is_left():
...               g, a = a, g
...           D = {}
...           for k, v in a.dict().items():
...               nk = [0]*len(k)
...               for i in range(len(k)):
...                   nk[g(i+1)-1] = k[i]
...               D[tuple(nk)] = v
...           return a.parent()(D)

sage: R.<x, y, z> = QQ['x, y, z']
sage: G = SymmetricGroup(3)
sage: act = SymmetricGroupAction(G, R)
sage: t = x + 2*y + 3*z

sage: act(G((1, 2)), t)
2*x + y + 3*z
sage: act(G((2, 3)), t)
x + 3*y + 2*z
sage: act(G((1, 2, 3)), t)
3*x + y + 2*z

This should fail, since we haven’t registered the left action:

sage: G((1,2)) * t
Traceback (most recent call last):
...
TypeError: ...

Now let’s make it work:

sage: R._unset_coercions_used()
sage: R.register_action(act)
sage: G((1, 2)) * t
2*x + y + 3*z
register_coercion(mor)

Update the coercion model to use mor : P \to \text{self} to coerce from a parent P into self.

For safety, an error is raised if another coercion has already been registered or discovered between P and self.

EXAMPLES:

sage: K.<a> = ZZ['a']
sage: L.<b> = ZZ['b']
sage: L_into_K = L.hom([-a]) # non-trivial automorphism
sage: K.register_coercion(L_into_K)

sage: K(0) + b
-a
sage: a + b
0
sage: K(b) # check that convert calls coerce first; normally this is just a
-a

sage: L(0) + a in K # this goes through the coercion mechanism of K
True
sage: L(a) in L # this still goes through the convert mechanism of L
True

sage: K.register_coercion(L_into_K)
Traceback (most recent call last):
...
AssertionError: coercion from Univariate Polynomial Ring in b over Integer Ring to Univariate Polynomial Ring in a over Integer Ring already registered or discovered
register_conversion(mor)

Update the coercion model to use \text{mor} : P \to \text{self} to convert from P into self.

EXAMPLES:

sage: K.<a> = ZZ['a']
sage: M.<c> = ZZ['c']
sage: M_into_K = M.hom([a]) # trivial automorphism
sage: K._unset_coercions_used()
sage: K.register_conversion(M_into_K)

sage: K(c)
a
sage: K(0) + c
Traceback (most recent call last):
...
TypeError: ...
register_embedding(embedding)

Update the coercion model to use \text{embedding} : \text{self} \to
P to embed self into the parent P.

There can only be one embedding registered; it can only be registered once; and it must be registered before using this parent in the coercion model.

EXAMPLES:

sage: S3 = AlternatingGroup(3)
sage: G = SL(3, QQ)
sage: p = S3[2]; p.matrix()
[0 0 1]
[1 0 0]
[0 1 0]

By default, one can’t mix matrices and permutations:

sage: G(p)
Traceback (most recent call last):
...
TypeError: Cannot coerce (1,3,2) to a 3-by-3 matrix over Rational Field
sage: G(1) * p
Traceback (most recent call last):
...
TypeError: ...
sage: phi = S3.hom(lambda p: G(p.matrix()), codomain = G)
sage: phi(p)
[0 0 1]
[1 0 0]
[0 1 0]
sage: S3._unset_coercions_used()
sage: S3.register_embedding(phi)
sage: S3.coerce_embedding()
Generic morphism:
  From: AlternatingGroup(3)
  To:   Special Linear Group of degree 3 over Rational Field
sage: S3.coerce_embedding()(p)
[0 0 1]
[1 0 0]
[0 1 0]

Hmm, some more work is apparently in order:

sage: G(p)                               # todo: not implemented
sage: G(1) * p                           # todo: not implemented

The following more advanced examples fail since Sage 4.3, by lack of support for field morphisms from a field into a subfield of an algebra (they worked by abuse beforehand).

sage: x = QQ[‘x’].0 sage: t = abs(ZZ.random_element(10^6)) sage: K = NumberField(x^2 + 2*3*7*11, “a”+str(t)) sage: a = K.gen() sage: K_into_MS = K.hom([a.matrix()]) # todo: not implemented sage: K._unset_coercions_used() sage: K.register_embedding(K_into_MS) # todo: not implemented

sage: L = NumberField(x^2 + 2*3*7*11*19*31, “b”+str(abs(ZZ.random_element(10^6)))) sage: b = L.gen() sage: L_into_MS = L.hom([b.matrix()]) # todo: not implemented sage: L._unset_coercions_used() sage: L.register_embedding(L_into_MS) # todo: not implemented

sage: K.coerce_embedding()(a) # todo: not implemented [ 0 1] [-462 0] sage: L.coerce_embedding()(b) # todo: not implemented [ 0 1] [-272118 0]

sage: a.matrix() * b # todo: not implemented [-272118 0] [ 0 -462] sage: a * b.matrix() # todo: not implemented [-272118 0] [ 0 -462]

sage.structure.parent.Set_PythonType(theType)

Return the (unique) Parent that represents the set of Python objects of a specified type.

EXAMPLES:

  sage: from sage.structure.parent import Set_PythonType
  sage: Set_PythonType(list)
  Set of Python objects of type 'list'
  sage: Set_PythonType(list) is Set_PythonType(list)
  True
  sage: S = Set_PythonType(tuple)
  sage: S([1,2,3])
  (1, 2, 3)

S is a parent which models the set of all lists:
  sage: S.category()
  Category of sets
EXAMPLES:
sage: R = sage.structure.parent.Set_PythonType(int) sage: S = sage.structure.parent.Set_PythonType(float) sage: Hom(R, S) Set of Morphisms from Set of Python objects of type ‘int’ to Set of Python objects of type ‘float’ in Category of sets
class sage.structure.parent.Set_PythonType_class

Bases: sage.structure.parent.Set_generic

cardinality()

EXAMPLES:

sage: S = sage.structure.parent.Set_PythonType(bool)
sage: S.cardinality()
2
sage: S = sage.structure.parent.Set_PythonType(int)
sage: S.cardinality()
4294967296                        # 32-bit
18446744073709551616              # 64-bit
sage: S = sage.structure.parent.Set_PythonType(float)
sage: S.cardinality()
18437736874454810627
sage: S = sage.structure.parent.Set_PythonType(long)
sage: S.cardinality()
+Infinity
object()

EXAMPLES:

sage: S = sage.structure.parent.Set_PythonType(tuple)
sage: S.object()
<type 'tuple'>
class sage.structure.parent.Set_generic

Bases: sage.structure.parent.Parent

Abstract base class for sets.

TESTS:

sage: Set(QQ).category()
Category of sets
object()
sage.structure.parent.dir_with_other_class(self, cls)

Emulates dir(self), as if self was also an instance cls, right after caller_class in the method resolution order (self.__class__.mro())

EXAMPLES:

sage: class A(object):
...      a = 1
...      b = 2
...      c = 3
sage: class B(object):
...      b = 2
...      c = 3
...      d = 4
sage: x = A()
sage: x.c = 1; x.e = 1
sage: sage.structure.parent.dir_with_other_class(x, B)
[..., 'a', 'b', 'c', 'd', 'e']

Check that objects without dicts are well handled

sage: F.<x0,x1> = BooleanPolynomialRing() sage: hasattr(F, ‘__dict__’) False sage: sage.structure.parent.dir_with_other_class(F, B) [..., ... ‘__class__’, ..., ‘_test_pickling’, ..., ‘b’, ..., ‘extension’, ...]
sage.structure.parent.getattr_from_other_class(self, cls, name)

INPUT:

- ``self``: some object
- ``cls``: a class
- ``name``: a string

Emulates getattr(self, name), as if self was an instance of cls.

If self is an instance of cls, raises an AttributeError, to avoid a double lookup. This function is intended to be called from __getattr__, and so should not be called if name is an attribute of self.

TODO: lookup if such a function readilly exists in Python, and if not triple check this specs and make this implementation rock-solid.

Caveat: this is pretty hacky, does not handle caching, there is no guarantee of robustness with super calls and descriptors, ...

EXAMPLES:

sage: from sage.structure.parent import getattr_from_other_class
sage: class A(object):
...        def inc(self):
...            return self + 1
...        @lazy_attribute
...        def lazy_attribute(self):
...            return repr(self)
sage: getattr_from_other_class(1, A, "inc")
<bound method A.inc of 1>
sage: getattr_from_other_class(1, A, "inc")()
2

Caveat: lazy attributes don’t work currently with extension types, with or without a __dict__:

sage: getattr_from_other_class(1, A, “lazy_attribute”) Traceback (most recent call last): ... AttributeError: ‘sage.rings.integer.Integer’ object has no attribute ‘lazy_attribute’ sage: getattr_from_other_class(ZZ, A, “lazy_attribute”) Traceback (most recent call last): ... AttributeError: ‘sage.rings.integer_ring.IntegerRing_class’ object has no attribute ‘lazy_attribute’ sage: getattr_from_other_class(PolynomialRing(QQ, name=’x’, sparse=True).one(), A, “lazy_attribute”) ‘1’ sage: getattr_from_other_class(PolynomialRing(QQ, name=’x’, implementation=”FLINT”).one(), A, “lazy_attribute”) Traceback (most recent call last): ... AttributeError: ‘sage.rings.polynomial.polynomial_rational_flint.Polynomial_rational_flint’ object has no attribute ‘lazy_attribute’

In general, descriptors are not yet well supported, because they often do not accept to be cheated with the type of their instance:

sage: A.__weakref__.__get__(1)
Traceback (most recent call last):
...
TypeError: descriptor '__weakref__' for 'A' objects doesn't apply to 'sage.rings.integer.Integer' object

When this occurs, an AttributeError is raised:

sage: getattr_from_other_class(1, A, "__weakref__")
Traceback (most recent call last):
...
AttributeError: 'sage.rings.integer.Integer' object has no attribute '__weakref__'

This was caught by #8296 for which we do a couple more tests:

sage: "__weakref__" in dir(A)
True
sage: "__weakref__" in dir(1)
True
sage: 1.__weakref__
Traceback (most recent call last):
...
AttributeError: 'sage.rings.integer.Integer' object has no attribute '__weakref__'
sage: import IPython
sage: _ip = IPython.ipapi.get()
sage: _ip.IP.magic_psearch('n.__weakref__') # not tested: only works with an interactive shell running

Caveat: When __call__ is not defined for instances, using A.__call__ yields the method __call__ of the class. We use a workaround but there is no guarantee for robustness.

sage: getattr_from_other_class(1, A, “__call__”) Traceback (most recent call last): ... AttributeError: ‘sage.rings.integer.Integer’ object has no attribute ‘__call__’
sage.structure.parent.is_Parent(x)

Return True if x is a parent object, i.e., derives from sage.structure.parent.Parent and False otherwise.

EXAMPLES:

sage: from sage.structure.parent import is_Parent
sage: is_Parent(2/3)
False
sage: is_Parent(ZZ)
True
sage: is_Parent(Primes())
True    
sage.structure.parent.is_extension_type(cls)
INPUT:
  • cls: a class

Tests whether cls is an extension type (int, list, cython compiled classes, ...)

EXAMPLES
sage: from sage.structure.parent import is_extension_type sage: is_extension_type(int) True sage: is_extension_type(list) True sage: is_extension_type(ZZ.__class__) True sage: is_extension_type(QQ.__class__) False
sage.structure.parent.normalize_names(ngens, names)

TESTS:

sage: sage.structure.parent.normalize_names(5, 'x')
('x0', 'x1', 'x2', 'x3', 'x4')
sage: sage.structure.parent.normalize_names(2, ['x','y'])
('x', 'y')

Previous topic

The set of prime numbers.

Next topic

Coercion

This Page