def countY(circuit): Ts = 0 Tcache = [] # recent T gates to check for any consecutive Ts Ms = [] MTs = [] # measurements depending on which a T is performed # depending on postselection we might not need those T # these are not counted in Ts variable lineNr = 0 for line in circuit.splitlines(): lineNr += 1 gate = standardGate(line, lineMode=True) # identify measured qubits if 'M' in gate: Midx = gate.index('M') if Midx not in Ms: Ms.append(Midx) # Check for operations performed on already measured qubits for Midx in Ms: if gate[Midx] not in ['M', '_']: raise SyntaxError("Line %d: Cannot perform operations to measured qubit %d" % (lineNr, Midx)) if 'T' in gate and 'M' not in gate: Ts += 1 # count T gate # Put qubit into Tcache Tidx = gate.index('T') if Tidx in Tcache: # already there: consecutive Ts raise SyntaxError("Line %d: Consecutive Ts on qubit %d" % (lineNr, Midx)) Tcache.append(Tidx) # remove qubits from Tcache if a gate is encountered for Tidx in Tcache: if gate[Tidx] != '_': Tcache.remove(Tidx) # T that is applied conditionally on a measurement if 'T' in gate and 'M' in gate: Midx = gate.index('M') MTs.append(Midx) return Ts, Ms, MTs
def gadgetize(circuit, Mdict, y): n = len(standardGate(circuit.split('\n', 1)[0], lineMode=True)) size = n + len(y) phases = [] # list of numbers in Z_4 xs = [] # lists of bitstrings zs = [] def gen(idx, val, phases, xs, zs): xs.append(np.zeros(size).astype(int)) z = np.zeros(size) z[idx] = 1 zs.append(z) phase = 0 if val == 1: phase = 2 phases.append(phase) return phases, xs, zs # set measured ancillas for M in Mdict.keys(): phases, xs, zs = gen(M, Mdict[M], phases, xs, zs) # set T ancillas for i in range(n, size): phases, xs, zs = gen(i, y[i-n], phases, xs, zs) # conjugate stabilizer generators: tpos = n # from main import printProjector # printProjector((phases, xs, zs)) for line in reversed(circuit.splitlines()): if line == "": continue gate = standardGate(line, lineMode=True) # skip if contains a measurement if 'M' in gate: Midx = gate.index('M') if Mdict[Midx] == 0: continue std = standardGate(line) if False: pr = " " for i in line: if i == "T": pr = pr + "[" + i + str(y[tpos-n]) + "]" elif i == "M": pr = pr + "[" + i + "1]" else: pr = pr + "[" + i + " ]" print(pr) idxs = [] for letter in std: idxs.append(gate.index(letter)) lookup = { "H": {(0, 0): (0, 0, 0), (1, 0): (0, 0, 1), (0, 1): (0, 1, 0), (1, 1): (2, 1, 1)}, # X->Z, Z->X "X": {(0, 0): (0, 0, 0), (1, 0): (0, 1, 0), (0, 1): (2, 0, 1), (1, 1): (2, 1, 1)}, # X->X, Z->-Z "S": {(0, 0): (0, 0, 0), (1, 0): (1, 1, 1), (0, 1): (0, 0, 1), (1, 1): (1, 1, 0)}, # X->Y, Z->Z } def conjugateLookup(std, idxs, phase, xs, zs): if (len(std) == 1): mapping = lookup[std] state = (xs[idxs[0]], zs[idxs[0]]) ph, xs[idxs[0]], zs[idxs[0]] = mapping[state] phase = (phase + ph) % 4 else: # evaluate CNOT top = "" bot = "" if xs[idxs[0]] == 1: top += "X" bot += "X" if zs[idxs[0]] == 1: top += "Z" if xs[idxs[1]] == 1: bot += "X" if zs[idxs[1]] == 1: top += "Z" bot += "Z" top = top.replace("ZZ", "") bot = bot.replace("XX", "") xs[idxs[0]] = 1 if ("X" in top) else 0 zs[idxs[0]] = 1 if ("Z" in top) else 0 xs[idxs[1]] = 1 if ("X" in bot) else 0 zs[idxs[1]] = 1 if ("Z" in bot) else 0 return phase, xs, zs for g in range(len(phases)): # update all generators if std == "T": # execute T gadget phases[g], xs[g], zs[g] = conjugateLookup("CX", [idxs[0], tpos], phases[g], xs[g], zs[g]) if y[tpos-n] == 1: phases[g], xs[g], zs[g] = conjugateLookup("S", idxs, phases[g], xs[g], zs[g]) # prepend HS^\dagger to t registers in compiled circuit for tUpdateGate in ["H", "S", "S", "S"]: phases[g], xs[g], zs[g] = conjugateLookup(tUpdateGate, [tpos], phases[g], xs[g], zs[g]) continue phases[g], xs[g], zs[g] = conjugateLookup(std, idxs, phases[g], xs[g], zs[g]) if std == "T": tpos += 1 # printProjector(([phases[0]], [xs[0]], [zs[0]])) # printProjector((phases, xs, zs)) # test = input() # print("") # printProjector((phases, xs, zs)) # raise ValueError("hi") return phases, xs, zs
def projectors(circ, measure, verbose=False, x=None, y=None): n = len(compilecirc.standardGate(circ.splitlines()[0], lineMode=True)) t, Ms, MTs = gadgetize.countY(circ) # t - number of T gates # Ms - qubits measured by circuit # MTs - qubits measured by circuit, depending on # which a T gate is performed # postselect circuit measured qubits if x is None: Mselect = np.random.randint(2, size=len(Ms)) else: tmpX = [] if len(x) != len(Ms): raise ValueError("x needs to have length %d" % len(Ms)) for l in x: if l == "0": tmpX.append(0) elif l == "1": tmpX.append(1) else: raise ValueError("Only 0s and 1s allowed in x string.") Mselect = np.array(tmpX).astype(int) # Increment t for measurement-dependent Ts for M in MTs: idx = Ms.index(M) if Mselect[idx] == 1: t += 1 # pack circuit into measurement dictionary Mdict = {} for M in Ms: idx = Ms.index(M) Mdict[M] = Mselect[idx] if M in measure.keys(): continue measure[M] = Mselect[idx] # postselect measurement results on Ts if y is None: y = np.random.randint(2, size=t) else: tmpY = [] if len(y) != t: raise ValueError("y needs to have length %d" % t) for l in y: if l == "0": tmpY.append(0) elif l == "1": tmpY.append(1) else: raise ValueError("Only 0s and 1s allowed in y string.") y = np.array(tmpY).astype(int) # Print measurement information: if verbose: print("n = %d, t = %d" % (n, t)) print("Measurement info:") mstring = "" for i in range(n): if i in measure: mstring += str(measure[i]) else: mstring += "_" print("x:", mstring) print("y:", "".join(y.astype(str).tolist())) G = gadgetize.gadgetize(circ, measure, y) H = gadgetize.gadgetize(circ, Mdict, y) return G, H, n, t # also return n and t for convenience