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

x.__init__(...) initializes x; see x.__class__.__doc__ for signature

class sage.structure.parent.AttributeErrorMessage

Bases: object

Tries to emulate the standard Python AttributeError message.

NOTE:

The typical fate of an attribute error is being caught. Hence, under normal circumstances, nobody will ever see the error message. The idea for this class is to provide an object that is fast to create and whose string representation is an attribute error’s message. That string representation is only created if someone wants to see it.

EXAMPLES:

sage: 1.bla  #indirect doctest
Traceback (most recent call last):
...
AttributeError: 'sage.rings.integer.Integer' object has no attribute 'bla'
sage: QQ[x].gen().bla
Traceback (most recent call last):
...
AttributeError: 'sage.rings.polynomial.polynomial_rational_flint.Polynomial_rational_flint' object has no attribute 'bla'
sage: from sage.structure.parent import AttributeErrorMessage
sage: AttributeErrorMessage(int(1),'bla')
'int' object has no attribute 'bla'

AUTHOR:

  • Simon King (2011-05-21)
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: Alternating group of order 3!/2 as a permutation group
  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

EXAMPLES:

sage: S = sage.structure.parent.Set_PythonType(float)
sage: S.category()
Category of sets
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