The C interface of the mmgroup project

Introduction

This document describes the functionality of the C modules in this project. For most of these C modules there is also a python extension. Unless otherwise stated, each documented C function is wrapped by a Cython function with the same name and signature.

Note that almost all parameters of such a C function are declared as (signed or unsigend) integers, or as pointers to such integers. In python, a numpy array of appropriate dtype may be passed as an argument to a parameter delared as a pointer to an integer.

Description of the mmgroup.mat24 extension

The automatically generated file mat24_functions.c contains the C code for the functions exported by the python extension mmgroup.mat24.

These functions are documented in file mat24_functions.c. Here just give an overview of the functionality of that module.

File mat24_functions.c has been generated from the source file mat24_functions.ske using the code generator in module mmgroup.generate_c.

The functions in file mat24_functions.c perform basic computations in the Golay code, in its cocode, and in the Mathieu group Mat24. They also deal with Parker loop Pl and with its automorphism group AutPl. A more comfortable python interface to these objects is provided by the python classes GCode, Cocode, PLoop, and AutPL in module mmgroup.

All C functions in file mat24_functions.c start with the prefix mat24_. These functions are also called from C functions in other modules. Therefore we store the binary code for these functions in a shared library. For some of these functions there are also macros (defined with #define), starting with mat24_def_.

There is a one-to-one correspondence between the functions in mat24_functions.c and the function exported from the python extension mmgroup.mat24, see subsection Mapping C functions to python functions for details.

The python class Mat24 in module mmgroup.dev.mat24.mat24_ref contains pure python implementations of most functions of the mmgroup.mat24 extension as class methods. This class is used for testing and as a substitute for the mmgroup.mat24 extension in an early stage of the build process.

In the following subsections the term C functions refers to the C functions in file mat24_functions.c. In the documentation, the names of the C functions are given without the mat24_ prefix. The term API reference means the main document The mmgroup API reference of this project.

The Golay code C and its cocode C*

The Mathieu group Mat24 operates as a permutation group on a set of 24 elements which we label with numbers 0,…,23 for use in Python and C. So it also operates on a vector space V = GF(2)**24, with GF(2) = {0,1}. Here ** means exponentiation.

A vector v in a vector space over GF(2) is called a bit vector. We represent a bit vector as an integer, so that the i-th bit of v (with valence 2**i) is the i-th component of v.

The Golay code C ia a 12-dimensional subspace of V fixed by Mat24. There are functions for checking and completing codewords and for getting the syndrome of a 24-bit vector.

We internally use a basis of V such that the first 12 basis vectors are a transversal of the Golay cocode and the last 12 basis vectors span the Golay code. These basis vectors are listed in the API reference.

We represent vectors in V, C and C* and in the subset of octads of C as follows:

The 759 octads are numbered from 0 to 758. They do not form a vector space. The 2**12 Golay code words are represented as binary numbers 0 to 4095. The 2**12 cocode words are represented as binary numbers 0 to 4095. A more detailed description is given in the API reference.

As usual, binary numbers representing bit vectors are added with the XOR operation ^. Unused high bits in input bit vectors are ignored.

Functions changing an object from one representation xxx to another representation yyy are named xxx_to_yyy, where xxx, yyy is as follows:

vect:      standard representation of a bit vector in V = GF(2)**24
           coded as a 24-bit integer. 
vintern:   internal representation a bit vector in V as a vector in
           the basis given above, coded as 24-bit integer. 
gcode:     representation of a Golay code word in the basis given
           given above, coded as 12-bit integer. 
octad:     representation as an octad numbered from 0 to 758
           (in lexical order given by representation  'gcode')
cocode:    representation as a cocode word in the basis given above, 
           coded as 12-bit integer. 

All these representations are given as unsigned integers.

We implement the following conversion functions:

vect_to_vintern, vintern_to_vect, vect_to_cocode, 
vintern_to_vect, gcode_to_vect, cocode_to_vect.

Here irrelevant bits of the input are ignored. Function cocode_to_vect returns one of many possible solutions.

In the following functions the input is checked and the function fails in case of an error:

vect_to_gcode, vect_to_octad, gcode_to_octad,
octad_to_vect, octad_to_gcode

In case of failure, these C functions return a special value as indicated in the documentation of the function in the .c file. The corresponding python functions raise ValueError in case of failure.

Function syndrome() takes a vector v and calculates its syndrome, which is a vector of minimum weight equivalent to v modulo the Golay code. Function cocode_syndrome() takes a cocode representation of a cocode word instead.

Function scalar_prod() returns the scalar product of a Golay code vector in gcode and a cocode vector in cocode representation.

The Mathieu group Mat24

This class also contains support for the Mathieu group Mat24. An element of Mat24 can be represented in one of the following ways:

perm:    Representation as an array of length 24 encoding a 
         permutation of the integers 0,...,23 as a mapping.

m24num:  Representation as an integer 0 <= i < 244823040. Here i
         is the number of the permutation in lexicographic order.
         So the identity permutation is coded as 0. 

matrix:  Representation as a 12 x 12 bit matrix acting on the Golay
         code by right multiplication. This matrix acts on a Golay 
         code vectors (given in the 'gcode' representation) by
         right multiplication. 
         Such a matrix is implemented as an array of integers with
         each integer corresponding to a row vector of the matrix. 
         The purpose of this representation is to support 
         the Parker loop and its automorphism group. Therefore a
         row vector is implemented as a 32-bit integer.

We implement the following conversion functions:

m24num_to_perm, perm_to_m24num, perm_to_matrix, matrix_to_perm.

There is a function perm_check() for checking if an array of length 24 really represents an element of the Mathieu group Mat24. All other function operating on Mat24 in any way do not check if their inputs are really in Mat24. They will output garbage on bad input, but they are not supposed to crash.

The easiest way to create a random element of Mat24 is to create a random integer 0 <= x < 244823040, and to call function m24num_to_perm(x).

Operation of the group Mat24 on vectors

Elements of Mat24 operate from the right on vectors in V = GF(2)**24 or on Golay code or cocode vectors. A function performing such an operation has the name:

op_<vector>_<group>

where <vector> indicates the representation of the vector space and <group> indicates the representation of the group. We implement the functions:

op_vect_perm, op_gcode_matrix, op_gcode_perm, op_cocode_perm.

E.g. function op_gcode_matrix operates on a Golay code word (in gcode representation) by right multiplying an element m of Mat24 with it. Here element m is a 12 times 12 matrix (in matrix representation).

Group operation in the group Mat24

Multiplication and inversion in the group Mat24 is supported for the permutation representation perm. Therefore we have functions:

mul_perm, inv_perm

The Parker loop Pl

We support the Parker loop Pl and also its automorphism group AutPl.

An element of Pl is a pair (v, s), with v a Golay code word and s a sign bit, as described in the API reference. We represent the element (v, s) as a 13-bit integer, with v given by bits 0,…,11 (in gcode representation) and the sign s given by bit 12. We call this representation of the Parker loop the ploop representation. So we can convert and element of C in ‘gcode’ representation to an element of Pl in ploop representation by adjusting the sign in bit 12.

Function mul_ploop() returns the product of two elements of the Parker Loop. Function inv_ploop() returns the inverse of ab element of the Parker loop.

Let theta be the cocycle for the Parker loop defined in the API reference. For an element v1 of of C or Pl in gcode or ploop representation, the function ploop_theta(v1) returns the value theta(v1) (which is in C*) in cocode representation. Function ploop_cocode(v1, v2) returns the value of the coycle theta(v1, v2), which is 0 or 1.

The group AutPl of standard automorphisms of the Parker loop

An automorphism of the Parker loop is implemented as an array a of twelve 32-bit integers. The lowest 13 bits of a[i] encode the image of the i-th basis vector of the Parker loop. Here the basis of the Parker loop corresponds to the selected basis of the Golay code, and each basis vector has positive sign.

The bits 13,...,24 of the vectors a[i] encode a quadratic form which facilitates computations in AutPl, as described in section Implementing Automorphisms of the Parker loop in the Guide for developers.

This representation of AutPl is called the autpl representation. We only use the autpl representation for elements of AutPl.

Function perm_to_autpl(c, p) computes an automorphism m of the Parker loop created from an element p of Mat24 (given in perm representation) and a cocode element c (given in cocode representation). If m is equal to the result of perm_to_autpl(c, p), then we can get back p and c be computing p = autpl_to_perm(m) and c = autpl_to_cocode(m).

Function cocode_to_autpl(c) is equivalent to function perm_to_autpl(c, p0), where p0 is the identity permutation. Note that:

perm_to_autpl(c, p) = cocode_to_autpl(c) * perm_to_autpl(0, p).   

Here perm_to_autpl(0, p) is equivalent to standard representative of p in AutPl, and cocode_to_autpl(c) is a diagonal automorphism, as described in section Automorphisms of the Parker loop of the API reference.

Function op_ploop_autpl(v, m) applies Parker loop automorphism m to element v of Pl and returns the result.

Function mul_autpl(m1, m2) computes the product m1 * m2 of the Parker loop automorphisms m1 and m2. Function inv_autpl(m1) computes the inverse of the Parker loop automorphism m1.

Auxiliary functions

Here is an overview of some auxiliary functions in this class. They are described in the corresponding function documentation::

bw24              bit weight of the lowest 24 bits of an integer
lsbit24           min(24, least significant bit pos.) for an integer 
gcode_weight      weight of a Golay code word in 'gtype' representation
vect_to_bit_list  given a bit vector in V, it computes the lists of
                  the positions of the 0 bits and of the 1 bits of v.
extract_b24       extract bits from bit vector using a 24-bit mask
spread_b24        spread bit vector according to a 24-bit mask

Internal operation

For switching from the standard representation to the internal representation we use three tables with 2**8 entries of 24 bit length. For switching back from internal to standard representation we use three other tables of the same format. There are also tables for computing the syndrome of a vector in V with respect to the Golay code. There is yet another table for the cocycle theta of the Parker loop.

Abbreviations for functions and parameters in this class

The following list of abbreviations used in names of functions allows to infer the action of most functions in this module:

Abbreviation  Meaning                                   Data type

assoc         associator (in Golay code or Parker loop)
autpl         automorphism of the Parker loop Pl        uint32_t[12]
bw24          bit weight of the lowest 24 bits of an int
cap           intersection (of Golay code elements)
cocode        element of Golay cocode C*                uint32_t
cocycle       cocycle:  Pl times Pl  ->  {0,1}
comm          commutator (in Golay code or Pl)
gcode         element of Golay code C                   uint32_t
inv           inversion (in Mat24, Pl, or AutPl)
lsbit24       least significant bit of an integer, 
              counting bits 0,...,23 only 
m24num        number of an element of Mat24             uint32_t         
matrix        element of Mat24 as binary matrix 
              acting on the Golay code C                uint32_t[12] 
mul           multiplication (in Mat24, Pl, or AutPl)
net           Benes network for an element of Mat24     uint32_t[9]
octad         number of an octad, i.e. a Golay code
              element of weight 8                       uint32_t
op            op_<vector>_<operation> means: 
              apply <operation> to <vector>  
op_all        apply operation to all vectors
perm          element of Mat24 as a permutation         uint8_t[24]
ploop         element of the Parker loop Pl             uint32_t    
pow           power operator (in Pl)
scalar_prod   scalar product (of Golay code and cocode)
suboctad      suboctad, see function suboctad_to_cocode
syndrome      syndrome (after decoding Golay code)      uint32_t 
theta         cocycle theta: Pl -> C^* in Parker loop
to            <x>_to_<y> means: return representation <y>
              of an object given in representation <x>
vect          vector in V = GF(2)**24                   uint32_t
vintern       vector in V, in internal representation   uint32_t

Conventions for parameters in C functions

Parameters of functions are either integers or arrays of integers. Here all integer types are unsigned and of fixed length, such as uint8_t, uint16_t or uint32_t.

The type of a parameter is given by a single letter in the name of the parameter:

Name  Meaning                                           Type
a     array specified in documentation of function      unspecified 
c     Golay cocode element, represented as 'cocode'     uint32_t
m     permutation in Mat24 or automorphism of Pl
      represented as a bit matrix                       uint32_t[12]
p     permutation in Mat24 represented as 'perm'        uint8_t[24] 
u_<x> unsigned integer, e.g.                            unspecified
       u_exp:   integer denoting an exponent
       u_m24:   number of a permutation in Mat24
       u_octad: number of octad, 0 < u_octad < 259
       u_width: integer denoting a bit width
v     vector in V, Golay code C or Parker loop Pl 
      represented as vect, vintern, gcode or ploop      uint32_t  

Integer input parameters have name u_<x>, e.g. u_m24, u_exp. An integer computed by a function is returned as return value. Input array parameters have a digit as a suffix, e.g.: v1, v2, m1. Output array parameters have the suffix _out, e.g.: p_out. Input/output array parameters have the suffix _io, e.g.: m_io.

Mapping C functions to python functions

All C functions in module mat24_functions are documented. This documentation is not repeated for the corresponding python functions in module mmgroup.mat24.

The rules for converting a C function to a python function are as follows:

  • To obtain the name of the python function, strip off the prefix mat24_ from the name of a C functions.

  • To obtain the tuple of input parameters for the python function, take the tuple of parameters of the C function and drop are parameters with suffix _out.

    In the corresponding python function, an iterable object may be passed where the C function expects a pointer to an integer. The minimum length of that iterable object is either clear from the context or documented in the C function.

  • To obtain the return value of the python function, check if the C function has any parameters with suffix _out. Then the sequence of returned objects is the sequence of these parameters, possibly preceded by the return value if the C function returns an integer value.

    As usual in python, a sequence of length 1 is returned as a single object, and a sequence of length >= 1 is returned as a tuple.

    A parameter with suffix _out is returned as a list of integers.

  • A C function may fail under certain circumstances.

    A failure of a function is indicated in the return value of the function. Details are given in the documentation of the function. The corresponding python function raises ValueError if the C function fails.

  • The python function drops the return value of the C function from the sequence of returned python objects in certain cases.

    If the documentation of the C function contains the phrase ‘Returns 0 in case of success and (anything else) in case of failure’ then the return value is just a status indicator and hence dropped by the python function.

    If the documentation of the C function contains a phrase like ‘Returns the length of the list xxx_out’ then the python function adjusts the length of that returned list appropriately and drops the return value.

  • Parameters with suffix _io refer to pointers in the C function and hence to iterables in the corresponding python function. Here the sufix _io means that the function may modify that iterable object.

C interface

Header files

File mat24_functions.h is the header file for mat24_functions.c.

Defines

MAT24_ORDER

Order of Mathieu group Mat24. This is equal to 244823040.

MAT24_SUBOCTAD_WEIGHTS

(MAT24_SUBOCTAD_WEIGHTS >> x) & 1 is halved weight of suboctad x (mod 2).

mat24_def_lsbit24(v1)

Macro version of function mat24_lsbit24.

mat24_def_lsbit24_pwr2(v1)

Special macro version of function mat24_lsbit24.

This is faster than mat24_def_lsbit24, but here v1 must be power of two.

mat24_def_parity12(v1)

Parity of vector v1 of 12 bits length.

Generate a sequence of statements that replaces v1 by the bit parity of v1 & 0xfff.

mat24_def_octad_to_gcode(o)

Eqivalent to mat24_def_octad_to_gcode(o)

mat24_def_octad_to_gcode(o) returns the number of the Golay code word corresponding to octad o. Parameter o is not checked.

mat24_def_gcode_to_octad(v)

Eqivalent to mat24_def_gcode_to_octad(v)

mat24_def_gcode_to_octad(v) returns the number of the octad corresponding to Golay code vector v, with v in gcode. It returns garbage if v is not an octad.

mat24_def_not_nonstrict_octad(v)

Check if v (or its complement) is an octad.

Returns 0 if v (or its complement) is an octad and 1 otherwise.

Vector v must be given in gcode representation

mat24_def_gcode_to_vect(v)

Convert Golay code element number v to a vector in GF(2)^24

Macro version of function mat24_gcode_to_vect.

mat24_def_syndrome_from_table(t)

Convert entry t of table MAT24_SYNDROME_TABLE to syndrome.

An entry t of the table MAT24_SYNDROME_TABLE encodes an odd cocode syndrome. The macro returns that syndrome as a bit vector.

mat24_def_suboctad_weight(u_sub)

Equivalent to mat24_suboctad_weight(u_sub)

Enums

enum mat24_rand_flags

Flags describing subgroups of the Mathieu group \(M_{24}\).

This enumeration contains flags describing some subgroups of the Mathieu group \(M_{24}\) fixing certain subsets (or sets of subsets) of the set \(\tilde{\Omega} = \{0,\ldots,23\}\) on which the group \(M_{24}\) acts. Intersetions of these subgroups may be described by combining these flags with the bitwise or operator |. For each flag we state the set being fixed.

Values:

enumerator MAT24_RAND_2

fixes \(\{2, 3\} \)

enumerator MAT24_RAND_o

fixes \(\{0, \ldots,7 \}\)

enumerator MAT24_RAND_t

fixes \(\{\{8i,\ldots,8i+7\} \mid i < 3 \}\)

enumerator MAT24_RAND_s

fixes \(\{\{4i,\ldots,4i+3\} \mid i < 6 \}\)

enumerator MAT24_RAND_l

fixes \(\{\{2i, 2i+1\} \mid 4 \leq i < 12 \}\)

enumerator MAT24_RAND_3

fixes \(\{1, 2, 3\} \)

Functions

static inline uint32_t mat24_inline_cocode_to_suboctad(uint32_t c1, uint32_t v1, uint32_t u_strict)

Inline version of function mat24_cocode_to_suboctad

static inline uint32_t mat24_inline_suboctad_to_cocode(uint32_t u_sub, uint32_t u_octad)

Inline version of function suboctad_to_cocode

int32_t mat24_check_endianess(void)

Check endianess of the machine.

Return 0 if machine is little endian, 1 if it is Big endian, and -1 if endianess could not be detected.

C functions for the Mathieu group \(M_{24}\)

File mat24_tables.c contains tables that are used in the file mat24_functions.c.

Variables

const uint16_t MAT24_OCT_DEC_TABLE[759]

Table for converting octad to gcode representation.

The public macro mat24_def_octad_to_gcode uses this table

const uint16_t MAT24_OCT_ENC_TABLE[2048]

Table for converting gcode to octad representation.

The public macro mat24_def_gcode_to_octad uses this table

const uint16_t MAT24_THETA_TABLE[]

Table containing data about the Golay code.

For 0 <= d < 0x800 entry d contains the following information the code word d, with d in gcode representation.

Bit 11,…,0: mat24_ploop_theta(d)

Bit 14,…,12: Bit weight of code word d in GF(2)**24 divided by 4

Bit 15: reserved

We have d**2 = (-1)**<Bit 12 of entry d> for d in the Parker loop.

const uint8_t MAT24_OCTAD_ELEMENT_TABLE[759 * 8]

For 0 <= i < 759, the entries 8*i,...8*i+7 in the table MAT24_OCTAD_ELEMENT_TABLE are the bit positions of the octad with the number i.

File mat24_functions.c contains the C implementation of the functionality of Python module mmgroup.mat24

This covers the Golay code, its cocode, the Parker loop, the Mathieu group Mat24, and the group of standard automorphisms of the Parker loop.

Functions

uint32_t mat24_lsbit24(uint32_t v1)

Return position of least significant bit of an integer.

The function returns the minimum of the number 24 and the position of the least significant bit of v1. It uses a De Bruijn sequence.

uint32_t mat24_bw24(uint32_t v1)

Returns the bit weight of the lowest 24 bits of v1.

uint32_t mat24_vect_to_bit_list(uint32_t v1, uint8_t *a_out)

Stores the positions of 1-bits of a bit vector to an array.

Let w be the bit weight of the bit vector v1 & 0xffffff, i.e. number of bits of v1 at positions < 24 equal to one. Then the ordered bit positions where the corresponding bit of v1 is 1 are stored in a_out[0],...,a_out[w-1].

Then (v1 & 0xffffff) has 24 - w zero bits. The ordered list of the positions of these zero bits is stored in a_out[w],...,a_out[23].

The function returns the bit weight w.

uint32_t mat24_vect_to_list(uint32_t v1, uint32_t u_len, uint8_t *a_out)

Stores the positions of 1-bits of a bit vector to an array.

Let w be the minimum of the input u_len and the bit weight of the bit vector v1 & 0xffffff, i.e. number of bits of v1 at positions < 24 equal to one. Then the first w bit positions where the corresponding bit of v1 is 1 are stored in a_out[0],...,a_out[w-1] in natural order. The function returns w.

For small values w this function is faster than function mat24_vect_to_bit_list.

uint32_t mat24_extract_b24(uint32_t v1, uint32_t u_mask)

Extract the bits of 24-bit vector v1 given by the mask u_mask

If u_mask has bits equal to one at positions i_0, i_1, ..., i_k (in ascending order) then the bit of v1 at position i_j is copied to the bit at position j of the return value for j = 0,...,k.

uint32_t mat24_spread_b24(uint32_t v1, uint32_t u_mask)

Spread bits of 24-bit vector v1 according to the mask u_mask

If u_mask has bits equal to one at positions i_0, i_1, ..., i_k (in ascending order) then the bit of v1 at position j is copied to the bit at position i_j of the return value for j = 0,...,k.

uint32_t mat24_vect_to_vintern(uint32_t v1)

Convert bit vector v1 in GF(2)^24 from vector to vintern representation.

uint32_t mat24_vintern_to_vect(uint32_t v1)

Convert bit vector v1 in GF(2)^24 from vintern to vector representation.

uint32_t mat24_vect_to_cocode(uint32_t v1)

Return Golay cocode element corresponding to a bit vector in GF(2)^24.

This amounts to reducing the vector v1 (given in vector representation) modulo the Golay code. The function returns the cocode element corresponding to v1 in cocode representation.

uint32_t mat24_gcode_to_vect(uint32_t v1)

Convert Golay code element number v1 to a vector in GF(2)^24

Input v1 is a Golay code element in gcode representation. The function returns the bit vector corresponding to v1 in vector representation.

uint32_t mat24_cocode_to_vect(uint32_t c1)

Return a vector in GF(2)^24 corresponding to cocode element.

Here c1 is the number of a cocode element in cocode representation. One of 2**12 possible preimages of c1 in GF(2)^24 is returned in vector representation.

uint32_t mat24_vect_to_gcode(uint32_t v1)

Return a vector in GF(2)^24 as a Golay code element.

If the vector v1 (given in vector representation) is in the Golay code then the function returns the number of that Golay code word. Thus the return value is in gcode representation.

If v1 is not in the Golay code then the function returns (uint32_t)(-1).

uint32_t mat24_gcode_to_octad(uint32_t v1, uint32_t u_strict)

Return a Golay code vector as an octad.

If u_strict is even then the function acts as follows:

If the Golay code vector v1 (given in gcode representation) is an octad or a complement of an octad then the function returns the number of that octad. Thus the return value is in octad representation. Then we have 0 <= octad(v1, strict) < 759.

If v1 is not a (possibly complemented) octad then the function returns (uint32_t)(-1).

If u_strict is odd then the function returns (uint32_t)(-1) also in case of a complemented octad v1.

uint32_t mat24_vect_to_octad(uint32_t v1, uint32_t u_strict)

Return a vector in GF(2)^24 as an octad.

If u_strict is even then the function acts as follows:

If the vector v1 (given in vector representation) is an octad or a complement of an octad then the function returns the number of that octad. Thus the return value is in octad representation. Then we have 0 <= octad(v1, strict) < 759.

If v1 is not a (possibly complemented) octad then the function returns (uint32_t)(-1).

If u_strict is odd then the function returns (uint32_t)(-1) also in case of a complemented octad v1.

uint32_t mat24_octad_to_gcode(uint32_t u_octad)

Convert an octad to a Golay code vector.

Given an octad u_octad (in octad representation), the function returns the number of the corresponding Golay code number in gcode representation.

There are 759 octads. The function returns (uint32_t)(-1) in case u_octad >= 759.

uint32_t mat24_octad_to_vect(uint32_t u_octad)

Convert an octad to a bit vector in GF(2)^24.

Given an octad u_octad (in octad representation), the function returns bit vector corresponding to that octad in vector representation.

There are 759 octads. The function returns (uint32_t)(-1) in case u_octad >= 759.

uint32_t mat24_cocode_syndrome(uint32_t c1, uint32_t u_tetrad)

Return Golay code syndrome of cocode element c1.

Here c1 is a cocode element in cocode representation. mat24_cocode_syndrome(c1, u_tetrad) is equivalent to mat24_syndrome(mat24_cocode_to_vect(c1), u_tetrad).

The function returns a Golay code syndrome as described in the documentation of function mat24_syndrome().

uint32_t mat24_syndrome(uint32_t v1, uint32_t u_tetrad)

Return Golay code syndrome of word v1.

Here v1 is an arbitrary word in GF(2)**24 in vector representation. The function returns a Golay code syndrome of v1 (of minimum possible bit weight) as a bit vector in vector representation.

Such a syndrome is unique if it has weight less than 4. In that case the unique syndrome is returned.

If the minimum weight of the syndrome is four then the six possible syndroms form a partition of the the underlying set of 24 elements. In this case we return the syndrome of bit weight four where the bit at position u_tetrad is set. Therefore parameter u_tetrad must satisfy 0 <= u_tetrad <= 24; otherwise the function fails.

If the minimum weight of the sydrome is at most three, parameter u_tetrad must satisfy 0 <= u_tetrad < 24; otherwise the function fails.

The function returns (uint32_t)(-1) in case of failure.

uint32_t mat24_gcode_weight(uint32_t v1)

Returns bit weight of Golay code word v1 divided by 4.

Here 0 <= v1 < 4096 is the number of a Golay code word, i.e. v1 is given in gcode representation.

uint32_t mat24_gcode_to_bit_list(uint32_t v1, uint8_t *a_out)

Store bit positions of Golay code v1 in array a_out

Here 0 <= v1 < 4096 is the number of a Golay code word, i.e. v1 is given in gcode representation. The Golay code word v1 is stored in the array referred by a_out as an ordered list of the positions of the bits being set in the word v1.

That array must have physical length at least 24. The function returns the actual length of the returned array, which is equal to the bit weight of the word v1.

uint32_t mat24_cocode_weight(uint32_t c1)

Return the minimum possible weight of the cocode vector c1

Here c1 is a cocode element in cocode representation.

uint32_t mat24_cocode_to_bit_list(uint32_t c1, uint32_t u_tetrad, uint8_t *a_out)

Store Golay code syndrome of cocode word c1 in an array.

Here c1 is an cocode word in cocode representation. The function stores the sorted bit positions of the syndrome of c1 in the array referred by a_out and returns the actual length of that array, which is the weight of the syndrome. The array referred by a_out must have physical length at least 4.

Such a syndrome is unique if it has weight less than 4. In that case the unique syndrome is returned.

If the minimum weight of the syndrome is four then the six possible syndroms form a partition of the the underlying set of 24 elements. In this case we return the syndrome of bit weight four where the bit at position u_tetrad is set. Therefore parameter u_tetrad must satisfy 0 <= u_tetrad < 24; otherwise the function fails.

If the minimum weight of the sydrome is at most three, parameter u_tetrad must satisfy 0 <= u_tetrad <= 24; otherwise the function fails.

The function returns (uint32_t)(-1) in case of failure.

uint32_t mat24_cocode_to_sextet(uint32_t c1, uint8_t *a_out)

Store a cocode word c1 in array a_out as a sextet.

Here c1 is an cocode word in cocode representation. That cocode word must correspond to a syndrome of length four, i.e. the syndrome must be a tetrad. Otherwise the function fails.

The function stores the six tetrads that make up the sextet c1 in a_out[4*i],...,a_out[4*i+3] for i = 0,...,5. The (ordered) tetrads are stored in lexical order.

The function returns (uint32_t)(-1) if c1 has not minimum weight 4.

uint32_t mat24_scalar_prod(uint32_t v1, uint32_t c1)

Return scalar product of Golay code and cocode vector.

v1 is a Golay code vector in ‘gcode’ representation, c1 is a cocode vector in cocode representation.

Actually the function returns the bit parity of v1 & c1 & 0xfff.

uint32_t mat24_cocode_to_suboctad(uint32_t c1, uint32_t v1, uint32_t u_strict)

Convert cocode element c1 to suboctad of octad v1

The function converts a cocode element c1 (in cocode representation) and an octad v1 (in gcode representation) to a suboctad.

The function returns (o << 6) + u_sub. Here o is the octad number corresponding to octad v1 and u_sub is the suboctad number corresponding to the cocode element c1, if u_octad is an octad and c1 is an even subset of u_octad.

Each octad v1 has 64 even subsets, when each subset of v1 is identified with its complement in v1. These subsets are called suboctads. Let b_0, ..., b_7 be the elements of the octad v1 in the order as returned by applying function mat24_octad_entries to octad number o. Then the even subset (b_0 , b_i) has suboctad number 2**(i-1) for i = 1,...,6. Combining suboctads by symmetric difference corresponds to combining their numbers by xor. The empty subocatad has number zero. This yields a one-to-one correspondence between the integers 0,...,63 and the suboctads of a fixed octad v1, when identifying a suboctad ith its complement.

At present elements of the octads are ordered in natural order. But this is subject to change!

The function fails if v1 is not a octad or c1 cannot be represented as an even subset of v1. If v1 is a complement of an octad o the o is taken instead of v1. If u_strict is set then the pair (v1, c1) must correspond a short Leech lattice vector. Otherwise is suffices to specify v1 up to an additive term \(\Omega\).

The function returns (uint32_t)(-1) in case of failure.

uint32_t mat24_suboctad_to_cocode(uint32_t u_sub, uint32_t u_octad)

Convert even suboctad of octad to cocode representation.

The function converts a suboctad u_sub (in suboctad representation) of an octad u_octad (in octad representation) to a cocode element. It returns that cocode element in cocode representation. This is a partial inverse of function mat24_suboctad_to_cocode(). The ordering of the suboctads is described in that function.

The function fails if u_octad does not represent an octad. It returns (uint32_t)(-1) in case of failure.

uint32_t mat24_octad_entries(uint32_t u_octad, uint8_t *a_out)

List the entries of an octad.

The function writes the list of entries of octad u_octad (in octad representation) into the array a_out of length 8. The order of these entries is the order used for the conversion of suboctads. It may differ from the natrual order.

The function returns 0 if u_octad is the number of an octad and (uint32_t)(-1) otherwise.

uint32_t mat24_suboctad_weight(uint32_t u_sub)

Return parity of halved bit weight of the even suboctad.

Here parameter u_sub is the number of a suboctad. A suboctad cooresponds to a subset of an octad of even parity. The function returns 0 is the bit weight of that subset is divisible by four and 1 otherwise.

The numbering of suboctads is described in the documentation of function mat24_suboctad_to_cocode().

uint32_t mat24_suboctad_scalar_prod(uint32_t u_sub1, uint32_t u_sub2)

Return scalar product of two suboctads.

The function returns the scalar product of the two suboctads with the numbers u_sub1, u_sub2.

Here the scalar product is the parity of the vector u_sub1 & u_sub2 when u_sub1 and u_sub2 are given as subsets of an octad in vector notation.

But in this functions parameters u_sub1, u_sub2 are suboctad numbers as documented in function mat24_suboctad_to_cocode().

uint32_t mat24_cocode_as_subdodecad(uint32_t c1, uint32_t v1, uint32_t u_single)

Represent a cocode element as a subset of a docecad.

Given a Golay cocode element c1 (in cocode representation) and a dodecad v1 (in gcode representation), the function returns a bit vector c_out equivalent to the cocode word c1, which is a subset of the dodecad d1. This is possible if the scalar product of c1 and the complement of v1 is even. Otherwise the function fails.

The user may specify a bit position 0 <= u_single < 24 disjoint from the bits of dodecad d1. Then that bit of the return value c_out will be set if the scalar product mentioned above is odd, and the function succeeds also in this case.

The intersection of c_out with v1 has bit weight at most 6. If that bit weight is equal to 6 then c_out contains the least significant bit of the bit vector corresponding to v1.

The function fails if v1 is not a dodecad. It returns (uint32_t)(-1) in case of failure.

uint32_t mat24_ploop_theta(uint32_t v1)

Returns the theta function for the Parker loop.

Here function theta() is a quadratic function from the Golay code C to the cocode C*. Parameter v1 of function theta is a Golay code word in gcode representation. The result of the theta function is returned as a Golay cocode word in cocode representation.

The cocycle of the Parker loop is given by:

     cocycle(v1, v2) =   mat24_scalar_prod(theta(v1), v2),

where mat24_scalar_prod() computes the scalar product.

The function evluates the lower 12 bits of v1 only. Thus v1 may also be an element of the Parker loop.

uint32_t mat24_ploop_cocycle(uint32_t v1, uint32_t v2)

Returns the cocycle of the Parker loop.

Here parameters v1 and v2 are Golay code vectors in gcode representations or elements of the Parker loop, coded as in function mat24_mul_ploop. Then the Parker loop product of v1 and v2 is given by

v1 (*) v2  =  v1 ^ v2 * (-1)**cocycle(v1, v2).

uint32_t mat24_mul_ploop(uint32_t v1, uint32_t v2)

Returns the product of two elements of the Parker loop.

Here the Parker loop elements v1 and v2 are integers coded as follows:

bit 0,...,11:   a Golay code word in ``gcode`` representation

bit 12:         Parker loop sign

The other bis ofv1 and v2 are ignored.

uint32_t mat24_pow_ploop(uint32_t v1, uint32_t u_exp)

Returns a power of an element of the Parker loop.

Here v1 is a the Parker loop element coded as in function mat24_mul_ploop(). u_exp is the exponent. The function returns the power v1 ** exp as an element of the Parker loop.

E.g. mat24_pow_ploop(v1, 3) is the inverse of v1.

uint32_t mat24_ploop_comm(uint32_t v1, uint32_t v2)

Return commutator of Golay code words v1 and v2

This is equal to 0 if the intersection of the bit vectors v1 and v2 has bit weight 0 mod 4, and equal to 1 is that intersection has bit weight 2 mod 4. Words v1 and v2 must be given in gcode representation.

For Parker loop elements v1 and v2 (coded as in function mat24_mul_ploop) the commutator of v1 and v2 is equal to

   (-1) ** mat24_ploop_comm(v1, v2),

where ** denotes exponentiation.

uint32_t mat24_ploop_cap(uint32_t v1, uint32_t v2)

Return intersection of two Golay code words as cocode word.

Here v1 and v2 are Golay code words in gcode representation. The result is a cocode word returned in cocode representation.

uint32_t mat24_ploop_assoc(uint32_t v1, uint32_t v2, uint32_t v3)

Return associator of Golay code words v1, v2, and v3

This is the parity of the intersection of the bit vectors v1, v2, and v3. So the function returns 0 or 1. Vectors v1, v2, v3 are in gcode representation.

The associator of three Parker loop elements v1, v2, v3 is equal to

 (-1) ** mat24_ploop_assoc(v1, v2, v3) .

Here v1, v2, v3 are encoded as in function mat24_mul_ploop().

uint32_t mat24_ploop_solve(uint32_t *p_io, uint32_t u_len)

Return cocode element that kills signs of Parker loop elements.

Here p_io refers to an array of u_len Parker loop elements are coded as in function mat24_mul_ploop(). 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 in the bits 0,...,11 of the return value. For that order we assume that lower bits have higher valence. If no such cocode element exists, the function fails.

We set bit 12 of the return value to indicate a failure.

The array p_io is destroyed. More specifically, the first k entries of that array are changed to an array of linear independent Parker loop elements. When these k elements are mapped to positive Parker loop elements, this also yields a solution of the original problem. If the problem cannot be solved then we put p_io[k-1] = 0x1000.

The function returns the value k in bits 31,...,16 of the result.

uint32_t mat24_perm_complete_heptad(uint8_t *p_io)

Complete permutation in the Mathieu group Mat24 from 7 images.

This is an auxilary function for function mat24_perm_from_heptads(). We use the terminology introduced in that function.

The function completes the array p_io to a permutation p in the Mathieu group Mat24. On output, permutation p is given as a mapping i -> p_io[i] for i = 0,...,23.

On input, the images p_io[i] must be given for i = 0,1,2,3,4,5,8; the other entries of p_io are ignored.

The set (p_io[i], i = 0,1,2,3,4,5,8) must be an umbral heptad with distiguished element p_io[8]. Then the mapping i -> p_io[i], i = 0,1,2,3,4,5,8 is a feasible mapping between umbral heptads; it extends to a unique permutation in Mat24. Note that 8 is the distingished element of the umbral heptad (0,1,2,3,4,5,8).

The function returns 0 if the mapping given on input can be extended to an element of Mat24, and a nonzero value otherwise.

Implementation idea:

We choose pentads, i.e. subsets of size 5 of the set (0,....,23) that consist of known values p_io[i]. We calculate the syndromes of such pentads, which are triads, i.e. sets of size three. Calculating the syndromes of the preimages of these pentads we obtain mappings between triads. Intersecting triads in a suitable way we obtain mappings between singletons, and hence peviously unknown images of elements of the set (0,....,23).

uint32_t mat24_perm_check(uint8_t *p1)

Check if permutation is in in the Mathieu group Mat24.

The function checks the mapping i -> p1[i], i = 0,...,23.

It returns zero if that mapping is a permutation in Mat24 and a nonzero value otherwise.

The implementation uses function mat24_perm_complete_heptad().

uint32_t mat24_perm_complete_octad(uint8_t *p_io)

Complete an octad given by 6 elements of it.

Given entries p_io[i], i = 0,1,2,3,4,5, we calculate values p_io[6], p_io[7] such that the set (p_io[i], 0 <= i < 8) is an octad. Furthermore, we order the values p_io[6], p_io[7] in such way that the mapping i -> p_io[i] may be extended to a permutation in the grpup Mat24. This restrtiction determines the order uniquely. Note that the set 0,...,7 is an octad, which is called the standard octad.

The set p_io[i], i = 0,1,2,3,4,5 must be a subset of an octad; otherwise the function fails. The function returns 0 in case of success and (uint32_t)(-1) in case of failure.

The implementation is a simplified version of function mat24_perm_complete_heptad().

uint32_t mat24_perm_from_heptads(uint8_t *h1, uint8_t *h2, uint8_t *p_out)

Complete a mapping to a permutation in the Mathieu group Mat24

A permutation in the Mathieu group Mat24 is a mapping from the set (0,...,23) to itself. The function completes the mapping h1[i] -> h2[i], 0 <= i < 7, 0 <= h1[i], h2[i] < 24 to a permutation i -> p_out[i], 0 <= i < 24 in the group Mat24. The result is returned in the array p_out[i].

The sets h1[i], 0 <= i < 7 and h2[i], 0 <= i < 7 must be umbral heptads, and the mapping from h1 to h2 must be feasible.

An umbral heptad is a set of seven elements of the set (0,...,23) which is not a subset of an octad. The syndrome of an umbral heptad, i.e. the smallest set equivalent to the heptad modulo the Golay code, is a singleton containing exactly one element of the umbral heptad. That element is called the distiguished element of the heptad. A feasible mapping from an umbral heptad to another umbral heptad is a mapping that maps the distiguished element of the first heptad to the distiguished element of the second heptad.

It can be shown that a feasible mapping from an umbral heptad to another umbral heptad extends to a unique element of the Mathieu group.

The function returns 0 if the mapping h1[i] -> h2[i] can be extended to an element of Mat24 and (uint32_t)(-1) otherwise.

The implementation uses function mat24_perm_complete_heptad().

uint32_t mat24_perm_from_map(uint8_t *h1, uint8_t *h2, uint32_t n, uint8_t *p_out)

Complete a mapping to a permutation in the Mathieu group Mat24

A permutation in the Mathieu group Mat24 is a mapping from the set (0,...,23) to itself. The function tries to complete the mapping h1[i] -> h2[i], 0 <= i < n, 0 <= h1[i], h2[i] < 24 to a permutation i -> p_out[i], 0 <= i < 24 in the Mathieu group Mat24. In case of success, such a permutation is stored in the array p_out.

The function returns

-1 if the mapping h1[i] -> h2[i] does not extend to a legal permutation of the numbers 0,…,23. Note that duplicate entries in h1 or h2 are illegal.

0 if no such permutation exists in the Mathieu group Mat24.

1 if the mapping h1[i] -> h2[i] extends to a unique permutation in Mat24.

2 if if the mapping h1[i] -> h2[i] can be completed to several permutations in Mat24, and not all entries h1[i] can be covered by an octad. This may happen in case n = 6 only.

3 if if the mapping h1[i] -> h2[i] can be completed to several permutations in Mat24, and all entries h1[i] can be covered by an octad.

If the return value is greater then zero then a suitable permutation in Mat24 is returned in the array referred by p_out. The function computes the lowest permutation (in lexical order) that maps h1 to `h2.

Caution:

Some input mappings allow several output permutations. Changing the specification of this function such that the same input leads to a different output permutation destroys the interoperability between different versions of the project!!

uint32_t mat24_m24num_to_perm(uint32_t u_m24, uint8_t *p_out)

Compute permutation in the Mathieu group Mat24 from its number.

The Mathieu group has order 244823040. We assign numbers 0 <= n < 244823040 to the elements of Mat24, in lexicographic order, with 0 the number of the neutral element. This is just a convenient way to refer to an element of Mat24.

The function calculates the permutation with the number u_m24 and stores it in the array p_out as a mapping i -> p_out[i]. 0 <= u_m24 < 244823040 must hold; otherwise the function fails.

The function returns 0 in case of success and (uint32_t)(-1) in case of failure.

uint32_t mat24_perm_to_m24num(uint8_t *p1)

Compute number of a permutation in the Mathieu group Mat24

This is the inverse of function mat24_m24num_to_perm().

Given a permutation i -> p1[i], 0 <= i < 24, the function returns the number n of that permutation. We have 0 <= n < 244823040, as described in function mat24_m24num_to_perm.

The function returns garbage if p1 is not a valid permutation in Mat24. One may use function mat24_perm_check() to check if permutation p1 is in Mat24.

void mat24_perm_to_matrix(uint8_t *p1, uint32_t *m_out)

Convert permutation in the Mathieu group Mat24 to bit matrix.

The input of the function is the permutation p: i -> p1[i], 0 <= i < 24 which must be in the Mathieu group Mat24.

The function computes a 12 times 12 bit matrix m, acting on a Golay code vector v (in gcode representation) by right multiplication. Then we have v * m = p(v). Bit m[i,j] is stored in bit j of the the integer m_out[i].

Output m_out[i], 0 <= i < 12 contains garbage if p is not in Mat24.

Implementation idea:

In the standard basis of GF(2)**24, that operation corresponds to a permutation. We have precomputed a matrix converting that standard basis to an internal basis, where Golay code words are visible, and also the inverse of that matrix. Thus the operation is just an (optimized) sequence of matrix multiplications.

void mat24_matrix_to_perm(uint32_t *m1, uint8_t *p_out)

Convert bit matrix to permutation in the Mathieu group Mat24

This is the inverse of function mat24_perm_to_matrix()

The input m1 of the function is a bit matrix that maps a Golay code word v to p(v) = v * m1, as described in function mat24_perm_to_matrix().

The function computes the permutation p: i -> p_out[i], 0 <= i < 24, from that matrix and stores the result in the output vector p_out of length 24.

Output p_out[i], 0 <= i < 24 contains garbage if m1 is not bit matrix corresponding to an element of mat24.

Implementation idea:

We could have reversed the operation of function mat24_perm_to_matrix(), but the following implememtation is faster:

Converting rows of matrix m1 from gcode to standard representation yields the images of some Golay code words as bit vectors. Intersecting these bit vectors in a suitable way yields the images of singletons and hence the requestend permutation.

void mat24_matrix_from_mod_omega(uint32_t *m1)

Complete bit matrix for Mathieu group Mat24 from submatrix.

Let the input m1 of the function be a 12 times 12 bit matrix that maps a Golay code word v to p(v) = v * m1, as described in function mat24_perm_to_matrix().

There are cases where the first 11 rows and columns of m1 can be deduced from an external source, but the last row and column is unknown. This means the operation of an element of Mat24 on the Golay code is know modulo the code word Omega = (1,...,1) only. This function completes such an 11 times 11 bit matrix to a 12 times 12 matrix in place.

static uint32_t dodecad_to_heptad(uint8_t *d1, uint8_t *h_out)

Compute a (unique) heptad from a dodecad.

This is a (rather technical) auxiliary function for function mat24_perm_from_dodecads

Let d1 be a dodecad as in function mat24_perm_from_dodecads().

There is a unique umbral heptad h, as defined in the documentation of function mat24_perm_from_heptads(), satisfying the properties described below.

The function evaluates the first 9 elements d1[i], 0 <= i < 9. It fails if these elements are not a subset of a dodecad or not pairwise disjoint.

The function returns 0 in case of success and (uint32_t)(-1) in case of failure.

Properties of heptad h:

h contains d1[i] for 0 <= i < 5 and also the unique element h_5 in the intersection of d1 and the syndrome S5 of the set (d1[i], 0 <= i < 5).

Let S5 = (h_5, h_6, h_7) such that Mat24 contains a mapping that maps i to d1[i] for i < 5 and i to h_i for i = 5, 6, 7. This determines h_6 uniquely. Then h_6 is not in the dodecad d1. Let T be the tetrad containing the set (d1[0], d1[1], d1[2], h_6). Tetrad T contains exactly one set U intersecting d1 in 3 elements disjoint to (d1[i], 0 <= i < 5). Then heptad h contains the element d_7 of the singleton U \ d1 as ts distinguished element.

The function puts h_out[i] = d1[i] for 0 <= i < 5 and h_out[5] = h_5, h_out[6] = d_7.

uint32_t mat24_perm_from_dodecads(uint8_t *d1, uint8_t *d2, uint8_t *p_out)

Find permutation in Mat24 mapping one dodecad to another.

A dodecad is a word of the Golay code of weight 12. Given two dodecads d1, d2, and five elements d1[0],...,d1[4] of d1, and also five elements d2[0],...,d2[4] of d2, there is a unique permutation p in Mat24 with the following properties:

   d1     is mapped to   d2  ,

   d1[i]  is mapped to   d2[i]  for  0 <= i < 5 .

On input the function takes two dodecads d1, d2 as arrays of integers, and it computes a permutation p: i -> p_out[i] satisfying the properies given above. In case of success it stores p in the array p_out. Only the first 9 elements d1[i], d2[i], 0 <= i < 9 of d1 and d2 are evalutated. There is at most one dodecad containing d1[i], 0 <= i < 9 and at most one dodecad containing d2[i], 0 <= i < 9, assuming d1[i] != d1[j] and d2[i] != d2[j] for i != j.

The function fails if the evaluated entries of d1 or of d2 are not a subset of a dodecad or not pairwise disjoint.

The function returns 0 in case of success and (uint32_t)(-1) in case of failure.

The implementation calls function dodecad_to_heptad() for contructing (unique) heptads h1 and h2 from d1 and d2. Then it calls function mat24_perm_from_heptads() for computing the unique permutation in Mat24 that maps h1 to h2.

uint32_t mat24_op_vect_perm(uint32_t v1, uint8_t *p1)

Apply a permutation in the Mathieu group Mat24 to a bit vector.

Apply the permutation p: i -> p1[i] to the bit vector v1. This maps bit i of v1 to bit p1[i] of the returned result.

uint32_t mat24_op_gcode_matrix(uint32_t v1, uint32_t *m1)

Apply a 12 times 12 bit matrix to a Golay code vector.

A matrix 12 times 12 bit matrix m must be encoded in the input parameter m1 as specified in function mat24_perm_to_matrix(). The function returns the matrix product v1 * m as bit vector. Input v1 and the return value are Golay code words given in gcode representation.

uint32_t mat24_op_gcode_perm(uint32_t v1, uint8_t *p1)

Apply a permutation in the group Mat24 to a Golay code vector.

Apply the permutation p: i -> p1[i] (which must be an element of the Mathieu group Mat24) to the Golay code word v1, with v1 given in gcode representation.

The function returnes the permuted Golay code word in gcode representation.

uint32_t mat24_op_cocode_perm(uint32_t c1, uint8_t *p1)

Apply a permutation in the group Mat24 to a Golay cocode element.

Apply the permutation p: i -> p1[i] (which must be an element of the Mathieu group Mat24) to the Golay cocode element c1, with c1 given in cocode representation.

The function returnes the permuted cocode word in cocode representation.

void mat24_mul_perm(uint8_t *p1, uint8_t *p2, uint8_t *p_out)

Compute product of two permutations in the Mathieu group Mat24

Here inputs p1, p2 must be permutations represented as mappings i -> p1[i], i -> p2[i]. The function computes the product p1 * p2 and stores it in the array p_out in the same form. Thus p_out[i] = p2[p1[i]].

Input errors are not detected, but output buffer overflow is prevented. Any overlap between p1, p2, and p_out is possible.

void mat24_inv_perm(uint8_t *p1, uint8_t *p_out)

Compute the inverse of a permutation in the Mathieu group Mat24

Here input p1 must be a permutation represented as mapping i -> p1[i]. The function computes the inverse of p1 and stores it in the array p_out in the same form. Thus p_out[p1[i]] = i.

Input errors are not detected, but output buffer overflow is prevented. Any overlap between p1 and p_out is possible.

void mat24_autpl_set_qform(uint32_t *m_io)

Auxiliary function for function mat24_perm_to_autpl()

Given a Parker loop autmorphism a, the function computes a quadratic form qf on the Golay code defined as follows:

qf(g[i]) = 0 for all basis vectors g[i] of the standard basis of the Golay code. Furthermore we have

  qf(v1 + v2) = qf(v1) + qf(v2) + b(v1, v2) ,

where b is a bilinear form on the Golay code defined by

  b(x,y) = theta(p(x), p(y)) + theta(x, y)    (mod 2).

Here p is the element of the group Mat24 obtained by taking the automorphism a modulo sign, and theta is the cocycle of the Parker loop. Then b is an alternating bilinear form by [Seys20], Lemma 4.1.

Let b[i,j] = b(g[i], g[j]) where g[i] is the i-th basis vector of the Golay code in our selected standard basis.

Input m_io represents the Parker loop automorphism a as documented in function mat24_perm_to_autpl(). Here we modify the array m_io by storing the bit b[i,j] in bit 13+j of entry m_io[i] for i > j.

So the quadratic form qf is now also stored in the array m_io representing the automorphsm a. As explained in [Seys20] this facilitates computation in the automorphism group of the Parker loop.

void mat24_perm_to_autpl(uint32_t c1, uint8_t *p1, uint32_t *m_out)

Construct a Parker loop automorphism.

The function combines a cocode element c1 and a permutation p in the Mathieu group Mat24 to a Parker loop automorphism. Here c1 must be given in cocode representation. Permutation p must be given as a mapping i -> p1[i], 0 <= i < 24.

Up to sign, the image of an element of the Parker loop is the corresponding Golay code vector permuted by the permutation p. The sign of the image of the i-th positive basis vector of the Parker loop is given by bit i of c1. This determines the automorphism uniqely. We will write AutPL(c1, p) for that automorphism.

The function returns the automorphism AutPL(c1, p) as an array m_out of 12 integers of type uint32_t. The lowest 13 bits of m_out[i] contain the image of the i-th positive basis vector. Here each image is encoded as a Parker loop element as in function mat24_mul_ploop.

We also compute a quadratic form in the higher bits of the entries

of

m_out, as described in function mat24_autpl_set_qform(). This facilitates computations in the automorphism group of the Parker loop.

Let Id be the neutral element in Mat24. Then we have

  AutPL(c1, p)  = AutPL(c1, Id) * AutPL(0, p) .

void mat24_cocode_to_autpl(uint32_t c1, uint32_t *m_out)

Compute a diagonal Parker loop automorphism.

The function converts a cocode element c1 to a Parker loop automorphism. Here c1 must be given in cocode representation. Such an automorphism is called a diagnonal asutomorphism; it changes the signs of the Parker loop elements only.

The resulting automorphism is stored in m_out in the same way as in function mat24_cocode_to_autpl().

If p0 is an array representing the neutral element of the group Mat24 then mat24_cocode_to_autpl(cl, m_out) is equivalent to mat24_perm_to_autpl(c1, p0, m_out).

void mat24_autpl_to_perm(uint32_t *m1, uint8_t *p_out)

Extract permutation from Parker loop automorphism.

Ignoring the signs of the Parker loop, an automorphism m1 of the Parker loop is an automorphism of the Golay code and can be represented as a permutation in the Mathieu group Mat24. Here m1 must be encoded as described in function mat24_perm_to_autpl().

The function computes the permutation in the group Mat24 corresponding the automorphism m1 and returns it in p1 as a mapping i -> p1[i].

uint32_t mat24_autpl_to_cocode(uint32_t *m1)

Extract cocode element from Parker loop automorphism.

Given an automorphism m1 of the Parker loop, as constructed by function mat24_perm_to_autpl(), the function returns a cocode element c in cocode representation. Element c has the following property:

Let p be the permutation in Mat24 obtained from m1 by calling mat24_autpl_to_perm(m1, p). Then calling mat24_perm_to_autpl(c, p, m2), where m2 is a suitable array, constructs a copy m2 of m1 from c and p.

uint32_t mat24_op_ploop_autpl(uint32_t v1, uint32_t *m1)

Apply a Parker loop automorphism to a Parker Loop element.

Apply Parker loop automorphism m1 to Parker Loop element v1 and return the result as a Parker Loop element.

Here m1 is a Parker loop autmorphism as constructed by function mat24_perm_to_autpl(). v1 and the return value is an element of the Parker loop, encoded as in function mat24_mul_ploop().

void mat24_mul_autpl(uint32_t *m1, uint32_t *m2, uint32_t *m_out)

Compute the product of two Parker Loop automorphisms.

Given two Parker Loop automorphism m1, m2 the function computes m1 * m2 and stores the result in m_out. All Parker loop automorphisms are encoded as in function mat24_perm_to_autpl().

For an element a of the Parker loop we have m_out(a) = m2(m1(a)).

void mat24_inv_autpl(uint32_t *m1, uint32_t *m_out)

Compute the inverse of a Parker Loop automorphisms.

Given a Parker Loop automorphism m1 the function computes the inverse of m1 and stores the result in m_out. All Parker loop automorphisms are encoded as in function mat24_perm_to_autpl().

void mat24_perm_to_iautpl(uint32_t c1, uint8_t *p1, uint8_t *p_out, uint32_t *m_out)

Compute inverse Parker Loop automorphism from permutation.

This is equivalent to

  mat24_inv_perm(p1, p_out);
  mat24_perm_to_autpl(c1, p1, m_temp);  
  mat24_inv_autpl(m_temp, m_out);

The function saves some intermedate steps so that it is faster.

void mat24_perm_to_net(uint8_t *p1, uint32_t *a_out)

Compute modified Benes network for permutation of 24 entries.

The Benes network is computed for the permutation p: i -> p1[i]. The network consists of 9 layers. The returned array a_out of length 9 describes that network. In layer i, entry j is to be exchanged with entry j + d[i], if bit j of the value a_out[i] is set. Here d[i] = 1,2,4,8,16,8,4,2,1 for i = 0,...,8. For all such exchange steps we have j & d[i] == 0. We also assert that no entry with index >=24 will be touched.

void mat24_op_all_autpl(uint32_t *m1, uint16_t *a_out)

Auxiliary function for computing in the monster.

The function is used for applying the automorphism m1 of the Parker loop to a vector of the 196884-dimensional representation of the monster. m1 is encoded as in function mat24_perm_to_autpl().

It computes a table a_out[i], i = 0,...,0x7ff, such that (a_out[i] & 0x7ff) is the image m1(i) of the Parker loop element i modulo the center of the Parker loop. Signs are stored in bits 12,...,14 of a_out[i] as follows:

  Bit 12: (sign of m1(i)) ^ (odd &  P(i))

  Bit 13: (sign of m1(i))

  Bit 14: (sign of m1(i)) ^ (bit 11 of m1(i))

Here odd is the parity of the automorphism, and P() is the power map of the Parker loop.

void mat24_op_all_cocode(uint32_t c1, uint8_t *a_out)

Auxiliary function for computing in the monster.

This is a simplified version of function mat24_op_all_autpl(), which is used for applying the diagonal automorphism c1 of the Parker loop (encoded in cocode representation) to a vector of a representation of the monster.

The function computes a table a_out[i], i= 0,...,0x7ff, containing the signs related to this operation as follows:

   Bit 0:  (sign of c1(i)) ^ (odd &  P(i))

   Bit 1:  (sign of c1(i))

   Bit 2:  same as bit 1

Here odd and P() are as in function mat24_op_all_autpl().

C functions for generating random elements of \(M_{24}\)

File mat24_random.c contains the C implementations of the functions for generation random elements of some subgroups of the Mathieu group \(M_{24}\).

Equivalent python function are coded in module mmgroup.tests.test_mat24.test_mat24_rand.

A subgroup of \(M_{24}\) is decribed by an integer of type uint_32_t encoding a bit mask. Each bit in that mask encodes a certain subgroup of \(M_{24}\). By combining several bits with bitwise pr we may evcode the intersection of the subgroups corresponding to the bits being set.

The mapping of the bits to the subgroups is given in the description of the enum type mat24_rand_flags in file mat24_functions.h.

Functions

uint32_t mat24_complete_rand_mode(uint32_t u_mode)

Complete an intersection of subgroups of \(M_{24}\).

Here the integer u_mode is a combination of flags of type enum mat24_rand_flags describing an intersection \(H\) of subgroups of \(M_{24}\). Then the group \(H\) may be contained in more subgroups of \(M_{24}\) encoded as bits of an integer of type enum mat24_rand_flags. This function sets all bits in u_mode corresponding to groups containing \(H\). Furthermore, the function clears all unused bits in parameter u_mode.

The function returns the modified parameter u_mode.

int32_t mat24_perm_in_local(uint8_t *p1)

Compute some subgroups containing an element of \(M_{24}\).

Let \(p_1\) be a permutation in \(M_{24}\) given as an array of 24 integers. The function computes a set of subgroups of \(M_{24}\) containing \(p_1\). These computations are done for all subgroups corresponding to the flags defined in enum mat24_rand_flags. The function returns an integer mode that is the combination of flags of type enum mat24_rand_flags describing the subgroups of \(M_{24}\) containing \(H\) .

The function returns -1 if \(p_1\) is not in \(M_{24}\).

int32_t mat24_perm_rand_local(uint32_t u_mode, uint32_t u_rand, uint8_t *p_out)

Generate a random element of a subgroup of \(M_{24}\).

The function generates an element of a subgroup \(H\) of the Mathieu group \(M_{24}\). Here the parameter u_mode is a combination of flags of type enum mat24_rand_flags describing the group \(H\) as an intersection of subgroups of \(M_{24}\). The generated permutation is stored in the array p_out of length 24.

Parameter u_rand is an integer describing the element of subgroup \(H\) to be generated. Here u_rand is reduced modulo the order of \(H\). In order to generate a uniform random element of \(H\), the user should generate a uniform random number 0 <= u_rand < MAT24_ORDER, where MAT24_ORDER is the order of the group \(M_{24}\).

The function returns 0 in case of success and -1 in case of failure.

int32_t mat24_m24num_rand_local(uint32_t u_mode, uint32_t u_rand)

Generate number of random element of a subgroup of \(M_{24}\).

The function generates an element of a subgroup \(H\) of the Mathieu group \(M_{24}\). Here the parameters u_mode and and u_rand are as in function mat24_perm_rand_local.

The function returns the number of the generated element of \(M_{24}\) in case of success and -1 in case of failure.

See function mat24_m24num_to_perm for the numbering of the elements of \(M_{24}\).

int32_t mat24_m24num_rand_adjust_xy(uint32_t u_mode, uint32_t v)

Make an element of the Parker loop compatible with a subgroup.

Here parameter v is an element \(d\) of the Parker loop encoded as in file mat24.c. Parameter u_mode describes a subgroup of the Mathieu group \(M_{24}\) as in function mat24_perm_rand_local.

Eventually, we want to construct random elements in a larger group then \(M_{24}\). For some values of u_mode we want to use additional generators corresponding to a subloop of the Parker loop. Here the details are dictated by the 2-local structure of the Monster.

If u_mode is set so that one or more Golay cocode vectors of weight 2 are fixed pointwise then we require the scalar product of \(d\) with all these fixed weight-2 vectors to be zero. The function modifies the Parker loop element \(d\) appropriately and returns the modified element.

Generating C code for the mmgroup.mat24 extension

In this section we give a brief overview over the modules in mmgroup.dev.mat24 used for generating C code for the mmgroup.mat24 extension.

Module mat24_ref

Module mat24_ref contains the class Mat24.

Class Mat24 in module mmgroup.dev.mat24.mat24_ref is the table-providing class used by the code generator to generate the C file mat24_functions.c. That C file contains the functionality of the python extension mmgroup.mat24. Class Mat24 may also be used as a table-providing class for generating other C files.

Class Mat24 also exports the same functions as the mmgroup.mat24 extension as class methods. So it can be used for testing that extension and also as a pure-python substitute for that extension.

Class Mat24 is based on class mat24tables.Mat24Tables. It also uses classes, functions and tables from the following modules in mmgroup.dev.mat24:

  • make_addition_table, make_mul_transp, mat24aux, mat24heptad, mat24theta

Module mat24tables

Module mat24tables contains the class Mat24Tables

Class Mat24Tables is a base for class Mat24 in module mmgroup.dev.mat24.mat24_ref. It contains the basis of the Golay code and of (a set of representatives of) the Golay cocode. It also contains tables for fast conversion of a vector in V = GF(2)**24 from the standard to the internal representation and vice versa. That class also contains a table of the syndromes of all 2048 odd elements of the cocode. The 759 octads, i.e. Golay code word of length 8 are numbered from 0 to 758. Class Mat24Tables provides tables for computing the number of a octad from a Golay code word representing an octad and vice versa.

Class Mat24Tables also contains python versions of some C functions in file mat24_functions.c using these tables.

Module mat24aux

The module contains classes Lsbit24Function and MatrixToPerm.

Class Lsbit24Function contains tables and directives for computing the least significant bit of a 24-bit integer using a DeBruijn sequence. This may be overkill for such a simple function, but it may also be considered as a didactic example of a table-providing class.

Class MatrixToPerm contains a directive that generates highly optimized code for converting a bit matrix acting on a Golay code word to a permutation in the Mathieu group Mat_24. Here we assume the bit matrix actually encodes an element of the Mathieu group; otherwise garbage is returned. The Golay code word must be given as an integer in gcode representation.

Module mat24heptad

Support for completing a permutation in the Mathieu group

A heptad is s subset of seven elements of the set on which that Mathieu group acts. Under certain circumstances a mapping from one heptad to another heptad can be completed to a unique element of mat_24. The C function mat24_perm_from_heptads in file mat24_functions.c performs that task. It calls a simpler function mat24_perm_complete_heptad in that file which completes a mapping from a fixed heptad to variable heptad.

This file contains python implementations of functions mat24_perm_complete_heptad and mat24_perm_from_heptads.

The C function mat24_perm_to_m24num maps the elements of the Mathieu group Mat_24 to the set if integers 0, ..., order(Mat_24). Function mat24_perm_complete_heptad is also used as a subroutine of function mat24_m24num_to_perm which compute the inverse of that mapping. This module also contains python implementations of these functions.

Class HeptadCompleter is a table-providing class that is used for code generation. It also exports the functionality of this module.

Function hint_for_complete_heptads prints an explanation of the implementation of function mat24_perm_complete_heptad, including some precalulated data required for that implementation.

Function test_complete_octad test the correcteness of function mat24_perm_complete_heptad. Here it suffices to check that the identity permutation is generated correctly from the required input values, and that the intersections of (possibly complemented) octads and syndromes are indeed singletons if the ought to be singletons.

Module make_addition_table

The module contains class BitMatrixMulFix.

Class BitMatrixMulFix contains a directive that generates code to multiply a fixed bit matrix with a variable bit matrix.

The C function mat24_perm_to_matrix uses this kind of matrix multiplication to convert a permutation in the Mathieu group to a bit matrix operating on a Golay code word.

Module make_mul_transp

The module contains class BitMatrixMulTransp.

Class BitMatrixMulTransp contains a directive that generates code to multiply a bit vector with the transposed matrix of a fixed bit matrix. Depending on the size of the matrix and the bit length of the underlying integer type, several bit vectors can be multiplied with the same matrix simultaneously.

The C function mat24_autpl_set_qform uses that matrix multiplication, see section Implementing Automorphisms of the Parker loop in the Guide for developers for background.

Description of the mmgroup.generators extension

Module generators contains the definition of the generators of the monster, so that they may be used in C files. It also contains support for the subgroups \(N_{0}\) of structure \(2^{2+11+2\cdot11}.(\mbox{Sym}_3 \times M_{24})\) and \(G_{x0}\) of structure \(2^{1+24}.\mbox{Co}_1\) of the monster, as described in [Con85] and [Sey20]. Here \(M{24}\) is the Mathieu group acting on 24 elements, and \(\mbox{Co}_1\) is the automorphism group of the 24-dimensional Leech lattice modulo 2.

Here we fully support the computation in the subgroup \(N_{0}\) based on the generators defined in this module, so that a word in the generators of \(N_{0}\) can easily be reduced to a standard form.

We also support the operation of the group \(G_{x0}\) on the Leech lattice mod 2 and mod 3 (in some cases up to sign only). For a full support of the subgroup \(G_{x0}\) we also have to compute in a Clifford group, which is implemented in module clifford12.

Our set of generators of the monster group is defined in section The Monster group. The C implementation of this set of generators is defined in section Header file mmgroup_generators.h.

The intersection \(N_{0} \cap G_{x0}\) is a group \(N_{x0}\) of structure \(2^{1+24}.2^{11}.M_{24}\). The group \(M_{24}\) (and, to some extent, also the group \(N_{x0}\)) is supported by the C functions in file mat24_functions.c.

The Leech lattice and the extraspecial group \(Q_{x0}\)

Let \(Q_{x0}\) the normal subgroup of \(G_{x0}\) of structure \(2^{1+24}\). Then \(Q_{x0}\) is an extraspecial 2 group and also a normal subgroup of \(G_{x0}\). Let \(\Lambda\) be the Leech lattice. The quotient of \(Q_{x0}\) by its center \(\{\pm1\}\) is isomorphic to \(\Lambda/2 \Lambda\), which is the Leech lattice modulo 2.

For \(e_i \in Q_{x0}\) let \(\tilde{e}_i\) be the vector in the Leech lattice (mod 2) corresponding to \(\pm e_i\). Then \(e_1^2 = (-1)^s\) for \(s = \langle\tilde{e}_1, \tilde{e}_1 \rangle /2\), where \(\langle.,.\rangle\) is the scalar product in the Leech lattice. For the commutator \([e_1, e_2]\) we have \([e_1, e_2] = (-1)^t\), \(t = \langle \tilde{e}_1, \tilde{e}_2 \rangle\).

Leech lattice encoding of the elements of \(Q_{x0}\)

An element of of \(Q_{x0}\) can be written uniquely as a product \(x_d \cdot x_\delta\), \(d \in \mathcal{P} , \delta \in \mathcal{C}^*\), see [Sey20], section 5. Here \(\mathcal{P}\) is the Parker loop and \(\mathcal{C}^*\) is the Golay cocode. We encode the element \(x_d \cdot x_\delta\) of \(Q_{x0}\) as an integer \(x\) as follows:

\[x = 2^{12} \cdot d \oplus (\delta \oplus \theta(d)) \, .\]

Here elements of the Parker loop and elements of the cocode are encoded as integers as in section The Parker loop and The Golay code and its cocode. \(\theta\) is the cocycle given in section The basis of the Golay code and of its cocode, and ‘\(\oplus\)’ means bitwise addition modulo 2. Note that a Parker loop element is 13 bits long (with the most significant bit denoting the sign) and that a cocode element is 12 bits long.

From this representation of \(Q_{x0}\) we obtain a representation of a vector in the Leech lattice modulo 2 by dropping sign bit, i.e. the most significant bit at position 24. A vector addition in the Leech lattice modulo 2 can be done by applying the XOR operator ^ to the integers representing the vectors, ignoring the sign bit.

Special elements of the group \(Q_{x0}\)

We write \(\Omega\) for the positive element of the Parker loop such that \(\tilde{\Omega}\) is the Golay code word \((1,\ldots,1)\) as in [Con85] and [Sey20]. In this specifiction we also write \(\Omega\) for the element \(x_{\Omega}\) of \(Q_{x0}\) and for the element \(\tilde{x}_{\Omega}\) of the Leech lattice modulo 2 if the domain of \(\Omega\) is clear from the context. Then \(\Omega\) has Leech lattice encoding 0x800000 in our chosen basis of the Golay code; and the element \(\Omega\) of \(\Lambda/2 \Lambda\) corresponds to the standard coordinate frame of the real Leech lattice.

For fast computations in the monster group it is vital to compute in the centralizer of a certain short element \(x_{\beta}\) of \(Q_{x0}\), where \(\beta\) is an even coloured element of the Golay cocode, as described in [Sey20]. Here we choose the cocode element \(\beta\) corresponding to the element \((0,0,1,1,0,\ldots,0)\) of \(\mbox{GF}_2^{24}\). Then the centralizer of \(x_{\beta}\) is isomorphic to the a double cover baby monster group and contains the generators \(\tau\) and \(\xi\) of the monster. We also write \(\beta\) for the element \(x_{\beta}\) of \(Q_{x0}\) and for the element \(\tilde{x}_{\beta}\) of \(\Lambda/2 \Lambda\) in the same way as for the element \(\Omega\). Then \(\beta\) has Leech lattice encoding 0x200 in our chosen basis.

Module gen_leech_reduce.c contains functions for rotating arbitrary type-4 vectors in \(\Lambda/2 \Lambda\) to \(\Omega\) and for rotating arbitrary type-2 vectors in \(\Lambda/2 \Lambda\) to \(\beta\).

Computations in the Leech lattice modulo 3

For the construction of the subgroup \(G_{x0}\) of the monster we also require the automorphism group \(\mbox{Co}_0\) of the real Leech lattice, as decribed in [Con85]. That group has a faithful representation as an automophism group of \(\Lambda/3 \Lambda\), but not of \(\Lambda/2 \Lambda\). Module gen_leech3.c provides functions for computing in the Leech Lattice modulo 3.

Note that in [Sey20] the operation of the generators \(x_d, y_d, x_\delta, x_\pi, \xi\) of \(G_{x0}\) is also defined on the real Leech lattice.

Header file mmgroup_generators.h

The header file mmgroup_generators.h contains definitions for the C files in the generator extension. This extension comprises files mm_group_n.c, gen_xi_functions.c, and gen_leech.c.

In this header we also define an enum MMGROUP_ATOM_TAG_ that specifies the format of an atom that acts as a generator of the monster group.

Defines

MMGROUP_ATOM_TAG_ALL

Tag field of a monster group atom

MMGROUP_ATOM_DATA

Data field of a monster group atom

gen_leech2_def_mul(x1, x2, result)

Macro version of function gen_leech2_mul.

Macro gen_leech2_def_mul(x1, x2, result) is equivalent to the statement result = gen_leech2_mul(x1, x2). The macro generates a sequence of statements!

Caution:

Here result must be an integer lvalue that is different from both integers, x1 and x2!

Enums

enum MMGROUP_ATOM_TAG_

In this header file we also define the tags for the atoms generating the Monster group. An element of the monster group is represented as an array of integers of type uint32_t, where each integer represents an atom, i.e. an atomic element of the monster. An atom represents a triple (sign, tag, value), and is encoded in the following bit fields of an unsigned 32-bit integer:

  Bit 31  | Bit 30,...,28  | Bit  27,...,0
  --------|----------------|----------------
   Sign   | Tag            | Value

Standard tags and values are defined as in the constructor of the Python class mmgroup.mm, see section The monster group in the API reference. If the sign bit is set, this means that the atom bit given by the pair (tag, value) has to be inverted. In ibid., a tag is given by a small letter. These small letters are converted to 3-bit numbers as follows:

  Tag  | Tag number | Range of possible values i
  -----|------------|----------------------------
   'd' |     1      |  0 <= i < 0x1000
   'p' |     2      |  0 <= i < 244823040
   'x' |     3      |  0 <= i < 0x2000
   'y' |     4      |  0 <= i < 0x2000
   't' |     5      |  0 <= i < 3
   'l' |     6      |  0 <= i < 3

A tag with tag number 0 is interpreted as the neutral element. A tag with tag number 7 is illegal (and reserved for future use).

Tags with other letters occuring in the constructor of class MM are converted to a word of atoms with tags taken from the table above.

For tags ‘t’ and ‘l’ the values 0 <= i <= 3 are legal on input.

Values:

enumerator MMGROUP_ATOM_TAG_1

Tag indicating the neutral element of the group

enumerator MMGROUP_ATOM_TAG_I1

Tag indicating the neutral element of the group

enumerator MMGROUP_ATOM_TAG_D

Tag corresponding to ‘d’

enumerator MMGROUP_ATOM_TAG_ID

Tag corresponding to inverse of tag ‘d’

enumerator MMGROUP_ATOM_TAG_P

Tag corresponding to ‘p’

enumerator MMGROUP_ATOM_TAG_IP

Tag corresponding to inverse of tag ‘p’

enumerator MMGROUP_ATOM_TAG_X

Tag corresponding to ‘x’

enumerator MMGROUP_ATOM_TAG_IX

Tag corresponding to inverse of tag ‘x’

enumerator MMGROUP_ATOM_TAG_Y

Tag corresponding to ‘y’

enumerator MMGROUP_ATOM_TAG_IY

Tag corresponding to inverse of tag ‘y’

enumerator MMGROUP_ATOM_TAG_T

Tag corresponding to ‘t’

enumerator MMGROUP_ATOM_TAG_IT

Tag corresponding to inverse of tag ‘t’

enumerator MMGROUP_ATOM_TAG_L

Tag corresponding to ‘l’

enumerator MMGROUP_ATOM_TAG_IL

Tag corresponding to inverse of tag ‘l’

C functions implementing the group \(N_{0}\)

We describe an implementation of the subgroup \(N_{0}\) of the monster group.

The subgroup \(N_{0}\) of the monster group of structure \(2^{2+11+2\cdot 11}.(\mbox{Sym}_3 \times\mbox{M}_{24})\) has been described in [Con85]. Theorem 5.1 in [Sey20] reduces the group operation in \(N_{0}\) to easy calculations in the Parker loop \(\mathcal{P}\), the Colay cocode \(\mathcal{C}^*\), and the group \({{\rm Aut}_{{\rm St}} \mathcal{P}}\) of standard automorphisms of \(\mathcal{P}\). The loops \(\mathcal{P}\), \(\mathcal{C}^*\), and \({{\rm Aut}_{{\rm St}} \mathcal{P}}\) are described in section Basic structures. Module mat24_functions.c provides the required functions for computing in these loops.

Using the notation in section The Monster group we may describe an element of \(N_{0}\) as a product:

\[\tau^t y_f x_e x_\delta x_\pi \; ; \quad 0 \leq t < 3; \; e, f \in \mathcal{P}; \; \delta \in \mathcal{C}^*; \; \pi \in {{\rm Aut}_{{\rm St}} \mathcal{P}} \; .\]

This representation is unique if we require \(f\) to be in a transversal of \(\mathcal{P} / Z(\mathcal{P})\) and \(\pi\) to be a standard representative in \({{\rm Aut}_{{\rm St}} \mathcal{P}}\) as described in section Automorphisms of the Parker loop.

We store an element of \(N_{0}\) an a quintuple \(t, f, e, \delta, \pi\) of five integers of type uint32_t. For \(f, e\) we use the numbering in class PLoop in module mmgroup; for \(\delta\) we use the numbering in class Cocode in module mmgroup. Here \(\pi\) refers to a standard representative in \({{\rm Aut}_{{\rm St}} \mathcal{P}}\). These standard representatives correspond to the elements of the Mathieu group \(\mbox{M}_{24}\); and we use the numbering of the elements of \(\mbox{M}_{24}\) described in class AutPL| in module mmgroup.

Most functions in module mm_group_n.c take a pointer to a 5-tuple \((t, f, e, \delta, \pi)\) representing an element \(g\) of the \(N_{0}\) as their first argument. Then the tuple representing \(g\) is modified to a tuple representing an element \(g_2 = g \cdot g_1\), with the element \(g_1\) of \(N\) given by one or more subsequent arguments of the function.

The functions in module mm_group_n.c may cause some overhead due to the fact that element of the Mathieu group \(\mbox{M}_{24}\) is represented as an integer. But compared to an operation of the monster group on its 196884-dimensional that overhead is negligible.

C interface for file mm_group_n.c

The functions in file mm_group_n.c implement the subgroup \(N_0\) of structure \(2^{2+11+2\cdot11}.(\mbox{Sym}_3 \times \mbox{Mat}_{24})\) on the monster group. Elements of \(N_0\) are represented as arrays of five integers of type uint32_t as described in the document The C interface of the mmgroup project.

Functions

void mm_group_n_mul_delta_pi(uint32_t *g, uint32_t delta, uint32_t pi)

Multiply \(g \in N_{0}\) with \(x_\delta x_\pi\).

Put \(g = g x_\delta x_\pi\). Here the integer \(\delta\) represents an element of the Golay cocode and \(\pi\) is the number of an element of \(M_{24}\) corresponding to a standard representative in the automorphism group \(\mbox{AUT}{(\mbox{PL})}\) of the Parker loop.

\(g\) is given as an array of five 32-bit integers.

void mm_group_n_mul_inv_delta_pi(uint32_t *g, uint32_t delta, uint32_t pi)

Multiply \(g \in N_{0}\) with \((x_\delta x_\pi)^{-1}\).

Put \(g = g \cdot (x_\delta x_\pi)^{-1}\). Here \(g\), \(x_\delta\), and \(x_\pi\) are as in function mm_group_n_mul_delta_pi.

void mm_group_n_mul_x(uint32_t *g, uint32_t e)

Multiply \(g \in N_{0}\) with \(x_e\).

Put \(g = g \cdot x_e\). Here the integer \(e\) represents an element of the Parker loop.

\(g\) is given as an array of five 32-bit integers.

void mm_group_n_mul_y(uint32_t *g, uint32_t f)

Multiply \(g \in N_0\) with \(y_f\).

Put \(g = g \cdot y_f\). Here the integer \(f\) represents an element of the Parker loop.

\(g\) is given as an array of five 32-bit integers.

void mm_group_n_mul_t(uint32_t *g, uint32_t t)

Multiply \(g \in N_0\) with the triality element.

Put \(g = g \cdot \tau^t\), where \(\tau\) is the triality element. \(g\) is given as an array of five 32-bit integers.

void mm_group_n_clear(uint32_t *g)

Set \(g \in N_0\) to the value of the neutral element.

Put \(g = 1\). Element \(g\) of \(N_0\) is given as an array of five 32-bit integers.

void mm_group_n_copy_element(uint32_t *g_1, uint32_t *g_2)

Copy the element \(g_1\) of \(N_0\) to \(g_2\).

Elements \(g_1, g_2\) of \(N_0\) are given as arrays of five 32-bit integers.

void mm_group_n_mul_element(uint32_t *g_1, uint32_t *g_2, uint32_t *g_3)

Multiply elemnts of the group \( N_0\).

Put \(g_3 = g_1 \cdot g_2\). Elements \(g_1, g_2, g_3\) of \(N_0\) are given as an array of five 32-bit integers.

These arrays may overlap.

void mm_group_n_mul_inv_element(uint32_t *g_1, uint32_t *g_2, uint32_t *g_3)

Multiply \(g_1 \in N_0\) with \(g_2^{-1} \in N_0\).

Put \(g_3 = g_1 \cdot g_2^{-1}\). Elements \(g_1, g_2, g_3\) of \(N_0\) are given as arrays of five 32-bit integers.

These arrays may overlap.

void mm_group_n_inv_element(uint32_t *g_1, uint32_t *g_2)

Invert an element \(g_1\) of \(N_0\).

Put \(g_2 = g_1^{-1}\). Elements \(g_1, g_2\) of \(N_0\) are given as arrays of five 32-bit integers.

These arrays may overlap.

void mm_group_n_conjugate_element(uint32_t *g_1, uint32_t *g_2, uint32_t *g_3)

Conjugate \(g_1 \in N_0\) with \(g_2 \in N_0\).

Put \(g_3 = g_2^{-1} \cdot g_1 \cdot g_2\). Elements \(g_1, g_2, g_3\) of \(N_0\) are given as arrays of five 32-bit integers.

These arrays may overlap.

uint32_t mm_group_n_mul_word_scan(uint32_t *g, uint32_t *w, uint32_t n)

Multiply \(g \in N_0\) with an element of the monster.

Let w be a word of generators of the monster group of length n. Let k be the greatest number such that all prefixes of w of length at most k are in the group \(N_{0}\). Let \(a\) be the element of \(N_{0}\) corresponding to the prefix of w of length k.

Let \(g \in N_{0}\) is given as an array g of five 32-bit integers.

Then the function replaces the value \(g\) in the array g by \(g \cdot a\). It returns the number k of atoms of the word w processed.

uint32_t mm_group_n_mul_atom(uint32_t *g, uint32_t atom)

Multiply \(g \in N_{0}\) with an atom.

Put \(g = g \cdot a\). Here \(a\) is the generator of the group \(N_0\) given by parameter atom as described in the header file mmgroup_generators.h.

\(g\) is given as an array of five 32-bit integers.

The function returns 0 in case of success and the (possibly simplified) atom in case of failure.

uint32_t mm_group_n_scan_word(uint32_t *w, uint32_t n)

Scan word of generators of the monster for membership in \(N_{0}\).

Let w be a word of generators of the monster group of length n. The function returns the greatest number k such that all prefixes of w of length at most k are in the group \(N_{0}\).

uint32_t mm_group_n_conj_word_scan(uint32_t *g, uint32_t *w, uint32_t n)

Conjugate \(g \in N_0\) with an element of the monster.

Let w be a word of generators of the monster group of length n. Let k be the greatest number such that all prefixes of w of length at most k are in the group \(N_{0}\). Let \(a\) be the element of \(N_{0}\) corresponding to the prefix of w of length k.

Let \(g \in N_{0}\) is given as an array g of five 32-bit integers.

Then the function replaces the value \(g\) in the array g by \(a^{-1} \cdot g \cdot a\). It returns the number k of atoms of the word w processed.

uint32_t mm_group_n_reduce_element(uint32_t *g)

Reduce \(g \in N_0\) to a standard form.

The representation of \(g\) is reduced to a standard form.

Technically, we reduce the product \(y_f x_e\) to \(y_{f'} x_{e'}\), such that \(0 \leq f' < \mbox{0x800}\) holds. If \(x_e\) is in the center of the Parker loop, but \(x_f\) is not in that center then we put \(x_{e'}=0\).

\(g\) is given as an array of five 32-bit integers.

uint32_t mm_group_n_reduce_element_y(uint32_t *g)

Reduce \(g \in N_0\) to a standard form.

The representation of \(g\) is reduced to a standard form.

Here we reduce the product \(y_f x_e\) to \(y_{f'} x_{e'}\), such that \(0 \leq f' < \mbox{0x800}\) always holds.

\(g\) is given as an array of five 32-bit integers.

uint32_t mm_group_n_to_word(uint32_t *g, uint32_t *w)

Convert \(g \in N_0\) to a word of generators.

The representation of \(g\) is converted to a word of generators of the monster group. The entries of that word are stored in the buffer referred by parameter w. The entries of that word are encoded as described in file mmgroup_generators.h. Word w may have up to five entries. The function returns the length of the word w.

\(g\) is given as an array of five 32-bit integers.

The element \(g\) is reduced with function mm_group_n_reduce_element.

It is legal to put w = g.

uint32_t mm_group_n_right_coset_N_x0(uint32_t *g)

Map \(g \in N_0\) to an element of \(N_{x0}\).

The function changes the element \(g\) of \(N_0\) to an element \(g'\) of \(N_{x0}\) and returns an exponent \(0 \leq e < 3\) such that \(g = g' \cdot \tau^e\).

uint32_t mm_group_n_to_word_std(uint32_t *g, uint32_t *w)

Convert \(g \in N_0\) to a standard word of generators.

The representation of \(g\) is converted to a word of generators of the monster group in the standard order. The entries of that word are stored in the buffer referred by parameter w.

The standard order of the generators of the monster group in the reduced representation of an element of \(N_0\) differs from the order of the generators returned by function mm_group_n_to_word. Apart from this difference the action of this function is the same as in that function.

It is legal to put w = g.

int32_t mm_group_n_conj_to_Q_x0(uint32_t *g)

Transform element of \(N_0\) to an element of \(Q_{x0}\).

Let \(g \in N_x0\) be stored in the array g of five 32-bit integers.

The function tries to calculate a number \(0 \leq e < 3\) and an element \(q\) of the subgroup \(Q_{x0}\) of \(N_0\) with \(g = \tau^{-e} q \tau^e\). Here \(\tau\) is the triality element in \(N_0\).

In case of succes we return the element \(q\) in bits 24,…,0 of the return value in Leech lattice encoding and we return the number \(e\) in bits 26,…,25 of the return value. In case of failure we return -1.

uint32_t mm_group_split_word_n(uint32_t *word, uint32_t length, uint32_t *g)

Split an element of \(N_0\) from a word of generators.

Given a array word of generators of the monster group of a given length, that word is split into a possibly shorter word word1 and an element g of the group \(N_0\) such that word = word1 * g. Then word1 is a prefix of word. The function returns the length of the prefix word1 of word. It does not change word. Here we just scan the word from the right, checking for atoms ordered in a way compatible to our representation of \(N_0\).

Words of generators of the monster are implemented as described in file mmgroup_generators.h. Output \(g\) is given as an array of five 32-bit integers.

uint32_t mm_group_mul_words(uint32_t *w1, uint32_t l1, uint32_t *w2, uint32_t l2, int32_t e)

Multiply a word of generators of the monster.

Given a word w1 of length l1 and a word w2 of length l2 of generators of the monster group, we compute the product w3 = w1 * w2**e. Here ** means exponentiation; negative exponents are supported. The word w1 is replaced by the word w3. The funcion returns the length of the word w3.

A word representing w2**e is appended to word w1 and then the result is simplified using the relations inside the group \(N_0\). The result w3 is reduced (with respect to these relations) if input w1 is reduced.

Caution!

The buffer for the word w1 referred by pointer w1 must be able to store at least l1 + 2 * abs(e) * l2 entries of type uint32_t. Here the user must provide sufficient space!

Words of generators of the monster are implemented as described in file mmgroup_generators.h.

void mm_group_invert_word(uint32_t *w, uint32_t l)

Invert a word of generators of the monster.

Given a word w of length l the function changes the word in the buffer w to its inverse in place.

Words of generators of the monster are implemented as described in file mmgroup_generators.h.

uint32_t mm_group_check_word_n(uint32_t *w1, uint32_t l1, uint32_t *g_out)

Check if a words of generators of the monster is in \(N_0\).

We check if the word w1 of length l1 of generators of the monster group is in the subroup \(N_0\). If this is the case then we store the word w1 as an element of \(N_0\) in the array g_out of five 32-bit integers as desribed above. The function returns the following status information:

0: w1 is the neutral element the monster group

1: w1 is in \(N_0\), but not the neutral element

2: w1 is not in \(N_0\)

3: Nothing is known about w1

We check the relations in the generators \(N_0\) only. The output in g_out is valid only if the function returns 0 or 1.

Words of generators of the monster are implemented as described in file mmgroup_generators.h.

uint32_t mm_group_words_equ(uint32_t *w1, uint32_t l1, uint32_t *w2, uint32_t l2, uint32_t *work)

Check if two word of generators of the monster are equal.

We check if the word w1 of length l1 of generators of the monster group is equal to the word w2 of length l2 of generators. The function returns the following status information:

0: w1 == w2

1: w1 != `w2

Greater than 1: equality of w1 and w2 is not known

If the function cannot check the equality of w1 and w2 then it computes a word w3 of length l3 of generators of the monster group such that w1 == w2 if and only if w3 is the neutral element. Then the function stores the word w3 in the buffer work and it returns l3 + 2.

Buffer work must have size at least max(2 * l1, l1 + 2 * l2).

Words of generators of the monster are implemented as described in file mmgroup_generators.h.

C functions for the operation of \(G_{x0}\) on the Leech lattice

The functions in file gen_leech.c implement the operation of the subgroup \(G_{x0}\) (of structure \(2^{1+24}.\mbox{Co}_1\)) of the monster group on its extraspecial subgroup \(Q_{x0}\) (of structure \(2^{1+24}\)) by conjugation.

Here an element of the group \(Q_{x0}\) is given as a 25-bit integer in Leech lattice encoding. From that encoding we obtain an encoding of \(\Lambda / 2 \Lambda\) (where \(\Lambda\) is the Leech lattice) by dropping the most significant bit. This corresponds to the isomorphism \(Q_{x0} / \{\pm1\} \cong \Lambda / 2 \Lambda\).

An element of the group \(G_{x0}\) is encoded as an array of 32-bit integers, where each integer corresponds to a generator of the group, as described in the documentation of the header file mmgroup_generators.h.

A vector in the Leech lattice modulo 2 has a type and also a subtype as described in The mmgroup guide for developers, section Computations in the Leech lattice modulo 2. Here the subtype is a two-digit decimal number, where the first digit is the type. Function gen_leech2_subtype returns the subtype as a BCD-coded integer. E.g. the subtype 46 is returned as the hexadecimal integer 0x46. So the type can be obtained from the subtype via a shift operation.

For computations in the group \(G_{x0}\) or \(\mbox{Co}_1\) it is important to find an element of \(G_{x0}\) that maps an arbitrary type-4 vector in \(\Lambda / 2 \Lambda\) to the unique type-4 vector \(\Omega\), see Computations in the Leech lattice modulo 2. Function gen_leech2_reduce_type4 performs this task.

The construction of the group \(G_{x0}\) also requires some computations in the automorphism group \(\mbox{Co}_0\) (of structure \(2.\mbox{Co}_1\)) of \(\Lambda\), see [Con85], section 9 or [Sey20], section 9. The group \(\mbox{Co}_0\) acts faithfully on \(\Lambda / 3 \Lambda\). Therefore file gen_leech.c also provides some functions for dealing with vectors in the Leech lattice modulo 3.

For any \(w \in \Lambda\) the vector \(v = \sqrt{8} \cdot w\) has integral coordinates. We represent a vector \(w \in \Lambda / 3 \Lambda\) by the vector \(v = \sqrt{8} \cdot w\), with the coordinates of \(v\) taken modulo 3. We represent each coordinate \(v_i, 0 \leq i < 24\) as a two-bit integer; so all 48 bits of \(v\) fit into the a 64-bit integer. Let \(v_{i,1}, v_{i,0}\) be the bits of \(v_{i}\) of valence \(2^1\) and \(2^0\), respectively. Then we encode \(v\) as an 48-bit integer \(x\) so that bit \(24 \cdot j+i\) of \(x\) is equal to \(v_{i,j}\). This encoding looks peculiar, but it greatly simplifies the interaction with the functions in file mat24_functions.c. We call this encoding the Leech lattice mod 3 encoding. On input, both bits \(v_{i,1}, v_{i,0}\) may be equal to 1; but on output, at most on of these bits is equal to 1.

In [Sey20], section 9.3, the generators \(x_d, x_\delta, y_\delta, x_\pi, \xi\) of \(G_{x0}\) are also defined as generators of a group \(G_{x1}\) with \(|G_{x1}:G_{x0}| = 2\). The group \(G_{x1}\) operates on \(\Lambda\), but the group \(G_{x0}\) does not. So we take these generators as generators of \(G_{x1}\). The kernel of the operation of \(G_{x1}\) on \(\Lambda\) is the group \(Q_{x0}\) generated by the elements \(x_d, x_\delta, y_{-1}, d \in \mathcal{P}, \delta \in \mathcal{C}\). Here the element \(y_{\Omega}\) acts as the central element \(-1\).

C interface for file gen_leech.c

The functions in file gen_leech.c implement operations on the vectors of the Leech lattice modulo 2 and on the subgroup \(Q_{x0}\). We use the terminology defined in the document The C interface of the mmgroup project, section Description of the mmgroup.generators extension.

Functions

uint32_t gen_leech2_mul(uint32_t x1, uint32_t x2)

Return product of two elements the group \(Q_{x0}\).

Here all elements of the group \(Q_{x0}\) are encoded in Leech lattice encoding. The function returns the product of the elements x1 and x2 of \(Q_{x0}\).

uint32_t gen_leech2_pow(uint32_t x1, uint32_t e)

Return power of element the group \(Q_{x0}\).

Here all elements of the group \(Q_{x0}\) are encoded in Leech lattice encoding. The function returns the power x1**e of the element x1 of \(Q_{x0}\).

uint32_t gen_leech2_scalprod(uint32_t x1, uint32_t x2)

Return scalar product in the Leech lattice modulo 2.

Here all elements of Leech lattice modulo 2 are encoded in Leech lattice encoding**. The function returns the scalar product of the vectors x1 and x2 in the Leech lattice modulo 2, which may be 0 or 1.

uint32_t gen_leech2_op_word(uint32_t q0, uint32_t *g, uint32_t n)

Perform operation of \(G_{x0}\) on \(Q_{x0}\).

The function returns the element \(g^{-1} q_0 g\) for \(q_0 \in Q_{x0}\) and \(g \in G_{x0}\). Here \(g\) is given as a word of genenators of length \(n\) in the array g. Each atom of the word \(g\) is encoded as defined in the header file mmgroup_generators.h. Parameter \(q_0\) and the result are encoded in Leech lattice encoding.

The function succeeds also in case \(g \notin G_{x0}\) if \(h^{-1} q_0 h \in G_{x0}\) for all prefixes \(h\) of \(g\).

uint32_t gen_leech2_op_atom(uint32_t q0, uint32_t g)

Atomic operation of \(G_{x0}\) on \(Q_{x0}\).

Equivalent to gen_leech2_op_word(q0, &g, 1).

uint32_t gen_leech2_prefix_Gx0(uint32_t *g, uint32_t len_g)

Scan prefix in \(G_{x0}\) of a word in the monster group.

Let \(g \in G_{x0}\) be stored in the array g of length len_g as a word of generators of the subgroup \(G_{x0}\) of the monster. The function returns the maximum length len_g such that every prefix of the word in g of length <= len_g is in the group \(G_{x0}\).

uint32_t gen_leech2_op_word_many(uint32_t *q, uint32_t m, uint32_t *g, uint32_t n)

Perform operation of \(G_{x0}\) on elements of \(Q_{x0}\).

Let q be an array of m elements \(q_j\) of the group \(Q_{x0}\) in Leech lattice encoding. Let \(g\) be an element of the group \(G_{x0}\) encoded in the array g of length n as in function gen_leech2_op_word.

The function computes the elements \(q'_j = g^{-1} q_j g\) for \(0 \leq j < m\) and stores \(q'_j\) in a[j].

The function applies the same prefix of the word in the array g to all entries of q. It stops if the application of any prefix of the word in g to any element \(q_j\) fails; and it returns the length of the prefix of a that has been applied to all elements \(q_j\). Hence the function returns n in case of success and a number 0 \leq k < n if not all atoms of q could be applied to all entries of q.

The function succeeds on an individual entry \(q_j\) if and only if function gen_leech2_op_word succeeds on the same value.

uint32_t gen_leech2_op_word_leech2(uint32_t l, uint32_t *g, uint32_t n, uint32_t back)

Perform operation of \(G_{x0}\) on Leech lattice mod 2.

Let \(g_0 \in G_{x0}\) be stored in the array referred by g as a word of generators of the subgroup \(G_{x0}\) of the monster. Here \(g_0\) is given as a word of generators of length \(n\) in the array g. Each atom of the word \(g\) is encoded as defined in the header file mmgroup_generators.h. Put \(g = g_0\) if back == 0 and \(g = g_0^{-1}\) otherwise.

The function returns the element \(l \cdot g\) for a vector \(l\) in the Leech lattice mod 2.

Parameter \(l\) is encoded in Leech lattice encoding, igoring the sign. The function returns \(l \cdot g\), in Leech lattice encoding, with the sign bit set to zero.

The function is optimized for speed. It returns garbage if any generator in the buffer g is not in \(G_{x0}\).

int32_t gen_leech2_op_word_leech2_many(uint32_t *a, uint32_t m, uint32_t *g, uint32_t n, uint32_t back)

Perform operation of \(G_{x0}\) on elements ofLeech lattice mod 2.

Let a be an array of m elements \(l_j\) of the Leech lattice mod 2 in Leech lattice encoding. Let \(g_0\) be an element of the group \(G_{x0}\) encoded in the array g of length n as in function gen_leech2_op_word. Put \(g = g_0\) if back == 0 and \(g = g_0^{-1}\) otherwise.

The function replaces each element \(l_j\) in the array m by \(l_j \cdot g\). Elements \(l_j\) are encoded in Leech lattice encoding, igoring the sign.

The function is optimized for speed. It returns zero in case of success. It stores garbage in the array m and returns a negative value if any generator in the buffer g is not in \(G_{x0}\).

int32_t gen_leech2_op_word_matrix24(uint32_t *g, uint32_t n, uint32_t back, uint32_t *a)

Convert operation of group \(G_{x0}\) to 24 times 24 bit matrix.

Let \(g_0 \in G_{x0}\) be stored in the array referred by g as a word of generators of the subgroup \(G_{x0}\) of the monster. Here \(g_0\) is given as a word of generators of length \(n\) in the array g. Each atom of the word \(g\) is encoded as defined in the header file mmgroup_generators.h. Put \(g = g_0\) if back == 0 and \(g = g_0^{-1}\) otherwise.

The function converts \(g\) to a \(24 \times 24\) bit matrix \(a\) acting on the vectors of the Leech lattice mod 2 (encoded in Leech lattice encoding) by right multiplication. Such matrices form a natural representation of the Conway group \(\mbox{Co}_1\).

The function returns 0 in case of success and a negative value in case of error.

void gen_leech2_op_mul_matrix24(uint32_t *a1, uint32_t n, uint32_t *a)

Multiply an n times 24 bit matrix with a 24 times 24 bit matrix.

The function multiplies the n times 24 bit matrix a1 with the 24 times 24 bit matrix a and stores the result in matrix a1.

Thus array a1 must have length n; and array a1 must have length 24.

int32_t gen_leech2_map_std_subframe(uint32_t *g, uint32_t len_g, uint32_t *a)

Transform the standard subframe of Leech lattice mod 2

A frame in the Leech lattice \(\Lambda\) is a maximal set of pairwise orthogonal vectors of type 4. In \(\Lambda / 2 \Lambda\) (which is the Leech lattice mod 2) a frame is mapped to a unique vector of type 4. The standard frame \(\Omega\) in \(\Lambda\) consists of the type-8 vectors parallel to the basis vectors in \(\Lambda\).

The subframe \(S(F)\) of a frame \(F\) in \(\Lambda\) is the set \(\{ (u + v)/2 \mid u, v \in F, u \neq \pm v\}\). Any frame \(S(F)\) in \(\Lambda\) contains \(48\) type-4 vectors, and its subframe contains \(48 \cdot 46\) type-2 vectors.

The image of \(S(F)\) in \(\Lambda / 2 \Lambda\) spans a 12-dimensional maximal isotropic subpace \(\langle S(F) \rangle\) of \(\Lambda / 2 \Lambda\), and the type-2 vectors in \(\langle S(F) \rangle\) are precisely images of \(S(F)\).

Then for the standard frame \(\Omega\) we have

\( \langle S(\Omega) \rangle = \{ \lambda_\delta, \lambda_\Omega + \lambda_\delta \mid \delta \in \mathcal{C}^*, \delta \, \mbox{even} \} \) .

Here \(\mathcal{C}^*\) is the Golay cocode, and and \(\lambda_c\) is the element of \(\Lambda / 2 \Lambda\) corresponding to the Golay code or cocode element \(c\).

Then \(\langle S(\Omega) \rangle\) is spanned by \(\lambda_\Omega\) and \(\lambda_{\{0,j\}}, 1 \leq j < 24\). Here \(\{i,j\}\) is the Golay cocode word corresponding to the sum of basis vectors \(i\) and \(j\) of \(\mbox{GF}_2^{24}\).

The elements \(x_\Omega\) and \(x_{\{0,j\}}\) of the group \(Q_{x0}\) are preimages of \(\lambda_\Omega\) and \(\lambda_{\{0,j\}}\) under the natural homomorphism from \(Q_{x0}\) to \(\Lambda / 2 \Lambda\). These elements of \(Q_{x0}\) play an important role in the representation \(196883_x\) of the monster group. For computations in the subgroup \(G_{x0}\) of the monster we sometimes want to compute the images of these elements of \(Q_{x0}\) under conjugation by an element \(g\) of \(G_{x0}\).

Let \(g \in G_{x0}\) be stored in the array g of length len_g as a word of generators of the subgroup \(G_{x0}\) of the monster. Then this function computes the following 24 elements of \(Q_{x0}\):

\( x_\Omega^g, x_{\{0,1\}}^ g, \ldots, x_{\{0,23\}}^g\).

The function stores these 24 elements in the array a (in that order) in Leech lattice encoding.

In case of success the function returns the number of entries of the word g being processed. It returns a negative value in case of failure.

C interface for file gen_leech_type.c

The functions in file gen_leech_type.c implement operations for detecting the type of a vector in the Leech lattice modulo 2.

We use the terminology defined in the document The C interface of the mmgroup project, section Description of the mmgroup.generators extension.

Functions

uint32_t gen_leech2_subtype(uint64_t v2)

Return subtype of vector in Leech lattice mod 2.

The function returns the subtype of the vector \(v_2\) in the Leech lattice modulo 2 as a BCD-coded two-digit integer. \(v_2\) must be given in Leech lattice encoding.

The subtype of a vector in the Leech lattice mod 2 is defined in The mmgroup guide for developers, section Computations in the Leech lattice modulo 2.

uint32_t gen_leech2_type(uint64_t v2)

Return type of a vector in the Leech lattice mod 2.

This function returns the type of the vector \(v_2\) in the Leech lattice modulo 2. That type may be 0, 2, 3, or 4.

This function is a bit faster than function gen_leech2_subtype().

uint32_t gen_leech2_type2(uint64_t v2)

Compute subtype if vector in Leech lattice mod 2 is of type 2.

This function returns the subtype of the vector \(v_2\) in the Leech lattice modulo 2 if \(v_2\) is of type 2 and 0 otherwise.

It is faster than function gen_leech2_type().

uint32_t gen_leech2_count_type2(uint32_t *a, uint32_t n)

Count type-2 vectors in an affine subspace of the Leech lattice mod 2.

This function returns the number of type-2 vectors in an affine subspace \(V\) of the Leech lattice mod 2. Subspace \(V\) is defined by an array \(a\) of length \(n\) of bit vectors. If \(a_1,\ldots,a_{n-1}\) are linear independent then \(V\) is given by:

\(V = \{a_0 + \sum_{i=1}^{n-1} \lambda_i a_i \mid \lambda_i=0,1\}\).

int32_t gen_leech2_start_type24(uint32_t v)

Auxiliary function for function gen_leech2_reduce_type4.

The function returns the subtype of a vector \(v\) of type 2 in the Leech lattice modulo 2, provided that \(v + \beta\) is of type 4. It returns 0 in the special case \(v = \beta + \Omega\) and a negative value if \(v\) is not of type 2 or \(v + \beta\) is not of type 4.

It is used for rotating a type-4 vector \(v\) which is orthogonal to \(\beta\) (in the real Leech lattice) into the \(v + \Omega\). That rotation will fix the special short vector \(\beta\).

int32_t gen_leech2_start_type4(uint32_t v)

Auxiliary function for function gen_leech2_reduce_type4.

The function returns the subtype of a vector \(v\) of type 4 in the Leech lattice modulo 2. Parameter \(v\) must a vector of type 4 in Leech lattice encoding. The function returns the subtype of \(v\) that will be used for reduction in function gen_leech2_reduce_type4.

This function takes care of the special vectors \(\Omega\) and \(\beta\) the Leech lattice modulo 2.

It is used for rotating a type-4 vector \(v\) into \(\Omega\). If this is possible, that rotation will fix the special short vector \(\beta\).

Therefore the function returns 0 in case \(v = \Omega\). It returns the subtype of \(v + \beta\) if \(\beta\) and \(v + \beta\) are of type 2 and orthogonal in the real Leech lattice.

The function returns a negative value if \(v\) is not of type 4.

C interface for file gen_leech3.c

The functions in file gen_leech3.c implement operations on the vectors of the Leech lattice modulo 3 and on the subgroup \(Q_{x0}\). We use the terminology defined in the document The C interface of the mmgroup project, section Description of the mmgroup.generators extension.

Functions

uint32_t gen_leech3_scalprod(uint64_t v3_1, uint64_t v3_2)

Scalar product of two vectors in the Leech lattice mod 3.

The function returns the scalar product of the vectors \(v_{3,1}, v_{3,2}\). The parameters are given in Leech lattice mod 3 encoding. The result is between 0 and 2.

uint64_t gen_leech3_add(uint64_t v3_1, uint64_t v3_2)

Add two vectors in the Leech lattice mod 3.

The function returns the sum of the vectors \(v_{3,1}, v_{3,2}\). The parameters and the result are given in Leech lattice mod 3 encoding.

uint64_t gen_leech3_neg(uint64_t v3)

Negate a vector in the Leech lattice mod 3.

The function returns the negated vector \(v_{3}\). The parameter and the result are given in Leech lattice mod 3 encoding.

uint64_t gen_leech2to3_short(uint64_t v2)

Map short vector from \(\Lambda/2\Lambda\) to \(\Lambda/3\Lambda\).

Here parameter \(v_2\) is a short vector (i.e. a vector of type 2) in \(\Lambda/2\Lambda\) in Leech lattice encoding.

The function returns a short vector in \(\Lambda/3\Lambda\) corresponding to \(v_2\) in Leech lattice mod3 encoding.

The result is unique upto sign only. The function returns 0 if \(v_2\) is not short.

uint64_t gen_leech2to3_abs(uint64_t v2)

Map vector from \(\Lambda/2\Lambda\) to \(\Lambda/3\Lambda\).

Here parameter \(v_2\) is a short vector of type 2 or 3. in \(\Lambda/2\Lambda\) in Leech lattice encoding.

The function returns a vector in \(\Lambda/3\Lambda\) corresponding to \(v_2\) in Leech lattice mod3 encoding.

The result is unique upto sign only. The function returns 0 if \(v_2\) is not of type 2 or 3.

uint64_t gen_leech3to2_short(uint64_t v3)

Map short vector from \(\Lambda/3\Lambda\) to \(\Lambda/2\Lambda\).

Here parmeter \(v_3\) is a short vector (i.e. a vector of type 2) in \(\Lambda/3\Lambda\) in Leech lattice mod 3 encoding.

The function returns a short vector in \(\Lambda/2\Lambda\) corresponding to \(v_3\) in Leech lattice encoding.

The result is unique. The function returns 0 if \(v_3\) is not short. This function is an inverse of function gen_leech2to3_short.

uint64_t gen_leech3to2(uint64_t v3)

Map vector from \(\Lambda/3\Lambda\) to \(\Lambda/2\Lambda\).

Here parameter \(v_3\) is a vector in \(\Lambda/3\Lambda\) in Leech lattice mod 3 encoding.

If a shortest preimage \(v\) of \(v_3\) in \(\Lambda\) if of type \(t\) with \(t \leq 4\) then the function computes the (unique) vector \(v_2\) in \(\Lambda/2\Lambda\) that has the same preimage \(v\) in \(\Lambda\). Otherwise the function fails.

In case of success the function returns \(2^{24} \cdot t + v_2\), with \(v_2\) given in Leech lattice encoding. The function returns uint64_t(-1) in case of failure.

uint64_t gen_leech3to2_type4(uint64_t v3)

Map type-4 vector from \(\Lambda/3\Lambda\) to \(\Lambda/2\Lambda\).

Here parameter \(v_3\) must be a type-4 vector in \(\Lambda/3\Lambda\) in Leech lattice mod 3 encoding.

The function returns a type-4 vector in \(\Lambda/2\Lambda\) corresponding to \(v_3\) in Leech lattice encoding.

The result is unique. The function returns 0 if \(v_3\) is not of type 4.

uint64_t gen_leech3_op_xi(uint64_t v3, uint32_t e)

Special case of function gen_leech3_op_vector_word

For internal purposes only. This is equivalent to gen_leech3_op_vector_word(v3, g), where g encodes the element \(\xi^e\) of \(G_{x1}\).

Parameter \({v_3}\) and the result are given Leech lattice mod 3 encoding.

uint64_t gen_leech3_op_vector_word(uint64_t v3, uint32_t *pg, uint32_t n)

Operation of \(G_{x1}\) on the Leech lattice mod 3.

The function returns the element \(v_3 g\) for \(v_3 \in \Lambda/3\Lambda\) and \(g \in G_{x0}\). Here \(g\) is given as a word of genenators of length \(n\) in the array pg. Each atom of the word \(g\) is encoded as defined in the header file mmgroup_generators.h.

Parameter \({v_3}\) and the result are given Leech lattice mod 3 encoding.

uint64_t gen_leech3_op_vector_atom(uint64_t v3, uint32_t g)

Atomic operation of \(G_{x1}\) on the Leech lattice mod 3.

Equivalent to gen_leech3_op_vector_word(v3, &g, 1).

Parameter \({v_3}\) and the result are given Leech lattice mod 3 encoding.

C interface for file gen_leech_reduce.c

The functions in file gen_leech_reduce.c implement the transformation of vectors of the Leech lattice modulo 2 to a standard form by applying an element of the subgroup \(G_{x0}\) of the monster.

We use the terminology defined in the document The C interface of the mmgroup project, section Description of the mmgroup.generators extension.

Functions

int32_t gen_leech2_reduce_type2(uint32_t v, uint32_t *pg_out)

Map short vector in Leech lattice to standard vector.

Let \(v \in \Lambda / 2 \Lambda\) of type 2 be given by parameter v in Leech lattice encoding. Then the function constructs a \(g \in G_{x0}\) that maps \(v\) to the standard short vector \(v_0\). Here \(v_0\) is the short vector the Leech lattice propotional to \(e_2 - e_3\), where \(e_i\) is the \(i\)-th basis vector of \(\{0,1\}^{24}\).

The element \(g\) is returned as a word in the generators of \(G_{x0}\) of length \(n \leq 6\). Each atom of the word \(g\) is encoded as defined in the header file mmgroup_generators.h.

The function stores \(g\) as a word of generators in the array pg_out and returns the length \(n\) of that word. It returns a negative number in case of failure, e.g. if \(v\) is not of type 2.

Caution:

An input vector allows several outputs. Changing the implementation of this function such that the same input leads to a different output destroys the interoperability between different versions of the project!!

int32_t gen_leech2_reduce_type2_ortho(uint32_t v, uint32_t *pg_out)

Map (orthogonal) short vector in Leech lattice to standard vector.

Let \(v \in \Lambda / 2 \Lambda\) of type 2 be given by parameter v in Leech lattice encoding.

In the real Leech lattice, (the origin of) the vector \(v\) must be orthogonal to the standard short vector \(v_0\). Here \(v_0\) is the short vector in the Leech lattice propotional to \(e_2 - e_3\), where \(e_i\) is the \(i\)-th basis vector of \(\{0,1\}^{24}\).

Let \(v_1\) be the short vector in the Leech lattice proportional to \(e_2 + e_3\). Then the function constructs a \(g \in G_{x0}\) that maps \(v\) to \(v_1\) and fixes \(v_0\).

The element \(g\) is returned as a word in the generators of \(G_{x0}\) of length \(n \leq 6\). Each atom of the word \(g\) is encoded as defined in the header file mmgroup_generators.h.

The function stores \(g\) as a word of generators in the array pg_out and returns the length \(n\) of that word. It returns a negative number in case of failure, e.g. if \(v\) is not of type 2, or not orthogonal to \(v_0\) in the real Leech lattice.

Caution:

An input vector allow several outputs. Changing the implementation of this function such that the same input leads to a different output destroys the interoperability between different versions of the project!!

int32_t gen_leech2_reduce_type4(uint32_t v, uint32_t *pg_out)

Map a frame in the Leech lattice to the standard frame.

A frame in the Leech lattice \(\Lambda\) is a maximal set of type-4 vectors which are equal modulo \(2 \Lambda\). A frame is equivalent to a type-4 vector in \(\Lambda / 2 \Lambda\).

Let \(v \in \Lambda / 2 \Lambda\) of type 4 be given by parameter v in Leech lattice encoding. Then the function constructs a \(g \in G_{x0}\) that maps \(v\) to the standard frame \(\Omega\). The standard frame \(\Omega\) consists of the type-4 vectors parallel to the coordinate axes.

The element \(g\) is returned as a word in the generators of \(G_{x0}\) of length \(n \leq 6\). Each atom of the word \(g\) is encoded as defined in the header file mmgroup_generators.h. Let \(H\) be the stabilizer of \(\Omega\). We choose a representative \(g\) in the coset \(gH\) such that the inverse \(g^{-1}\) is a short as possible.

The function stores \(g\) as a word of generators in the array pg_out and returns the length \(n\) of that word. It returns a negative number in case of failure, e.g. if \(v\) is not of type 4.

The function uses the method described in the The mmgroup guide for developers, section Computations in the Leech lattice modulo 2.

We make one additional assertion.

Let \(v_0\) be the standard short vector in the Leech lattice proportional to \(e_2 - e_3\), where \(e_i\) is the \(i\)-th basis vector of \(\{0,1\}^{24}\). If \(v_0 + v\) is of type 2 then this function computes the same result as function gen_leech2_reduce_type2_ortho applied to the vector \(v_0 + v\). In this case the result of this function centralizes \(v_0\). This convention greatly simplifies computations in the baby monster group.

Caution:

An input vector allow several outputs. Changing the implementation of this function such that the same input leads to a different output destroys the interoperability between different versions of the project!!

uint32_t gen_leech2_type_selftest(uint32_t start, uint32_t n, uint32_t *result)

Test functions gen_leech2_subtype and gen_leech2_reduce_type4

Function gen_leech2_subtype may tested as follows:

We compute the subtypes of all \(2^{24}\) vectors \(v \in \Lambda / 2 \Lambda\) and we count the obtained subtypes in an array result of length 0x50. The sizes of the orbits of each subtype are known form [Iva99], so a high-level test routine may check the array result.

During that process we may also test function gen_leech2_reduce_type4. Whenever vector of type-4 vector \(v\) occurs, we compute a g \in G_{x0} that maps \(v\) to the standard frame using function gen_leech2_reduce_type4; and we check the correctness of that mapping using function gen_leech2_op_word. We return the number of successful such operations. This number must be equal to the number of type-4 vectors in \(\Lambda / 2 \Lambda\).

Since this test takes a long time, a high-level function might want to distribute it over several processes. So this function acccumulates the test results for the vectors \(v\) with \(\mbox{start} \leq v < \mbox{start} + \mbox{n}\) only.

int32_t gen_leech2_pair_type(uint64_t v1, uint64_t v2)

Isomorphsim type of pair of vectors in Leech lattice mod 2.

Let \(v_1\) and \(v_2\) be two vectors in the Leech lattice mod 2 and put \(v_3 = v_1 + v_2\). Let \(t_i, i = 1,2,3\) be the type of \(v_i\). Then the triple \((t_1, t_2, t_3)\) depends on the isomorphism type of the pair \((v_1, v_2)\).

If one or three of the vectors \((v_1, v_2, v_3)\) are of type 4 then there may be several isomorphism types of pairs \((v_1, v_2)\) with the same triple \((t_1, t_2, t_3)\). In this case we define a subtype \(s\) as follows. We map one of the vectors \(v_i\) of type 4 to the standard frame \(\Omega\). Let \(w\) be the image of any of the vectors \(v_j, j \neq i\) under any such mapping. Then we compute the subtype of \(w\) with function gen_leech2_subtype, and we let \(s\) be the last hexadecimal digit of the computed subtype. The result \(s\) does not depend on the choices made in the description given above.

After reordering \(t_1, t_2, t_3\), the possibilties for tuples \((t_1, t_2, t_3, s)\) with a relevant component \(s\) are (4,2,2,0), (4,3,3,4), (4,3,3,6), (4,4,4,0), (4,4,4,4), (4,4,4,6).

If none of the vectors \((v_1, v_2, v_3)\) is of type 4 then all the shortest corresponding vectors in the Leech lattice are defined up to sign. Then these vectors may span a space of dimension 2 or 3; and we put \(s = 1\) if that dimension is equal to 3, and \(s = 0\) otherwise. The case \(s = 1\) may occur if and only if at least two of the vectors \((v_1, v_2, v_3)\) are of type 3 and none is of type 4.

In all other cases there is at most one such isomorphism type; and we put \(s = 0\).

The function takes \(v_1\) and \(v_2\) as parameters v1 and v2 in Leech lattice encding. It returns

\( 2^{12} \cdot t_1 + 2^8 \cdot t_2 + 2^4 \cdot t_3 + s .\)

We sketch a proof that the returned result actually describes the isomorphism type of the pair \((v_1, v_2)\). If one of the vectors \((v_1, v_2, v_3)\) is of type 4 then may map that vector to the standard frame \(\Omega\) and check the possible subtypes of the other vectors. These subtypes are described in subsection Computations in the Leech lattice modulo 2 of The guide for developers, or in [Iva99], Section 4.4 ff.

Otherwise the shortest preimages \(v'_1, v'_2, v'_3\) of \(v_1, v_2, v_3\) in the Leech lattice are defined up to sign. The cases where these preimages span a subspace of dimension 2 of the Leech lattice are well known, see e.g. [CS99] or [CCN+85]. If \(v'_1, v'_2, v'_3\) span a 3-dimensional space, then it is easy to see that at least two of these vectors must be of type 3, and that all vectors \((v'_1 \pm v'_2 \pm v'_3)/2\) (with all possible combinations of signs) are in the Leech lattice and of type at most 3. The 3-dimensional lattices satifying these properties can be shown to be S-lattices, as defined in [CCN+85]. Note that the S-lattices are also known.

Warning: This function has not yet been tested!

C interface for file gen_leech_reduce_n.c

Given a vector \(v\) in the extraspecial group \(Q_{x0}\), the functions in this module compute a transformation from \(v\) to a standard representative of the class \(v^N\).

Here \(Q_{x0}\) and \(N = N_{x0}\) are as in the project documentation.

We use the terminology defined in the document The C interface of the mmgroup project, section Description of the mmgroup.generators extension.

Functions

int32_t gen_leech2_reduce_n(uint32_t v, uint32_t *pg_out)

Reduce Leech lattice vector modulo the group N_x0.

Given a vector \(v\) in the extraspecial group \(Q_{x0}\), we want to find a standard representative of the class \(v^N\), where \(Q_{x0}\) and \(N = N_{x0}\) are as in the project documentation. Here \(v\) is given in parameter v in Leech lattice encoding.

This function computes such a representative \(v_1 \in Q_{x0}\). It returns an element \(r\) of \(N_{x0}\) with \(v_1 = v^r\) as a word of length three in the generators of \(N_{x0}\). That word is stored in the buffer referred by pg_out as an array of 3 integers of type uint32_t as described in section The monster group in the API reference.

The user may call function gen_leech2_op_word in file gen_leech.c for computing \(v_1 = v^r\). There is a faster alternative for computing \(v_1\) described in the documetnation of function gen_leech2_reduce_n.

The representative \(v_1\) depends on the subtype of \(v\) only, with one exception listed below. Here the subtype can be also computed by function gen_leech2_subtypein file gen_leech.c; it is explained in The mmgroup guide for developers in section Computations in the Leech lattice modulo 2.

If the subtype of \(v\) is 00then we have \(v_1 =v\) for the two vectors \(v = x_{\pm 1}\) of that subtype.

We compute a mapping \(r = x_\pi y_e x_f\), where \(r\) is an automorphism of the Parker loop, \(e\) is in the Parker loop, and \(f\) is either in the Parker loop or in the Golay cocode.

The result \(r\) is in the even subgroup of \(N_{x0}\) except in case \(v = x_{-\Omega}\), where this is not possible.

See section Orbits of the group N_x0 in the group Q_x0 in The mmgroup guide for developers for our choice of the representative of the \(N_{x0}\) orbits.

int32_t gen_leech2_reduce_n_rep(uint32_t subtype)

Compute standard representative of Q_x0 modulo the group N_x0.

Given a subtype subtype in the Leech lattice modulo 2, the function returns a representative of a nonzero orbit of \(Q_{x0}\) under the action of \(N_{x0}\).

See the table in section Orbits of the group N_x0 in the group Q_x0 in The mmgroup guide for developers for our choice of such a representative.

C interface for file gen_leech_reduce_22.c

Let \(\Lambda / 2\Lambda\) be the Leech lattice modulo 2. Let \(v_2, v_3 \in \Lambda / 2\Lambda\) with \(\mbox{type}(v_2) = 2\) and \(\mbox{type}(v_3) = \mbox{type}(v_2 + v_3) = t\) for \(2 \leq t \leq 3\). We want to compute the set of all vectors \(v_4 \in \Lambda / 2\Lambda\) of type 4 with \(\mbox{type}(v_2 + v_4) = \mbox{type}(v_3 + v_4) = \mbox{type}(v_2 + v_3 + v_4) = 2\). There are 891 or 100 such vectors in case \(t = 2\) or \(t = 3\), respectively. Function gen_leech2_u4_2xx in this module computes the list of all such vectors.

We motivate this (rather complicated) computation as follows. The main algorithm for the reduction of an element of the Monster in Version v1.0.0 the mmgroup package works in the representation \(\rho_{15}\) of the monster, with coefficients taken mod 15. In future versions it would be faster to work in representation \(\rho_{3}\) instead. For the reduction algorithm we have to compute certain sets \(U_4(v)\) for axis \(v\) in the representation of the Monster as defined in [Sey22]. This computation is considerably more difficult for \(v \in \rho_3\) than for \(v \in \rho_{15}\). It turns out that for \(t = 2,3\) the sets computed above are just the sets \(U_4(v)\) when \(v\) is in the orbit called ‘6A’ or ‘10A’ in [Sey22]. Given an axis \(v \in \rho_3\) in one of these orbits, we can effectively compute suitable vectors \(v_2, v_3\) as discussed above.

For that computation we need a function that maps vectors \(v_2, v_3\) to fixed vectors \(w_2, w_{3t} \in \Lambda / 2\Lambda\). Function gen_leech2_reduce_2xx essentially performs this task; see the documentation of that function for details.

The group fixing \(v_2\) and \(v_3\) is the unitary group \(\mbox{PSU}_6(2)\) in case \(t = 2\), and the Higman-Sims group in case \(t = 3\), see [CS99], Ch. 10.3.4. A detailed discussion of the case \(t = 3\) is given in [CS99], Ch. 10.3.5.

We use the terminology defined in the document The C interface of the mmgroup project, Section Description of the mmgroup.generators extension. Vectors in \(\Lambda / 2\Lambda\) are given in Leech lattice encoding; the sign bit is ignored on input and set to zero on output.

Functions

int32_t gen_leech2_n_type_22(uint32_t n)

Generate certain type-2 vectors in the Leech lattice.

The function maps the integers \( 0 \leq n < 4600\) to the 4600 type-2 vectors \(v\) in the Leech lattice mod 2, such that that \(v+w\) is also of type 2 in the Leech lattice mod 2. Here \(w\) is the standard type-2 vector \((0, 0, 4, -4, 0, \ldots, 0)\). Such a vector \(v\) is returned in Leech lattice encoding.

The function returns a negative value in case of failure; e.g. if \(n \geq 4600\).

int32_t gen_leech2_find_v4_2xx(uint32_t v2, uint32_t v3, uint64_t *seed)

Support for reducing a certain pair of vectors in the Leech lattice.

Let v2 be a vector of type 2 in the Leech lattice mod 2, and let v3 be a vector of type t in the Leech lattice mod 2, with 2 <= t <= 3, such that v2 + v3 is also of type t. Here v2 and v3 must be given in Leech lattice encoding.

The function computes a vector v4 of type 4 such that v2 + v4, v3 + v4, and v2 + v3 + v4 are all of type 2. Vector v4 can be used in function gen_leech2_find_v4_2xx for reducing the pair (v2, v3) to a standard pair of vectors.

The function returns the value t * 0x1000000 + v4 in case of succcess, with t as above. Parameter seed is a seed for a random generator as described in module gen_random.c. The function returns a negative value in case of failure.

The function tries to find random vectors vx of type 2 in the Leech lattice mod 2 such that both, vx + v2, and vx + v3 are of type 2. Then the vector v4 = v2 + v3 + vx satifies the required conditions.

For finding vx we transform v2 to the standard type-2 vector with a transformation tf. Then we generate random type-2 vectors with function gen_leech2_n_type_22. These random type-2 vectors are interpreted as transformed vectors tf(vx); and we will check the required conditions on the transformed vectors tf(v2), tf(v3), and tf(vx).

int32_t gen_leech2_reduce_2xx(uint32_t v2, uint32_t v3, uint32_t v4, uint32_t *g)

Reduce a certain pair of vectors in the Leech lattice.

Let v2 be a vector of type 2 in the Leech lattice mod 2, and let v3 be a vector of type t in the Leech lattice mod 2, with 2 <= t <= 3, such that v2 + v3 is also of type t. Here v2 and v3 must be given in Leech lattice encoding.

The function computes an element g of the Conway group \(\mbox{Co}_1\) such that v2 * g = w2 and v3 * g = w3 with w2 = (0, 0, 4, -4, 0, ..., 0). Here we have w3 = (0, 4, -4, 0, ..., 0) if t == 2 and w3 = (1, 1, 5, 1, ..., 1) if t == 3.

The function stores g in the array g as a word of length k of generators of the subgroup G_x0 of the Monster. These generators are encoded as described in file mmgroup_generators.h. We have k <= 8; so the array g must have size at least 8.

The function returns the value k >= 0 in case of succcess, and a negative value in case of failure.

Parameter v4 must be a type-4 vector satisfying certain conditions as described in de documentation of function gen_leech2_find_v4_2xx. That function computes a suitable random vector v4.

Internally, we use function gen_leech2_reduce_type4 to find an element g_0 of \(\mbox{Co}_1\) that maps v4 to the standard type-4 vector Omega. To finish up, we just have to calculate an element g_1 of the subgroup \(2^{11}.M_{24}\) of \(\mbox{Co}_1\) such that g = g_0 * g_1 satisfies the required conditions.

int32_t gen_leech2_map_2xx(uint32_t *g, uint32_t n, uint32_t t, uint32_t *a)

Auxiliary function for function gen_leech2_u4_2xx

Let v2, v3 be as in function gen_leech2_reduce_2xx. Let g be an element of the group G_x0 of length n that maps v2 and v3 to w2 and w3, with w2, w3 as in function gen_leech2_reduce_2xx. Here g should have been computed by that function.

The function computes all vectors v4 of type 4 in the Leech lattice mod 2 such that v2 + v4, v3 + v4, and v2 + v3 + v4 are of type 2. It stores the list of these vectors in the array a and returns the length of that list.

Array a must have length 891 in case t == 2 and length 100 in case t == 3. Other values of t are illegal.

int32_t gen_leech2_u4_2xx(uint32_t v2, uint32_t v3, uint64_t *seed, uint32_t *a)

Compute a certain set of type-4 vectors in the Leech lattice mod 2.

Let v2 be a vector of type 2 in the Leech lattice mod 2, and let v3 be a vector of type t in the Leech lattice mod 2, with 2 <= t <= 3, such that v2 + v3 is also of type t. Here v2 and v3 must be given in Leech lattice encoding.

The function computes all vectors v4 of type 4 in the Leech lattice mod 2 such that v2 + v4, v3 + v4, and v2 + v3 + v4 are of type 2. There are 891 such vectors in case t = 2 and 100 such vectors in case t = 3. The list all these vectors is stored in the array a in Leech lattice encoding in an undefined order. Thus array a must have length at least 891.

The function uses a random generator. Parameter seed is a seed for a random generator as described in module gen_random.c.

The function returns the length of the array a in case of succcess, and a negative value in case of failure.

C interface for file gen_union_find.c

The functions in file gen_union_find.c implement a union-find algorithm. This may be used e.g. for computing the orbits of the Leech lattice mod 2 under the action of a group.

Functions

int32_t gen_ufind_init(uint32_t *table, uint32_t length)

Initialize table for union-find algorithm.

The function initializes an array referred by parameter table for performing a union-find algorithm on the set S(length) of integers i with 0 <= i < length. Array table must have size length. Here 0 <= length <= 0x40000000must hold.

Then table will store a partition of the set S(length) in an internal format. Initially, that partition consists of singletons. One may use function gen_ufind_union for joining two sets of that partition. Any set in the partition is represented by an element of that set. The rules for computing a representative of a set are not disclosed to the user. You may use function gen_ufind_find for finding the representative of a set. After calling function gen_ufind_find_all_min each set is represented by its smallest element. The representative of a set may change after calling function gen_ufind_union.

The function returns 0 in case of success, and a negative value in case of failure (e.g. if length is too big.)

int32_t gen_ufind_find(uint32_t *table, uint32_t length, uint32_t n)

The find function for the union-find algorithm.

Let S(length) be the set of integers i with 0 <= i < length; and let a partition of S(length) be stored in the array table (of size length) as described in function gen_ufind_init.

The function returns the representative of the set containing the integer n. It returns a negative number in case of failure, e.g. if n >= length.

void gen_ufind_union(uint32_t *table, uint32_t length, uint32_t n1, uint32_t n2)

The union function for the union-find algorithm.

Let S(length) be the set of integers i with 0 <= i < length; and let a partition of S(length) be stored in the array table (of size length) as described in function gen_ufind_init.

The function joins the sets in the partition containg the elements n1 and n2.

int32_t gen_ufind_find_all_min(uint32_t *table, uint32_t length)

Choose smallest representatives for sets in union-find algorithm.

Let S(length) be the set of integers i with 0 <= i < length; and let a partition of S(length) be stored in the array table (of size length) as described in function gen_ufind_init.

The function chooses the smallest element of a set in a partition as the representative of the set. Thus a subsequent call to function gen_ufind_find with parameter n returns the smallest element of the set containg n.

The function returns the number of the sets in the partition. A negative return value indicates a error the array table.

int32_t gen_ufind_partition(uint32_t *table, uint32_t length, uint32_t *ind, uint32_t len_ind)

Output the partition computed by the union-find algorithm.

Let S(length) be the set of integers i with 0 <= i < length; and let a partition of S(length) be stored in the array table (of size length) as described in function gen_ufind_init.

We assume that the partition stored in the array table contains n_sets sets. Then we overwrite the array table with a list of lists, where each list corresponds to a set in the partition. We write some index infomation for interpreting these lists into the array ind of length len_ind.

Array table will contain the length elements of the set S(length) in such a way that for 0 <= i < n_sets the i-th set in the partition is equal to the set of integers given by table[ind[i]], ..., table[ind[i+1] - 1].

So the array ind must have size at least n_sets + 1; i.e. we must have len_ind > n_sets. Function gen_ufind_find_all_min must be called beford calling this function. Note that function gen_ufind_find_all_min returns n_sets in case of success.

The entries table[ind[i]], ..., table[ind[i+1] - 1] corresponding to set in the partition are sorted. The sets of the partition are sorted by their smallest elements.

The function returns n_set in case of success and a negative value in case of failure.

void gen_ufind_union_affine(uint32_t *table, uint32_t length, uint32_t *m, uint32_t len_m, uint32_t v)

Perform affine union operations in the union-find algorithm.

Let S(length) be the set of integers i with 0 <= i < length; and let a partition of S(length) be stored in the array table (of size length) as described in function gen_ufind_init.

In this function the entries of S(length) are interpreted as bit vectors. The function joins the set containing i with the set containg i * M + v for all i. Here M the bit matrix over GF(2) referred by m with len_m rows, and v a bit vector. All bit vector arithmetic is done over GF(2).

int32_t gen_ufind_union_leech2(uint32_t *table, uint32_t *g, uint32_t len_g)

Join orbits of the Leech lattice mod 2.

The Conway group \(\mbox{Co}_1\) has a natural action on on \(\Lambda / 2 \Lambda\), i.e. on the Leech lattice mod 2. The subgroup \(G_{x0}\) of the Monster (of structure \(2^{1+24}.\mbox{Co}_1\)) has the same action on on \(\Lambda / 2 \Lambda\).

In this function we assume that the array table contains a partition of the set \(\{i \mid 0 \leq i < 2^{24}\}\) as described in function gen_ufind_init. In that function the array table should have been initialized with a length of \(2^{24}\).

Here each integer \(i\) is interpreted as an element of \(\Lambda / 2 \Lambda\) in Leech lattice encoding, as described in the document The C interface of the mmgroup project, section Description of the mmgroup.generators extension.

Let \(g \in G_{x0}\) be stored in the array referred by g as a word of generators of the subgroup \(G_{x0}\) of the monster. Here \(g\) is given as a word of generators of length len_g in the array g. Each atom of the word \(g\) is encoded as defined in the header file mmgroup_generators.h.

Then the function joins the set containing \(i\) with the set containing \(i \cdot g\) for all \(i\), using the union-find algorithm.

The function returns 0 in case of success and a negative value in case of failure (e.g. if \(g\) is not in \(G_{x0}\)).

C functions for the generator \(\xi\) of the monster group

Reference implementation for module gen_xi_functions.c

The methods in class GenXi in this module are a reference implementations of the functions in module gen_xi_functions.c. These functions deal with the operation of the generator \(\xi\) of the monster defined in [Sey20], Section 9, on a subgroup \(Q_{x0}\), and also with the monomial part of that operation on the 196884-dimensional representation \(\rho\) of the monster.

Generator \(\xi\) is an element of the subgroup \(G_{x0}\) of the monster. It operates on the normal subgroup \(Q_{x0}\) of \(G_{x0}\) of structure \(2^{1+24}\) by conjugation as described in cite:Seysen20, Lemma 9.5. Method gen_xi_op_xi of class GenXi implements the operation of the generators \(\xi^e\) on the group \(Q_{x0}\). Here the elements of \(Q_{x0}\) are given in Leech lattice encoding, as described in The C interface of the mmgroup project, Section Description of the mmgroup.generators extension.

Representation \(\rho\) has a subspace \(98280_x\) of dimension 98280 on which \(G_{x0}\) operates monomially in the same way as it operates on the short elements of \(Q_{x0}\). Here the basis vectors of \(98280_x\) (together with their opposite vectors) can be identified with the short elements of \(Q_{x0}\).

For a fast implementation of the operation of \(\xi^e\) on \(98280_x\) we precompute tables as described in the mmgroup guide for developers, Section Some mathematical aspects of the implementation, Subsection Implementing generators of the Monster group, Subsubsection Monomial operation of the generators xi^e. We adopt the terminology from that subsection.

In that subsection the basis vectors of \(98280_x\) are subdivided into five boxes, so that \(\xi\) acts as a permutation on these boxes. Within each of these boxes the basis vectors of \(98280_x\) are numbered in a natural way as described in that subsection. Operator \(\xi\) permutes the five boxes 1,…,5 as follows:

  • 1 -> 1, 2 -> 2, 3 -> 4 -> 5 -> 3 .

For each short vector in \(Q_{x0}\) we encode the number of the box containing the vector, the position of the vector inside the box, and and the sign of the vector in the lowest 19 bits of a 32-bit integer as follows:

  • Bit 18…16: number of the box, running from 1 to 5.

  • Bit 15: sign bit

  • Bit 14…0: Entry inside the box

We call this encoding the Short vector encoding of a short vector in \(Q_{x0}\).

Methods gen_xi_short_to_leech and gen_xi_leech_to_short in class GenXi convert elements of \(Q_{x0}\) from Leech lattice encoding to Short vector encoding and vice versa. Method gen_xi_op_xi_short uses these two methods together with method gen_xi_op_xi in order to compute the operation of \(\xi^e\) on a vector in \(98280_x\) encoded in Short vector encoding.

Using method gen_xi_op_xi_short we may easily compute tables for the operation of \(\xi\) and \(\xi^2\) on a specific box. Method gen_xi_make_table computes an array containing the lower 16 bits of the images of the entries of a box under the operation \(\xi^e\). Bits 18…16 of these images can be reconstructed from the permutation of the boxes given above.

C interface for file gen_xi_functions.c

The functions in file gen_xi_function.c correspond to the python functions in class mmgroup.dev.generators.gen_xi_ref.GenXi. They are used for computing the tables required for implementing the operation of generator \(\xi\) on the representation \(\rho_p\) of the Monster.

Functions

uint32_t gen_xi_g_gray(uint32_t v)

Compute function \(\gamma\) on a Golay code element.

Here parameter \(v\) is encoded in gcode representation, and the return value \(\gamma(v)\) is encoded in cocode representation. These representations are defined in the Description of the mmgroup.mat24 extension.

uint32_t gen_xi_w2_gray(uint32_t v)

Compute function \(w_2\) on a Golay code element.

Here parameter \(v\) is encoded in gcode representation as defined in the Description of the mmgroup.mat24 extension. The function returns \(w_2(v)\), which may be 0 or 1.

uint32_t gen_xi_g_cocode(uint32_t c)

A kind of an inverse of function \(\gamma\).

Given a cocode element \(c\) in cocode representation, the function returns the unique grey Golay code vector \(v\) such that \(\gamma(v)\) is equal to the grey part of \(c\). \(v\) is returned in gcode representation, see the Description of the mmgroup.mat24 extension.

uint32_t gen_xi_w2_cocode(uint32_t v)

Compute function \(w_2\) on a Golay cocode element.

Here parameter \(v\) is encoded in cocode representation as defined in the Description of the mmgroup.mat24 extension. The function returns \(w_2(v)\), which may be 0 or 1.

uint32_t gen_xi_op_xi(uint32_t x, uint32_t e)

Conjugate an element of the group \(Q_{x0}\) with \(\xi^e\).

The function returns \(\xi^{-e} x \xi^e\) for an element \(x\) of the group \(Q_{x0}\). Input \(x\) and the return value are encoded in Leech lattice encoding as described above.

uint32_t gen_xi_op_xi_nosign(uint32_t x, uint32_t e)

Similar to function gen_xi_op_xi, ingoring sign.

The function returns \(\xi^{-e} x \xi^e\) for an element \(x\) of the group \(Q_{x0}\). Input \(x\) and the return value are encoded in Leech lattice encoding as described above. This function computes the result up to sign only.

uint32_t gen_xi_leech_to_short(uint32_t x)

Convert the encoding of an element of the group \(Q_{x0}\).

The function converts an element \(x\) of the group \(Q_{x0}\) from Leech lattice encoding to Short vector encoding and returns the converted element.

uint32_t gen_xi_short_to_leech(uint32_t x)

Convert the encoding of an element of the group \(Q_{x0}\).

The function converts an element \(x\) of the group \(Q_{x0}\) from Short vector encoding to Leech lattice encoding and returns the converted element.

An invalid value \(x\) is converted to 0.

uint32_t gen_xi_op_xi_short(uint32_t x, uint32_t u)

Conjugate an element of the group \(Q_{x0}\) with \(\xi^e\).

The function returns \(\xi^{-e} x \xi^e\) for an element \(x\) of the group \(Q_{x0}\). In contrast to function gen_xi_op_xi, the input \(x\) and the return value are encoded in short vector encoding as described above.

Any invalid element \(x\) is converted to \(x\).

uint32_t gen_xi_make_table(uint32_t b, uint32_t e, uint16_t *ptab)

Create table for operation of \(\xi^e\),.

The function creates a table ptab such that ptab[i] encodes \(\xi^{-e} x \xi^e\), where \(x = 2^{16} \cdot b + i\) encodes a short vector in \(Q_{x0}\) in Short vector encoding. The length of the gererated table is \(l(b) = 2496, 23040, 24576, 32768, 32768\) for \(b = 1,2,3,4,5\).

Here ptab[i] contains the lower 16 bits of the table entry only, with bit 15 equal to the sign bit. The upper 3 bits of ptab[i] depend on b only; they can be deduced from the permutation of the boxes b = 1,...,5 by \(\xi\) described above.

Not all table indices i correspond to short vectors. We put p[i] = i for all invalid indices i.

void gen_xi_invert_table(uint16_t *ptab, uint32_t len, uint32_t ncols, uint16_t *pres, uint32_t len_res)

Invert a table created by function gen_xi_make_table

The function computes the inverse table of a table ptab of length len created by function gen_xi_make_table and stores the inverse table in the buffer referred by pres.

Parameter len_res is the length of the returned table. Parameter ncols must be 24 or 32. This means that only entries ptab[32*i + j] with 0 <= j < ncols are valid.

The details of the inversion of such a table are rather tricky; they are coded in class mmgroup.dev.mm_basics.mm_tables_xi.Pre_MM_TablesXi. The purpose of this function is to speed up the calculations in that class.

void gen_xi_split_table(uint16_t *ptab, uint32_t len, uint32_t mod, uint32_t *psign)

Split a table created by function gen_xi_invert_table

The function splits the sign bit from a table ptab of length len created by function gen_xi_invert_table and replaces ptab[i] by (ptab[i] & 0x7fff) % mod. The sign bit ptab[32*i+j] >> 15 is stored in bit j of entry psign[i].

The purpose of this function is to speed up the computations in class mmgroup.dev.mm_basics.mm_tables_xi.Pre_MM_TablesXi.

C functions implementing a random generator

The python extension mmgroup.generators provides a random generator. Its main purpose is the very fast generation of a vector of integers modulo \(p\) for the representation \(\rho_p\) of the monster group for small numbers \(p\).

The internal operation of the random generator is described in file gen_random.c. Here we describe the python interface of the random generator.

This interface is given by a set of functions that can be imported from python module mmgroup.generators.

A python function dealing with the randon generator requires a seed. Here a seed is either an object returned by function rand_make_seed or None (default).

The default seed is (hopefully) thread save, and it is initialized from volatile sources such as the time, the process and the thread id, etc.

A seed created by function rand_make_seed is initalized from a fixed source (which is a 64-bit integer).

Each seed may be used by one thread only. In python a seed is crreated for each thread whenever needed.

Python functions in module mmgroup.generators

The following functions should be imported from module mmgroup.generators.

.rand_get_seed()

Return the seed associated with the current thread

This function is used for obtaining a suitable python object for calling a C function that deals with the random generator. The user must not modify that seed!

.rand_make_seed(valu)

Create a deterministic seed object for the random generator

The function creates a seed object and returns that object. It is intialized with parameter value, which must be an unsigned 64-bit integer.

.rand_bytes_modp(p, num_bytes, seed=None)

Return a random array of integers mod p

The function returns a one-dimensional numpy array of uniform distributed random integers x with 0 <= x < p. That array has length num_bytes and has dtype = numpy.uint8.

Parameter seed must be either None (default, referring to the default seed) or a seed object created by function rand_make_seed.

.rand_fill_bytes_modp(p, array_bytes, seed=None)

Fill an existing array with random integers mod p

The function fills the one-dimensional numpy array array_bytes with uniform distributed random integers x with 0 <= x < p. That array must have dtype = numpy.uint8.

Parameter seed must be either None (default, referring to the default seed) or a seed object created by function rand_make_seed.

.rand_gen_modp(p, seed=None)

Return random integer x with 0 <= x < p.

Here 1 <= p <= 2**32 must hold.

Parameter seed must be either None (default, referring to the default seed) or a seed object created by function rand_make_seed.

C interface for file gen_random.c

File gen_random.c provides a fast random generator for creating random vectors in the representations of the monster group. This generator supports very fast generation of long random vectors of integers modulo p for p <= 256.

The seed for such a random generator is given by an array of type uint64_t seed[MM_GEN_RNG_SIZE]. In this version we have MM_GEN_RNG_SIZE = 4.

The function gen_rng_seed(uint64_t *seed) fills an existing array seed of that type with random seed data. See description of that function for details. The idea is that in a mulithreading environment every thread has its own random generator. There is also a function gen_rng_seed_no for a determistic intialization of the seed. The user should call function gen_rng_seed_init for initialization.

The function gen_rng_bytes_modp(uint32_t p, uint8_t *out, uint32_t len, uint64_t *seed) writes len uniform random numbers x_i with 0 <= x_i < p to the array referred by the pointer out. Here 1 < p <= 256 must hold.

This function is optimized for generating large random vectors with, say, len >= 1000, as required for the representation of the monster.

Function gen_rng_modp generates a single random number x with 0 <= x < p for a 32-bit number p.

Internal operation.

We use the xoshiro256** pseudo random number generator, see

https://bashtage.github.io/randomgen/bit_generators/xoshiro256.html

We seed a master generator from various external random sources, e.g. the system time, the process id, etc. We use xoshiro256** for both, the master and the user generator. We shift the master generator by 2**128 steps for seeding a user generator.

Functions

void gen_rng_seed_init(void)

Seed the master random generator with random data.

Here seed is an array of 4 integers of type uint64_t.

The function fills the seed with random data, using various sources, e.g. the system time, the process id, etc.

void gen_rng_seed_no(uint64_t *seed, uint64_t seed_no)

Seed a user random generator deterministically.

Here seed is an array of 4 integers of type uint64_t.

The function fills the seed with data depending on the number seed_no.

void gen_rng_seed(uint64_t *seed)

Seed a user random generator from the master random generator.

Here seed is an array of 4 integers of type uint64_t.

The function seeds the user generator with the seed seed from the master random generator. The function is thread-safe if function gen_rng_seed_init() has been called before.

Each thread must use its own seed.

int32_t gen_rng_bytes_modp(uint32_t p, uint8_t *out, uint32_t len, uint64_t *seed)

Generate random integers modulo a small number p

Generate an array of length len of random 8-bit integers x with 0 <= x < p. Here 1 < p <= 256 must hold.

These random integers are written to the array referred by out.

Parameter seed points to the seed for the random generator. That seed must have been created by function gen_rng_seed_rnd` or by a similar function.

The function returns zero in case of success and a nonzero value otherwise.

uint32_t gen_rng_modp(uint32_t p, uint64_t *seed)

Generate one random integer modulo a number p

Generate an integer x with 0 <= x < p. Here 0 <= p < 2**32 must hold. p = 0 is interpreted as p = 2**32.

Parameter seed points to the seed for the random generator. That seed must have been created by function gen_rng_seed_rnd or by a similar function.

The function returns the random number x.

uint64_t gen_rng_bitfields_modp(uint64_t p, uint32_t d, uint64_t *seed)

Generate random bit field of integers modulo a number p

Generate integers x_i, 0 <= i < 64 / d with 0 <= x < p. Here parameter d is the with of a bit field. The function reuturns an unsigned 64-bit integer x with the random value x_i stored in bits i*d + d - 1,..., i *d of x. In case d = 0 we generate one random number in x.

Here 1 <= p < 2**d must hold. p = 0 is interpreted as p = 2**64.

Parameter seed points to the seed for the random generator. That seed must have been created by function gen_rng_seed_rnd or by a similar function.

Description of the mmgroup.clifford12 extension

Quadratic state vectors

The C functions in modules qstate.c and qsmatrix.c perform operations on quadratic state matrices given by triples \((e, A, Q)\) as described in API reference the section Long-term stable storage of vectors of the representation.

A quadratic state vector \(v\) of type qbstate12_type with component ncols = n models a complex vector in a vector space \(V\) of dimension \(2^n\), or an \(2^{n-k} \times 2^k\) matrix.

The basis of vector space \(V\) is labelled by the elements of the Boolean vector space \(\mathbb{F}_2^n\). In C and python programs we represent the element \((x_{n-1}, \ldots, x_{0})\) of \(\mathbb{F}_2^n\) by the integer \(\sum_{0 \leq i < n} 2^i \cdot x_i\). This leads to a natural representation of a vector \(v \in V\) as a one-dimensional complex array of length \(2^n\), starting with index \(0\).

A quadratic state matrix is a quadratic shape vector augmented by an information about its matrix shape. For more details we refer to the description of struct qbstate12_type in file clifford12.h.

Header file clifford12.h

File clifford.h is the header file for shared library mmgroup_clifford12. This comprises the following C modules:

{0}.

Defines

QSTATE12_UNDEF_ROW

Marker for an undefined row index in function qstate12_row_table()

Enums

enum qstate12_error_type

Error codes for functions in this module.

Unless otherwise stated, the functions in modules qstate.c, qmatrix.c, and xsp2co1.c return nonnegative in case of success and negative values in case of failure. Negative return values mean error codes as follows:

Values:

enumerator ERR_QSTATE12_NOTFOUND

No object with the requested property found.

enumerator ERR_QSTATE12_INCONSISTENT

State is inconsistent.

enumerator ERR_QSTATE12_QUBIT_INDEX

Qubit index error.

enumerator ERR_QSTATE12_TOOLARGE

State is too large for this module.

enumerator ERR_QSTATE12_BUFFER_OVFL

Buffer overflow; usually there are now enough rows available.

enumerator ERR_QSTATE12_Q_NOT_SYMM

Bit matrix part Q is not symmetric.

enumerator ERR_QSTATE12_BAD_ROW

Bad row index for bit matrix.

enumerator ERR_QSTATE12_INTERN_PAR

Internal parameter error. Usually a bad row has been requested.

enumerator ERR_QSTATE12_SCALAR_OVFL

Overflow or underflow in scalar factor.

enumerator ERR_QSTATE12_CTRL_NOT

Bad control_not gate. A qubit in a ctrl-not gate cannot control itself.

enumerator ERR_QSTATE12_SHAPE_COMP

Shape mismatch in comparing matrices.

enumerator ERR_QSTATE12_SCALAR_INT

Scalar factor is not an integer.

enumerator ERR_QSTATE12_PARAM

Parameter error.

enumerator ERR_QSTATE12_DOMAIN

Matrix is not is the expected domain.

enumerator ERR_QSTATE12_SHAPE_OP

Shape mismatch in matrix operation.

enumerator ERR_QSTATE12_MATRIX_INV

Matrix is not invertible.

enumerator ERR_QSTATE12_PAULI_GROUP

Matrix is not in the Pauli group.

enumerator ERR_QSTATE12_NOT_MONOMIAL

Matrix is not monomial.

enumerator ERR_QSTATE12_LEECH_OP

Internal error in operation of group Co_0.

enumerator ERR_QSTATE12_REP_GX0

Error in operation of group 2^{1+24}.Co_1.

enumerator ERR_QSTATE12_NOTIN_XSP

Element of G_x0 not in 2^{1+24}.

enumerator ERR_QSTATE12_GX0_TAG

Bad tag for atom in group G_x0.

enumerator ERR_QSTATE12_GX0_BAD_ELEM

Bad element of group G_x0.

struct qstate12_type
#include <clifford12.h>

Description of a quadratic state matrix.

More precisely, a variable of this type decribes the representation \((e', A, Q)\) of a quadratic mapping \(f(e', A, Q)\). Here \(A\) is an \((1+m) \times n\) bit matrix. \(Q\) is a symmetric \((1+m) \times (1+m)\) bit matrix representing an symmetric bilinear form. We always have \(Q_{0,0}=0\). Then the quadratic mapping \(f(e', A, Q): \mathbb{F}_2^n \rightarrow \mathbb{C} \) is given by

\[ f(e',A,Q)(x) = e' \cdot \sum_{{\{{y = (y_0,\ldots,y_m) \in \mathbb{F}_2^{m+1} \mid y_0 = 1, y \cdot A = x\}}}} \exp \left( \frac {\pi \sqrt{{-1}}}{2} \sum_{j,k=0}^m Q_{j,k} y_j y_k \right) \, . \]

Matrices \(A\) and \(Q\) are concatenated to an \( (m+1) \times (n+m+1)\) matrix \(M\) such that \(M_{i,j} = A_{i,j}\) for \(j < n\) and \(M_{i,j} = Q_{i-n,j}\) for \(j \geq n\). Matrix \(M\) is encoded in a one-dimensional array of unsigned 64-bit integers. Here bit \(j\) of entry \(i\) of the array corresponds to \(M_{i,j}\), with bit \(0\) the least significant bit.

We do not update column \(0\) of matrix \(Q\). \(Q_{j,0}\) is inferred from \(Q_{0,j}\) for \(j>0\) and we always have \(Q_{0,0} = 0.\)

We also interpret a quadratic mapping \( f \) from \(\mathbb{F}_2^n\) to \(\mathbb{C}\) as a complex \(2^{n-k} \times 2^k\) matrix \(U\) with entries \(U[i,j] = f(b_0,\ldots,b_{n-1})\), where \((b_{n-1},...,b_0)_2\), is the binary representation of the integer \(i \cdot 2^k + j\), and \(k\) is given by component shape1 of the structure.

The current implementation requires \(n + m <= 63\), so that all columns of the bit matrix \(M\) fit into a 64-bit integer. With this restriction we may still multiply \(2^{12} \times 2^{12}\) matrices \(U\) given as quadratic mappings.

A quadratic mapping may be reduced with function qstate12_reduce() without notice, whenever appropriate.

Warning:

If an external function changes any component of a structure qs of type qstate12_type (or the content of qs.data) then it must set qs.reduced to zero. The only

legal way to

set

qs.reduced to a nonzero value is to call function qstate12_reduce. The functions in this module assume that qs is actually reduced if qs.reduced is not zero.

Public Members

uint32_t maxrows

No of entries of type uint64_t allocated to component data

uint32_t nrows

No \(m + 1\) of rows of bit matrix \(A\) The value nrows = 0 encodes the zero mapping.

uint32_t ncols

No \(n\) of columns of bit matrices \(A\) and \(Q\).

int32_t factor

A integer \(e\) encoding the complex scalar factor \(e'=\) \(\exp(e \pi \sqrt{-1}/4) \cdot 2^{\lfloor e/16 \rfloor / 2}\)

uint32_t shape1

Describes the shape of the quadratic state matrix as explained above.

uint32_t reduced

This is set to 1 if the state is reduced and to 0 otherwise.

uint64_t *data

Pointer to the data bits of the matrix \(M = (A,Q)\).

C functions in bitmatrix64.c

File bitmatrix64.c contains functions for computing with bit matrices. A bit matrix of up to 64 columns is stored in an array of integers of type uint64_t. If matrix m is stored in the array a then entry m[i,j] is given by bit j of the entry a[i] of the array a. Here bit j has valence \(2^j\).

In functions dealing with bit matrices we always give the number of rows as a parameter. In some functions we also give the number of columns. In other functions we assume that there are 64 columns, with unused columns ignored.

Functions

uint32_t uint64_parity(uint64_t v)

Returns the parity of a 64-bit integer v.

uint32_t uint64_low_bit(uint64_t v)

Returns position of lowest bit of a 64-bit integer v.

In case v = 0 the function returns 64.

uint32_t uint64_bit_len(uint64_t v)

Returns the bit length of a 64-bit integer v.

uint32_t uint64_bit_weight(uint64_t v)

Returns the bit weight of a 64-bit integer v.

uint32_t uint64_to_bitarray(uint64_t v, uint8_t *bl)

Convert 64-bit integer v to a bit list.

Return the (sorted) array bl of the positions of all bits set in the integer v. The function returns the length of the computed array, which is equal to the bit weight of v. Array bl should have length at least 64. The function is optimized for integers v of low bit weight.

void bitmatrix64_add_diag(uint64_t *m, uint32_t i, uint32_t j)

Add a diagonal matrix to a bit matrix.

Here bit matrix m has i rows.

We add one to all entries m[k, j+k] for 0 <= k < i`.

void bitmatrix64_mask_rows(uint64_t *m, uint32_t i, uint64_t mask)

Mask rows of a bit matrix.

Here bit matrix m has i rows.

We replace all rows m[k] of m by m[k] & mask. So we zero the entries m[i,j] for all j where bit j is zero in parameter mask.

int32_t bitmatrix64_find_masked_row(uint64_t *m, uint32_t i, uint64_t mask, uint64_t v)

Find (masked) row of a bit matrix with a certain value.

Here bit matrix m has i rows.

The function returns the lowest index k such that m[k] & mask == v holds. The function returns ERR_QSTATE12_NOTFOUND if no such k exists.

int32_t bitmatrix64_to_numpy(uint64_t *m, uint32_t rows, uint32_t cols, uint8_t *a)

Convert bit matrix to numpy array.

Here bit matrix m has rows rows and cols columns.

Then a bust be an array of rows * cols bytes length. The function writes the bit m[i,j] to a[i * cols + j].

The function returns rows * cols in case of success and ERR_QSTATE12_NOTFOUND in case of failure, e.g. if cols > 64.

void bitmatrix64_from_32bit(uint32_t *a32, uint32_t n, uint64_t *a64)

Copy array of 32-bit integers to array of 64-bit integers.

Copy the array a32 of n 32-bit integers to the array a64 of n 64-bit integers. Entries of the array are zero extended.

void bitmatrix64_to_32bit(uint32_t *a32, uint32_t n, uint64_t *a64)

Copy array of 64-bit integers to array of 32-bit integers.

Copy the array a64 of n 64-bit integers to the array a32 of n 32-bit integers. Upper 32 bits of the entries are dropped.

uint32_t bitmatrix64_find_low_bit(uint64_t *m, uint32_t imin, uint32_t imax)

Find position of lowest bit in a bit matrix.

The function returns the lowest position k of a bit in the bit matrix m, which is set to one. It scans only the bits at positions imin <= k < imax. If all bits at these positions are zero then the function returns imax.

Here bit j of m[i] is considered at position 64*i + j. Bit matrix m should have at least (imax + 63) >> 6 entries.

void bitmatrix64_mul(uint64_t *m1, uint64_t *m2, uint32_t i1, uint32_t i2, uint64_t *m3)

Bit matrix multiplication.

Here m1 and m2 have i1 and i2 rows, respectively. Only the lowest i2 columns of m1 are inspected. Matrix m2 may have any number of columns, up to 64.

The function computes the matrix product m1 * m2 in the array m3. Matrix m3 has i1 rows and the same number of columns as m2.

Array m3 may be equal to array m1; but it may not overlap with m2.

uint64_t bitmatrix64_vmul(uint64_t v, uint64_t *m, uint32_t i)

Multiplication of a vector with a bit matrix.

Here matrix m has i rows; and v is a bit vector. The function returns the product of the bit vector v with the bit matrix m. If bit vector v has k leading zero bits then only the lowest 64 - k rows of matrix m are inspected.

int32_t bitmatrix64_rot_bits(uint64_t *m, uint32_t i, int32_t rot, uint32_t nrot, uint32_t n0)

Rotate columns of a bit matrix.

Here bit matrix m has i rows.

For n0 <= j < n0 + nrot we map column j of matrix m to column n0 + (j + rot) % nrot. E.g. nrot = 3, rot = 1, n0 = 0 means that columns are mapped as 0->1, 1->2, 2->0.

The function returns 0 in case of success and a negative value if any column with index >= 64 is involved in the rotation.

int32_t bitmatrix64_xch_bits(uint64_t *m, uint32_t i, uint32_t sh, uint64_t mask)

Exchange columns of a bit matrix.

Here bit matrix m has i rows.

Exchange column j with column j + sh of the bit matrix m, if bit j of mask is set. If bit j of mask is set then bit j + sh of mask must not be set. No bit of mask at position greater or equal to 64 - sh may be set.

E.g. qstate12_xch_bits(m, 1, 0x11) maps columns (…,6,5,4,3,2,1,0) to columns (…,6,4,5,3,2,0,1).

The function returns 0 in case of success and a negative value if any column with index >= 64 is involved in the operation.

int32_t bitmatrix64_reverse_bits(uint64_t *m, uint32_t i, uint32_t n, uint32_t n0)

Reverse columns of a bit matrix.

Here bit matrix m has i rows.

The function reverses n columns of the bit matrix m starting at column n0. So it exchanges bit m[k, n0 + j] with bit m[k, n0 + n - 1 - j] for 0 <= j < n and 0 <= k < i`.

The function returns 0 in case of success and a negative value if any column with index >= 64 is involved in the operation.

int32_t bitmatrix64_t(uint64_t *m1, uint32_t i, uint32_t j, uint64_t *m2)

Transpose a bit matrix.

Here m1 is an i times j bit matrix. Here bit m1[m,n] is given by (m1[m] >> n) & 1. The function writes the transposed bit matrix of m1 to m2.

uint32_t bitmatrix64_echelon_h(uint64_t *m, uint32_t i, uint32_t j0, uint32_t n)

Convert bit matrix m to reduced row echelon form.

Matrix m has i rows. Here we assume that the leading bit of a row of matrix m is the most significant bit in that row. For echelonizing m, we pivot over nolumns j0-1,...,j0-n in that order, ignoring the other columns. We perform row operations (on all possible columns 0,…,63) for changing matrix m.

The function returns the number of rows in the (echelonized) matrix m that have a nonzero bit in at least one of the pivoted columns.

uint32_t bitmatrix64_echelon_l(uint64_t *m, uint32_t i, uint32_t j0, uint32_t n)

Convert bit matrix m to reduced row echelon form.

Matrix m has i rows. Here we assume that the leading bit of a row of matrix m is the least significant bit in that row. For echelonizing m, we pivot over columns j0,...,j0+n-1 in that order, ignoring the other columns. We perform row operations (on all possible columns 0,…,63) for changing matrix m.

The function returns the number of rows in the (echelonized) matrix m that have a nonzero bit in at least one of the pivoted columns.

int32_t bitmatrix64_cap_h(uint64_t *m1, uint64_t *m2, uint32_t i1, uint32_t i2, uint32_t j0, uint32_t n)

Compute intersection of row spaces of two bit matrices.

Bit matrix m1 has i1 rows, and bit matrix m2 has i2 rows. Both matrices, m1 and m2, must be given in reduced echelon form in the sense of function bitmatrix64_echelon_h.

The function changes m1 and m2 using row operations in such a way that the lower rows of m1 and m2 contain the intersection of the rows spaces of the two matrices. For computing that intersection we consider only columns j0-1,...,j0-n of matrices m1 and m2. However, we do each row operation on all possible columns 63,...,0.

The function returns the dimension n_cap of the intersection of the row spaces of m1 and m2, ignoring all columns except for columns j0-1,...,j0-n.

Assume that the echelonized input matrices m1 (and m2) have r1 (and r2) leading rows that have at least one nonzero bit in columns j0-1,...,j0-n, respectively. Then the function changes the leading r1 rows of m1 and the leading r2 rows of m2 only. These rows are changed in such a way that the last n_cap of these rows of the two matrices are equal in columns j0-1,...,j0-n. So these last rows contain the intersection of the row spaces of m1 and m2`, ignoring all columns but j0-1,...,j0-n.

Parameters j0 and n must be the same in this function, and in the previous two calls to function bitmatrix64_echelon_h used for echelonizing matrices m1 and m2. This function computes garbage in the arrays m1 and m2, unless both matrices, m1 and m2, have been echelonized in that way before calling this function.

A negative return value n_cap means that at least one of the input matrices m1 and m2 has not been echelonized correctly. This function may or may not detect an incorrect echelonization of an input matrix.

int32_t bitmatrix64_inv(uint64_t *m, uint32_t i)

Bit matrix inversion.

Here mhas i rows and i columns. i <= 32 must hold. The function inverts matrix m in place.

It returns 0 in case of success and ERR_QSTATE12_MATRIX_INV if m is not invertible.

int64_t bitmatrix64_solve_equation(uint64_t *m, uint32_t i, uint32_t j)

Solve a linear bit equation.

Let M be the bit matrix with i rows and j columns stored in the array m in the usual way. Let v be the column bit vector stored in column j of the array m.

Then the function tries to solve the equation transpose(v) * m = x If such a solution x exists then the function returns the transposed vector of the column vector x as a nonnegative integer.

The function returns ERR_QSTATE12_MATRIX_INV if no solution x exists. As a side effect, it echelonizes the lowest j + 1 columns of matrix m with function bitmatrix64_echelon_h.

C functions in uint_sort.c

File uint_sort.c contains functions for sorting arrays of unsigned 32-bit and 64-bit integers.

Functions

void bitvector32_copy(uint32_t *a_src, uint32_t n, uint32_t *a_dest)

Copy an array to type uint32_t[]

Copy the array a_src of length n to a_dest. Any kind of overlap is supported.

void bitvector32_heapsort(uint32_t *a, uint32_t n)

Sort an array a of length n with heap sort.

Here a is an array of n integers of type uint32_t. The array is sorted in place. The function is exported for debuging. You should use function bitvector32_sort.

void bitvector32_sort(uint32_t *a, uint32_t n)

Sort an array a of type uint32_t[] of length n

Here a is an array of n integers of type uint32_t. The array is sorted in place.

We use quicksort as the main sorting algorithm. If quicksort reaches a certain recursion level then we switch to heap sort. This guarantees a run time of \(O(n \mathop{log}(n))\). For small arrays we use insert sort.

void bitvector64_copy(uint64_t *a_src, uint32_t n, uint64_t *a_dest)

Copy an array to type uint64_t[]

Copy the array a_src of length n to a_dest. Any kind of overlap is supported.

void bitvector64_heapsort(uint64_t *a, uint32_t n)

Sort an array a of length n with heap sort.

Here a is an array of n integers of type uint64_t. The array is sorted in place. The function is exported for debuging. You should use function bitvector64_sort.

void bitvector64_sort(uint64_t *a, uint32_t n)

Sort an array a of type uint64_t[] of length n

Here a is an array of n integers of type uint64_t. The array is sorted in place.

We use quicksort as the main sorting algorithm. If quicksort reaches a certain recursion level then we switch to heap sort. This guarantees a run time of \(O(n \mathop{log}(n))\). For small arrays we use insert sort.

C functions in qstate12.c

File qstate12.c contains functions for quadratic state matrices as described in the API reference in section Computation in the Clifford group.

C functions in this module are prefixed with qbstate12_. Unless otherwise stated, these functions return an int32_t, where a nonegative value is interpreted as success, and a negative value is intepreted as failure. Error codes are documented in file clifford12.h.

Typical names for parameters of functions in this module are:

Name           | Parameter type
-------------- | ----------------------------------------------
pqs, pqs1, ... |  Pointer to structure of type qbstate12_type
nqb            |  Number of qubits, i.e. of columns of matrix 
               |  A in a structure of type qbstate12_type.
nrows          |  Number of rows of matrix   A  or   Q  
               |  in a structure of type qbstate12_type.
i, i1, ...     |  Index of a row of matrix   A  or   Q  ,  
               |  starting with 0.
j, j1, ...     |  Index of a column of matrix   A  , with a 
               |  column of   A  , corrsesponding to a qubit, 
               |  starting with j = 0.
               |  If appropriate, an index  j >= ncols refers 
               |  to column (j - ncols) of matrix    Q  .
pv, pv1,...    |  Pointer to a row or column vector of matrix 
               |  A, Q  or   M  .

Functions

int32_t qstate12_check(qstate12_type *pqs)

Check a structure of type qstate12_type.

Return 0 if ok, or an error code if there is any error in the structure referred by pqs. Part Q[i,0] is set to Q[0, i]. Part Q[0,0] is set ot 0. Irrelevant data bits in valid data rows are zeroed.

int32_t qstate12_set_mem(qstate12_type *pqs, uint64_t *data, uint32_t size)

Assign memory to a structure of type qstate12_type.

Assign the array data of size integers of type uint64_t as memory to structure of type qstate12_type referred by pqs.

int32_t qstate12_zero(qstate12_type *pqs, uint32_t nqb)

Set a structure of type qstate12_type to a zero vector.

Set the structure referrred by pqs to a zero column vector of length 2**nqb, corresponding to the zero state of nqb qubits.

int32_t qstate12_vector_state(qstate12_type *pqs, uint32_t nqb, uint64_t v)

Set a structure of type qstate12_type to a state vector.

Set the structure referrred by pqs to a unit column vector |v> of length 2**nqb. Thus for the quadratic mapping qs referred by pqs we have qs(x) = 1 if x is equal to the bit vector v and qs(x) = 0 otherwise.

int32_t qstate12_set(qstate12_type *pqs, uint32_t nqb, uint32_t nrows, uint64_t *data, uint32_t mode)

Set a data in a structure of type qstate12_type

Set pqs->nqb = nqb so that the state qs referred by pqs corresponds to a column vector of length 2**nqb. Set pqs->nrows = nrows, and copy nrows rows of the array data to pqs->data.

If mode == 1 then copy the upper triangular part of the data bit matrix part Q to the lower triangular part.

If mode == 2 then copy the lower triangular part of the data bit matrix part Q to the upper triangular part.

Otherwise the part Q of the data bit matrix must be symmetric.

The resulting state is checked with function qstate12_check().

int32_t qstate12_copy(qstate12_type *pqs1, qstate12_type *pqs2)

Copy a state of type qstate12_type to another state.

Copy the data in the state referred by pqs1 to the data in the state referred by pqs2. Memory must have been allocated to the state pqs2 with function qstate12_set_mem()

int32_t qstate12_copy_alloc(qstate12_type *pqs1, qstate12_type *pqs2, uint64_t *data, uint32_t size)

Copy a state of type qstate12_type and allocate mamory.

Equivalent to

 qstate12_set_mem(pqs2, data, size);
 qstate12_copy(pqs1,pqs2);

int32_t qstate12_factor_to_complex(int32_t factor, double *pcomplex)

Convert a scalar factor to a complex number.

The function takes a scalar factor (as given by the component factor in the structure qstate12) and converts that factor to a complex number. The real part of the result is returned in pcomplex[0] and the imaginary part in pcomplex[1].

Caution:

If bit 3 of factor is set then the function calculates the complex number 0.

The function returns:

  4  if the result is complex, but not real.

  3  if the result is real, but not rational.

  2  if the result is rational, but not integral.

  1  if the result is integral, but not  zero.

  0  if the result is zero.

  ERR_QSTATE12_SCALAR_OVFL in case of overflow or underflow

int32_t qstate12_factor_to_int32(int32_t factor, int32_t *pi)

Convert a scalar factor to a 32-bit integer.

The function takes a scalar factor (as given by the component factor in the structure qstate12) and converts that factor to an integer. That integer is stored in pi[0].

Caution:

If bit 3 of factor is set then the function calculates the integer zero.

The function returns:

1 if the result is integral, but not zero.

0 if the result is zero.

ERR_QSTATE12_SCALAR_OVFL in case of overflow or underflow

ERR_QSTATE12_SCALAR_INT if the result is not an integer

int32_t qstate12_conjugate(qstate12_type *pqs)

Conjugate the state referred by pqs

The function changes the state referred by pqs to its complex complex state.

int32_t qstate12_mul_scalar(qstate12_type *pqs, int32_t e, uint32_t phi)

Multiply the state referred by pqs by a scalar.

The function multiplies the state qs referred by pqs with the scalar

  2**(e/2) * exp(phi * pi * sqrt(-1) / 4)  .

int32_t qstate12_abs(qstate12_type *pqs)

Replace entries of a state by their absolute values.

The function replaces the entries of the state referred by pqs by their absolute values.

uint64_t qstate12_get_column(qstate12_type *pqs, uint32_t j)

Return column j of the bit matrix M stored in pqs->data as a bit vector in an integer of type uin64_t.

int32_t qstate12_del_rows(qstate12_type *pqs, uint64_t v)

Delete all rows i, 1 <= i < pqs->nrows, from the bit matrix M stored in pqs->data, if bit i in the bit vector *pv is set. Here we also adjust the quadratic form Q which is part of the bit matrix M. Row 0 is never deleted.

int32_t qstate12_insert_rows(qstate12_type *pqs, uint32_t i, uint32_t nrows)

Insert nrows zero rows into the bit matrix M stored in pqs->data, starting before row i. The corresponding zero columns of the quadratic form Q, which is part of the bit matrix M, are also inserted. 1 <= i <= nrows must hold. This process multiplies the state vector by the scalar 2**nrows.

int32_t qstate12_mul_Av(qstate12_type *pqs, uint64_t v, uint64_t *pw)

Let M be the bit matrix stored in pqs->data. We return the matrix product w = A * Transposed(v), where A is the A part of the bit matrix M, as a bit vector.

int32_t qstate12_rot_bits(qstate12_type *pqs, int32_t rot, uint32_t nrot, uint32_t n0)

Rotate qubit arguments of the state qs referred by pqs.

For n0 <= i < n0 + nrot we map qubit i to qubit n0 + (i + rot) % nrot. E.g. nrot = 3, rot = 1, n0 = 0 means that bits are mapped as 0->1, 1->2, 2->0. Let nn1 = pqs->ncols. Then the function changes the quadratic mapping qs to referred by pqs to qs1 with

 qs1(x[nn1-1],...,x[n0+nrot],y[nrot-1],...,y[0],x[n0-1],...,x[0])
 = qs(x[nn1-1],...,x[n0+nrot],z[nrot-1],...,z[0],x[n0-1],...,x[0]),

where z[j] = y[j - rot (mod 3)].

int32_t qstate12_xch_bits(qstate12_type *pqs, uint32_t sh, uint64_t mask)

Exchange qubit arguments of the state qs referred by pqs.

Exchange qubit j with argument bit j + sh of the state qs referred by pqs, if bit j of mask is set. If bit j of mask is set then bit j + sh of mask must not be set. No bit of mask at position greater or equal to pqs->ncols - sh may be set.

E.g. qstate12_xch_bits(pqs, 1, 0x11) changes the quadratic mapping qs to qs1 with

 qs1(...,x6,x5,x4,x3,x2,x1,x0) = qs(...,x6,x4,x5,x3,x2,x0,x1) .

void qstate12_pivot(qstate12_type *pqs, uint32_t i, uint64_t v)

Auxiliary function for function qstate12_reduce().

Let M be the bit matrix stored in pqs->data. The pivoting process is controlled by the bit vector v. If k < i and bit k of vis set then row i of bit matrix Mis xored to row k of M. The columns of the part Q of M are also adjusted. 1 <= i < pqs->nrows must hold. Pivoting does not change the state.

For internal use only. Input conditions are not checked.

int32_t qstate12_sum_up_kernel(qstate12_type *pqs)

Auxiliary function for function qstate12_reduce().

Sum up the kernel of the transformation matrix A, which is part of the bit matrix M = pqs->data. We assume that A is echelonized in the sense that all nonzero rows of A are linear independent and that they occur before the zero rows.

int32_t qstate12_echelonize(qstate12_type *pqs)

Convert the state represented by pqs to echelon form.

The representation state referred by pqs is converted to (not necessarily reduced) echelon form, and the kernel of bit matrix A, which is part of the bit matrix M = pqs->data is summed up as described in the guide, section ‘Reducing the representation of a quadratic mapping’. The representation of the state is not changed if this is already the case.

This function does not change the state.

int32_t qstate12_check_reduced(qstate12_type *pqs)

Check if the state represented by pqs is reduced.

The function puts pqs->reduced = 1 if the state is reduced; and it performs no action on pqs otherwise. It returns the updated value of pqs->reduced (which is 0 or 1) in case of success, and a negative value in case of failure.

This check is much faster than actually performing a reduction. The function may perform some easy reduction steps on the state.

int32_t qstate12_reduce(qstate12_type *pqs)

Reduce the state represented by pqs

The representation state referred by pqs is converted to reduced echelon form, and the kernel of bit matrix A, which is part of the bit matrix M = pqs->data is summed up as described in the guide, section ‘Reducing the representation of a quadratic mapping’. The representation of the state is not changed if this is already the case.

This function does not change the state.

int32_t qstate12_row_table(qstate12_type *pqs, uint8_t *row_table)

Compute a certain table for the state referred by pqs

Compute a row table for the state qs referred by pqs. Here qs must be in (not necessarily reduced) echelon form.

The function computes row_table[j] = i if the leading bit of row i of part A of the bit matrix M = pqs->data is in column j, for 0 <= j < pqs->ncols and 1 <= i < pqs->nrows. If now such row exists for column j then we put row_table[j] = QSTATE12_UNDEF_ROW.

The representation of the state is not changed.

int32_t qstate12_equal(qstate12_type *pqs1, qstate12_type *pqs2)

Check equality of two states.

Return 1 if the states referred by pqs1 and pqs2 are equal, 0 if not, and a negative number in case of error. Both states are reduced before comparing them.

int32_t qstate12_extend_zero(qstate12_type *pqs, uint32_t j, uint32_t nqb)

Insert qubits into a state and set them to 0.

We insert nqb zero qubits into the state qs referred by pqs starting at position j.

Let n = pqs->ncols so that the state qs referred by pqs depends on n qubits. We change qs to the following state qs1 depending on n + nqb qubits:

qs1(x[n-1],...,x[j],y[nqb-1],...,y[0],x[j-1]...,x[0]) is equal to qs(x[n-1],...,x[j],x[j-1]...,x[0]) if y[0] = ... = y[nqb-1] = 0 and equal to zero otherwise. So we increment pqs->ncols by nqb.

If the input is reduced then the result is also reduced. pqs->shape1 is set to 0, i.e. a column vector is returned.

int32_t qstate12_extend(qstate12_type *pqs, uint32_t j, uint32_t nqb)

Insert qubits into a state.

We insert nqb qubits into the state qs referred by pqs starting at position j.

Let n = pqs->ncols so that the state qs referred by pqs depends on n qubits. We change qs to the following state qs1 depending on n + nqb qubits:

qs1(x[n-1],...,x[j],y[nqb-1],...,y[0],x[j-1]...,x[0]) is equal to qs(x[n-1],...,x[j],x[j-1]...,x[0]). So we increment pqs->ncols by nqb.

The result is not reduced. pqs->shape1 is set to 0, i.e. a column vector is returned.

int32_t qstate12_sum_cols(qstate12_type *pqs, uint32_t j, uint32_t nqb)

Sum up the functional values for some qubits.

We sum up nqb qubits of the state qs referred by pqs starting at position j.

Let n = pqs->ncols so that the state qs referred by pqs depends on n qubits. We change qs to the following state qs1 depending on n - ncols qubits:

qs1(x[n-1],...,x[j+nqb],x[j-1],...,x[0]) = sum_{x[j+nqb-1],...,x[j]} qs1(x[nn1-1],...,x[0]) . So we decrement pqs->ncols by nqb.

The output is not reduced. pqs->shape1 is set to 0, i.e. a column vector is returned.

int32_t qstate12_restrict_zero(qstate12_type *pqs, uint32_t j, uint32_t nqb)

Restrict nqb qubits starting at postion j to 0.

Let n = pqs->ncols so that the state qs referred by pqs depends on n qubits. We change qs to the following state qs1 depending on n qubits:

qs1(x[n-1],...,x[0]) is equal to qs(x[n-1],...,x[0]) if x[j] = ... = x[j+nqb-1] = 0 and equal to zero otherwise. We do not change the shape of qs.

The output is reduced if the input is reduced.

int32_t qstate12_restrict(qstate12_type *pqs, uint32_t j, uint32_t nqb)

Restrict some qubits to 0 and delete them.

Similar to function qstate12_restrict_zero, but with deleting the restricted qubits.

Let n = pqs->ncols so that the state qs referred by pqs depends on n qubits. We change qs to the following state qs1 depending on n1 = n - nqb qubits:

qs1(x[n1-1],...,x[0]) is equal to qs(x[n1-1],...,x[j],0,...,0,x[j-1],...,x[0]). So we decrement pqs->ncols by nqb.

The output is not reduced. pqs->shape1 is set to 0, i.e. a column vector is returned.

In quantum computing theory this operation can be interpreted as measurement of the corresponding qubits with postselection, setting all measured qubits to 0.

int32_t qstate12_gate_not(qstate12_type *pqs, uint64_t v)

Apply a not gate to a state.

Change the state qs referred by pqs to a state qs1 with qs1(x) = qs(x (+) v), where '(+)' is the bitwise xor operation. The result is not reduced.

Computing qstate12_gate_not(pqs, 1 << j) corresponds to negating qubit j.

int32_t qstate12_gate_ctrl_not(qstate12_type *pqs, uint64_t vc, uint64_t v)

Apply a contol-not gate to a state.

Change the state qs referred by pqs to a state qs1 with qs1(x) = qs(x (+) <vc,x> * v), where '(+)' is the bitwise xor operation, and <.,.> is the scalar product of bit vectors. The result is not reduced. The scalar product of the bit vectors j and jc must be zero. Otherwise the ctrl not operation is not unitary.

Computing qstate12_gate_ctrl_not(pqs, 1 << jc, 1 << j), for jc != j, corresponds to applying a controlled not gate to qubit j, contolled by qubit jc.

int32_t qstate12_gate_phi(qstate12_type *pqs, uint64_t v, uint32_t phi)

Apply a phase gate to a state.

Change the state qs referred by pqs to a state qs1 with qs1(x) = qs(x) * sqrt(-1)**(phi * <v,x>), where <.,.> is the scalar product of bit vectors and '**' denotes exponentiation. The result is reduced if the input is reduced. Computing qstate12_gate_ph(pqs, 1 << j, phi) corresponds to applying a phase (phi * pi/2) gate to qubit j.

int32_t qstate12_gate_ctrl_phi(qstate12_type *pqs, uint64_t v1, uint64_t v2)

Apply a controlled phase gate to a state.

Change the state qs referred by pqs to a state qs1 with qs1(x) = qs(x) * (-1)**(<v1,x>*<v2,x>), where <.,.> is the scalar product of bit vectors and '**' denotes exponentiation.

The result is reduced if the input is reduced. Computing qstate12_gate_ctrl_phi(pqs, 1 << j1, 1 << j2) corresponds to applying a phase pi gate to qubit j2 controlled by qubit j1.

int32_t qstate12_gate_h(qstate12_type *pqs, uint64_t v)

Apply Hadamard gates to a state.

Apply a Hadamard gate to all qubits j of the state qs (referred by pqs) with v & (1 << j) == 1. Applying a Hadamard gate to gate j changes a state qs to a state 1/sqrt(2) * qs1, where qs1(..,x[j+1],x_j,x[j-1],..) = qs(..,x[j+1],0,x[j-1],..)

  • (-1)**(x_j) * qs(..,x[j+1],1,x[j-1],..) . The result is not reduced.

C functions in qstate12io.c

File qstate12io.c contains functions for converting quadratic state matrices as described in the API reference in section Computation in the Clifford group to complex numbers.

The function in this module are coded according the conventions in module qstate12.c.

Functions

int32_t qstate12_complex(qstate12_type *pqs, double *pc)

Expand a state to an array of complex numbers.

Expand the state qs referred by pqs to the array referred by the pointer pc. The real part of qs[i] is stored in pc[2*i] and the imaginary part of qs[i] is stored in pc[2*i+1]. The function reduces qs. Here the integer i is interpreted as a bit vector as usual.

pqs->shape1 is ignored. The user has to care for the shape of the returned array. The state qs is reduced.

Caution: The function sets 2 * 2**pqs->ncols entries in the array pc.

Return value is as in function qstate12_entries.

int32_t qstate12_entries(qstate12_type *pqs, uint32_t n, uint32_t *v, double *pc)

Convert entries of a state to complex numbers.

The function computes the entries qs[v[i]] of the state qs referred by pqs for 0 <= i < n and stores these entries in the array pc. The real part of qs[v[i]] is stored in pc[2*i] and the imaginary part is stored in pc[2*i+1]. The state qs is reduced.

Caution: The function sets 2 * n entries in the array pc.

Depending on the computed matrix entries, the function returns

 4  if all entries are complex, but not all are real.

 3  if all entries are real, but not all are rational.

 2  if all entries are rational, but not all are integers.

 1  if all entries are integers, but not all are zero.

 0  if all entries are zero.

A negative return value indicates an error.

int32_t qstate12_int32(qstate12_type *pqs, int32_t *pi)

Expand a state to an array of 32-bit integers.

Expand the state qs referred by pqs to the array of integers (of type int32_t) referred by the pointer pi. Here qs[i] is stored in pi[i]. The function reduces qs. The integer i is interpreted as a bit vector as usual.

pqs->shape1 is ignored. The user has to care for the shape of the returned array. The state qs is reduced.

Caution: The function sets 2**pqs->ncols entries in the array pc.

The function returns 0 in case of success, and a negative value in case of error, e.g. if qs is not integral or in case of integer overflow.

int32_t qstate12_to_signs(qstate12_type *pqs, uint64_t *bmap)

Store the signs of a real quadratic state in an array.

Let qs be the quadratic state matrix referred by pqs. Assume that qs is real and has shape (I, J). Let t[i * 2**J + j] = 0, 1, or 3, if entry qs[i, j] is zero, positive, or negative, respectively. Then we store t[k] in bits 2 * l + 1, 2 * l of bmap[k >> 5], with l = k & 0x1f.

The function returns 0 in case of success, and a negative value in case of error, e.g. if qs is not a real matrix.

int32_t qstate12_compare_signs(qstate12_type *pqs, uint64_t *bmap)

Compare signs of a real quadratic state with an array.

Let qs and bmap as in function qstate12_to_signs. The function returns 1 if the signs of qs are stored in in bmap as described in function qstate12_to_signs. Otherwise the function returns 0. The function also returns 0 if matrix qs is not real. It returns a negative value in case of an error.

int32_t qstate12_from_signs(uint64_t *bmap, int32_t n, qstate12_type *pqs)

Construct a state vector from a sign matrix.

Let bmap be an array with 2**n entries representing 0 or (positive or negative) signs as in function qstate12_to_signs. Then bmap may correspond to a quadratic state vector with entries 0, 1, and -1.

If these enrties correspond to a quadratic state vector V (with entries 0, 1, and -1) then the function stores V in the quadratic state vector qs referred by pqs and returns 0. The returned state qs is a (column) vector of shape (0, n). If the array bmap does not correspond to any quadtatic state vector then the function sets qs to the zero vector of shape (0, n)and returns -1.

The function returns negative value less than -1 in case of an error.

int32_t qstate12_mul_matrix_mod3(qstate12_type *pqs, uint64_t *v, uint64_t w)

Compute a certain product modulo 3 with a state matrix.

Let qs be the quadratic state matrix referred by pqs. Assume that qs is rational and has shape (I, J). Let q be the row vector of length 2**(I+J) with q[i * 2**J + j] = qs[i, j].

We consider v as a 2**(I+J) times 32 matrix M of integers modulo 3 with M[k,l] stored in bits 2*l+1 and 2*l of entry v[k]. We consider w as a column vector of 32 integers mod 3 with w[l] stored in bits 2*l+1 and 2*l of the variable w.

Then the function returns the matrix product q * M * v (modulo 3) as a nonnegative integer less than 3. It returns a negative value in case of error, e.g. if qs is not rational.

C functions in qmatrix12.c

File qmatrix12.c

contains basic functions for quadratic

state matrices as described in the

API reference in section Computation in the Clifford group. The functions is this module are based on the functions in module qstate.c and on the data structures defined in module clifford12.h.

C functions in this module are prefixed with qbstate12_. Unless otherwise stated, these functions return an int32_t, where a nonegative value is interpreted as success, and a negative value is intepreted as failure. Error codes returned by functions in this module are documented in file clifford12.h.

A structure qs of type qstate12_type defines a function \(f: \mathbb{F}_2^n \rightarrow \mathbb{C}\) and also a \(2^{n-k} \times 2^k\) matrix \(M\) with entries \(M[i,j] = f(2^k \cdot i+j)\). Here we idenitfy the nonegative integers \( < 2^n\) with the bit vectors given by their binary representation. For the shape parameters \(n, k\) of a state qs we have \(n\) = qs.ncols, \(k\) = qs.qstate1. Thus indices of vectors and matrices start with 0, as usual in C and python.

While the functions in module qstate.c mostly ingnore the shape parameter \(k\), the functions in this module use that shape parameter for determining the shape of a matrix.

Functions

int32_t qstate12_std_matrix(qstate12_type *pqs, uint32_t rows, uint32_t cols, uint32_t rk)

Create a standard matrix with entries 1 in the diagonal.

Create a standard matrix qs with 2**rows rows and 2**cols columns as an object of type qstate12_type, and store the result in *pqs. Diagonal entries qs[i,i] are equal to one for ì < 2**rk. All other entries of the matrix are zero. 0 <= rk < min(nows, cols) must hold.

int32_t qstate12_unit_matrix(qstate12_type *pqs, uint32_t nqb)

Create a unit matrix.

Create a unit matrix with 2**nqb rows and 2**nqb columns as an object of type qstate12_type, and store the result in *pqs.

int32_t qstate12_monomial_column_matrix(qstate12_type *pqs, uint32_t nqb, uint64_t *pa)

Create matrix with one nonzero entry in each column.

Create a matrix T with 2**nqb rows and 2**nqb columns as an object of type qstate12_type, and store the result in *pqs.

Matrix T is a real 2**nqb times 2**nqb transformation matrix which is monomial in the sense that each column contains exactly one nonzero entry 1 or -1. So left multiplication with T maps unit vectors to (possibly negated) unit vectors. It transforms a complex column vector w of length 2**nqb to a vector T * w.

pa refers to an array a of integers a[i] of length nqb + 1. Each integer a[i] is interpreted as a bit field via its binary representation. So a[i,j] means (a[i] >> j) & 1. a[i, j1:j2] means the bit field a[i,j1],...,a[i,j2-1].

For any bit vector v of length nqb let |v> be the unit vector with index v. For any bit vector v of length nqb + 1 let |v> be the (possibly negated) unit vector (-1)**v[nqb] * |v[0:nqb]>. |v1 ^ v2> and |1 << v1> are defined via the corrresponding operators << and ^ in C.

Then T maps

 |0>      to  |a[0, 0:nqb+1]>

 |1 << i> to  |a[0, 0:nqb+1] ^ a[i+1, 0:nqb+1]>

T maps unit vectors to (possibly negated) unit vectors, so T(v) is well defined by |T(v)> = T(|v>) for a bit field v of length nqb + 1. We have

 |T(v1 ^ v2)> = (-1)**Q(v1,v2) * |T(v1) ^ T(v2) ^ T(0)>,

for bit fields v1, v2 of length nqb + 1 and an alternating bilinear form Q depending on the lowest nqb bits of v1 and v2 only. Thus T is defined by the above equation and Q. The bilinear form Q is defined by:

Q(v1, v2) = Q(v2, v1),  Q(v1, v1) = 0,  and

Q(1 << i, 1 << j) =  a[i + 1, j + nqb + 1]``,  
for ``0 <= j < i < nqb``.

int32_t qstate12_monomial_row_matrix(qstate12_type *pqs, uint32_t nqb, uint64_t *pa)

Create matrix with one nonzero entry in each row.

Similar to qstate12_monomial_column_matrix(); but we create a matrix T which is monomial in the sense that each row contains exactly one nonzero entry 1 or -1. qstate12_monomial_row_matrix(*pqs, nqb, *pa) creates the transposed matrix of qstate12_monomial_column_matrix(*pqs, nqb, *pa).

int32_t qstate12_monomial_matrix_row_op(qstate12_type *pqs, uint32_t *pa)

Obtain operation of a monomial quadratic state matrix.

A monomial matrix maps unit vectors to unit vectors, if we ignore scalar factors. Let qs be the matrix referred by pqs. We assume that qs has exactly one nonzero entry in each row. Then right multiplication with qs maps unit vectors to unit vectors, up to a scalar factor. Otherwrise the function returns ERR_QSTATE12_NOT_MONOMIAL.

If we label each unit vector by a bit vector then the operation of qs on these bit vectors labels is affine. If qs has shape (r,c) then we compute an r+1 times c bit matrix a in the array referred by pa with the following property:

Label (b[0],...,b[r-1]) is mapped to label (1, b[0],...,b[r-1]) * a.

The function returns the number r+1 of rows of a.

From bit matrix a we can construct a unique matrix qs1, of the same shape as qs, that maps unit vectors to unit vectors as given by the mapping of the labels. In case r == c this can be done by calling qstate12_monomial_row_matrix(pqs1, r, pa), where pqs1 points to a buffer for qs1.

Then qs can be obtained by multiplying qs1 with a diagonal matrix.

int32_t qstate12_mat_reshape(qstate12_type *pqs, int32_t rows, int32_t cols)

Change the shape of a matrix.

Reshape the matrix T referred by pqs will be a 2**rows times 2**cols matrix.

The old shape of the matrix is (n-k, k) with n, k given by n = pqs->ncols, k = pqs->shape1. Reshaping must not change the number of entries, so rows + cols == n must hold. We follow the convention in the python numpy package for reshaping matrices. Thus index [i,j] of a matrix with shape (I,J) corresponds to the index i * 2**J + j in the one-dimensional array storing that matrix.

If rows or cols is -1 then it is calculated from the old shape of the matrix. If both, rows and cols, are -1 then rows is set to 0 and cols is calculated.

int32_t qstate12_mat_t(qstate12_type *pqs)

Transpose a matrix in place.

The quadratic state matrix qs referred by pqs is transposed in place. The result is not reduced.

int32_t qstate12_mat_trace(qstate12_type *pqs, double *p_trace)

Compute the trace of a quadratic state matrix.

The function computes the trace of the quadratic state matrix qs referred by pqs. The real part of the trace is stored in p_trace[0] and the imaginary part is stored in p_trace[1]. The state qs is reduced.

Return value is as in function qstate12_factor_to_complex.

int32_t qstate12_mat_itrace(qstate12_type *pqs, int32_t *p_itrace)

Compute the trace of a quadratic state matrix.

Similar to function qstate12_mat_trace, buth the trace is stored as an integer in p_itrace[0].

Return value is as in function qstate12_factor_to_int32.

int32_t qstate12_prep_mul(qstate12_type *pqs1, qstate12_type *pqs2, uint32_t nqb)

Auxiliary low-level function for function qstate12_product()

Prepare the states qs1 and qs2 referred by pqs1 and pqs2 for matrix multiplication. Here the summation in that operation runs over first nqb qubits of qs1 and qs2, regardless of the shape of the input matrices. The function returns a number row_pos, so that, after preparation, the first nqb columns of submatrices A1 and A2 of the bit matrices M1 and M2 corresponding to qs1 and qs2 will be equal in the following sense:

  A1[i,j] = A2[i,j]       for i <  'row_pos', j < 'nqb' ,   

  A1[i,j] = A2[i,j] = 0   for i >= 'row_pos', j < 'nqb' .

Also, matrices A1 and A2 will both have rank row_pos - 1, when excluding row 0 of the two matrices. Some rows of A1 or A2 may be deleted to achieve this situation. The result of the summation of the matrix products of qs1 and qs2 over the first nqb columns (which is used by the matrix multiplication procedure) is not changed by this operation. Apart from this assertion, both states are changed, and they may have less rows than before. They may even be changed to zero, if the result of the multiplication is zero.

The algorithm used here is explained in the API reference in section Multiplication of quadratic mappings. In the notation in that section the algorithm computes states \(\mbox{qs1}', \mbox{qs2}'\) with \( (\mbox{qs1}' \odot \mbox{qs2}')_n = (\mbox{qs1} \odot \mbox{qs2})_n, \), where \( n\) = nqb.

pqs1->shape1 and pqs2->shape1 are ignored.

int32_t qstate12_product(qstate12_type *pqs1, qstate12_type *pqs2, uint32_t nqb, uint32_t nc)

Compute a certain product of two states.

Compute a certain product qs3 of the states qs1 and qs2 referred by pqs1 and pqs2 and store the (reduced) result in *pqs1. Overlap between pqs1 and pqs2 is possible. qs2 is not changed.

Let n1 = pqs1->ncols, n2 = pqs2->ncols. Put qs1a = qstate12_extend(qs, n1, n2-nqb), qs2a = qstate12_extend(qs, nqb, n1-nqb). Then qs1a and qs2a are complex functions on (nn1 + nn2 - nqb) bits. Let qs3a be the complex function which is the product of the functions qs1a and qs2a. Then we have qs3 = qstate12_sum_cols(qs3a, 0, nc).

E.g. qstate12_product(pqs1, pqs2, nc, nc) is the tensor contraction over the first nc qubits of qs1 and qs2. In case pqs1->ncols = pqs2->ncols = n the function qstate12_product(pqs1, pqs2, n, 0) returns the product of qs1 and qs2 (considered as functions). Furthermore, and qstate12_product(pqs1, pqs2, n, n) returns the scalar product of qs1 and qs2 (considered as vectors).

In general, qstate12_product(pqs1, pqs2, n, 0) corresponds to the function \( (\mbox{qs1}' \odot \mbox{qs2}')_n \) defined in section Products and tensor products of quadratic mappings of the API reference.

pqs1->shape1 is set to 0. The user should set pqs1->shape1 to a reasonable value.

int32_t qstate12_matmul(qstate12_type *pqs1, qstate12_type *pqs2, qstate12_type *pqs3)

Compute the matrix product of two matrices.

Compute the matrix product qs3 of the matices qs1 and qs2 referred by pqs1 and pqs2 and store the (reduced) result in *pqs3. Overlap between pqs1, pqs2, pqs3 is possible.

If qs1 has shape (r1,c1) and qs2 has shape (r2,c2) then c1 == r2 must hold, and qs3 has shape (r1,c2)

Note that a shape (r,c) of a matrix qs means a 2**r times 2**c matrix, where r+c == qs.ncols, c = qs.shape1.

int32_t qstate12_pauli_vector(qstate12_type *pqs, uint64_t *pv)

Check if a matrix is in the Pauli group and convert it to a vector.

The Pauli group of \(n\) qubits is the normal subgroup of the Clifford group of \(n\) qubits generated by the not gates, the phase \(\pi\) gates, and by the scalar multiples of the unit matrix by a fourth root of unity.

We represent an element of the Pauli group as a product of \(2n+2\) generators of order 2 or 4. The sequence of these exponents is stored in a bit vector (coded as an integer) as follows:

 Bit 2n+1:  a scalar factor sqrt(-1)

 Bit 2n:    a scalar factor -1

 Bit n+i:   a not gate applied to qubit i, 0 <= i < n

 Bit i:     a phase pi gate applied to qubit i, 0 <= i < n

See section The Pauli group in the API reference for details.

If the matrix qs referred by pqs is in the Pauli group of \(n\) qubits then the function returns \(n\) and stores qs as an element of the Pauli group in the vector v referred by pv. Otherwise the function returns a negative error code.

int32_t qstate12_pauli_matrix(qstate12_type *pqs, uint32_t nqb, uint64_t v)

Convert element of the Pauli group to a matrix.

Here parameter v encodes an element of the Pauli group of nqb qubits as described in function qstate12_pauli_vector(). The function converts v to a matrix of shape (nqb, nqb) in the Clifford group and stores the result in the state referred by pqs.

uint64_t qstate12_pauli_vector_mul(uint32_t nqb, uint64_t v1, uint64_t v2)

Multiplication of two elements of the Pauli group.

Here parameters v1 and v2encode two elements of the Pauli group of nqb qubits as described in function qstate12_pauli_vector().

The function returns the product v1 * v2 encoded in the same way.

uint64_t qstate12_pauli_vector_exp(uint32_t nqb, uint64_t v, uint32_t e)

Exponentiation of an element of the Pauli group.

Here parameter v1 encodes an element of the Pauli group of nqb qubits as described in function qstate12_pauli_vector().

The function returns the power v1 ** e encoded in the same way.

The Pauli group has exponent 4, so qstate12_pauli_vector_exp(nqb, v1, 3) returns the inverse of v1.

int32_t qstate12_reduce_matrix(qstate12_type *pqs, uint8_t *row_table)

Perform a special reduction on a quadratic state matrix.

This function performs a special reduction on the quadratic state matrix qs referred by pqs, as described in the API reference**, section Reducing a quadratic state matrix.

This kind of reduction is differnt from the reduction in function qstate12_reduce(). It is used internally for computing traces and norms of matrices. It is also used in function qstate12_pauli_conjugate().

The function also computes a table with pqs->nrows + pqs->ncols entries in the array referred by parameter row_table which is used internally for the operations mentioned above.

int32_t qstate12_mat_lb_rank(qstate12_type *pqs)

compute the rank of a quadratic state matrix

Let qs be the quadratic state matrix referred by pqs. The function returns the binary logarithm of the rank of matrix qs, which is an integer in case of the nonzero matrix. It returns -1 if qs is the zero matrix. A return value less than -1 is an error code. Matrix qs is reduced.

int32_t qstate12_mat_inv(qstate12_type *pqs)

Compute the inverse of a quadratic state matrix.

Let qs be the quadratic state matrix referred by pqs. The function computes the (reduced) inverse of matrix qs in place. It returns ERR_QSTATE12_MATRIX_INV if the matrix is not invertible.

int32_t qstate12_to_symplectic(qstate12_type *pqs, uint64_t *pA)

Convert quadratic state matrix to a symplectic bit matrix.

Here the quadratic state matrix qs referred by pqs must be of shape (k, k) and invertible. The function computes a 2k times 2k bit matrix A.

If vis a bit vector representing an element of the Pauli group then v * A represents the element qs * v * qs^{-1} of the Pauli group, up to a scalar factor. Elements of the Pauli group of k qubits are encoded as in function qstate12_pauli_vector(), ignoring the bits at positions 2k and 2k + 1. So pA[i] will contain the image of the i-th basis vector of the Pauli group, with bits 2k and 2k + 1 set to zero.

The function returns 2*k in case of success and a negative value in case of an error. In any case it suffices if the array referred by pA has 32 entries.

This function can be considered as a simplified version of function qstate12_pauli_conjugate. It computes the natural homomorphism from the group of invertible quadratic state matrices shape (k, k) to the symplectic group \(\mbox{S}_{2k}(2)\).

int32_t qstate12_to_symplectic_row(qstate12_type *pqs, uint32_t n)

Conjugate unit vector in Pauli group by quadratic state matrix.

Here the quadratic state matrix qs referred by pqs must be of shape (k, k) and invertible.

Let v be the bit vector with bit n set and the other bits cleared. Here elements of the Pauli group of k qubits are encoded as in function qstate12_pauli_vector(), ignoring the bits at positions 2k and 2k + 1.

The function returns the element qs * v * qs^{-1} of the Pauli group, up to a scalar factor as an integer, encoded in the same way as input vector v, ignoring the scalar factor.

Thus in case of success the return value is the same as row n of the matrix computes by function qstate12_to_symplectic_row. The function returns a negative value in case of an error.

int32_t qstate12_pauli_conjugate(qstate12_type *pqs, uint32_t n, uint64_t *pv, uint32_t arg)

Conjugate Pauli group elements with a Clifford group element.

Here the quadratic state matrix qs referred by pqs must be of shape (k,k) and invertible. The array v referred by pv has n entries v[0],...,v[n-1]. Each of these entries is interpreted as an element of the Pauli group of k qubits, encoded as in function qstate12_pauli_vector().

The function replaces v[i] by the Pauli group element w[i] = qs * v[i] * qs**(-1) for all i < n. w[i] is encoded in the same way as v[i].

Parameter arg should usually be a nonzero value. In case arg == 0 the (complex) argument of the outputs v[i] is not computed.

This function uses function qstate12_reduce_matrix(). The operation of this function is explained in the API reference, section Reducing a quadratic state matrix .

Computing in the subgroup \(G_{x0}\) of the Monster

The functions in file xsp2co1.c implement the group operation of the subgroup \(G_{x0}\) (of structure \(2^{1+24}.\mbox{Co}_1\)) of the monster.

Represenation of \(G_{x0}\) on the tensor product \(4096_x \otimes \Lambda\)

In [Sey20], section 7.4 and 9, the generators \(x_d, x_\delta, y_\delta, x_\pi, \xi\) of \(G_{x0}\) are also defined as generators of a group \(G_{x1} \subset G(4096_x) \times G(24_x)\). Here \(G(4096_x)\) is a subgroup of the real Clifford group \(\mathcal{C}_{12}\) operating on the 4096-dimensional real vector space \(4096_x\), and \(G(24_x)\) is the automorphism group \(\mbox{Co}_0\) of the Leech lattice \(\Lambda\). \(G_{x1}\) is a preimage of \(G_{x0}\) with \(|G_{x1}:G_{x0}| = 2\), and \(G_{x0}\) operates faithfully on the tensor product \(4096_x \otimes_\mathbb{Z} \Lambda\). Let \((x_g, L_g) \in G_{x1} \subset G(4096_x) \times G(24x)\). Component \(x_g\) determines component \(L_g\) of the pair up to sign. So it suffices to store the image \(v_g = v_0 \cdot L_g\) of a fixed shortest vector \(v_0 \in \Lambda / 3 \Lambda\) instead of the whole automorphism \(L_g\) of \(\Lambda\). We put \(v_0 = (0,0,4,-4,0,...,0)\) in the standard basis of the Leech lattice.

We can reconstruct \(L_g\) from \(x_g\) and \(v_g\) as follows. \(G(4096_x)\) has an extraspecial subgroup \(Q_{x1}\) of structure \(2^{1+24}\). The quotient of \(Q_{x1}\) by its center \(Z(Q_{x1})\) is isomorphic to \(\Lambda / 2 \Lambda\). By definition of \(G_{x1}\), the element \(L_g\) operates on \(\Lambda / 2 \Lambda\) in the same way as \(x_g\) operates on \(Q_{x1}/Z(Q_{x1})\) by conjugation. So that operation of \(L_g\) can be reconstructed from \(x_g\). A short vector in \(\Lambda / 2 \Lambda\) has precisely two short preimages in \(\Lambda\) which are opposite. Thus the image of one short vector is known exactly, and the images of all short vectors are known up to sign. Since \(L_g\) preserves the scalar product, the images of all short vectors in \(\Lambda\) can be computed. Function xsp2co1_elem_to_leech_op in file xsp2co1.c constructs \(L_g\) from \(x_g\) and \(v_g\). The functions in file gen_leech.c support operations on \(\Lambda / 2 \Lambda\) and \(\Lambda / 3 \Lambda\).

The \(G_{x0}\) representation

We actually represent an element of \(G_{x0}\) in G_x0 representation. This is as an array elem of 26 integers of type uint64_t. That array contains a pair \((x_g,v_g)\) as described above with \(v_g\) stored in the first entry elem[0], and \(x_g\) stored in the remaining 25 entries of array elem.

Vector \(v_g \in \Lambda / 3 \Lambda\) is stored in elem[0] as a 48-bit integer in Leech lattice mod 3 encoding as described in the documentation of module gen_leech.c.

We store the inverse \(x_g^{-1}\) of \(x_g\) in the upper 25 entries of the array elem. The rationale for storing the inverse is that this simplifies the operation of \(G(4096)_x\) on \(Q_{x1}\) by conjugation, using function qstate12_pauli_conjugate in file qmatrix12.c. Note that \(x_g^{-1} = x_g^\top\), since \(x_g\) is orthogonal.

In section Long-term stable storage of vectors of the representation an element \(c\) of \(\mathcal{C}_{12}\) is given as a real \(2^{12} \times 2^{12}\) matrix stored in a structure of type qstate12_type. In case \(c \in G(4096_x)\) that matrix has rational entries, where the denominators are powers of two. A structure of type qstate12_type representing a \(c \in G(4096_x)\) contains a triple \((e,A,Q)\). There \(e\) is a signed integral power of two, and \((A,Q)\) is a pair of bit matrices with up to 25 rows and up to 49 colums, where the first 24 columns belong to matrix \(A\), and the remaining columns belong to matrix \(Q\).

Changing the sign of \(e\) corresponds to negation of a matrix \(x_g^{-1} \in G(4096_x)\) given by \((e,A,Q)\) or to multiplication by \(x_{-1}\). In the group \(G_{x0}\), changing the sign of \(v_g\) has the same effect as changing the sign of \(x_g\); so \(e\) can always be made positive. The absolute value of \(e\) is just there for scaling the operator norm of a matrix \(c\) to 1, and can be omitted.

So we can represent one of the values \(\pm x_g^{-1}\) as a pair \((A,Q)\) with an implied positive scalar factor \(e\) as above, and negate component \(v_g\) of the pair \((x_g,v_g)\), if necessary.

Assuming that \(x_g^{-1}\) is stored in a structure qs of type qstate_type, we copy all valid entries qs.data[i] to elem[i+1]. This amounts to copying the components \((A,Q)\) of the triple \((e,A,Q)\) from qs to elem. We always reduce the quadratic state matrix in qs before copying it, as indicated in section Long-term stable storage of vectors of the representation. We fill unused entries and bits in the array elem with zeros. Thus the representation of a \(g \in G_{x0}\) in memory is unique. qs can easily be reconstructed from elem, assuming that the scalar factor \(e\) in the triple \((e,A,Q)\) is positive, and that trailing zero entries in the array elem are unused.

A word \(w\) in the generators of the subgroup \(G_{x0}\) of the monster group can be converted to G_x0 representation by calling function xsp2co1_set_elem_word in file xsp2co1.c. Here the word \(w\) must be given as an array ` of unsigned 32-bit integers as described in section Header file mmgroup_generators.h.

The normal subgroup \(Q_{x0}\) of \(G_{x0}\)

The group \(G_{x0}\) also has an extraspecial normal subgroup \(Q_{x0}\) of structure \(2^{1+24}\). There is a natural isomorphism between the normal subgroup \(Q_{x1}\) of \(G(4096_x)\) and \(Q_{x0}\). Considering \(G(4096_x)\) and \(G(24_x)\) as matrix groups, this isomorphism is given by:

\[x \in Q_{x1} \, \longmapsto \, x \otimes 1 \in G(4096_x) \otimes G(24_x) \cong G_{x0} \, .\]

We identify \(Q_{x0}\) with \(Q_{x1}\). Both groups are generated by elements \(x_d, x_\delta\), as described above.

We also represent an element of \(Q_{x0}\) as a 25-bit integer in Leech lattice encoding as described in section Description of the mmgroup.generators extension.

Changing the basis of the space \(4096_x\)

We also use the vectors \((d'), d \in \mathcal{P}\) as basis vectors of \(4096_x\) , where:

\[(d)' = \frac{1}{\sqrt2} \left( d_1^+ + d_1^- \right) \, , \quad d_1^\pm = \frac{1}{\sqrt2} \left( (d)' \pm (\Omega d)' \right) \, . \]

The reason for introducing this basis is that the operation of \(G_{x0}\) on \(Q_{x0}\) (by conjugation) is easy when we use this basis. Then we obviously have \((-d)' = -(d)'\). We can transform the coordinates of a vector \(v\) from the basis given by \(d^\pm\) to the basis given by \((d)'\) by multiplying the coordinate vector with a matrix \(H\). Multiplication with matrix \(H\) is equivalent to applying the Hadamard gate to Qubit 11 (i.e. to the bit with valence \(2^{11}\)) of the coordinates of \(v\) . Thus \(H\) is an involution.

The numbering of the 4096 basis vectors \((d)'\) corresponds to the numbering of the positive elemments of \(\mathcal{P}\). We may put \((d \oplus 2^{12})' = -(d)'\), where \(\oplus\) means bitwise addition modulo 2; then that correspondence holds for all values \(0 \leq d < 2^{13}\). The exact defnition of the operator \(\oplus\) on the Parker loop \(\mathcal{P}\) is given in section Implementing generators of the Monster group.

In this basis the operation of the extraspecial group \(Q_{x0}\) is very simple:

  • \(x_e x_{\theta(e)}\) maps \((d)'\) to \((d \oplus e)'\) for \(e \in \mathcal{P}\). Here \(\theta\) is the cocycle, see section The Parker loop. In the language of quantum computing this corresponds to a sequence of commuting not gates.

  • \(x_\epsilon\) maps \((d)'\) to \((-1)^{\langle d, \epsilon\rangle}(d)'\) for \(\epsilon \in \mathcal{C}^*\). Here \(\mathcal{C}^*\) is the Golay cocode. In the language of quantum computing this corresponds to a sequence of commuting phase-\(\pi\) gates.

Since \(H\) is in \(\mathcal{C}_{12}\), it operates on \(Q_{x0}\) by conjugation. The following subtlety has to be considered in function conv_pauli_vector_xspecial (in file xsp2cco1.c) implementing that operation. \(H\) exchanges the anticcommuting elements of \(Q_{x0}\) of with numbers 0x800 and 0x800000 in Leech lattice encoding. Thus it negates the element with number 0x800800.

Converting an element in G_x0 representation to a word of generators

Perhaps the most inportant function in this module is function xsp2co1_elem_to_word. This function converts an element of the subgroup \(G_{x0}\) from G_x0 representation to a word \(w\) of generators of the monster group. That word \(w\) is unique and reduced in the sense explained in the description of that function.

There is also a companion function xsp2co1_reduce_word that first converts an arbitrary word in the generators of the subgroup \(G_{x0}\) of the monster to G_x0 representation. Then it calls function xsp2co1_elem_to_word to compute the reduced word equal to the input word.

We briefly explain the implementation of function xsp2co1_elem_to_word. Let \(g \in G_{x0}\) be given in G_x0 representation. The function first computes the image \(g^{-1} x_\Omega g\) of \(x_\Omega\) using function xsp2co1_xspecial_conjugate, with \(\Omega\) as in [Sey20]. Using that image and function gen_leech2_reduce_type4 in file gen_leech.c we can compute a word \(w_1\) in the generators of \(G_{x0}\) such that \(g_1 = g \cdot w_1\) stabilizes \(x_\Omega\) up to sign.

Then \(g_1\) is in the monomial subgroup \(N_{x0}\) of \(G_{x0}\) of structure \(2^{1+24}.2^{11}.M_{24}\). For any \(g_1 \in N_{x0}\) function xsp2co1_elem_monomial_to_xsp computes a word in the generators of the monster that is equal to \(g_1\).

Given a monomial element \(g_1\) in G_x0 representation, function xsp2co1_elem_monomial_to_xsp computes a word of generators equal to \(g_1\) as follows. Ignoring signs and identifying the basis vector \(d_1^+\) of \(4096_x\) with the basis vector \(d_1^-\), Table 3 in [Sey20] states that \(x_\pi y_e x_f\) maps \(d_1^\pm\) to \((d^\pi e f)_1^\pm\). This corresponds to an affine mapping \(d \mapsto d^\pi e f\) from \(\mathcal{C} / \langle \Omega \rangle\) to itself, from which we can easily compute \(e, f\) (modulo sign and \(\Omega\)), and also \(\pi\). Thus we can find a word \(w_2\) in the generators \(x_\pi y_e x_f\), such that \(g_2 = g_1 w_2\) stabilizes all basis vectors of \(4096_x\) up to sign, and possibly exchanging \(d_1^+\) with \(d_1^-\). Then \(g_2\) can easily be converted into a word in the generators \(x_{-1}, x_\Omega, x_\delta, \delta \in \mathcal{C}^*\).

We use function qstate12_monomial_matrix_row_op in file qmatrix12.c for obtaining the affine mapping \(d \mapsto d^\pi e f\) from the G_x0 representation of \(g_1\). We use the functions in file mat24_functions.c for obtaining \(\pi\) from that affine mapping.

C functions in xsp2co1.c

File xsp2co1.c contains functions for computing in the subgroup \(G_{x0}\) (of structure \(2^{1+24}.\mbox{Co}_1\)) of the monster.

Functions

uint64_t xsp2co1_find_chain_short_3(uint64_t v3_1, uint64_t v3_2)

Find vector not orthogonal to vectors in Leech lattice mod 3.

Given two vectors \(v_{3,1}\) and \(v_{3,2}\) in the Leech lattice mod 3, the function returns a vector \(v_{3,3}\) such that the scalar product \(\langle v_{3,3}, v_{3,i} \rangle\) is not zero for both, \(i = 1, 2\). \(v_{3,3}\) has precisely two nonzero coordinates and is hence short. Such a vector \(v_{3,3}\) exists if none of the vectors \(v_{3,1}, v_{3,2}\) is zero. If no such vector \(v_{3,3}\) exists then the function returns 0.

All vectors \(v_{3,i}\) are in given in Leech lattice mod 3 encoding.

int32_t xsp2co1_chain_short_3(qstate12_type *pqs, uint32_t n, uint64_t *psrc, uint64_t *pdest)

Apply transformation in \(G_{x0}\) to vectors in Leech lattice mod 3.

Let \(x_g \in G(4096_x)\) be given by the quadratic state matrix referred by pqs. Let psrc be an array \((v_0,\ldots v_{n-1})\) of \(n\) short vectors in the Leech lattice mod 3, given in Leech lattice mod 3 encoding. We try to compute \(w_i = v_i x_g\) for \(0 \leq i < n\) and to store \(w_i\) in pdest[i], also in Leech lattice mod 3 encoding. Unfortunately, the short vector \(w_i\) is defined to sign only. In other words, \(x_g\) may correspond to two different elements \(\pm g\) in the automorphisem group \(\mbox{Co}_0\) of the Leech lattice.

To specify the correct \(g \in \mbox{Co}_0\), we must store the correct value \(w_0 = v_0 x_0\) in pdest[0] and provide short vectors \(v_i\) such that the scalar product \(\langle v_{i-1}, v_i\rangle \) of adjacent vectors is not zero for \(i>0\). The the correct values \(v_i x_i\) are determined by ortohonality of \(g\).

The function returns garbage if the input conditions for the \(x_g, v_0, w_0\) are not satisfied. It returns a negative value if two adjacent vectors \(v_0\) are orthognal or not short.

int32_t xsp2co1_elem_to_qs_i(uint64_t *elem, qstate12_type *pqs)

Get component \(x_g^{-1} \in G(4096_x)\) from \(g \in G_{x0}\).

Let \(g \in G_{x0}\) be stored in the array elem, in G_x0 representation. This means that \(g\) is given as a pair \((x_g, v_g) \in G(4096_x) \times \Lambda / 3 \Lambda\) . The function stores \(x_g^{-1}\) in the structure qs of type qstate12_type referred by pqs.

Component \(v_g\) is equal to elem[0] (in Leech lattice mod 3 encoding).

Caution:

This is a low-level function. After returning, the structure qs and the array elem share the same data block.

Caution:

Internally, we store the inverse of component \(x_g\) in the element \(g\), and this function also stores that inverse in the structure qs.

int32_t xsp2co1_elem_to_qs(uint64_t *elem, qstate12_type *pqs)

Get component \(x_g \in G(4096_x)\) from \(g \in G_{x0}\).

Let \(g \in G_{x0}\) be stored in the array elem, in G_x0 representation. This means that \(g\) is given as a pair \((x_g, v_g) \in G(4096_x) \times \Lambda / 3 \Lambda\) . The function stores \(x_g\) in the structure qs of type qstate12_type referred by pqs. \(x_g\) represents a \(4096 \times 4096\) matrix.

Caution:

The structure referred by pqs must provide sufficient memory for data, see function qstate12_set_mem in file qstate12.c; here pqs->data should be at least 25.

int32_t xsp2co1_qs_to_elem_i(qstate12_type *pqs, uint64_t v_g, uint64_t *elem)

Construct \(g \in G_{x0}\) from pair \((x_g, v_g)\).

The function constructs a \(g \in G_{x0}\) as a pair \((x_g, v_g) \in G(4096_x) \times \Lambda / 3 \Lambda\) and stores the result in the array elem in G_x0 representation. The value \(x_g^{-1} \in G(4096_x)\) must be given as a structure of type qstate12_type referred by pqs. The value \(v_g \in \Lambda / 3 \Lambda\) must be given by parameter v3 in Leech lattice mod 3 encoding.

Caution:

As a low-level function, this function may construct a value \(g\) which is not in \(G_{x0}\). Function xsp2co1_set_elem_word should be used for constructing an element of \(G_{x0}\) instead.

Caution:

Internally, we store the inverse of component \(x_g\) in the element \(g\), and this function also requires that inverse in the structure referred by pqs.

int32_t xsp2co1_reduce_elem(uint64_t *elem)

Reduce an \(g \in G_{x0}\) to a standard form.

Let \(g \in G_{x0}\) be stored in the array elem, in G_x0 representation. This means that \(g\) is stored as a pair \((x_g, v_g) \in G(4096_x) \times \Lambda / 3 \Lambda\) . The function reduces the components \(x_g\), \(v_g\) to their standard form in place.

In functions that construct elements of \(G_{x0}\) these components are reduced automatically.

void xsp2co1_neg_elem(uint64_t *elem)

Negate an \(g \in G_{x0}\).

Let \(g \in G_{x0}\) be stored in the array elem, in G_x0 representation. The function negates \(g\) in place.

Negation is equivalent to multiplication with the generator \(x_{-1}\), and also to multiplication with the generator \(y_{\Omega}\).

void xsp2co1_copy_elem(uint64_t *elem1, uint64_t *elem2)

Copy a \(g \in G_{x0}\).

The function copies the element of \(G_{x0}\) stored in the array elem1 (in G_x0 representation) to the array elem2.

int32_t xsp2co1_elem_to_bitmatrix(uint64_t *elem, uint64_t *pA)

Map element of \(G_x0\) to a bit matrix.

The function maps the element of \(G_{x0}\)

stored in

the array

elem (in G_x0 representation) to a \(24 \times 24\) bit matrix \(A\), which will be stored in the array referred by pA. Bit matrix \(A\) operates on a vector on the Leech lattice modulo 2 (in Leech lattice encoding) by right multiplication.

So this function computes the homomorphism from the group \(G_{x0}\) onto the group \({Co}_1\).

int32_t xsp2co1_mul_elem(uint64_t *elem1, uint64_t *elem2, uint64_t *elem3)

Multiply two elements of the group \(G_{x0}\).

Let \(g_1, g_2 \in G_{x0}\) be stored in the arrays elem1, elem2 in G_x0 representation. The function computes \(g_1 \cdot g_2 \) and stores the result in the array elem3 in G_x0 representation.

Any kind of overlapping beween the arrays elem1, elem2, and elem3 is allowed.

int32_t xsp2co1_inv_elem(uint64_t *elem1, uint64_t *elem2)

Invert an element of the group \(G_{x0}\).

Let \(g_1 \in G_{x0}\) be stored in the array elem1, in G_x0 representation. The function computes \(g_1^{-1}\) and stores the result in the array elem2 in G_x0 representation.

Any kind of overlapping beween the arrays elem1 and elem2 is allowed.

int32_t xsp2co1_conj_elem(uint64_t *elem1, uint64_t *elem2, uint64_t *elem3)

Conjugate elements of the group \(G_{x0}\).

Let \(g_1, g_2 \in G_{x0}\) be stored in the arrays elem1, elem2 in G_x0 representation. The function computes \(g_2^{-1} \cdot g_1 \cdot g_2 \) and stores the result in the array elem3 in G_x0 representation.

Any kind of overlapping beween the arrays elem1, elem2, and elem3 is allowed.

int32_t xsp2co1_xspecial_conjugate(uint64_t *elem, uint32_t n, uint64_t *ax, uint32_t sign)

Conjugation of elements of \(Q_{x0}\) with an element of \(G_{x0}\).

Let \(x_0,\ldots,x_{n-1}\) a list of \(n\) elements of \(Q_{x0}\) stored in the the array ax in Leech lattice encoding. Let \(g \in G_{x0}\) be stored in the array elem in G_x0 representation.

Then the function replaces the element \(x_i\) by \(g^{-1} x_i g\) for \(0 \leq i < n\).

Parameter sign should usually be a nonzero value. In case sign = 0 the signs of the returned vectors are not computed.

int32_t xsp2co1_xspecial_img_omega(uint64_t *elem)

Conjugation of \(\Omega\) with an element of \(G_{x0}\).

Let \(g \in G_{x0}\) be stored in the array elem in G_x0 representation, and let \(\Omega\) be the standard frame in the Leech latice. The function returns \(g^{-1} \Omega g\) in Leech lattice encoding, ignoring the sign of the result.

int32_t xsp2co1_elem_check_fix_short(uint64_t *elem1, uint32_t v)

Check if an element of \(G_{x0}\) fixes a short vector.

This function is deprecated!

Let \(v\) be the short vector in the Leech lattice modulo 2 given by parameter v in Leech lattice encoding. Let \(g \in G_{x0}\) be stored in the array elem in G_x0 representation.

If \(v\) is short then it corresponds to a vector \(v'\) in the 24-dimensional Leech lattice \(\Lambda\) up to sign. Note that the operation of \(G_{x0}\) on \(\Lambda\) is also defined up to sign only. So in the general case we cannot distinguish whether \(g\) fixes or negates \(v'\).

However, \(G_{x0}\) has a (faithful) representation on the tensor product \(\Lambda \otimes 4096_x\) for a certain representation \(4096_x\), see [Con85] for details. Here the operation of \(G_{x0}\) on \(4096_x\) is also defined up to sign only.

If the character of \(g\) on \(4096_x\) is nonzero then we can flip the signs in both representations, \(\Lambda\) and \(4096_x\), without changing the tensor product. Thus requiring the character of \(g\) on \(4096_x\) to be positive determines the sign of the operation of \(g\) on \(\Lambda\) uniquely. In this case we return 0 if \(g\) fixes the vector \(v'\), and 1 if \(g\) negates \(v'\). Otherwise we return 6.

If the character of \(g\) on \(4096_x\) is zero then there is no way to determine the sign of the operation of \(g\) on \(\Lambda\). In this case we return 2 if \(g\) fixes the vector \(v'\) (up to sign) and 6 otherwise.

In cases where \(g\) and \(-g\) are in different classes in \(G_{x0}\) we may sometimes obtain the class of \(g\) by using this function.

We return a negative value in case of any error. It is an error if \(v'\) is not short, i.e. \(v\) is not of type 2.

int32_t xsp2co1_xspecial_vector(uint64_t *elem)

Convert \(x \in Q_{x0}\) from \(G_{x0}\) rep to Leech.

Let \(x \in Q_{x0} \subset G_{x0}\) be stored in the array elem in G_x0 representation. The function returns \(x\) as an integer in Leech lattice encoding.

The function returns a negative number in case of error. E.g. in case \(x \notin Q_{x0}\) it returns ERR_QSTATE12_NOTIN_XSP.

void xsp2co1_unit_elem(uint64_t *elem)

Store neutral element of \(G_{x0}\).

The function stores the neutral element of \(G_{x0}\) in the array elem in G_x0 representation.

uint32_t xsp2co1_is_unit_elem(uint64_t *elem)

Check if elem is neutral element of \(G_{x0}\).

The function returns 1 if elem is the neutral element of \(G_{x0}\) and 0 otherwise.

int32_t xsp2co1_elem_xspecial(uint64_t *elem, uint32_t x)

Convert \(x \in Q_{x0}\) from Leech to \(G_{x0}\) rep

Let \(x \in Q_{x0} \subset G_{x0}\) be stored in parameter x in Leech lattice encoding. The function converts \(x\) to G_x0 representation and stores the result in the array elem .

int32_t xsp2co1_mul_elem_word(uint64_t *elem, uint32_t *a, uint32_t n)

Right multiply an element of \(G_{x0}\) with a word of generators.

Let \(g \in G_{x0}\) be stored in the array elem in G_x0 representation. We replace \(g\) by \(g \cdot w\), where \(w\) is a word in the generators of \(G_{x0}\) of length \(n\). \(w\) is stored in the array a, and each entry of a encodes a generator of \(G_{x0}\) as described in file mmgroup_generators.h.

The function fails and returns ERR_QSTATE12_GX0_TAG if not all atoms of the word \(w\) are in \(G_{x0}\).

int32_t xsp2co1_set_elem_word(uint64_t *elem, uint32_t *a, uint32_t n)

Convert word of generators of \(G_{x0}\) to G_x0 representation.

Let \(w\) be a word in the generators of \(G_{x0}\) of length \(n\). \(w\) is stored in the array a, and each entry of a encodes a generator of \(G_{x0}\) as described in file mmgroup_generators.h. We convert the word \(w\) to an element of \(G_{x0}\) in G_x0 representation and store the result in the array elem.

The function fails and returns ERR_QSTATE12_GX0_TAG if not all atoms of the word \(w\) are in \(G_{x0}\).

int32_t xsp2co1_mul_elem_atom(uint64_t *elem, uint32_t v)

Right multiply an element of \(G_{x0}\) with a generator.

Equivalent to xsp2co1_mul_elem_word(elem, &v, 1)

int32_t xsp2co1_set_elem_atom(uint64_t *elem, uint32_t v)

Convert a generator of \(G_{x0}\) to G_x0 representation.

Equivalent to xsp2co1_set_elem_word(elem, &v, 1)

int32_t xsp2co1_set_elem_word_scan(uint64_t *elem, uint32_t *a, uint32_t n, uint32_t mul)

Convert word of generators of \(G_{x0}\) to G_x0 representation.

Parameters and operation are as in function xsp2co1_set_elem_word. But in contrast to function xsp2co1_set_elem_word, this function succeeds also if just a prefix of the word a is in the subgroup \(G_{x0}\).

Let k be the greatest number such that all prefixes of a of length at most k are in the group \(G_{x0}\). Let \(a_k\) be the element of \(G_{x0}\) corresponding to the prefix of a of length k.

If parameter mul is zero then we convert the word \(a_k\) to an element of \(G_{x0}\) in G_x0 representation and store the result in the array elem. Otherwise we multiply the element elem with the word \(a_k\) and store the array in elem.

C functions in xsp2co1_word.c

File xsp2co1_word.c contains additional functions for computing in the subgroup \(G_{x0}\) (of structure \(2^{1+24}.\mbox{Co}_1\)) of the monster. This file can be considered as a supplement to file xsp2co1.c.

Functions

uint64_t xsp2co1_to_vect_mod3(uint64_t x)

Auxiliary function for function xsp2co1_add_short_3_leech

The function converts a vector in \(\Lambda / 3\Lambda\) from Leech lattice mod 3 encoding to the encoding to the encoding of a vector in \((\mathbb{Z} / 3\mathbb{Z})^{24}\) used in the mmgroup.mm_op extension.

uint64_t xsp2co1_from_vect_mod3(uint64_t x)

Inverse of function xsp2co1_to_vect_mod3

The function converts a vector in \(\Lambda / 3\Lambda\) from the encoding of a vector in \((\mathbb{Z} / 3\mathbb{Z})^{24}\) used in the mmgroup.mm_op extension to the Leech lattice mod 3 encoding.

int32_t xsp2co1_elem_to_leech_op(uint64_t *elem, int8_t *pdest)

Get Leech lattice matrix from \(g \in G_{x0}\).

Let \(g \in G_{x0}\) be stored in the array elem, in G_x0 representation. \(G_{x0}\) operates faithfully on the space \(4096_x \otimes_\mathbb{Z} \Lambda\). This function constructs a \(24 \times 24\) integer matrix \(L_g\) such that \(\frac{1}{8} L_g\) corresponds to the operation of \(g\) on \(\Lambda\). It stores entry \(L_g[i,j]\) in dest[24*i+j]. Matrix \(L_g\) is unique up to sign.

Function xsp2co1_elem_to_qs(elem,...) computes a (representation of) an orthogonal \(4096 \times 4096\) matrix \(x_g\) such that right multiplication with the Kronecker product \(\frac{1}{8} x_g \otimes L_g\) is equal to the action of \(g\) on \(4096_x \otimes \Lambda\).

int32_t xsp2co1_short_3_to_leech(uint64_t x, int8_t *pdest)

Compute integral short Leech lattice vector from vector mod 3.

Given a short Leech lattice vector x (modulo 3) in Leech lattice mod 3 encoding, the function computes the real coordinates of vector x in the array referred by pdest. pdest must have length 24. As usual, the norm (i.e. the squared sum of the coordinates) of the computed short vector is normalized to 32.

The function returns 0 if x encodes a short Leech lattice vector mod 3 and a negative value otherwise.

int32_t xsp2co1_short_2_to_leech(uint64_t x, int8_t *pdest)

Compute integral short Leech lattice vector from vector mod 2.

Given a short Leech lattice vector x (modulo 2) in Leech lattice encoding, the function computes the real coordinates of vector x in the array referred by pdest. pdest must have length 24. As usual, the norm (i.e. the squared sum of the coordinates) of the computed short vector is normalized to 32. Note that the result is defined up to sign only. Here the function chooses an arbitrary sign.

The function returns 0 if x encodes a short Leech lattice vector mod 2 and a negative value otherwise.

int32_t xsp2co1_elem_monomial_to_xsp(uint64_t *elem, uint32_t *a)

Map monomial element of \(G_{x0}\) to element of \(Q_{x0}\).

Let \(g \in G_{x0}\) stored in the array elem. The matrix corresponding to \(g\) in the representation \(4096_x\) must be monomial. The function computes a word \(w\) of in the generators of \(G_{x0}\) such that \(g w \in Q_{x0}\). The word \(w\) has length at most 2 and is stored in the array a. Each entry of a encodes a generator of \(G_{x0}\) as described in file mmgroup_generators.h. The function returns the length of that word.

The atoms in the word have tags p, y in that order. Each word is stored as the inverse of a generator.

int32_t xsp2co1_elem_to_word(uint64_t *elem, uint32_t *a)

Convert element of \(G_{x0}\) to a word in its generators.

Let \(g \in G_{x0}\) be stored in the array elem. The function converts \(g\) to a reduced word in the generators of \(g\) and stores that word in the array a. Then each entry of a encodes a generator of \(G_{x0}\) as described in file mmgroup_generators.h. The function returns the length of that word.

The reduced word stored in the array a may have up to 10 entries. The tags of the entries in that word are xdyplplplp in that order. See documentation of class mmgroup.MMGroup for the meaning of these tags. Each entry of a word may encode the neutral element as a generator; then that entry is dropped. We assert that the number of entries with tag l is minimal.

int32_t xsp2co1_reduce_word(uint32_t *a, uint32_t n, uint32_t *a1)

Reduce a word of generators of \(G_{x0}\).

Let \(g \in G_{x0}\) be stored in the array a as a word \(w\) of length \(n\). The function computes the reduced word \(w_1\) equal to \(w\) in the array a1 and returns the length of the reduced word. Legal tags for the word \(w\) are d, x, y, p, and l. See documentation of class mmgroup.MMGroup for the meaning of these tags.

It uses function xsp2co1_elem_to_word for computing \(w_1\). The word \(w_1\) stored in the array a1 may have up to 10 entries. Arrays a and a1 may overlap.

int32_t xsp2co1_elem_subtype(uint64_t *elem)

Return the subtype of an element of \(G_{x0}\).

Let \(g \in G_{x0}\) be stored in the array elem. The function returns the subtype of \(g\). If \(g\) maps the standard frame \(\Omega\) of the Leech lattice modulo 2 to a frame of subtype \(t\) then \(g\) has subtype \(t\).

The subtype is returned as an integer as in function gen_leech2_subtype in module gen_leech.c.

Since the subtype is determined by the size of the denominators of the representation \(4096_x\), it can be computed very fast.

The function returns -1 in case of an error.

uint32_t xsp2co1_check_word_g_x0(uint32_t *w, uint32_t n)

Check if a word of generators of the monster is in \(G_{x0}\).

We check if the word w of length n of generators of the monster group is in the subgroup \(G_{x0}\). The function returns the following status information:

0: w is in \(G_{x0}\)

1: w is not in \(G_{x0}\)

2: Nothing is known about w

Words of generators of the monster are implemented as described in file mmgroup_generators.h.

int32_t xsp2co1_isotropic_type4(uint32_t v, uint64_t *pB, int32_t n)

Compute maximal isotropic space corresponding to a type-4 vector.

Any type-4 vector in the Leech lattice mod 2 corrsponds to a unique maximal isotropic space (of dimension 12) in the Leech lattice mod 2. E.g. the standard type-4 vector \(\Omega\) corresponds to the space spanned by \(\Omega\) and (the images in the Leech lattice mod 2 of) all even Golay cocode words.

Given a type-4 vector \(v\) in Leech lattice encoding, we usually want to find the intersection of the isotropic space \(v^{(\perp)}\) corresponding to \(v\) with a given linear subspace \(X\) of the Leech lattice mod 2. Let \(X\) be the space spanned by the vectors \(b_0,...,b_{n-1}\) in the array B referred by pB of size n.

The function modifies the basis in the array B, so that it will be the a basis of the space \(v^{(\perp)} \cup X\) of dimension m in reduced echelon form. The function returns m in case of success and a negative value in case of failure. The function fails if \(v\) is not of type 4.

In case \(n < 0\) we assume that B is a basis of the whole Leech lattice mod 2 and return a basis of \(v^{(\perp)}\) in B. Array B must have size at least \(\max(n, 12)\).

int32_t xsp2co1_elem_row_mod3(uint64_t *elem, uint32_t column, uint64_t *v)

A low-level function to be used for testing.

A projection matrix \(\Pi\) is a symmetric matrix with one eigenvalue 1 and the other eigenvalues equal to zero operating on an Euclidean vector space. Let \(g \in G_{x0}\) be stored in the array elem in G_x0 representation. This function left multiplies \(g\) by a certain projection matrix \(\Pi\). The result \(y = \Pi \cdot g\) is an element of the vector space \(4096_x \otimes 24_x\). The function reduces the coordinates of \(y\) modulo 3 and stores the result in the array v in a format compatible the format used in the mmgroup.mm_op extension.

Right multiplcation of \(g\) by \(G_{x0}\) commutes with left multiplication of \(g\) by \(\Pi\), so that we can test the right multiplication by \(G_{x0}\) implemented in this module against the corresponding multiplication implemented in the mmgroup.mm_op extension. This leads to the important interoperability test in the python function mmgroup.tests.test_clifford.test_xs1_vector.test_vector.

We specify the projection matrix \(\Pi\) as a tensor product \(\Pi_{4096} \otimes \Pi_{24}\). Here \(\Pi_{24}\) projects onto the fixed short Leech lattice vector \((0,0,1,-1,0, \ldots,0)\). \(\Pi_{4096}\) is the projection onto the coordinate with number column of the space \(4096_x\).

Remark:

The result is an array with 4096 entries corresponding to the entries with tags Z and Y of a vector in the represention \(\rho_3\), as described in section The Representation of the Monster Group of the API reference.

Warning:

This function works only if the data type uint_mmv_t used in the mmgroup.mm_op extension is equal to the data type uint64_t.

int32_t xsp2co1_elem_read_mod3(uint64_t *v, uint64_t *elem, uint32_t row, uint32_t column)

Read entry of a transfromed vector of the monster rep modulo 3.

Let \(g \in G_{x0}\) be stored in the array elem. Let \(v\) be the vector of the representation \(4096_x \otimes 24_x\) modulo 3 stored in the array v in a format compatible to the format used in the mmgroup.mm_op extension. Then the function returns the entry of the vector \(v' = v \cdot g^{-1}\) in row 0 <= row < 4096 and column 0 <= column < 24 of \(v'\).

This function is considerably faster than the computation of \(v' = v \cdot g^{-1}\) using the functions in the mmgroup.mm_op extension.

In case column = 24 the function returns the value (v'[row,2] - v'[row,3]) mod 3. In case of success the return value is a nonnegative integer less than 3. A negative return value indicates failure.

C functions in xsp2co1_elem.c

File xsp2co1_elem.c contains functions for analyzing elements of the subgroup \(G_{x0}\) (of structure \(2^{1+24}.\mbox{Co}_1\)) of the monster.

Functions

int32_t xsp2co1_elem_to_N0(uint64_t *elem, uint32_t *g)

Convert element of \(G_{x0}\) to element of \(N_{0}\).

Let \(g \in G_{x0}\) be stored in the array elem1 in G_x0 representation. The function converts \(g\) to an element \(N_0\), as described in the documentation of file mm_group_n.c. The result is stored in the array of length 5 referred by parameter g.

The function returns 0 in case of success and ERR_QSTATE12_GX0_BAD_ELEM if \(g\) is not and \(N_0\).

int32_t xsp2co1_elem_from_N0(uint64_t *elem, uint32_t *g)

Convert element of \(N_{0}\) to element of \(G_{x0}\).

Let \(g \in N_{0}\) be stored in the array of length 5 referred by parameter g as described in the documentation of file mm_group_n.c. We convert \(g\) to an element in G_x0 representation and store the result in the array elem.

The function returns 0 in case of success and ERR_QSTATE12_GX0_BAD_ELEM if \(g\) is not in \(G_{x0}\).

int32_t xsp2co1_conjugate_elem(uint64_t *elem, uint32_t *a, uint32_t n)

Conjugate element of \(G_{x0}\) by an element of monster group.

Let \(g \in G_{x0}\) be stored in the array elem in G_x0 representation. Let \(w\) be a word in the generators of the monster group of length n. \(w\) is stored in the array a, and each entry of a encodes a generator of the monster group described in file mmgroup_generators.h.

The function tries to replace \(g\) by \(h = w^{-1} g w\). The function succeeds if for any prefix \(w_i\) of the word \(w\) we have \(w_i^{-1} g w_i \in G_{x0}\).

int32_t xsp2co1_power_elem(uint64_t *elem1, int64_t e, uint64_t *elem2)

Exponentiation of an element of the group \(G_{x0}\).

Let \(g \in G_{x0}\) be stored in the array elem1 in G_x0 representation. The function computes the power \(g^e\) and stores the result in the array elem2 in G_x0 representation. Here \(-2^{63} < e < 2^{63}\) must hold.

A negative return value indicates an error.

Any kind of overlapping beween the arrays elem1 and elem2 is allowed.

int32_t xsp2co1_power_word(uint32_t *a1, uint32_t n, int64_t e, uint32_t *a2)

Exponentiation of an word of the group \(G_{x0}\).

Let \(w\) be a word in the generators of \(G_{x0}\) of length n. \(w\) is stored in the array a1, and each entry of a1 encodes a generator of \(G_{x0}\) as described in file mmgroup_generators.h.

The function stores \(w^e\) in the array a2 in the same format as the word \(w\) and returns the length of the computed word, which is at most 10.

A negative return value indicates an error.

Any kind of overlapping beween the arrays a1 and a2 is allowed.

int32_t xsp2co1_odd_order_bitmatrix(uint64_t *bm)

Compute odd part of the order of an element of \(\mbox{Co}_1\).

Let an element \(g\) of \(\mbox{Co}_1\) be given as a 24 times 24 bit matrix in the array bm acting on the Leech lattice modulo 2 by right multiplication. Here vectors in the Leech lattice modulo 2 are given in Leech lattice encoding.

Then the function returns the odd part of the order of \(g\). It returns a negative value in case of failure.

int32_t xsp2co1_half_order_elem(uint64_t *elem1, uint64_t *elem2)

Compute (halved) order of an element of the group \(G_{x0}\).

Let \(g \in G_{x0}\) be stored in the array elem1 in G_x0 representation. The function returns the order of the element \(g\).

If the order \(o\) of \(g\) is even then the function stores \(g^{o/2}\) in the array elem2 in G_x0 representation. Otherwise it stores the neutral element in elem2.

A negative return value indicates an error.

Any kind of overlapping beween the arrays elem1 and elem2 is allowed.

int32_t xsp2co1_order_elem(uint64_t *elem)

Compute order of an element of the group \(G_{x0}\).

Let \(g \in G_{x0}\) be stored in the array elem1 in G_x0 representation. The function returns the order of the element \(g\).

A negative return value indicates an error.

int32_t xsp2co1_half_order_word(uint32_t *a1, uint32_t n, uint32_t *a2)

Compute (halved) order of a word of the group \(G_{x0}\).

Let \(w\) be a word in the generators of \(G_{x0}\) of length n. \(w\) is stored in the array a1, and each entry of a1 encodes a generator of \(G_{x0}\) as described in file mmgroup_generators.h.

If the order \(o\) of the word \(w\) is even then the function stores \(w^{o/2}\) in the array a2 in the same format as the word \(w\). Otherwise it stores the empty word in a2. The length k of the word in a2 is at most 10.

The function returns the value 0x100 *o + k.

A negative return value indicates an error.

int32_t xsp2co1_order_word(uint32_t *a, uint32_t n)

Compute order of a word of the group \(G_{x0}\).

Let \(w\) be a word in the generators of \(G_{x0}\) of length n. \(w\) is stored in the array a, and each entry of a encodes a generator of \(G_{x0}\) as described in file mmgroup_generators.h.

The function returns the order of \(w\) .

A negative return value indicates an error.

uint32_t xsp2co1_leech2_count_type2(uint64_t *a, uint32_t n)

Count type-2 vectors in an affine subspace of the Leech lattice mod 2.

This function returns the number of type-2 vectors in an affine subspace \(V\) of the Leech lattice mod 2. Subspace \(V\) is defined by an array \(a\) of length \(n\) of bit vectors. \(V\) is given by:

\(V = \{a_0 + \sum_{i=1}^{n-1} \lambda_i a_i \mid \lambda_i=0,1\}\).

Caution:

The function may change the description of the affine space \(V\) in the array \(a\) to a different description of the same space \(V\).

Remark:

This function is a much faster version of the function gen_leech2_count_type2 in file gen_leech.c. The implementation of the latter function is much simpler; so we keep it for test purposes.

int32_t xsp2co1_trace_98280(uint64_t *elem, int32_t (*f_fast)(uint64_t*))

Compute character of \(\rho_{98280}\) of element of \(G_{x0}\).

This function is for internal use only.

Let \(g \in G_{x0}\) be stored in the array elem in G_x0 representation. The function returns the character of the representation \(\rho_{98280}\).

This function may takes a long time is it does not use precomputed tables. However, precomputing such tables may requires this function (being alled without any precomputed tables).

In parameter f_fast the user may specify a function with signature int32_t (*f_fast)(uint64_t *elem) that returns the character \(\rho_{98280}\) in some cases. That function should use precomputed tables for computing that character and return an error code if it cannot compute a character. In this case we use the standard method form computing the requested character. If f_fast is NULL then we always use the standard method.

Any value less than -0x1000000 returned by function f_fast or by this function is to be interpreted as an error.

int32_t xsp2co1_traces_small(uint64_t *elem, int32_t *ptrace)

Workhorse for function xsp2co1_traces_all

This function is for internal use only.

Parameters and operation are are as in function xsp2co1_traces_all. But this function does not compute the character of the representation \(\rho_{98280}\) in ptrace[3].

int32_t xsp2co1_traces_all(uint64_t *elem, int32_t *ptrace)

Compute relevant characters of element of \(G_{x0}\).

This function is for internal purposes only.

Let \(g \in G_{x0}\) be stored in the array elem in G_x0 representation. The function computes the characters of the representations \(\rho_{24}, \rho_{576}, \rho_{4096}, \rho_{98280}\) and stores the result in ptrace[0],..., ptrace[3] in that order.

This function returns 0 in case of success and a nonzero value otherwise.

There is a considerably faster function xsp2co1_traces_all in module xsp2co1_traces.c performing exactly the same operation depending in the same input parameters. More details are given in the documentation of that function.

In contrast to function xsp2co1_traces_fast, this function does not use any precomputed tables. Actually, this function is used for precomputing those tables.

int32_t xsp2co1_rand_word_N_0(uint32_t *w, uint32_t in_N_x0, uint32_t even, uint64_t *seed)

Generate a random element of the group \(N_0\).

The function computes a uniform distributed random element \(g\) of the subgroup \(N_0\) of structure \(2^{2+11+22}.(M_{24} \times \mbox{Sym}_3)\) of the monster. The group \(N_0\) is generated by the generators with tags x, y, d, p, t.

Thw function stores a word representing the element \(g\) in the buffer w and returns the length of that word.

The length of the word in the buffer w is at most 5.

If parameter in_N_x0 is nonzero then we compute a random element of the subgroup \(N_{x0}\) of index 3 in \(N_0\) generated by the generators with tags x, y, d, p.

If parameter even is nonzero then we compute a random element of the subgroup \(N_{\mbox{even}}\) of index 2 in \(N_{0}\) generated by the generators with tags x, y, d, p, t, where all generators with tag d correspond to even Golay cocode words.

If both, in_N_x0 and even, are nonzero then we compute a random element of \(N_{xyz0} = N_{\mbox{even}} \cap N_{x0}\).

The function uses the internal random generator in file gen_random.c. Parameter seed must be a seed for a random generator as described in file gen_random.c.

int32_t xsp2co1_rand_word_G_x0(uint32_t *w, uint64_t *seed)

Generate a random element of the group \(G_{x0}\).

The function computes a uniform distributed random element \(g\) of the group \(G_{x0}\). It stores a word representing the element \(g\) in the buffer w and returns the length of that word.

The length of the word in the buffer w is at most 10.

The function uses the internal random generator in file gen_random.c. Parameter seed must be a seed for a random generator as described in file gen_random.c.

A negative return value indicates an error.

C functions in leech3matrix.c

File leech3matrix.c contains functions for computing with matrices corresponding to the part with tag ‘A’ of a vector of the representation of the monster modulo 3. Note that this part has a natural interpretation as a symmetric matrix on the Leech lattice.

For these computations we deal with i0 times i1 matrices m for i0 <= 24, i1 <= 48, Such a matrix is stored in an array a of integers of type uint64_t of length 24 * 3. Here the entry m[i,j] is stored in a[3*i + j/16], bits 4 * (j % 16),..., 4 * (j % 16) + 3. We call a the matrix mod 3 encoding of the matrix m.

Unless otherwise stated, we assume that the lower two bits of such a bit field have arbitrary values, and that the higher two bits of that bit field are zero.

There are functions for loading such a matrix m from a vector in a represenation of the monster, for elechonization of m, for computing the kernel of m etc.

Functions

void leech3matrix_echelon(uint64_t *a)

Echelonize a matrix of integers mod 3.

Here a is a matrix in matrix mod 3 encoding as documented in the header of this file. That matrix is transformed to row echelon form. We echelonize columns 0,…,23 of matrix a in that order. The matrix is not converted to reduced echelon form.

void leech3matrix_compress(uint64_t *a, uint64_t *v)

compress a matrix in matrix mod 3 encoding

Let a be an 24 times 48 matrix in matrix mod 3 encoding. We consider a as a pair of two matrices Ah, Al, withAl in columns 0,…,23 and Ah in columns 24,…,47 of a.

We store matrix Al in the entriesv[0], ..., v[23], and matrix Ah in the entriesv[24], ..., v[47]. Here column j of a row of Ah or Al is reduced modulo 3 (so it has value 0, 1, or 2) and that value is stored in bits 2*j+1 and 2*j of the corresponding entry of v.

So each of the matrices Al and Ah will be encoded as the part with tag ‘A’ of a vector in the representation \(\rho_3\) of the monster modulo 3.

The overlapping v == a is legal; any other kind of overlappig between v and a is illegal.

void leech3matrix_sub_diag(uint64_t *a, uint64_t diag, uint32_t offset)

Subtract diagonal matrix from matrix in matrix mod 3 encoding.

Let a be an 24 times 48 matrix in matrix mod 3 encoding.

We subtract a diagonal matrix from a. More precisely, we subtract the integer diag from all entries a[i, i+offset], for i = 0,...,23.

uint64_t leech3matrix_rank(uint64_t *a, uint32_t d)

Rank and kernel of a 24 times 24 matrix modulo 3.

Let r be the rank of the 24 times 24 matrix b = a - d * 1. Here the entries of that matrix are taken modulo 3, d is an integer, and 1 is the unit matrix. Input a is a 24 times 24 matrix in matrix mod 3 encoding as documented in the header of this file.

Let r be the rank of matrix b with entries taken modulo 3. If matrix b has rank 23 then its kernel is one dimensional. In that case the kernel contains two nonzero vectors +-w, and we define w to be one of these vectors. Otherwise we let w be the zero vector.

The function returns the value (r << 48) + w, with w the vector defined above given in Leech lattice mod 3 encoding as described in The C interface of the mmgroup project.

Input a is destroyed.

uint64_t leech3matrix_vmul(uint64_t v, uint64_t *a)

Multiply vector with a 24 times 24 matrix modulo 3.

Input a is a 24 times 24 matrix encoded as the part with tag ‘A’ of a vector in the representation \(\rho_3\) of the monster modulo 3. Input v is a vector of 24 integers modulo 3 encoded in Leech lattice mod 3 encoding. The function computes the product \(v \cdot a\) of the vector \(v\) and the matrix \(a\) and returns the result in Leech lattice mod 3 encoding.

Vector \(v\) has 24 entries. If the upper \(k\) entries of \(v\) are zero then we access the first \(24-k\) rows of matrix \(a\) only. So the buffer referred by a must have length (24 - k) in this case.

int32_t leech3matrix_prep_type4(uint64_t *a, uint32_t n, uint64_t *w, uint64_t *seed)

Prepare subspace of Leech lattice mod 3 for finding type-4 vectors.

Let \(a\) be the subspace of the Leech lattice mod 3 spanned by the vectors in the array a of length n. Here each entry of the array a is encoded in the same way as a row of the part with tag ‘A’ of a vector in the representation \(\rho_3\) of the Monster modulo 3. Usually, such a subspace of the Leech lattice mod 3 is computed by applying function leech3matrix_echelon to a (suitably modified) part with tag ‘A’ of a vector in the representation \(\rho_3\).

The function computes an array of 2*n random vectors taken from the space \(a\) and stores these 2*n vectors in the array w in Leech lattice mod 3 encoding. Half of the vectors in array w are obtained by left multiplying matrix \(a\) with a random upper triangular matrix; and the other half of these vectors is obtained by left multiplication with a random lower triangular matrix. Parameter seed points to a random generator for generating random matrices; see module gen_random.c for details.

Thus the vectors in array w span the space \(a\); and we may obtain random vectors in \(a\) by performing random additions and subtractions of the entries of w. E.g. function leech3matrix_prep_type4 generates random type-4 in the space space \(a\) using that method.

The function returns the size 2*n of the array w in case of success and a negative value in case of failure. Any overlapping between the arrays a and w is allowed. On input, 1 <= n <= 12 must hold.

int32_t leech3matrix_rand_type4(uint64_t *w, uint32_t n, uint32_t trials, uint64_t *seed)

Random type-4 vector in subspace of Leech lattice mod 3.

The function tries to find a random type-4 vector in a subspace \(a\) of the Leech lattice mod 3 spanned by the vectors in the array a of length n. Here the entries of the array w should be given in Leech lattice mod 3 encoding, as e.g. returned by function leech3matrix_prep_type4. That function returns more vectors generating the space \(a\) than necessary in order to facilitate the generation of random vectors in \(a\).

The function performs up to trials random additions or subtractions in the space \(a\), until it finds a type-4 vector. If such a type-4 vector has been found then that vector is returned as a vector v in the Leech lattice mod 2 in Leech lattice encoding. Parameter seed points to a random generator for generating the required random data; see module gen_random.c for details.

If a type-4 vector v has been found then the function returns (t << 4) + v. Here 0 <= v < 0x1000000 is the vector found in the Leech lattice mod 2 in Leech lattice encoding; and t is the number of trials required to find v. In case t > 127 we put t = 127.

If no type-4 vector has been found after trials trials then the function returns 0.

The function returns a negative value in case of failure; e.g. if the random generator has failed.

int32_t leech2matrix_add_eqn(uint64_t *m, uint32_t nrows, uint32_t ncols, uint64_t a)

Add an equation to a system of linear bit equations.

The idea behind this function is that an external process generates rows of a bit matrix with ncols columns, with 0 < ncols <= 32. This function checks such a row a and accepts it, if it linearly independent of all previously accepted rows. Thus at most ncols rows can be accepted. The nrows already accepted rows are stored in the array m. The function returns 1 if row a is accepted and 0 otherwise. A negative return value indicates an error. The size of the array m should be at least ncols.

Let A be the ncols times ncols matrix of all accepted rows a[i], 0 <= i < ncols; and let I be the ncols time ncols unit matrix. We left multiply A with a matrix T such that T * A = I. Thus T = A**(-1). Technically, we perform row operations on the matrix A[:nrows] containing the first nrows lines already accepted, such that T * A[:rnows] is in reduced echelon form. We also perform the same row operations on the unit matrix to obtain T. We store T[:rnows] in columns 0,...,ncols-1 of matrix M and T*A[:rnows] in columns ncols,...,2*ncols-1 of matrix M.

One may use function leech2matrix_solve_eqn for solving a system of linear equations obtained in that way.

uint32_t leech2matrix_solve_eqn(uint32_t *m, uint32_t ncols, uint64_t v)

Solve a system of linear bit equations.

Let A be a nonsingular ncols times ncols bit matrix stored in the array m in the special form as described in function leech2matrix_add_eqn.

The function returns the solution w of the equation w * A = v.

Caution:

Here m is of of type uint32_t *, but the corresponding parameter in function leech2matrix_add_eqn is of type uint64_t *. This simplifies the use of this function in most pplications.

uint32_t leech2_matrix_basis(uint32_t *v2, uint32_t n, uint64_t *basis, uint32_t d)

Subspace generated by vectors of Leech lattice modulo 2.

Compute a basis of the subspace of the Leech lattice modulo 2 generated by the vectors v2[0],...,v2[n-1].

The function returns the dimension k of that subspace and computes a basis of that subspace in basis[i], 0 <= i < k.

Here d must be an upper bound for the dimension k. If k is unknown, one should put d = 24.

Bits 23,…,0 of the output matrix are echelonized in a special way. Here the columns are processed in the order:

11, 22, 21, …, 13, 12, 10, 9, …, 1, 0, 23.

One of the advantages of this echelonization is that the vector \(\Omega\) (encoded as 0x800000) will occur in the basis if it is in the subspace, and that there are many even vectors (i.e. vectors orthogonal to \(\Omega\)) in the basis.

int32_t leech2_matrix_orthogonal(uint64_t *a, uint64_t *b, uint32_t k)

Compute standard orthogonal complement in Leech lattice mod 2.

Let \(A = a_0\ldots, a_{k-1}\) be a matrix of \(k\) vectors in the Leech lattice mod 2 stored in the array a. The function returns a basis \(B = b_0\ldots, b_{23}\) of the Leech lattice mod 2 in the array b, and it returns a number \(m\) such that the vectors \(b_m\ldots, b_{23}\) are a basis of the orthogonal complement of the space generated by the row vectors of \(A\).

If the vectors \((a_0\ldots, a_{k-1})\) are linear independent then the function returns \(m = k\), and vector \(b_i, i < k\) is orthogonal to all vectors \(a_j\) with \(j \neq i\).

The basis \(B = b_0\ldots, b_{23}\) is stored in the array b.

We require \(k \leq 24\). The function returns \(m \geq 0\) in case of success a negative value in case of failure.

uint32_t leech2_matrix_radical(uint32_t *v2, uint32_t n, uint64_t *basis, uint32_t d)

Radical of subspace generated by vectors of Leech lattice mod 2.

Compute the radical of the subspace of the Leech lattice modulo 2 generated by the vectors v2[0],...,v2[n-1]. Here the radical is the intersection of the space generated by v2[0],...,v2[n-1] with the orthogonal complement of that space.

Input parameters v2, n, and d are as in function leech2_matrix_basis. A basis of the radical of the space is computed in basis. The basis is echelonized as in function leech2_matrix_basis. The function returns the dimension k of radical spanned by that basis.

uint32_t leech2_matrix_expand(uint64_t *basis, uint32_t dim, uint32_t *v2)

List vectors in a subspace of the Leech lattice modulo 2.

The function computes all 2**dim vectors of the subspace V of the Leech lattice modulo 2 given by the basis

basis[0], ..., basis[dim - 1] .

These vectors are written into the array v2. The function

void leech3_vect_mod3_to_signs(uint64_t *v, uint64_t mult, uint32_t n, uint64_t *signs)

Map vector in rep of the Monster mod 3 to array of signs.

Let v be a part of a vector of the 198884-dimensional representation of the monster group modulo 3, which is organized as a n times 24 matrix of integers mod 3. Here v is the relevant part of a vector encoded as in the mmgroup.mm_op extension; see The C interface of the mmgroup project, section Description of the mmgroup.mm extension for details.

Let mult be a vector of 24 integers mod 3 encoded in the Leech lattice mod 3 encoding.

The function computes the scalar product of each row of v with mult and stores the n signs of these products in the array sign. Here the signs are stored in natural order and encoded as in function qstate12_to_signs in module qstate12io.c. As usual, the integers 0, 1, and 2 (mod 3) are mapped to 0, ‘+’, and ‘-’, respectively.

Output array signs should have at least (n + 31) >> 5 entries.

C functions in involutions.c

File involutions.c contains functions for transforming involutions of the subgroup \(G_{x0}\) (of structure \(2^{1+24}.\mbox{Co}_1\)) of the monster.

We try to transform such involutions to a standard form via conjugation by elements of the monster group.

Functions

int32_t xsp2co1_involution_invariants(uint64_t *elem, uint64_t *invar)

Compute invariant spaces for an involution in \(G_{x0}\).

Let \(g\) be the element of the group \(G_{x0}\) stored in the array given by parameter elem. Let \(\Lambda_2\) be the Leech lattice mod 2, with vectors in \(\Lambda_2\) coded in Leech lattice encoding as usual. Conjugation by \(g\) is a linear operation on \(\Lambda_2\), since the vectors in \(\Lambda_2\) correspond to the elements of the normal subgroup \(Q_{x0}\) of structure \(2^{1+24}\) (modulo the centre of \(G_{x0}\)). Let \(A = A(g)\) be the \(24 \times 24\) bit matrix that performs this operation on \(\Lambda_2\) by right multiplication. Put \(A_1 = A - 1\), and let \(I_1\) be the image of matrix \(A_1\).

In this function we require that the image of \(g\) in the factor group \(\mbox{Co}_1\) of \(G_{x0}\) has order 1 or 2; othereise the function fails. That condition is equivalent to \(A^2 = 1\), and also to \(A_1^2 = 0\). If this is the case then we have:

\((\ker A_1)^\perp = I_1 \subset \ker A_1 = (I_1)^\perp\).

Any element \(v \in \ker A_1\) is invariant under \(g\), and so the corresponding element in \(Q_{x0}\) is invariant up to sign. The elements of \(Q_{x0}\) invariant under \(g\) (modulo the center of \(Q_{x0}\)) form a subspace \((\ker A_1)^+\) of \(\ker A_1\) of codimension \(0\) or \(1\). Let \((I_1)^+\) be the orthogonal complement of \((\ker A_1)^+\). Then \(I_1\) has the same codimension in \((I_1)^+\). The purpose of this function is to compute a basis of the smaller of the two spaces \((I_1)^+\) or \(\ker A_1\).

We compute an output matrix in the array invar and return the number k of rows of that matrix in case of success. We use the following column bits of the output matrix.

23,…,0: Basis vector \(v_i\) of \(I_1\) or \((I_1)^+\)

55,…,32: Preimage (under \(A_1\)) of basis vector \(v_i\), undefined if \(v_i \notin I_1\)

27: Here a nonzero bit in row 0 indicates an error.

In bits 24,…,26 of the output matrix we return the following linear forms on the space spanned by basis vectors:

Case 1: \(I_1 = (I_1)^+\) or \(I_1 = \ker A_1 \)

Then we return a basis of \(I_1\), and we have \(k = \dim I_1 = \dim (I_1)^+ \in \{0, 8, 12\}\)

Bit 26: 0

Bit 25: type of basis vector (modulo 2)

Bit 24: sign of basis vector in \(I_1\)

Case 2: \(I_1 \neq (I_1)^+\) and \(I_1 \neq \ker A_1 \)

Then we return a basis of \((I_1)^+\), and we have \(k - 1 = \dim (I_1)^+ - 1 = \dim I_1 \in \{0, 8\}\).

Bit 26: 0 if and only if the basis vector is in \(I_1\)

Bit 25: 0

Bit 24: sign of the basis vector if the vector is in \(I_1\), and type of the basis vector (mod 2) otherwise

Parameter invar must be an array of length 12. Zero lines are appeded to that array so that its length will be 12.

The function returns the dimension k of the computed basis, and a negative value in case of error. The return value ERR_QSTATE12_GX0_BAD_ELEM means that that the image of \(g\) in \(\mbox{Co}_1\) has order greater than \(2\).

Bits 26,…,0 of the output matrix are echelonized in a special way. Here the columns are processed in the order:

26, 25, 24, 11, 22, 21, …, 13, 12, 10, 9, …, 1, 0, 23.

One of the advantages of this echelonization is that the vector \(\Omega\) (encoded as 0x800000) will occur in the basis if it is in the subspace, and that there are many even vectors (i.e. vectors orthogonal to \(\Omega\)) in the basis. Also, bits 26, 25, 24 may be nonzero at most in the first two columns of the output matrix.

int32_t xsp2co1_involution_orthogonal(uint64_t *invar, uint32_t col)

Compute some orthogonal complement for involution invariants.

Let \(g\) be an element of the group \(G_{x0}\) such that the image of \(g\) in \(\mbox{Co}_1\) has order 1 or 2. For that element \(g\), let \(A, A_1\), and \(I_1\) be as in function xsp2co1_involution_invariants.

In this function the input parameter invar must be equal to the output invar of function xsp2co1_involution_invariants applied to the element \(g\).

There is a nondegenerate bilinear form \( \langle \langle .,. \rangle \rangle\) on \(I_1\) given by

\(\langle \langle x,y \rangle \rangle = \langle \pi(x),y \rangle\),

where \(\pi(x)\) is any preimage of \(x\) under \(A_1\), and \(\langle .,. \rangle\) is the scalar product on the Leech lattice modulo 2. The form \(\langle \langle .,. \rangle \rangle \) is also called the Wall parametrization, see [Wal63]. If the image of \(g\) in \(\mbox{Co}_1\) has order at most two then the Wall parametrization is a symmetric bilinear form.

The function computes the orthogonal complement \(v\) of a linear form \(l\) on the Leech lattice modulo 2 under the Wall parametrization. Then \(v\) is a vector in the Leech lattice modulo two. If parameter col is 0 or 1 then we let \(l\) be the linear form in column col + 25 of matrix invar.

The function returns \(v\) in case of success and a negative value in case of failure.

[Wal63] G. E. Wall. On the conjugacy classes in the unitary, symplectic and orthogonal groups. J. Australian Math. Soc. 3, pp 1–63, 1963.

int32_t xsp2co1_involution_find_type4(uint64_t *invar, uint32_t guide)

Find type-4 vector in a space computed by xsp2co1_involution_invariants.

Let \(g\) be the element of the group \(G_{x0}\), and for that element \(g\) let \(A, A_1, I_1\), and \((I_1)^+\) be as in function xsp2co1_involution_invariants.

Here input parameter invar must be the output invar of function xsp2co1_involution_invariants applied to the element \(g\). This function is successful in case \(\dim I_1 = 8\) only.

We return a type-4 vector the space \(I_1\). If no such vector exists then we return 0.

Parameter guide should usually be zero. If guide is a type-4 vector in the Leech lattice mod 2 satisfying the assumptions the return value vthen the function returns v = guide. Otherwise parameter guide is ignored.

int32_t xsp2co1_elem_find_type4(uint64_t *elem, uint32_t guide)

Try to simplify an element in \(G_{x0}\) via conjugation.

Let \(g\) be the element of the group \(G_{x0}\) stored in the array given by parameter elem. In this function we require that the image of \(g\) in the factor group \(\mbox{Co}_1\) of \(G_{x0}\) has order 1 or 2; otherwise the function fails.

Let \(\Lambda_2\) be the Leech lattice mod 2, with vectors in \(\Lambda_2\) coded in Leech lattice encoding as usual. Let \(\Omega\) be the standard frame in \(\Lambda_2\).

Then the function tries to find a vector \(v \in \Lambda_2\) with the following property:

For any \(h \in G_{x0}\) with \(v \cdot h = \Omega\) we have \(h^{-1} g h \in N_{x0}\).

The function returns \(v\) in case of success and a negative value in case of an error. It returns ERR_QSTATE12_GX0_BAD_ELEM if no suitable vector \(v\) has been found.

The function succeeds if the following two conditions hold:

  • \(g\) is in class 1A, 2A, 2B or 4A of the monster group

  • \(g^2\) is in subgroup \(Q_{x0}\) of \(G_{x0}\).

In these two cases there is also a power \(\tau^e\) of the triality element \(\tau\) with

\(\tau^{-e} h^{-1} g h \tau^e \in Q_{x0}\).

Caution:

The last statement has been checked for classes 1A, 2A and 2B only!

Parameter guide should usually be zero. If guide is a type-4 vector in the Leech lattice mod 2 satisfying the assumptions the return value vthen the function returns v = guide. Otherwise parameter guide is ignored. It is also ignored in case \(g \in Q_{x0}\).

int32_t xsp2co1_elem_conj_G_x0_to_Q_x0(uint64_t *elem, uint32_t *a)

Try to map an element of \(G_{x0}\) to \(Q_{x0}\).

Let \(g\) be the element of the group \(G_{x0}\) stored in the array given by parameter elem. The function tries to find an element \(h\) in the monster group with \(h^{-1} g h = q \in Q_{x0}\).

The function succeeds if the following two conditions hold:

  • \(g\) is in class 1A, 2A, 2B or 4A of the monster group

  • \(g^2\) is in subgroup \(Q_{x0}\) of \(G_{x0}\).

The function stores \(h\) in the output array a as a word of generators of the monster group. Array a must be of length 7 . The function returns \(q\) in bits 24,...,0 of the return value, and number of atoms in the array a in bits 27, 26, 25 of the return value The data in the array a are padded with zeros.

The function returns a negative value in case of failure. It returns ERR_QSTATE12_GX0_BAD_ELEM if no suitable element \(h\) can be found.

int32_t xsp2co1_elem_conjugate_involution(uint64_t *elem, uint32_t *a)

Map an involution in \(G_{x0}\) to a standard form.

Let \(g\) be an involution of the group \(G_{x0}\) stored in the array given by parameter elem.

The function computes an element \(a\) in the monster such that \(h = a^{-1} g a\) is one of the following elements of the subgroup \(Q_{x0}\) of \(G_{x0}\):

If \(g = 1\) then \(h = a = 1\).

If \(g\) is a 2A involution then \(h\) is the involution in \(Q_{x0}\) corresponding to the Golay cocode word with entries \(2,3\) being set.

If \(g\) is a 2B involution then \(h\) is the central involution \(z\) in \(Q_{x0}\).

The element \(a\) is stored in the array a as a word of generators of the monster group. In case of success the function returns 0x100 * I + len(a), where len(a) is the length of the array a. We put I = 0 if \(g = 1\). We put I = 1, 2 if \(g\) is a 2A or 2B involution, respectively.

The function returns ERR_QSTATE12_GX0_BAD_ELEM if \(g\) is not an involution.

The array a must have length at least \(14\).

C functions in xsp2co1_traces.c

File xsp2co1_traces.c contains functions for computing characters of some representations of the subgroup \(G_{x0}\) (of structure \(2^{1+24}.\mbox{Co}_1\)) of the monster.

Such computations can be very expensive, especially for some classes of involutions, or for elements that map to involutions in the factor group \(\mbox{Co}_1\) of \(G_{x0}\).

This file contains a function xsp2co1_elem_involution_class for the classification of elements that map to involutions in \(\mbox{Co}_1\).

Function xsp2co1_traces_fast uses a precomputed table for computing the characters of elements of \(G_{x0}\). That table is addressed by the class information computed by function xsp2co1_elem_involution_class. The functions in module mmgroup\tests\test_involutions.make_involution_samples.py precompute that table. We simply copy and paste the table from the output of that python function to to this file.

The precomputation of the table requires the function xsp2co1_traces_all in file xsp2co1_elem.c. That function computes the same characters as function xsp2co1_traces_fast without using precomputed tables.

Function xsp2co1_elem_involution_class does not use a precomputed table, but the verification of this function requires inspection of the output of the module make_involution_samples.py mentioned above.

Functions

int32_t xsp2co1_elem_involution_class(uint64_t *elem)

Compute class information for certain elements of \(G_{x0}\)

Let \(g \in G_{x0}\) be stored in the array elem in G_x0 representation. If \(g\) maps to an involution in the factor group \(\mbox{Co}_1\) of \(G_{x0}\) then the function returns a nonzero value indicating some class information about \(g\). Otherwise the function returns 0.

The class information in the return value is to interpreted as follows:

  bits  7 .. 0: class of element  g  in the Monster group, e.g
                0x21 means class 2A,
                0x41 means class 4A, 0x42 means class 4B, etc.

  bits 11 .. 8: Class of element  g  in the factor group Co_1
                0 means class 1A in Co_1
                1 means class 2A in Co_1
                2 means class 2B in Co_1
                3 means class 2C in Co_1

  bit 12:       0 if  g  and  -g  are in the same class in the Monster
                1 otherwise

  bit 13:       1 if q g  is equal to or powers up to -1
                0 otherwise

All other bits in the return value are set to zero.

Here \(-1\) is the central involution \(x_{-1}\) in \(G_{x0}\), and \(-g = x_{-1} \cdot g\) .

Write \(h(g)\) as an abbreviation for the result of this function applied to an element \(g\) of \(G_{x0}\). Then the following assertions have been checked computationally in files make_involution_samples.py, or test_xp2_traces.py, or can easily be checked mathematically.

Possible values \(h(g)\) (depending on the class of \(g Q_{x0}\)) in \(\mbox{Co}_1\) are:

 class 1A: 0x1011, 0x3022, 0x0022, 0x0021, 0x2041
 class 2A: 0x1121, 0x1122, 0x0143, 0x2143, 0x0142, 0x0141, 0x0122
 class 2B: 0x0244, 0x2244
 class 2C: 0x0322, 0x0341, 0x0344, 0x2382, 0x0343, 0x0342

The value \(h(g)\) determines the characters of the representations \(98280_x, 299_x, 24_x, 4096_x\) of \(g\) uniquely, where by construction of \(G_{x0}\) the last two characters are determined up to sign only.

The class of an involution \(g\) is determined uniquely by \(h(g)\).

int32_t xsp2co1_traces_fast(uint64_t *elem, int32_t *ptrace)

Compute relevant characters of element of \(G_{x0}\).

Let \(g \in G_{x0}\) be stored in the array elem in G_x0 representation. The function computes the characters of the representations \(\rho_{24}, \rho_{576}, \rho_{4096}, \rho_{98280}\) and stores the result in ptrace[0],..., ptrace[3] in that order. Here \(\rho_{576}\) is the tensor square of \(\rho_{24}\).

This function returns 0 in case of success and a nonzero value otherwise.

Note that the tensor product \(\rho_{24} \otimes \rho_{4096}\) is well defined, but the factors of that product are defined up to sign only. We normalize the characters corresponding to \(\rho_{24}\) and \(\rho_{4096}\) so that the first nonzero value of these two characters (in the order given above) is positive.

So this function performs the same action as function xsp2co1_traces_all in file xsp2co1_elem.c, but it is considerably faster, since it uses precomputed tables for som hard cases.

int32_t xsp2co1_elem_conjugate_involution_Gx0(uint64_t *elem, uint32_t guide, uint32_t *a)

Map an involution in \(G_{x0}\) to a standard form.

Let \(g\) be an involution in the group \(G_{x0}\) stored in the array given by parameter elem in G_x0 representation.

The function computes an element \(a\) in \(G_{x0}\) such that \(h = a^{-1} g a\) is a (fixed) representative of the class of \(g\) in the group \(G_{x0}\).

The element \(a\) is stored in the array a as a word of generators of the monster group. In case of success the function returns 0x100 * iclass + len(a), where len(a) is the length of the data in the array a, and iclass is explained below. The function returns a negative value in case of failure, e.g. if \(g\) has order greater than 2. The array a must have length at least \(10\).

In the sequel we list the representatives of all classes of involutions in \(G_{x0}\) computed by this function. For any such representative we also list the number iclass indicating the class of the involution as computed by function xsp2co1_elem_involution_class.

iclass = 0x1101: the neutral element \(x_1\)

iclass = 0x3022: the central involution \(x_{-1}\)

iclass = 0x0021: the element \(x_{\{2,3\}}\)

iclass = 0x0022: the element \(x_{\Omega}\)

iclass = 0x1121: the element \(y_o\)

iclass = 0x1122: the element \(x_{-1} y_o\)

iclass = 0x0122: the element \(y_o x_{\{8,9\}}\)

iclass = 0x0322: the element \(y_D x_{\{0, 12\}}\)

Here in \(x_{\{i,j\}}\) the index \(\{i,j\}\) indicates a Golay cocode word of length 2 given by the entries \(i\) and \(j\). Octad \(o\) is the standard octad \(\{0,1,2,3,4,5,6,7\}\). Dodecad \(D\) is the standard dodecad \(\{0, 4, 8, 13, 14, 15, 17, 18, 19, 21, 22, 23\}\).

Parameter guide should usually be zero. If guide is a type-4 vector \(v_4\) in the Leech lattice mod 2 such that the two conditions \(h = a^{-1} g a\) and \(v_4 \cdot a = \Omega\) can both be achieved then we compute an element \(a\) satisfying these two conditions. Otherwise parameter guide is ignored. Here \(\Omega\) is the standard frame in the Leech lattice.

int32_t xsp2co1_map_involution_class_Gx0(uint32_t iclass, uint32_t *a)

Map an involution class in \(G_{x0}\) to its representative.

Here parameter class must be a class number of an involution in the group \(G_{x0}\) as returned by function xsp2co1_elem_conjugate_involution_Gx0.

Then the function computes the representative \(h\) of the class of involutions in \(G_{x0}\) as it is computed by function xsp2co1_elem_conjugate_involution_Gx0.

The element \(h\) is stored in the array a as a word of generators of the monster group. In case of success the function returns the length len(a) of the data in the array a. The function returns a negative value in case of failure, e.g. if iclass does not correspond to an involution. The array a must have length at least \(2\).

C functions in xsp2co1_map.c

File xsp2co1_map.c contains functions for computing an element \(g\) of the group \(G_{x0} = 2^{1+24}.\mbox{Co}_1\) from the action of \(g\) on the normal subgroup \(Q_{x0} = 2^{1+24}\) of \(G_{x0}\).

Here the group \(G_{x0}\) is the maximal subgroup of the Monster used in our construction of the Monster. We store an element of \(G_{x0}\) as word of generators of that group as described in filemmgroup_generators.h. Internally, we also use the G_x0 representation for elements of \(G_{x0}\) as described in file xsp2co1.c.

Elements of the group \(Q_{x0}\) are stored in Leech lattice encoding as described in section Description of the mmgroup.generators extension.

Note that an element of \(G_{x0}\) is determined by its action on \(Q_{x0}\) up to sign only.

The main function xsp2co1_elem_from_mapping in this module tries to find the ‘nicer’ of the elements \(\pm g\) from its action on \(Q_{x0}\).

Functions

int32_t xsp2co1_Co1_get_mapping(uint32_t *m1, uint32_t *m2, uint32_t *m_out)

Compute a certain mapping from \(Q_{x0}\) to itself.

Let \(g \in G_{x0}\) be such that \(g\) maps \(m_{1,j}\) to \(m_{2,j}\) via conjugation, for \(m_{i,j} \in Q_{x0}\), \(i = 1,2; 0 \leq j \leq 24\). If the \(m_{1,j}\) (considered as vectors in \(\Lambda/2 \Lambda\)) are linear independent then there is at most one such \(g\), up to sign.

Here inputs \(m_{1,j}, m_{2,j}\) are given in the arrays m1, m2 in Leech lattice encoding.

The function computes \(g\) as a mapping \(m_{0,j} \mapsto m_{3,j}\), where \(m_{0,j}\) is the standard basis of \(\Lambda/2 \Lambda\) (with \(m_{0,j}\) = 1 << j in Leech lattice encoding). The function stores the vectors \(m_{3,j}\) in the array m_out of length 24 in Leech lattice encoding.

Let \(o\) be the odd part of the order of \(g\), so that \(g\) has order \(2^k \cdot o\).

The function returns a negative value if it detects an error, and it returns \(o\) if it does not detect any error. If the function returns \(o \geq 0\) and the output is a correct image of the standard basis then there exists a \(g \in G_{x0}\) that maps \(m_{1,j}\) to \(m_{2,j}\). The order of any such element \(g\) divided by \(o\) is a power of two.

Any overlapping between the arrays referred by m1, m2, m_out is allowed.

int32_t xsp2co1_Co1_matrix_to_word(uint32_t *m, uint32_t *g)

Compute preimage in \(G_{x0}\) of automorphism on \(Q_{x0}\).

Let matrix \(m\) (given by parameter m) be a 24 times 25 bit matrix that describes an automorphism \(g'\) acting on \(Q_{x0}\). Here row m[i] is the image of the (positive) element in \(Q_{x0}\) corresponding to the i-th basis vector of \(\Lambda/2\Lambda\), and m[i] is encoded in Leech lattice encoding.

If possible, the function computes a \(g \in G_{x0}\) that acts on \(Q_{x0}\) by conjugation in the same way as \(g'\) acts on \(Q_{x0}\). If such a \(g\) exists, it is determined up to sign only.

In case of success the function stores \(g\) as a word of generators of \(G_{x0}\) in the buffer referred by parameter g and returns the length of that word. In case of failure the function returns a negative value.

Array g must have length at least 10.

int32_t xsp2co1_elem_from_mapping(uint32_t *m1, uint32_t *m2, uint32_t *g)

Compute \(g \in G_{x0}\) from its operation on \(Q_{x0}\).

Let \(g \in G_{x0}\) be such that \(g\) maps \(m_{1,j}\) to \(m_{2,j}\) via conjugation, for \(m_{i,j} \in Q_{x0}\), \(i = 1,2; 0 \leq j \leq 24\). If the \(m_{1,j}\) (considered as vectors in \(\Lambda/2 \Lambda\)) are linear independent then there is at most one such \(g\), up to sign.

Here inputs \(m_{1,j}, m_{2,j}\) are given in the arrays m1, m2 in Leech lattice encoding.

If possible then the function computes a \(g \in G_{x0}\) that maps \(m_{1,j}\) to \(m_{2,j}\) via conjugation. In case of success it stores \(g\) as a word of generators of \(G_{x0}\) in the buffer referred by parameter g and returns the length of that word in the lower 8 bits of the return value. In case of failure the function returns a negative value.

Array g must have length at least 10.

Note that \(g\) is determined up to sign only. The function makes a considerable effort to disambiguate the two elements \(\pm g\).

If one of the two element \(\pm g\) has odd order then the other one has necessarily even order; in that case we return the element with odd order. Otherwise both elements have the same (even) order \(2^k \cdot o, o\) odd. Then at most one of the elements \(\pm g^o\) may have a negative character \(\chi(g^o)\) in the representation \(\rho_{24} \otimes \rho_{4096}\) of \(G_{x0}\); and in this case we return an element with \(\chi(g^o) \geq 0\). This leads to a disambiguation of \(\pm g\) in case \(\chi(g^o) \neq 0\).

We store the order of the computed element \(g\) in bits 15…,8 of the return value. We set bit 16 of the return value precisely if we could disambiguate \(g\) from \(-g\), i.e. in case \(\chi(g^o) \neq 0\). (Note that \(g^o = 1\) if \(g\) has odd order, implying \(\chi(g^o) > 0\).)

The function returns a negative value in case of failure.

Description of the mmgroup.mm_op extension

Module mmgroup.mm_op is implemented as an extension in the mmgroup package implemented in Cython. The main source file for that extension is mm_op.pyx in directory src.mmgroup.dev.mm_op. Each documented .c in this module function has been wrapped by a Cython function with the same name and the same signature.

Module mmgroup.mm_op implements the 196884-dimensional rational representation \(\rho_p\) of the monster group modulo several fixed odd moduli \(p = 2^k-1\).

Representation of a vector in \(\rho_p\)

We describe the representations of a vector in \(\rho_p\)

The most important representation of a vector in \(\rho_p\) is the internal representation. All operations of the monster group are performed on vectors in in \(\rho_p\) in internal representation.

In the internal representation a vector is stored as an array of 247488 entries, where each entry is a bit field representing an integer modulo p. Some components of a vector are stored twice in that array and some entries of the array are unused. This special structure facilitates the implementation of the operations of the monster group.

The entries of a vector are organized as tuples (tag, i0, i1). Here indices i0, i1 refer to a two-dimensional array as indicated in column Size of the following table. As usual in C, entries with adjacent last index i1 are stored in adjacent locations. For a mathematical description of an entry (tag, i0, i1), see section The Representation of the Monster Group in the API reference.

Array sizes for tags in the internal representation

Tag

Size

Space used

Remarks

Offset

A

24 x 24

24 x 32

(1), (2)

0

B

24 x 24

24 x 32

(1), (2), (3)

768

C

24 x 24

24 x 32

(1), (2), (3)

1536

T

759 x 64

759 x 64

(4),

2304

X

2048 x 24

2048 x 32

(1),

50880

Z

2048 x 24

2048 x 32

(1),

116416

Y

2048 x 24

2048 x 32

(1),

181952

Remarks

  1. As indicated in column Space used, an array of size n times 24 is stored in a space reserved for an array of size n times 32. So the entry with index (tag, i0, i1) is stored at location Offset(tag) + 32 * i0 + i1. Unused entries must be equal to zero.

  2. The entry given by (tag, i0, i1) must be equal to the entry given by (tag, i1, i0).

  3. The diagonal entry given by (tag, i0, i0) must be equal to zero.

  4. The entry with index (T, i0, i1) is stored at location Offset(T) + 64 * i0 + i1.

The entries of a vector in internal representation are stored a one-dimensional array of integers of type uint_mmv_t. Here type uint_mmv_t may be one of the C integer types uint64_t or uint32_t, depending on the value INT_BITS. At present INT_BITS is set to the value 64. Several adjacent entries of a vector are stored as bit fields in a single integer of type uint_mmv_t. Entries with a lower index are stored at bits with lower valence.

The number of bits in a bit field is always a power of two. So e.g. for p = 3 we use 2 bits; for p = 7 we use 4 bits with the highest bit unused. In case p = 2**k - 1, legal values for an entry are 0,...,2**k - 1, with 2**k - 1 equal to 0. Thus negation of a value can be done by complementing all k bits of that value. Apart from negation, the matrices corresponding to operations of the monster may add, subtract and half the entries of a vector (modulo p). These operations can easily be done on several entries simultaneously by manipulating a just single integer of type uint_mmv_t.

As indicated above, we reserve 32 entries for arrays of integers modulo p with 24 entries. So we require about 25.7% more memory than necessary. In some cases we need this extra memory anyway. E.g. for p = 3 a 64-bit integer may store 32 entries, so that there will always be a slack of 8 entries when storing 24 entries.

Function mm_aux_mmv_size(p) returns the number of integers of type uint_mmv_t required for storing a vector in external representation.

When writing or calculating entries with tags A, B, C then the high-level function for manipulating vectors in internal representation make sure that e.g. entries with indices (A, i0, i1) and (A, i1, i0) are always set to the same value. These functions also make sure that unused bits in a bit field and unused bit fields are set to zero. Note that a bit field with value zero may contain the value p instead.

The external representation of a vector in \(\rho_p\)

There is also a so-called external representation of a vector in R_p. This is used to facilitate the access to vectors by external modules. Here the vector is represented as an array of 196884 integers of type uint8_t. Basis vectors are ordered similar to the ordering for the internal representation, but here the entries are in one-to-one correspondence with the basis vectors. In the external representation there are no unused or duplicated entries.

More precisely, the order of the entries is:

Order of entries in external representation

Entries

Condition

No of entries

Offset

(A, i0, i1)

i0 = i1

24

0

(A, i0, i1)

i0 > i1

276

24

(B, i0, i1)

i0 > i1

276

300

(C, i0, i1)

i0 > i1

276

576

(T, i0, i1)

759*64

852

(X, i0, i1)

2048*24

49428

(Z, i0, i1)

2048*24

98580

(Y, i0, i1)

2048*24

147732

Indices (tag, i,j) for tag = A, B, C, i > j are ordered as follows:

  • (1,0),

  • (2,0), (2,1),

  • (3,0), (3,1), (3,2),

  • ...

  • (i,0), (i,1), ..., (i,i-1),

  • ...

  • (24,0), (24,1), ..., (24,23).

Function mm_aux_bytes_to_mmv() converts a vector from external to internal representation, Function mm_aux_mmv_to_bytes() does the inverse conversion.

The sparse representation of a vector in \(\rho_p\)

The Python interface to vectors in \(\rho_p\) is optimized for readability and not for speed. Here a typical task is to read and modify single entries of a vector. In the internal representation the coordinates of a vector are indexed by tuples containing a string. Transferring tuples or strings from Python to C is awful. Here we need a representation of a vector in \(\rho_p\) where a single entry of a vector can be stored in an integer variable.

A vector in \(\rho_p\) can be stored in the sparse representation. Here a vector is stored as an array of 32-bit integers, where each entry stands for a multiple of a basis vector. A component of a vector is stored in the bit fields of an integer as a tuple (tag, i0, i1, value). Here the tuple (tag, i0, i1) is as in the external representation, and value is the value of the coordinate of the vector corresponding to (tag, i0, i1). Entries with coordinate zero may be dropped. A 32-bit integer encodes a tuple (tag, i0, i1, value) in bit fields as shown in the following table.

Bit fields in the sparse representation

Bits

Meaning

27..25

Tag: A = 1, B = 2, C = 3, T = 4, X = 5, Z = 6, Y = 7

24..15

Index i0

13.. 8

Index i1

7.. 0

The value of the coordinate of the basis vector; if the modulus p is 2**k - 1 then only the lowest k bits are evaluated.

In a C function the length of a sparse representation of a vector must be given as a parameter to the function. The order of the entries is irrelevant in the sparse representation. A sparse representation generated by a C function contains at most one entry for each tuple (tag, i0, i1). On input, entries with several equal tuples (tag, i0, i1) are accepted. Unless stated otherwise, the corresponding values of such equal tuples are added.

In an entry with tag A, B, or C generated by this module we always have i0 >= i1. The value of an entry generated by this module is always less than the modulus p.

When reading an entry, a value with 0 <= value <= p is accepted. Entries with tag A, B, or C and i < j are also accepted. Illegal tags or indices are usually ignored on input.

Header file mm_basics.h

The header file mm_basics.h contains basic definitions for dealing with vectors of the 198884-dimensional representation of the monster group, as described in The C interface of the mmgroup project, section Description of the mmgroup.mm extension.

It also contains prototypes for the C files in the mm extension. This extension comprises the files mm_aux.c, mm_crt.c, mm_group_word.c, mm_random.c, mm_tables.c, mm_tables_xi.c.

Defines

mm_aux_bad_p(p)

Return 0 if p is a good modulus and a nonzero value otherwise.

Typedefs

typedef uint64_t uint_mmv_t

Used for the representation of the monster group.

Internally, a vector in the 196884-dimensional representation of the monster is stored as an array of integers of type uint_mmv_t. Here several entries are stored in such an integer. See enum MM_AUX_OFS_type for more details.

Enums

enum MM_AUX_OFS

This enumeration contains the offsets for the tags A,B,C,T,X,Z,Y in a vector in the 196884-dimensional representation of the monster, stored in the internal representation.

Such an offset counts the number of entries starting at the beginning of th vector. Note that several entries of a vector are stored in a 64-bit integer. Also there may be duplicate or unused entries in a vector, in order to speed up the operation of the monster group on a vector.

Values:

enumerator MM_AUX_OFS_A

Offset for tag A

enumerator MM_AUX_OFS_B

Offset for tag B

enumerator MM_AUX_OFS_C

Offset for tag C

enumerator MM_AUX_OFS_T

Offset for tag T

enumerator MM_AUX_OFS_X

Offset for tag X

enumerator MM_AUX_OFS_Z

Offset for tag Z

enumerator MM_AUX_OFS_Y

Offset for tag Y

enumerator MM_AUX_LEN_V

Total length of the internal representation

enum MM_AUX_XOFS

This enumeration contains the offsets for the tags A,B,C,T,X,Z,Y in a vector in the 196884-dimensional representation of the monster, stored in the external representation.

In external representation, a vector is stored as a contiguous array of bytes.

Values:

enumerator MM_AUX_XOFS_D

Offset for diagonal entries of tag A

enumerator MM_AUX_XOFS_A

Offset for tag A

enumerator MM_AUX_XOFS_B

Offset for tag B

enumerator MM_AUX_XOFS_C

Offset for tag C

enumerator MM_AUX_XOFS_T

Offset for tag T

enumerator MM_AUX_XOFS_X

Offset for tag X

enumerator MM_AUX_XOFS_Z

Offset for tag Z

enumerator MM_AUX_XOFS_Y

Offset for tag Y

enumerator MM_AUX_XLEN_V

Total length of the external representation

enum MM_SPACE_TAG

This enumeration defines the values of the tags A,B,C,T,X,Z,Y in a vector in the 196884-dimensional representation of the monster, stored in the sparse representation.

In the sparse representation an entry of a vector is stored as a tuple of bit fields (tag, par1, par2, value) inside an integer of type uint32_t as follows:

Bits 27,...,25:  tag (as indicated below)

Bits 24,...,14:  par1 (an integer of up to 11 bits)

Bits 13,..., 8:  par2 (an integer of up to 6 bits)

Bits  7,..., 0:  value (Reserved for the value of an entry)

Values:

enumerator MM_SPACE_TAG_A

Encodes tag A

enumerator MM_SPACE_TAG_B

Encodes tag B

enumerator MM_SPACE_TAG_C

Encodes tag C

enumerator MM_SPACE_TAG_T

Encodes tag T

enumerator MM_SPACE_TAG_X

Encodes tag X

enumerator MM_SPACE_TAG_Z

Encodes tag Z

enumerator MM_SPACE_TAG_Y

Encodes tag Y

struct mm_sub_op_pi64_type
#include “mm_basics.h”

Auxiliary structure for the structure mm_sub_op_pi_type

An array of type mm_sub_op_pi64_type[759] encodes the operation of \(x_\epsilon x_\pi\) on the representation of the monster group for entries with tag T. Assume that entry (T, i, j) is mapped to entry +-(T, i1, j1). Then i1 depends on i only, and j1 depends on i and j. For fixed i the mapping j -> j1 is linear if we consider the binary numbers j and j1 as bit vectors.

Entry i1 of the array of type mm_sub_op_pi64_type[759] describes the preimage of (T, i1, j1) for all 0 <= j1 < 64 as documented in the description of the members preimage and perm.

Note that the values 1, 3, 7, 15, 31, 63 occur as differences j1 ^ (j1 - 1) when counting j1 from 0 up to 63. So the preimage of (T, i1, j1) can be computed from the preimage of (T, i1, j1 - 1) using linearity and the approprate entry in member perm.

We remark that in case of an odd value epsilon the mapping for tag T requires a postprocessing step that cannot be derived from the infomration in this structure. Then entry (T, i, j) has to be negated if the bit weight of the subset of octade i corresponding to index j has bit weight 2 modulo 4.

In the sequel we describe the meaning of entry i1 an an array of elements of type mm_sub_op_pi64_type.

Public Members

uint16_t preimage

Bits 9…0 : preimage i such that (T, i, .) maps to +-(T, i1, .)

Bit 12: sign bit: (T, i, .) maps to -(T, i1, .) if bit 12 is set

uint8_t perm[6]

Member perm[k] is a value vsuch that (T, i, v) maps to +-(T, i1, 2 * 2**k - 1)

struct mm_sub_op_pi_type
#include “mm_basics.h”

Structure used for preparing an operation \(x_\epsilon x_\pi\).

Function mm_sub_prep_pi computes some tables required for the operation of \(x_\epsilon x_\pi\) on the representation of the monster group, and stores these tables in a structure of type mm_sub_op_pi_type.

The structure of type mm_sub_op_pi_type has the following members:

Public Members

uint32_t eps

A 12-bit integer describing an element \(\epsilon\) of the Golay cocode.

uint32_t pi

An integer describing the element \(\pi\) of the Mathieu group \(M_{24}\) as in module mat24_functions.c.

uint8_t perm[24]

The permutation 0...23 -> 0...23 given by the element \(\pi\) of \(M_{24}\).

uint8_t inv_perm[24]

The inverse of the permutation perm.

uint32_t benes_net[9]

A representation of Benes network for computing permutationperm, as described in function mat24_perm_to_net in file mat24_functions.c.

uint16_t tbl_perm24_big[2048 + 72]

For tags A, B, C, X, Y, Z, an entry (tag, i, j) of the representation of the monster is mapped to entry (tag1, i1, j1), with i1 depending on i (and the tag), and j1 depending on j only.

If tbl_perm24_big[i1] & 0x7ff = i for 0 <= i1 < 2048 then (tag, i, j) ia mapped to (Tag, i1, perm[j]), up to sign, for tags X, Y and Z. In case of an odd \(\epsilon\), tags Y and Z have to be exchanged. The value tbl_perm24_big[2048 + 24*k + i1] & 0x7ff describes the preimage of (tag, i1, j1) in a similar way, where tag = A, B, C, for k = 0, 1, 2.

Bits 12,…,15 of tbl_perm24_big[i1] encode the signs of the preimages of the corresponding entry of the rep. Bits 12, 13, and 14 refer to the signs for the preimages for the tags X, Z and Y, respectively. Bit 15 refers to the signs for the preimages for tags A, B and C. If the corresponding bit is set, the preimage has to be negated.

Note that function mat24_op_all_autpl in module mat24_functions.c computesthe first 2048 entries of the table.

mm_sub_op_pi64_type *tbl_perm64

A description of the operation of \(x_\epsilon x_\pi\) on the entries with tag T, see structure mm_sub_op_pi64_type. Entry d of the Arrary refers to the octad o(d) with number d. It contains the followint information_

Bits 5,…,0: Associator \delta' = A(o(d), f)) encoded as a suboctad

Bits 11,…,6: Associator a = A(o(d), ef)) encoded as a suboctad.

Caution:

Pointer tbl_perm64 must be initialized with an array of type mm_sub_op_pi64_type a_tbl_perm64[759].

struct mm_sub_op_xy_type
#include “mm_basics.h”

Structure used for preparing an operation \(y_f x_e x_\epsilon\).

The operation of \(g = y_f x_e x_\epsilon\), (or, more precisely, of its inverse \(g^{-1}\)) on the representation of the monster group is described in section Implementing generators of the Monster group in the The mmgroup guide for developers.

Function mm_sub_prep_xy in file mm_tables.c collects the data required for this operation in a structure of type mm_sub_op_xy_type.

Public Members

uint32_t f

A 13-bit integer describing an element \(f\) of the Parker loop.

uint32_t e

A 13-bit integer describing an element \(e\) of the Parker loop.

uint32_t eps

A 12-bit integer describing an element \(\epsilon\) of the Golay cocode.

uint32_t f_i

Bit \(i\) of member f_i is the scalar product of \(f\) and the singleton cocode word \((i)\).

These bits are used for the operation of \(g^{-1}\) on entries with tag A.

uint32_t ef_i

Bit \(i\) of member ef_i is the scalar product of \(ef\) and the singleton cocode word \((i)\).

These bits are used for the operation of \(g^{-1}\) on entries with tags B, and C.

uint32_t lin_i[3]

Put \(g_0 = e\), \(g_1 = g_2 = f\). For k = 0, 1,2, the bit \(i\) of member lin_i[k] is the scalar product of \(g_k\) and the singleton cocode word \((i)\).

These bits are used for the operation of \(g^{-1}\) on entries with tags X, Z, and Y.

uint32_t lin_d[3]

Let U_k = X, Z, Y for k = 0, 1, 2. If the cocode element \(\epsilon\) is even then we put U'_k = U_k, otherwise we put U'_k = X, Y, Z for k = 0, 1, 2. The operation \(g^{-1}\) maps the vector with tag (U_k, d, i) to (-1)**s times the vector with tag (U'_k, d ^ lin[d], i). Here ** denotes exponentiation and we have

s = s(k, d, i) = (lin_i[k] >> i) + (sign_XYZ[d] >> k).

If k = 0 and \(\epsilon\) is odd then we have to correct s(k, d, i) by a term <d, i>.

uint8_t *sign_XYZ

Pointer sign_XYZ refers to an array of length 2048. This is used for calculations of signs as described above. Here we use the formula in section Implementing generators of the Monster group of the mmgroup guide for developers, dropping all terms depending on i.

uint16_t *s_T

Pointer s_T refers to an array of length 759. Entry d of this array refers to the octad o(d) with number d. The bits of entry d are interpreted as follows:

Bits 5,…,0: The asscociator delta' = A(o(d), f) encoded as a suboctad of octad o(d)).

Bits 13,…,8: The asscociator alpha = A(o(d), ef) encoded as a suboctad of octad o(d)). From his information we can compute the scalar product <ef, \delta> for each suboctad delta of o(d) as an intersection of tow suboctads. Here we assume that delta is represented as such a suboctad.

Bit 14: The sign bit s(d) = P(d) + P(de) + <d, eps>, where P(.) is the squaring map in the Parker loop.

Bit 15: Parity bit |eps| of the cocode word eps.

Then \(g^{-1}\) maps the vector with tag (T, d, delta) to (-1)**s' times the vector with tag (T, d, \delta ^ delta'). Here ** denotes exponentiation and we have

s' = s'(T, d, delta) = s(d) + <\alpha, \delta> + |delta| * |eps| / 2.

Here the product <\alpha, \delta> must be computed as the bit length of an intersection of two suboctads.

Header file mm_op_p.h

C interface for file mm_index.c

File mm_index.c provides the basic functions for converting an index of a vector of the 196884-diemnsional representation of the monster between internal, external, and sparse notation.

Functions

uint32_t mm_aux_index_extern_to_sparse(uint32_t i)

Convert an index from external to sparse representation.

The function converts an index i for the external representation of a vector to an index for the sparse representation of a vector and returns the converted index. The function returns 0 in case i >= 196884`.

Indices for the sparse representation are defined as in enum MM_SPACE_TAG in file mm_basics.h.

void mm_aux_array_extern_to_sparse(uint32_t *a, uint32_t len)

Convert index array from external to sparse representation.

The function converts an array a of indices for the external representation to an array of indices for the sparse representation of a vector. All indices in the array a of length len are converted in place, using function mm_aux_index_extern_to_sparse.

int32_t mm_aux_index_sparse_to_extern(uint32_t i)

Convert an index from sparse to external representation.

The function converts an index i for the sparse representation of a vector to an index for the external representation of a vector and returns the converted index. The function returns -1 if the input i denotes an illegal index. The coordinate value encoded in the input i is ignored.

Indices for the sparse representation are defined as in enum MM_SPACE_TAG in file mm_basics.h.

int32_t mm_aux_index_sparse_to_leech(uint32_t i, int32_t *v)

Convert sparse index to a short vector in the Leech lattice.

The function converts an index i for the sparse representation of a vector to a vector v in the Leech lattice. This conversion is successful if i denotes a legal index for one of the tags tags B, C, T, X. Then the function computes a short Leech lattice vector (scaled to norm 32) in the array v. Output v is determined up to sign only; that sign is implementation dependent.

The function returns 0 in case of a successful conversion and -1 in case of failure.

uint32_t mm_aux_index_sparse_to_leech2(uint32_t i)

Convert sparse index to a short vector in the Leech lattice mod 2.

The function converts an index i for the sparse representation of a vector to a vector v in the Leech lattice mod 2. This conversion is successful if i denotes a legal index for one of the tags tags B, C, T, X. The function returns a short Leech lattice vector modulo 2, encoded in Leech lattice encoding, as described in section Description of the mmgroup.generators extension.

The function returns 0 in case of failure.

uint32_t mm_aux_index_leech2_to_sparse(uint32_t v2)

Convert short vector in the Leech lattice mod 2 to sparse rep.

The function converts an value v2 representing a vector in the Leech lattice mod 2 to a sparse index and returns that sparse index. It returns 0 if v2 is not a short Leech lattice vector.

uint32_t mm_aux_index_intern_to_sparse(uint32_t i)

Convert an index from internal to sparse representation.

The function converts an index i for the internal representation of a vector to an index for the sparse representation of a vector and returns the converted index. The function returns 0 in case of a bad index.

Indices for the sparse representation are defined as in enum MM_SPACE_TAG in file mm_basics.h.

int32_t mm_aux_index_sparse_to_intern(uint32_t i)

Convert an index from sparse to internal representation.

The function converts an index i for the sparse representation of a vector to an index for the internal representation of a vector and returns the converted index. The function returns -1 if the input i denotes an illegal index. The coordinate value encoded in the input i is ignored.

Indices for the sparse representation are defined as in enum MM_SPACE_TAG in file mm_basics.h.

int32_t mm_aux_index_extern_to_intern(uint32_t i)

Convert an index from external to internal representation.

The function converts an index i for the external representation of a vector to an index for the internal representation of a vector and returns the converted index. The function returns -1 in case i >= 196884.

int32_t mm_aux_index_check_intern(uint32_t i)

Check an index in internal representation.

The function checks an index i in the internal representation of a vector. Some entries of the vectors are stored at two different locations, e.g entries A[i,j], B[i,j], C[i,j] for i != j.

The function returns the other location of the same entry (as an index in internal representation) if there is any. It returns 0 if that entry is stored at exactly one location, and -1 if index i is illegal.

C interface for file mm_aux.c

File mm_aux.c provides the basic functions for dealing with the representations of the monster group modulo various small integers p = 2**n-1, 2 <= n <= 8. Here the integer p is called the modulus.

Especially, we deal with vectors in such a representation as described in The C interface of the mmgroup project, section Description of the mmgroup.mm extension.

For such a vector there is an internal representation, an external representation, and also a sparse representation, as described in the documentation mentioned above.

The functions in this file provide access to the internal representation of such a vector. The also support the conversion between the different representations of a vector.

Usually, the order of the parameters of functions in this file is:

  1. Modulus p, if present

  2. The input value or the input data array

  3. Any parameters that do not affect the positions in the output array

  4. The output data array

  5. Parameters (e.g. lengths, indices) that affect the positions of the 
     data being modified in the output array

Among others, functions in this file use functions from file mm_index.c for converting indices of vectors to different representations.

A vector modulo p is organized in rows of 32 entries. In many rows only 24 of the 32 bits are used; but in some all 32 bit are used. Dtails are given in the API reference of the project.

Functions

uint8_t mm_aux_get_mmv(uint32_t p, uint_mmv_t *mv, uint32_t i)

Read entry at index from vector in internal representation.

The function returns the entry with index i of the vector mv with modulus p. The return value is reduced modulo p. Index i must be given in internal representation. The function returns garbage in case of an illegal index.

void mm_aux_put_mmv(uint32_t p, uint8_t value, uint_mmv_t *mv, uint32_t i)

Write entry to a vector at an index in internal representation.

The function sets the entry of the vector mv with modulus p at the index i to the given value. 0 <= value <= p must hold.

Here the index i must be given in internal representation. Writing at an illegal index performs no action.

void mm_aux_add_mmv(uint32_t p, uint8_t value, uint_mmv_t *mv, uint32_t i)

Add to entry of a vector at an index in internal representation.

The function adds the given value to the entry of the vector mv with modulus p at the index i. Here 0 <= value <= p must hold.

The index i must be given in internal representation. Writing at an illegal index performs no action.

void mm_aux_read_mmv32(uint32_t p, uint_mmv_t *mv, uint32_t i, uint8_t *b, uint32_t len)

Read entries from vector in internal representation.

Read entries of vector mv (stored in internal representation with modulus p) and store these entries in the array b. Here len is the number of rows to be read starting at row i. Each row consists of 32 entries.

Here p must be a legal modulus.

void mm_aux_write_mmv32(uint32_t p, uint8_t *b, uint_mmv_t *mv, uint32_t i, uint32_t len)

Write data to a vector in internal representation.

Write data from the array b to the vector mv (stored in internal representation with modulus p). Here len is the number of rows to be read starting at row i. Each row consists of 32 entries.

Here p must be a legal modulus.

void mm_aux_read_mmv24(uint32_t p, uint_mmv_t *mv, uint32_t i, uint8_t *b, uint32_t len)

Read rows of length 24 from vector in internal representation.

Read entries of vector mv with modulus p and store these entries in the array b, , starting at row i. Here mv is a vector of rows of 24 entries, with 8 entries slack after each row. len is the number of such rows to be read. So altogether 24 * len entries are read from mv and written to array b; the 8 bytes slack after each 24-byte row are dropped. Vector b is reduced modulo p.

Here p must be a legal modulus.

void mm_aux_write_mmv24(uint32_t p, uint8_t *b, uint_mmv_t *mv, uint32_t i, uint32_t len)

Write rows of length 24 to vector in internal representation.

Write data from the array b to the vector mv with modulus p. We take 24 * len bytes from the array b and write them to the vector mv, starting at row i. Here mv is considered as a vector of rows of 24 entries, with 8 entries slack after each row; so len is the number of such 24-byte rows to be written. The entries in the slack after each row written to mv are set to zero.

Here p must be a legal modulus.

uint32_t mm_aux_mmv_size(uint32_t p)

Return the size of a vector in internal representation.

The function returns the number of integers of type uint_mmv_t required to store a vector of the representation \(\rho_p\) (in internal representation) with modulus p.

The function returns 0 if p is illegal modulus.

uint32_t mm_aux_int_fields(uint32_t p)

Return number of entries stored in integer of type uint_mmv_t

The function returns the number of entries of a vector of the representation \(\rho_p\) (in internal representation) that can be stored in an integer of type uint_mmv_t.

The function returns 0 if p is illegal modulus.

uint32_t mm_aux_v24_ints(uint32_t p)

Return number of integers of type uint_mmv_t to store 24 entries.

The function returns the number of integers of type uint_mmv_t required to store a part of 24 entries of a vector of the representation \(\rho_p\) (in internal representation) with modulus p. Such parts of 24 entries arise naturally in the construction of \(\rho_p\).

The function returns 0 if p is illegal modulus.

void mm_aux_zero_mmv(uint32_t p, uint_mmv_t *mv)

Zero a vector in internal representation.

The function sets all entries of the vector mv with modulus p in internal representation to zero.

void mm_aux_random_mmv(uint32_t p, uint_mmv_t *mv, uint64_t *seed)

Randomize a vector in internal representation.

The function randomizes all entries of the vector mv with modulus p in internal representation uniformly using the internal random generator in file gen_random.c. Parameter seed must be a seed for a random generator as described in file gen_random.c.

int32_t mm_aux_reduce_mmv(uint32_t p, uint_mmv_t *mv)

Reduce a vector in internal representation.

The function reduces all entries of the vector mv with modulus p in internal representation to a standard form, so that equal vectors are represented by equal arrays of integers.

Note that a zero entry in such a vector can be represented either by the bit string 0...0 or by 1...1. This functions sets all zero entries of the vector to 0...0.

The function returns 0 if it detects no error. It may return the following error codes:

-1: Bad modulus p

-2: A one bit outside a valid bit field for an entry has been found

int32_t mm_aux_reduce_mmv_fields(uint32_t p, uint_mmv_t *mv, uint32_t nfields)

Auxiliary function of function mm_aux_reduce_mmv

The function performs the same operation as function mm_aux_reduce_mmv. But instead of all entries of the vector mv, it reduces the first len entries only.

int32_t mm_aux_check_mmv(uint32_t p, uint_mmv_t *mv)

Check a vector in internal representation for errors.

The function checks all entries of the vector mv with modulus p in internal representation for errors. It returns 0 if it detects no error. It may return the following error codes:

-1: Bad modulus p

-2: A one bit outside a valid bit field for an entry has been found

-3: A subfield of 24 entries has an illegal nonzero entry at index >= 24

-4: The vector has an illegal nonzero diagonal entry

-5: The symmetric part of the vector is not actually symmetric

As a side effect, mv is reduced with function mm_aux_reduce_mmv.

void mm_aux_small24_expand(uint8_t *b_src, uint8_t *b_dest)

Convert part of vector from external to internal representation.

Conversion between the internal and the external representation of a vector is straightforward, except for entries with tags A, B, C. The entries with these tags are stored in the first 852 entries of the external representation. In the internal representation the entries with these tags are spread over three symmetric 24 times 24 times matrices.

This function maps the 852 entries of the array b_src (corresponding to tags A, B, C) to the array b_dest of size 3 * 24 * 24 (corresponding to three symmetric 24 times 24 times matrices). Function mm_aux_write_mmv24 can be used to write the data from the array b_dest to the initial segment of the internal representation of a vector.

void mm_aux_small24_compress(uint8_t *b_src, uint8_t *b_dest)

Convert part of vector from internal to external representation.

Conversion between the internal and the external representation of a vector is straightforward, except for entries with tags A, B, C. The entries with these tags are stored in the first 852 entries of the external representation. In the internal representation the entries with these tags are spread over three symmetric 24 times 24 times matrices.

This function maps the 3 * 24 * 24 entries of the array b_src (corresponding to three symmetric 24 times 24 times matrices) to the 852 entries of the array b_dest (corresponding to tags A, B, C in external representation).

This reverses the effect of function mm_aux_small24_expand. Function mm_aux_read_mmv24 can be used to read the data from the initial segment of the internal representation of a vector to the array b_src, before calling this function.

void mm_aux_mmv_to_bytes(uint32_t p, uint_mmv_t *mv, uint8_t *b)

Convert vector from internal to external representation.

Read all entries of vector mv (stored in internal representation with modulus p) and store these entries in the array b in external representation.

Output vector b is reduced modulo p. It must have length 196884.

void mm_aux_bytes_to_mmv(uint32_t p, uint8_t *b, uint_mmv_t *mv)

Convert vector from external to internal representation.

Read all entries of the array `b (of length 196884, containing a vector in external representation) and store these entries the vector mv. Here mv is a vector stored in internal representation with modulus p.

Any entry x in the array b must satisfy 0 <= x <= p. The vector mv is an array of n integers of type uint_mmv_t with n = mm_aux_mmv_size(p).

int32_t mm_aux_mmv_to_sparse(uint32_t p, uint_mmv_t *mv, uint32_t *sp)

Convert vector from internal to sparse representation.

Read all entries of vector mv (stored in internal representation with modulus p) and store these entries in the array sp in sparse representation. Each entry in the array sp represents a nonzero entry of the vector. The function returns the length of the output array sp or an negative value in case of error. Negative return values are as in function check_mmv_buffer.

Output vector sp is reduced modulo p. The buffer for array sp must have length 196884. Input vector mv is checked with function check_mmv_buffer.

void mm_aux_mmv_extract_sparse(uint32_t p, uint_mmv_t *mv, uint32_t *sp, uint32_t length)

Extract entries from a vector in internal representation.

The function extracts certain entries from the vector mv depending on the vector sp. Here mv is a vector stored in internal representation with modulus p. Vector sp is a vector of length length in sparse representation.

The entries of vector sp are updated with the corresponding entries of mv. If sp has an entry with a certain label then the coordinate of that entry is set to the corresponding coordinate of vector mv. If several entries of sp have the same label then the same coordinate is taken from mv several times.

Bit 7,…,0 of any entry of sp should be either 0 or p. If that value is 0 then the coordinate is read to bits 7,…,0 of that entry. If that entry is p then the negative coordinate is read instead. Other values of these bits are strongly discouraged; but technically we XOR the corresponding coordinate of vector mv to these bits; and we then change a result p to zero. There is a special case where this detail is relevant.

uint32_t mm_aux_mmv_get_sparse(uint32_t p, uint_mmv_t *mv, uint32_t sp)

Extract one entry of a vector in internal representation.

The statement uint32_t sp1 = mm_aux_mmv_get_sparse(p, mv, sp); is equivalent to

  uint32_t sp1 = sp; mm_aux_mmv_extract_sparse(p, mv, &sp1, 1);

void mm_aux_mmv_add_sparse(uint32_t p, uint32_t *sp, uint32_t length, uint_mmv_t *mv)

Add vector in sparse rep to vector in internal representation.

The function adds a vector sp in sparse representation to a vector mv in internal representation with modulus p. Vector sp has length length, and each value x in an entry of vector sp must satisfy 0 <= x <= p. Different entries in sp with the same index are added up.

void mm_aux_mmv_set_sparse(uint32_t p, uint_mmv_t *mv, uint32_t *sp, uint32_t length)

Set certain entries of a vector in internal representation.

The function sets certain entries of the vector mv depending on the vector sp. Vector mv is given in internal representation with modulus p. Vector sp is given in sparse representation and has length length.

If sp has an entry with a certain label then the corresponding entry of mv is set to to the value coded in that entry of sp. Each of these values x must satisfy 0 <= x <= p. Duplicate entries in sp with the same label and different values are illegal; in that case the value of mv is undefined.

int32_t mm_aux_mmv_extract_sparse_signs(uint32_t p, uint_mmv_t *mv, uint32_t *sp, uint32_t n)

Extract signs of a vector in internal representation.

The function extracts the signs of certain entries of the vector mv depending on the vector sp. Vector mv is given in internal representation with modulus p. Vector sp is given in sparse representation and has length n.

Entry sp[i] specifies a multiple c[i] * u[i] of a unit vector u[i]. Let m[i] be the coordinate of vector mv with respect to the unit vector u[i]. We put s[i] = 0 if m[i] = c[i] and s[i] = 1 if m[i] = -c[i]. In all other cases we assign a random value 0 or 1 to s[i]. Then the function returns the sum of the values s[i] << i, where i ranges from 0 to n - 1.

int32_t mm_aux_mmv_extract_x_signs(uint32_t p, uint_mmv_t *mv, uint64_t *elem, uint32_t *a, uint32_t n)

Extract some bits of a vector in internal representation.

The function extracts the least significant bits of certain entries of the vector mv depending on the vector a. Vector mv is given in internal representation of the Monster with modulus p.

Vector a is a an array of n elements of the group Q_x0 of structure \(2^{1+24}\), with each element given in Leech lattice encoding. Here each element of Q_x0 must correspond to a Leech lattice vector of type 2; otherwise the function fails.

The array elem represents an element of the group G_x0 of structure \(2^{1+24}.\mbox{Co}_1\), given in G_x0 representation. Internally, the function transforms (i.e. conjugates) all elements of Q_x0 in the array a with the element elem, i.e. it calculates the element a'[i] = elem^-1 * a[i] * elem of Q_x0. Then it extracts the least significant bit b[i] of the entry of the vector mv with the coordinate labelled by a'[i].

Note that negating a'[i] corresponds to negating the coordinate with label a'[i] in the vector mv. Hence when negating a'[i] we also have to flip the bit b[i]. The function fails in case a'[i] = 0.

The function returns the sum of the values b[i] << i, where i ranges from 0 to n - 1. The function also fails in case i > 31. It returns a negative value in case of failure.

Assume that a vector mv' = mv * q * g is given with a known vector mv, a known g in G_x0, and an unknown q in Q_x0. Then the main use case of this function is to find the element q (up to sign) without modifying mv'.

int32_t mm_aux_mul_sparse(uint32_t p, uint32_t *sp, uint32_t length, int64_t f, uint32_t p1, uint32_t *sp1)

Scalar multiplication and modular reduction in sparse representation.

The function multiplies a vector sp in sparse representation with a factor f and reduces the result modulo a number p1.

The vector sp has length length and is stored in sparse representation as a vector modulo an odd number 2 < p < 256. The result is reduced modulo the number p1 and stored in sparse representation in the array sp1.

The number p1 must be odd and satisfy 2 < p < 256. In case f != 0 the number p1 must divide p * abs(f).

The function returns the length of the array sp1 in case of success and -1 in case of failure.

The two arrays sp and sp1 may be non overlapping or equal.

int32_t mm_aux_get_mmv_leech2(uint32_t p, uint_mmv_t *mv, uint32_t v2)

Read entry from vector in internal rep indexed by Leech lattice.

The function returns the entry with index i of the vector mv with modulus p. Here i must be an index referring to a vector of type 2 in the Leech lattice modulo 2 in Leech lattice encoding. The sign bit 24 of i is evaluated as expected. The return value is reduced modulo p.

The function returns a negative value if i is not a vector of type 2 in the Leech lattice modulo 2 or p is not a legal modulus.

uint64_t mm_aux_hash(uint32_t p, uint_mmv_t *mv)

Compute hash value of vector in internal.

The function returns a hash value of the vector mv with modulus p. It also tries to distinguish between different sparse vectors. Therefore it tries to hash over about 100 nonzero integers of type uint_mmv_t. So if mv is sparse then the function might have to scan considerably more zero entries.

C interface for file mm_tables.c

File mm_tables.c contains functions for supporting the operations \(x_\epsilon x_\pi\) and \(y_f x_e x_\epsilon\) on the vectors of the 198884-dimesional represention \(\rho_p\) modulo a small number \(p\). These operations are implemented in separate packages for the differnet values of \(p\). But there are commom preprocessing steps required for all values \(p\). These common preprocessing steps are implemented here.

The monomial operations \(x_\epsilon, x_\pi, y_f, x_e\) are as defined in the API Reference, section The monster group. Here \(\epsilon\) is an element of the Golay cocode represented as a 12-bit integer. \(f\) and \(e\) are elements of the Parker loop represented as 13-bit integers. \(\pi\) is a automorphism of the Parker loop. In the API Reference, section Automorphisms of the Parker loop we number a certain set of these automorphisms in the same way as the elements of the Mathieu group \(M_{24}\). We denote such an automorphism \(\pi\) by its number.

Functions

void mm_sub_prep_pi(uint32_t eps, uint32_t pi, mm_sub_op_pi_type *p_op)

Compute information for operation \(x_\epsilon x_\pi\).

Given an element \(x_\epsilon x_\pi\) of the monster by parameters eps and pi, the function computes the relevant data for performing that operation on the representation of the monster. These data are stored in the structure of type mm_sub_op_pi_type referred by parameter p_op.

Caution:

Component p_op->tbl_perm64 must be initialized with an array of type mm_sub_op_pi64_type a_tbl_perm64[759] befor aclling this function!!!.

int32_t mm_sub_test_prep_pi_64(uint32_t eps, uint32_t pi, uint32_t *p_tbl)

For internal use only!

This is an auxiliary function for testing function mm_sub_prep_pi.

Given eps and pi as in function mm_sub_prep_pi, this function executes function mm_sub_prep_p(eps, pi, p_op) and stores the output p_op->tbl_perm64 (as an array of length 759 * (1 + 6)) in the array referred by p_tbl.

void mm_sub_prep_xy(uint32_t f, uint32_t e, uint32_t eps, mm_sub_op_xy_type *p_op)

Compute information for operation \(y_f x_e x_\epsilon\).

Given an element \(y_f x_e x_\epsilon\) of the monster by parameters f, e, and eps, the function computes the relevant data for performing that operation on the representation of the monster. These data are stored in the structure of type mm_sub_op_xy_type referred by parameter p_op.

Caution!

Component p_op->sign_XYZ must either be NULL or refer to an array of type uint8_t of length 2048. Component p_op->s_T must either be NULL or refer to an array of type uint8_t of length 759.

C interface for file mm_op_p_vector.c

File mm_op_p_vector.c implements the operation of the Monster group on a vector in the representation \(\rho_p\) of the Monster modulo \(p\).

The representation \(\rho_p\) is equal to the 196884-dimensional representation \(\rho\) of the monster, with coefficients taken modulo \(p\), as defined in section The representation of the monster group in the API reference.

Unless otherwise stated, the first parameter of a function in this module is the modulus \(p\), and that modulus must be one of the values 3, 7, 15, 31, 127, or 255.

An element of \(\rho_p\) is implemented as an array of integers of type uint_mmv_t as described in section Description of the mmgroup.mm extension in this document.

The number of entries of a vector of type uint_mmv_t[] for modulus \(p\) is equal to mm_aux_mmv_size(p).

Functions

int32_t mm_op_copy(uint32_t p, uint_mmv_t *mv1, uint_mmv_t *mv2)

Copy vector mv1 in \(\rho_p\) to mv2

Here modulus p must be one of the values 3, 7, 15, 31, 127, or 255.

int32_t mm_op_compare_len(uint32_t p, uint_mmv_t *mv1, uint_mmv_t *mv2, uint32_t len)

Compare arrays mv1 and mv2 of integers.

The function compares parts of the two vectors mv1 and mv2of the representation \(\rho_p\).

Here the function compares len integers of type uint_mmv_t starting at the pointers mv1 and mv2. These integers are interpreted as arrays of bit fields containing integers modulo p.

The function returns 0 in case of equality and 1 otherwise.

Here modulus p must be one of the values 3, 7, 15, 31, 127, or 255.

int32_t mm_op_compare(uint32_t p, uint_mmv_t *mv1, uint_mmv_t *mv2)

Compare vectors mv1 and mv2 of \(\rho_p\).

The function compares two vectors mv1 and mv2 of the representation \(\rho_p\).

It returns 0 in case of equality and 1 otherwise.

Here modulus p must be one of the values 3, 7, 15, 31, 127, or 255.

int32_t mm_op_checkzero(uint32_t p, uint_mmv_t *mv)

Check if a vector mv in \(\rho_p\) is zero.

The function checks it the vector mv in the representation \(\rho_p\) is zero.

It returns 0 in case mv == 0 and 1 otherwise. It is optimized for the case that mv is expected to be zero.

Here modulus p must be one of the values 3, 7, 15, 31, 127, or 255.

int32_t mm_op_vector_add(uint32_t p, uint_mmv_t *mv1, uint_mmv_t *mv2)

Add vectors mv1 and mv2 of \(\rho_p\).

The function adds the two vectors mv1 and mv2 of the representation \(\rho_p\) and stores the result in the vector mv1.

Here modulus p must be one of the values 3, 7, 15, 31, 127, or 255.

int32_t mm_op_scalar_mul(uint32_t p, int32_t factor, uint_mmv_t *mv1)

Multiply vector mv1 of \(\rho_p\) with scalar.

The function multiplies the vector mv1 of the representation \(\rho_p\) and with the (signed) integer factor and stores the result in the vector mv1.

Here modulus p must be one of the values 3, 7, 15, 31, 127, or 255.

int32_t mm_op_compare_mod_q(uint32_t p, uint_mmv_t *mv1, uint_mmv_t *mv2, uint32_t q)

Compare two vectors of \(\rho_p\) modulo \(q\).

The function compares two vectors mv1 and mv2 of the representation \(\rho_p\) modulo a number \(q\). Here \(q\) should divide \(p\).

It returns 0 in case of equality, 1 in case of inequality, and 2 if \(q\) does not divide \(p\).

Here modulus p must be one of the values 3, 7, 15, 31, 127, or 255.

int32_t mm_op_store_axis(uint32_t p, uint32_t x, uint_mmv_t *mv)

Set a vector in \(\rho_p\) to an axis.

Let x be an element of the subgroup \(Q_{x0}\) if the Monster that maps to a short Leech lattice vector. Here x must be given in Leech lattice encoding as in the Description of the mmgroup.generators extension in the documentation of the C interface.

Then x corresponds to vector in \(\rho_p\) that is called a 2A axis. The function stores that 2A axis in mv.

Here modulus p must be one of the values 3, 7, 15, 31, 127, or 255.

int32_t mm_op_pi(uint32_t p, uint_mmv_t *v_in, uint32_t delta, uint32_t pi, uint_mmv_t *v_out)

Compute automophism of the Parker loop on a vector.

File mm_op_pi.c implements the operation of the generators \(x_\pi\) and \(x_\delta\) of the monster group on a vector in the representation \(\rho_p\) of the Monster modulo p. Here p must be 3, 7, 15, 31, 127, or 255.

Here generators \(x_\pi\) and \(x_\delta\) are defined as automorphisms of the Parker loop as in section The monster group of the API reference. An automophism of the Parker loop is specified by a pair of integers d, pi as in the constructor of the Python class AutPL, see section Automophisms of the Parker loop in the API reference.

The exact operation of an automorphism of the Parker loop on \(\rho\) is as defined in [Seysen19].

Note that the integers d, pi mentioned above describe the number of an element of the Golay cocode and the number of a permutation in the Mathieu group \(M_{24}\), respectively. Internally, we use the C functions in file mat24_functions.c and the function mm_sub_prep_pi in file mm_tables.c for converting the integers d, pi to mathematical objects that can be used for implementing the operation on \(\rho_p\). These conversions are very fast compared to the cost for the operation on \(\rho_p\). This helps us to keep the C interface for these operations simple.

Let v_in be a vector of the representation \(\rho_p\) of the monster group. Then the function computes this automorphism on the input vector v_in and stores the result in the output vector v_out. Input vector v_in is not changed.

Here modulus p must be one of the values 3, 7, 15, 31, 127, or 255.

int32_t mm_op_xy(uint32_t p, uint_mmv_t *v_in, uint32_t f, uint32_t e, uint32_t eps, uint_mmv_t *v_out)

Compute an operation of the monster group on a vector.

Let v_in be a vector of the representation \(\rho_p\) of the monster group.

The function implements the operation of the element \(y_f \cdot x_e \cdot x_\epsilon\) of the monster group on a vector v_in in the representation \(\rho_p\) of the monster.

The integers f and e occuring in the generators \(y_f\) and \(x_e\) encode elements of the Parker loop. The integer eps encodes the element \(\epsilon\) of the Golay cocode occuring in the generator \(x_\epsilon\), as indicated in the header of this file. The function computes this operation of the element of the monster (given by parameters f, e, eps) on the input vector v_in and stores the result in the output vector v_out.

Input vector v_in is not changed.

Here modulus p must be one of the values 3, 7, 15, 31, 127, or 255.

int32_t mm_op_omega(uint32_t p, uint_mmv_t *v, uint32_t d)

Compute an operation of the monster group on a vector.

Let v be a vector of the representation \(\rho_p\) of the monster group.

The function implements the operation of the element \(x_d\) of the monster group on a vector v in the representation \(\rho_p\) of the monster. Here d must be one of the integers 0, 0x800, 0x1000, or 0x1800, encoding the generators \(x_1, x_\Omega, x_{-1}\), or \(x_{-\Omega}\), respectively.

The function computes the operation \(x_d\) on the vector v and overwrites the vector v with the result. The function can be considered as a simplified (and much faster) version of function mm_op_xy.

Here modulus p must be one of the values 3, 7, 15, 31, 127, or 255.

int32_t mm_op_t_A(uint32_t p, uint_mmv_t *v_in, uint32_t e, uint_mmv_t *v_out)

Compute part A of operation of \(\tau^e\) on vector``.

Function mm_op_t_A computes a the operation of the monster group element \(\tau^e\) on a vector v_in and stores the A part of the result in a vector v_out. That operation depends on a parameter e. The other entries of vector v_out are not changed. See section The representation of the monster group in the API reference for tags of entries of a vector in the representation of the monster. Note that the entries of vector v_out with tag A also depend on entries of vector v_in with tags different from A.

Here modulus p must be one of the values 3, 7, 15, 31, 127, or 255.

int32_t mm_op_word(uint32_t p, uint_mmv_t *v, uint32_t *g, int32_t len_g, int32_t e, uint_mmv_t *work)

Compute operation of the monster group on a vector.

Let \(v\) be a vector of the representation \(\rho_p\) of the monster group stored in the array referred by v.

Let \(g\) be the element of the monster group stored in the array of length len_g referred by the pointer g.

Then the function computes the vector \(v \cdot g^e\) and overwrites the vector in the array v with that vector. Here \(e\) is the exponent given by the integer e.

The function requires a work buffer (referrd by work), which is an array of mm_aux_mmv_size(p) entries of type uint_mmv_t. So the work buffer has the same size as the vector v.

The function returns 0 in case of success and a nonzero value in case of failure.

Internally, the function simplifies all substrings of the string representing the word \(g^e\), except for atoms corresponding to nonzero powers of the generator \(\xi\). So the user need not ‘optimize’ the input \(g\). Of course, this simplification does not change the input array g.

Here modulus p must be one of the values 3, 7, 15, 31, 127, or 255.

int32_t mm_op_word_tag_A(uint32_t p, uint_mmv_t *v, uint32_t *g, int32_t len_g, int32_t e)

Restriction of function mm_op_word to tag A

Function mm_op_word computes the operation of an element \(h = g^e\) of the monster group a vector v and overwrites v with the result of that operation. \(h\) depends on parameters g, len_g, and e of this function.

Function mm_op_word_tag_A computes the same automorphism on the entries of the vector v with tag A only, and ignores the other entries of v. See section The representation of the monster group in the API reference for tags of entries of a vector in the representation of the monster.

The function overwrites the vector v with the result. Here only entries of v with tag A are changed.

Parameters and return value are the same as in function mm_op_word, except that a work buffer is not required here. Also, the function fails and returns a nonzero value, if the word that makes up the group element \(h\) contains any nonzero powers of the generator \(\tau\) of the monster group. Note that such a power of \(\tau\) does not fix the the part of the vector v with tag A. Powers of the generator \(\tau\) correspond to atoms with tag t.

This function is much faster than function mm_op_word. Array v must have 24 * mm_aux_v24_ints(p) entries of type uint_mmv_t.

Here modulus p must be one of the values 3, 7, 15, 31, 127, or 255.

int32_t mm_op_word_ABC(uint32_t p, uint_mmv_t *v, uint32_t *g, int32_t len_g, uint_mmv_t *v_out)

Compute ABC part of the operation of the monster group on a vector.

Let \(v\) be a vector of the representation \(\rho_p\) of the monster group stored in the array referred by v.

Let \(g\) be the element of the monster group stored in the array of length len_g referred by the pointer g. Here \(g\), and also all prefixes of the word representing \(g\), must be in the set \(G_{x0} \cdot N_0\).

The function computes the parts with tags A, B, and C of the vector \(v \cdot g\) and stores the result in the array v_out. The other parts of the vector \(v \cdot g\) are not computed. Here the array v_out must have 72 * mm_aux_v24_ints(p) entries of type uint_mmv_t.

This function is much faster than function mm_op_word. It is mainly used for dealing with a 2A axis \(v\).

Here modulus p must be one of the values 3, 7, 15, 31, 127, or 255.

int32_t mm_op_scalprod(uint32_t p, uint_mmv_t *v1, uint_mmv_t *v2)

Compute the scalar product of two vectors.

Let \(v_1, v_2\) be vectors of the representation \(\rho_p\) of the monster group stored in the array referred by v1, v2.

The function returns the scalar product \((v_1, v_2)\) (reduced modulo \(p\)) in case of success and a negative value in case of failure.

Here modulus p must be one of the values 3, 7, 15, 31, 127, or 255.

Variables

const uint8_t MM_OP_P_TABLE[] = {0x03, 0x07, 0x0f, 0x1f, 0x7f, 0xff, 0x00}

Table of legal moduli p, terminted by zero.

C interface for file mm_op_p_axis.c

File mm_op_p_axis.c implements the operation of the Monster group on a vector in the representation \(\rho_p\) of the Monster modulo \(p\). In this module such a vector is usually a 2A axis.

The representation \(\rho_p\) is equal to the 196884-dimensional representation \(\rho\) of the monster, with coefficients taken modulo \(p\), as defined in section The representation of the monster group in the API reference.

The first parameter of a function in this module is the modulus \(p\), and that modulus must be one of the values specified in the function.

An element of \(\rho_p\) is implemented as an array of integers of type uint_mmv_t as described in section Description of the mmgroup.mm extension in this document.

The number of entries of a vector of type uint_mmv_t[] for modulus \(p\) is equal to mm_aux_mmv_size(p).

Functions

int32_t mm_op_load_leech3matrix(uint32_t p, uint_mmv_t *v, uint64_t *a)

Load the ‘A’ part of a vector of the representation of the monster.

The function loads the part of with tag ‘A’ of a vector v of the representation of the monster modulo p to the matrix a. Here matrix a will be given in matrix mod 3 encoding as documented in the header of file leech3matrix.c.

Here modulus p must be one of the values 3, or 15.

int64_t mm_op_eval_A_rank_mod3(uint32_t p, uint_mmv_t *v, uint32_t d)

Rank of ‘A’ part of a vector of the representation of the monster.

Let a be the symmetric 24 times matrix corresponding to the part with tag ‘A’ of a input vector v in the representation of the monster modulo p. Let b = a - d * 1, for an integer input d, where 1 is the unit matrix.

Let r be the rank of matrix b with entries taken modulo 3. If matrix b has rank 23 then its kernel is one dimensional. In that case the kernel contains two nonzero vectors +-w, and we define w to be one of these vectors. Otherwise we let w be the zero vector.

The function returns the value (r << 48) + w, with w the vector defined above given in Leech lattice mod 3 encoding as described in The C interface of the mmgroup project.

The number of entries of parameter v for modulus \(p\) is equal to 24 * mm_aux_int_fields(p).

Here modulus p must be 3, or 15.

int32_t mm_op_eval_A_aux(uint32_t p, uint_mmv_t *v, uint32_t m_and, uint32_t m_xor, uint32_t row)

Auxiliary function for mm_op_eval_A

Let matrix A be the part with tag ‘A’ of a vector v of the representation of the monster modulo p}.

Let m_and[i] and m_xor[i] be the bit i of m_and and m_xor, respectively. Define a vector y = (y[0],...,y[23]) by: y[i] = m_and[i] * (-1)**m_xor[i].

If row >= 24 the function returns res = y * A * transpose(y) (modulo p). We have 0 < res < 0x8000, but res is not reduced modulo p.

In case row < 24 define the vector z by z[i] = y[i] if i = row and z[i] = 0 otherwise. Put zz = z * A * transpose(y) (modulo p). We have 0 < res < 0x8000, but res is not reduced modulo p.

Here modulus p must be 3, or 15.

int32_t mm_op_eval_A(uint32_t p, uint_mmv_t *v, uint32_t v2)

Evaluate A part in rep of monster at a short Leech vector.

Let v be a vector in the 196884-dimensional representation of the monster group modulo p, encoded as described in section Description of the mmgroup.mm<p> extensions in the description of the C interface. The entries corresponding to tag ‘A’ of v form a symmetric 24 times 24 matrix \(A\).

Let \(v_2\) be a short Leech lattice vector given by parameter v2, encoded as a vector in the Leech lattice modulo 2. Then \(v_2\) is determined up to sign and \(v_2 A v_2^\top\) is determined uniquely.

The function returns \(r = v_2 A v_2^\top\) modulo p, with \(0 \leq r < p\) in case of success. It returns -1 if \(v_2\) is not short (i.e. not of type 2).

The short Leech lattice vector \(v_2\) (of norm 4) is scaled to norm 32 as usual, when \(v_2\) is given in integer coordinates.

Here modulus p must be 3, or 15.

int32_t mm_op_norm_A(uint32_t p, uint_mmv_t *v)

Compute norm of the ‘A’ part of a vector in the rep of the monster.

Assume that v is a vector in the representation of the monster modulo p. Then the part of v with tag ‘A’ is considered as a symmetric 24 times 24 matrix. The function returns the norm (i.e. the sum of the squares of the entries) of that matrix.

Here modulus p must be 3, or 15.

int32_t mm_op_watermark_A(uint32_t p, uint_mmv_t *v, uint32_t *w)

Watermark ‘A’ part of a vector of the representation of the monster.

Let matrix A be the part with tag ‘A’ of a vector v of the representation of the monster modulo p.

Then we watermark 24 the rows of matrix A. For each of the rows A[i], 0 <= i < 24 we compute a watermark w(i) in the array w. Note that the watermark w(i) contains an information about the marked row i in its lower bits. We store the sorted array of these watermarks in the array w of length

  1. If all these watermarks (ignoring the information about the row) are different, we can easily recognize a permutation of the rows of matrix A by comparing the watermark of matrix A with the watermark of the permuted matrix A.

The watermark w[i] depends on the distribution of the absolute values of the entries w[i, j] (modulo p) of row i. Thus permutations of the columns and sign changes in the matrix do not affect these watermarks.

The function returns 0 in case of success and a negative value in case of error.

When working in the representation modulo p = 3 we fail unless at least nine rows of A have a unique watermark. This is sufficient for reconstructing a permutation in the Mathieu group.

In the other cases the watermark of row \(i\) is equal to \(i + 32\cdot S(A,i)\). Here \(S(A,i)\) depends on the entries of matrix A. The value \(S(A,i)\) it is invariant under sign changes of any off-diagonal elements of A. It is also invariant under any permutation of the symmetric matrix A fixing row and column \(i\).

We assert that watermarking A + k*I succeeds if and only if watermarking A succeeds, for any multiple k*I of the unit matrix.

Here modulus p must be 3, or 15.

int32_t mm_op_watermark_A_perm_num(uint32_t p, uint32_t *w, uint_mmv_t *v)

Compute permutation from watermarks of matrices.

Let matrix A be the part with tag ‘A’ of a vector v of the representation of the monster modulo p. Let w be the watermark of another matrix A' which is obtained from A by permutations of the rows and columns, and by sign changes. Here the watermark w must have been computed by function mm_op_watermark_A.

Then the function watermarks matrix A and computes a permutation that maps A' to A. If that permutation is in the Mathieu group \(M_{24}\) then the function returns the number of that permutation, as given by function mat24_perm_to_m24num in file mat24_functions.c.

The function returns a nonegative permutation number in case of success and a negative value in case of error.

If all watermarks in the array w (ignoring the information about the row in the lower 5 bits) are different then there is at most one permutation that maps A' to A. If that permutation is in \(M_{24}\) then the function returns the number of that permutation. In all other cases the function fails.

In case p = 3 we succeed already if the first 9 watermarks in the array w are different. Then there is at most one permutation in \(M_{24}\) that maps A' to A.

Here modulus p must be 3, or 15.

int32_t mm_op_eval_X_find_abs(uint32_t p, uint_mmv_t *v, uint32_t *p_out, uint32_t n, uint32_t y0, uint32_t y1)

Find certain entries of a vector of the monster rep modulo %p

Let v be a vector of the monster group representation modulo %p. The function tries to find all entries of the monomial part of v with absolute values y0 and y1, 1 <= y0, y1 <= p / 2. In case y1 = 0 entries with value y1 are ignored.

Here the monomial part of v consists of the entries with tags ‘B’, ‘C’, ‘T’, ‘X’. The coordinates of these entries correspond to the short vectors of the Leech lattice.

Output is written into the array p_out of length n. If the monomial part of v contains an entry with absolute value y0 then the coordinate of that entry is written into array p_out in Leech lattice encoding. If that part of v contains an entry with absolute value y1 then the coordinate of that entry is witten into that array in the same encoding.

In addition, for entries in v with absolute value y1 the bit 24 of the corresponding entry in p_out is set. In p_out, the entries with absolute value y1 are stored after those with absolute value y0. Entries with the same absolute value are stored in the same order as in v.

The function returns the number of valid entries in the array p_out. If the length n of p_out is too small then some entries will be dropped without notice.

Here modulus p must be 15.

int32_t mm_op_eval_X_count_abs(uint32_t p, uint_mmv_t *v, uint32_t *p_out)

Count certain entries of a vector of the monster rep modulo p

Let v be a vector of the monster group representation modulo p. The function counts the absolute values of all entries of the monomial part of v.

Here the monomial part of v consists of the entries with tags ‘B’, ‘C’, ‘T’, ‘X’. The coordinates of these entries correspond to the short vectors of the Leech lattice.

Output is written into the array p_out of length p/2 + 1. Entry p_out[i] contains the number if entries of the monomial part of v with absolute value i for 0 <= i <= p/2 + 1.

Here modulus p must be 15.

The function returns 0.

Internal operation

The source code for the functions in modules mm_aux.c and mm_tables.c is located in subdirectory src/mmgroup/dev/mm_basics. The code generation process genrates .c files from the source files with extension .ske in that subdirectory. Protoypes for the functions in these .c files can be found in file mm_basics.h.

The source code for the functions in modules mm_op_p_vector.c and mm_op_p_axis.c is located in subdirectory src/mmgroup/dev/mm_op. The code generation process genrates .c files from the source files with extension .ske in that subdirectory. Protoypes for the functions in these files can be found in file mm_o_p.h.

The code gerneration process for C files derived from the source files in the src/mmgroup/dev/mm_basics directory is straightforward. In the remainder of this subsection we describe the code gerneration process for files derived form sources in the src/mmgroup/dev/mm_op directory.

For reasons of efficiency a dedicated set of .c files is generated from the .ske```files in the ``src/mmgroup/dev/mm_op directory for each modulus p supported. Here corresponding .c files for different moduli are generated from the same source file with extension .ske. The .c functions in modulea mm_op_p_vector.c and mm_op_p_axis.c``contain automatically-generated ``case statements that call the working functions for the appropriate modulus p. Any of these .c functions takes the modulus p as its first argument, and dispatches the work to the subfunction dedicated to the appropriate modulus p.

E.g. the function mm_op_pi in module mm_op_p_vector.c` calls working functions ``mm3_op_pi, mm7_op_pi, etc. in case p = 3, p = 7, etc., for doing the actual work in the representation of the Monster modulo p. Such working functions are implemented in different .c files, with one .c file for each modulus. So the functions mm3_op_pi and mm7_op_pi are implemented in the .c files mm3_op_pi.c and mm7_op_pi.c`, respectively. Here such a .c file may implement several working functions for the same modules p.

All functions exported from file mm_op_p_vector.c support the same set of moduli p. Functions exported from file mm_op_p_axis.c deal with 2A axes; the usually support moduli 3 and 15 (or just one of these two values) only; see documentation of the corresponding functions.

So the process of switching to the appropriate function for a given modulus is completely invisible for Python and Cython.

Basic table-providing classes for module mmgroup.mm_op

We describe the class MM_Basics and its subclass MM_Const

Class MM_Basics in module mmgroup.dev.mm_basics.mm_basics defines several constants that describe the organization of vectors of integers modulo \(p\) used in the representation modulo \(\rho_p\). Here several integers modulo \(p\) are stored in a single unsigned integer of type uint_mmv_t. Type uint_mmv_t is equal to the standard C integer type uint64_t on a 64-bit system.

For a 32-bit system that type may be changed to uint32_t by adjusting the variable INT_BITS in this module, but this has no longer been tested for several years.

Constants used for generating representations of the monster

Name

Value

FIELD_BITS

Number of bits used to store an integer modulo P. This is the smallest power of two greater than or equal to P_BITS.

INT_BITS

Number of bits available in an unsigned integer of type uint_mmv_t. This is equal to 64.

INT_FIELDS

Number of integers modulo P that is stored in a variable of type uint_mmv_t. INT_FIELDS is a power of two and equal to INT_BITS/FIELD_BITS.

LOG_FIELD_BITS

Binary logarithm of the value FIELD_BITS.

LOG_INT_BITS

Binary logarithm of the value INT_BITS.

LOG_INT_FIELDS

Binary logarithm of the value INT_FIELDS.

LOG_V24_INTS

Binary logarithm of the value V24_INTS.

LOG_V64_INTS

Binary logarithm of the value V64_INTS.

MVV_ENTRIES

Number of integers modulo P contained in a vector of the representation of the monster, including unused entries.

MVV_INTS

Number of integers of type uint_mmv_t required to store a vector of the representation of the monster. This value depends on P.

P

The modulus of the current representation being generated. P is equal to 2**P_BITS - 1.

P_BITS

Bit length of modulus P.

V24_INTS

Number of integers of type uint_mmv_t used up to store a vector of 24 integers modulo P. V24_INTS is always a power of two.

V64_INTS

Number of integers of type uint_mmv_t used to store a vector of 64 integers modulo P. V64_INTS is always a power of two.

V64_INTS_USED

Number of integers of type uint_mmv_t actually used to store a vector of 24 integers modulo P. This may be less than V24_INTS.

Class MM_Const in module mmgroup.dev.mm_basics.mm_basics is a table-providing class for the C functions used by python extension mmgroup.mm. Most C functions in that module take the modulus p as a parameter. They need fast access to the constants given in the table above for all legal values of p. Using the code generator with the tables and directives provided by class MM_Const, we can easily obtain these values for any legal p as in the following example:

// Return a nonzero value if p is a bad modulus,  
// i.e. not p = 2**k - 1 for some 2 <= k <= 8
#define mm_aux_bad_p(p) (((p) & ((p)+1)) | (((p)-3) & ((0UL-256UL))))

// Create a precomputed table containing the constants for modulus p
// %%USE_TABLE
static const uint32_t MMV_CONST_TABLE[] = {
// %%TABLE MMV_CONST_TAB, uint32
};

int do_something_for_modulus_p(uint 32_t p, ...)
{
    uint32_t modulus_data, p_bits, field_bits;
    // Reject any illegal modulus p
    if (mm_aux_bad_p(p)) return -1;
    // Load the constants for modulus p to variable modulus_data
    // %%MMV_LOAD_CONST  p, modulus_data;
    // Load value P_BITS for modulus P to p_bits (using modulus_data)
    p_bits = %{MMV_CONST:P_BITS,modulus_data};
    // Load value FIELD_BITS for modulus P to field_bits 
    field_bits = %{MMV_CONST:FIELD_BITS,modulus_data};
    // Now do the actual work of the function using these values
    ...
}
class mmgroup.dev.mm_basics.mm_basics.MM_Const(**kwds)

This is the basic table-providing class for module mmgroup.mm

The main purpose of this class is to provide the constants defined in class MM_Basics, for a variable modulus ``p as shown in the example above.

This class provides the directive MMV_CONST_TAB for generating all tables, and the directive MMV_LOAD_CONST for storing the table of constants, for a specific modulus p, in an integer variable. The string formatting function MMV_CONST can be used for extracting a specific constant from that variable, as indicated in the example above.

Internally, we use a deBruijn sequence to translate the value p to an index for the table generated via the directive MMV_CONST_TAB.

Constants not depending on the modulus p, such as INT_BITS, LOG_INT_BITS, and MMV_ENTRIES are available as attributes of class MM_Const. They can also be coded with the code generator directly via string formatting, e.g.:

uint_8_t  a[%{MMV_ENTRIES}];

For a fixed modulus p the constants depending on p can also be coded with the code generator via string formatting, e.g.:

a >>= %{P_BITS:3};

That constant also availble in the form MM_Const().P_BITS(3).

Class MM_Const provides a string-formatting function shl which generates a shift expression Here:

%{shl:expression, i}

generates an expression equivalent to ((expression) << i). Here i must be an integer. In case i < 0 we generate ((expression) >> -i) instead. E.g. %{shl:'x',-3} evaluates to x >> 3.

Class MM_Const provides another string-formatting function smask which generates an integer constant to be used as a bit mask for integers of type uint_mmv_t. Here:

%{smask:value, fields, width}

with integers value, fields, and width creates such a bit mask. For 0 <= i and 0 <= j < width, the bit of that mask at position i * width + j is set if both, bit i of the integer fields and bit j of the integer value are set. A bit in the mask at position INT_BITS or higher is never set.

Any of the arguments value and fields may either be an integer or anything iterable that yields a list of integers. Then this list is interpreted as a list of bit positions. A bit of that argument is set if its position occurs in that list and cleared otherwise.

E.g. %{smask:3, [1,2], 4} evaluates to 0x330.

snippet(source, *args, **kwds)

Generate a C code snippet

This method calls function c_snippet in module mmgroup.generate_c to generate a C code snippet. Parameters are as in function c_snippet. The method returns the generated C code snippet as a string.

In addition, all tables and directives available in the given instance of the table-providing class are also passed as arguments to function c_snippet.

We describe the class MM_Op.

Class MM_Op in module mmgroup.dev.mm_op.mm_op is a table-providing class for the C functions used by python extensions mmgroup.mm<p>. Each of these extensions implements the operation of monster group modulo a fixed number p.

Class MM_Op provides the same functionality as class MM_Const in module mmgroup.dev.mm_basics.mm_basics. Since modulus p is fixed in an instance of class MM_Op, all constants provided by the base class MM_Basics of MM_Op and MM_Const (see table Constants used for generating representations of the monster) have fixed values. So they are available as attributes of an instance of class MM_Op and they may also be used by the code generator directly via string formatting.

The string formatting functions %{shl:expression,i} and %{smask:value, fields, width} work as in class MM_Op. In addition, parameter fields defaults to -1 (i.e. the mask is set in all bit fields) and width defaults to the constant FIELD_BITS. i.e. the number of bits used to store an integer modulo p. E.g. for p = 7 we have FIELD_BITS = 4, and %{smask:1} evaluates to the 64-bit integer constant 0x1111111111111111.

Class MM_Op also provides the following directives:

MMV_ROTL src, count, dest

Rotate the bit fields of a variable of type uint_mmv_t

Here src is an integer of type uint_mmv_t which is interpreted as an array of bit fields, where each bit field stores a number modulo p. Then each bit field is rotated left by count bits. This means that the numbers in all bit fields are multiplied by 2**count. count must be an integer. It is reduced modulo the bit length of p. So count may also be negative. The result of the rotation is written to the variable dest which should also be of type uint_mmv_t. dest defaults to src.

MMV_UINT_SPREAD src, dest, value

Spread bits of an integer src to the bit fields of dest. Here src may be any integer variable and dest should be a variable of type uint_mmv_t. If bit i of the integer src is set then the i-th bit field of variable dest is set to value; otherwise it is set to zero. value must be an integer with 0 <= value <= p; default is p.

class mmgroup.dev.mm_op.mm_op.MM_Op(**kwds)

Supports basic operations on integers of type uint_mmv_t.

Here an integer of type uint_mmv_t is interpreted as an array of bit fields, where each bit field stores an integer modulo p for a fixed number p. This class is similar to class MM_Const in module mmgroup.dev.mm_basics.mm_basics, where the modulus p may be variable.

Usage of this class is documented in the module documentation.

Parameters:

p (int) – This is the modulus 3 <= p < 256. p + 1 must be a power of two.

Deprecated stuff

In older versions of the mmgroup project the functionality of the mm_op extension was spread over several Cython extensions with names mm, mm3, mm7, mm15, etc. There are now python modules emulating the functionality of these deprecated extensions, which appear to work in Windows and Linux, but possibly not in macOS.

Note that the modules mmgroup.mm, mmgroup.mm3, mmgroup.mm7, etc., which emulate the old functionality, are also deprecated. So the user is strongly discouraged from using these deprecated modules. He or she should use the mm_op extension instead!

In older versions some python functions had to select the C function for the requested modulus, which has caused rather nasty problemes in some cases.

In the mm_op extension each documented .c function has been wrapped by a Cython function with the same name and the same signature. In the depreceated modules naming conventions are considerably more complicated.

Description of the mmgroup.mm_reduce extension

The functions in this module implement the fast reduction of an element of the monster group described in [Sey22]. There we define a triple of vector \((v_1, v^+, v^-)\) in the representation \(\rho_{15}\) such that an element \(g\) of the monster van be recognized from the triple \((v_1 \cdot g, v^+ \cdot g, v^- \cdot g)\). A precomputed vector \(v_1\) is stored in file mm_order_vector.c. Module mm_order.c contains functions for computing the order of an element of the monster. Module mm_reduce.c contains the function mm_reduce_M that implements the fast reduction algorithm in the monster group.

Generating an order vector

Python module mmgroup.dev.mm_reduce.find_order_vector

This module computes an order vector in a representation of the monster.

In [Sey22] we define a vector \(v_1\) in the representation \(\rho_{15}\) of the monster group \(\mathbb{M}\) that is used for recognizing elements of the subgroup \(G_{x0}\) of \(\mathbb{M}\). Vector \(v_1\) is also used for computing the order of an element of \(\mathbb{M}\), so we call \(v_1\) an order vector here.

\(v_1\) is constructed from two vectors \(v_{71} \in \rho_3\) and \(v_{94} \in \rho_5\) via Chinese remaindering. This module contains functions for computing vectors \(v_{71}\) and \(v_{94}\) with the properties required in [Sey22].

These computations are the most time-consuming part of the computation of \(v_1\); and we use multiprocessing for performing these computations.

All computations in the monster group are done with instances of class MM0, but not MM. The reason for this is that class MM requires the existence of an order vector.

Header file mm_reduce.h

Yet to be documented

Typedefs

typedef struct gt_subword_s gt_subword_type

typedef for structure struct gt_subword_s

typedef struct gt_subword_buf_s gt_subword_buf_type

typedef for structure struct gt_subword_buf_s

struct mm_compress_type
#include <mm_reduce.h>

Structure for storing an element of the Monster compactly.

A properly reduced element of the Monster stored in a structure of type gt_word_type may also be encoded in this structure in a more compact form. This facilitates the conversion of that element to an integer, which is out of the scope of this module.

This structure may store an element of the Monster as a word of generators of shape

\[ y_f \, x_d \, x_{\delta} \, \pi \, c_1 \, \tau_1 \, c_2 \, \tau_1 \, c_3 \, \tau_3 \, \ldots \, , \]

where \(d, f \in \mathcal{P}, \delta \in \mathcal{C}^*\), and \(\pi \in \mbox{Aut}_{\mbox{St}} \mathcal{P}\). Here \(\pi\) must correspond to a generator with tag p. See section Implementation of the generators of the monster group in the API reference for details. \(\tau_i\) is wqaul to generator \(\tau\) or to its inverse.

A generator \(c_i\) is an element of the group \(G_{x0}\) referred by a 24-bit integer c_i. This must be one of the following:

If c_i represents a type-4 vector in Leech lattice encoding then this encodes the element of \(G_{x0}\) computed by applying the C function gen_leech2_reduce_type4 to c_i.

If c_i represents a type-2 vector in Leech lattice encoding then this encodes the element of \(G_{x0}\) computed by applying the C function gen_leech2_reduce_type2 to c_i.

The product \(y_f \, x_d \, x_{\delta} \, \pi\) is encdoded in component nx, and the other generators are encdoded in the entries of component w, as desribed in the procedures below.

For background see section Computations in the Leech lattice modulo 2 in The mmgroup guide for developers.

Public Members

uint64_t nx

encoding of \( y_f \, x_d \, x_{\delta} \, \pi\)

uint32_t w[MM_COMPRESS_TYPE_NENTRIES]

encoding of \(c_i, \tau_i\)

uint32_t cur

index of last entry intered into component w

uint32_t back

True if structure is filled in reverse order.

struct gt_subword_s
#include <mm_reduce.h>

Stucture to store a subword of generators of the Monster.

This structure is required in file mm_shorten.c.

It stores a subword of a word in the Monster in a node of a circular doubly-linked list. There is a dedicated EOF (end of file) mark in that list, in which member eofis set to 1; and elsewhere member eof is set to zero. Note that standard operations like insertions and deletions are easier in a circular doubly-linked list than in a standard doubly-linked list.

Member data contains a word \(g\) of generators of the subgroup \(G_{x0}\); and member t_exp contains an exponent \(0 \leq e < 3\). Then the structure represents the element \(g \tau^e\) of the Monster, where \(\tau\) is the triality element in the subgroup \(N_0\) of the Monster.

The length of a word in member data is limited to the size MAX_GT_WORD_DATA - 1; and we will reduce that word using function xsp2co1_reduce_word in module xsp2co1.c if necessary. Note that member data may contain atoms with tags 'x', 'y', 'd', 'p', 'l' only, and the inversion bit in such an atom is always cleared.

Member img_Omega contains the image \(g \cdot \Omega\), where \(\Omega\) is the standard frame of the Leech lattice mod 2. Here img_Omega is given in Leech lattice encoding. Member reduced is 1 if the word in member data is reduced (by function xsp2co1_reduce_word) and 0 otherwise.

Public Members

uint32_t eof

0 means standard subword, 1 means EOF mark

uint32_t length

Number of entries in component data

uint32_t img_Omega

Image of \(\Omega\) under element.

uint32_t t_exp

Exponent of final tag ‘t’.

uint32_t reduced

True if part ‘data’ is reduced.

struct gt_subword_s *p_prev

Pointer to previous subword.

struct gt_subword_s *p_next

Pointer to previous subword.

uint32_t data[MAX_GT_WORD_DATA]

Element of monster group.

struct gt_subword_buf_s
#include <mm_reduce.h>

Structure to store an array of entries of type gt_subword_type

We allocate several entries of of type gt_subword_type with a single call to function malloc. This saves a considerable amount of interaction with the operating system.

This structure contains an array of type gt_subword_type[] plus the necessary bookkeeping information.

Public Members

uint32_t capacity

max No of type gt_subword_s entries

uint32_t n_used

used No of type gt_subword_s entries

struct gt_subword_buf_s *p_next

Pointer to next buffer.

gt_subword_type subwords[1]

array of subwords in this buffer

struct gt_word_type
#include <mm_reduce.h>

Stucture to store a word of generators of the Monster.

This structure is required in file mm_shorten.c.

It stores a word in the Monster in a circular doubly-linked list of nodes of type gt_subword_type. Each of these node represents subword of that word, and there is a dedicated EOF (end of file) mark in that list that marks both, the beginning and the end of the list. Member p_end always points to the EOF mark.

The structure contains a pointer p_node pointing to one of the nodes of the list, which we will call the current node. Some functions in these module take a pointer to this structure, and the perform operations on the current node. The pointer may be manipulated with function gt_word_seek.

Public Members

gt_subword_type *p_end

Pointer to the end mark subword.

gt_subword_type *p_node

Pointer to current subword.

gt_subword_type *p_free

Pointer to list of free subwords.

int32_t reduce_mode

Mode for the reduction of a word.

uint32_t is_allocated

1 if this structure has been allcocated

gt_subword_buf_type *pb0

pointer to first buffer

gt_subword_buf_type *pbe

pointer to last buffer

gt_subword_buf_type buf

1st buffer containing Array of subwords

C interface for file mm_order_vector.c

File mm_order_vector contains the precomuted order_vector and data related to that order_vector,

It also contains functions for retrieving these data.

Functions

int32_t mm_order_load_tag_data(uint32_t n, uint32_t *buf, uint32_t buf_size)

Load data from tables to a buffer.

The propose of this function is to provide the information for checking the correctness of the precomuted order vector \(v_1\) stored in this module.

This function stores precomputed data in a buffer buf of type uint32_t[buf_size], where buf_size is the size of the buffer. Parameter n specifies the value to be stored in the buffer. The function returns the length of the data in buffer. If the buffer is too short for that data then the function returns -1. A size parameter buf_size = 128 is sufficient for all cases of n.

In case n = 0 the function returns the array TAG_VECTOR of length 97. The tag vector is documented in file mm_order.c.

In case n > 0 the function returns data required for verifying the precomputed order vector \(v_1\). The order vector is discussed in [Sey22].

In case n = 1,2,3 the function returns the values \(g_{71} \in \mathbb{M}, v^0_{71} \in \rho_{3}, g \in \mathbb{M} \), respecively, in the internal sparse format. With these data we can compute:

\(v_{71} = \sum_{i=0}^{70} v^0_{71} \cdot g_{71}^i g\pmod{3}\).

In case n = 4 the function returns the integer \(D\) in entry 0 of the buffer. In case n = 5, 6 the function returns the values \(g_{94} \in \mathbb{M}, v^0_{94}\), respectively. With this data we can compute:

\(v_{94} = \sum_{i=0}^{93} (-1)^i \cdot v^0_{94} \cdot g_{94}^i \pmod{5}\).

\(v_1 = 10 \cdot v_{71} + 6 \cdot v_{94} + 5 \cdot D \cdot 1_\rho \pmod{15}\).

Here \(1_\rho\) is defined as in [Sey22]. We can also check that the order vector \(v_1\) satisfies the properties required in [Sey22].

In case n = 7, 8, 9, the function returnes the parts TAGS_Y, TAGS_X, and TAG_SIGN, respectively, of the TAG_VECTOR. For a description of the TAG_VECTOR see the description of enum tag_offsets in file order_vector.c.

void mm_order_load_vector(uint_mmv_t *p_dest)

Load order vector from tables to a buffer.

The function stores the precomputed order vector \(v_1\) into the array referred by p_dest. That array must must be sufficiently long to store a vector of the representation \(\rho_{15}\).

See [Sey22] for a decription of the properties of the vector \(v_1\) in the representation \(\rho_{15}\) of the monster group.

The standard way to obtain the number of entries of type unint_mmv_t required for a vector of the representation \(\rho_{15}\) is to call function mm_aux_mmv_size(15) in file mm_aux.c.

int32_t mm_order_compare_vector(uint_mmv_t *p_v)

Compare vector with precomputed order vector.

The function compares the vector \(v\) in the representation \(\rho_{15}\) of the monster group referred by p_v with the precomputed order vector \(v_1\).

The function returns 0 in case of equality and 1 otherwise.

int32_t mm_order_compare_vector_part_A(uint_mmv_t *p_v)

Compare A part of vector with precomputed order vector.

The function compares the A part the vector \(v\) in the representation \(\rho_{15}\) of the monster group referred by p_v with the A part of the precomputed order vector \(v_1\).

The function returns 0 in case of equality and 1 otherwise.

uint64_t mm_order_hash_vector(uint_mmv_t *p_dest)

Return the hash value of the ordervector.

The function returns the hash value of the precomuted order vector as given by function mm_aux_hash in file mm_aux.c.

C interface for file mm_order.c

File mm_order.c contains functions for checking if an element is in the subgroup \(G_{x0}\) (or \(Q_{x0}\)) of the monster group. If this is the case, the element is expressed as a word in the generators of the corresponding subgroup.

File mm_order.c also contains functions for computing the order of an element of the monster.

All these function use the precomputed order_vector in file mm_order_vector.c

Enums

enum tag_offsets

The following enumeration contains the offsets of the (static) array TAG_VECTOR of unsigned 32-bit integers stored in this module. This array contains data that are required for dealing with the precomputed order vector \(v_1\) stored in this module. That order vector can be obtained by calling function mm_order_load_vector in module mm_order_vector.c. The data in the array TAG_VECTOR can be obtained by calling function mm_order_load_tag_data(0, buf, 97) in the same module, with buf of type uint32_t[97].

The purpose of the order vector \(v_1\) is to identify an element \(g\) of the subgroup \(G_{x0}\) of the monster from \(v_1 \cdot g\), as described in [Sey22]. The basic steps of this identification process are:

  1. Given \(v_1 \cdot g\), reduce \(g\) to an element \(g_1\) of \(N_{x0}\), see [Sey22] for background.

  2. Given \(v_1 \cdot g_1\), reduce \(g_1 \in N_{x0}\) to \(g_2\), where \(g_2\) is in a certain 2 group of structure \(2^{1+24+11}\). Therefore we have to watermark of the A part of \(v_1 \cdot g_1\) with function mm_op15_watermark_A in module mm15_op_eval_A.c. Then we have to check that watermark against the precomputed watermark of the A part of \(v_1\) using function mm_op15_watermark_A_perm_num in the same module. That precomputed watermark (of length 24) is stored in the array TAG_VECTOR at offset OFS_WATERMARK_PERM.

  3. Given \(v_1 \cdot g_2\), with \(g_2\) as above, we want to present \(g_2\) as a product of generators \(g_2 = x_d x_\delta y_e\). For computing the factor \(y_e\) we have to extract (the signs of) 11 entries of \(v_1 \cdot g_2\), with function mm_aux_mmv_extract_sparse_signs in module mm_aux.c. The coordinates and the expected values of these 11 entries are stored at the TAG_VECTOR at offset OFS_TAGS_Y. Once having extracted these sign bits we have to solve the linear equation system given by the 11 entries at the TAG_VECTOR at offset OFS_SOLVE_Y. This solution gives the value \(e\) of the factor \(y_e\).

  4. For computing the part \(x_d x_\delta\) of \(g_2\) (up to sign) we have to extract (the signs of) 24 more entries of the vector \(v_1 \cdot g_2 y_e^{-1}\) and to solve another equation system as in the previous step. Here the relevant coordinates are stored at the TAG_VECTOR at offset OFS_TAGS_X, and the equation system is stored at offset OFS_SOLVE_X.

  5. To correct the sign in the part \(x_d x_\delta\), we have to extract another sign from \(v_1 \cdot g_2 \cdot y_e^{-1} (x_d x_\delta)^{-1}\) at the coordinate given by OFS_TAGS_SIGN.

Values:

enumerator OFS_NORM_A

Sum of the squares of the A part of \(v_1\) (mod 15)

enumerator OFS_DIAG_VA

For compatibly with older versions, always equal to 0

enumerator OFS_WATERMARK_PERM

Watermark of the A part of \(v_1\) (mod 15)

enumerator OFS_TAGS_Y

Entries of \(v_1\) used for computing \(y_e\)

enumerator OFS_SOLVE_Y

Equation system used for computing \(y_e\)

enumerator OFS_TAGS_X

Entries of \(v_1\) used for computing \(x_d\)

enumerator OFS_SOLVE_X

Equation system used for computing \(x_e\)

enumerator OFS_TAG_SIGN

Entry of \(v_1\) used for computing sign of \(x_d\)

Functions

int32_t mm_order_check_in_Gx0_fast(uint_mmv_t *v)

Quick check if a vector is an image under an element of \(G_{x0}\).

Assume that the vector \(v\) in \(\rho_{15}\) referred by v is the image of the precomputed order vector \(v_1\) under an (unknown) element \(g\) of the monster. The function checks if that element \(g\) is in the subgroup \(G_{x0}\) of the monster.

The function may return 0, 1, or a negative value. If it returns 0 then \(g\) is not in \(G_{x0}\). If it returns 1 then \(g\) is in \(G_{x0}\) with an error probability that is negligible for all practial purposes. A negative return value indicates an error.

int32_t mm_order_check_in_Gx0(uint_mmv_t *v, uint32_t *g, uint32_t mode, uint_mmv_t *work)

Check if a vector is an image under an element of \(G_{x0}\).

Assume that the vector \(v\) in \(\rho_{15}\) referred by v is the image of the precomputed order vector \(v_1\) under an (unknown) element \(g\) of the monster. We want to find that element \(g\) if it is in the subgroup \(G_{x0}\) of the monster.

We first describe the action of the function in the standard case that parameter mode is zero.

The function computes the element \(g\) if it is in the subgroup \(G_{x0}\) of the monster; otherwise it fails.

In case of success the function writes the element \(g\) into the array g as a word in the generators of \(G_{x0}\) of length at most 11; and the function returns the length of that word.

If no such element \(g_1\) has been found then the function returns a number greater than 256. Then the exact return value gives some indication why no such element \(g\) has been found; this is for debugging only.

A negative return value indicates an internal error.

The function also requires an array work of length 15468 as a work buffer. This is the capacity required for storing a vector in the representation \(\rho_{15}\).

Parameter mode is a bit field that modifies the action of the function as follows:

If bit 0 is set, then the inverse of the result \(g\) is returned in buffer g.

If bit 1 is set then we insert an atom into the word in g that indicates the image of the vector \(\Omega \in \Lambda / 2 \Lambda\) under conjugation with \(g_0\). Such an atom is considered as a comment, acting as the neutral element of the monster group. This is required for function mm_reduce_M in file mm_reduce.c. See documentation in that file for details.

If bit 2 is set then we perform an action to be used for internal tests only. Then the function computes an element \(g_1\) such that \(g^{-1} g_1\in Q_{x0}\) holds in case \(g \in G_{x0}\). If \(g \notin G_{x0}\) then the function detects this fact with high probability. Usually, \(g_1\) is a shorter word in the generators of the monster than \(g\).

If bit 3 is set then input vector v is preserved; but the work buffer work must have the (doubled) length 30936 for storing an additional temporary copy of vector v.

int32_t mm_order_element_Gx0(uint32_t *g, uint32_t n, uint32_t *h, uint32_t o)

Compute exponent \(e\) such that \(g^e \in G_{x0}\).

Let \(g\) be the element of the monster group stored in the array g as a word of generators of the monster group of length n.

The function computes the smallest exponent \(e\) such that \(g^e\) is in \(G_{x0}\). Then the function writes \(h = g^e\) into the buffer h as a word of generators of \(G_{x0}\) of length at most 10. Let \(k\) be the length of the word representing \(h\). Then the function returns the value \(\mbox{0x100} \cdot e + k\); here we have \(1 \leq e \leq 119\) and \(0 \leq k \leq 10\).

Computation of \(e\) is time consuming, and in some cases we are interested in small values of \(e\) only. Parameter o is an upper bound for the exponent \(e\) . The function may abort and return 0 in if \(e\) is greater than o; then the data in buffer h are invalid.

A negative return value indicates an internal error.

int32_t mm_order_element_M(uint32_t *g, uint32_t n, uint32_t o)

Compute order of an element \(g\) of the monster.

Let \(g\) be the element of the monster group stored in the array g as a word of generators of the monster group of length n.

The function returns the order of \(g\).

Computation of the order is time consuming, and in some cases we are interested in small orders only. Parameter o is an upper bound for the order. The function may return 0 if the order is greater than o.

A negative return value indicates an error.

C interface for file mm_reduce.c

Function mm_reduce_M is the most important C function in this project. It reduces an arbitrary element of the monster group (represented as a word in the generators of the monster) to a word of fixed maximum length. Function mm_reduce_M uses the method in [Sey22] for reducing an element of the monster.

Here a word in the monster group is represented as an array of 32-bit integers of type uint32_t as described in section The monster group in the API reference. Such an array represents a word of generators of the monster. Thus group multiplication is concatenation of words. An generator in a word (given by an entry in the array of integers) can be inverted by flipping its most significant bit.

According to [Sey22], and element \(g\) of the monster group can be computed from the images \(v^+ g\), \(v^- g\), and \(v_1 g\). Here \(v^+, v^- \in \rho_{15} \) are certain 2A axes, and \(v_1 \in \rho_{15}\) is certain vector used for recognizing an element of the subgroup \(G_{x0}\) of the monster, see ibid. for details. Representation \(\rho_{15}\) is the 198884-dimensional faithful representation of the monster group modulo 15. Vector \(v_1\) is also called an order vector; and we can use the functions in module mm_order.c for dealing with a fixed precomputed order vector.

An element of \(\rho_{15}\) is implemented as a array of MM_OP15_LEN_V integers of type uint_mmv_t as described in The C interface of the mmgroup project, section Description of the mmgroup.mm extension. The value MM_OP15_LEN_V is defined in file mm_op15.h.

For reducing an element \(g\) of the monster we have to analyze the images \(v^+ g\), \(v^- g\), and \(v_1 g\) in that order. This file also exports subfunctions for performing these tasks. We use subfunctions mm_reduce_vector_vp, mm_reduce_vector_vm, and mm_reduce_vector_v1 for analyzing \(v^+ g\), \(v^- g\), and \(v_1 g\), respectively. These functions accumulate their results in a buffer (of type uint32_t[] and size at least 128) that will eventually contain the reduced word equal to \(g\).

The following code example shows an implementation of the main function mm_reduce_M based on these subfunctions, ignoring the error handling.

// The following buffers contain the element ``g`` to be reduced
uint32_t a[256];                 // buffer containing element ``g``
uint32_t n;                      // length of data in buffer ``a``

// The folling buffers will contain the reduced element ``g``
uint32_t r[256];                 // buffer for reduced element ``g``
uint32_t len;                    // length of data in buffer ``r``

// Temporary work buffers
uint_mmv_t v[MM_OP15_LEN_V];     // buffer for 2A axes 
uint_mmv_twork[MM_OP15_LEN_V];   // work buffer

mm_op15_store_axis(V_PLUS, v );  // Store 2A axis V_PLUS in v
mm_op15_word(v, a, n, 1, work);  // multiply v_plus with g
mm_reduce_vector_vp(0, v, mode, r, work); // reduce V_PLUS * g  

mm_op15_store_axis(V_MINUS, v );  // Store 2A axis V_MINUS in v
mm_op15_word(v, a, n, 1, work);  // multiply v_minus with g
mm_reduce_vector_vm(0,v, r, work); // reduce V_MINUS * g    

mm_order_load_vector(v);         // load the order vector v_1
mm_op15_word(v, a, n, 1, work);  // multiply v_1 with g 
len = mm_reduce_vector_v1(v, r, work);  // reduce v_1 vector

// Now buffer ``r`` contains the reduced word of length ``len``
// equal to the element ``g`` of the monster group.   

Functions

uint32_t mm_reduce_2A_axis_type(uint_mmv_t *v)

Return type of a 2A axis in rep of monster.

Let v be a vector in the 196884-dimensional representation of the monster group modulo 15, encoded as described in section Description of the mmgroup.mm<p> extensions in the description of the C interface.

If v is a 2A axis then the function computes the type of the 2A axis. Each 2A axis corresponds uniquely to a 2A involution \(x\) in the monster. Let \(z\) be the central involution in the subgroup \(G_{x0}\) of the monster. Then the type of the 2A axis v is the class of the product \(xz\) in the monster, which has one of the values 2A, 2B, 4A, 4B, 4C, 6A, 6C, 6F, 8B, 10A, 10B, 12C

The function returns a value n * 2**28 + k * 2**24 + v. Here n is the number of the class, e.g. n == 6 for class 6C, k encodes the letter in the name of the class (‘A’ = 1, ‘B’ = 2, …) e.g. k = 3 for class 6C.

Output v is either 0 or a vector in the Leech lattice mod 2 found during the computation. Here a vector of type 2 is returned for classes 2A, 6A; and a vector of type 4 is returned for class 4A.

Caution:

This is a quick disambiguation of the type of a 2A axis. The function may return any axis type if v is not a 2A axis.

int32_t mm_reduce_analyze_2A_axis(uint_mmv_t *v, uint32_t *r)

Analyze a 2A axis in rep of monster.

Let v be a vector in the 196884-dimensional representation of the monster group modulo 15, encoded as described in section Description of the mmgroup.mm<p> extensions in the description of the C interface.

If v is a 2A axis then the function analyzes the type of the 2A axis. Each 2A axis corresponds uniquely to a 2A involution \(x\) in the monster. Let \(z\) be the central involution in the subgroup \(G_{x0}\) of the monster. Then the type of the 2A axis v is the class of the product \(xz\) in the monster, which has one of the values 2A, 2B, 4A, 4B, 4C, 6A, 6C, 6F, 8B, 10A, 10B, 12C

The function returns 0 in case of success and a negative value value if it detects an error.

Buffer r should be at least 896 bytes long. It will contain the result.

Details are yet to be documented!!!

Caution:

This is a quick analyis of a 2A axis. The function returns garbage if v is not a 2A axis.

uint32_t mm_reduce_find_type4(uint32_t *v, uint32_t n, uint32_t v2)

Select a type-4 vector from list of Leech lattice vectors

Let v be an array of n vectors in the Leech lattice modulo 2 in Leech lattice encoding.

If parameter v2 is 0 then the function returns the least vector of type 4 in the list v with repect to a certain ordering. If list v contains no vector of type 4 then the function returns 0.

Here subtypes are ordered as follows: [48, 40, (42, 44), 46, 43]. So e.g. a vector of subtype 40 is selected in favor of a vactor of subtype 46. We do not distinguish between substypes 42 and 44. From all vectors of the same subtype (equalizing subtypes 42 and 44) we return the smallest vector in the list in the natural order.

If v2 is nonzero then we accept only vectors v1 in the list v with type(v1) = 4 and type(v1 + v2) = 2.

The list v is destroyed. More precisely, all entries of v are masked with 0xffffff and subjected to an (undocumented) permutation.

int32_t mm_reduce_transform_v4(uint_mmv_t *v, uint32_t v4, uint32_t *target_axes, uint32_t *r, uint_mmv_t *work)

Transform a 2A axis to a ‘better’ 2A axis.

The function tries to transform the 2A axis \(v\) given by parameter v to a better axis. Therefore it first applies a transformation \(g \in G_{x0}\) given by parameter v4. Here v4 must be a vector of type 4 in the Leech lattice mod 2 in Leech lattice encoding. Then \(g\) is the transformation mapping v4 to the standard type 4-vector Omega as computed by function gen_leech2_reduce_type4.

In the next step this function tries to apply the transfomations \(\tau^e\) for \(e = 1,2\) (in that order) until the type of the axis \(v g \tau^e\) is in a set of types given by parameter target_axes. Here target_axes must be an array of length 2 containing the feasible axis types, encoded as described in function mm_reduce_2A_axis_type. An unused entry in array target_axes should be set to 0xffffffff.

If a suitable transformed axis \(v g \tau^e\) has been found then the function stores the element \(g \tau^e\) in the array referred by r; and it returns the length of the data in the array r. Then it also overwrites the axis in buffer v with the transformed axis.

The function returns a negative value in case of any error, e.g. if no suitable transformed axis \(v\) has been found; then buffers v and r will contain garbage.

If the original axis \(v\) is orthogonal to the standard 2A axis then the transformed axis is also orthogonal to that axis.

Buffer r must have size at least 7. The function requires a work buffer work of the same type and size as parameter v.

int32_t mm_reduce_load_axis(uint_mmv_t *v, uint32_t s)

Load the 2A axis \(v^+\) or \(v^-\) into a vector in \(\rho_{15}\).

The function loads the 2A axis \(v^+\) (in case s == 0) or \(v^-\) (in case s == 1) in the vector referred by v. Other values of s are illegal. Vector v will store an element of the representation \(\rho_{15}\) of the monster group as described in the header of this file. Vectors \(v^+\) and \(v^-\) are defined in [Sey22], Section 6.

int32_t mm_reduce_map_axis(uint32_t *vt, uint_mmv_t *v, uint32_t *a, uint32_t n, uint_mmv_t *work)

Transform a 2A axis with an element of the Monster group.

Here we encode a 2A axis \(v\) as follows. It may be encoded as an element of the group \(Q_{x0}\) in the integer referred by vt. In case *vt = 0 we encode \(v\) as a vector of the representation \(\rho_{15}\) of the Monster in the array v. In case *vt != 0 the value *vt must be a short element (corresponding to a type-2 vector in \(\Lambda/2\Lambda\)) of the group \(Q_{x0}\), in Leech lattice encoding. v is ignored in case *vt != 0.

Let \(g\) be the element of the monster given as a word of generators in the buffer a of length n. Then the function computes \(v' = v \cdot g\) and stores \(v'\) in *vt (if possible), or in v. In last case it sets *vt to zero.

The function returns 0 in case of success an a negative value in case of failure. It requires a work buffer work of the same type and size as parameter v.

int32_t mm_reduce_vector_vp(uint32_t *vt, uint_mmv_t *v, uint32_t mode, uint32_t *r, uint_mmv_t *work)

Step 1 of the reduction of an element of the monster.

Here inputs vt and v of this function encode a 2A axis \(v\) as described in function mm_reduce_map_axis. Here \(v\) is considered as an image \(v = v^+ \cdot g\) of \(v^+\) under the operation of an (unknown) element \(g\) of the Monster group.

The function computes an element \(h\) of the monster group with \(v \cdot h = v^+\). We use the method in [Sey22], Section 6 ff. for computing \(h\).

The function stores \(h\) as a word of generators in the array r as described in the header of this file and returns the length of this word. That word is augmented by some atoms acting as comments

The buffer referred by r should have length at least 128.

Parameter mode is yet to be documented! In the current version is should be set to 1. This means that the function will strictly follow the reduction method in ibid.

A negative return value indicates a fatal error.

Parameter work must refer to a work buffer of size at least MM_OP15_LEN_V.

More details are yet to be documented!!!!

int32_t mm_reduce_vector_shortcut(uint32_t stage, uint32_t mode, uint32_t axis, uint32_t *r)

Simulate steps of the reduction of an element of the monster.

Yet to be documented!!!!

int32_t mm_reduce_vector_vm(uint32_t *vt, uint_mmv_t *v, uint32_t *r, uint_mmv_t *work)

Step 2 of the reduction of an element of the monster.

This function should be applied immediately after function mm_reduce_vector_vp.

Here inputs vt and v of this function encode a 2A axis \(v\) as described in function mm_reduce_map_axis. Here \(v\) is considered as an image \(v = v^+ \cdot g\) of \(v^+\) under the operation of an (unknown) element \(g\) of the Monster group.

The function computes an element \(h\) of the monster group with \(v \cdot h = v^-\). We use the method in [Sey22], Section 8, for computing \(h\). Note that \(h\) preserves the 2A axis \(v^+\).

The function appends \(h\) as a word of generators to the array r as described in the header of this file and returns the length of the data in the array r. That information in buffer r is augmented by some atoms acting as comments.

The buffer referred by r should have length at least 128.

A negative return value indicates a fatal error.

Parameter work must refer to a work buffer of size at least MM_OP15_LEN_V.

More details are yet to be documented!!!!

int32_t mm_reduce_vector_v1(uint_mmv_t *v, uint32_t *r, uint_mmv_t *work)

Step 3 of the reduction of an element of the monster.

This function should be applied immediately after functions mm_reduce_vector_vp and mm_reduce_vector_vm.

Here input v of this function must refer to the image of the precomputed order vector \(v_1 \in \rho_{15}\) under the operation of the same element \(g\) of the monster group as described in the documentation of function mm_reduce_vector_vp.

The function computes an element \(h\) of the monster group with \(v \cdot h = v_1\). We use the method in [Sey22], Appendix A. for computing \(h\). Note that \(h\) preserves the 2A axes \(v^+\) and \(v^-\).

The function appends \(h\) as a word of generators to the array r as described in the header of this file and returns the length of the data in the array r. That information in buffer r is augmented by some atoms acting as comments.

Finally, the function computes a word in the buffer that is equal to the element \(g\) of the monster group and stores that element in the buffer r. The function returns the length of data in buffer r.

The buffer referred by r should have length at least 128.

A negative return value indicates a fatal error.

Parameter work must refer to a work buffer of size at least MM_OP15_LEN_V.

static inline int32_t mm_reduce_vector_v1_mod3(uint_mmv_t *v, uint32_t *r, uint_mmv_t *work)

Variant of Step 3 of the reduction of an element of the monster.

This is a variant of function mm_reduce_vector_v1 that works with precomputed order vector \(v_1 \in \rho_{3}\) instead of an order vector in \(\rho_{15}\). Parameters and operation are the same as in function mm_reduce_vector_v1. But here input v of this function must refer to the image of the precomputed order vector \(v_1 \in \rho_{3}\) under the operation of the same element \(g\) of the monster group as described in the documentation of function mm_reduce_vector_vp.

The order vector \(v_1 \in \rho_{3}\) can be obtained by using the functions in module mm_vector_v1_mod3.c.

This function is faster than function mm_reduce_vector_v1. But the order vector in \(v_1 \in \rho_{3}\) may have a non-trivial centralizer in the Monster; so we cannot use it for checking equality of elements of the Monster.

The buffer referred by r should have length at least 128.

A negative return value indicates a fatal error.

Parameter work must refer to a work buffer of size at least MM_OP3_LEN_V.

static inline int32_t mm_reduce_vector_shorten(uint32_t *a, uint32_t n, uint32_t *r, uint_mmv_t *work)

Special case of Step 3 of reduction of an element of the monster.

This is a variant of function mm_reduce_vector_v1 that works in the special case when all prefixes of a word \(g\) in the Monster map the 2A-involutions \(v^+\) and \(v^-\) to 2A-involutions in the subgroup \(Q_{x0}\) of \(G_{x0}\). Here the mapping is done via conjuation. That condition holds e.g if all prefixes of the word \(g\) are in \(\in N_x \cdot G_{x0}\).

The function returns 0 in case of success and a negative value in case of failure. In case of success, this function is (at least) 100 times faster than function mm_reduce_vector_v1. So using this function leads to a substantial speedup when computing in the 2-local subgroups \(N_x\) or \(G_{x0}\) of the Monster. The user profits from this speedup without effort, since function mm_reduce_M calls function mm_reduce_vector_shorten when appropriate.

Here the original element \(g\) of the Monster to be reduced must be stored in the buffer a of length n as usual. Parameters r and work are as in function mm_reduce_vector_v1.

If function mm_reduce_vector_shorten returns a negative value then we may retry the final reduction of \(g\) with one of the functions mm_reduce_vector_v1 or mm_reduce_vector_v1_mod3.

int32_t mm_reduce_vector_incomplete(uint32_t *r)

Step finish incomplete reduction of an element of the monster.

This function should be applied immediately after function mm_reduce_vector_vp or after function mm_reduce_vector_vn.

The function outputs the word already stored in the buffer r computed by the functions mm_reduce_vector_vp and, possibly, mm_reduce_vector_vn and returns the length of the data in buffer r. Here we assume that subsequent calls to the functions in the sequence

mm_reduce_vector_vp, mm_reduce_vector_vn, mm_reduce_vector_v1,

that have not yet been called, would contribute nothing the the output assembled in buffer r.

The buffer referred by r should have length at least 128.

A negative return value indicates a fatal error.

More details are yet to be documented!!!!

int32_t mm_reduce_M(uint32_t *a, uint32_t n, uint32_t mode, uint32_t *r)

Reduce an element in the monster group.

This is the most important function in the project. It reduces an arbitrary element \(g\) of the monster to an element \(h\). The value of the element \(h\) depends on the value of \(g\) as an element of the monster group, but not of the representation of \(g\).

Let \(g\) be the element of the monster given as a word of generators in the buffer a of length n. Then the function computes the reduced element \(h\) of the monster with \(g = h\). We use the method in [Sey22] for computing \(h\).

The function stores \(h\) as a word of generators in the array r as described in the header of this file and returns the length of this word.

A negative return value indicates a fatal error.

The buffer referred by r should have length at least 128.

Parameter mode should usually be set to zero. It is interpreted as follows:

If bit 0 of mode is set then the reduction is strictly compatible with the reduction process described in [Sey22]. Otherwise we perform a more practical reduction. The latter reduction ensures e.g. that an element of the subgroup \(G_{x0}\) is always represented as a word in the generators of that subgroup. This feature greatly accelerates computations in the subgroup \(G_{x0}\).

Bit 1 of mode should be set for tests only. If this bit is set then we omit some simplifications of the input prior to the actual reduction.

uint32_t mm_reduce_set_order_vector_mod15(uint32_t mode)

Set a special reduction mode for tests.

The end user need not call this function.

In [Sey22] we show how to compute an ‘order vector’ \(v_1\) in the representation \(\rho_{15}\) of the Monster satisfying the following property:

The only element of the Monster fixing \(v_1\) is the neutral element of the Monster.

For verifying this property of \(v_1\) is suffices to use the methods in [LPWW98]. Thus by tracing the an image of \(v_1\) under an element \(g\) back to its preimage \(v_1\) we obtain a verification of a reduction process which depends on classical results only.

By default, the main reduction function mm_reduce_M in this module uses an ‘order vector’ in \(\rho_3\) which leads to a faster reduction process than obtained by using \(v_1\).

When calling function mm_reduce_set_order_vector_mod15 with parameter mode = 1 then in all subsequent calls to function mm_reduce_M we will use an ‘order vector’ \(v_1\) in \(\rho_{15}\) as desrcibed above; and we will verify that the ‘reduced’ form of an element \(g\) of the Monster maps \(v_1\) to the same vector as the original element \(g\) does.

This gives a tester the capability to do long calcuations in the Monster group that will be verified by using classical results without referring to the theory developed in [Sey22].

The user may switch back to the default behaviour by calling function mm_reduce_set_order_vector_mod15 with parameter mode = 0. The function returns the value of parameter mode passed in the previous call (or the default value 0 at first call).

C interface for file mm_shorten.c

This file contains function for reducing a word of generators of the Monster group. Here word reduction is done by computations in the subgroups \(G_{x0} = 2^{1+24}.\mbox{Co}_1\) and \(N_0 = 2^{2+11+22}.(\mbox{Mat}_{24} \times \mbox{Sym}_3)\). To some extent, we will also compute in the Frobenius group of order 21 generated by the generators \(\tau\) and \(\xi\) of the Monster.

Internally, we store a word a of generators of the Monster in a structure of type gt_word_type. Here the word a is split into subwords, where each subword is in the double coset \(G_{x0} N_0\). Such a subword is represented as a word of generators of \(G_{x0}\), followed by a power of the triality element \(\xi\). A subword is stored in a structure of type gt_subword_type.

Unless otherwise stated, a negative return value of a function is this module indicates an error.

Structures required for this module are defined in file mm_reduce.h.

The following code block demonstrates the reduction of a word with the functions in this module (ignoring error handling).

// Assume that the following buffer contains a word ``g``
// of length 10 of generators of the Monster group
#define INPUT_LENGTH 10
uint32_t in_buf[INPUT_LENGTH]; 
// Declare a sufficiently long output buffer
#define MAX_OUTPUT_LENGTH 20
uint32_t out_buf[MAX_OUTPUT_LENGTH];
// Define a reduction mode as specified in function gt_word_alloc
#define REDUCTION_MODE 1 
// Allocate a structure for reduction
gt_word_type* p_gt = gt_word_alloc(REDUCTION_MODE, NULL, 0);
// Enter the word ``g`` into that structure
gt_word_append(p_gt, in_buf, INPUT_LENGTH);
// Reduce the word in that structure
gt_word_reduce(p_gt);
// Store the reduced word in the output buffer and the actual
// length of that word in variable output_length
int32_t output_length;
output_length = gt_word_store(p_gt, out_buf, MAX_OUTPUT_LENGTH);
// Free the allocated structure
gt_word_free(p_gt);

Functions

void gt_subword_clear(gt_subword_type *p_gtsub)

Low-level function.

Set a subword to the empty subword.

gt_word_type *gt_word_alloc(uint32_t mode, void *p_buffer, size_t nbytes)

Create a structure of type gt_word_type

This function creates a valid structure of type gt_word_type and returns a pointer to that structure. The returned structure contains the empty word of generators of the Monster. If a buffer p_buffer of nbytes bytes length is given as a parameter then the structure may be ininitalized inside this buffer. We recommend nbytes >= 4096.

If p_buffer = NULL or nbytes is too small then a buffer is allocated via the C function malloc. You should always use the returned pointer as a pointer to a valid structure of type gt_word_type containing the neutral element of the Monster.

The structure referred by returned pointer will contain the circular doubly linked list with a single entry of type gt_subword_type. This entry is an end-of-file mark. So the whole word stored in that structure corresponds to the neutral element of the Monster group.

In any case function gt_word_free must be called to free all buffers allocated by this function and, possibly, by other function in this module applied to the pointer returned by this function.

Parameter mode specifies the reduction mode:

mode = 0 means no reduction. Here the user has to apply the low-level functions in this module to perform reduction. This mode is used for testing.

mode = 1 means the maximum possible reduction which is supported.

mode = 2 means a lazy mode of reduction. The idea is to reduce only in cases where we can expect a substantial decrease of the word length. This mode is faster than mode = 1.

This function returns NULL in case of failure, e.g. if the C function malloc`fails to allocate memory.

void gt_word_free(gt_word_type *p_gt)

Free a structure of type gt_word_type

This function frees the memory allocated to a structure of type gt_word_type created by function gt_word_alloc. Ultimately, it must be applied to all pointers created by function gt_word_alloc in order to avoid memory leaks. It is safe to apply function gt_word_free to the NULL pointer.

void gt_word_clear(gt_word_type *p_gt)

Set content of the structure *p_gt to the empty word.

This function sets the value of the structure of type gt_word_type referred by p_gt to the neutral element of the Monster. It also frees all allocated memory blocks referred by p_gt, except for the block p_gt itself.

int32_t gt_word_insert(gt_word_type *p_gt)

Low-level function.

Insert an empty subword after the current subword pf *p_gt and set the position of the current subword to the new subword.

Return a negative value if out of memory.

int32_t gt_word_delete(gt_word_type *p_gt)

Low-level function.

Delete the current subword, and set the position of the current subword to its predecessor.

Return a negative value on an attempt to delete an EOF mark.

int32_t gt_word_seek(gt_word_type *p_gt, int32_t pos, uint32_t set)

Low-level function.

Move the pointer to the current subword to position ‘pos’ Her pos = 0 means the EOF mark if ‘set’ is True, and the position of the current subword otherwise. pos = i+1 means the subword after pos = i, pos = i-1 means node subword pos = i.

It is an error to move p_gt->p_node across the EOF node. When starting at the EOF node we may move forward or back until finding the EOF node once again, but not any further.

int32_t gt_word_append_sub_part(gt_word_type *p_gt, uint32_t *a, uint32_t n)

Low-level function.

Try to append a prefix of word ‘a’ of length ‘n’ to the current subword. Return number of atoms of ‘a’ sucessfully appended to that word. A negative return value inducates an error.

int32_t gt_word_reduce_sub(gt_word_type *p_gt, uint32_t sub_mode)

Low-level function.

Reduce current subword

sub_mode = 0: lazy reduction (just some word shortening)

sub_mode = 1: standard reduction

sub_mode = 2: as sub_mode 0, but move also prefix in N_x0 to previous subword

sub_mode = 3: as sub_mode 1, but move also prefix in N_x0 to previous subword

int32_t gt_word_rule_join(gt_word_type *p_gt)

Low-level reduction function.

Try to join current subwort p_gt->p_node with its predecessor. Return 1 and let p_gt->p_node point to first node being changed if the rule could be applied. Return 0 and do not change p_gt->p_node this is not the case. Return a negative number in case of a fatal error.

int32_t gt_word_rule_t_xi_t(gt_word_type *p_gt)

Low-level reduction function.

This function tries to apply a reduction rule:

\(\tau^{e_1} \xi^{e_2} \tau^{e_3} \rightarrow \xi^{e_4} \tau^{e_5} \xi^{e_6}\).

Such a rule exists for all \(0 < e_1, e_2, e_3 < 3\). This rule is applied to the subword p_gt->p_node and to its predecessor. Applying such a rule decreases the number of generators \(\tau^{\pm 1}\) in a word.

The function returns 1 and lets p_gt->p_node point to first node being changed if such rule could be applied. It returns 0 and does not change p_gt->p_node this is not the case. It returns a negative number in case of a fatal error.

To show the existence of suitable reduction rules as mentioned above we put \(a = \tau \xi^{-1}\). Then we verify that \(a\) has order 7 and that \(\tau^{-1} a \tau = a^2\) holds. Therefore is suffices to verify that the group operations corresponding to these relations fix the ‘order vector’ in the representation \(\rho_{15}\) .

Since \(\tau\) has order 3, the group generated by \(\tau\) and \(a\) (and hence also the group generated by \(\tau\) and \(\xi\)) is visibly a Frobenius group of order 21. Finding suitable reduction rules in such a group is easy.

int32_t gt_word_append(gt_word_type *p_gt, uint32_t *a, uint32_t n)

Append word of generators to strcture *p_gt

Append the word a of length n to the word in the buffer refered by p_gt.

A negative return value inducates an error.

At exit, pointer p_gt->p_node will point to the EOF mark.

uint32_t gt_word_n_subwords(uint32_t *a, uint32_t n)

Return number of subwords required to store element of the Monster.

Return (an upper bound for) the number of subwords in a doubly linked list of type gt_word_type required to store the word of generators of the Monster of length n referred by a.

int32_t gt_word_length(gt_word_type *p_gt)

Return the length of the word stored in *p_gt.

int32_t gt_word_reduce(gt_word_type *p_gt)

Reduce the word stored in *p_gt.

The function reduces the word \(g\) stored in the structure *p_gt. The mode of reduction depends on the parameter mode passed to function gt_word_alloc when creating that structure. The function returns:

0 if \(g\) is not known to be in any subgroup of the Monster

4 if \(g\) is known to be in \(G_{x0} \cdot N_0\)

5 if \(g\) is known to be in \(N_0\)

6 if \(g\) is known to be in \(G_{x0}\)

7 if \(g\) is known to be in \(N_{x0} = G_{x0} \cap N_0\)

A negative value in case of failure.

If the return value is > 0 (i.e. if we know \(g \in G_{x0} \cdot N_0\)) then we also have performed a full reduction of the result.

int32_t gt_word_store(gt_word_type *p_gt, uint32_t *pa, uint32_t maxlen)

Store the word in *p_gt in an array.

The function stores the word in *p_gt in the array a (of length maxlen) referred by pa. It returns the actual length nof the word stored in the array a.

The function returns a negative value in case of failure, e.g. if n > maxlen.

int32_t gt_word_to_mm_compress(gt_word_type *p_gt, mm_compress_type *p_c)

Compress word in *p_gt to strcuture of type mm_compress_type

Yet to be tested and documented!!!

int32_t gt_word_compress(gt_word_type *p_gt, uint64_t *p_n)

Compress word in *p_gt to a 256-bit integer``.

Yet to be tested and documented!!!

int32_t gt_word_shorten(uint32_t *g, uint32_t n, uint32_t *g1, uint32_t n1max, uint32_t mode)

Reduce a word of generators of the Monster group.

Let \(g\) be the word of the monster group of length n stored in the array g. The function reduces the word \(g\) and stores the reduced word in the buffer g1 of length n1max. It returns the actual length n1 of the reduced word.

Internally, the function uses a buffer of type gt_word_type for reduction. Parameter mode controls the reduction mode; it is specified as in function gt_word_alloc.

The function returns a negative value in case of failure, e.g. if n1 > n1max.

C interface for file mm_compress.c

This file contains functions for compressing a word of generators of the Monster group to an integer.

TODO: Documentation yet missing!

Functions

void mm_compress_pc_init(mm_compress_type *pc, uint32_t back)

yet to be documented

int32_t mm_compress_pc_add_nx(mm_compress_type *pc, uint32_t *m, uint32_t len)

yet to be documented

int32_t mm_compress_pc_add_type2(mm_compress_type *pc, uint32_t c)

yet to be documented

int32_t mm_compress_pc_add_type4(mm_compress_type *pc, uint32_t c)

yet to be documented

int32_t mm_compress_pc_add_t(mm_compress_type *pc, uint32_t t)

yet to be documented

int32_t mm_compress_pc(mm_compress_type *pc, uint64_t *p_n)

yet to be documented

int32_t mm_compress_pc_expand_int(uint64_t *p_n, uint32_t *m)

yet to be documented

C interface for file mm_vector_v1_mod3.c

File mm_vector_v1_mod3 contains a precomuted vector v1_mod3 of the representation of the monster group (mod 3). This vector can be used for obtaining an (unknown) element \(g\) of the subgroup \(G_{x0}\) of the monster from and image of that vector under \(g\).

So the functions in this module are similar to those in module mm_order.c, but faster.

Note that we cannot check membership in \(G_{x0}\) with the functions in this module!

This module is yet a stub!!!

Functions

void mm_order_load_vector_v1_mod3(uint_mmv_t *p_dest)

Load order vector from tables to a buffer.

The function stores the precomputed vector v1_mod3 into the array referred by p_dest. That array must must be sufficiently long to store a vector of the representation \(\rho_{3}\).

int32_t mm_order_compare_v1_mod3(uint_mmv_t *v)

Compare vector with precomputed order vector.

The function compares the vector \(v\) in the representation \(\rho_{3}\) of the monster group referred by v with \(v_1\) , where \(v_1\) is the precomute vector v1_mod3.

The function returns 0 in case of equality and 1 otherwise. It destroys the vector in the buffer v.

int32_t mm_order_find_Gx0_via_v1_mod3(uint_mmv_t *v, uint32_t *g)

Find an element \(g\) of the subgroup \(G_{x0}\).

Yet to be documented!!!

Shared libraries and dependencies between Cython extensions

For each Cython extension the relevant C functions are implemented in a shared library (or a DLL in Windows). Names of the corresponding DLLs in windows are given in the following table:

Shared libraries implementing Cython extensions

Cython extension

Implemented in shared library

mm_reduce

mmgroup_reduce

mm_op

mmgroup_mm_op

clifford12

mmgroup_mat24

generators

mmgroup_mat24

mat24

mmgroup_mat24

In Linux, the name of each shared library is prefixed by the string lib. So the Windows DLL mmgroup_mm_op.dll corresponds to the Linux shared library libmmgroup_mm_op.so.

Cython extensions depend on each other as shown in the following paragraph:

mm_reduce
   + mm_op
      + clifford12
         + generators
             + mat24