This is an interactive tutorial written with real code.
We start by importing the LCA class and setting up $\LaTeX$ printing.
In [1]:
from abelian import LCA
from IPython.display import display, Math
def show(arg):
"""This function lets us show LaTeX output."""
return display(Math(arg.to_latex()))
Initializing a locally compact abelian group (LCA) is simple. Every LCA can be written as a direct sum of groups isomorphic to one of: $\mathbb{Z}_n$, $\mathbb{Z}$, $T = \mathbb{R}/\mathbb{Z}$ or $\mathbb{R}$. Specifying these groups, we can initialize LCAs. Groups are specified by:
In [2]:
# Create the group Z_1 + R + Z_3
G = LCA(orders = [1, 0, 3],
discrete = [True, False, True])
print(G) # Standard printing
show(G) # LaTeX output
If no discrete parameter is passed, True is assumed and the LCA initialized will be a finitely generated abelian group (FGA).
In [3]:
# No 'discrete' argument passed,
# so the initializer assumes a discrete group
G = LCA(orders = [5, 11])
show(G)
G.is_FGA() # Check if this group is an FGA
Out[3]:
One way to create LCAs is using the direct sum, which "glues" LCAs together.
In [4]:
# Create two groups
# Notice how the argument names can be omitted
G = LCA([5, 11])
H = LCA([7, 0], [True, True])
# Take the direct sum of G and H
# Two ways: explicitly and using the + operator
direct_sum = G.sum(H)
direct_sum = G + H
show(G)
show(H)
show(direct_sum)
Python comes with a powerful slice syntax. This can be used to "split up" LCAs. LCAs of lower length can be created by slicing, using the built-in slice notation in Python.
In [5]:
# Return groups 0 to 3 (inclusive, exclusive)
sliced = direct_sum[0:3]
show(sliced)
# Return the last two groups in the LCA
sliced = direct_sum[-2:]
show(sliced)
Trivial groups can be removed automatically using remove_trivial. Recall that the trivial group is $\mathbb{Z}_1$.
In [6]:
# Create a group with several trivial groups
G = LCA([1, 1, 0, 5, 1, 7])
show(G)
# Remove trivial groups
G_no_trivial = G.remove_trivial()
show(G_no_trivial)
Recall that a group $G$ is an FGA if all the groups in the direct sum are discrete.
In [7]:
G = LCA([1, 5], discrete = [False, True])
G.is_FGA()
Out[7]:
If $G$ is an FGA, elements can be generated by max-norm by an efficient algorithm. The algorithm is able to generate approximately 200000 elements per second, but scales exponentially with the free rank of the group.
In [8]:
Z = LCA([0])
for element in (Z**2).elements_by_maxnorm([0, 1]):
print(element)
In [9]:
Z_5 = LCA([5])
for element in (Z_5**2).elements_by_maxnorm([0, 1]):
print(element)
The dual() method returns a group isomorphic to the Pontryagin dual.
In [10]:
show(G)
show(G.dual())
LCAs implement the Python iteration protocol, and they subclass the abstract base class (ABC) Sequence. A Sequence is a subclass of Reversible and Collection ABCs. These ABCs force the subclasses that inherit from them to implement certain behaviors, namely:
G in H statement: this checks whether $G$ is a contained in $H$.len(G) built-in, this check the length of the group.We now show this behavior with examples.
In [11]:
G = LCA([10, 1, 0, 0], [True, False, True, False])
# Iterate over all subgroups in G
for subgroup in G:
dual = subgroup.dual()
print('The dual of', subgroup, 'is', dual)
# Print if the group is self dual
if dual == subgroup:
print(' ->', subgroup, 'is self dual')
A LCA $G$ is contained in $H$ iff there exists an injection $\phi: G \to H$ such that every source/target of the mapping are isomorphic groups.
In [12]:
# Create two groups
G = LCA([1, 3, 5])
H = LCA([3, 5, 1, 8])
# Two ways, explicitly or using the `in` keyword
print(G.contained_in(H))
print(G in H)
The length can be computed using the length() method, or the built-in method len. In contrast with rank(), this does not remove trivial groups.
In [13]:
# The length is available with the len built-in function
# Notice that the length is not the same as the rank,
# since the rank will remove trivial subgroups first
G = LCA([1, 3, 5])
show(G)
print(G.length()) # Explicit
print(len(G)) # Using the built-in len function
print(G.rank())
The rank can be computed by the rank() method.
rank() method removes trivial subgroups.length() method does not remove trivial subgroups.
In [14]:
G = LCA([1, 5, 7, 0])
show(G)
G.rank()
Out[14]:
FGAs can be put into a canonical form using the Smith normal form (SNF). Two FGAs are isomorphic iff their canonical form is equal.
In [15]:
G = LCA([1, 3, 3, 5, 8])
show(G)
show(G.canonical())
The groups $G = \mathbb{Z}_3 \oplus \mathbb{Z}_4$ and $H = \mathbb{Z}_{12}$ are isomorphic because they can be put into the same canonical form using the SNF.
In [16]:
G = LCA([3, 4, 0])
H = LCA([12, 0])
G.isomorphic(H)
Out[16]:
General LCAs are isomorphic if the FGAs are isomorphic and the remaining groups such as $\mathbb{R}$ and $T$ can be obtained with a permutation. We show this by example.
In [17]:
G = LCA([12, 13, 0], [True, True, False])
H = LCA([12 * 13, 0], [True, False])
show(G)
show(H)
G.isomorphic(H)
Out[17]:
It is possible to project elements onto groups.
In [18]:
element = [8, 17, 7]
G = LCA([10, 15, 20])
G(element)
Out[18]: