def test_universal(): for trial in range(100): m, n = 3, 4 J = rand2(m, n) K = rand2(m, n) a = Chain([J]) b = Chain([K]) amorph = a.from_zero() bmorph = b.from_zero() am, bm, c, u = chain.pushout(amorph, bmorph) assert u is None C = c[0] mm, nn = C.shape f = rand_full_rank(nn - 2, nn) g, H, _ = solve.pushout(C, f) _c = Chain([H]) m = Morphism(c, _c, [g, f]) assert m * am is not None assert m * bm is not None _, _, _, u = chain.pushout(amorph, bmorph, m * am, m * bm, _c) assert u is not None assert u == m
def test_glue(): m = argv.get("m", 9) n = argv.get("n", 10) d = argv.get("d", 0) p = argv.get("p", 0.5) weight = argv.weight H1 = rand2(m, n, p, weight) G1 = find_kernel(H1) G1t = G1.transpose() H1t = H1.transpose() A1 = Chain([G1, H1t]) k1 = len(G1) print("H1") print(fstr(H1)) print() print(fstr(G1)) w = wenum(H1) print("wenum:", [len(wi) for wi in w]) H2 = rand2(m, n, p, weight) H2t = H2.transpose() G2 = find_kernel(H2) G2t = G2.transpose() A2 = Chain([G2, H2t]) k2 = len(G2) print("H2") print(fstr(H2)) print() print(fstr(G2)) w = wenum(H2) print("wenum:", [len(wi) for wi in w]) if k1 != k2: return k = k1 I = identity2(k) B = Chain([I, zeros2(k, 0)]) a = zeros2(n, k) for i in range(k): a[i, i] = 1 f1 = Morphism(B, A1, [dot2(G1, a), a, zeros2(m, 0)]) f2 = Morphism(B, A2, [dot2(G2, a), a, zeros2(m, 0)]) a, b, C, _ = chain.pushout(f1, f2) H = C[1].transpose() print("H:") print(fstr(H)) w = wenum(H) print("wenum:", [len(wi) for wi in w])
def build_random(n): weight = argv.get("weight", 3) coweight = argv.get("coweight") p = argv.get("p", 0.3) m = argv.get("m", n) mx = argv.get("mx", m) mz = argv.get("mz", m) if coweight is not None: Gx = rand2(n, mx, weight=coweight).transpose() Gz = rand2(n, mz, weight=coweight).transpose() else: Gx = rand2(mx, n, p=p, weight=weight) Gz = rand2(mz, n, p=p, weight=weight) Hx = Hz = None Gx = Gx[[i for i in range(m) if Gx[i].sum()], :] Gz = Gz[[i for i in range(m) if Gz[i].sum()], :] li = argv.get("li", True) if li: Gx = linear_independent(Gx) Gz = linear_independent(Gz) return Gx, Gz, Hx, Hz
def make_q(n, m, weight=None): k = n - 2 * m assert k >= 0 assert m > 0 Hx = [rand2(1, n, weight=weight)[0]] Hz = [] while 1: _Hx = array2(Hx) while 1: v = rand2(1, n, weight=weight) if dot2(Hx, v.transpose()).sum() == 0: break Hz.append(v[0]) if len(Hz) == m: break while 1: v = rand2(1, n, weight=weight) if dot2(Hz, v.transpose()).sum() == 0: break Hx.append(v[0]) Hx = array2(Hx) Hz = array2(Hz) assert dot2(Hx, Hz.transpose()).sum() == 0 return Hx, Hz
def sparsecss_FAIL(n, mx, mz, weight=3, **kw): print("sparsecss", n, mx, mz) k = n-mx-mz assert k>=0 Hz = rand2(mz, n, weight=weight) #print shortstrx(Hx) kern = numpy.array(solve.find_kernel(Hz)) mkern = kern.shape[0] print("kern:") print(shortstr(kern)) print() kern1 = zeros2(mkern, n) for i in range(mkern): v = rand2(1, mkern) kern1[i] = dot2(v, kern) print("kern1:") print(shortstr(kern1)) print() kern = kern1 Hx = [] for i in range(mx): j = randint(0, mkern-1) v = kern[j].copy() count = 0 while 1: v += kern[randint(0, mkern-1)] v %= 2 w = v.sum() if w==weight and count > 100: break count += 1 Hx.append(v) Hx = array2(Hx) print(shortstrx(Hx)) C = CSSCode(Hx=Hx, Hz=Hz, **kw) return C
def build_random_selfdual(n): weight = argv.get("weight", 3) m = argv.get("m", n) h = argv.get("h", 0) while 1: Gx = rand2(m, n, weight=weight) Gz = Gx.copy() Hx = Hz = None Gx = Gx[[i for i in range(m) if Gx[i].sum()], :] Gx = linear_independent(Gx) if len(Gx) < m: write("m") continue Gz = Gx.copy() Hx = find_stabilizers(Gz, Gx) Hz = find_stabilizers(Gx, Gz) if len(Hx) == h and len(Hz) == h: break write("H(%d,%d)" % (len(Hx), len(Hz))) print() return Gx, Gz, Hx, Hz
def make_morthogonal(m, n, genus): while 1: G = rand2(m, n) if strong_morthogonal(G, genus) and rank(G) == m and numpy.min( G.sum(0)): break return G
def rand_codes_slow(m, n, trials=10000): count = 0 while count < trials: H = rand2(m, n) if rank(H) == m: yield H count += 1
def make_ldpc(m, n, p=0.5, weight=None, dist=0): while 1: H = rand2(m, n, p, weight) d = classical_distance(H, dist) if d >= dist: break return H
def sparsecss_SLOWSLOW(n, mx, mz, weight=3, **kw): print("sparsecss", n, mx, mz) k = n-mx-mz assert k>=0 Hz = rand2(mz, n, weight=weight) Hx = rand2(mx, n, weight=weight) while 1: A = dot2(Hz, Hx.transpose()) A %= 2 rows = list(range(mz)) cols = list(range(mx)) if not A.max(): break #print A.sum(), while A.max() and rows and cols: i = choice(rows) j = choice(cols) v = rand2(1, n, weight=weight) if random()<=0.5: A[i, :] = 0 Hz[i] = v rows.remove(i) else: A[:, j] = 0 Hx[j] = v cols.remove(j) C = CSSCode(Hx=Hx, Hz=Hz, **kw) return C
def rand_full_rank(m, n=None): if n is None: n = m assert n >= m while 1: f = rand2(m, n) if rank(f) == m: break return f
def rand_span(A): while 1: m, n = A.shape v = rand2(m, m) A1 = dot2(v, A) assert A1.shape == A.shape if rank(A) == rank(A1): break assert rank(intersect(A, A1)) == rank(A) return A1
def sparsecss(n, mx, mz, weight=8, **kw): print("sparsecss", n, mx, mz) k = n-mx-mz assert k>=0 vec = lambda n=n, weight=weight : rand2(1, n, weight=weight) Hz = zeros2(0, n) Hx = zeros2(0, n) Hx = append2(Hx, vec()) while len(Hz)<mz or len(Hx)<mx: # append2 Hz rows = shortstr(Hz).split() #print rows while Hz.shape[0]<mz: v = vec() u = dot2(Hx, v.transpose()) if u.sum() == 0 and shortstr(v) not in rows: Hz = append2(Hz, v) break # append2 Hx rows = shortstr(Hx).split() #print rows while Hx.shape[0]<mx: v = vec() u = dot2(Hz, v.transpose()) if u.sum() == 0 and shortstr(v) not in rows: Hx = append2(Hx, v) break print(shortstrx(Hz, Hx)) print() bits = [] for i in range(n): if Hx[:, i].sum() == 0: bits.append(i) elif Hz[:, i].sum() == 0: bits.append(i) for i in reversed(bits): Hx = numpy.concatenate( (Hx[:, :i], Hx[:, i+1:]), axis=1) Hz = numpy.concatenate( (Hz[:, :i], Hz[:, i+1:]), axis=1) #print shortstrx(Hx, Hz) C = CSSCode(Hx=Hx, Hz=Hz, **kw) return C
def glue_gcolor(): from qupy.ldpc.gcolor import Lattice l = argv.get('l', 1) lattice = Lattice(l) #code = lattice.build_code() H = lattice.Hx print("H:", H.shape) print(shortstr(H)) m, n = H.shape H1 = zeros2(m + 1, n + 1) H1[1:, 1:] = H H1[0, :] = 1 # print() # print(shortstr(H1)) # for genus in range(1, 5): # print(genus, strong_morthogonal(H1, genus)) H = H1 genus = argv.get("genus", 3) H = H.astype(numpy.int32) n = H.shape[1] if argv.scramble: R = rand2(m, m) H = dot2(R, H) print("H:", H.shape) print(shortstrx(H)) assert dot2(H, H.transpose()).sum() == 0 i0 = argv.get("i0", 0) i1 = argv.get("i1", i0) i2 = argv.get("i2", n) i3 = argv.get("i3", i2 + 1) # glue i0<-->i2 and i1<-->i3 H2 = direct_sum(H, H) print(H2.shape) print(shortstrx(H2)) assert strong_morthogonal(H2, genus) print() H3 = glue_self_classical(H2, [(i0, i2), (i1, i3)]) print(H3.shape) print(shortstrx(H3)) assert strong_morthogonal(H3, genus) print() print(shortstr((H2[:m, 1:n] + H3[:m, 1:n]) % 2)) #print(eq2(H2[m+2:, i1+2:], H3[m:, i1:])) #print(classical_distance(H3)) return H3
def randcss(n, mx, mz, distance=None, **kw): """ http://arxiv.org/abs/quant-ph/9512032 Quantum error-correcting codes exist with asymptotic rate: k/n = 1 - 2H(2t/n) where H(p) = -p log p - (1-p) log (1-p) and t = floor((d-1)/2). """ while 1: k = n-mx-mz assert k>=0 #print "rate:", 1.*k//n #H = lambda p: -p*log(p) - (1-p)*log(1-p) #d = 56 #print 1-2*H(1.*d//n) # works! Hz = rand2(mz, n) #print shortstrx(Hx) kern = numpy.array(solve.find_kernel(Hz)) Hx = zeros2(mx, n) for i in range(mx): v = rand2(1, n-mx) Hx[i] = dot2(v, kern) C = CSSCode(Hx=Hx, Hz=Hz, **kw) if distance is None: break d = lookup_distance(C) if d < distance: continue d = lookup_distance(C.dual()) if d < distance: continue break return C
def random_code(n, k, kt, distance=1): "code length n, dimension k, transpose dimension kt" d = 0 while d < distance: H = rand2(n - k, n) if rank(H) < n - k: continue d = classical_distance(H, distance) K = H dt = 0 while dt < distance: R = rand2(kt, n - k) J = dot2(R, H) K = numpy.concatenate((H, J)) if rank(K) < n - k: continue dt = classical_distance(K.transpose()) return K
def randselfdual(m, n, rw): assert rw%2 == 0 H = solve.rand2(1, n, weight=rw) while len(H) < m: while 1: h = solve.rand2(1, n, weight=rw) if dot2(H, h.transpose()).sum() == 0: break H = numpy.concatenate((H, h)) print(H) idxs = numpy.where(H.sum(0)!=0)[0] H = H[:, idxs] print(H) code = CSSCode(Hz=H, Hx=H) return code
def rand_codes(m, n, trials=10000): assert 0<=m<=n k = n-m I = identity2(m) count = 0 while count < trials: H = zeros2(m, n) A = rand2(m, k) H[:, :k] = A H[:, k:] = I yield H count += 1
def get_codes(m, n): while 1: H = rand2(m, n) #H[0:2, :] = 0 H[:, 0] = 0 H[0, 0] = 1 #H[0:2, 0:3] = A #print(shortstr(H)) #print() if rank(H) < m: continue yield H
def random_code(n, k, kt, distance=1): "return parity check for code of length n, dimension k, transpose dimension kt" d = 0 while d<distance: H = rand2(n-k, n) if rank(H) < n-k: continue d = classical_distance(H, distance) K = H dt = 0 while dt<distance: R = rand2(kt, n-k) J = dot2(R, H) K = numpy.concatenate((H, J)) if rank(K) < n-k: continue dt = classical_distance(K.transpose()) m = len(K) assert k - n + m - kt == 0 return K
def test(n, k, dist=2, verbose=False): assert n > k if argv.rand: while 1: G = rand2(k, n) if rank(G) < k: continue dG = min_weight(G) if dG < dist: continue H = find_kernel(G) dH = min_weight(H) if dH < dist: continue break else: G = zeros2(k, n) jdx = 0 for idx in range(k): for kdx in range(dist): G[idx,jdx+kdx] = 1 jdx += dist-1 dG = min_weight(G) if n < 20 else None assert dG is None or dG == dist H = find_kernel(G) #print(".", flush=True, end="") H = row_reduce(H) search(G, H) if verbose: print("G =") print(shortstr(G)) print("weight =", dG) print() print("H =") print(shortstr(H)) print()
def glue_classical(): from bruhat.triply_even import build genus = argv.get("genus", 3) m = argv.get("dim", 7) idx = argv.get("idx", 144) H = build.get(m, idx) H = H.astype(numpy.int32) n = H.shape[1] if argv.scramble: R = rand2(m, m) H = dot2(R, H) print(shortstrx(H)) assert dot2(H, H.transpose()).sum() == 0 i0 = argv.get("i0", 0) i1 = argv.get("i1", i0) i2 = argv.get("i2", n) i3 = argv.get("i3", i2 + 1) # glue i0<-->i2 and i1<-->i3 #H2 = direct_sum(H, H) H2 = H #print(shortstrx(H2)) assert strong_morthogonal(H2, genus) print() H3 = glue_self_classical(H2, [(i0, i2), (i1, i3)]) print(shortstrx(H3)) assert strong_morthogonal(H3, genus) print() print(shortstr((H2[:m, 1:n] + H3[:m, 1:n]) % 2)) #print(eq2(H2[m+2:, i1+2:], H3[m:, i1:])) #print(classical_distance(H3)) return H3
def rand_span(H): op = dot2(rand2(1, len(H)), H)[0] return op
def glue_classical_self(): from bruhat.triply_even import build from bruhat.triply_even import codes24 genus = argv.get("genus", 3) if argv.golay: H = codes24.get("g_{24}") else: m = argv.get("dim", 7) idx = argv.get("idx", 144) H = build.get(m, idx) H = shorten(H, 0) return H = H.astype(numpy.int32) count = argv.get("count", 1) if count == 0: print(H.shape) print(shortstrx(H)) print("distance:", classical_distance(H)) for ii in range(count): m, n = H.shape R = rand2(m, m) H = dot2(R, H) if ii == 0: print(H.shape) print(shortstrx(H)) assert dot2(H, H.transpose()).sum() == 0 # i0 = argv.get("i0", 0) # i1 = argv.get("i1", i0) # i2 = argv.get("i2", 1) # i3 = argv.get("i3", i2+1) items = list(range(n)) i0 = random.choice(items) items.remove(i0) i1 = i0 i2 = random.choice(items) items.remove(i2) i3 = random.choice(items) items.remove(i3) # glue i0<-->i2 and i1<-->i3 print("strong_morthogonal(%d) = %s" % (genus, strong_morthogonal(H, genus))) print() H3 = glue_self_classical(H, [(i0, i2), (i1, i3)]) print(H3.shape) print(shortstrx(H3)) print("strong_morthogonal(%d) = %s" % (genus, strong_morthogonal(H3, genus))) H = H3 print("distance:", classical_distance(H)) return H
def do_draw(c0, c1, d0, d1, Lx, Lz, Hx, Hz, idxs, LxHx, LzHz, **kw): draw = Draw(c0, c1, d0, d1) mark_xop = draw.mark_xop mark_zop = draw.mark_zop mark_idx = draw.mark_idx m, n = LxHx.shape assert rank(LxHx) == len(LxHx) assert rank(Hx) == len(Hx) assert rank(Lx) == len(Lx) #print(rank(LxHx)) #print(rank(Hx)) #print(rank(Lx)) assert rank(Lx) + rank(Hx) == len(LxHx) + 1 if 0: w = n best = None for i in range(10000): v = rand2(1, m) lop = dot2(v, LxHx)[0] #print(lop) if lop.sum() < w: best = lop w = lop.sum() print(w) mark_xop(best) all_idxs = list(range(c1 * d0 + c0 * d1)) h_idxs, v_idxs = all_idxs[:c1 * d0], all_idxs[c1 * d0:] if 0: # find Hx stabilizers with horizontal support #for idx in h_idxs: # mark_idx(idx) PHx = in_support(Hx, h_idxs) op = rand_span(PHx) mark_xop(op) # plenty... if 0: left = [draw.get_hidx(row, 0) for row in range(d0)] right = [draw.get_hidx(row, 5) for row in range(d0)] for idx in left + right: mark_idx(idx) PLx = in_support(LxHx, left + right) lop = rand_span(PLx) mark_xop(lop) PLx_left = in_support(LxHx, left) PLx_right = in_support(LxHx, right) PLx = numpy.concatenate((PLx_left, PLx_right)) assert rank(PLx) == rank(PLx_left) + rank(PLx_right) U = solve(PLx.transpose(), lop) assert U is not None #print(U) draw.save("output.logop") return for op in Lx: #cl = color.rgb(0.2*random(), 0.2*random(), random(), 0.5) draw.mark_op(op, st_qubit=[blue] + st_thick, r=0.06, stroke=True) for op in Lz: #cl = color.rgb(0.2*random(), random(), 0.2*random(), 0.5) draw.mark_op(op, st_qubit=[green] + st_thick, r=0.12, stroke=True) for idx in idxs: draw.mark(idx, st_qubit=[red] + st_thick, r=0.16, stroke=True) correctable = draw.cvs #draw.save("output") rows = [[], []] margin = 0.2 mkbox = lambda cvs: MarginBox(cvs, margin, 2 * margin) for op in Lx: draw = Draw(c0, c1, d0, d1) draw.mark_op(op, st_qubit=[blue] + st_thick, r=0.06, stroke=True) rows[0].append(mkbox(draw.cvs)) for op in Lz: draw = Draw(c0, c1, d0, d1) draw.mark_op(op, st_qubit=[green] + st_thick, r=0.12, stroke=True) rows[1].append(mkbox(draw.cvs)) row = [None] * len(Lx) row[0] = correctable rows.append(row) box = TableBox(rows) cvs = box.render() cvs.writePDFfile("output.pdf")
def test_overlap(n, mx, mz, k, c0, c1, d0, d1, Hx, Hz, Lx, Lz, Lxi, Lzi, LxiHx, LziHz, Ic0, Ic1, Id0, Id1, C, D, KerC, CokerD, KerD, CokerC, **kw): if 0: # This makes len(idxs) much bigger, because we # end up with logops from many different rows/cols: Lx = shuff2(Lx) Lz = shuff2(Lz) assert dot2(LxiHx, Hz.transpose()).sum() == 0 def get_logops(idxs_C, idxs_Ct, idxs_D, idxs_Dt): # Lx -------------------------------------------------------------- Ic1 = identity2(c1)[idxs_C, :] Lx_h = kron(Ic1, CokerD), zeros2(len(idxs_C) * CokerD.shape[0], c0 * d1) Lx_h = numpy.concatenate(Lx_h, axis=1) assert dot2(Lx_h, Hz.transpose()).sum() == 0 Id1 = identity2(d1)[idxs_D, :] Lx_v = zeros2(CokerC.shape[0] * len(idxs_D), c1 * d0), kron(CokerC, Id1) Lx_v = numpy.concatenate(Lx_v, axis=1) Lxi = numpy.concatenate((Lx_h, Lx_v), axis=0) # Lz -------------------------------------------------------------- KerCt = KerC.transpose() Id0 = identity2(d0)[:, idxs_Dt] Lzt_h = kron(KerCt, Id0), zeros2(c0 * d1, KerCt.shape[1] * len(idxs_Dt)) Lzt_h = numpy.concatenate(Lzt_h, axis=0) assert dot2(Hx, Lzt_h).sum() == 0 KerDt = KerD.transpose() assert KerDt.shape[0] == d1 Ic0 = identity2(c0)[:, idxs_Ct] Lzt_v = zeros2(c1 * d0, len(idxs_Ct) * KerDt.shape[1]), kron(Ic0, KerDt) Lzt_v = numpy.concatenate(Lzt_v, axis=0) assert dot2(Hx, Lzt_v).sum() == 0 Lzti = numpy.concatenate((Lzt_h, Lzt_v), axis=1) Lzi = Lzti.transpose() # checking --------------------------------------------------------- assert dot2(Hx, Lzti).sum() == 0 assert rank(Lxi) == len(Lxi) # full rank assert rank(Lzi) == len(Lzi) # full rank assert eq_span(numpy.concatenate((Lxi, Hx)), LxiHx) assert eq_span(numpy.concatenate((Lzi, Hz)), LziHz) return Lxi, Lzi print("get_bipuncture(C)") l_C, r_C = get_bipuncture(C) print("get_bipuncture(Ct)") l_Ct, r_Ct = get_bipuncture(C.transpose()) print("get_bipuncture(D)") l_D, r_D = get_bipuncture(D) print("get_bipuncture(Dt)") l_Dt, r_Dt = get_bipuncture(D.transpose()) print("done.") left_Lxi, left_Lzi = get_logops(l_C, l_Ct, l_D, l_Dt) right_Lxi, right_Lzi = get_logops(r_C, r_Ct, r_D, r_Dt) randvec = lambda U: dot2(rand2(1, len(U)), U) print("shapes:", left_Lxi.shape, right_Lzi.shape, left_Lzi.shape, right_Lxi.shape) #while 1: for trial in range(100): l_op = randvec(left_Lxi) * randvec(right_Lzi) # intersection r_op = randvec(right_Lxi) * randvec(left_Lzi) # intersection assert (l_op * r_op).sum() == 0 op = l_op + r_op # union idxs = numpy.where(op)[0] print("(%.2f)" % (len(idxs) / n), end="", flush=True) assert is_correctable(**locals()) print() l_op = zeros2(n) for lx in left_Lxi: for lz in right_Lzi: lxz = lx * lz l_op += lxz r_op = zeros2(n) for lx in right_Lxi: for lz in left_Lzi: lxz = lx * lz r_op += lxz assert (l_op * r_op).sum() == 0 op = l_op + r_op #op = l_op idxs = numpy.where(op)[0] print("--> (%.2f)\n" % (len(idxs) / n), end="", flush=True) #assert is_correctable(**locals()) draw = Draw(c0, c1, d0, d1) for idx in idxs: draw.mark_idx(idx) if 0: #draw.mark_xop(Lx[0]) for xop in Lxi: draw.mark_xop(xop) #draw.mark_zop(Lz[0]) for zop in Lzi: draw.mark_zop(zop) # h_mark assert KerC.shape[1] == c1 for j, op in enumerate(KerC): for i in range(c1): row = i col = -j - 1 if op[i]: draw.h_mark(row, col, st_qubit=[green], stroke=True) else: draw.h_mark(row, col) assert CokerD.shape[1] == d0 for j, op in enumerate(CokerD): #print(op) for i in range(d0): col = i #row = -j-1 row = c1 + j if op[i]: draw.h_mark(row, col, st_qubit=[blue], stroke=True) else: draw.h_mark(row, col) # v_mark assert KerD.shape[1] == d1 for j, op in enumerate(KerD): for i in range(d1): col = i #row = -j-1 row = c0 + j if op[i]: draw.v_mark(row, col, st_qubit=[green], stroke=True) else: draw.v_mark(row, col) assert CokerC.shape[1] == c0 for j, op in enumerate(CokerC): #print(op) for i in range(c0): row = i col = -j - 1 if op[i]: draw.v_mark(row, col, st_qubit=[blue], stroke=True) else: draw.v_mark(row, col) draw.save("output.2") return is_correctable(**locals())
def find_triorth(m, k): # 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) if 0: A = list(span(G)) print(strong_morthogonal(A, 1)) print(strong_morthogonal(A, 2)) print(strong_morthogonal(A, 3)) G = [row for row in G if row.sum() % 2 == 0] return array2(G) #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 test(A, B, ma, na, mb, nb, Ina, Ima, Inb, Imb, ka, kb, kat, kbt, k, KerA, KerB, CokerA, CokerB, Lzi, Lxi, Hzi, Hxi, **kw): #print("ka=%s, kat=%s, kb=%s, kbt=%s"%(ka, kat, kb, kbt)) assert k == ka*kbt + kat*kb == len(Lzi) == len(Lxi) KerA = KerA.transpose() # use convention in paper KerB = KerB.transpose() # use convention in paper CokerA = CokerA.transpose() # use convention in paper CokerB = CokerB.transpose() # use convention in paper blocks = [ [kron(KerA, Imb), zeros2(na*mb, ma*kb), kron(Ina, B)], [zeros2(ma*nb, ka*mb), kron(Ima, KerB), kron(A,Inb)], ] print("blocks:", [[X.shape for X in row] for row in blocks]) #print(shortstrx(*blocks[0])) #print() #print(shortstrx(*blocks[1])) Hzt = cat((blocks[0][2], blocks[1][2]), axis=0) K = find_kernel(Hzt) assert len(K) == ka*kb # see proof of Lemma 3 Lzv = cat((blocks[0][0], blocks[1][0])).transpose() Lzh = cat((blocks[0][1], blocks[1][1])).transpose() assert dot2(Hxi, Lzv.transpose()).sum() == 0 # Hz = Hzt.transpose() # Hzi = linear_independent(Hz) # Lzhi = independent_logops(Lzh, Hzi, verbose=True) # print("Lzhi:", Lzhi.shape) # -------------------------------------------------------- # basis for all logops, including stabilizers lz = find_kernel(Hxi) # returns transpose of kernel #lz = rand_rowspan(lz) #print("lz:", lz.shape) assert len(lz) == k+len(Hzi) # vertical qubits Iv = cat((identity2(na*mb), zeros2(ma*nb, na*mb)), axis=0).transpose() # horizontal qubits Ih = cat((zeros2(na*mb, ma*nb), identity2(ma*nb)), axis=0).transpose() assert len(intersect(Iv, Ih))==0 # sanity check # now restrict these logops to vertical qubits #print("Iv:", Iv.shape) lzv = intersect(Iv, lz) #print("lzv:", lzv.shape) J = intersect(lzv, Lzv) assert len(J) == len(lzv) # -------------------------------------------------------- # now we manually build _lz supported on vertical qubits x = rand2(ka*mb, ka*nb) y = kron(KerA, Inb) assert eq2(dot2(blocks[0][2], y), kron(KerA, B)) v = (dot2(blocks[0][0], x) + dot2(blocks[0][2], y)) % 2 h = zeros2(ma*nb, v.shape[1]) _lzt = cat((v, h)) assert dot2(Hxi, _lzt).sum() == 0 #print(shortstr(_lzt)) _lz = _lzt.transpose() _lz = linear_independent(_lz) #print("*"*(na*mb)) #print(shortstr(_lz)) assert len(intersect(_lz, Ih)) == 0 assert len(intersect(_lz, Iv)) == len(_lz) J = intersect(_lz, lz) assert len(J) == len(_lz) J = intersect(_lz, Lzv) #print(J.shape, _lz.shape, Lzv.shape) assert len(J) == len(_lz) if 0: V = cat(blocks[0][:2], axis=1) H = cat(blocks[1][:2], axis=1) X = cat((V, H), axis=0) K = find_kernel(X) print(K.shape) V = cat(blocks[0], axis=1) H = cat(blocks[1], axis=1) X = cat((V, H), axis=0) K = find_kernel(X) print(K.shape) #print("-"*(ka*mb+ma*kb)) I = cat((identity2(ka*mb+ma*kb), zeros2(ka*mb+ma*kb, na*nb)), axis=1) J = intersect(K, I) print("J:", J.shape)