def make_theta_table(): r"""Creates the theta function for the Parker Loop theta() is a function from the Golay code C to the Golay cocode C*. Multiplication '(*)' in the Parker loop is defined by: a (*) b = (a ^ b) * (-1) ** scalar(theta(a),b) , Where '^' is the vector addition in GF(2)**12, and scalar(.,.) is the scalar product defined on C x C*. More specifically, theta is a quadratic form from C onto C*. The basis cc_i, i=0,...,11 of the cocode defined here is reciprocal to the basis c_i, i=0,...,11 of the cocode defined here. Define theta(i)[j] = theta(i) * c_j. Then theta(i) = sum[1 << theta(i)[j] for j in range(12) ]. To define theta as a quadratic form, it suffices to define theta on the basis vectors. We define theta on the basis vectors as given by function theta_to_basis_vector(v). There is a symmetric bilinear form B : from C x C onto C* associated with theta on all pairs of basis vectors satisfying: B(x,y) = theta(x+y) + theta(x) + theta(y) . B(x,y) = x :math:`\cap` Y Here :math:`\cap means intersection (i.e. bitwise 'and' in GF(2)**24) of two Golay code words. This allows us to compute theta(x), for all Golay code words, if theta is given on a basis of the Golay code. """ assert Mat24Tables.gcode_to_vect(0x800) == 0xffffff theta_table = numpy.zeros(0x800, dtype = uint16) thetas = theta_basis_vectors() for i in range(11): theta_table[1 << i] = Mat24Tables.vect_to_cocode(thetas[i]) for i in range(0x800): if (i & (i-1)) != 0: # i.e. if i is not 0 or a power of 2 i0 = 1 << v2(i) i1 = i ^ i0 cap = Mat24Tables.gcode_to_vect(i0) cap &= Mat24Tables.gcode_to_vect(i1) cc = Mat24Tables.vect_to_cocode(cap) theta_table[i] = theta_table[i0] ^ theta_table[i1] ^ cc return theta_table
def superoctads(contained, not_contained, partial = [], weight = 0): """Return a specific list of octads The function returns a specific list of octads, where each octad is given as a 24-bit integer describing a bit vector in ``vector`` representation. An octad is included in the list if it satisfies the following requiremets. * All entries in the list ``contained ``must be contained in the octad. * No entry in the list ``not_contained`` may be containe in the octad. * Precisely ``weight`` entries of the list ``partial`` must be in the octad. Each parameter ``contained `, ``not_contained``, or ``weight`` may be a list of different integers < 24 or a 24-bit integer representing a bit vetor. """ if not isinstance(contained, int): contained = list_to_set24(contained) if not isinstance(not_contained, int): not_contained = list_to_set24(not_contained) if not isinstance(partial, int): partial = list_to_set24(partial) return [x for x in octad_list() if (x & contained == contained) and (x & not_contained == 0) and (gc.bw24(x & partial) == weight) ]
def augment_theta_table(theta_table): """store bitweight of Golay code word i in table[i/2], bits 14..12""" for i in range(0x800): w = bw24(Mat24Tables.gcode_to_vect(i)) assert w in [0,8,12,16] theta_table[i] |= (w >> 2) << 12 return theta_table
def odd_syn(v): """Return Golay cocode syndrom of a bit vector v The bit vector v must have odd parity. """ coc = gc.vect_to_vintern(v) t = gc.syndrome_table[coc & 0x7ff ] return ( (1 << (t & 31)) ^ (1 << ((t >> 5) & 31)) ^ (1 << ((t >> 10) & 31)) )
def py_mat24_int_to_perm(n): """Return the k-th permutation p in the Mathieu group Mat24 Here the elements of the Mathieu group Mat24 are numbererd in lexicographic order. Any integer 0 <= n < 244823040 is evaluated in mixed-radix with bases 24, 23, 22, 21, 20, 3, 16, with valence decreasing from left to right. The first five digits determine the images of the first five elements of the permutation. The next digit determines the image of entry 5. That entry has 3 possible images, depending on the syndrome of the images of the first five elements. The final digit determines the image of element 8, i.e. the first element not in the standard octad. The images of the remaining elements 6, 7 and 9,...,23 are determined by calling function complete_heptad() This implementation of the function is easy to understand but not fast. Function ``mat24_int_to_perm`` is a faster version of this function. """ assert 0 <= n < MAT24_ORDER # Compute the output permutation in ``p`` p = [None] * 24 # List of elements to be permuted by Mat24 p_list = list(range(24)) # Convert n to mixed-radix digit list ``n_list`` as described above. n_list = split_n_mat24(n) # Compute the images of the first five numbers 0,...,4 under p # from the the first five digits of the digit list. # After assigning an image, delete that image from ``n_list``, # in order to avoid repetitions. for i in range(5): index = n_list[i] p[i] = p_list[index] del p_list[index] # Compute the syndrome these five images in the list ``syn``. # That syndrome has length 3. bitmap = sum(1 << i for i in p[:5]) cocode = gc.vect_to_cocode(bitmap) syn_tab = gc.syndrome_table[cocode & 0x7ff] syn = [(syn_tab >> i) & 31 for i in [0, 5, 10]] # Select image of the number 5 from that syndrome, # using entry 5 f the digit list ``n_list`` p[5] = syn[n_list[5]] # Let p_list be the list of number 0 <= i < 24 that are not images # any of the numbers of [0,1,2,3,4] and not in the syndrome. bitmap |= sum(1 << i for i in syn) p_list = [i for i in range(24) if ((1 << i) & bitmap) == 0] # Select image of the number 8 from that list, # using entry 6 f the digit list ``n_list`` p[8] = p_list[n_list[6]] mat24_complete_heptad(p) return p
def octad_list(): """Return the sorted list of all octads Each octad is given as a 24-bit integer describing a bit vector in `vector`` representation. Octads are sorted lexicographically, so lower bits of the integer are sorted with higher priority. """ global _OCTAD_LIST if _OCTAD_LIST is not None: return _OCTAD_LIST[:] l_oct = [set24_to_list(gc.octad_to_vect(o)) for o in range(759)] l_oct.sort() _OCTAD_LIST = [list_to_set24(l) for l in l_oct] return _OCTAD_LIST[:]
class Mat24(Mat24Tables): r"""Provide functions for the Mathieu group Mat24 and the Parker loop. These functions are exported as class methods. They are considered as pure python substitutes for the functions in the ``mmgroup.mat24`` extension. So they can be used in an early stage of the build process where that extension is not yet available. The are also used for testing the ``mmgroup.mat24`` package. See module ``mmgroup.dev.mat24.mat24_doc`` for an overview of the functionality of the ``mmgroup.mat24`` extension. This class is also a table-providing class used by the code generator for generating C code containing the functionality of the ``mmgroup.mat24`` extension. For details, see section *How the code generator is used* in *The mmgroup guide for developpers*. """ ## Create tables for faster computetion MAT24_ORDER = 244823040 matrix_to_perm_ = MatrixToPerm(Mat24Tables.basis[12:]) heptad_completer = HeptadCompleter(Mat24Tables) theta_table = make_augmented_theta_table() autpl_qf_table = make_autpl_qf_table(theta_table) autpl_qf_table64 = make_autpl_qf_table(theta_table, 64) verbose = False basis_weights_8 = Mat24Tables.basis_weights_8() # recip_basis(i & 31) shall not fail in C recip_basis_c = numpy.append(Mat24Tables.recip_basis.copy(), [0]*8) ## Collect tables and coding functions for generating C code tables = { "Mat24_enc_table0" : Mat24Tables.enc_table0, "Mat24_enc_table1" : Mat24Tables.enc_table1, "Mat24_enc_table2" : Mat24Tables.enc_table2, "Mat24_dec_table0" : Mat24Tables.dec_table0, "Mat24_dec_table1" : Mat24Tables.dec_table1, "Mat24_dec_table2" : Mat24Tables.dec_table2, "Mat24_basis" : Mat24Tables.basis, "Mat24_recip_basis" : recip_basis_c, "Mat24_syndrome_table" : Mat24Tables.syndrome_table, "Mat24_oct_enc_table" : Mat24Tables.oct_enc_table, "Mat24_oct_enc_offset" : Mat24Tables.oct_enc_offset, "Mat24_oct_dec_table" : Mat24Tables.oct_dec_table, "Mat24_theta_table" : theta_table, "Mat24_autpl_qf_table" : autpl_qf_table, "Mat24_autpl_qf_table64" : autpl_qf_table64, "Mat24_doc" : Mat24__doc__, "INT_BITS" : config.INT_BITS, "Mat24_basis_weights_8" : basis_weights_8, } directives = {} _subgenerators = [ Lsbit24Function, matrix_to_perm_, heptad_completer, BitMatrixMulFix(), BitMatrixMulTransp(), ] for gen in _subgenerators: tables.update(gen.tables()) directives.update(gen.directives()) @classmethod def str_basis(cls, with_reciprocal_basis = False): def show(text, basis): s = text + "\n" for i,x in enumerate(basis): if i % 6 == 0: s += " " s += "%06x " % x if i % 6 == 5: s += "\n" return s s = "We list the basis vectors of the Golay code and of its cocode." s += """ Basis vectors have indices 0,...,11. Each basis vector is displayed as a hexadecimal number with bit i (of valence 2**i) corresponding to component i of the basis vector in GF(2)^24 for i = 0,...,23. Golay cocode vectors are to be understood modulo the Golay code. """ s += show( "Golay cocode basis", cls.basis[:12] ) s += show( "Golay code basis" , cls.basis[12:] ) if with_reciprocal_basis: s += show( "Reciprocal basis", cls.recip_basis ) return s @classmethod def show_basis(cls, with_reciprocal_basis = True): print(cls.str_basis(with_reciprocal_basis)) ########################################################################### # Parker Loop ########################################################################### @classmethod def ploop_theta(cls, v1): """Return the theta function for the Parker loop. theta is a quadratic from from the Golay code C to the cocode C*. Here parameter v1 of function theta is represented as a Golay code word. The result of the function is represented as a Golay cocode word. The cocycle of the Parker loop is given by: cocycle(v1,v2) = scalar(theta(v1), v2) with scalar(.,.) the scalar product. """ return cls.theta_table[v1 & 0x7ff] & 0xfff @classmethod def ploop_cocycle(cls, v1, v2): """Return the cocycle of the Parker loop. Then the Parker Loop product is given by v1 (*) v2 = v1 ^ v2 * (-1)**cocycle(v1, v2) . """ s = cls.ploop_theta(v1) & v2 & 0xfff s ^= s >> 6 s ^= s >> 3 s = 0x96 >> (s & 7) return s & 1 @classmethod def mul_ploop(cls, v1, v2): """Return the Parker loop product v1 (*) v2 Here v1 and v2 are integers coded as follows: bit 0,...,11: representation as Golay code word bit 12: Parker loop sign otther bits: ignored """ return v1 ^ v2 ^ (cls.ploop_cocycle(v1, v2) << 12) @classmethod def pow_ploop(cls, v1, u_exp): """Return power v1 ** u_exp of the Parker loop element v1.""" return (v1 & -(u_exp & 1)) ^ ( cls.theta_table[v1 & 0x7ff] & ((u_exp & 2) << 11) ) @classmethod def ploop_comm(cls, v1, v2): """Return commutator of Golay code word v1 and v2. This is 0 if the intersection of the vectors v1 and v2 has bit weight 0 mod 4 and 1 is that intersection has bit weight 2 mod 4. v1 and v2 are in 'gvect' or 'ploop' representation. """ cap = cls.gcode_to_vect(v1) & cls.gcode_to_vect(v2) return (cls.bw24(cap) >> 1) & 1 @classmethod def ploop_assoc(cls, v1, v2, v3): """Return associator of Golay code words v1, v2 and v3 This the parity of the intersection of the vectors v1, v2 and v3. v1, v2 and v3 are in 'gvect' or 'ploop' representation. """ assoc = (cls.gcode_to_vect(v1) & cls.gcode_to_vect(v2) & cls.gcode_to_vect(v3)) return cls.bw24(assoc) & 1 @classmethod def ploop_cap(cls, v1, v2): """Return intersection of two Golay code words as cocode word. v1 and v2 are in 'gvect' or 'ploop' representation, the result is returned in 'cocode' representation. """ return ( cls.theta_table[(v1 ^ v2) & 0x7ff] ^ cls.theta_table[v1 & 0x7ff] ^ cls.theta_table[v2 & 0x7ff] ) & 0xfff @classmethod def ploop_solve(cls, a): """Return cocode element that kills signs of Parker loop elements Here 'a' is an array of Parker loop elements. The function tries to find a cocode element that makes all these Parker loop elements positive, when operating on them as a diagonal automorphism. The function returns the least cocode element in lexical order satisfying that condition. For that order we assume that lower bits have higher valence. If no such cocode element exists, ValueError is raised. """ a1 = [x & 0x1fff for x in a] basis, columns = pivot_binary_low(a1) res = 0 for b, col in zip(basis, columns): res |= ((b >> 12) & 1) << col ########################################################################### # Mathieu group M24: conversion between representations ########################################################################### @classmethod def perm_complete_heptad(cls, p_io): """Complete a permutation p given by p_io to an element of Mat24. p must be a list of length 24. Entries p[i], i = 0,1,2,3,4,5,8. must make up a valid umbral heptad, i.e. a heptad not contained in an octad. p[0],...,p[5] must be contained in an octad, p[8] must not be contained in that octad. The other entries of input p are ignored. It can be shown that such a permutation p can be completed to a unique element of Mat24. The function returns 0 in case of success and raises ValueError otherwise. In case of success, p_io is completed to an element of the Mathieu group Mat24. """ if cls.heptad_completer.complete_heptad(p_io) != 0: raise ValueError("Could not complete permutation in Mat24") @classmethod def perm_check(cls, p1): """Check if permutation p1 is in in the Mathieu group Mat24. The function returns zero iff this is the case. """ p2 = list(p1[:]) if cls.perm_complete_heptad(p2): return -1 return p2 != list(p1[:]) @classmethod def perm_complete_octad(cls, p_io): """Complete an octad given by 6 elements of it. This is a simplified version of function perm_complete_heptad(). We complete an octad from entries p_io[i], i = 0,1,2,3,4,5. We store the remaining entries of the octad in p_io[6], p_io[7]. The order of these two entries is such that there exists a permutation in Mat24 that maps from the (ordered) standard octad 0,...,7 to p_io[0],...,p_io[7]. If p_io[5] is None, that it is set to the lowest possible value such that there is an octad p_io[0],...,p_io[7]. """ assert len(p_io) >= 8 p = list(p_io[:8]) + [None] * 16 v = sum([1 << i for i in p[:5]]) syn = cls.syndrome(v) if p_io[5] in (24, None): p[5] = cls.lsbit24(syn) p[8] = cls.lsbit24((v | syn) ^ 0xffffff) cls.perm_complete_heptad(p) p_io[6:8] = p[6:8] @classmethod def perm_from_heptads(cls, h1, h2): """Try to find a permutation p that maps heptad h1 to h2 h1 and h2 must be lists of length 7 defining two umbral heptads, i.e. heptads not contained in an octad. If a permutation p in the Mathieu group Mat24 that maps h1 to h2 exists, it is unique. The function returns p if such a p exists an is unique and it returns None otherwise. """ return cls.heptad_completer.perm_from_heptads(h1, h2) @classmethod def m24num_to_perm(cls, u_m24): """Return permutation with number u_m24 in the Mathieu group Mat24. The inverse of this function is member function Mat24_perm_to_int() This is just a short and convenient way to number elements of Mat24. Input u_m24 = 0 gives the identity permutation. 0 <= u_m24 < 244823040 = order(Mat24) must hold. For internal operation see mat24Heptad.HeptadCompleter.int_to_perm """ p = cls.heptad_completer.int_to_perm(u_m24) return p @classmethod def perm_to_m24num(cls, p1): """Convert permutation p1 in the Mathieu group Mat24 to an integer. This reverses member function int_to_perm(). The input permutation is not checked. """ return cls.heptad_completer.perm_to_int(p1[:]) @classmethod def perm_to_matrix(cls, p1): """Convert a permutation p1 in Mat24 to a matrix. The matrix is a 12 x 12 bit matrix acting on the Golay code vectors by right multiplication. Permutation p is not checked to be a member of the Mathieu group. """ mat = [cls.recip_basis[p1[i]] >> 12 for i in range(24)] return bit_mat_mul(cls.basis[12:], mat) @classmethod def matrix_to_perm(cls, m1): """Convert element of Mat24 from matrix to permutation. The matrix m1 is a 12 x 12 bit matrix acting on the Golay code vectors by right multiplication. The matrix is not checked. The permutation is represented as a list. """ basis = [cls.gcode_to_vect(v & 0xfff) for v in m1] return cls.matrix_to_perm_.compute(basis) ##################################################################### # Mathieu group M24: operation of group elements ##################################################################### @classmethod def op_vect_perm(cls, v1, p1): """Apply a permutation p1 to a vector v1 in GF(2)**24 Here p1 is the permutation that maps i to p1[i] for i=0,...,23. """ return sum([ ((v1 >> i) & 1) << p1[i] for i in range(24) ]) @classmethod def op_gcode_matrix(cls, v1, m1): """Apply the 12 x 12 bit matrix m1 to a Golay code vector Here application means right multiplication v1 * m1. The code vector v1 is given in 'gcode' representation. """ return reduce( __xor__, [x for i,x in enumerate(m1) if v1 & (1<<i)], 0 ) @classmethod def op_gcode_perm(cls, v1, p1): """Apply a permutation p1 to a Golay code vector v1 Here p1 is the permutation that maps i to p1[i] for i=0,...,23, representing an element of the Mathieu group M24. Golay code vector v1 is given in gcode representation. """ v1 = cls.gcode_to_vect(v1) v1 = cls.op_vect_perm(v1, p1) return cls.vect_to_vintern(v1) >> 12 @classmethod def op_cocode_perm(cls, c1, p1): """Apply a permutation p to a Golay cocode vector c1 Here p1 is the permutation that maps i to p1[i] for i=0,...,23, representing an element of the Mathieu group M24. Golay cocode vector c1 is given in cocode representation. """ v = c1 y = - (((v >> 11) + 1) & 1) # y = 0 if v is odd else -1 v ^= cls.recip_basis[0] & y # make v odd res = cls.recip_basis[p1[0]] & y # .. and adjust result syn = cls.syndrome_table[v & 0x7ff ] # get syndrome res ^= cls.recip_basis[p1[syn & 31]] # do 1st syndrome vector syn = (syn >> 5) & 0x3ff # mask out 2nd, 3rd synd. vector syn &= ((syn + 0x100) >> 10) - 1 # kill syn if v >= 24*32 res ^= cls.recip_basis[p1[syn & 31]] ^ cls.recip_basis[p1[syn >> 5]] # do 1st and 2nd syndrome vector return res & 0xfff @classmethod def mul_perm(cls, p1, p2): """Return p1 * p2, with p1, p2 in the Mathieu group M24 p1, p2 are represented as permutations """ return [ p2[p1[i]] for i in range(24) ] @classmethod def inv_perm(cls, p1): """Return inverse of p1, with p1 in the Mathieu group M24 p1, is represented as a permutations """ l = [None] * 24 for i, x in enumerate(p1): l[x] = i return l ########################################################################### # Automorphisms of the Parker Loop ########################################################################### @classmethod def autpl_set_qform(cls, m_io): """Recompute quadratic form on Parker loop automorphism m_io This functions augments the Parker loop automorphism m_io by a quadratic form qf. The form qf simplifies the application of m_io to Parker loop elements and also the multiplication of Parker loop automorphisms. The form qf is stored in bits 13,...,24 of the entries of m_io. At present the documatation of the mathematical background behind that form is out of the scope of the module. """ for i in range(12): qq = cls.ploop_theta(1 << i) & ((1 << i) - 1) s = cls.ploop_theta(m_io[i]) for j in range(i): t = s & m_io[j] t ^= t >> 6 t ^= t >> 3 qq ^= ((0x96 >> (t & 7)) & 1) << j m_io[i] = (m_io[i] & 0x1fff) ^ (qq << 13) return m_io @classmethod def perm_to_autpl(cls, c1, p1): """Combine Mat24 and cocode element to Parker loop automorphism Given an element p1 of the Mathieu group Mat24 (in permutation representation) and a Golay cocode element c1 (in cocode representation), the function returns a Parker loop automorphism m as a 12 x (12+13) matrix, i.e. in 'autpl' representation. m contains the 12 images of the basis vectors of the Parker loop and a quadratic form for simplfying its operation on Pl. """ m = cls.perm_to_matrix(p1) for i in range(12): m[i] ^= ((c1 >> i) & 1) << 12 return cls.autpl_set_qform(m) @classmethod def cocode_to_autpl(cls, c1): """Convert cocode element c1 to Parker loop automorphism Same as perm_to_autpl(c1, p), with p the identity permutation """ return [(1 << i) + (((c1 >> i) & 1) << 12) for i in range(12)] autpl_to_perm = matrix_to_perm @classmethod def autpl_to_cocode(cls, m1): """Extract cocode vector c from Parker loop automorphism m1 Then m1 = perm_to_autpl(c, p), where p is the permutation obtained by calling autpl_to_perm(m1). Note that m1 = cocode_to_autpl(c) * perm_to_autpl(0, p). """ return sum( ((m1[i] >> 12) & 1) << i for i in range(12) ) @classmethod def op_ploop_autpl(cls, v1, m1): """Apply Parker loop automorphism m1 to Parker Loop element v1 Here m1 is a Parker loop autmorphism (in autpl representation) and v1 is a cocde vector (in cocode representation). The function returns the resluting cocode vector v1 * m1. """ v = v1 t = v & 0x1000 for i in range(12): t ^= m1[i] & -((v >> i) & 1) v = (t >> 13) & v v ^= v >> 6 v ^= v >> 3 v = (0x96 >> (v & 7)) & 1 return (t & 0x1fff) ^ (v << 12) @classmethod def mul_autpl(cls, m1, m2): """Return product m1 * m2 of Parker loop automorphisms m1, m2 Here m1, m2 and the result is in 'autpl' representation. """ m3 = [cls.op_ploop_autpl(m1[i], m2) for i in range(12)] return cls.autpl_set_qform(m3) @classmethod def inv_autpl(cls, m1): """Return inverse of Parker loop automorphism m1 Here m1 and the inverse of it is in 'autpl' representation. """ p = cls.matrix_to_perm(m1) p = cls.inv_perm(p) mi = cls.perm_to_matrix(p) m0 = cls.mul_autpl(mi, m1) for i in range(12): assert mi[i] & -0x1000 == 0, map(hex, mi) assert m0[i] & 0xfff == 1 << i mi[i] ^= m0[i] & 0x1000 return cls.autpl_set_qform(mi) @classmethod def perm_to_iautpl(cls, c1, p1): """Return (inverse of p1, inverse of element of perm_to_autpl(c1, p1)) This will be optimized in the fast version. """
def mat24_perm_to_int(p1): """Convert permutation p1 in the Mathieu group Mat24 to an integer. This reverses member function mat24_int_to_perm(). The input permutation is not checked. The function returns an undefined integer if permutation p1 is not in the Mathieu group. """ # We reverse to operation of function ``mat24_int_to_perm``. # We compute the digits of the result n in mixed-radix # representation as described in function ``mat24_int_to_perm``. # Digits are taken from the images p1[m], m = 0,1,2,3,4,5,8 in # that order. # Get first digit k from p1[0] n = k = p1[0] # We also accumulate the images p1[0],...,p1[4] in a bitmap bitmap = 1 << k # For obtaining the next digits n_m from p1[m], m = 1,2,3, we # put n_m = p1[m] - d[m]. Here d[m] is a difference that must # be subtracted n_m in order to ensure 0 <= n_m < 24-m. # Computation of d[m] is a bit tricky. We keep the array # d[m], 0 <= m < 24 in the 64-bit integer d as a bit field, # reserving two bits at positions 2*m, 2*m+1 for d[m]. After # selecting the image k = p1[m], we put n_m = k - d[k]. Then we # have to replace d[u] by d[u + 1] + 1 for u > k. We don't # change d[u] for u < k. Since a permutation has no repetitions, # we may ignore d[k]. We start with d[u] = 0 for all u. # After putting n_0 = p1[0], we put d[u] = 1 for u >= n_0. d = DFIELD1 << (2*k) for i in [23, 22, 21]: # Store current entry of p1 in k and adjust bitmap k = p1[24-i] bitmap |= 1 << k # Set digit n_k = k - d[k] and compute next step for n n = i * n + k - ((d >> (2*k)) & 3) # Adjust bit field d: replace d[u] by d[u + 1] + 1 for u >= k. # Unfortunately, we would get an overflow in the last round. So # in the last round we replace replace d[u] by d[u + 1] instead, # and we remember the last value k in the variable ``last``. if i > 21: d += DFIELD1 << (2*k) else: last = k # Obtain digit n_4 from p[4]. This works in the same way as # assigning the previous imgaes; but here we have to increment # d[k] by one in case k >= last. k = p1[4] bitmap |= 1 << k n = 20 * n + k - ((d >> (2*k)) & 3) - (k >= last) # Now we have obtained the the digis n_m, 0 <= m < 5, from the # images p[m], 0 <= m < 5. An we have computed the bitmap of these # images in ``bitmap``. Next we compute the syndrome of these five # images in ``syn``. Here integer ``syn`` is to be interpreted as a # list of three bit fields of length 5. Each field contains an # entry of the syndrome. These entries are ordered. # Also let syn1 = syn[1] be the middle entry of that list. cocode = gc.vect_to_cocode(bitmap) syn = gc.syndrome_table[cocode & 0x7ff] syn1 = (syn >> 5) & 31 # Compute next digit n_5 = k from p[5]. Put k = 2 if p1[5] > syn1, # k = 1 if p1[5] == syn1, and k = 0 if p1[5] < syn1. k = int(p1[5] > syn1) + int(p1[5] >= syn1) # Enter that digit into the accumulated value n n = 3 * n + k # Enter all entries of the syndrome into the bitmap bitmap |= (1 << (syn & 31)) | (1 << syn1) bitmap |= (1 << ((syn >> 10) & 31)) # Delete all bits at positions >= p1[8] from the bitmap. The the # last digit of n ist the bit weight of that bit map. d = gc.bw24(((1 << p1[8]) - 1) & bitmap) # Enter the last digit into n n = 16 * n + p1[8] - d # Change n to zero if it is too large n & ((n >= MAT24_ORDER) - 1); return n
def mat24_int_to_perm(n): """Return the k-th permutation in the Mathieu group Mat24 Here the elements of the Mathieu group Mat24 are numbererd in lexicographic order, starting with 0. This is a fast version of function ``py_mat24_int_to_perm``. There is also a C version of this function. """ # See function ``py_mat24_int_to_perm`` for a simpler implementation # We document the tricks used to accelerate that implementation. assert 0 <= n < MAT24_ORDER # Compute the output permutation in ``p`` p = [None] * 24 # Compute ``n1`` such that ``k = n1 >> SH`` is the current digit in # the mixed-radix representation of n as described in function # ``py_mat24_int_to_perm``. For obtaining the next digit we subtract # k << SH from ``n1`` and then we multiply ``n1`` by ``i``, # for i = 23, 22, 21, 20, 3, 16 in that order. n1 = FACTOR24 * n # Let k be the first digit of n. Store this in p[0] p[0] = k = n1 >> SH n1 -= k << SH # process n1 for next digit # We also accumulate the images p[0],...,p[4] in a bitmap bitmap = 1 << k # For assigning the next images p[m], m = 1,2,3, we take the # m-th digit n_m from n and we put p[m] = n_m + d[m]. Here d[m] # is a difference that must be added to n_m in order to avoid # repetitions in the images of p[m], 0 <= m < 5. # Computation of d[m] is a bit tricky. We keep the array # d[m], 0 <= m < 24 in the 64-bit integer d as a bit field, # reserving two bits at positions 2*m, 2*m+1 for d[m]. After # selecting the image k = n_m, we put p[m] = k + d[k]. Then we # have to replace d[u] by d[u + 1] + 1 for u >= k. We don't # change d[u] for u < j. We start with d[u] = 0 for all u. # After putting p[0] = k, we put d[u] = 1 for u >= k. d = DFIELD1 << (2*k) for i in [23, 22, 21]: # Obtain next digit k of n and adjust n1 n1 = i * n1 k = n1 >> SH n1 -= k << SH # put p[24-i] = j = k - d[k] p[24-i] = j = k + ((d >> (2*k)) & 3) # Adjust bit field d: replace d[u] by d[u + 1] + 1 for u >= k. # Unfortunately, we would get an overflow in the last round. So # in the last round we replace replace d[u] by d[u + 1] instead, # and we remember the last value k in the variable ``last``. mask = (1 << (2*k)) - 1 if i > 21: d = (((d + DFIELD1) >> 2) & ~mask) + (d & mask) else: d = ((d >> 2) & ~mask) + (d & mask) last = k # Enter the image j into the bitmap bitmap |= 1 << j # Assign the image p[4]. This works in the same way as assigning # the previous imgaes; but here we have to increment d[k] by one # in case k >= last. n1 = 20 * n1 k = n1 >> SH n1 -= k << SH p[4] = k + ((d >> (2*k)) & 3) + (k >= last) bitmap |= 1 << p[4] # Now we have assigned the images p[m], 0 <= m < 5. An we have # computed the bitmap of these images in ``bitmap``. Next we # compute the syndrome of these five images in ``syn``. Here # integer ``syn`` is to be interpreted as a list of three bit # fields of length 5. Each field contains an entry of the # syndrome. These entries are ordered. cocode = gc.vect_to_cocode(bitmap) syn = gc.syndrome_table[cocode & 0x7ff] # Get next digit k from n. Assign the k-th entry of the # syndrome to p[5] k = (3 * n1) >> SH p[5] = (syn >> (5 * k)) & 31 # Enter all entries of the syndrome into the bitmap bitmap |= (1 << (syn & 31)) | (1 << ((syn >> 5) & 31)) bitmap |= (1 << ((syn >> 10) & 31)) # Complement the bitmap. So the bitmap has 16 bits set. bitmap ^= 0xffffff # Compute ordered list of positions of the bits set in the bitmap j = 0 p1 = [None] * 24 for i in range(24): p1[j] = i; # write index i to output pos. j += (bitmap >> i) & 1; # increment output pos. if bitmap[i]=1 # Let k (where k = n % 16) be the last digit of n. Assign the k-th # entry of that list to p[8]. p[8] = p1[n & 15] # Now we have assigned p[m], m = 0,1,2,3,4,5,8; and we procceed # as in in function ``py_mat24_int_to_perm``. mat24_complete_heptad(p) return p
p_io[23] = lsb24(sALN & ~sACE & ~sJLM); return err; ####################################################################### ####################################################################### # Function based on function complete_heptad() ####################################################################### ####################################################################### ####################################################################### # Compute the n-th element of the Mathieu group Mat24 ####################################################################### STD_OCTAD = gc.vect_to_octad(0xff) def split_n_mat24(n): """Auxiliary function for function ``py_mat24_int_to_perm`` This function converts the integer 0 <= n < 244823040 to mixed-radix notation with bases [16, 3, 20, 21, 22, 23, 24]. It returns the list of the 8 corresponding digits, with the highest digit first. """ n_list = [] for d in [16, 3, 20, 21, 22, 23, 24]: n, r = divmod(n, d)