def test_gen_xi_ref(gen_xi_class, ref, ntests=200): xi, xi_ref = gen_xi_class.gen_xi_op_xi, ref.gen_xi_op_xi x_old = 0 for x in xi_samples(ntests): for i in range(0, 3): res, ref_ = xi(x, i), xi_ref(x, i) assert res == ref_, lmap(hex, [i, x, res, ref_]) assert gen_xi_class.gen_xi_g_gray(x) == ref.gen_xi_g_gray(x) assert gen_xi_class.gen_xi_w2_gray(x) == ref.gen_xi_w2_gray(x) assert gen_xi_class.gen_xi_g_cocode(x) == ref.gen_xi_g_cocode(x) assert gen_xi_class.gen_xi_w2_cocode(x) == ref.gen_xi_w2_cocode(x) x_old = x xi_short = gen_xi_class.gen_xi_op_xi_short to_leech = gen_xi_class.gen_xi_short_to_leech to_short = gen_xi_class.gen_xi_leech_to_short xi_short_ref = ref.gen_xi_op_xi_short to_leech_ref = ref.gen_xi_short_to_leech for x in short_samples(ntests): xl = to_leech_ref(x) assert to_leech(x) == xl, lmap(hex, [x, xl, to_leech(x)]) x_norm = normalize_short_vector_code(x) assert to_short(xl) == x_norm, lmap(hex, [xl, x, x_norm, to_short(xl)]) for i in range(1, 3): ref = xi_short_ref(x, i) assert xi_short(x, i) == ref, lmap(hex, [i, x, xi_short(x, i), ref]) pass
def better_cocode_basis(cls, verbose=False): """Returns a good basis of a traversal of the Golay cocode. The asserted properties of the cocode basis are as in the main comment of module mat24.py. """ def e(i, a): v = [0] * 6 v[i] = a return v hexa = lmap(e, [2, 2, 1, 1, 0, 0], [2, 1, 2, 1, 2, 1]) #basis = [0x111111,0x011111,0x110000,0x101000,0x100100,0x100010,] basis = [ 0x111111, 0x1, 0x110, 0x1010, 0x10010, 0x100010, ] basis += lmap(cls.hexacode_vector_to_mog, hexa) basis = basis[2:] + basis[:2] cls.check_cocode_basis(basis) return basis
def test_gen_xi_short(gen_xi_class, ntests): xi = gen_xi_class.gen_xi_op_xi xi_short = gen_xi_class.gen_xi_op_xi_short to_leech = gen_xi_class.gen_xi_short_to_leech to_short = gen_xi_class.gen_xi_leech_to_short for x in short_samples(ntests): xl = to_leech(x) x_norm = normalize_short_vector_code(x) assert to_short(xl) == x_norm, lmap(hex, [x, xl, x_norm, to_short(xl)]) for i in range(1, 3): continue y = xi_short(x, i) assert y, lmap(hex, [x, xl, xi(xl, i), y]) ref = to_short(xi(xl, i)) assert y == ref, lmap(hex, [x, xl, xi(xl, i), ref, y])
def make_aux_table(self): """Prepare table for casting out the i-th column in the MOG Let col[i], i=0,...,5 is the vector that casts out the entries in the i-th column in the MOG. col[i] is computed from the basis vectors in self.basis with function and_basis(). self.bw_aux_table is a table with 6 entries, where entry i is used to compute col[i] by member function make_aux_table. """ blackwhite = self.blackwhite lbw = len(blackwhite) bw_combinations = [None] * 6 bw_dict = dict(zip([0xf << (4 * i) for i in range(6)], range(6))) done = False for k in range(1, lbw + 1): for sgn in iter_ascending_bitweight((1 << k) - 1): signs = [(sgn >> i) & 1 for i in range(k)] for pos in lmap(bits2list, iter_bitweight(k, 1 << lbw)): basispos = [blackwhite[i] for i in pos] z = list(zip(basispos, signs)) res = self.and_basis(z) try: bw_combinations[bw_dict[res]] = z del bw_dict[res] done = not None in bw_combinations if done: break except: pass if done: break if done: break self.bw_aux_table = bw_combinations if self.verbose: print("bw_comb", bw_combinations)
def test_inverse(): print("Testing matrix inversion ...") MAXDIM_INVERSE_TEST = 20 NSIGMA = 4.0 elementary_testcases = [([3, 2], [3, 2]), ([2, 4, 1], [4, 1, 2])] for a, b in elementary_testcases: assert bit_mat_inverse(a) == b if a != b: assert bit_mat_inverse(b) == a n_cases = [0] * (MAXDIM_INVERSE_TEST + 1) n_ok = [0.0] * (MAXDIM_INVERSE_TEST + 1) for a in inverse_testcases(): ok, n = False, len(a) if n > MAXDIM_INVERSE_TEST: continue n_cases[n] += 1 try: inv = bit_mat_inverse(a) ok = True n_ok[n] += 1 except: pass if ok: unit = [1 << i for i in range(n)] prod = bit_mat_mul(a, inv) assert prod == unit, (lmap(hex, a), lmap(hex, inv), lmap(hex, prod)) cases_displayed = [10] for n in range(1, MAXDIM_INVERSE_TEST): if n_cases[n] > 10: import math N, N_ok = n_cases[n], n_ok[n] p = 1.0 for i in range(1, n + 1): p *= 1.0 - 0.5**i mu = N * p sigma = math.sqrt(N * p * (1.0 - p)) if n in cases_displayed: print( "Dim. %d, %d cases, ratio of invertibles: %.3f, expected: %.3f+-%.3f" % (n, N, N_ok / N, p, sigma / N)) assert abs(mu - N_ok) < NSIGMA * sigma, (n, p, mu, sigma, N, N_ok) print("Matrix inversion test passed")
def historical_check(cls, GolayBasis, generate=False): """Checks the Golay code basis against basis a stored older basis The basis is checked against basis cls.HISTORICAL_REVERSED_BASIS. If 'generate' is set, the basis cls.HISTORICAL_REVERSED_BASIS is recomputed by function make_lex_golay_code_basis(). """ ref = lmap(reverse24, cls.HISTORICAL_REVERSED_BASIS) ref = pivot_binary_high(ref)[0] assert GolayBasis == ref if generate: assert HISTORICAL_REVERSED_BASIS == make_lex_golay_code_basis()
def augment_colored_basis(self): """Augment self.basis by addding some vectors to the basis. The augmented vectors are XOR combinations of the basis vectors. The following tables are computed: self.basis_augment is a list of entries, where each entry represents an augmented basis vector as a list of the indices of basis vectors. The corresponding basis vectors are to be XORed to obtain the new basis vector. self.col_aux_table is a list of 5 entries. Here entry i is the instruction how to compute the i-th auxiliary vector aux[i] from the basis vectors with member function and_basis. self.out_table is a table of 24 auxiliary vectors. For obtaining the i-th singleteon we have to compute col[i >> 2] & aux[self.out_table[i]] . Here col[i] is the vector that casts out the entries in the i-th column in the MOG. A table for computing col[i] is prepared in member function make_aux_table. """ colored = self.colored basis_list = [] value_list = [] colored_6 = [] self.basis_augment = [] done = False for pos in lmap(bits2list, iter_ascending_bitweight(1 << len(colored))): basispos = [colored[i] for i in pos] value = reduce(__xor__, [self.basis[p] for p in basispos], 0) if bw24(value) == 12: basis_list.append(basispos) value_list.append(value) for i, val in enumerate(value_list): if bw24(value ^ val) == 12: for j in [i, -1]: b, v = basis_list[j], value_list[j] if len(b) > 1: self.basis_augment.append(b) b = [len(self.basis)] self.basis.append(v) self.colored.append(b) colored_6.append(b[0]) done = True break if done: break c0, c1 = colored_6[0], colored_6[1] self.col_aux_table = [[(self.odd_v, 0)], [(self.odd_v, 1)], [(c0, 0), (c1, 0)], [(c0, 0), (c1, 1)], [(c0, 1), (c1, 0)]] col_aux_values = lmap(self.and_basis, self.col_aux_table) self.out_table = [] for i in range(24): mask = 0xf << (i & -4) for j, v in enumerate(col_aux_values): if v & mask == 1 << i: self.out_table.append(j) break assert len(self.out_table) == 24 if self.verbose: print("augmented basis for Golay code to perm rep of Mat24") print(lmap(hex, self.basis)) print("augment:", self.basis_augment, "col", colored_6) print("col_aux_table:", self.col_aux_table) print("values:", [hex(x & 0xffffff) for x in col_aux_values]) print("t_out:", self.out_table)
class MM_Const(MM_Basics): """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``. """ # Names of the constants to be stored in an integer for each p entries = [ "LOG_INT_FIELDS", "INT_FIELDS", "LOG_FIELD_BITS", "FIELD_BITS", "P_BITS", ] primes = [(1 << k) - 1 for k in range(2, 9)] # list of all p lengths = [0] * len(entries) # list of max bit length of constants for p in primes: data = MM_Basics.sizes(p) for i, name in enumerate(entries): lengths[i] |= data[name] lengths = lmap(bitlen, lengths) #print ("SUM MM_Const bitlengths", sum(lengths)) assert sum(lengths) <= 32 pos = {} # pos is a dictionary of shape start = 0 # {entry_name:(start_pos,max_value)} for i, length in enumerate(lengths): pos[entries[i]] = (start, (1 << length) - 1) start += length deBruijnMult = 0xe8 # deBruijn sequence table = [0] * 8 # This will be the table of constants for p in primes: # Assemble that table data = MM_Basics.sizes(p) index = (((p + 1) * deBruijnMult) >> 8) & 7 # scramble indices of # table access via deBruijn sequence value = 0 for entry, (shift, mask) in pos.items(): # assemble entry for p assert 0 <= data[entry] <= mask value += data[entry] << shift table[index] = value # store entry for p in table #print("pos", pos) #rint("tbl", lmap(hex,table)) T_NAME = "MMV_CONST_TAB" # python name of contant table F_NAME = "MMV_CONST" # function name "MMV_CONST" LOAD_F_NAME = "MMV_LOAD_CONST" # directive name "MMV_LOAD_CONST" # Some more constants are definded as dividend/INT_FIELDS # with dividends for contant names given by: dividend = { "V64_INTS": 64, "V24_INTS": 32, "MMV_INTS": MM_Basics.MMV_ENTRIES, } def __init__(self): new_tables = { self.T_NAME: self.table, "P_LIST": config.PRIMES, "MMV_CONST": UserFormat(self.f, "ss"), "smask": UserFormat(smask, fmt=c_hex), } self.tables = self.tables.copy() self.tables.update(new_tables) self.directives = { "MMV_LOAD_CONST": UserDirective(self.gen_get_const_table, "ss", "NAMES"), } for name in [ "P", "P_BITS", "FIELD_BITS", "LOG_FIELD_BITS", "INT_FIELDS", "LOG_INT_FIELDS", "V24_INTS", "LOG_V24_INTS", "V24_INTS_USED", "V64_INTS", "LOG_V64_INTS", ]: f = partial(_attr_from_table, name) setattr(self, name, f) self.tables[name] = UserFormat(f, "i") @classmethod def gen_get_const_table(cls, names, p, const_p): """Code generating method for directive MMV_LOAD_CONST According to rules of class TableGenerator the first argument of this method may be an implicit dictionary that translates the python name of the table of constants to the C name of that table. So names[cls.T_NAME] is the appropriate C name. We calculate the index for the given input p and load the entry of that table corresponding to p (which is an integer) to the destination given by const_p. """ s = "// Store constant table for {p} to {const_p}\n".format( const_p=const_p, p=p) s += "{c} = {t}[(((({p}) + 1) * {mul}) >> 8) & 7];\n".format( c=const_p, p=p, t=names[cls.T_NAME], mul=cls.deBruijnMult) return s @classmethod def gen_get_const_expr(cls, const_name, const_p): """General code generating method for function MMV_CONST It generates code that extracts the constant with name const_name from the entry const_p for a specific p. """ sh, mask = cls.pos[const_name] s = "(({t}{sh}) & {mask})".format(t=const_p, mask=mask, sh=" >> " + str(sh) if sh else "") return s @classmethod def gen_div_int_fields(cls, const_name, const_p): """Code generating for constants with names in self.dividend All these constants are defined as dividend/INT_FIELDS. The dividents for these contant names are taken from dictionary cls.dividend. The constant INT_FIELDS is calculated by method gen_get_const_expr() of this class. """ n_entries = cls.dividend[const_name] log_ = cls.gen_get_const_expr("LOG_INT_FIELDS", const_p) return "({0} >> {1})".format(n_entries, log_) @classmethod def f(cls, name, *args): """Code generating method for function MMV_CONST It generates code that returns the constant with given 'name'. """ try: return cls.gen_div_int_fields(name, *args) except: return cls.gen_get_const_expr(name, *args)