Base class for parent objects¶
CLASS HIERARCHY:
SageObject
CategoryObject
Parent
A simple example of registering coercions:
sage: class A_class(Parent):
....: def __init__(self, name):
....: Parent.__init__(self)
....: self._populate_coercion_lists_()
....: self.rename(name)
....:
....: def category(self):
....: return Sets()
....:
....: def _element_constructor_(self, i):
....: assert(isinstance(i, (int, Integer)))
....: return ElementWrapper(self, i)
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
>>> from sage.all import *
>>> class A_class(Parent):
... def __init__(self, name):
... Parent.__init__(self)
... self._populate_coercion_lists_()
... self.rename(name)
....:
>>> def category(self):
... return Sets()
....:
>>> def _element_constructor_(self, i):
... assert(isinstance(i, (int, Integer)))
... return ElementWrapper(self, i)
>>> A = A_class("A")
>>> B = A_class("B")
>>> C = A_class("C")
>>> def f(a):
... return B(a.value+Integer(1))
>>> class MyMorphism(Morphism):
... def __init__(self, domain, codomain):
... Morphism.__init__(self, Hom(domain, codomain))
....:
>>> def _call_(self, x):
... return self.codomain()(x.value)
>>> f = MyMorphism(A,B)
>>> f
Generic morphism:
From: A
To: B
>>> B.register_coercion(f)
>>> C.register_coercion(MyMorphism(B,C))
>>> A(A(Integer(1))) == A(Integer(1))
True
>>> B(A(Integer(1))) == B(Integer(1))
True
>>> C(A(Integer(1))) == C(Integer(1))
True
>>> A(B(Integer(1)))
Traceback (most recent call last):
...
AssertionError
When implementing an element of a ring, one would typically provide the
element class with _rmul_
and/or _lmul_
methods for the action of a
base ring, and with _mul_
for the ring multiplication. However, prior to
Issue #14249, it would have been necessary to additionally define a method
_an_element_()
for the parent. But now, the following example works:
sage: from sage.structure.element import RingElement
sage: class MyElement(RingElement):
....: def __init__(self, parent, x, y):
....: RingElement.__init__(self, parent)
....: def _mul_(self, other):
....: return self
....: def _rmul_(self, other):
....: return self
....: def _lmul_(self, other):
....: return self
sage: class MyParent(Parent):
....: Element = MyElement
>>> from sage.all import *
>>> from sage.structure.element import RingElement
>>> class MyElement(RingElement):
... def __init__(self, parent, x, y):
... RingElement.__init__(self, parent)
... def _mul_(self, other):
... return self
... def _rmul_(self, other):
... return self
... def _lmul_(self, other):
... return self
>>> class MyParent(Parent):
... Element = MyElement
Now, we define
sage: P = MyParent(base=ZZ, category=Rings())
sage: a = P(1,2)
sage: a*a is a
True
sage: a*2 is a
True
sage: 2*a is a
True
>>> from sage.all import *
>>> P = MyParent(base=ZZ, category=Rings())
>>> a = P(Integer(1),Integer(2))
>>> a*a is a
True
>>> a*Integer(2) is a
True
>>> Integer(2)*a is a
True
- class sage.structure.parent.Parent[source]¶
Bases:
CategoryObject
Base class for all parents.
Parents are the Sage/mathematical analogues of container objects in computer science.
INPUT:
base
– an algebraic structure considered to be the “base” of this parent (e.g. the base field for a vector space)category
– a category or list/tuple of categories. The category in which this parent lies (or list or tuple thereof). Since categories support more general super-categories, this should be the most specific category possible. If category is a list or tuple, aJoinCategory
is created out of them. If category is not specified, the category will be guessed (seeCategoryObject
), but will not be used to inherit parent’s or element’s code from this category.names
– names of generatorsnormalize
– whether to standardize the names (remove punctuation, etc.)facade
– a parent, or tuple thereof, orTrue
If
facade
is specified, thenSets().Facade()
is added to the categories of the parent. Furthermore, iffacade
is notTrue
, the internal attribute_facade_for
is set accordingly for use bySets.Facade.ParentMethods.facade_for()
.Internal invariants:
self._element_init_pass_parent == guess_pass_parent(self, self._element_constructor)
Ensures that__call__()
passes down the parent properly to_element_constructor()
. See Issue #5979.
Todo
Eventually, category should be
Sets
by default.- __call__(x=0, *args, **kwds)[source]¶
This is the generic call method for all parents.
When called, it will find a map based on the Parent (or type) of x. If a coercion exists, it will always be chosen. This map will then be called (with the arguments and keywords if any).
By default this will dispatch as quickly as possible to
_element_constructor_()
though faster pathways are possible if so desired.
- _populate_coercion_lists_(coerce_list=[], action_list=[], convert_list=[], embedding=None, convert_method_name=None, element_constructor=None, init_no_parent=None, unpickling=False)[source]¶
This function allows one to specify coercions, actions, conversions and embeddings involving this parent.
IT SHOULD ONLY BE CALLED DURING THE __INIT__ method, often at the end.
INPUT:
coerce_list
– list of coercion Morphisms toself
and parents with canonical coercions toself
action_list
– list of actions on and byself
convert_list
– list of conversion Maps toself
and parents with conversions toself
embedding
– a single Morphism fromself
convert_method_name
– a name to look for that other elements can implement to create elements ofself
(e.g._integer_
)init_no_parent
– ifTrue
omit passingself
in as the first argument of element_constructor for conversion. This is useful if parents are unique, or element_constructor is a bound method (this latter case can be detected automatically).
- __mul__(x)[source]¶
This is a multiplication method that more or less directly calls another attribute
_mul_
(single underscore). This is because__mul__
cannot be implemented via inheritance from the parent methods of the category, but_mul_
can be inherited. This is, e.g., used when creating twosided ideals of matrix algebras. See Issue #7797.EXAMPLES:
sage: MS = MatrixSpace(QQ, 2, 2) # needs sage.modules
>>> from sage.all import * >>> MS = MatrixSpace(QQ, Integer(2), Integer(2)) # needs sage.modules
This matrix space is in fact an algebra, and in particular it is a ring, from the point of view of categories:
sage: MS.category() # needs sage.modules Category of infinite finite dimensional algebras with basis over (number fields and quotient fields and metric spaces) sage: MS in Rings() # needs sage.modules True
>>> from sage.all import * >>> MS.category() # needs sage.modules Category of infinite finite dimensional algebras with basis over (number fields and quotient fields and metric spaces) >>> MS in Rings() # needs sage.modules True
However, its class does not inherit from the base class
Ring
:sage: isinstance(MS, Ring) # needs sage.modules False
>>> from sage.all import * >>> isinstance(MS, Ring) # needs sage.modules False
Its
_mul_
method is inherited from the category, and can be used to create a left or right ideal:sage: # needs sage.modules sage: MS._mul_.__module__ 'sage.categories.rings' sage: MS * MS.1 # indirect doctest Left Ideal ( [0 1] [0 0] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field sage: MS * [MS.1, 2] Left Ideal ( [0 1] [0 0], [2 0] [0 2] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field sage: MS.1 * MS Right Ideal ( [0 1] [0 0] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field sage: [MS.1, 2] * MS Right Ideal ( [0 1] [0 0], [2 0] [0 2] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field
>>> from sage.all import * >>> # needs sage.modules >>> MS._mul_.__module__ 'sage.categories.rings' >>> MS * MS.gen(1) # indirect doctest Left Ideal ( [0 1] [0 0] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field >>> MS * [MS.gen(1), Integer(2)] Left Ideal ( [0 1] [0 0], <BLANKLINE> [2 0] [0 2] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field >>> MS.gen(1) * MS Right Ideal ( [0 1] [0 0] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field >>> [MS.gen(1), Integer(2)] * MS Right Ideal ( [0 1] [0 0], <BLANKLINE> [2 0] [0 2] ) of Full MatrixSpace of 2 by 2 dense matrices over Rational Field
- __contains__(x)[source]¶
True
if there is an element ofself
that is equal tox
under==
, or ifx
is already an element ofself
. Also,True
in other cases involving the Symbolic Ring, which is handled specially.For many structures we test this by using
__call__()
and then testing equality betweenx
and the result.The Symbolic Ring is treated differently because it is ultra-permissive about letting other rings coerce in, but ultra-strict about doing comparisons.
EXAMPLES:
sage: 2 in Integers(7) True sage: 2 in ZZ True sage: Integers(7)(3) in ZZ True sage: 3/1 in ZZ True sage: 5 in QQ True sage: I in RR # needs sage.rings.real_mpfr sage.symbolic False sage: RIF(1, 2) in RIF # needs sage.rings.real_interval_field True sage: # needs sage.symbolic sage: SR(2) in ZZ True sage: pi in RIF # there is no element of RIF equal to pi False sage: sqrt(2) in CC True sage: pi in RR True sage: pi in CC True sage: pi in RDF True sage: pi in CDF True
>>> from sage.all import * >>> Integer(2) in Integers(Integer(7)) True >>> Integer(2) in ZZ True >>> Integers(Integer(7))(Integer(3)) in ZZ True >>> Integer(3)/Integer(1) in ZZ True >>> Integer(5) in QQ True >>> I in RR # needs sage.rings.real_mpfr sage.symbolic False >>> RIF(Integer(1), Integer(2)) in RIF # needs sage.rings.real_interval_field True >>> # needs sage.symbolic >>> SR(Integer(2)) in ZZ True >>> pi in RIF # there is no element of RIF equal to pi False >>> sqrt(Integer(2)) in CC True >>> pi in RR True >>> pi in CC True >>> pi in RDF True >>> pi in CDF True
Note that we have
sage: 3/2 in RIF # needs sage.rings.real_interval_field True
>>> from sage.all import * >>> Integer(3)/Integer(2) in RIF # needs sage.rings.real_interval_field True
because
3/2
has an exact representation inRIF
(i.e. can be represented as an interval that contains exactly one value):sage: RIF(3/2).is_exact() # needs sage.rings.real_interval_field True
>>> from sage.all import * >>> RIF(Integer(3)/Integer(2)).is_exact() # needs sage.rings.real_interval_field True
On the other hand, we have
sage: 2/3 in RIF # needs sage.rings.real_interval_field False
>>> from sage.all import * >>> Integer(2)/Integer(3) in RIF # needs sage.rings.real_interval_field False
because
2/3
has no exact representation inRIF
. SinceRIF(2/3)
is a nontrivial interval, it cannot be equal to anything (not even itself):sage: RIF(2/3).is_exact() # needs sage.rings.real_interval_field False sage: RIF(2/3).endpoints() # needs sage.rings.real_interval_field (0.666666666666666, 0.666666666666667) sage: RIF(2/3) == RIF(2/3) # needs sage.rings.real_interval_field False
>>> from sage.all import * >>> RIF(Integer(2)/Integer(3)).is_exact() # needs sage.rings.real_interval_field False >>> RIF(Integer(2)/Integer(3)).endpoints() # needs sage.rings.real_interval_field (0.666666666666666, 0.666666666666667) >>> RIF(Integer(2)/Integer(3)) == RIF(Integer(2)/Integer(3)) # needs sage.rings.real_interval_field False
- _coerce_map_from_(S)[source]¶
Override this method to specify coercions beyond those specified in
coerce_list
.If no such coercion exists, return
None
orFalse
. Otherwise, it may return either an actual Map to use for the coercion, a callable (in which case it will be wrapped in a Map), orTrue
(in which case a generic map will be provided).
- _convert_map_from_(S)[source]¶
Override this method to provide additional conversions beyond those given in
convert_list
.This function is called after coercions are attempted. If there is a coercion morphism in the opposite direction, one should consider adding a section method to that.
This MUST return a Map from
S
toself
, orNone
. IfNone
is returned then a generic map will be provided.
- _get_action_(S, op, self_on_left)[source]¶
Override this method to provide an action of
self
onS
orS
onself
beyond what was specified inaction_list
.This must return an action which accepts an element of
self
and an element ofS
(in the order specified byself_on_left
).
- _an_element_()[source]¶
Return an element of
self
.Want it in sufficient generality that poorly-written functions will not work when they are not supposed to. This is cached so does not have to be super fast.
EXAMPLES:
sage: QQ._an_element_() 1/2 sage: ZZ['x,y,z']._an_element_() x
>>> from sage.all import * >>> QQ._an_element_() 1/2 >>> ZZ['x,y,z']._an_element_() x
- _repr_option(key)[source]¶
Metadata about the
_repr_()
output.INPUT:
key
– string; a key for different metadata informations that can be inquired about
Valid
key
arguments are:'ascii_art'
: The_repr_()
output is multi-line ascii art and each line must be printed starting at the same column, or the meaning is lost.'element_ascii_art'
: same but for the output of the elements. Used insage.repl.display.formatter
.'element_is_atomic'
: the elements print atomically, that is, parenthesis are not required when printing out any of \(x - y\), \(x + y\), \(x^y\) and \(x/y\).
OUTPUT: boolean
EXAMPLES:
sage: ZZ._repr_option('ascii_art') False sage: MatrixSpace(ZZ, 2)._repr_option('element_ascii_art') # needs sage.modules True
>>> from sage.all import * >>> ZZ._repr_option('ascii_art') False >>> MatrixSpace(ZZ, Integer(2))._repr_option('element_ascii_art') # needs sage.modules True
- _init_category_(category)[source]¶
Initialize the category framework.
Most parents initialize their category upon construction, and this is the recommended behavior. For example, this happens when the constructor calls
Parent.__init__()
directly or indirectly. However, some parents defer this for performance reasons. For example,sage.matrix.matrix_space.MatrixSpace
does not.EXAMPLES:
sage: P = Parent() sage: P.category() Category of sets sage: class MyParent(Parent): ....: def __init__(self): ....: self._init_category_(Groups()) sage: MyParent().category() Category of groups
>>> from sage.all import * >>> P = Parent() >>> P.category() Category of sets >>> class MyParent(Parent): ... def __init__(self): ... self._init_category_(Groups()) >>> MyParent().category() Category of groups
- _is_coercion_cached(domain)[source]¶
Test whether the coercion from
domain
is already cached.EXAMPLES:
sage: R.<XX> = QQ sage: R._remove_from_coerce_cache(QQ) sage: R._is_coercion_cached(QQ) False sage: _ = R.coerce_map_from(QQ) sage: R._is_coercion_cached(QQ) True
>>> from sage.all import * >>> R = QQ; (XX,) = R._first_ngens(1) >>> R._remove_from_coerce_cache(QQ) >>> R._is_coercion_cached(QQ) False >>> _ = R.coerce_map_from(QQ) >>> R._is_coercion_cached(QQ) True
- _is_conversion_cached(domain)[source]¶
Test whether the conversion from
domain
is already set.EXAMPLES:
sage: P = Parent() sage: P._is_conversion_cached(P) False sage: P.convert_map_from(P) Identity endomorphism of <sage.structure.parent.Parent object at ...> sage: P._is_conversion_cached(P) True
>>> from sage.all import * >>> P = Parent() >>> P._is_conversion_cached(P) False >>> P.convert_map_from(P) Identity endomorphism of <sage.structure.parent.Parent object at ...> >>> P._is_conversion_cached(P) True
- Hom(codomain, category=None)[source]¶
Return the homspace
Hom(self, codomain, category)
.INPUT:
codomain
– a parentcategory
– a category orNone
(default:None
) IfNone
, the meet of the category ofself
andcodomain
is used.
OUTPUT:
The homspace of all homomorphisms from
self
tocodomain
in the categorycategory
.See also
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
>>> from sage.all import * >>> R = PolynomialRing(QQ, Integer(2), names=('x', 'y',)); (x, y,) = R._first_ngens(2) >>> 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
>>> from sage.all import * >>> n = Integer(5); Hom(n,Integer(7)) Set of Morphisms from 5 to 7 in Category of elements of Integer Ring >>> z=(Integer(2)/Integer(3)); Hom(z,Integer(8)/Integer(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
>>> from sage.all import * >>> 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). See :func:`~sage.categories.homset.Hom()
for details.
- an_element()[source]¶
Return 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 exceptionEmptySetError
.This calls
_an_element_()
(which see), and caches the result. Parent are thus encouraged to override_an_element_()
.EXAMPLES:
sage: CDF.an_element() # needs sage.rings.complex_double 1.0*I sage: ZZ[['t']].an_element() t
>>> from sage.all import * >>> CDF.an_element() # needs sage.rings.complex_double 1.0*I >>> 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
>>> from sage.all import * >>> Set([]).an_element() Traceback (most recent call last): ... EmptySetError
- category()[source]¶
EXAMPLES:
sage: P = Parent() sage: P.category() Category of sets sage: class MyParent(Parent): ....: def __init__(self): pass sage: MyParent().category() Category of sets
>>> from sage.all import * >>> P = Parent() >>> P.category() Category of sets >>> class MyParent(Parent): ... def __init__(self): pass >>> MyParent().category() Category of sets
- coerce(x)[source]¶
Return x as an element of
self
, if and only if there is a canonical coercion from the parent of x toself
.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
>>> from sage.all import * >>> QQ.coerce(ZZ(Integer(2))) 2 >>> ZZ.coerce(QQ(Integer(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 # needs sage.modules sage: V.coerce(0) # needs sage.modules (0, 0, 0, 0, 0, 0, 0)
>>> from sage.all import * >>> V = GF(Integer(7))**Integer(7) # needs sage.modules >>> V.coerce(Integer(0)) # needs sage.modules (0, 0, 0, 0, 0, 0, 0)
- coerce_embedding()[source]¶
Return 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 ofself
and usually denotes a special relationship (e.g. sub-objects, choice of completion, etc.)EXAMPLES:
sage: # needs sage.rings.number_field sage: x = polygen(ZZ, 'x') 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 with a = -1.465571231876768? 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 with a = 0.2327856159383841? + 0.7925519925154479?*I To: Complex Lazy Field Defn: a -> 0.2327856159383841? + 0.7925519925154479?*I
>>> from sage.all import * >>> # needs sage.rings.number_field >>> x = polygen(ZZ, 'x') >>> K = NumberField(x**Integer(3) + x**Integer(2) + Integer(1), embedding=Integer(1), names=('a',)); (a,) = K._first_ngens(1) >>> K.coerce_embedding() Generic morphism: From: Number Field in a with defining polynomial x^3 + x^2 + 1 with a = -1.465571231876768? To: Real Lazy Field Defn: a -> -1.465571231876768? >>> K = NumberField(x**Integer(3) + x**Integer(2) + Integer(1), embedding=CC.gen(), names=('a',)); (a,) = K._first_ngens(1) >>> K.coerce_embedding() Generic morphism: From: Number Field in a with defining polynomial x^3 + x^2 + 1 with a = 0.2327856159383841? + 0.7925519925154479?*I To: Complex Lazy Field Defn: a -> 0.2327856159383841? + 0.7925519925154479?*I
- coerce_map_from(S)[source]¶
Return a
Map
object to coerce fromS
toself
if one exists, orNone
if no such coercion exists.EXAMPLES:
By Issue #12313, a special kind of weak key dictionary is used to store coercion and conversion maps, namely
MonoDict
. In that way, a memory leak was fixed that would occur in the following test:sage: import gc sage: _ = gc.collect() sage: K = GF(1<<55,'t') # needs sage.rings.finite_rings sage: for i in range(50): # needs sage.rings.finite_rings sage.schemes ....: a = K.random_element() ....: E = EllipticCurve(j=a) ....: b = K.has_coerce_map_from(E) sage: _ = gc.collect() sage: len([x for x in gc.get_objects() if isinstance(x, type(E))]) # needs sage.rings.finite_rings sage.schemes 1
>>> from sage.all import * >>> import gc >>> _ = gc.collect() >>> K = GF(Integer(1)<<Integer(55),'t') # needs sage.rings.finite_rings >>> for i in range(Integer(50)): # needs sage.rings.finite_rings sage.schemes ... a = K.random_element() ... E = EllipticCurve(j=a) ... b = K.has_coerce_map_from(E) >>> _ = gc.collect() >>> len([x for x in gc.get_objects() if isinstance(x, type(E))]) # needs sage.rings.finite_rings sage.schemes 1
- convert_map_from(S)[source]¶
This function returns a
Map
from \(S\) toself
, which may or may not succeed on all inputs. If a coercion map from S toself
exists, then the it will be returned. If a coercion fromself
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 Generic map: From: Rational Field To: Integer Ring sage: m(-35/7) -5 sage: parent(m(-35/7)) Integer Ring
>>> from sage.all import * >>> m = ZZ.convert_map_from(QQ) >>> m Generic map: From: Rational Field To: Integer Ring >>> m(-Integer(35)/Integer(7)) -5 >>> parent(m(-Integer(35)/Integer(7))) Integer Ring
- element_class()[source]¶
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=None, self_on_left=True, self_el=None, S_el=None)[source]¶
Return an action of
self
onS
orS
onself
.To provide additional actions, override
_get_action_()
.Warning
This is not the method that you typically want to call. Instead, call
coercion_model.get_action(...)
which caches results (thisParent.get_action
method does not).
- has_coerce_map_from(S)[source]¶
Return
True
if there is a natural map fromS
toself
. Otherwise, returnFalse
.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
>>> from sage.all import * >>> RDF.has_coerce_map_from(QQ) True >>> RDF.has_coerce_map_from(QQ['x']) False >>> RDF['x'].has_coerce_map_from(QQ['x']) True >>> RDF['x,y'].has_coerce_map_from(QQ['x']) True
- hom(im_gens, codomain=None, check=None, base_map=None, category=None, **kwds)[source]¶
Return the unique homomorphism from
self
tocodomain
that sendsself.gens()
to the entries ofim_gens
.This 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 homomorphismcodomain
– the codomain of the homomorphismbase_map
– a map from the base ring to the codomain; if not given, coercion is usedcheck
– 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.EXAMPLES:
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): ... ValueError: relations do not all (canonically) map to 0 under map determined by images of generators sage: # needs sage.rings.finite_rings 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
>>> from sage.all import * >>> R = PolynomialRing(ZZ, names=('x',)); (x,) = R._first_ngens(1) >>> f = R.hom([Integer(5)], QQ) >>> f(x**Integer(2) - Integer(19)) 6 >>> R = PolynomialRing(QQ, names=('x',)); (x,) = R._first_ngens(1) >>> f = R.hom([Integer(5)], GF(Integer(7))) Traceback (most recent call last): ... ValueError: relations do not all (canonically) map to 0 under map determined by images of generators >>> # needs sage.rings.finite_rings >>> R = PolynomialRing(GF(Integer(7)), names=('x',)); (x,) = R._first_ngens(1) >>> f = R.hom([Integer(3)], GF(Integer(49),'a')) >>> 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 >>> f(x + Integer(6)) 2 >>> f(x**Integer(2) + Integer(1)) 3
Natural morphism:
sage: f = ZZ.hom(GF(5)) sage: f(7) 2 sage: f Natural morphism: From: Integer Ring To: Finite Field of size 5
>>> from sage.all import * >>> f = ZZ.hom(GF(Integer(5))) >>> f(Integer(7)) 2 >>> f Natural morphism: From: Integer Ring To: Finite Field of size 5
There might not be a natural morphism, in which case a
TypeError
is raised:sage: QQ.hom(ZZ) Traceback (most recent call last): ... TypeError: natural coercion morphism from Rational Field to Integer Ring not defined
>>> from sage.all import * >>> QQ.hom(ZZ) Traceback (most recent call last): ... TypeError: natural coercion morphism from Rational Field to Integer Ring not defined
- is_exact()[source]¶
Test whether the ring is exact.
Note
This defaults to true, so even if it does return
True
you have no guarantee (unless the ring has properly overloaded this).OUTPUT:
Return
True
if elements of this ring are represented exactly, i.e., there is no precision loss when doing arithmetic.EXAMPLES:
sage: QQ.is_exact() True sage: ZZ.is_exact() True sage: Qp(7).is_exact() # needs sage.rings.padics False sage: Zp(7, type='capped-abs').is_exact() # needs sage.rings.padics False
>>> from sage.all import * >>> QQ.is_exact() True >>> ZZ.is_exact() True >>> Qp(Integer(7)).is_exact() # needs sage.rings.padics False >>> Zp(Integer(7), type='capped-abs').is_exact() # needs sage.rings.padics False
- register_action(action)[source]¶
Update the coercion model to use
action
to act onself
.action
should be of typesage.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 _act_(self, g, a): ....: D = {} ....: for k, v in a.monomial_coefficients().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: # needs sage.groups 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: # needs sage.groups 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
>>> from sage.all import * >>> import sage.categories.action >>> import operator >>> 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 _act_(self, g, a): ... D = {} ... for k, v in a.monomial_coefficients().items(): ... nk = [Integer(0)]*len(k) ... for i in range(len(k)): ... nk[g(i+Integer(1))-Integer(1)] = k[i] ... D[tuple(nk)] = v ... return a.parent()(D) >>> # needs sage.groups >>> R = QQ['x, y, z']; (x, y, z,) = R._first_ngens(3) >>> G = SymmetricGroup(Integer(3)) >>> act = SymmetricGroupAction(G, R) >>> t = x + Integer(2)*y + Integer(3)*z >>> # needs sage.groups >>> act(G((Integer(1), Integer(2))), t) 2*x + y + 3*z >>> act(G((Integer(2), Integer(3))), t) x + 3*y + 2*z >>> act(G((Integer(1), Integer(2), Integer(3))), t) 3*x + y + 2*z
This should fail, since we have not registered the left action:
sage: G((1,2)) * t # needs sage.groups Traceback (most recent call last): ... TypeError: ...
>>> from sage.all import * >>> G((Integer(1),Integer(2))) * t # needs sage.groups Traceback (most recent call last): ... TypeError: ...
Now let’s make it work:
sage: # needs sage.groups sage: R._unset_coercions_used() sage: R.register_action(act) sage: G((1, 2)) * t 2*x + y + 3*z
>>> from sage.all import * >>> # needs sage.groups >>> R._unset_coercions_used() >>> R.register_action(act) >>> G((Integer(1), Integer(2))) * t 2*x + y + 3*z
- register_coercion(mor)[source]¶
Update the coercion model to use \(mor : P \to \text{self}\) to coerce from a parent
P
intoself
.For safety, an error is raised if another coercion has already been registered or discovered between
P
andself
.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
>>> from sage.all import * >>> K = ZZ['a']; (a,) = K._first_ngens(1) >>> L = ZZ['b']; (b,) = L._first_ngens(1) >>> L_into_K = L.hom([-a]) # non-trivial automorphism >>> K.register_coercion(L_into_K) >>> K(Integer(0)) + b -a >>> a + b 0 >>> K(b) # check that convert calls coerce first; normally this is just a -a >>> L(Integer(0)) + a in K # this goes through the coercion mechanism of K True >>> L(a) in L # this still goes through the convert mechanism of L True >>> 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)[source]¶
Update the coercion model to use \(\text{mor} : P \to \text{self}\) to convert from
P
intoself
.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: ...
>>> from sage.all import * >>> K = ZZ['a']; (a,) = K._first_ngens(1) >>> M = ZZ['c']; (c,) = M._first_ngens(1) >>> M_into_K = M.hom([a]) # trivial automorphism >>> K._unset_coercions_used() >>> K.register_conversion(M_into_K) >>> K(c) a >>> K(Integer(0)) + c Traceback (most recent call last): ... TypeError: ...
- register_embedding(embedding)[source]¶
Add embedding to coercion model.
This method updates the coercion model to use \(\text{embedding} : \text{self} \to P\) to embed
self
into the parentP
.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) # needs sage.groups sage: G = SL(3, QQ) # needs sage.groups sage: p = S3[2]; p.matrix() # needs sage.groups [0 0 1] [1 0 0] [0 1 0]
>>> from sage.all import * >>> S3 = AlternatingGroup(Integer(3)) # needs sage.groups >>> G = SL(Integer(3), QQ) # needs sage.groups >>> p = S3[Integer(2)]; p.matrix() # needs sage.groups [0 0 1] [1 0 0] [0 1 0]
In general one cannot mix matrices and permutations:
sage: # needs sage.groups sage: G(p) Traceback (most recent call last): ... TypeError: unable to convert (1,3,2) to a rational 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)
>>> from sage.all import * >>> # needs sage.groups >>> G(p) Traceback (most recent call last): ... TypeError: unable to convert (1,3,2) to a rational >>> phi = S3.hom(lambda p: G(p.matrix()), codomain=G) >>> phi(p) [0 0 1] [1 0 0] [0 1 0] >>> S3._unset_coercions_used() >>> S3.register_embedding(phi)
By Issue #14711, coerce maps should be copied when using outside of the coercion system:
sage: phi = copy(S3.coerce_embedding()); phi # needs sage.groups Generic morphism: From: Alternating group of order 3!/2 as a permutation group To: Special Linear Group of degree 3 over Rational Field sage: phi(p) # needs sage.groups [0 0 1] [1 0 0] [0 1 0]
>>> from sage.all import * >>> phi = copy(S3.coerce_embedding()); phi # needs sage.groups Generic morphism: From: Alternating group of order 3!/2 as a permutation group To: Special Linear Group of degree 3 over Rational Field >>> phi(p) # needs sage.groups [0 0 1] [1 0 0] [0 1 0]
This does not work since matrix groups are still old-style parents (see Issue #14014):
sage: G(p) # not implemented # needs sage.groups
>>> from sage.all import * >>> G(p) # not implemented # needs sage.groups
Though one can have a permutation act on the rows of a matrix:
sage: G(1) * p # needs sage.groups [0 0 1] [1 0 0] [0 1 0]
>>> from sage.all import * >>> G(Integer(1)) * p # needs sage.groups [0 0 1] [1 0 0] [0 1 0]
Some more advanced examples:
sage: # needs sage.rings.number_field 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()]) sage: K._unset_coercions_used() sage: K.register_embedding(K_into_MS) sage: # needs sage.rings.number_field 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()]) sage: L._unset_coercions_used() sage: L.register_embedding(L_into_MS) sage: K.coerce_embedding()(a) # needs sage.rings.number_field [ 0 1] [-462 0] sage: L.coerce_embedding()(b) # needs sage.rings.number_field [ 0 1] [-272118 0] sage: a.matrix() * b.matrix() # needs sage.rings.number_field [-272118 0] [ 0 -462] sage: a.matrix() * b.matrix() # needs sage.rings.number_field [-272118 0] [ 0 -462]
>>> from sage.all import * >>> # needs sage.rings.number_field >>> x = QQ['x'].gen(0) >>> t = abs(ZZ.random_element(Integer(10)**Integer(6))) >>> K = NumberField(x**Integer(2) + Integer(2)*Integer(3)*Integer(7)*Integer(11), "a"+str(t)) >>> a = K.gen() >>> K_into_MS = K.hom([a.matrix()]) >>> K._unset_coercions_used() >>> K.register_embedding(K_into_MS) >>> # needs sage.rings.number_field >>> L = NumberField(x**Integer(2) + Integer(2)*Integer(3)*Integer(7)*Integer(11)*Integer(19)*Integer(31), ... "b" + str(abs(ZZ.random_element(Integer(10)**Integer(6))))) >>> b = L.gen() >>> L_into_MS = L.hom([b.matrix()]) >>> L._unset_coercions_used() >>> L.register_embedding(L_into_MS) >>> K.coerce_embedding()(a) # needs sage.rings.number_field [ 0 1] [-462 0] >>> L.coerce_embedding()(b) # needs sage.rings.number_field [ 0 1] [-272118 0] >>> a.matrix() * b.matrix() # needs sage.rings.number_field [-272118 0] [ 0 -462] >>> a.matrix() * b.matrix() # needs sage.rings.number_field [-272118 0] [ 0 -462]
- sage.structure.parent.is_Parent(x)[source]¶
Return
True
if x is a parent object, i.e., derives from sage.structure.parent.Parent andFalse
otherwise.EXAMPLES:
sage: from sage.structure.parent import is_Parent sage: is_Parent(2/3) doctest:warning... DeprecationWarning: the function is_Parent is deprecated; use 'isinstance(..., Parent)' instead See https://github.com/sagemath/sage/issues/37922 for details. False sage: is_Parent(ZZ) True sage: is_Parent(Primes()) True
>>> from sage.all import * >>> from sage.structure.parent import is_Parent >>> is_Parent(Integer(2)/Integer(3)) doctest:warning... DeprecationWarning: the function is_Parent is deprecated; use 'isinstance(..., Parent)' instead See https://github.com/sagemath/sage/issues/37922 for details. False >>> is_Parent(ZZ) True >>> is_Parent(Primes()) True