Skip to content

Commit e85de39

Browse files
author
Release Manager
committed
gh-40542: Implement composition of pseudomorphisms If $f$ and $g$ are composable pseudomorphisms, in general the composite $f \circ g$ is no longer a pseudomorphism. It is however the case when there is no twisting derivation. In this PR, we implement the computation of the composition in this case. ### 📝 Checklist <!-- Put an `x` in all the boxes that apply. --> - [x] The title is concise and informative. - [x] The description explains in detail what this PR is about. - [ ] I have linked a relevant issue or discussion. - [x] I have created tests covering the changes. - [x] I have updated the documentation and checked the documentation preview. URL: #40542 Reported by: Xavier Caruso Reviewer(s): user202729, Xavier Caruso
2 parents a6b81bc + 9b2aa1e commit e85de39

File tree

1 file changed

+77
-0
lines changed

1 file changed

+77
-0
lines changed

src/sage/modules/free_module_pseudomorphism.py

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -507,6 +507,83 @@ def _richcmp_(self, other, op):
507507
"""
508508
return richcmp(self._matrix, other._matrix, op)
509509

510+
def _composition_(self, right, homset):
511+
r"""
512+
Return the composition of ``self`` and ``right``.
513+
514+
EXAMPLES:
515+
516+
When there is no twisting derivation, the composite is still
517+
a pseudomorphism and it is computed as such::
518+
519+
sage: Fq.<z> = GF(7^3)
520+
sage: Frob = Fq.frobenius_endomorphism()
521+
sage: V = Fq^2
522+
sage: mat = matrix(2, [1, z, z^2, z^3])
523+
sage: f = V.pseudohom(mat, Frob)
524+
sage: f * f
525+
Free module pseudomorphism (twisted by z |--> z^(7^2)) defined by the matrix
526+
[ z^2 + 2*z + 4 3*z^2 + 4*z + 3]
527+
[2*z^2 + 2*z + 5 4*z^2 + 5*z + 6]
528+
Domain: Vector space of dimension 2 over Finite Field in z of size 7^3
529+
Codomain: Vector space of dimension 2 over Finite Field in z of size 7^3
530+
531+
We note that the side is preserved::
532+
533+
sage: fr = f.side_switch()
534+
sage: fr
535+
Free module pseudomorphism (twisted by z |--> z^7) defined as left-multiplication by the matrix
536+
[ 1 z^2]
537+
[ z z^2 + 3]
538+
Domain: Vector space of dimension 2 over Finite Field in z of size 7^3
539+
Codomain: Vector space of dimension 2 over Finite Field in z of size 7^3
540+
sage: fr * fr
541+
Free module pseudomorphism (twisted by z |--> z^(7^2)) defined as left-multiplication by the matrix
542+
[ z^2 + 2*z + 4 2*z^2 + 2*z + 5]
543+
[3*z^2 + 4*z + 3 4*z^2 + 5*z + 6]
544+
Domain: Vector space of dimension 2 over Finite Field in z of size 7^3
545+
Codomain: Vector space of dimension 2 over Finite Field in z of size 7^3
546+
547+
For general pseudomorphisms, a formal composed map is returned::
548+
549+
sage: P.<x> = ZZ[]
550+
sage: d = P.derivation()
551+
sage: M = P^2
552+
sage: f = M.pseudohom([[1, 2*x], [x, 1]], d)
553+
sage: f * f
554+
Composite map:
555+
From: Ambient free module of rank 2 over the integral domain Univariate Polynomial Ring in x over Integer Ring
556+
To: Ambient free module of rank 2 over the integral domain Univariate Polynomial Ring in x over Integer Ring
557+
Defn: Free module pseudomorphism (twisted by d/dx) defined by the matrix
558+
[ 1 2*x]
559+
[ x 1]
560+
Domain: Ambient free module of rank 2 over the integral domain Univariate Polynomial Ring in x over Integer Ring
561+
Codomain: Ambient free module of rank 2 over the integral domain Univariate Polynomial Ring in x over Integer Ring
562+
then
563+
Free module pseudomorphism (twisted by d/dx) defined by the matrix
564+
[ 1 2*x]
565+
[ x 1]
566+
Domain: Ambient free module of rank 2 over the integral domain Univariate Polynomial Ring in x over Integer Ring
567+
Codomain: Ambient free module of rank 2 over the integral domain Univariate Polynomial Ring in x over Integer Ring
568+
"""
569+
if (isinstance(right, FreeModulePseudoMorphism)
570+
and self._derivation is None and right._derivation is None):
571+
if self._morphism is None:
572+
morphism = right._morphism
573+
mat = right._matrix * self._matrix
574+
else:
575+
if right._morphism is None:
576+
morphism = self._morphism
577+
else:
578+
morphism = self._morphism * right._morphism
579+
mat = right._matrix.apply_map(self._morphism) * self._matrix
580+
parent = right.domain().pseudoHom(morphism, codomain=self.codomain())
581+
f = FreeModulePseudoMorphism(parent, mat, "left")
582+
if self.side() == 'right' and right.side() == 'right':
583+
f._side = "right"
584+
return f
585+
return super()._composition_(right, homset)
586+
510587
def ore_module(self, names=None):
511588
r"""
512589
Return the Ore module over which the Ore variable acts

0 commit comments

Comments
 (0)