Path: blob/master/src/sage/coding/codecan/autgroup_can_label.pyx
8815 views
r"""1Canonical forms and automorphisms for linear codes over finite fields.23We implemented the algorithm described in [Feu2009]_ which computes, a unique4code (canonical form) in the equivalence class of a given5linear code `C \leq \GF{q}^n`. Furthermore, this algorithm will return the6automorphism group of `C`, too. You will find more details about the algorithm7in the documentation of the class8:class:`~sage.coding.codecan.autgroup_can_label.LinearCodeAutGroupCanLabel`.910The equivalence of codes is modeled as a group action by the group11`G = {\GF{q}^*}^n \rtimes (Aut(\GF{q}) \times S_n)` on the set12of subspaces of `\GF{q}^n` . The group `G` will be called the13semimonomial group of degree `n`.1415The algorithm is started by initializing the class16:class:`~sage.coding.codecan.autgroup_can_label.LinearCodeAutGroupCanLabel`.17When the object gets available, all computations are already finished and18you can access the relevant data using the member functions:1920* :meth:`~sage.coding.codecan.autgroup_can_label.LinearCodeAutGroupCanLabel.get_canonical_form`2122* :meth:`~sage.coding.codecan.autgroup_can_label.LinearCodeAutGroupCanLabel.get_transporter`2324* :meth:`~sage.coding.codecan.autgroup_can_label.LinearCodeAutGroupCanLabel.get_autom_gens`2526People do also use some weaker notions of equivalence, namely27**permutational** equivalence and monomial equivalence (**linear** isometries).28These can be seen as the subgroups `S_n` and `{\GF{q}^*}^n \rtimes S_n` of `G`.29If you are interested in one of these notions, you can just pass30the optional parameter ``algorithm_type``.3132A second optional parameter ``P`` allows you to restrict the33group of permutations `S_n` to a subgroup which respects the coloring given34by ``P``.3536AUTHORS:3738- Thomas Feulner (2012-11-15): initial version3940REFERENCES:4142.. [Feu2009] T. Feulner. The Automorphism Groups of Linear Codes and43Canonical Representatives of Their Semilinear Isometry Classes.44Advances in Mathematics of Communications 3 (4), pp. 363-383, Nov 20094546EXAMPLES::4748sage: from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel49sage: C = codes.HammingCode(3, GF(3)).dual_code()50sage: P = LinearCodeAutGroupCanLabel(C)51sage: P.get_canonical_form().gen_mat()52[1 0 0 0 0 1 1 1 1 1 1 1 1]53[0 1 0 1 1 0 0 1 1 2 2 1 2]54[0 0 1 1 2 1 2 1 2 1 2 0 0]55sage: LinearCode(P.get_transporter()*C.gen_mat()) == P.get_canonical_form()56True57sage: A = P.get_autom_gens()58sage: all( [ LinearCode(a*C.gen_mat()) == C for a in A])59True60sage: P.get_autom_order() == GL(3, GF(3)).order()61True6263If the dimension of the dual code is smaller, we will work on this code::6465sage: C2 = codes.HammingCode(3, GF(3))66sage: P2 = LinearCodeAutGroupCanLabel(C2)67sage: P2.get_canonical_form().check_mat() == P.get_canonical_form().gen_mat()68True6970There is a specialization of this algorithm to pass a coloring on the71coordinates. This is just a list of lists, telling the algorithm which72columns do share the same coloring::7374sage: C = codes.HammingCode(3, GF(4, 'a')).dual_code()75sage: P = LinearCodeAutGroupCanLabel(C, P=[ [0], [1], range(2, C.length()) ])76sage: P.get_autom_order()7786478sage: A = [a.get_perm() for a in P.get_autom_gens()]79sage: H = SymmetricGroup(21).subgroup(A)80sage: H.orbits()81[[1], [2], [3, 5, 4], [6, 10, 13, 20, 17, 9, 8, 11, 18, 15, 14, 16, 12, 19, 21, 7]]8283We can also restrict the group action to linear isometries::8485sage: P = LinearCodeAutGroupCanLabel(C, algorithm_type="linear")86sage: P.get_autom_order() == GL(3, GF(4, 'a')).order()87True8889and to the action of the symmetric group only::9091sage: P = LinearCodeAutGroupCanLabel(C, algorithm_type="permutational")92sage: P.get_autom_order() == C.permutation_automorphism_group().order()93True94"""95#*****************************************************************************96# Copyright (C) 2012 Thomas Feulner <[email protected]>97#98# Distributed under the terms of the GNU General Public License (GPL)99# as published by the Free Software Foundation; either version 2 of100# the License, or (at your option) any later version.101# http://www.gnu.org/licenses/102#*****************************************************************************103104from sage.coding.codecan.codecan import PartitionRefinementLinearCode105from sage.combinat.permutation import Permutation106from sage.functions.other import factorial107108def _cyclic_shift(n, p):109r"""110If ``p`` is a list of pairwise distinct coordinates in ``range(n)``,111then this function returns the cyclic shift of112the coordinates contained in ``p``, when acting on a vector of length `n`.113114Note that the domain of a ``Permutation`` is ``range(1, n+1)``.115116EXAMPLE::117118sage: from sage.coding.codecan.autgroup_can_label import _cyclic_shift119sage: p = _cyclic_shift(10, [2,7,4,1]); p120[1, 3, 8, 4, 2, 6, 7, 5, 9, 10]121122We prove that the action is as expected::123124sage: t = range(10)125sage: p.action(t)126[0, 2, 7, 3, 1, 5, 6, 4, 8, 9]127"""128x = range(1, n + 1)129for i in range(1, len(p)):130x[p[i - 1]] = p[i] + 1131x[p[len(p) - 1]] = p[0] + 1132return Permutation(x)133134class LinearCodeAutGroupCanLabel:135r"""136Canonical representatives and automorphism group computation for linear137codes over finite fields.138139There are several notions of equivalence for linear codes:140Let `C`, `D` be linear codes of length `n` and dimension `k`.141`C` and `D` are said to be142143- permutational equivalent, if there is some permutation `\pi \in S_n`144such that `(c_{\pi(0)}, \ldots, c_{\pi(n-1)}) \in D` for all `c \in C`.145146- linear equivalent, if there is some permutation `\pi \in S_n` and a147vector `\phi \in {\GF{q}^*}^n` of units of length `n` such that148`(c_{\pi(0)} \phi_0^{-1}, \ldots, c_{\pi(n-1)} \phi_{n-1}^{-1}) \in D`149for all `c \in C`.150151- semilinear equivalent, if there is some permutation `\pi \in S_n`, a152vector `\phi` of units of length `n` and a field automorphism `\alpha`153such that154`(\alpha(c_{\pi(0)}) \phi_0^{-1}, \ldots, \alpha( c_{\pi(n-1)}) \phi_{n-1}^{-1} ) \in D`155for all `c \in C`.156157These are group actions. This class provides an algorithm that will compute158a unique representative `D` in the orbit of the given linear code `C`.159Furthermore, the group element `g` with `g * C = D` and the automorphism160group of `C` will be computed as well.161162There is also the possibility to restrict the permutational part of this163action to a Young subgroup of `S_n`. This could be achieved by passing a164partition `P` (as a list of lists) of the set `\{0, \ldots, n-1\}`. This is165an option which is also available in the computation of a canonical form of166a graph, see :meth:`sage.graphs.generic_graph.GenericGraph.canonical_label`.167168EXAMPLES::169170sage: from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel171sage: C = codes.HammingCode(3, GF(3)).dual_code()172sage: P = LinearCodeAutGroupCanLabel(C)173sage: P.get_canonical_form().gen_mat()174[1 0 0 0 0 1 1 1 1 1 1 1 1]175[0 1 0 1 1 0 0 1 1 2 2 1 2]176[0 0 1 1 2 1 2 1 2 1 2 0 0]177sage: LinearCode(P.get_transporter()*C.gen_mat()) == P.get_canonical_form()178True179sage: a = P.get_autom_gens()[0]180sage: (a*C.gen_mat()).echelon_form() == C.gen_mat().echelon_form()181True182sage: P.get_autom_order() == GL(3, GF(3)).order()183True184"""185186def __init__(self, C, P=None, algorithm_type="semilinear"):187"""188see :class:`LinearCodeAutGroupCanLabel`189190INPUT:191192- ``C`` -- a linear code193194- ``P`` (optional) -- a coloring of the coordinates i.e. a partition195(list of disjoint lists) of [0 , ..., C.length()-1 ]196197- ``algorithm_type`` (optional) -- which defines the acting group, either198199* ``permutational``200201* ``linear``202203* ``semilinear``204205EXAMPLES::206207sage: from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel208sage: C = codes.HammingCode(3, GF(2)).dual_code()209sage: P = LinearCodeAutGroupCanLabel(C)210sage: P.get_canonical_form().gen_mat()211[1 0 0 0 1 1 1]212[0 1 0 1 0 1 1]213[0 0 1 1 1 1 0]214sage: P2 = LinearCodeAutGroupCanLabel(C, P=[[0,3,5],[1,2,4,6]],215....: algorithm_type="permutational")216sage: P2.get_canonical_form().gen_mat()217[1 1 1 0 0 0 1]218[0 1 0 1 1 0 1]219[0 0 1 0 1 1 1]220"""221from sage.groups.semimonomial_transformations.semimonomial_transformation_group import SemimonomialTransformationGroup222from sage.coding.linear_code import LinearCode223224if not isinstance(C, LinearCode):225raise TypeError("%s is not a linear code"%C)226227self.C = C228mat = C.gen_mat()229F = mat.base_ring()230S = SemimonomialTransformationGroup(F, mat.ncols())231232if P is None:233P = [range(mat.ncols())]234235pos2P = [-1] * mat.ncols()236for i in range(len(P)):237P[i].sort(reverse=True)238for x in P[i]:239pos2P[x] = i240241col_list = mat.columns()242nz = [i for i in range(mat.ncols()) if not col_list[i].is_zero()]243z = [(pos2P[i], i) for i in range(mat.ncols()) if col_list[i].is_zero()]244z.sort()245z = [i for (p, i) in z]246247normalization_factors = [ F.one() ] * mat.ncols()248if algorithm_type == "permutational":249for c in col_list:250c.set_immutable()251else:252for x in nz:253normalization_factors[x] = col_list[x][(col_list[x].support())[0]]254col_list[x] = normalization_factors[x] ** (-1) * col_list[x]255col_list[x].set_immutable()256257normalization = S(v=normalization_factors)258normalization_inverse = normalization ** (-1)259col_set = list({col_list[y] for y in nz })260col2pos = []261col2P = []262for c in col_set:263X = [(pos2P[y], y) for y in range(mat.ncols()) if col_list[y] == c ]264X.sort()265col2pos.append([b for (a, b) in X ])266col2P.append([a for (a, b) in X ])267268zipped = zip(col2P, col_set, col2pos)269zipped.sort()270271col2P = [qty for (qty, c, pos) in zipped]272col_set = [c for (qty, c, pos) in zipped]273col2pos = [pos for (qty, c, pos) in zipped]274P_refined = []275p = [0]276act_qty = col2P[0]277for i in range(1, len(col_set)):278if act_qty == col2P[i]:279p.append(i)280else:281P_refined.append(p)282p = [i]283act_qty = col2P[i]284P_refined.append(p)285# now we can start the main algorithm:286from sage.matrix.constructor import matrix287288if len(col_set) >= 2 * mat.nrows():289# the dimension of the code is smaller or equal than290# the dimension of the dual code291# in this case we work with the code itself.292pr = PartitionRefinementLinearCode(len(col_set),293matrix(col_set).transpose(), P=P_refined, algorithm_type=algorithm_type)294295# this command allows you some advanced debuging296# it prints the backtrack tree -> must be activated when installing297# pr._latex_view(title="MyTitle") #this will provide you some visual representation of what is going on298299can_transp = pr.get_transporter()300can_col_set = pr.get_canonical_form().columns()301self._PGammaL_autom_gens = self._compute_PGammaL_automs(pr.get_autom_gens(),302normalization, normalization_inverse, col2pos)303self._PGammaL_autom_size = pr.get_autom_order_permutation()304self._PGammaL_autom_size *= pr.get_autom_order_inner_stabilizer()305self._full_autom_order = self._PGammaL_autom_size306self._PGammaL_autom_size /= (len(self.C.base_ring()) - 1)307elif mat.nrows() == len(col_set):308# it could happen (because of the recursive call, see `else`) that309# the canonical form is the identity matrix310n = len(col_set)311from sage.modules.free_module import VectorSpace312can_col_set = VectorSpace(F, n).gens()313A = []314self._full_autom_order = 1315S_short = SemimonomialTransformationGroup(F, n)316can_transp = S_short.one()317if algorithm_type != "permutational":318# linear or semilinear319if algorithm_type == "semilinear":320A.append(S_short(autom=F.hom([F.gen() ** F.characteristic()])))321self._full_autom_order *= F.degree()322for p in P_refined:323m = [F.one()] * n324m[p[0]] = F.gen()325A.append(S_short(v=m))326self._full_autom_order *= (len(F) - 1) ** n327for p in P_refined:328if len(p) > 1:329A.append(S_short(perm=_cyclic_shift(n, p[:2])))330if len(p) > 2:331# cyclic shift of all elements332A.append(S_short(perm=_cyclic_shift(n, p)))333self._full_autom_order *= factorial(len(p))334self._PGammaL_autom_size = self._full_autom_order / (len(F) - 1)335self._PGammaL_autom_gens = self._compute_PGammaL_automs(A,336normalization, normalization_inverse, col2pos)337else:338# use the dual code for the computations339# this might have zero columns or multiple columns, hence340# we call this algorithm again.341short_dual_code = LinearCode(matrix(col_set).transpose()).dual_code()342agcl = LinearCodeAutGroupCanLabel(short_dual_code,343P=P_refined, algorithm_type=algorithm_type)344can_transp = agcl.get_transporter()345can_transp.invert_v()346can_col_set = agcl.get_canonical_form().check_mat().columns()347A = agcl.get_autom_gens()348for a in A:349a.invert_v()350self._PGammaL_autom_gens = self._compute_PGammaL_automs(A,351normalization, normalization_inverse, col2pos)352self._PGammaL_autom_size = agcl.get_PGammaL_order()353self._full_autom_order = agcl.get_autom_order()354355count = 0356block_ptr = []357canonical_form = matrix(F, mat.ncols(), mat.nrows())358359perm = [-1] * mat.ncols()360mult = [F.one()] * mat.ncols()361362for i in range(len(can_col_set)):363img = can_transp.get_perm()(i + 1)364for j in col2pos[img - 1]:365pos = P[ pos2P[j] ].pop()366canonical_form[ pos ] = can_col_set[i]367mult[pos] = can_transp.get_v()[i]368perm[pos] = j + 1369370it = iter(z)371for p in P:372while len(p) > 0:373pos = p.pop()374perm[pos] = it.next() + 1375376self._canonical_form = LinearCode(canonical_form.transpose())377self._transporter = S(perm=Permutation(perm), v=mult, autom=can_transp.get_autom()) * normalization378self._trivial_autom_gens, a = self._compute_trivial_automs(normalization,379normalization_inverse, z, [pos2P[x] for x in z], zero_column_case=True)380self._full_autom_order *= a381382383for i in range(len(col2P)):384if len(col2P[i]) > 1:385A, a = self._compute_trivial_automs(normalization,386normalization_inverse, col2pos[i], col2P[i])387self._full_autom_order *= a388self._trivial_autom_gens += A389390@staticmethod391def _compute_trivial_automs(normalization, normalization_inverse, col2pos, col2P, zero_column_case=False):392"""393In order to call the algorithm described in394:class:`PartitionRefinementLinearCode` we remove395zero columns and multiple occurrences of the same column (up to396normalization).397398This function computes a set of generators for the allowed permutations399of such a block of equal columns (depending on the partition).400If ``zero_column_case==True`` we also add a generator for the401multiplication by units. Furthermore, we return the order of this group.402403INPUT:404405- ``normalization`` -- an element in the semimonomial transformation group `S`406407- ``normalization_inverse`` -- the inverse of ``normalization``408409- ``col2pos`` -- a list of disjoint indices in ``range(n)``410411- ``col2P`` -- an increasing list of integers, with412``len(col2P) == len(col2pos)`` with ``col2P[i] == col2P[j]`` if and413only if the indices ``col2pos[i]`` and ``col2pos[j]`` are in the414same partition415416- ``zero_column_case`` (boolean) -- set to ``True`` iff we are dealing417with the zero column418419OUTPUT:420421- a list of generators in `S`422423- the order of this group424425EXAMPLES::426427sage: from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel428sage: S = SemimonomialTransformationGroup(GF(3), 10)429sage: s = S.one()430sage: col2pos = [1,4,2,3,5]431sage: col2P = [1,1,3,3,3]432sage: LinearCodeAutGroupCanLabel._compute_trivial_automs(s, s, col2pos, col2P, True)433([((1, 2, 1, 1, 1, 1, 1, 1, 1, 1); (), Ring endomorphism of Finite Field of size 3434Defn: 1 |--> 1), ((1, 1, 1, 1, 1, 1, 1, 1, 1, 1); (2,5), Ring endomorphism of Finite Field of size 3435Defn: 1 |--> 1), ((1, 1, 2, 1, 1, 1, 1, 1, 1, 1); (), Ring endomorphism of Finite Field of size 3436Defn: 1 |--> 1), ((1, 1, 1, 1, 1, 1, 1, 1, 1, 1); (3,4), Ring endomorphism of Finite Field of size 3437Defn: 1 |--> 1), ((1, 1, 1, 1, 1, 1, 1, 1, 1, 1); (3,4,6), Ring endomorphism of Finite Field of size 3438Defn: 1 |--> 1)], 384)439"""440S = normalization.parent()441F = S.base_ring()442n = S.degree()443444beg = 0445if zero_column_case:446aut_order = (len(F) - 1) ** len(col2P)447else:448aut_order = 1449A = []450while beg < len(col2P):451if zero_column_case:452mult = [F.one()] * n453mult[col2pos[beg]] = F.primitive_element()454A.append(S(v=mult))455P_indx = col2P[beg]456j = beg + 1457while j < len(col2P) and col2P[j] == P_indx:458j += 1459460if j - beg > 1:461aut_order *= factorial(j - beg)462# we append a transposition of the first two elements463A.append(normalization_inverse *464S(perm=_cyclic_shift(n, col2pos[beg:beg + 2])) * normalization)465if j - beg > 2:466# we append a cycle on all elements467A.append(normalization_inverse *468S(perm=_cyclic_shift(n, col2pos[beg:j])) * normalization)469beg = j470return A, aut_order471472@staticmethod473def _compute_PGammaL_automs(gens, normalization, normalization_inverse, col2pos):474"""475In order to call the algorithm described in476:class:`sage.coding.codecan.codecan.PartitionRefinementLinearCode` we removed477zero columns and multiple occurrences of the same column (up to normalization).478This function lifts the generators ``gens`` which were returned to their full length.479480INPUT:481482- ``gens`` -- a list of semimonomial transformation group elements of length `m`483484- ``normalization`` -- a semimonomial transformation of length `n`485486- ``normalization_inverse`` -- the inverse of ``normalization``487488- ``col2pos`` -- a partition of ``range(n)`` into `m` disjoint sets,489given as a list of lists. The elements `g` in ``gens`` are only490allowed to permute entries of ``col2pos`` of equal length.491492OUTPUT:493494- a list of semimonomial transformations containing495``normalization`` which are the lifts of the elements in ``gens``496497EXAMPLES::498499sage: from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel500sage: S = SemimonomialTransformationGroup(GF(3), 10)501sage: T = SemimonomialTransformationGroup(GF(3), 5)502sage: s = S.one()503sage: col2pos = [[1,4,2,3,5], [0],[6],[8],[7,9]]504sage: gens = [T(perm=Permutation([1,3,4,2,5]), v=[2,1,1,1,1])]505sage: LinearCodeAutGroupCanLabel._compute_PGammaL_automs(gens, s, s, col2pos)506[((1, 2, 2, 2, 2, 2, 1, 1, 1, 1); (1,7,9), Ring endomorphism of Finite Field of size 3507Defn: 1 |--> 1)]508"""509S = normalization.parent()510n = S.degree()511A = []512for g in gens:513perm = range(1, n + 1)514mult = [S.base_ring().one()] * n515short_perm = g.get_perm()516short_mult = g.get_v()517for i in range(len(col2pos)):518c = col2pos[i]519img_iter = iter(col2pos[short_perm(i + 1) - 1])520for x in c:521perm[x] = img_iter.next() + 1522mult[x] = short_mult[i]523A.append(normalization_inverse * S(perm=perm, v=mult, autom=g.get_autom()) * normalization)524return A525526def get_canonical_form(self):527"""528Return the canonical orbit representative we computed.529530EXAMPLES::531532sage: from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel533sage: C = codes.HammingCode(3, GF(3)).dual_code()534sage: CF1 = LinearCodeAutGroupCanLabel(C).get_canonical_form()535sage: s = SemimonomialTransformationGroup(GF(3), C.length()).an_element()536sage: C2 = LinearCode(s*C.gen_mat())537sage: CF2 = LinearCodeAutGroupCanLabel(C2).get_canonical_form()538sage: CF1 == CF2539True540"""541return self._canonical_form542543def get_transporter(self):544"""545Return the element which maps the code to its canonical form.546547EXAMPLES::548549sage: from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel550sage: C = codes.HammingCode(3, GF(2)).dual_code()551sage: P = LinearCodeAutGroupCanLabel(C)552sage: g = P.get_transporter()553sage: D = P.get_canonical_form()554sage: (g*C.gen_mat()).echelon_form() == D.gen_mat().echelon_form()555True556"""557return self._transporter558559def get_autom_gens(self):560"""561Return a generating set for the automorphism group of the code.562563EXAMPLES::564565sage: from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel566sage: C = codes.HammingCode(3, GF(2)).dual_code()567sage: A = LinearCodeAutGroupCanLabel(C).get_autom_gens()568sage: Gamma = C.gen_mat().echelon_form()569sage: all([(g*Gamma).echelon_form() == Gamma for g in A])570True571"""572return self._PGammaL_autom_gens + self._trivial_autom_gens573574def get_autom_order(self):575"""576Return the size of the automorphism group of the code.577578EXAMPLES::579580sage: from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel581sage: C = codes.HammingCode(3, GF(2)).dual_code()582sage: LinearCodeAutGroupCanLabel(C).get_autom_order()583168584"""585return self._full_autom_order586587588def get_PGammaL_gens(self):589r"""590Return the set of generators translated to the group `P\Gamma L(k,q)`.591592There is a geometric point of view of code equivalence. A linear593code is identified with the multiset of points in the finite projective594geometry `PG(k-1, q)`. The equivalence of codes translates to the595natural action of `P\Gamma L(k,q)`. Therefore, we may interpret the596group as a subgroup of `P\Gamma L(k,q)` as well.597598EXAMPLES::599600sage: from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel601sage: C = codes.HammingCode(3, GF(4, 'a')).dual_code()602sage: A = LinearCodeAutGroupCanLabel(C).get_PGammaL_gens()603sage: Gamma = C.gen_mat()604sage: N = [ x.monic() for x in Gamma.columns() ]605sage: all([ (g[0]*n.apply_map(g[1])).monic() in N for n in N for g in A])606True607"""608Gamma = self.C.gen_mat()609res = []610for a in self._PGammaL_autom_gens:611B = Gamma.solve_left(a * Gamma, check=True)612res.append([B, a.get_autom()])613614return res615616def get_PGammaL_order(self):617r"""618Return the size of the automorphism group as a subgroup of619`P\Gamma L(k,q)`.620621There is a geometric point of view of code equivalence. A linear622code is identified with the multiset of points in the finite projective623geometry `PG(k-1, q)`. The equivalence of codes translates to the624natural action of `P\Gamma L(k,q)`. Therefore, we may interpret the625group as a subgroup of `P\Gamma L(k,q)` as well.626627EXAMPLES::628629sage: from sage.coding.codecan.autgroup_can_label import LinearCodeAutGroupCanLabel630sage: C = codes.HammingCode(3, GF(4, 'a')).dual_code()631sage: LinearCodeAutGroupCanLabel(C).get_PGammaL_order() == GL(3, GF(4, 'a')).order()*2/3632True633"""634return self._PGammaL_autom_size635636637