diff --git a/src/sage/modular/abvar/homspace.py b/src/sage/modular/abvar/homspace.py index 2333bbacd00..4412716e0ed 100644 --- a/src/sage/modular/abvar/homspace.py +++ b/src/sage/modular/abvar/homspace.py @@ -275,23 +275,19 @@ def _matrix_space(self): """ return MatrixSpace(ZZ, 2*self.domain().dimension(), 2*self.codomain().dimension()) - def _element_constructor_from_element_class(self, *args, **keywords): - """ - Used in the coercion framework. Unfortunately, the default method - would get the order of parent and data different from what is expected - in ``MatrixMorphism.__init__``. - - EXAMPLES:: + def _coerce_map_from_(self, other): + r""" + TESTS:: - sage: H = Hom(J0(11), J0(22)) - sage: phi = H(matrix(ZZ,2,4,[5..12])); phi # indirect doctest - Abelian variety morphism: - From: Abelian variety J0(11) of dimension 1 - To: Abelian variety J0(22) of dimension 2 + sage: t = J0(33).hecke_operator(2) + sage: t.parent().has_coerce_map_from(ZZ) + True """ - return self.element_class(self, *args, **keywords) + if self.matrix_space().has_coerce_map_from(other): + return True + return super()._coerce_map_from_(other) - def __call__(self, M, **kwds): + def _element_constructor_(self, M, **kwds): r""" Create a homomorphism in this space from M. M can be any of the following: diff --git a/src/sage/modules/matrix_morphism.py b/src/sage/modules/matrix_morphism.py index 5736d86932d..f586fafc4a8 100644 --- a/src/sage/modules/matrix_morphism.py +++ b/src/sage/modules/matrix_morphism.py @@ -95,11 +95,11 @@ class MatrixMorphism_abstract(sage.categories.morphism.Morphism): def __init__(self, parent, side='left'): """ - INPUT: + Initialize ``self``. - - ``parent`` -- a homspace + INPUT: - - ``A`` -- matrix + - ``parent`` -- a homspace EXAMPLES:: @@ -136,8 +136,8 @@ def _richcmp_(self, other, op): True sage: psi == 5 * id True - sage: psi == 5 # no coercion - False + sage: psi == 5 + True sage: id == End(V).identity() True """ @@ -159,15 +159,15 @@ def _call_(self, x): sage: V = QQ^3; W = QQ^2 sage: H = Hom(V, W); H - Set of Morphisms (Linear Transformations) from - Vector space of dimension 3 over Rational Field to - Vector space of dimension 2 over Rational Field + Set of Morphisms (Linear Transformations) + from Vector space of dimension 3 over Rational Field + to Vector space of dimension 2 over Rational Field sage: phi = H(matrix(QQ, 3, 2, range(6))); phi Vector space morphism represented by the matrix: [0 1] [2 3] [4 5] - Domain: Vector space of dimension 3 over Rational Field + Domain: Vector space of dimension 3 over Rational Field Codomain: Vector space of dimension 2 over Rational Field sage: phi(V.0) (0, 1) @@ -189,11 +189,11 @@ def _call_(self, x): We compute the image of some elements:: - sage: phi(V.0) #indirect doctest + sage: phi(V.0) # indirect doctest (1, 2, 3) sage: phi(V.1) (4, 5, 6) - sage: phi(V.0 - 1/4*V.1) + sage: phi(V.0 - 1/4*V.1) (0, 3/4, 3/2) We restrict ``phi`` to ``W`` and compute the image of an element:: @@ -325,8 +325,10 @@ def side_switch(self): Free module morphism defined as left-multiplication by the matrix [1 0] [1 1] - Domain: Ambient free module of rank 2 over the principal ideal domain Integer Ring - Codomain: Ambient free module of rank 2 over the principal ideal domain Integer Ring + Domain: Ambient free module of rank 2 over the principal ideal domain + Integer Ring + Codomain: Ambient free module of rank 2 over the principal ideal domain + Integer Ring sage: h2.side() 'right' sage: h2.side_switch().matrix() @@ -361,14 +363,14 @@ def inverse(self): Vector space morphism represented by the matrix: [-1 3] [ 1 -2] - Domain: Vector space of degree 3 and dimension 2 over Rational Field - Basis matrix: - [1 0 0] - [0 0 1] + Domain: Vector space of degree 3 and dimension 2 over Rational Field + Basis matrix: + [1 0 0] + [0 0 1] Codomain: Vector space of degree 4 and dimension 2 over Rational Field - Basis matrix: - [1 0 0 0] - [0 0 0 1] + Basis matrix: + [1 0 0 0] + [0 0 0 1] sage: inv(y) == x True @@ -390,7 +392,7 @@ def inverse(self): [ -9 4 -3 2] [-20 8 -7 4] [ -6 2 -2 1] - Domain: Ambient free module of rank 4 over the principal ideal domain ... + Domain: Ambient free module of rank 4 over the principal ideal domain ... Codomain: Ambient free module of rank 4 over the principal ideal domain ... sage: rho(y) == x True @@ -456,9 +458,9 @@ def inverse(self): sage: m = matrix(QQ, [[2, 1], [3, 4]]) sage: phi = V.hom(m, U) sage: inv = phi.inverse() - sage: (inv*phi).is_identity() + sage: (inv * phi).is_identity() True - sage: (phi*inv).is_identity() + sage: (phi * inv).is_identity() True """ return ~self @@ -467,48 +469,88 @@ def __rmul__(self, left): """ EXAMPLES:: - sage: V = ZZ^2; phi = V.hom([V.0+V.1, 2*V.1]) - sage: 2*phi + sage: V = ZZ^2; phi = V.hom([V.0 + V.1, 2*V.1]) + sage: 2 * phi Free module morphism defined by the matrix [2 2] [0 4]... - sage: phi*2 + sage: phi * 2 Free module morphism defined by the matrix [2 2] [0 4]... + + TESTS: + + Check that :issue:`28272` is fixed:: + + sage: V = VectorSpace(QQ,2) + sage: f = V.hom(identity_matrix(QQ,2)) + sage: 1/2 * f + Vector space morphism represented by the matrix: + [1/2 0] + [ 0 1/2]... + sage: f * 1/2 + Vector space morphism represented by the matrix: + [1/2 0] + [ 0 1/2]... + + Check that :issue:`16830` is fixed:: + + sage: K = GF(7); K + Finite Field of size 7 + sage: phi = End(K^2)([[1,1], [1,1]]); phi + Vector space morphism represented by the matrix: + [1 1] + [1 1] + Domain: Vector space of dimension 2 over Finite Field of size 7 + Codomain: Vector space of dimension 2 over Finite Field of size 7 + sage: 2 * phi + Vector space morphism represented by the matrix: + [2 2] + [2 2] + Domain: Vector space of dimension 2 over Finite Field of size 7 + Codomain: Vector space of dimension 2 over Finite Field of size 7 + sage: K(2) * phi + Vector space morphism represented by the matrix: + [2 2] + [2 2] + Domain: Vector space of dimension 2 over Finite Field of size 7 + Codomain: Vector space of dimension 2 over Finite Field of size 7 """ R = self.base_ring() return self.parent()(R(left) * self.matrix(), side=self.side()) + _rmul_ = _lmul_ = __rmul__ + def __mul__(self, right): r""" Composition of morphisms, denoted by \*. EXAMPLES:: - sage: V = ZZ^2; phi = V.hom([V.0+V.1, 2*V.1]) - sage: phi*phi + sage: V = ZZ^2; phi = V.hom([V.0 + V.1, 2*V.1]) + sage: phi * phi Free module morphism defined by the matrix [1 3] [0 4] - Domain: Ambient free module of rank 2 over the principal ideal domain ... + Domain: Ambient free module of rank 2 over the principal ideal domain ... Codomain: Ambient free module of rank 2 over the principal ideal domain ... sage: V = QQ^3 sage: E = V.endomorphism_ring() - sage: phi = E(Matrix(QQ,3,range(9))) ; phi + sage: phi = E(Matrix(QQ, 3, range(9))); phi Vector space morphism represented by the matrix: [0 1 2] [3 4 5] [6 7 8] - Domain: Vector space of dimension 3 over Rational Field + Domain: Vector space of dimension 3 over Rational Field Codomain: Vector space of dimension 3 over Rational Field - sage: phi*phi + sage: phi * phi Vector space morphism represented by the matrix: [ 15 18 21] [ 42 54 66] [ 69 90 111] - Domain: Vector space of dimension 3 over Rational Field + Domain: Vector space of dimension 3 over Rational Field Codomain: Vector space of dimension 3 over Rational Field sage: phi.matrix()**2 [ 15 18 21] @@ -519,25 +561,26 @@ def __mul__(self, right): sage: W = QQ**4 sage: E_VW = V.Hom(W) - sage: psi = E_VW(Matrix(QQ,3,4,range(12))) ; psi + sage: psi = E_VW(Matrix(QQ, 3, 4, range(12))); psi Vector space morphism represented by the matrix: [ 0 1 2 3] [ 4 5 6 7] [ 8 9 10 11] - Domain: Vector space of dimension 3 over Rational Field + Domain: Vector space of dimension 3 over Rational Field Codomain: Vector space of dimension 4 over Rational Field - sage: psi*phi + sage: psi * phi Vector space morphism represented by the matrix: [ 20 23 26 29] [ 56 68 80 92] [ 92 113 134 155] - Domain: Vector space of dimension 3 over Rational Field + Domain: Vector space of dimension 3 over Rational Field Codomain: Vector space of dimension 4 over Rational Field - sage: phi*psi + sage: phi * psi Traceback (most recent call last): ... - TypeError: Incompatible composition of morphisms: domain of left morphism must be codomain of right. - sage: phi.matrix()*psi.matrix() + TypeError: Incompatible composition of morphisms: + domain of left morphism must be codomain of right. + sage: phi.matrix() * psi.matrix() [ 20 23 26 29] [ 56 68 80 92] [ 92 113 134 155] @@ -548,22 +591,22 @@ def __mul__(self, right): sage: x = polygen(ZZ, 'x') sage: K. = NumberField(x^2 + 23) sage: V, VtoK, KtoV = K.vector_space() - sage: f = V.hom([V.0 - V.1, V.0 + V.1])*KtoV; f + sage: f = V.hom([V.0 - V.1, V.0 + V.1]) * KtoV; f Composite map: - From: Number Field in a with defining polynomial x^2 + 23 - To: Vector space of dimension 2 over Rational Field - Defn: Isomorphism map: - From: Number Field in a with defining polynomial x^2 + 23 - To: Vector space of dimension 2 over Rational Field + From: Number Field in a with defining polynomial x^2 + 23 + To: Vector space of dimension 2 over Rational Field + Defn: Isomorphism map: + From: Number Field in a with defining polynomial x^2 + 23 + To: Vector space of dimension 2 over Rational Field then - Vector space morphism represented by the matrix: - [ 1 -1] - [ 1 1] - Domain: Vector space of dimension 2 over Rational Field - Codomain: Vector space of dimension 2 over Rational Field + Vector space morphism represented by the matrix: + [ 1 -1] + [ 1 1] + Domain: Vector space of dimension 2 over Rational Field + Codomain: Vector space of dimension 2 over Rational Field sage: f(a) (1, 1) - sage: V.hom([V.0 - V.1, V.0 + V.1], side='right')*KtoV + sage: V.hom([V.0 - V.1, V.0 + V.1], side="right") * KtoV Composite map: From: Number Field in a with defining polynomial x^2 + 23 To: Vector space of dimension 2 over Rational Field @@ -571,17 +614,17 @@ def __mul__(self, right): From: Number Field in a with defining polynomial x^2 + 23 To: Vector space of dimension 2 over Rational Field then - Vector space morphism represented as left-multiplication by the matrix: - [ 1 1] - [-1 1] - Domain: Vector space of dimension 2 over Rational Field - Codomain: Vector space of dimension 2 over Rational Field + Vector space morphism represented as left-multiplication + by the matrix: + [ 1 1] + [-1 1] + Domain: Vector space of dimension 2 over Rational Field + Codomain: Vector space of dimension 2 over Rational Field - - We can test interraction between morphisms with different ``side``:: + We can test interaction between morphisms with different ``side``:: sage: V = ZZ^2 - sage: m = matrix(2, [1,1,0,1]) + sage: m = matrix(2, [1,1, 0,1]) sage: hl = V.hom(m) sage: hr = V.hom(m, side='right') sage: hl * hl @@ -650,13 +693,13 @@ def __mul__(self, right): else: return H(right.matrix() * self.matrix().transpose(), side='left') - def __add__(self, right): + def _add_(self, other): """ Sum of morphisms, denoted by +. EXAMPLES:: - sage: phi = (ZZ**2).endomorphism_ring()(Matrix(ZZ,2,[2..5])) ; phi + sage: phi = (ZZ**2).endomorphism_ring()(Matrix(ZZ, 2, [2..5])); phi Free module morphism defined by the matrix [2 3] [4 5] @@ -674,7 +717,7 @@ def __add__(self, right): [ 8 10] Domain: Ambient free module of rank 2 over the principal ideal domain ... Codomain: Ambient free module of rank 2 over the principal ideal domain ... - sage: psi = (ZZ**3).endomorphism_ring()(Matrix(ZZ,3,[22..30])) ; psi + sage: psi = (ZZ**3).endomorphism_ring()(Matrix(ZZ, 3, [22..30])); psi Free module morphism defined by the matrix [22 23 24] [25 26 27] @@ -684,13 +727,12 @@ def __add__(self, right): sage: phi + psi Traceback (most recent call last): ... - ValueError: inconsistent number of rows: should be 2 but got 3 + TypeError: unsupported operand parent(s) for +: ... :: - sage: V = ZZ^2 - sage: m = matrix(2, [1,1,0,1]) + sage: m = matrix(2, [1,1, 0,1]) sage: hl = V.hom(m) sage: hr = V.hom(m, side='right') sage: hl + hl @@ -715,28 +757,26 @@ def __add__(self, right): If the two morphisms do not share the same ``side`` attribute, then the resulting morphism will be defined with the default value. """ - # TODO: move over to any coercion model! - if not isinstance(right, MatrixMorphism): - R = self.base_ring() - return self.parent()(self.matrix() + R(right)) - if not right.parent() == self.parent(): - right = self.parent()(right, side=right.side()) + if not isinstance(other, MatrixMorphism): + # The coercion only guarantees that they have the same parent, + # but homspaces can use multiple types of element classes. + return NotImplemented if self.side() == "left": - if right.side() == "left": - return self.parent()(self.matrix() + right.matrix(), side=self.side()) - elif right.side() == "right": - return self.parent()(self.matrix() + right.matrix().transpose(), side='left') + if other.side() == "left": + return self.parent()(self.matrix() + other.matrix(), side=self.side()) + elif other.side() == "right": + return self.parent()(self.matrix() + other.matrix().transpose(), side="left") if self.side() == "right": - if right.side() == "right": - return self.parent()(self.matrix() + right.matrix(), side=self.side()) - elif right.side() == "left": - return self.parent()(self.matrix().transpose() + right.matrix(), side='left') + if other.side() == "right": + return self.parent()(self.matrix() + other.matrix(), side=self.side()) + elif other.side() == "left": + return self.parent()(self.matrix().transpose() + other.matrix(), side="left") def __neg__(self): """ EXAMPLES:: - sage: V = ZZ^2; phi = V.hom([V.0+V.1, 2*V.1]) + sage: V = ZZ^2; phi = V.hom([V.0 + V.1, 2*V.1]) sage: -phi Free module morphism defined by the matrix [-1 -1] @@ -748,11 +788,11 @@ def __neg__(self): """ return self.parent()(-self.matrix(), side=self.side()) - def __sub__(self, other): + def _sub_(self, other): """ EXAMPLES:: - sage: V = ZZ^2; phi = V.hom([V.0+V.1, 2*V.1]) + sage: V = ZZ^2; phi = V.hom([V.0 + V.1, 2*V.1]) sage: phi - phi Free module morphism defined by the matrix [0 0] @@ -761,7 +801,7 @@ def __sub__(self, other): :: sage: V = ZZ^2 - sage: m = matrix(2, [1,1,0,1]) + sage: m = matrix(2, [1,1, 0,1]) sage: hl = V.hom(m) sage: hr = V.hom(m, side='right') sage: hl - hr @@ -776,7 +816,7 @@ def __sub__(self, other): Free module morphism defined as left-multiplication by the matrix [0 0] [0 0]... - sage: hr-hl + sage: hr - hl Free module morphism defined by the matrix [ 0 -1] [ 1 0]... @@ -786,12 +826,10 @@ def __sub__(self, other): If the two morphisms do not share the same ``side`` attribute, then the resulting morphism will be defined with the default value. """ - # TODO: move over to any coercion model! if not isinstance(other, MatrixMorphism): - R = self.base_ring() - return self.parent()(self.matrix() - R(other), side=self.side()) - if not other.parent() == self.parent(): - other = self.parent()(other, side=other.side()) + # The coercion only guarantees that they have the same parent, + # but homspaces can use multiple types of element classes. + return NotImplemented if self.side() == "left": if other.side() == "left": return self.parent()(self.matrix() - other.matrix(), side=self.side())