Tutorial: Homomorphisms¶
This is an interactive tutorial written with real code. We start by setting up \(\LaTeX\) printing.
In [1]:
from IPython.display import display, Math
def show(arg):
return display(Math(arg.to_latex()))
Initializing a homomorphism¶
Homomorphisms between general LCAs are represented by the HomLCA
class. To define a homomorphism, a matrix representation is needed. In
addition to the matrix, the user can also define a target
and
source
explicitly.
Some verification of the inputs is performed by the initializer, for
instance a matrix \(A \in \mathbb{Z}^{2 \times 2}\) cannot represent
\(\phi: \mathbb{Z}^m \to \mathbb{Z}^n\) unless both \(m\) and
\(n\) are \(2\). If no target
/source
is given, the
initializer will assume a free, discrete group, i.e.
\(\mathbb{Z}^m\).
In [2]:
from abelian import LCA, HomLCA
# Initialize the target group for the homomorphism
target = LCA([0, 5], discrete = [False, True])
# Initialize a homomorphism between LCAs
phi = HomLCA([[1, 2], [3, 4]], target = target)
show(phi)
# Initialize a homomorphism with no source/target.
# Source and targets are assumed to be
# of infinite order and discrete (free-to-free)
phi = HomLCA([[1, 2], [3, 4]])
show(phi)
Homomorphisms between finitely generated abelian groups (FGAs) are also
represented by the HomLCA
class.
In [3]:
from abelian import HomLCA
phi = HomLCA([[4, 5], [9, -3]])
show(phi)
Roughly speaking, for a HomLCA
instance to represent a homomorphism
between FGAs, it must have:
- FGAs as source and target.
- The matrix must contain only integer entries.
Compositions¶
A fundamental way to combine two functions is to compose them. We create two homomorphisms and compose them: first \(\psi\), then \(\phi\). The result is the function \(\phi \circ \psi\).
In [4]:
# Create two HomLCAs
phi = HomLCA([[4, 5], [9, -3]])
psi = HomLCA([[1, 0, 1], [0, 1, 1]])
# The composition of phi, then psi
show(phi * psi)
If the homomorphism is an endomorphism (same source and target), repeated composition can be done using exponents.
\(\phi^{n} = \phi \circ \phi \circ \dots \circ \phi, \quad n \geq 1\)
In [5]:
show(phi**3)
Numbers and homomorphisms can be added to homomorphisms, in the same way that numbers and matrices are added to matrices in other software packages.
In [6]:
show(psi)
# Each element in the matrix is multiplied by 2
show(psi + psi)
# Element-wise addition
show(psi + 10)
Slice notation¶
Slice notation is available. The first slice works on rows (target group) and the second slice works on columns (source group). Notice that in Python, indices start with 0.
In [7]:
A = [[10, 10], [10, 15]]
# Notice how the HomLCA converts a list
# into an LCA, this makes it easier to create HomLCAs
phi = HomLCA(A, target = [20, 20])
phi = phi.project_to_source()
# Slice in different ways
show(phi)
show(phi[0, :]) # First row, all columns
show(phi[:, 0]) # All rows, first column
show(phi[1, 1]) # Second row, second column
Stacking homomorphisms¶
There are three ways to stack morphisms:
- Diagonal stacking
- Horizontal stacking
- Vertical stacking
They are all shown below.
Diagonal stacking¶
In [8]:
# Create two homomorphisms
phi = HomLCA([2], target = LCA([0], [False]))
psi = HomLCA([2])
# Stack diagonally
show(phi.stack_diag(psi))
Horizontal stacking¶
In [9]:
# Create two homomorphisms with the same target
target = LCA([0], [False])
phi = HomLCA([[1, 3]], target = target)
source = LCA([0], [False])
psi = HomLCA([7], target=target, source=source)
# Stack horizontally
show(phi.stack_horiz(psi))
Vertical stacking¶
In [10]:
# Create two homomorphisms, they have the same source
phi = HomLCA([[1, 2]])
psi = HomLCA([[3, 4]])
# Stack vertically
show(phi.stack_vert(psi))
Calling homomorphisms¶
In Python, a callable
is an object which implements a method for
function calls. A homomorphism is a callable object, so we can use
phi(x)
to evaluate x
, i.e. send x
from the source to the
target.
We create a homomorphism.
In [11]:
# Create a homomorphism, specify the target
phi = HomLCA([[2, 0], [0, 4]], [10, 12])
# Find the source group (orders)
phi = phi.project_to_source()
show(phi)
We can now call it. The argument must be in the source group.
In [12]:
# An element in the source, represented as a list
group_element = [1, 1]
# Calling the homomorphism
print(phi(group_element))
# Since [6, 4] = [1, 1] mod [5, 3] (source group)
# the following is equal
print(phi([6, 4]) == phi([1, 1]))
[2, 4]
True
Calling and composing¶
We finish this tutorial by showing two ways to calculate the same thing:
- \(y = (\phi \circ \psi)(x)\)
- \(y = \phi(\psi(x))\)
In [13]:
# Create two HomLCAs
phi = HomLCA([[4, 5], [9, -3]])
psi = HomLCA([[1, 0, 1], [0, 1, 1]])
x = [1, 1, 1]
# Compose, then call
answer1 = (phi * psi)(x)
# Call, then call again
answer2 = phi(psi(x))
# The result is the same
print(answer1 == answer2)
True