Canonical forms and automorphisms for linear codes over finite fields¶
We implemented the algorithm described in [Feu2009] which computes, a unique
code (canonical form) in the equivalence class of a given
linear code \(C \leq \GF{q}^n\). Furthermore, this algorithm will return the
automorphism group of \(C\), too. You will find more details about the algorithm
in the documentation of the class
LinearCodeAutGroupCanLabel
.
The equivalence of codes is modeled as a group action by the group \(G = {\GF{q}^*}^n \rtimes (Aut(\GF{q}) \times S_n)\) on the set of subspaces of \(\GF{q}^n\) . The group \(G\) will be called the semimonomial group of degree \(n\).
The algorithm is started by initializing the class
LinearCodeAutGroupCanLabel
.
When the object gets available, all computations are already finished and
you can access the relevant data using the member functions:
People do also use some weaker notions of equivalence, namely
permutational equivalence and monomial equivalence (linear isometries).
These can be seen as the subgroups \(S_n\) and \({\GF{q}^*}^n \rtimes S_n\) of \(G\).
If you are interested in one of these notions, you can just pass
the optional parameter algorithm_type
.
A second optional parameter P
allows you to restrict the
group of permutations \(S_n\) to a subgroup which respects the coloring given
by P
.
AUTHORS:
Thomas Feulner (2012-11-15): initial version
EXAMPLES:
sage: from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel
sage: C = codes.HammingCode(GF(3), 3).dual_code()
sage: P = LinearCodeAutGroupCanLabel(C)
sage: P.get_canonical_form().generator_matrix()
[1 0 0 0 0 1 1 1 1 1 1 1 1]
[0 1 0 1 1 0 0 1 1 2 2 1 2]
[0 0 1 1 2 1 2 1 2 1 2 0 0]
sage: LinearCode(P.get_transporter()*C.generator_matrix()) == P.get_canonical_form()
True
sage: A = P.get_autom_gens()
sage: all(LinearCode(a*C.generator_matrix()) == C for a in A)
True
sage: P.get_autom_order() == GL(3, GF(3)).order()
True
>>> from sage.all import *
>>> from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel
>>> C = codes.HammingCode(GF(Integer(3)), Integer(3)).dual_code()
>>> P = LinearCodeAutGroupCanLabel(C)
>>> P.get_canonical_form().generator_matrix()
[1 0 0 0 0 1 1 1 1 1 1 1 1]
[0 1 0 1 1 0 0 1 1 2 2 1 2]
[0 0 1 1 2 1 2 1 2 1 2 0 0]
>>> LinearCode(P.get_transporter()*C.generator_matrix()) == P.get_canonical_form()
True
>>> A = P.get_autom_gens()
>>> all(LinearCode(a*C.generator_matrix()) == C for a in A)
True
>>> P.get_autom_order() == GL(Integer(3), GF(Integer(3))).order()
True
If the dimension of the dual code is smaller, we will work on this code:
sage: C2 = codes.HammingCode(GF(3), 3)
sage: P2 = LinearCodeAutGroupCanLabel(C2)
sage: P2.get_canonical_form().parity_check_matrix() == P.get_canonical_form().generator_matrix()
True
>>> from sage.all import *
>>> C2 = codes.HammingCode(GF(Integer(3)), Integer(3))
>>> P2 = LinearCodeAutGroupCanLabel(C2)
>>> P2.get_canonical_form().parity_check_matrix() == P.get_canonical_form().generator_matrix()
True
There is a specialization of this algorithm to pass a coloring on the coordinates. This is just a list of lists, telling the algorithm which columns do share the same coloring:
sage: C = codes.HammingCode(GF(4, 'a'), 3).dual_code()
sage: P = LinearCodeAutGroupCanLabel(C, P=[ [0], [1], list(range(2, C.length())) ])
sage: P.get_autom_order()
864
sage: A = [a.get_perm() for a in P.get_autom_gens()]
sage: H = SymmetricGroup(21).subgroup(A)
sage: H.orbits()
((1,),
(2,),
(3, 5, 4),
(6, 19, 16, 21, 9, 10, 15, 8, 20, 11, 14, 13, 7, 12, 18, 17))
>>> from sage.all import *
>>> C = codes.HammingCode(GF(Integer(4), 'a'), Integer(3)).dual_code()
>>> P = LinearCodeAutGroupCanLabel(C, P=[ [Integer(0)], [Integer(1)], list(range(Integer(2), C.length())) ])
>>> P.get_autom_order()
864
>>> A = [a.get_perm() for a in P.get_autom_gens()]
>>> H = SymmetricGroup(Integer(21)).subgroup(A)
>>> H.orbits()
((1,),
(2,),
(3, 5, 4),
(6, 19, 16, 21, 9, 10, 15, 8, 20, 11, 14, 13, 7, 12, 18, 17))
We can also restrict the group action to linear isometries:
sage: P = LinearCodeAutGroupCanLabel(C, algorithm_type='linear')
sage: P.get_autom_order() == GL(3, GF(4, 'a')).order()
True
>>> from sage.all import *
>>> P = LinearCodeAutGroupCanLabel(C, algorithm_type='linear')
>>> P.get_autom_order() == GL(Integer(3), GF(Integer(4), 'a')).order()
True
and to the action of the symmetric group only:
sage: P = LinearCodeAutGroupCanLabel(C, algorithm_type='permutational')
sage: P.get_autom_order() == C.permutation_automorphism_group().order()
True
>>> from sage.all import *
>>> P = LinearCodeAutGroupCanLabel(C, algorithm_type='permutational')
>>> P.get_autom_order() == C.permutation_automorphism_group().order()
True
- class sage.coding.codecan.autgroup_can_label.LinearCodeAutGroupCanLabel(C, P=None, algorithm_type='semilinear')[source]¶
Bases:
object
Canonical representatives and automorphism group computation for linear codes over finite fields.
There are several notions of equivalence for linear codes: Let \(C\), \(D\) be linear codes of length \(n\) and dimension \(k\). The codes \(C\) and \(D\) are said to be
permutational equivalent, if there is some permutation \(\pi \in S_n\) such that \((c_{\pi(0)}, \ldots, c_{\pi(n-1)}) \in D\) for all \(c \in C\).
linear equivalent, if there is some permutation \(\pi \in S_n\) and a vector \(\phi \in {\GF{q}^*}^n\) of units of length \(n\) such that \((c_{\pi(0)} \phi_0^{-1}, \ldots, c_{\pi(n-1)} \phi_{n-1}^{-1}) \in D\) for all \(c \in C\).
semilinear equivalent, if there is some permutation \(\pi \in S_n\), a vector \(\phi\) of units of length \(n\) and a field automorphism \(\alpha\) such that \((\alpha(c_{\pi(0)}) \phi_0^{-1}, \ldots, \alpha( c_{\pi(n-1)}) \phi_{n-1}^{-1} ) \in D\) for all \(c \in C\).
These are group actions. This class provides an algorithm that will compute a unique representative \(D\) in the orbit of the given linear code \(C\). Furthermore, the group element \(g\) with \(g * C = D\) and the automorphism group of \(C\) will be computed as well.
There is also the possibility to restrict the permutational part of this action to a Young subgroup of \(S_n\). This could be achieved by passing a partition \(P\) (as a list of lists) of the set \(\{0, \ldots, n-1\}\). This is an option which is also available in the computation of a canonical form of a graph, see
sage.graphs.generic_graph.GenericGraph.canonical_label()
.EXAMPLES:
sage: from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel sage: C = codes.HammingCode(GF(3), 3).dual_code() sage: P = LinearCodeAutGroupCanLabel(C) sage: P.get_canonical_form().generator_matrix() [1 0 0 0 0 1 1 1 1 1 1 1 1] [0 1 0 1 1 0 0 1 1 2 2 1 2] [0 0 1 1 2 1 2 1 2 1 2 0 0] sage: LinearCode(P.get_transporter()*C.generator_matrix()) == P.get_canonical_form() True sage: a = P.get_autom_gens()[0] sage: (a*C.generator_matrix()).echelon_form() == C.generator_matrix().echelon_form() True sage: P.get_autom_order() == GL(3, GF(3)).order() True
>>> from sage.all import * >>> from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel >>> C = codes.HammingCode(GF(Integer(3)), Integer(3)).dual_code() >>> P = LinearCodeAutGroupCanLabel(C) >>> P.get_canonical_form().generator_matrix() [1 0 0 0 0 1 1 1 1 1 1 1 1] [0 1 0 1 1 0 0 1 1 2 2 1 2] [0 0 1 1 2 1 2 1 2 1 2 0 0] >>> LinearCode(P.get_transporter()*C.generator_matrix()) == P.get_canonical_form() True >>> a = P.get_autom_gens()[Integer(0)] >>> (a*C.generator_matrix()).echelon_form() == C.generator_matrix().echelon_form() True >>> P.get_autom_order() == GL(Integer(3), GF(Integer(3))).order() True
- get_PGammaL_gens()[source]¶
Return the set of generators translated to the group \(P\Gamma L(k,q)\).
There is a geometric point of view of code equivalence. A linear code is identified with the multiset of points in the finite projective geometry \(PG(k-1, q)\). The equivalence of codes translates to the natural action of \(P\Gamma L(k,q)\). Therefore, we may interpret the group as a subgroup of \(P\Gamma L(k,q)\) as well.
EXAMPLES:
sage: from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel sage: C = codes.HammingCode(GF(4, 'a'), 3).dual_code() sage: A = LinearCodeAutGroupCanLabel(C).get_PGammaL_gens() sage: Gamma = C.generator_matrix() sage: N = [ x.monic() for x in Gamma.columns() ] sage: all((g[0]*n.apply_map(g[1])).monic() in N for n in N for g in A) True
>>> from sage.all import * >>> from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel >>> C = codes.HammingCode(GF(Integer(4), 'a'), Integer(3)).dual_code() >>> A = LinearCodeAutGroupCanLabel(C).get_PGammaL_gens() >>> Gamma = C.generator_matrix() >>> N = [ x.monic() for x in Gamma.columns() ] >>> all((g[Integer(0)]*n.apply_map(g[Integer(1)])).monic() in N for n in N for g in A) True
- get_PGammaL_order()[source]¶
Return the size of the automorphism group as a subgroup of \(P\Gamma L(k,q)\).
There is a geometric point of view of code equivalence. A linear code is identified with the multiset of points in the finite projective geometry \(PG(k-1, q)\). The equivalence of codes translates to the natural action of \(P\Gamma L(k,q)\). Therefore, we may interpret the group as a subgroup of \(P\Gamma L(k,q)\) as well.
EXAMPLES:
sage: from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel sage: C = codes.HammingCode(GF(4, 'a'), 3).dual_code() sage: LinearCodeAutGroupCanLabel(C).get_PGammaL_order() == GL(3, GF(4, 'a')).order()*2/3 True
>>> from sage.all import * >>> from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel >>> C = codes.HammingCode(GF(Integer(4), 'a'), Integer(3)).dual_code() >>> LinearCodeAutGroupCanLabel(C).get_PGammaL_order() == GL(Integer(3), GF(Integer(4), 'a')).order()*Integer(2)/Integer(3) True
- get_autom_gens()[source]¶
Return a generating set for the automorphism group of the code.
EXAMPLES:
sage: from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel sage: C = codes.HammingCode(GF(2), 3).dual_code() sage: A = LinearCodeAutGroupCanLabel(C).get_autom_gens() sage: Gamma = C.generator_matrix().echelon_form() sage: all((g*Gamma).echelon_form() == Gamma for g in A) True
>>> from sage.all import * >>> from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel >>> C = codes.HammingCode(GF(Integer(2)), Integer(3)).dual_code() >>> A = LinearCodeAutGroupCanLabel(C).get_autom_gens() >>> Gamma = C.generator_matrix().echelon_form() >>> all((g*Gamma).echelon_form() == Gamma for g in A) True
- get_autom_order()[source]¶
Return the size of the automorphism group of the code.
EXAMPLES:
sage: from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel sage: C = codes.HammingCode(GF(2), 3).dual_code() sage: LinearCodeAutGroupCanLabel(C).get_autom_order() 168
>>> from sage.all import * >>> from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel >>> C = codes.HammingCode(GF(Integer(2)), Integer(3)).dual_code() >>> LinearCodeAutGroupCanLabel(C).get_autom_order() 168
- get_canonical_form()[source]¶
Return the canonical orbit representative we computed.
EXAMPLES:
sage: from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel sage: C = codes.HammingCode(GF(3), 3).dual_code() sage: CF1 = LinearCodeAutGroupCanLabel(C).get_canonical_form() sage: s = SemimonomialTransformationGroup(GF(3), C.length()).an_element() sage: C2 = LinearCode(s*C.generator_matrix()) sage: CF2 = LinearCodeAutGroupCanLabel(C2).get_canonical_form() sage: CF1 == CF2 True
>>> from sage.all import * >>> from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel >>> C = codes.HammingCode(GF(Integer(3)), Integer(3)).dual_code() >>> CF1 = LinearCodeAutGroupCanLabel(C).get_canonical_form() >>> s = SemimonomialTransformationGroup(GF(Integer(3)), C.length()).an_element() >>> C2 = LinearCode(s*C.generator_matrix()) >>> CF2 = LinearCodeAutGroupCanLabel(C2).get_canonical_form() >>> CF1 == CF2 True
- get_transporter()[source]¶
Return the element which maps the code to its canonical form.
EXAMPLES:
sage: from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel sage: C = codes.HammingCode(GF(2), 3).dual_code() sage: P = LinearCodeAutGroupCanLabel(C) sage: g = P.get_transporter() sage: D = P.get_canonical_form() sage: (g*C.generator_matrix()).echelon_form() == D.generator_matrix().echelon_form() True
>>> from sage.all import * >>> from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel >>> C = codes.HammingCode(GF(Integer(2)), Integer(3)).dual_code() >>> P = LinearCodeAutGroupCanLabel(C) >>> g = P.get_transporter() >>> D = P.get_canonical_form() >>> (g*C.generator_matrix()).echelon_form() == D.generator_matrix().echelon_form() True