Tutorial: Factoring homomorphisms

This is an interactive tutorial written with real code. We start by importing the LCA class, the HomLCA class and setting up \(\LaTeX\) printing.

In [1]:
from abelian import LCA, HomLCA
from IPython.display import display, Math

def show(arg):
    return display(Math(arg.to_latex()))

Initialization and source/target projections

We create a HomLCA instance, which may represent a homomorphism between FGAs. In this tutorial we will only consider homomorphisms between FGAs.

In [2]:
phi = HomLCA([[5, 10, 15],
              [10, 20, 30],
              [10, 5, 30]],
              target = [50, 20, 30])
show(phi)
$$\begin{pmatrix}5 & 10 & 15\\10 & 20 & 30\\10 & 5 & 30\end{pmatrix}:\mathbb{Z} \oplus \mathbb{Z} \oplus \mathbb{Z} \to \mathbb{Z}_{50} \oplus \mathbb{Z}_{20} \oplus \mathbb{Z}_{30}$$

Projecting to source

The source (or domain) is assumed to be free (infinite order). Calculating the orders is done with the project_to_source method, after which the orders of the columns are shown in the source group.

In [3]:
# Project to source, i.e. orders of generator columns
phi = phi.project_to_source()
show(phi)
$$\begin{pmatrix}5 & 10 & 15\\10 & 20 & 30\\10 & 5 & 30\end{pmatrix}:\mathbb{Z}_{30} \oplus \mathbb{Z}_{30} \oplus \mathbb{Z}_{10} \to \mathbb{Z}_{50} \oplus \mathbb{Z}_{20} \oplus \mathbb{Z}_{30}$$

Projecting to target

Projecting the columns onto the target group will make the morphism more readable. The project_to_target() method will project every column to the target group.

In [4]:
# Project the generator columns to the target group
phi = phi.project_to_target()
show(phi)
$$\begin{pmatrix}5 & 10 & 15\\10 & 0 & 10\\10 & 5 & 0\end{pmatrix}:\mathbb{Z}_{30} \oplus \mathbb{Z}_{30} \oplus \mathbb{Z}_{10} \to \mathbb{Z}_{50} \oplus \mathbb{Z}_{20} \oplus \mathbb{Z}_{30}$$

The kernel monomorphism

The kernel morphism is a monomorphism such that \(\phi \circ \operatorname{ker} (\phi) = 0\). The kernel of \(\phi\) is:

In [5]:
# Calculate the kernel
show(phi.kernel())
$$\begin{pmatrix}25 & 29 & 28\\10 & 2 & 28\\5 & 9 & 2\end{pmatrix}:\mathbb{Z} \oplus \mathbb{Z} \oplus \mathbb{Z} \to \mathbb{Z}_{30} \oplus \mathbb{Z}_{30} \oplus \mathbb{Z}_{10}$$

The kernel monomorphism is not projected to source by default, but doing so is simple.

In [6]:
show(phi.kernel().project_to_source())
$$\begin{pmatrix}25 & 29 & 28\\10 & 2 & 28\\5 & 9 & 2\end{pmatrix}:\mathbb{Z}_{6} \oplus \mathbb{Z}_{30} \oplus \mathbb{Z}_{15} \to \mathbb{Z}_{30} \oplus \mathbb{Z}_{30} \oplus \mathbb{Z}_{10}$$

Verify that \(\phi \circ \operatorname{ker} (\phi) = 0\).

In [7]:
show(phi * phi.kernel())
$$\begin{pmatrix}300 & 300 & 450\\300 & 380 & 300\\300 & 300 & 420\end{pmatrix}:\mathbb{Z} \oplus \mathbb{Z} \oplus \mathbb{Z} \to \mathbb{Z}_{50} \oplus \mathbb{Z}_{20} \oplus \mathbb{Z}_{30}$$

To clearly see that this is the zero morphism, use the project_to_target() method as such.

In [8]:
zero = phi * phi.kernel()
zero = zero.project_to_target()
show(zero)
$$\begin{pmatrix}0 & 0 & 0\\0 & 0 & 0\\0 & 0 & 0\end{pmatrix}:\mathbb{Z} \oplus \mathbb{Z} \oplus \mathbb{Z} \to \mathbb{Z}_{50} \oplus \mathbb{Z}_{20} \oplus \mathbb{Z}_{30}$$

