def ref_gen_leech2_reduce_n(v, verbose=0): vtype = gen_leech2_subtype(v) subtype = vtype & 0xf out = [] if subtype & 1: coc = (v ^ mat24.ploop_theta(v >> 12)) & 0xfff syn = mat24.cocode_syndrome(coc) src = mat24.vect_to_list(syn, mat24.bw24(syn)) assert len(src) in [1, 3] lst = [1, 2, 3] if subtype == 3 else [0] apply_perm(v, src, lst, len(src), out) v = gen_leech2_op_atom(v, out[0]) out.append(0xC0000000 + ((v >> 12) & 0x7ff)) v = gen_leech2_op_atom(v, out[1]) out.append(0xB0000000 + ((v >> 13) & 0x800)) elif subtype == 6: gv = (v >> 12) & 0xfff vect = mat24.gcode_to_vect(gv) src = mat24.vect_to_list(vect, mat24.bw24(vect)) assert len(src) == 12 dest = STD_DODECAD if (vtype == 0x36): coc = (v ^ mat24.ploop_theta(v >> 12)) & 0xfff w = mat24.bw24(mat24.cocode_as_subdodecad(coc, gv)) if w & 2: dest = CPL_DODECAD pi = mat24.perm_from_dodecads(dest, src) out.append(0xA0000000 + mat24.perm_to_m24num(pi)) op_y_x(v, TABLE_DODECAD, out) elif subtype in [2, 4]: if vtype == 0x34: find_octad_permutation_odd(v, out) else: find_octad_permutation(v, out) op_y_x(v, TABLE_OCTAD, out) elif subtype in [0, 8]: if ((v & 0x7ff) == 0): out.append(0xA0000000) out.append(0xC0000000) x = 0x800 if v & 0x1800000 == 0x1800000 else 0 out.append(0x90000000 + x) else: syn = mat24.cocode_syndrome(v & 0x7ff, 0) src = mat24.vect_to_list(syn, mat24.bw24(syn)) j = mat24.bw24(syn) & 2 lst, y0 = ([2, 3], 0x200) if j else ([0, 1, 2, 3], 0x400) apply_perm(v, src, lst, len(lst), out) v = gen_leech2_op_atom(v, out[0]) y = y0 if v & 0x800000 else 0 out.append(0xC0000000 + y) v = gen_leech2_op_atom(v, out[1]) x = y0 if v & 0x1000000 else 0 out.append(0xB0000000 + x) else: raise ValueError("Bad subtype " + hex(vtype)) assert len(out) == 3 return vtype, np.array(out, dtype=np.uint32)
def find_octad_permutation_odd(v, result, verbose=0): """ Find a suitable permutation for an octad. Similar to function ``find_octad_permutation`` in module ``mmgroup.dev.generators.gen_leech_reduce_n``. Here ``v, o, c`` are as in that function; but the scalar product of ``o`` and ``c`` must be 1. Apart from that operation is as in function ``find_octad_permutation``. We compute a permutation that maps octad ``o`` to the standard octad (0,1,2,3,4,5,6,7). If the cocode part ``c`` of ``v`` is not a suboctad of octad ``o`` then we map (one shortest representative of) ``c`` into the set (0,1,2,3,...7,8). """ coc = (v ^ mat24.ploop_theta(v >> 12)) & 0xfff w = mat24.gcode_weight(v >> 12) vect = mat24.gcode_to_vect((v ^ ((w & 4) << 21)) >> 12) src = mat24.vect_to_list(vect, 5) if mat24.cocode_weight(coc) == 4: sextet = mat24.cocode_to_sextet(coc) for i in range(0, 24, 4): syn = (1 << sextet[i]) | (1 << sextet[i + 1]) syn |= (1 << sextet[i + 2]) | (1 << sextet[i + 3]) special = syn & vect if special & (special - 1): break else: syn = mat24.cocode_syndrome(coc, 24) src.append(mat24.lsbit24(syn & ~vect)) return apply_perm(v, src, OCTAD_PLUS, 6, result, verbose)
def mmdata(self): """Return internal representation of corresponding monster element. That internal representation is returned as a numpy array of 32-bit integers. """ v = self.value x = (v >> 12) & 0x1fff d = (mat24.ploop_theta(v >> 12) ^ v) & 0xfff return np.array([0x30000000 + x, 0x10000000 + d], dtype=np.uint32)
def __and__(self, other): if isinstance(other, XLeech2): ov = other elif isinstance(other, (GCode, PLoop)): d = other.value & 0xfff ov = (d << 12) ^ mat24.ploop_theta(d) elif isinstance(ploop, Cocode): ov = ploop.value & 0xfff else: return NotImplemented return Parity(gen_leech2_scalprod(self.value, ov))
def isplit(self): r"""Split element into a product :math:`x_d \cdot x_\delta` The method returns a pair :math:`(d, \delta)` such that :math:`x_d \cdot x_\delta` is equal to the given element of :math:`Q_{x0}`. Here :math:`(d, \delta)` is returned as a pair of integers. """ v = self.value x = (v >> 12) & 0x1fff d = (mat24.ploop_theta(v >> 12) ^ v) & 0xfff return x, d
def apply_perm(v, src, dest, n, log_list, verbose=0): r"""Apply permutation to vector in Leech lattice mod 2. The function computes a permutation :math:`\pi` that maps the entries of the array ``src`` of length ``n`` to the entries of the array ``dest`` (of the same length) in the given order. Let :math:`v_2` be the vector in the Leech lattice mod 2 given by parameter ``v2``. The function returns :math:`v_2 x_\pi`. Parameter ``v2`` and the return value are given in Leech lattice encoding. Parameter ``p_res`` points to an integer where the function stores the element :math:`x_\pi` as a generator of the monster group as as described in file ``mmgroup_generators.h``. That generator is stored with tag ``MMGROUP_ATOM_TAG_IP`` so that we can compute the inverse of :math:`\pi` very efficiently. We compute the inverse of the lowest permutation (in lexical order) that maps ``dest[:n]`` to ``src[:n]``. """ res, p = mat24.perm_from_map(dest[:n], src[:n]) assert res > 0, (res, dest[:n], src[:n]) p_inv = mat24.inv_perm(p) p_num = mat24.perm_to_m24num(p) log_list.append(0xA0000000 + p_num) xd = (v >> 12) & 0xfff xdelta = (v ^ mat24.ploop_theta(xd)) & 0xfff m = mat24.perm_to_matrix(p_inv) xd = mat24.op_gcode_matrix(xd, m) xdelta = mat24.op_cocode_perm(xdelta, p_inv) v_out = (xd << 12) ^ xdelta ^ mat24.ploop_theta(xd) if verbose: print("Apply permutation (mapping v to gcode %s):\n%s" % (hex(mat24.gcode_to_vect(v_out >> 12)), p_inv)) return v_out
def xi_reduce_odd_type4(v, verbose=0): r"""Compute power of :math:`\xi` that reduces a vector ``v`` Let ``v`` be a vector in the Leech lattice mod 2 in Leech lattice encoding. We assume that ``v`` is of subtype 0x43. We compute an exponent ``e`` such that :math:`\xi^e` maps ``v`` to a vector of subtype 0x42 or 0x44. The function returns ``e`` if ``v`` is mapped to type 0x42 and ``0x100 + e`` if ``v`` is mapped to type 0x44. A negative return value indicates that no such exponent ``e`` exists. """ assert v & 0x800 # Error if cocode part ov v is even coc = (v ^ mat24.ploop_theta(v >> 12)) & 0xfff # Obtain cocode as table of bit fields of 5 bits tab = mat24.syndrome_table(coc & 0x7ff) # Check if the syndrome bits are in 3 different MOG columns. # We first XOR bit field i with bit field (i-1)(mod 3) # and then zero the lowest two bits of each bit field. tab ^= ((tab >> 5) & 0x3ff) ^ ((tab & 0x1f) << 10) tab &= 0x739c # Now all three bit fields are nonzero iff the syndrome bits # are in three differnt columns. Next add 32 - 4 to each bit # field in order to produce a carry if the field is nonzero. tab += 0x739c # Next we isolate the three carry bits tab &= 0x8420 # Return -1 if all carry bits are set, i.e all syndrome bits # are in different columns. if (tab == 0x8420): return -1 # Let scalar be the scalar product of the Golay part of v # with the standard tetrad \omega scalar = (v >> 22) & 1 # Exponent for element \xi of G_x0 is 2 - scalar exp = 2 - scalar if verbose: w = gen_leech2_op_atom(v, 0x60000000 + exp) print( "Reducing c = %s, subtype %s, t=%s, e=%d, to v = %s, subtype %s" % (hex(mat24.cocode_syndrome(coc, 0)), hex(gen_leech2_subtype(v)), hex(tab), exp, hex( mat24.gcode_to_vect(w >> 12)), hex(gen_leech2_subtype(w)))) # Return exponent for \xi in the lower 4 bits of the retrun value; # Return 0 in bit 8 if all syndrome bits of v are in the same # MOG column and 1 in bit 8 otherwise. return ((tab != 0) << 8) + exp
def MM_to_Q_x0(g): if check_mm_in_g_x0 is None: import_mm_order_functions() g = MM0('a', g.mmdata) if check_mm_in_g_x0(g) is None: raise ValueError(ERR_XL_IN_Q) g.reduce() res = 0 for atom in g.mmdata: tag = (atom >> 28) & 0x0f if res == 0 and tag == 3: res = ((atom & 0x1fff) << 12) ^ mat24.ploop_theta(atom) elif tag == 1: res ^= atom & 0xfff elif tag: raise ValueError(ERR_XL_IN_Q) return res
def find_octad_permutation(v, result, verbose=0): coc = (v ^ mat24.ploop_theta(v >> 12)) & 0xfff w = mat24.gcode_weight(v >> 12) vect = mat24.gcode_to_vect((v ^ ((w & 4) << 21)) >> 12) src = mat24.vect_to_list(vect, 5) syn = mat24.cocode_syndrome(coc, src[0]) & ~vect if syn: v5 = (1 << src[0]) | (1 << src[1]) | (1 << src[2]) v5 |= syn special = mat24.syndrome(v5, 24) src = src[:3] src.append(mat24.lsbit24(special & vect)) src.append(mat24.lsbit24(vect & ~(special | v5))) src.append(mat24.lsbit24(syn)) syn &= ~(1 << src[-1]) src.append(mat24.lsbit24(syn)) return apply_perm(v, src, OCTAD, len(src), result, verbose)
def iter_q(tag, r): if isinstance(r, Integral): e = r & 0x1ffffff elif isinstance(r, str): e = randint(r == 'n', 0x1ffffff) if len(r) != 1 or not d in "rn": raise ValueError(ERR_ATOM_VALUE % tag) else: try: e = r.as_Q_x0_atom() assert isinstance(e, Integral) except: raise TypeError(ERR_Q_x0) d = (e >> 12) & 0x1fff delta = (e ^ ploop_theta(d)) & 0xfff yield tag_dict['x'] + d yield tag_dict['d'] + delta
def theta(self, g2=None): """Return cocycle of Golay code words. The cocycle ``theta`` maps a pair of Golay code words to an integer modulo ``2``. It is linear in its second argument, so it may also be considered as a mapping from the Golay code to the cocode of the Golay code. :param g2: ``None`` (default) or another Golay code word of type |GCode|. :returns: * If ``g2`` is a code word of type |GCode|, we return the value ``g1.theta(g2) = theta(g1, g2)`` as a |Parity| object. * If ``g2`` is ``None`` (default), we return the value ``g1.theta() = theta(g1)`` as a |Cocode| object. Note that ``g1.theta(g2) == g1.theta() & g2 .`` The importance of the ``theta`` function comes from the fact that the multiplication of elements of the Parker loop is based on the cocycle. We embed the set of Golay code words into the set of positive Parker loop elements, which are instances of class |PLoop|. Let ``g1`` and ``g2`` be Golay code words of type |GCode|. Then ``PLoop(g1)`` and ``PLoop(g2)`` are the corresponding positive Parker loop elements, and ``g1.theta(g2)`` is an integer modulo ``2`` of type |Parity|. We have: ``PLoop(g1) * PLoop(g2) == (-1)**g1.theta(g2) * PLoop(g1 + g2) .`` """ th = mat24.ploop_theta(self.value) if g2 == None: complete_import() return Cocode(th) if isinstance(g2, GCode): return Parity(mat24.scalar_prod(g2.value, th)) err = "Types %s is illegal for method theta()" raise TypeError(err % type(g2))
def find_in_Q_x0(w): global err_in_g_x0_py w_x = mm_aux_mmv_extract_sparse_signs(15, w, ORDER_TAGS[OFS_TAGS_X:], 24) if w_x < 0: err_in_g_x0_py = 7 return None x = leech2matrix_solve_eqn(ORDER_TAGS[OFS_SOLVE_X:], 24, w_x) w_sign = ((x >> 12) & 0x7ff) ^ (x & 0x800) aa = np.array(ORDER_TAGS[OFS_TAG_SIGN:] ^ (w_sign << 14), dtype = np.uint32) sign = mm_aux_mmv_extract_sparse_signs(15, w, aa, 1) if sign < 0: err_in_g_x0_py = 8 return None x &= 0xffffff sign ^= uint64_parity(x & (x >> 12) & 0x7ff) x ^= (sign & 1) << 24 x ^= ploop_theta(x >> 12) #print("final x =", hex(x)) return x
def xi_reduce_dodecad(v, verbose=0): r"""Compute power of :math:`\xi` that reduces a vector ``v`` Let ``v`` be a vector in the Leech lattice mod 2 in Leech lattice encoding. We assume that ``v`` is of subtype 0x46. We compute an exponent ``e`` such that :math:`\xi^e` maps ``v`` to a vector of subtype 0x44. The function returns ``e`` if such an eponent exists. A negative return value indicates that no such exponent ``e`` exists. """ # Let ``vect`` be the Golay code part of v as a bit vector. vect = mat24.gcode_to_vect(v >> 12) # Set bit 4*i of s if all bits 4*i, 4*i+1, 4*i+2, 4*i+3 of # ``vect`` are equal, otherwise clear bit 4*i, for 0 <= i < 6. s1 = vect | (vect >> 2) s1 = s1 | (s1 >> 1) s0 = vect & (vect >> 2) s0 = s0 & (s0 >> 1) s = (s0 | ~s1) & 0x111111 # If the Golay code part of v is a docecad then either no or two # bits in s are set. Fail if no bit in s is set. if (s == 0): return -1 # Here two bits of s (in two different MOG columns) are set. # Set all bits in a MOG column if one bit is set in that column. # Thus the bits being set in s form a grey even octad. s *= 15 # Let 'coc' be the cocode part of v coc = v ^ mat24.ploop_theta(v >> 12) # Compute scalar product of octad s and ``coc`` in ``scalar`` tab = mat24.syndrome_table((mat24.recip_basis[0] ^ coc) & 0x7ff) scalar = s ^ (s >> (tab & 31)) ^ (s >> ((tab >> 5) & 31)) scalar ^= (s >> ((tab >> 10) & 31)) scalar &= 1 # The requested exponent is now equal to ``2 - scalar``. exp = 2 - scalar return exp
def __init__(self, value): if import_pending: complete_import() if isinstance(value, Integral): self.value = value & 0xfff elif isinstance(value, Cocode): self.value = value.value elif isinstance(value, GcVector): self.value = mat24.vect_to_cocode(value.value) elif isinstance(value, XLeech2): v = value.value self.value = (mat24.ploop_theta(v >> 12) ^ v) & 0xfff elif isinstance(value, str): if len(value) == 1 and value in "reo": self.value = randint(0, 0xfff) if "o" in value: self.value |= 0x800 if "e" in value: self.value &= 0x7ff else: raise ValueError(ERR_RAND % 'Cocode') else: vector = as_vector24(value) self.value = mat24.vect_to_cocode(vector)
def elem_to_word(elem, verbose=1): assert isinstance(elem, Xsp2_Co1) assert isinstance(verbose, int), verbose group = elem.group len_a = 0 a0 = np.zeros(16, dtype=np.uint32) img_Omega = elem.xsp_conjugate(0x800000) if verbose: print("Convert element g of G_x0 to word. Element is:") print(elem) print("g conjugates 0x800000 to %s" % hex(img_Omega)) len_a = gen_leech2_reduce_type4(img_Omega, a0) a0 = list(a0[:len_a]) elem_reduced = elem.copy().mul_data(a0) if verbose: print("Word w0 stabilizing Omega:") print(" ".join([hex(x) for x in a0])) print("After multiplication with w0, the element is:") print(elem_reduced) a1 = list(monomial_to_word(elem_reduced, verbose > 1)) elem_reduced = elem_reduced.mul_data(a1) x = elem_reduced.as_xsp() x ^= mat24.ploop_theta(x >> 12) a = [] if (x & 0x1fff000): a.append(0x30000000 + (x >> 12)) if (x & 0xfff): a.append(0x10000000 + (x & 0xfff)) a += [x ^ 0x80000000 for x in (a0 + a1)[::-1]] a = np.array(a, dtype=np.uint32) if verbose: print("Word is:\n " + str(MM.from_data(a))) return a
def value_from_ploop(ploop=0, cocode=None, *args): c = Cocode(cocode).ord if cocode else 0 if isinstance(ploop, Integral): d = ploop & 0x1ffffff elif isinstance(ploop, PLoop): d = ploop.value & 0x1fff d = (d << 12) ^ mat24.ploop_theta(d) elif isinstance(ploop, XLeech2): d = ploop.value elif isinstance(ploop, Cocode): d = ploop.value & 0xfff elif isinstance(ploop, AbstractMMGroupWord): d = MM_to_Q_x0(ploop) elif isinstance(ploop, str): if len(ploop) == 1 and ploop in "BCTXES": d = 0 a = tuple_to_sparse(0xff, ploop, cocode, *args) if len(a) == 1: a0 = a[0] d = mm_aux_index_sparse_to_leech2(a0) a0 &= 0xff if a0 == 0xfe: d ^= 0x1000000 elif a0 != 1: d = 0 if d: return d if ploop == "r": if cocode is None: return randint(0, 0x1ffffff) elif cocode in [0, 2, 3, 4]: return rand_xleech2_type(cocode) raise ValueError(ERR_XL_TUPLE) else: return TypeError(ERR_XL_TYPE % type(ploop)) return d ^ c
def leech2_start_type4(v): """Return subtype of a Leech lattice frame ``v`` used for reduction The function returns the subtype of a vector ``v`` of type 4 in the Leech lattice modulo 2. Parameter ``v2`` must be in Leech lattice encoding. The function returns the subtype of ``v`` that will be used for reduction in function ``gen_leech2_reduce_type4``. In that function we take care of the special case that ``v + v0`` is of type 2 for a specific short vector ``v0``. A simpler (but slower) implementation of thhis function is: If ``v ^ v0`` is of type 2 the return the subtype of ``v ^ v0``. Otherwise return the subtype of ``v``. The function returns 0 if ``v`` is equal to ``Omega`` and a negative value if ``v`` has not type 4. This is a refernece implementation for function ``gen_leech2_start_type4()`` in file ``gen_leech.c``. """ if v & 0x7ff800 == 0: # Then v or v + Omega is an even cocode element. # Return 0 if v == Omega and -1 if v == 0. if v & 0x7fffff == 0: return 0 if v & 0x800000 else -1 # Let w be the cocode weight. Return -2 if w == 2. if mat24.cocode_weight(v) != 4: return -2 # Here v has type 4. Let v23 be the standard type-2 vector. # Return 0x20 if v ^ v23 has type 2 and 0x40 otherwise. return 0x20 if mat24.cocode_weight(v ^ 0x200) == 2 else 0x40 if mat24.scalar_prod(v >> 12, v): # Then v has type 3 and we return -3 return -3 if v & 0x800: # Then the cocode word 'coc' of v is odd. coc = (v ^ mat24.ploop_theta(v >> 12)) & 0xfff syn = mat24.cocode_syndrome(coc) # If 'coc' has weight 1 then v is of type 2 and we return -2. if (syn & (syn - 1)) == 0: return -2 # Here v is of type 4. # Return 0x21 if v ^ v23 is of type 2 and 0x43 otherwise. if (syn & 0xc) == 0xc and (v & 0x200000) == 0: return 0x21 return 0x43 # Let w be the weight of Golay code part divided by 4 w = mat24.gcode_weight(v >> 12) if w == 3: # Then the Golay code part of v is a docecad and we return 0x46. return 0x46 # Here the Golay code part of v is a (possibly complemented) octad. # Add Omega to v if Golay part is a complemented octad. v ^= (w & 4) << 21 # Put w = 1 if that part is an octad and w = 0 otherwise. w = (w >> 1) & 1 # Let 'octad' be the octad in the Golay code word in vector rep. octad = mat24.gcode_to_vect(v >> 12) coc = v ^ mat24.ploop_theta(v >> 12) # cocode element of v # Return -2 if v is of type 2. sub = suboctad_type(octad, w, coc) if sub == 0: return -2 # Return 0x22 if v ^ v23 is shsort if suboctad_type(octad, w, coc ^ 0x200) == 0: return 0x22 # Otherwise return the subtype of v return 0x44 if sub & 2 else 0x42
def reduce_type4_std(v, verbose=0): r"""Map type-4 vector in Leech lattice to standard vector This is (almost) a python implementation of the C function ``gen_leech2_reduce_type4`` in file ``gen_leech.c``. Let ``v \in \Lambda / 2 \Lambda`` of type 4 be given by parameter ``v`` in Leech lattice encoding. Let ``Omega`` be the type- vector in the Leech lattice corresponding to the standard coordinate frame in the Leech lattice. Then the function constructs a ``g \in G_{x0}`` that maps ``v`` to ``Omega``. 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 4. We remark that the C function ``gen_leech2_reduce_type4`` treats certain type-4 vectors ``v`` in a special way as indicated in function ``reduce_type4``. """ if verbose: print("Transforming type-4 vector %s to Omega" % hex(v & 0x1ffffff)) vtype = gen_leech2_subtype(v) result = [] for _i in range(5): coc = (v ^ mat24.ploop_theta(v >> 12)) & 0xfff if verbose: vt = gen_leech2_subtype(v) coc_anchor = 0 if vt in [0x42, 0x44]: w = mat24.gcode_weight(v >> 12) vect = mat24.gcode_to_vect((v ^ ((w & 4) << 21)) >> 12) coc_anchor = mat24.lsbit24(vect) coc_syn = Cocode(coc).syndrome_list(coc_anchor) gcode = mat24.gcode_to_vect(v >> 12) print("Round %d, v = %s, subtype %s, gcode %s, cocode %s" % (_i, hex(v & 0xffffff), hex(vt), hex(gcode), coc_syn)) assert vtype == gen_leech2_subtype(v) if vtype == 0x48: if verbose: res = list(map(hex, result)) print("Transformation is\n", res) return np.array(result, dtype=np.uint32) elif vtype == 0x40: if v & 0x7ffbff: syn = mat24.cocode_syndrome(coc, 0) src = mat24.vect_to_list(syn, 4) v = apply_perm(v, src, LSTD, 4, result, verbose) #print("after type 40", hex(v), Cocode(v).syndrome(0)) exp = 2 - ((v >> 23) & 1) vtype = 0x48 elif vtype in [0x42, 0x44]: exp = xi_reduce_octad(v) if exp < 0: v = find_octad_permutation(v, result, verbose) exp = xi_reduce_octad(v) assert exp >= 0 vtype = 0x40 elif vtype == 0x46: exp = xi_reduce_dodecad(v, verbose) if exp < 0: vect = mat24.gcode_to_vect(v >> 12) src = mat24.vect_to_list(vect, 4) v = apply_perm(v, src, LSTD, len(src), result, verbose) exp = xi_reduce_dodecad(v, verbose) assert exp >= 0 vtype = 0x44 elif vtype == 0x43: exp = xi_reduce_odd_type4(v, verbose) if exp < 0: vect = mat24.gcode_to_vect(v >> 12) syn = mat24.cocode_syndrome(coc, 24) src = mat24.vect_to_list(syn, 3) #print("coc list", src) v = apply_perm(v, src, LSTD[1:], len(src), result, verbose) exp = xi_reduce_odd_type4(v, verbose) assert exp > 0 vtype = 0x42 + ((exp & 0x100) >> 7) exp &= 3 else: raise ValueError("WTF") if exp: exp = 0xE0000003 - exp v_old = v v = gen_leech2_op_atom(v, exp) assert v & 0xfe000000 == 0, (hex(v_old), hex(exp), hex(v)) result.append(exp) raise ValueError("WTF1")
def reduce_type2_ortho(v, verbose=0): r"""Map (orthgonal) short vector in Leech lattice to standard vector This is a python implementation of the C function ``gen_leech2_reduce_type2_ortho`` in file ``gen_leech.c``. 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 ``beta``. Here ``beta`` 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 ``beta'`` be the short vector in the Leech lattice propotional to ``e_2 + e_3``. Then the function constructs a ``g \in G_{x0}`` that maps ``v`` to ``beta'`` and fixes ``beta``. 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 ``beta'`` in the real Leech lattice. """ vtype = gen_leech2_subtype(v) if (vtype >> 4) != 2: raise ValueError("Vector is not short") if gen_leech2_type(v ^ 0x200) != 4: raise ValueError("Vector not orthogonal to standard vector") result = [] for _i in range(4): if verbose: coc = (v ^ mat24.ploop_theta(v >> 12)) & 0xfff vt = gen_leech2_subtype(v) coc_anchor = 0 if vt in [0x22]: w = mat24.gcode_weight(v >> 12) vect = mat24.gcode_to_vect((v ^ ((w & 4) << 21)) >> 12) coc_anchor = mat24.lsbit24(vect) coc_syn = Cocode(coc).syndrome_list(coc_anchor) gcode = mat24.gcode_to_vect(v >> 12) print("Round %d, v = %s, subtype %s, gcode %s, cocode %s" % (_i, hex(v & 0xffffff), hex(vt), hex(gcode), coc_syn)) assert vtype == gen_leech2_subtype(v) if vtype == 0x21: exp = xi_reduce_odd_type2(v) vtype = 0x22 elif vtype == 0x22: exp = xi_reduce_octad(v) if exp < 0: w = mat24.gcode_weight(v >> 12) vect = mat24.gcode_to_vect((v ^ ((w & 4) << 21)) >> 12) if vect & 0x0c: vect &= ~0x0c src = mat24.vect_to_list(vect, 2) + [2, 3] dest = [0, 1, 2, 3] else: src = [2, 3] + mat24.vect_to_list(vect, 3) v5 = (1 << src[2]) | (1 << src[3]) | (1 << src[4]) v5 |= 0x0c special = mat24.syndrome(v5, 24) src.append(mat24.lsbit24(special & vect)) dest = [2, 3, 4, 5, 6, 7] v = apply_perm(v, src, dest, len(src), result, verbose) exp = xi_reduce_octad(v) assert exp >= 0 vtype = 0x20 elif vtype == 0x20: if ((v & 0xffffff) == 0x800200): return np.array(result, dtype=np.uint32) syn = (mat24.cocode_syndrome(v, 0)) & ~0xc if syn and syn != 3: src = mat24.vect_to_list(syn, 2) + [2, 3] v = apply_perm(v, src, [0, 1, 2, 3], 4, result, verbose) exp = 2 - ((v >> 23) & 1) else: raise ValueError("WTF") if exp: exp = 0xE0000003 - exp v = gen_leech2_op_atom(v, exp) result.append(exp) raise ValueError("WTF1")
def reduce_type2(v, verbose=1): r"""Map (orthgonal) short vector in Leech lattice to standard vector This is a python implementation of the C function ``gen_leech2_reduce_type2`` in file ``gen_leech.c``. Let ``v \in \Lambda / 2 \Lambda`` of type 2 be given by parameter ``v`` in Leech lattice encoding. Let ``beta`` be 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}``. Then the function constructs a ``g \in G_{x0}`` that maps ``v`` to ``beta``. 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. """ vtype = gen_leech2_subtype(v) if (vtype >> 4) != 2: raise ValueError("Vector is not short") result = [] for _i in range(4): if verbose: coc = (v ^ mat24.ploop_theta(v >> 12)) & 0xfff vt = gen_leech2_subtype(v) coc_anchor = 0 if vt in [0x22]: w = mat24.gcode_weight(v >> 12) vect = mat24.gcode_to_vect((v ^ ((w & 4) << 21)) >> 12) coc_anchor = mat24.lsbit24(vect) coc_syn = Cocode(coc).syndrome_list(coc_anchor) gcode = mat24.gcode_to_vect(v >> 12) print("Round %d, v = %s, subtype %s, gcode %s, cocode %s" % (_i, hex(v & 0xffffff), hex(vt), hex(gcode), coc_syn)) assert vtype == gen_leech2_subtype(v) if vtype == 0x21: exp = xi_reduce_odd_type2(v) vtype = 0x22 elif vtype == 0x22: exp = xi_reduce_octad(v) if exp < 0: w = mat24.gcode_weight(v >> 12) vect = mat24.gcode_to_vect((v ^ ((w & 4) << 21)) >> 12) src = mat24.vect_to_list(vect, 4) dest = [0, 1, 2, 3] v = apply_perm(v, src, dest, 4, result, verbose) exp = xi_reduce_octad(v) assert exp >= 0 vtype = 0x20 elif vtype == 0x20: exp = 0 # map v to stadard cocode word [2,3] if v & 0x7fffff != 0x200: syn = (mat24.cocode_syndrome(v, 0)) src = mat24.vect_to_list(syn, 2) v = apply_perm(v, src, [2, 3], 2, result, verbose) # correct v2 if v2 is the cocode word [2,3] + Omega if v & 0x800000: atom = 0xC0000200 # operation y_d such that d has odd scalar # product with cocode word [2,3] v = gen_leech2_op_atom(v, atom) result.append(atom) assert v & 0xffffff == 0x200 return np.array(result, dtype=np.uint32) else: raise ValueError("WTF") if exp: exp = 0xE0000003 - exp v = gen_leech2_op_atom(v, exp) result.append(exp) raise ValueError("WTF1")
def op_xy(v, eps, e, f): """Multiply unit vector v with group element This function multplies a (multiple of a) unit vector v with the group element g = d_<eps> * (x_<e>)**(-1) * (y_<f>)**(-1) . It returns the vector w = v * g. This function uses the same formula for calculating w = v * g, which is used in the implementation of the monster group for computing v = w * g ** (-1). Input vector v must be given as a tuple as described in class mmgroup.structures.abstract_mm_rep_space.AbstractMmRepSpace. Output vector is returned as a tuple of the same shape. """ if len(v) == 3: v = (1,) + v v_value, v_tag, v_d, v_j = v parity = (eps >> 11) & 1 # parity of eps if v_tag == 'X': w_d = v_d ^ (f & 0x7ff) sign = v_d >> 12 d = v_d & 0x7ff c = m24.vect_to_cocode(1 << v_j) sign += m24.gcode_weight(e ^ f) sign += m24.gcode_weight(f) sign += m24.gcode_weight(d) * (parity + 1) sign += m24.gcode_weight(d ^ e ^ f) sign += m24.scalar_prod(e, c) cc = c if parity else 0 cc ^= eps ^ m24.ploop_theta(f) ^ m24.ploop_cap(e, f) sign += m24.scalar_prod(d, cc) sign += f >> 12 return (-1)**sign * v_value, 'X', w_d, v_j elif v_tag in 'YZ': tau = v_tag == 'Y' sigma = (tau + parity) & 1 w_tag = "ZY"[sigma] sign = (v_d >> 12) + ((v_d >> 11) & tau) w_d = (v_d ^ e ^ (f & ~-sigma)) & 0x7ff #print("YZ, w_d", hex(w_d), hex(v_d ^ (e & 0x7ff) ^ (f & sigma_1)), err) # Next we check the sign d = v_d & 0x7ff c = m24.vect_to_cocode(1 << v_j) sign += m24.ploop_cocycle(f, e) * (sigma + 1) sign += m24.gcode_weight(f) * (sigma) sign += m24.gcode_weight(d ^ e) sign += m24.gcode_weight(d ^ e ^ f) sign += m24.scalar_prod(f, c) cc = eps ^ m24.ploop_theta(e) cc ^= m24.ploop_theta(f) * ((sigma ^ 1) & 1) sign += m24.scalar_prod(d, cc) sign += (e >> 12) + (f >> 12) * (sigma + 1) sign += ((e >> 11) & sigma) return (-1)**sign * v_value, w_tag, w_d, v_j elif v_tag == 'T': d = m24.octad_to_gcode(v_d) w_j = v_j ^ as_suboctad(f, d) sign = m24.gcode_weight(d ^ e) + m24.gcode_weight(e) sign += m24.scalar_prod(d, eps) sign += m24.suboctad_scalar_prod(as_suboctad(e ^ f, d), v_j) sign += m24.suboctad_weight(v_j) * parity return (-1)**sign * v_value, 'T', v_d, w_j elif v_tag in 'BC': m = v_tag == 'C' c = m24.vect_to_cocode((1 << v_d) ^ (1 << v_j)) n = m ^ m24.scalar_prod(f, c) w_tag = "BC"[n] w_i, w_j = max(v_d, v_j), min(v_d, v_j) sign = m * parity + m24.scalar_prod(e ^ f, c) return (-1)**sign * v_value, w_tag, w_i, v_j elif v_tag == 'A': w_i, w_j = max(v_d, v_j), min(v_d, v_j) c = m24.vect_to_cocode((1 << v_d) ^ (1 << v_j)) sign = m24.scalar_prod(f, c) return (-1)**sign * v_value,'A', w_i, v_j else: raise ValueError("Bad tag " + v_tag)
def test_Parker_loop(): print("\nTesting operation on Parker loop") for i in range(200): # Test power map, inveriosn, sign, theta, and conversion to GCode n1 = randint(0, 0x1fff) p1 = PLoop(n1) assert p1.ord == n1 == p1.gcode + 0x1000 * (1 - p1.sign) / 2 if (i < 2): print("Testing Parker loop element", p1, ", GCode = ", GCode(p1)) assert len(p1) == mat24.gcode_weight(n1) << 2 assert p1.theta() == Cocode(mat24.ploop_theta(n1)) assert p1 / 4 == p1.theta(p1) == Parity(mat24.ploop_cocycle(n1, n1)) assert p1**2 == PLoopZ(p1 / 4) == (-PLoopZ())**(p1 / 4) assert p1**(-5) == PLoopZ(p1 / 4) * p1 == (-1)**(p1 / 4) * p1 == 1 / p1 assert (1 / p1).ord ^ n1 == (mat24.gcode_weight(n1) & 1) << 12 assert -1 / p1 == -p1**3 == -(1 / p1) assert p1 * (-1 / p1) == PLoopZ(1) == -PLoopZ() assert abs(p1).ord == GCode(p1).ord == p1.ord & 0xfff assert p1 != GCode(p1) assert +p1 == 1 * p1 == p1 * 1 == p1 assert p1 != -p1 and p1 != ~p1 assert (-p1).ord == p1.ord ^ 0x1000 assert (~p1).ord == p1.ord ^ 0x800 s, o, p1_pos = p1.split() assert s == (p1.ord >> 12) & 1 assert o == (p1.ord >> 11) & 1 assert p1.sign == (-1)**s assert p1_pos.ord == p1.ord & 0x7ff assert PLoopZ(s, o) * p1_pos == p1 assert PLoopZ(0, o) * p1_pos == abs(p1) assert PLoopZ(s + 1, o) * p1_pos == -p1 assert -p1 == -1 * p1 == p1 * -1 == p1 / -1 == -1 / p1**-5 assert PLoopZ(s, 1 + o) * p1_pos == ~p1 assert PLoopZ(1 + s, 1 + o) * p1_pos == ~-p1 == -~p1 == ~p1 / -1 assert 2 * p1 == GCode(0) == -2 * GCode(p1) assert -13 * p1 == GCode(p1) == 7 * GCode(p1) if len(p1) & 7 == 0: assert p1**Parity(1) == p1 assert p1**Parity(0) == PLoop(0) else: with pytest.raises(ValueError): p1**Parity(randint(0, 1)) assert p1.bit_list == mat24.gcode_to_bit_list(p1.value & 0xfff) assert p1.bit_list == GcVector(p1).bit_list assert p1.bit_list == GCode(p1).bit_list assert PLoop(GcVector(p1) + 0) == PLoop(GCode(p1) + 0) == abs(p1) assert p1 + 0 == 0 + p1 == GCode(p1) + 0 == 0 + GCode(p1) assert Cocode(GcVector(p1)) == Cocode(0) assert p1 / 2 == Parity(0) # Test Parker loop multiplication and commutator n2 = randint(0, 0x1fff) p2 = PLoop(n2) coc = Cocode(mat24.ploop_cap(n1, n2)) if (i < 1): print("Intersection with", p2, "is", coc) p2inv = p2**-1 assert p1 * p2 == PLoop(mat24.mul_ploop(p1.ord, p2.ord)) assert p1 / p2 == p1 * p2**(-1) assert p1 + p2 == p1 - p2 == GCode(p1 * p2) == GCode(n1 ^ n2) assert (p1 * p2) / (p2 * p1) == PLoopZ((p1 & p2) / 2) assert p1 & p2 == coc assert p1.theta() == Cocode(mat24.ploop_theta(p1.ord)) assert p1.theta(p2) == Parity(mat24.ploop_cocycle(p1.ord, p2.ord)) assert (p1 & p2) / 2 == p1.theta(p2) + p2.theta(p1) assert p1 & p2 == p1.theta() + p2.theta() + (p1 + p2).theta() assert int((p1 & p2) / 2) == mat24.ploop_comm(p1.ord, p2.ord) assert GcVector(p1 & p2) == GcVector(p1) & GcVector(p2) assert ~GcVector(p1 & p2) == ~GcVector(p1) | ~GcVector(p2) assert Cocode(GcVector(p1 & p2)) == p1 & p2 # Test associator n3 = randint(0, 0x1fff) p3 = PLoop(n3) assert p1 * p2 * p3 / (p1 * (p2 * p3)) == PLoopZ(p1 & p2 & p3) assert int(p1 & p2 & p3) == mat24.ploop_assoc(p1.ord, p2.ord, p3.ord) i = randint(-1000, 1000) par = Parity(i) s3 = ((p3 & p1) & p2) + par assert s3 == ((p3 & p1) & p2) + par assert s3 == i + (p1 & (p2 & p3)) assert s3 == par + (p1 & (p2 & p3)) # Test some operations leading to a TypeError with pytest.raises(TypeError): p1 & p2 & p3 & p1 with pytest.raises(TypeError): coc & coc with pytest.raises(TypeError): GCode(p1) * GCode(p2) with pytest.raises(TypeError): 1 / GCode(p2) with pytest.raises(ValueError): coc / 4 with pytest.raises(TypeError): p1 * coc with pytest.raises(TypeError): coc * p1 types = [GcVector, GCode, Cocode, PLoop] for type_ in types: with pytest.raises(TypeError): int(type_(0)) print("Parker Loop test passed")
def ploop_to_xsp2co1(g, pl): res = g.word_type(group=g) value = (c.value & 0x1fff) value = (value << 12) ^ mat24.ploop_theta(value) chk_qstate12(xsp2co1_elem_xspecial(res._data, value)) return res
def str(self): v = self.value x = (v >> 12) & 0x1fff d = (mat24.ploop_theta(v >> 12) ^ v) & 0xfff return "XL2<x_%s*d_%s>" % (ihex(x, 3), ihex(d, 3))