Pseudomorphisms of free modules

AUTHORS:

  • Xavier Caruso, Yossef Musleh (2024-09): initial version

class sage.modules.free_module_pseudomorphism.FreeModulePseudoMorphism(parent, f, side)[source]

Bases: Morphism

Let \(M, M'\) be modules over a ring \(R\), \(\theta: R \to R\) a ring homomorphism, and \(\delta: R \to R\) a \(\theta\)-derivation, which is a map such that:

\[\delta(xy) = \theta(x)\delta(y) + \delta(x)y.\]

A pseudomorphism \(f : M \to M\) is an additive map such that

\[f(\lambda x) = \theta(\lambda)f(x) + \delta(\lambda) x\]

The map \(\theta\) (resp. \(\delta\)) is referred to as the twisting endomorphism (resp. the twisting derivation) of \(f\).

Note

The implementation currently requires that \(M\) and \(M'\) are free modules.

We represent pseudomorphisms by matrices with coefficient in the base ring \(R\). The matrix \(\mathcal M_f\) representing \(f\) is such that its lines (resp. columns if side is "right") are the coordinates of the images of the distinguished basis of the domain (see also method matrix()). More concretely, let \(n\) (resp. \(n'\)) be the dimension of \(M\) (resp. \(M'\)), let \((e_1, \dots, e_n)\) be a basis of \(M\). For any \(x = \sum_{i=1}^n x_i e_i \in M\), we have

\[f(x) = \begin{pmatrix} \theta(x_1) & \cdots & \theta(x_n) \end{pmatrix} \mathcal M_f + \begin{pmatrix} \delta(x_1) & \cdots & \theta(x_n) \end{pmatrix} .\]

When side is "right", the formula is

\[\begin{split}f(x) = \mathcal M_f \begin{pmatrix} \theta(x_1) \\ \vdots \\ \theta(x_n) \end{pmatrix} + \begin{pmatrix} \delta(x_1) \\ \vdots \\ \theta(x_n) \end{pmatrix} .\end{split}\]

This class is not supposed to be instantiated directly; the user should use instead the method sage.rings.module.free_module.FreeModule_generic.pseudohom() to create a pseudomorphism.

matrix()[source]

Return the underlying matrix of this pseudomorphism.

It is defined as the matrix \(M\) whose lines (resp. columns if side is "right") are the coordinates of the images of the distinguished basis of the domain.

EXAMPLES:

sage: Fq.<z> = GF(7^3)
sage: Frob = Fq.frobenius_endomorphism()
sage: M = Fq^3
sage: f = M.pseudohom([[1, z, 3], [0, 1, z^2], [z+1, 1, 1]], Frob)
sage: f.matrix()
[    1     z     3]
[    0     1   z^2]
[z + 1     1     1]
>>> from sage.all import *
>>> Fq = GF(Integer(7)**Integer(3), names=('z',)); (z,) = Fq._first_ngens(1)
>>> Frob = Fq.frobenius_endomorphism()
>>> M = Fq**Integer(3)
>>> f = M.pseudohom([[Integer(1), z, Integer(3)], [Integer(0), Integer(1), z**Integer(2)], [z+Integer(1), Integer(1), Integer(1)]], Frob)
>>> f.matrix()
[    1     z     3]
[    0     1   z^2]
[z + 1     1     1]

sage: e1, e2, e3 = M.basis()
sage: f(e1)
(1, z, 3)
sage: f(e2)
(0, 1, z^2)
sage: f(e3)
(z + 1, 1, 1)
>>> from sage.all import *
>>> e1, e2, e3 = M.basis()
>>> f(e1)
(1, z, 3)
>>> f(e2)
(0, 1, z^2)
>>> f(e3)
(z + 1, 1, 1)
ore_module(names=None)[source]

Return the Ore module over which the Ore variable acts through this pseudomorphism.

INPUT:

  • names – a string, a list of strings or None, the names of the vector of the canonical basis of the Ore module; if None, elements are represented as vectors in \(K^d\) (where \(K\) is the base ring)

EXAMPLES:

sage: Fq.<z> = GF(7^3)
sage: Frob = Fq.frobenius_endomorphism()
sage: V = Fq^2
sage: mat = matrix(2, [1, z, z^2, z^3])
sage: f = V.pseudohom(mat, Frob)

sage: M = f.ore_module()
sage: M
Ore module of rank 2 over Finite Field in z of size 7^3 twisted by z |--> z^7
>>> from sage.all import *
>>> Fq = GF(Integer(7)**Integer(3), names=('z',)); (z,) = Fq._first_ngens(1)
>>> Frob = Fq.frobenius_endomorphism()
>>> V = Fq**Integer(2)
>>> mat = matrix(Integer(2), [Integer(1), z, z**Integer(2), z**Integer(3)])
>>> f = V.pseudohom(mat, Frob)

>>> M = f.ore_module()
>>> M
Ore module of rank 2 over Finite Field in z of size 7^3 twisted by z |--> z^7

Here \(M\) is a module over the Ore ring \(\mathbb F_q[X; \text{Frob}]\) and the variable \(X\) acts on \(M\) through \(f\):

sage: S.<X> = M.ore_ring()
sage: S
Ore Polynomial Ring in X over Finite Field in z of size 7^3 twisted by z |--> z^7
sage: v = M((1,0))
sage: X*v
(1, z)
>>> from sage.all import *
>>> S = M.ore_ring(names=('X',)); (X,) = S._first_ngens(1)
>>> S
Ore Polynomial Ring in X over Finite Field in z of size 7^3 twisted by z |--> z^7
>>> v = M((Integer(1),Integer(0)))
>>> X*v
(1, z)

The argument names can be used to give chosen names to the vectors in the canonical basis:

sage: M = f.ore_module(names=('v', 'w'))
sage: M.basis()
[v, w]
>>> from sage.all import *
>>> M = f.ore_module(names=('v', 'w'))
>>> M.basis()
[v, w]

or even:

sage: M = f.ore_module(names='e')
sage: M.basis()
[e0, e1]
>>> from sage.all import *
>>> M = f.ore_module(names='e')
>>> M.basis()
[e0, e1]

Note that the bracket construction also works:

sage: M.<v,w> = f.ore_module()
sage: M.basis()
[v, w]
sage: v + w
v + w
>>> from sage.all import *
>>> M = f.ore_module(names=('v', 'w',)); (v, w,) = M._first_ngens(2)
>>> M.basis()
[v, w]
>>> v + w
v + w

We refer to sage.modules.ore_module for a tutorial on Ore modules in SageMath.

side()[source]

Return the side of vectors acted on, relative to the matrix.

EXAMPLES:

sage: Fq.<z> = GF(7^3)
sage: Frob = Fq.frobenius_endomorphism()
sage: V = Fq^2

sage: m = matrix(2, [1, z, z^2, z^3])
sage: h1 = V.pseudohom(m, Frob)
sage: h1.side()
'left'
sage: h1([1, 0])
(1, z)

sage: h2 = V.pseudohom(m, Frob, side="right")
sage: h2.side()
'right'
sage: h2([1, 0])
(1, z^2)
>>> from sage.all import *
>>> Fq = GF(Integer(7)**Integer(3), names=('z',)); (z,) = Fq._first_ngens(1)
>>> Frob = Fq.frobenius_endomorphism()
>>> V = Fq**Integer(2)

>>> m = matrix(Integer(2), [Integer(1), z, z**Integer(2), z**Integer(3)])
>>> h1 = V.pseudohom(m, Frob)
>>> h1.side()
'left'
>>> h1([Integer(1), Integer(0)])
(1, z)

>>> h2 = V.pseudohom(m, Frob, side="right")
>>> h2.side()
'right'
>>> h2([Integer(1), Integer(0)])
(1, z^2)
side_switch()[source]

Return the same morphism, acting on vectors on the opposite side.

EXAMPLES:

sage: Fq.<z> = GF(7^3)
sage: Frob = Fq.frobenius_endomorphism()
sage: V = Fq^2

sage: m = matrix(2, [1, z, z^2, z^3])
sage: h1 = V.pseudohom(m, Frob)
sage: h1
Free module pseudomorphism (twisted by z |--> z^7) defined by the matrix
[      1       z]
[    z^2 z^2 + 3]
Domain: Vector space of dimension 2 over Finite Field in z of size 7^3
Codomain: Vector space of dimension 2 over Finite Field in z of size 7^3

sage: h2 = h1.side_switch()
sage: h2
Free module pseudomorphism (twisted by z |--> z^7) defined as left-multiplication by the matrix
[      1     z^2]
[      z z^2 + 3]
Domain: Vector space of dimension 2 over Finite Field in z of size 7^3
Codomain: Vector space of dimension 2 over Finite Field in z of size 7^3
>>> from sage.all import *
>>> Fq = GF(Integer(7)**Integer(3), names=('z',)); (z,) = Fq._first_ngens(1)
>>> Frob = Fq.frobenius_endomorphism()
>>> V = Fq**Integer(2)

>>> m = matrix(Integer(2), [Integer(1), z, z**Integer(2), z**Integer(3)])
>>> h1 = V.pseudohom(m, Frob)
>>> h1
Free module pseudomorphism (twisted by z |--> z^7) defined by the matrix
[      1       z]
[    z^2 z^2 + 3]
Domain: Vector space of dimension 2 over Finite Field in z of size 7^3
Codomain: Vector space of dimension 2 over Finite Field in z of size 7^3

>>> h2 = h1.side_switch()
>>> h2
Free module pseudomorphism (twisted by z |--> z^7) defined as left-multiplication by the matrix
[      1     z^2]
[      z z^2 + 3]
Domain: Vector space of dimension 2 over Finite Field in z of size 7^3
Codomain: Vector space of dimension 2 over Finite Field in z of size 7^3

We check that h1 and h2 are the same:

sage: v = V.random_element()
sage: h1(v) == h2(v)
True
>>> from sage.all import *
>>> v = V.random_element()
>>> h1(v) == h2(v)
True
twisting_derivation()[source]

Return the twisting derivation of the pseudomorphism (or None if the twisting derivation is zero).

EXAMPLES:

sage: P.<x> = ZZ[]
sage: d = P.derivation()
sage: M = P^2
sage: f = M.pseudohom([[1, 2*x], [x, 1]], d)
sage: f.twisting_derivation()
d/dx
>>> from sage.all import *
>>> P = ZZ['x']; (x,) = P._first_ngens(1)
>>> d = P.derivation()
>>> M = P**Integer(2)
>>> f = M.pseudohom([[Integer(1), Integer(2)*x], [x, Integer(1)]], d)
>>> f.twisting_derivation()
d/dx

sage: Fq.<z> = GF(7^3)
sage: Frob = Fq.frobenius_endomorphism()
sage: V = Fq^2
sage: f = V.pseudohom([[1, z], [0, z^2]], Frob)
sage: f.twisting_derivation()
>>> from sage.all import *
>>> Fq = GF(Integer(7)**Integer(3), names=('z',)); (z,) = Fq._first_ngens(1)
>>> Frob = Fq.frobenius_endomorphism()
>>> V = Fq**Integer(2)
>>> f = V.pseudohom([[Integer(1), z], [Integer(0), z**Integer(2)]], Frob)
>>> f.twisting_derivation()
twisting_morphism()[source]

Return the twisting morphism of the pseudomorphism (or None if the twisting morphism is the identity).

EXAMPLES:

sage: Fq.<z> = GF(7^3)
sage: Frob = Fq.frobenius_endomorphism()
sage: V = Fq^2
sage: f = V.pseudohom([[1, z], [0, z^2]], Frob)
sage: f.twisting_morphism()
Frobenius endomorphism z |--> z^7 on Finite Field in z of size 7^3
>>> from sage.all import *
>>> Fq = GF(Integer(7)**Integer(3), names=('z',)); (z,) = Fq._first_ngens(1)
>>> Frob = Fq.frobenius_endomorphism()
>>> V = Fq**Integer(2)
>>> f = V.pseudohom([[Integer(1), z], [Integer(0), z**Integer(2)]], Frob)
>>> f.twisting_morphism()
Frobenius endomorphism z |--> z^7 on Finite Field in z of size 7^3

sage: P.<x> = ZZ[]
sage: d = P.derivation()
sage: M = P^2
sage: f = M.pseudohom([[1, 2*x], [x, 1]], d)
sage: f.twisting_morphism()
>>> from sage.all import *
>>> P = ZZ['x']; (x,) = P._first_ngens(1)
>>> d = P.derivation()
>>> M = P**Integer(2)
>>> f = M.pseudohom([[Integer(1), Integer(2)*x], [x, Integer(1)]], d)
>>> f.twisting_morphism()