The cokernel epimorphism

The kernel morphism is an epimorphism such that \(\operatorname{coker}(\phi) \circ \phi = 0\). The cokernel of \(\phi\) is:

In [9]:
show(phi.cokernel())
$$\begin{pmatrix}1 & 0 & 0\\0 & 1 & 4\\18 & 17 & 4\end{pmatrix}:\mathbb{Z}_{50} \oplus \mathbb{Z}_{20} \oplus \mathbb{Z}_{30} \to \mathbb{Z}_{5} \oplus \mathbb{Z}_{5} \oplus \mathbb{Z}_{20}$$

We verify the factorization.

In [10]:
show((phi.cokernel() * phi))
$$\begin{pmatrix}5 & 10 & 15\\50 & 20 & 10\\300 & 200 & 440\end{pmatrix}:\mathbb{Z}_{30} \oplus \mathbb{Z}_{30} \oplus \mathbb{Z}_{10} \to \mathbb{Z}_{5} \oplus \mathbb{Z}_{5} \oplus \mathbb{Z}_{20}$$

Again it is not immediately clear that this is the zero morphism. To verify this, we again use the project_to_target() method as such.

In [11]:
zero = phi.cokernel() * phi
zero = zero.project_to_target()

show(zero)
$$\begin{pmatrix}0 & 0 & 0\\0 & 0 & 0\\0 & 0 & 0\end{pmatrix}:\mathbb{Z}_{30} \oplus \mathbb{Z}_{30} \oplus \mathbb{Z}_{10} \to \mathbb{Z}_{5} \oplus \mathbb{Z}_{5} \oplus \mathbb{Z}_{20}$$

The image/coimage factorization

The image/coimage factorization is \(\phi = \operatorname{im}(\phi) \circ \operatorname{coim}(\phi)\), where the image is a monomorphism and the coimage is an epimorphism.

The image monomorphism

Finding the image is easy, just call the image() method.

In [12]:
im = phi.image()
show(im)
$$\begin{pmatrix}0 & 25 & 40\\0 & 10 & 0\\0 & 0 & 25\end{pmatrix}:\mathbb{Z}_{1} \oplus \mathbb{Z}_{2} \oplus \mathbb{Z}_{30} \to \mathbb{Z}_{50} \oplus \mathbb{Z}_{20} \oplus \mathbb{Z}_{30}$$

A trivial group \(\mathbb{Z}_1\) is in the source. It can be removed using remove_trivial_subgroups().

In [13]:
im = im.remove_trivial_groups()
show(im)
$$\begin{pmatrix}25 & 40\\10 & 0\\0 & 25\end{pmatrix}:\mathbb{Z}_{2} \oplus \mathbb{Z}_{30} \to \mathbb{Z}_{50} \oplus \mathbb{Z}_{20} \oplus \mathbb{Z}_{30}$$

The coimage epimorphism

Finding the coimage is done by calling the coimage() method.

In [14]:
coim = phi.coimage().remove_trivial_groups()
show(coim)
$$\begin{pmatrix}1 & 0 & 1\\22 & 29 & 6\end{pmatrix}:\mathbb{Z}_{30} \oplus \mathbb{Z}_{30} \oplus \mathbb{Z}_{10} \to \mathbb{Z}_{2} \oplus \mathbb{Z}_{30}$$

Verify the image/coimage factorization

We now verify that \(\phi = \operatorname{im}(\phi) \circ \operatorname{coim}(\phi)\).

In [15]:
show(phi)
show((im * coim).project_to_target())
$$\begin{pmatrix}5 & 10 & 15\\10 & 0 & 10\\10 & 5 & 0\end{pmatrix}:\mathbb{Z}_{30} \oplus \mathbb{Z}_{30} \oplus \mathbb{Z}_{10} \to \mathbb{Z}_{50} \oplus \mathbb{Z}_{20} \oplus \mathbb{Z}_{30}$$
$$\begin{pmatrix}5 & 10 & 15\\10 & 0 & 10\\10 & 5 & 0\end{pmatrix}:\mathbb{Z}_{30} \oplus \mathbb{Z}_{30} \oplus \mathbb{Z}_{10} \to \mathbb{Z}_{50} \oplus \mathbb{Z}_{20} \oplus \mathbb{Z}_{30}$$
In [16]:
(im * coim).project_to_target() == phi
Out[16]:
True