def eq(self, other): "Two codes are equal if their generating matrices have the same span." G1, G2 = self.G, other.G if len(G1) != len(G2): return False A = dot2(self.H, other.G.transpose()) B = dot2(other.H, self.G.transpose()) assert (A.sum()==0) == (B.sum()==0) return A.sum() == 0
def main_fail(): print() print("==" * 70) H = """ 012345678901234 0 YXZZ........... 1 X..X.XX.X...... 2 ZZ..ZZ.......Z. 3 .X..Y....XZ.... 4 .ZX....Y.Z..... 5 .....ZZZ.Z....Z 6 ..Z...ZZ....ZZ. 7 ...Z....Y.XX... 8 ..XX.......ZY.. 9 .........ZXX..Y 10 .....X.....XXXX 11 ....X.X.X.X..X. """ H = syparse(H) print() print(shortstr(H)) m = len(H) n = H.shape[1] // 2 assert n == 15 F = symplectic(n) C = dot2(H, dot2(F, H.transpose())) print(C.shape) for i in range(m): for j in range(i + 1, m): if C[i, j]: print("fail:", i, j) print(rank(H)) H = linear_independent(H) print("H=", H.shape) print(shortstr(H)) HF = dot2(H, F) K = array2(find_kernel(HF)) print("K=", K.shape) print(shortstr(K)) HK = numpy.concatenate((H, K)) L = linear_independent(HK) print() print(shortstr(L))
def gen(): r = argv.get("r", None) # degree m = argv.get("m", None) if r is not None and m is not None: code = reed_muller(r, m) #print(code) #print("d =", code.get_distance()) #code.dump() #code = code.puncture(3) #print(code) code = code.puncture(0) print(code) for g in code.G: print(shortstr(g), g.sum()) print() #code.dump() #print("d =", code.get_distance()) return for m in range(2, 8): for r in range(0, m+1): code = reed_muller(r, m) print(code, end=" ") if code.is_selfdual(): print("is_selfdual", end=" ") if code.is_morthogonal(2): print("is_biorthogonal", end=" ") if code.is_morthogonal(3): print("is_triorthogonal", end=" ") if dot2(code.H, code.H.transpose()).sum()==0: print("***", end=" ") p = code.puncture(0) if p.is_morthogonal(3): print("puncture.is_triorthogonal", end=" ") if p.is_selfdual(): print("puncture.is_selfdual", end=" ") if dot2(p.H, p.H.transpose()).sum()==0: print("***", end=" ") print() if p.is_triorthogonal() and p.k < 20: G = p.G #print(shortstr(G)) A = list(span(G)) A = array2(A) print(is_morthogonal(A, 3))
def main_torus(): n = 8 # ZZZZZZZZ|XXXXXXXX # 12345678|12345678 H = parse(""" 111..1..|........ 1..1....|1.1..... ........|11.11... .1..1...|.1...1.. ..1...1.|...1..1. ...11.11|........ .....1.1|....1..1 ........|..1..111 """.replace("|", "")) print() print("H=") print(shortstr(H)) F = symplectic(n) C = dot2(H, dot2(F, H.transpose())) for i in range(n): for j in range(i + 1, n): if C[i, j]: print("fail:", i + 1, j + 1) print(rank(H)) H = linear_independent(H) print("H=") print(shortstr(H)) HF = dot2(H, F) K = array2(find_kernel(HF)) print("K=") print(shortstr(K)) HK = numpy.concatenate((H, K)) L = linear_independent(HK) print() print(shortstr(L))
def check_toric(): global toric # arff ! import qupy.ldpc.solve import bruhat.solve qupy.ldpc.solve.int_scalar = bruhat.solve.int_scalar from bruhat.solve import shortstr, zeros2, dot2, array2, solve from numpy import alltrue, zeros, dot l = argv.get("l", 2) from qupy.ldpc.toric import Toric2D toric = Toric2D(l) Hx, Hz = toric.Hx, toric.Hz assert alltrue(dot2(Hx, Hz.transpose()) == 0) from qupy.condmat.isomorph import Tanner, search src = Tanner.build2(Hx, Hz) #tgt = Tanner.build2(Hx, Hz) tgt = Tanner.build2(Hz, Hx) # weak duality mx, n = Hx.shape mz, n = Hz.shape fns = [] perms = [] for fn in search(src, tgt): assert len(fn) == mx + mz + n bitmap = [] for i in range(n): bitmap.append(fn[i + mx + mz] - mx - mz) perm = tuple(bitmap) #print(bitmap) fixed = [i for i in range(n) if bitmap[i] == i] print("perm:", perm) print("fixed:", fixed) g = Perm(perm, list(range(n))) assert g.order() == 2 perms.append(perm) for hx in Hx: print(toric.strop(hx)) print("--->") hx = array2([hx[i] for i in perm]) print(toric.strop(hx)) print("--->") hx = array2([hx[i] for i in perm]) print(toric.strop(hx)) print() check_dualities(Hz, Hx.transpose(), perms)
def test_rm(): params = [(r, m) for m in range(2, 8) for r in range(1, m)] r = argv.get("r", None) # degree m = argv.get("m", None) if r is not None and m is not None: params = [(r, m)] for (r, m) in params: #code = reed_muller(r, m) # for code in [ reed_muller(r, m), reed_muller(r, m).puncture(0) ]: for code in [reed_muller(r, m)]: if argv.puncture: print(code, end=" ", flush=True) code = code.puncture(0) code = code.get_even() if argv.puncture==2: code = code.puncture(0) code = code.get_even() G = code.G k, n = G.shape #code = Code(G) #d = code.get_distance() d = "." print("puncture [%d, %d, %s]" % (n, k, d), end=" ", flush=True) else: G = code.G print(code, end=" ", flush=True) i = 1 while i<8: if (is_morthogonal(G, i)): print("(%d)"%i, end="", flush=True) i += 1 else: break if i > code.k: print("*", end="") break print() if argv.show: print(G.shape) print(shortstr(G)) print(dot2(G, G.transpose()).sum()) if len(G) >= 14: continue A = array2(list(span(G))) for i in [1, 2, 3]: assert strong_morthogonal(G, i) == strong_morthogonal(A, i)
def test(): for idx, H in enumerate(items): H = array2(H) #print(H.shape) print(names[idx]) print(shortstr(H)) assert (dot2(H, H.transpose()).sum()) == 0 # orthogonal code G = H for genus in range(1, 4): print(strong_morthogonal(G, genus), end=" ") print() keys = [0, 4, 8, 12, 16, 20, 24] counts = {0: 0, 4: 0, 8: 0, 12: 0, 16: 0, 20: 0, 24: 0} for v in span(G): counts[v.sum()] += 1 print([counts[k] for k in keys]) print()
def intersect(G1, G2): G = numpy.concatenate((G1, G2)) #print("intersect") #print(G1, G2) #print(G) G = G.transpose() #print("find_kernel", G.shape) K = find_kernel(G) if not K: K = numpy.array(K) K.shape = 0, G.shape[1] else: K = numpy.array(K) #print("K:") #print(K, K.shape) G = dot2(K[:, :len(G1)], G1) #print("G:") #print(G, G.shape) #print() G = normal_form(G) return G
def make_bring(): # ------------------------------------------------ # Bring's curve rotation group ngens = 2 a, ai, b, bi = range(2 * ngens) rels = [(ai, a), (bi, b), (a, ) * 5, (b, ) * 2] rels += [(a, a, a, b) * 3] graph = Schreier(2 * ngens, rels) #graph.DEBUG = True graph.build() assert len(graph) == 60 # == 12 * 5 # --------------- Group ------------------ G = graph.get_group() #burnside(G) assert len(G.gens) == 4 ai, a, bi, b = G.gens assert a == ~ai assert b == ~bi assert a.order() == 5 assert b.order() == 2 H = Group.generate([a]) faces = G.left_cosets(H) assert len(faces) == 12 #hom = G.left_action(faces) ab = a * b K = Group.generate([ab]) vertices = G.left_cosets(K) assert len(vertices) == 12 J = Group.generate([b]) edges = G.left_cosets(J) assert len(edges) == 30 from bruhat.solve import shortstr, zeros2, dot2 from numpy import alltrue, zeros, dot def get_adj(left, right): A = zeros2((len(left), len(right))) for i, l in enumerate(left): for j, r in enumerate(right): lr = l.intersect(r) A[i, j] = len(lr) return A Hz = get_adj(faces, edges) Hxt = get_adj(edges, vertices) #print(shortstr(Hz)) #print() #print(shortstr(Hxt)) #print() assert alltrue(dot2(Hz, Hxt) == 0) # ------------ _oriented version --------------- # Bring's curve reflection group ngens = 3 a, ai, b, bi, c, ci = range(2 * ngens) rels = [ (ai, a), (bi, b), (ci, c), (a, ) * 2, (b, ) * 2, (c, ) * 2, (a, b) * 5, (b, c) * 5, (a, c) * 2, ] a1 = (b, a) b1 = (a, c) rels += [(3 * a1 + b1) * 3] graph = Schreier(2 * ngens, rels) graph.build() assert len(graph) == 120 # == 12 * 10 # --------------- Group ------------------ G = graph.get_group() assert len(G) == 120 a, ai, b, bi, c, ci = G.gens a1 = b * a b1 = a * c assert a1.order() == 5 assert b1.order() == 2 bc = b * c ba = b * a assert bc.order() == 5 assert ba.order() == 5 L = Group.generate([a1, b1]) assert len(L) == 60, len(L) orients = G.left_cosets(L) assert len(orients) == 2 #L, gL = orients orients = list(orients) H = Group.generate([a, b]) assert len([g for g in H if g in L]) == 5 faces = G.left_cosets(H) assert len(faces) == 12 # unoriented faces #hom = G.left_action(faces) o_faces = [face.intersect(orients[0]) for face in faces] # oriented faces r_faces = [face.intersect(orients[1]) for face in faces] # reverse oriented faces K = Group.generate([b, c]) vertices = G.left_cosets(K) assert len(vertices) == 12 # unoriented vertices o_vertices = [vertex.intersect(orients[0]) for vertex in vertices] # oriented vertices J = Group.generate([a, c]) u_edges = G.left_cosets(J) assert len(u_edges) == 30 # unoriented edges J = Group.generate([c]) edges = G.left_cosets(J) assert len(edges) == 60, len(edges) # oriented edges ? # Here we choose an orientation on each edge: pairs = {} for e in u_edges: pairs[e] = [] for e1 in edges: if e.intersect(e1): pairs[e].append(e1) assert len(pairs[e]) == 2 def shortstr(A): s = str(A) s = s.replace("0", '.') return s Hz = zeros((len(o_faces), len(u_edges)), dtype=int) for i, l in enumerate(o_faces): for j, e in enumerate(u_edges): le = l.intersect(e) if not le: continue e1, e2 = pairs[e] if e1.intersect(le): Hz[i, j] = 1 elif e2.intersect(le): Hz[i, j] = -1 else: assert 0 Hxt = zeros((len(u_edges), len(o_vertices)), dtype=int) for i, e in enumerate(u_edges): for j, v in enumerate(o_vertices): ev = e.intersect(v) if not ev: continue e1, e2 = pairs[e] if e1.intersect(ev): Hxt[i, j] = 1 elif e2.intersect(ev): Hxt[i, j] = -1 else: assert 0 #print(shortstr(Hz)) #print() #print(shortstr(Hxt)) #print() assert alltrue(dot(Hz, Hxt) == 0)
def check_dualities(Hz, Hxt, dualities): from bruhat.solve import shortstr, zeros2, dot2, array2, solve, span from numpy import alltrue, zeros, dot import qupy.ldpc.solve import bruhat.solve qupy.ldpc.solve.int_scalar = bruhat.solve.int_scalar from qupy.ldpc.css import CSSCode Hz = Hz % 2 Hx = Hxt.transpose() % 2 code = CSSCode(Hz=Hz, Hx=Hx) print(code) n = code.n Lx, Lz = code.Lx, code.Lz # check we really do have weak dualities here: for perm in dualities: Hz1 = Hz[:, perm] Hxt1 = Hxt[perm, :] assert solve(Hxt, Hz1.transpose()) is not None assert solve(Hz1.transpose(), Hxt) is not None Lz1 = Lz[:, perm] Lx1 = Lx[:, perm] find_xz = solve(concatenate((Lx, Hx)).transpose(), Lz1.transpose()) is not None find_zx = solve(concatenate((Lz1, Hz1)).transpose(), Lx.transpose()) is not None #print(find_xz, find_zx) assert find_xz assert find_zx # the fixed points live simultaneously in the homology & the cohomology fixed = [i for i in range(n) if perm[i] == i] if len(fixed) == 0: continue v = array2([0] * n) v[fixed] = 1 v.shape = (n, 1) find_xz = solve(concatenate((Lx, Hx)).transpose(), v) is not None find_zx = solve(concatenate((Lz, Hz)).transpose(), v) is not None #print(find_xz, find_zx) assert find_xz assert find_zx from qupy.ldpc.asymplectic import Stim as Clifford s_gates = [] h_gates = [] s_names = [] for idx, swap in enumerate(dualities): fixed = [i for i in range(n) if swap[i] == i] print(idx, fixed) for signs in cross([(-1, 1)] * len(fixed)): # <------- does not scale !!! XXX v = [0] * n for i, sign in enumerate(signs): v[fixed[i]] = sign ux = numpy.dot(Hx, v) uz = numpy.dot(Hz, v) if numpy.alltrue(ux == 0) and numpy.alltrue(uz == 0): #print("*", end=" ") break #else: # assert 0 #print(v) #print() # transversal S/CZ g = Clifford.identity(n) name = [] for i in range(n): j = swap[i] if j < i: continue if j == i: assert v[i] in [1, -1] if v[i] == 1: op = Clifford.s_gate(n, i) name.append("S_%d" % (i, )) else: op = Clifford.s_gate(n, i).inverse() name.append("Si_%d" % (i, )) else: op = Clifford.cz_gate(n, i, j) name.append("CZ_%d_%d" % (i, j)) g = op * g name = "*".join(reversed(name)) s_names.append(name) #print(g) #print() #assert g.is_transversal(code) s_gates.append(g) h = Clifford.identity(n) for i in range(n): h = h * Clifford.h_gate(n, i) for i in range(n): j = swap[i] if j <= i: continue h = h * Clifford.swap_gate(n, i, j) #print(g) #print() #assert h.is_transversal(code) h_gates.append(h) if 0: print() print("CZ:") CZ = Clifford.cz_gate(2, 0, 1) op = (1., [0, 0], [1, 1]) for i in range(4): print(op) op = CZ(*op) return for idx, sop in enumerate(s_gates): print("idx =", idx) #for hx in Hx: perm = dualities[idx] #for hx in span(Hx): for hx in Hx: #print("hx =", hx) #print(s_names[idx]) phase, zop, xop = sop(1., None, hx) assert numpy.alltrue(xop == hx) # fixes x component print(phase, zop, xop, dot2(zop, xop)) for (i, j) in enumerate(perm): if xop[i] and xop[j] and i < j: print("pair", (i, j)) if toric is None: continue print("xop =") print(toric.strop(xop)) print("zop =") print(toric.strop(zop)) print()
def is_422_cat_selfdual(Hz, Hx, perm): "concatenate with the [[4,2,2]] code and see if we get a self-dual code" from numpy import alltrue, zeros, dot import qupy.ldpc.solve import bruhat.solve qupy.ldpc.solve.int_scalar = bruhat.solve.int_scalar from bruhat.solve import shortstrx, zeros2, dot2, array2, solve from qupy.ldpc.css import CSSCode Cout = CSSCode(Hz=Hz, Hx=Hx) #print(Cout) #Cin = CSSCode(Hz=array2([[1,1,1,1]]), Hx=array2([[1,1,1,1]])) #print(Cin) pairs = [] singles = [] for i in range(Cout.n): j = perm[i] if j < i: continue if i == j: singles.append(i) else: pairs.append((i, j)) #print(singles, pairs) M = len(singles) + 4 * len(pairs) # encoding matrices enc_z = zeros2(M, Cout.n) enc_x = zeros2(M, Cout.n) row = 0 for col in singles: enc_z[row, col] = 1 enc_x[row, col] = 1 row += 1 H = [] for (i, j) in pairs: enc_z[row, i] = 1 # 1010 enc_z[row + 2, i] = 1 enc_z[row, j] = 1 # 1100 enc_z[row + 1, j] = 1 enc_x[row, i] = 1 # 1100 enc_x[row + 1, i] = 1 enc_x[row, j] = 1 # 1010 enc_x[row + 2, j] = 1 h = array2([0] * M) h[row:row + 4] = 1 H.append(h) row += 4 assert row == M #print(shortstrx(enc_z, enc_x)) Hz = dot2(enc_z, Cout.Hz.transpose()).transpose() Hx = dot2(enc_x, Cout.Hx.transpose()).transpose() assert alltrue(dot2(Hz, Hx.transpose()) == 0) Hz = numpy.concatenate((Hz, H)) Hx = numpy.concatenate((Hx, H)) assert alltrue(dot2(Hz, Hx.transpose()) == 0) C = CSSCode(Hz=Hz, Hx=Hx) assert C.k == Cout.k #print(C) lhs = (solve(Hz.transpose(), Hx.transpose()) is not None) rhs = (solve(Hx.transpose(), Hz.transpose()) is not None) return lhs and rhs
def make_surface_54(G_0): "find Z/2 chain complex" from bruhat.solve import shortstr, zeros2, dot2 from numpy import alltrue, zeros, dot print("|G_0| =", len(G_0)) a, ai, b, bi, c, ci, d, di = G_0.gens # 5,5 coxeter subgroup G_1 = Group.generate([a, b, c]) assert G_0.is_subgroup(G_1) # orientation subgroup L_0 = Group.generate([a * b, a * d]) assert G_0.is_subgroup(L_0) orients = G_0.left_cosets(L_0) assert len(orients) == 2 H_1 = Group.generate([a, b]) faces = G_1.left_cosets(H_1) #face_vertices = G_0.left_cosets(H_1) # G_0 swaps faces & vertices K_1 = Group.generate([b, c]) vertices = G_1.left_cosets(K_1) J_1 = Group.generate([a, c]) edges_1 = G_1.left_cosets(J_1) print("faces: %d, edges: %d, vertices: %d" % (len(faces), len(edges_1), len(vertices))) J_0 = Group.generate([a, d]) assert len(J_0) == 8 edges_0 = G_0.left_cosets(J_0) print("edges_0:", len(edges_0)) assert G_0.is_subgroup(J_0) act_edges_0 = G_0.action_subgroup(J_0) #print("here") #while 1: # sleep(1) from bruhat.solve import shortstr, zeros2, dot2, array2, solve from numpy import alltrue, zeros, dot def get_adj(left, right): A = zeros2((len(left), len(right))) for i, l in enumerate(left): for j, r in enumerate(right): lr = l.intersect(r) A[i, j] = len(lr) > 0 return A Hz = get_adj(faces, edges_0) Hxt = get_adj(edges_0, vertices) Hx = Hxt.transpose() #print(shortstr(Hz)) #print() #print(shortstr(Hxt)) #print() assert alltrue(dot2(Hz, Hxt) == 0) # act_fold_faces = G_0.action_subgroup(H_1) dualities = [] idx = 0 for i, g in enumerate(G_0): assert g in G_0 h_edge_0 = act_edges_0[g] n_edge = len(h_edge_0.fixed()) # h_fold_faces = act_fold_faces[g] # n_fold_faces = len(h_fold_faces.fixed()) count = 0 #perms = [] # extract action on the fixed edges for e0 in edges_0: e1 = g * e0 if e0 != e1: continue count += 1 # perm = e0.left_mul_perm(g) # #print(perm, end=" ") # perms.append(perm) assert count == n_edge if g in G_1: continue elif g.order() != 2: continue #elif g in L_0: # continue #elif g.order() == 2 and g not in G_1 and g not in L_0: perm = h_edge_0.get_idxs() dualities.append(perm) #if g not in L_0: # check_432(Hz, Hx, perm) # return result = is_422_cat_selfdual(Hz, Hx, perm) print( "%d: [|g|=%s,%s.%s.%s.%s]" % ( len(dualities), g.order(), n_edge or ' ', # num fixed edges # n_fold_faces or ' ', # num fixed faces+vertexes [" ", "pres" ][int(g in G_1)], # preserves face/vertex distinction ["refl", "rot "][int(g in L_0)], # oriented or un-oriented [" ", "selfdual"][result], ), end=" ", flush=True) print() print()
def search(): # Bravyi, Haah, 1209.2426v1 sec IX. # https://arxiv.org/pdf/1209.2426.pdf verbose = argv.get("verbose") m = argv.get("m", 6) # _number of rows k = argv.get("k", None) # _number of odd-weight rows # these are the variables N_x xs = list(cross([(0, 1)]*m)) maxweight = argv.maxweight minweight = argv.get("minweight", 1) xs = [x for x in xs if minweight <= sum(x)] if maxweight: xs = [x for x in xs if sum(x) <= maxweight] N = len(xs) lhs = [] rhs = [] # bi-orthogonality for a in range(m): for b in range(a+1, m): v = zeros2(N) for i, x in enumerate(xs): if x[a] == x[b] == 1: v[i] = 1 if v.sum(): lhs.append(v) rhs.append(0) # tri-orthogonality for a in range(m): for b in range(a+1, m): for c in range(b+1, m): v = zeros2(N) for i, x in enumerate(xs): if x[a] == x[b] == x[c] == 1: v[i] = 1 if v.sum(): lhs.append(v) rhs.append(0) # # dissallow columns with weight <= 1 # for i, x in enumerate(xs): # if sum(x)<=1: # v = zeros2(N) # v[i] = 1 # lhs.append(v) # rhs.append(0) if k is not None: # constrain to k _number of odd-weight rows assert 0<=k<m for a in range(m): v = zeros2(N) for i, x in enumerate(xs): if x[a] == 1: v[i] = 1 lhs.append(v) if a<k: rhs.append(1) else: rhs.append(0) A = array2(lhs) rhs = array2(rhs) #print(shortstr(A)) B = pseudo_inverse(A) soln = dot2(B, rhs) if not eq2(dot2(A, soln), rhs): print("no solution") return if verbose: print("soln:") print(shortstr(soln)) soln.shape = (N, 1) rhs.shape = A.shape[0], 1 K = array2(list(find_kernel(A))) #print(K) #print( dot2(A, K.transpose())) #sols = [] #for v in span(K): best = None density = 1.0 size = 99*N trials = argv.get("trials", 1024) count = 0 for trial in range(trials): u = rand2(len(K), 1) v = dot2(K.transpose(), u) #print(v) v = (v+soln)%2 assert eq2(dot2(A, v), rhs) if v.sum() > size: continue size = v.sum() Gt = [] for i, x in enumerate(xs): if v[i]: Gt.append(x) if not Gt: continue Gt = array2(Gt) G = Gt.transpose() assert is_morthogonal(G, 3) if G.shape[1]<m: continue if 0 in G.sum(1): continue if argv.strong_morthogonal and not strong_morthogonal(G, 3): continue #print(shortstr(G)) # for g in G: # print(shortstr(g), g.sum()) # print() _density = float(G.sum()) / (G.shape[0]*G.shape[1]) #if best is None or _density < density: if best is None or G.shape[1] <= size: best = G size = G.shape[1] density = _density if 0: #sols.append(G) Gx = even_rows(G) assert is_morthogonal(Gx, 3) if len(Gx)==0: continue GGx = array2(list(span(Gx))) assert is_morthogonal(GGx, 3) count += 1 print("found %d solutions" % count) if best is None: return G = best #print(shortstr(G)) for g in G: print(shortstr(g), g.sum()) print() print("density:", density) print("shape:", G.shape) G = linear_independent(G) A = list(span(G)) print(strong_morthogonal(A, 1)) print(strong_morthogonal(A, 2)) print(strong_morthogonal(A, 3)) #print(shortstr(dot2(G, G.transpose()))) if 0: B = pseudo_inverse(A) v = dot2(B, rhs) print("B:") print(shortstr(B)) print("v:") print(shortstr(v)) assert eq2(dot2(B, v), rhs)
def check(self): G, H = self.G, self.H assert rank(G) == len(G) A = dot2(H, G.transpose()) assert A.sum()==0
def search_selfdual(): verbose = argv.get("verbose") m = argv.get("m", 6) # _number of rows k = argv.get("k", None) # _number of odd-weight rows maxweight = argv.get("maxweight", m) minweight = argv.get("minweight", 1) # these are the variables N_x print("building xs...") if 0: xs = cross([(0, 1)]*m) xs = [x for x in xs if minweight <= sum(x) <= maxweight] prune = argv.get("prune", 0.5) xs = [x for x in xs if random() < prune] xs = [] N = argv.get("N", m*100) colweight = argv.get("colweight", maxweight) assert colweight <= m for i in range(N): x = [0]*m total = 0 while total < colweight: idx = randint(0, m-1) if x[idx] == 0: x[idx] = 1 total += 1 xs.append(x) N = len(xs) lhs = [] rhs = [] # bi-orthogonality for a in range(m): for b in range(a+1, m): v = zeros2(N) for i, x in enumerate(xs): if x[a] == x[b] == 1: v[i] = 1 if v.sum(): lhs.append(v) rhs.append(0) k = 0 # all rows must have even weight # constrain to k _number of odd-weight rows assert 0<=k<m for a in range(m): v = zeros2(N) for i, x in enumerate(xs): if x[a] == 1: v[i] = 1 lhs.append(v) if a<k: rhs.append(1) else: rhs.append(0) logops = argv.logops A = array2(lhs) rhs = array2(rhs) #print(shortstr(A)) print("solve...") B = pseudo_inverse(A) soln = dot2(B, rhs) if not eq2(dot2(A, soln), rhs): print("no solution") return if verbose: print("soln:") print(shortstr(soln)) soln.shape = (N, 1) rhs.shape = A.shape[0], 1 K = array2(list(find_kernel(A))) print("kernel:", K.shape) if len(K)==0: return #print(K) #print( dot2(A, K.transpose())) #sols = [] #for v in span(K): best = None density = 1.0 size = 99*N trials = argv.get("trials", 1024) count = 0 print("trials...") for trial in range(trials): u = rand2(len(K), 1) v = dot2(K.transpose(), u) #print(v) v = (v+soln)%2 assert eq2(dot2(A, v), rhs) if v.sum() >= size: continue if v.sum() < m: continue if v.sum(): print(v.sum(), end=" ", flush=True) size = v.sum() if logops is not None and size != 2*m+logops: continue Gt = [] for i, x in enumerate(xs): if v[i]: Gt.append(x) Gt = array2(Gt) G = Gt.transpose() if dot2(G, Gt).sum() != 0: # not self-dual print(shortstr(dot2(G, Gt))) assert 0 return #if G.shape[1]<m: # continue if 0 in G.sum(1): print(".", end="", flush=True) continue #print(shortstr(G)) # for g in G: # print(shortstr(g), g.sum()) # print() _density = float(G.sum()) / (G.shape[0]*G.shape[1]) #if best is None or _density < density: if best is None or G.shape[1] <= size: best = G size = G.shape[1] density = _density if 0: #sols.append(G) Gx = even_rows(G) assert is_morthogonal(Gx, 3) if len(Gx)==0: continue GGx = array2(list(span(Gx))) assert is_morthogonal(GGx, 3) count += 1 print("found %d solutions" % count) if best is None: return G = best #print(shortstr(G)) f = open("selfdual.ldpc", "w") for spec in ["Hx =", "Hz ="]: print(spec, file=f) for g in G: print(shortstr(g), file=f) f.close() print() print("density:", density) print("shape:", G.shape) if 0: B = pseudo_inverse(A) v = dot2(B, rhs) print("B:") print(shortstr(B)) print("v:") print(shortstr(v)) assert eq2(dot2(B, v), rhs)
def search_extend(): # Extend the checks of a random code to make it triorthogonal. # Based on the search function above. verbose = argv.get("verbose") m = argv.get("m", 6) n = argv.get("n", m+2) k = argv.get("k") # odd _numbered rows ( logical operators) code = argv.get("code", "rand") if code == "rand": while 1: G0 = rand2(m, n) counts = G0.sum(0) if min(counts)==2 and rank(G0) == m: cols = set() for i in range(n): cols.add(tuple(G0[:, i])) if len(cols) == n: # no repeated cols break elif code == "toric": G0 = parse(""" 11.11... .111..1. 1...11.1 """) # l=2 toric code X logops + X stabs l = argv.get("l", 3) G0 = build_toric(l) m, n = G0.shape else: return code = Code(G0, check=False) print(shortstr(G0)) print("is_triorthogonal:", code.is_triorthogonal()) # these are the variables N_x xs = list(cross([(0, 1)]*m)) N = len(xs) lookup = {} for i, x in enumerate(xs): lookup[x] = i lhs = [] rhs = [] taken = set() for i in range(n): x = G0[:, i] idx = lookup[tuple(x)] assert idx not in taken taken.add(idx) if verbose: for idx in range(N): print(idx, xs[idx], "*" if idx in taken else "") for idx in taken: v = zeros2(N) v[idx] = 1 lhs.append(v) rhs.append(1) # bi-orthogonality for a in range(m): for b in range(a+1, m): v = zeros2(N) for i, x in enumerate(xs): if x[a] == x[b] == 1: v[i] += 1 assert v.sum() lhs.append(v) rhs.append(0) # tri-orthogonality for a in range(m): for b in range(a+1, m): for c in range(b+1, m): v = zeros2(N) for i, x in enumerate(xs): if x[a] == x[b] == x[c] == 1: v[i] += 1 assert v.sum() lhs.append(v) rhs.append(0) # dissallow columns with weight <= 1 for i, x in enumerate(xs): if sum(x)<=1: v = zeros2(N) v[i] = 1 lhs.append(v) rhs.append(0) if k is not None: # constrain to k _number of odd-weight rows assert 0<=k<m for a in range(m): v = zeros2(N) for i, x in enumerate(xs): if x[a] == 1: v[i] = 1 lhs.append(v) if a<k: rhs.append(1) else: rhs.append(0) A = array2(lhs) rhs = array2(rhs) if verbose: print("lhs:") print(shortstr(A)) print("rhs:") print(shortstr(rhs)) B = pseudo_inverse(A) soln = dot2(B, rhs) if not eq2(dot2(A, soln), rhs): print("no solution") return if verbose: print("soln:") print(shortstr(soln)) soln.shape = (N, 1) rhs.shape = A.shape[0], 1 K = array2(list(find_kernel(A))) best = None density = 1.0 size = 9999*n trials = argv.get("trials", 1024) count = 0 for trial in range(trials): u = rand2(len(K), 1) v = dot2(K.transpose(), u) #print(v) assert dot2(A, v).sum()==0 #if v.sum() != n: # continue assert v[0]==0 v = (v+soln)%2 assert eq2(dot2(A, v), rhs) Gt = list(G0.transpose()) for i, x in enumerate(xs): if v[i] and not i in taken: Gt.append(x) if not Gt: continue Gt = array2(Gt) G = Gt.transpose() if verbose: print("G0") print(shortstr(G0)) print("solution:") print(shortstr(G)) assert is_morthogonal(G, 3) if G.shape[1]<m: continue if 0 in G.sum(1): continue #print(shortstr(G)) # for g in G: # print(shortstr(g), g.sum()) # print() _density = float(G.sum()) / (G.shape[0]*G.shape[1]) #if best is None or _density < density: if best is None or G.shape[1] < size: best = G density = _density size = G.shape[1] if 0: #sols.append(G) Gx = even_rows(G) assert is_morthogonal(Gx, 3) if len(Gx)==0: continue GGx = array2(list(span(Gx))) assert is_morthogonal(GGx, 3) count += 1 print("found %d solutions" % count) G = best #print(shortstr(G)) for g in G: print(shortstr(g), g.sum()) print() print("density:", density)