def dumb_correction(self, syndromes, origin=False): """ Connects all detection events to the closest boundary of the appropriate type. Simple dumb decoder. Throughout this method, we will treat a Z syndrome as indicating a Z error. Note that these syndromes are supported on the X ancillas of the layout and vice versa. There's an optional argument, origin, that will just move all observed syndromes to the same corner of the lattice, near the origin. TODO: Make it work with toric BC? """ corr_dict = {'Z': sp.Pauli(), 'X': sp.Pauli()} if origin: for err, synd, pt in zip('ZX', syndromes, [(0, 0), (2, 0)]): crds = [self.layout.map.inv[_] for _ in synd] corr_dict[err] *= product( [self.path_pauli(_, pt, err) for _ in crds]) else: for err, synd in zip('ZX', syndromes): crds = [self.layout.map.inv[_] for _ in synd] corr_dict[err] *= product([ self.path_pauli(_, self.bdy_info(_)[1], err) for _ in crds ]) return corr_dict['X'], corr_dict['Z']
def add2check(extractor, err_synd, two_err, anc, check_tp, alph_order=True): tp = check_tp ntp = 'XZ'.replace(tp, '') for i in range(len(extractor[tp])): if len(list(extractor[tp][i])) == 3: # if it is a two-qubit gate for err in two_err: # add 16 kinds of pauli errors one by one error = sp.Pauli() if err[0] == 'X' or err[0] == 'Y': error *= sp.Pauli({int(list(extractor[tp][i])[1])}, {}) if err[0] == 'Z' or err[0] == 'Y': error *= sp.Pauli({}, {int(list(extractor[tp][i])[1])}) if err[1] == 'X' or err[1] == 'Y': error *= sp.Pauli({int(list(extractor[tp][i])[-1])}, {}) if err[1] == 'Z' or err[1] == 'Y': error *= sp.Pauli({}, {int(list(extractor[tp][i])[-1])}) synd1 = {'X': [], 'Z': []} synd1[tp], error = esm_apply_err(extractor[tp], anc, error, rg=i + 1, bk=True) # The execution breaks here because we assume if we see a syndrome or a flag, # we will stop this round and start a full of new round # if extractor[ntp]: # synd1[ntp], error = esm_apply_err(extractor[ntp], anc, error) # synd1[ntp] = set() # apply these errors to the extractors to get the syndromes of next perfect round synd2 = {'X': [], 'Z': []} if alph_order: synd2[tp], error = esm_apply_err(extractor[tp], anc, error) if extractor[ntp]: synd2[ntp], error = esm_apply_err( extractor[ntp], anc, error) else: synd2[ntp], error = esm_apply_err(extractor[ntp], anc, error) if extractor[tp]: synd2[tp], error = esm_apply_err( extractor[tp], anc, error) # define a tuple here, the first (last) two elements are the synds from the first(second) round of esm synd_list = (tuple(synd1['X']), tuple(synd1['Z']), tuple(synd2['X']), tuple(synd2['Z'])) if synd_list in err_synd.keys(): if (error.x_set, error.z_set) in err_synd[synd_list]: pass else: err_synd[synd_list].append((error.x_set, error.z_set)) else: err_synd[synd_list] = [(error.x_set, error.z_set)] else: pass return err_synd
def run(self): err_synds = self.generate_err_synd_pairs() ft_value = 0 for synd_list in err_synds: for i in err_synds[synd_list]: if len(i[0]) == 2 or len(i[1]) == 2: if (set(synd_list[0]) | set(synd_list[1])) & set(self.q_flag): pass else: print('synds are', synd_list) print('errs are', i) raise('two errors but no flags') if len(err_synds[synd_list]) > 1: for i in range(len(err_synds[synd_list])-1): for j in range(i+1, len(err_synds[synd_list])): err1 = sp.Pauli() err2 = sp.Pauli() err1 *= sp.Pauli(err_synds[synd_list][i][0], err_synds[synd_list][i][1]) err2 *= sp.Pauli(err_synds[synd_list][j][0], err_synds[synd_list][j][1]) # check the fault-tolerance by checking whether the multiplication of two errors # is a logical. This should be ok for esm circuit checking, but how about logical gates for stab in self.init_stabs: if (err1 * err2).com(stab): print(err1,'err1') print(err2,'err2') ft_value = 0 print('One case that errors are going to be a logical,here') print(synd_list,'synd_list') print(synd_list, err_synds[synd_list]) return ft_value else: pass if (err1 * err2).com(self.init_logs[0]): ft_value = 0 print('One case that could cause a logical error') print(synd_list, err_synds[synd_list]) return ft_value elif (err1 * err2).com(self.init_logs[1]): ft_value = 0 print('One case that could cause a logical error') print(synd_list, err_synds[synd_list]) return ft_value else: ft_value = 1 else: ft_value = 1 return ft_value
def logicals(self): x_set = [ self.map[_] for _ in filter(lambda pr: pr[0] == 1, self.datas) ] z_set = [ self.map[_] for _ in filter(lambda pr: pr[1] == 1, self.datas) ] return [sp.Pauli(x_set, []), sp.Pauli([], z_set)]
def dist_7_single_sample(): test_layout = cm.SCLayoutClass.SCLayout(7) x_stabs = list(test_layout.stabilisers()['X'].values()) ds = [test_layout.map[d] for d in test_layout.datas] x_errs = [sp.Pauli({d}, set()) for d in ds] x_errs += [sp.Pauli(pr, set()) for pr in it.combinations(ds, r=2)] log = test_layout.logicals()[0] prob_dist = single_prob(weight_dist(x_stabs, x_errs[52], log, x_errs[52]), 0.01)
def dist_5_check(): """ runs weight-1 and 2 errors for a dist-5 SC """ test_layout = cm.SCLayoutClass.SCLayout(5) x_stabs = list(test_layout.stabilisers()['X'].values()) ds = [test_layout.map[d] for d in test_layout.datas] x_errs = [sp.Pauli({d}, set()) for d in ds] x_errs += [sp.Pauli(pr, set()) for pr in it.combinations(ds, r=2)] log = test_layout.logicals()[0] for err in x_errs: prob_dist = single_prob(weight_dist(x_stabs, err, log, err), 0.01) if prob_dist[0] < 2.: #dummy print(err, prob_dist)
def half_err_synd_pairs(self,err_synd,f_c1,f_c2,one_type_ancillas): for i in range(len(f_c1)): if len(list(f_c1[i])) == 3: # if it is a two-qubit gate for err in self.two_error: # add 16 kinds of pauli errors one by one error = sp.Pauli() if err[0] == 'X' or err[0] == 'Y': error *= sp.Pauli({int(list(f_c1[i])[1])}, {}) if err[0] == 'Z' or err[0] == 'Y': error *= sp.Pauli({}, {int(list(f_c1[i])[1])}) if err[1] == 'X' or err[1] == 'Y': error *= sp.Pauli({int(list(f_c1[i])[-1])}, {}) if err[1] == 'Z' or err[1] == 'Y': error *= sp.Pauli({}, {int(list(f_c1[i])[-1])}) syndromes1, error = esm_apply_err(f_c1, one_type_ancillas, error, rg=i+1, bk=False) # The execution breaks here because we assume if we see a # syndrome or a flag, we will stop this round and start a full of # new round syndromes2, error = esm_apply_err_skip_P(f_c2, self.ancillas, error) syndromes1_x = (syndromes1 & self.x_ancillas) syndromes1_z = (syndromes1 & self.z_ancillas) syndromes2_x = (syndromes2 & self.x_ancillas) syndromes2_z = (syndromes2 & self.z_ancillas) synd_list = (tuple(syndromes1_x),tuple(syndromes1_z),tuple(syndromes2_x),tuple(syndromes2_z)) # if syndromes1_x == set() and syndromes1_z == set(): # pass if synd_list in err_synd.keys(): if (error.x_set, error.z_set) in err_synd[synd_list]: pass else: err_synd[synd_list].append((error.x_set,error.z_set)) else: err_synd[synd_list]= [(error.x_set,error.z_set)] else: pass return(err_synd)
def weight_dist(stab_gens, identity, log, coset_rep): """ If we iterate over an entire stabiliser group, we can calculate the weight of each Pauli of the form c * l * s for c a coset representative for the normaliser of the stabiliser, l a choice of logical operator, and s a stabiliser. Colloquially, c is generated by a one-to-one map from the syndromes. The weight will range from 0 to nq, where nq is the number of qubits in the system, and there'll be many Paulis with each weight. Therefore, we return an array full of counts. """ # First get nq nq = len(reduce(union, (s.support() for s in stab_gens))) wt_counts_I = np.zeros((nq + 1, ), dtype=np.int_) wt_counts_L = np.zeros((nq + 1, ), dtype=np.int_) i = 0 for subset in powerset(stab_gens): i += 1 s = reduce(mul, subset, sp.Pauli()) wt_counts_I[(s * identity * coset_rep).weight()] += 1 wt_counts_L[(s * log * coset_rep).weight()] += 1 #if i % 1000 == 0: # print(i) return [wt_counts_I, wt_counts_L]
def run_second_round(self, err, synd1, i): corr = sp.Pauli() synd2 = set() subcir = self.esm_circuits[i + 2] synd_err = circ2err( subcir, fowler_model(subcir, self.p1, self.p1, self.p1, self.pI), err, self.ancillas) synd2 |= synd_err[0] err = synd_err[1] # Choose lut decoder based on whether there is a flag, then find corrections flag_qs = synd1 & set(self.q_flag) synd_qx = sorted(synd2 & set(self.q_syndx)) synd_qz = sorted(synd2 & set(self.q_syndz)) if len(flag_qs): try: corr *= self.lut_flag[tuple(flag_qs)][tuple(synd_qx)] corr *= self.lut_flag[tuple(flag_qs)][tuple(synd_qz)] except KeyError: pass else: try: corr *= self.lut_synd[tuple(synd_qz)] corr *= self.lut_synd[tuple(synd_qx)] except KeyError: pass return (err, synd2, corr)
def check_hld(outputname, trials, qeccode): import tensorflow as tf with tf.device('/CPU:0'): model = tf.keras.models.load_model('nn_model/' + outputname + '.model') no_error = 0 total_errors = 0 for trial in range(trials): for key in qeccode.datas: qeccode.fnl_errsx[key] = 0 qeccode.fnl_errsz[key] = 0 synd_list = np.zeros((qeccode.num_anc * 2)) synd_zeros = np.zeros((qeccode.num_anc * 2)) corr = sp.Pauli() err, synd1 = qeccode.run_first_round() if len(synd1): err, synd2, corr = qeccode.run_second_round(err, synd1) else: synd2 = dict() no_error += 1 err_fnl = err err *= corr synd_fnl = set() for i in range(len(qeccode.esm_circuits)): subcir = qeccode.esm_circuits[i] synd_err = circ2err(subcir, fowler_model(subcir, 0, 0, 0), err, qeccode.ancillas) synd_fnl |= synd_err[0] err = synd_err[1] err *= qeccode.lut_synd[tuple( sorted(synd_fnl & set(qeccode.q_syndx)))] err *= qeccode.lut_synd[tuple( sorted(synd_fnl & set(qeccode.q_syndz)))] err_tp = singlelogical_error_index(err, qeccode.init_logs) for key in err_fnl.x_set: qeccode.fnl_errsx[key] = 1 for key in err_fnl.z_set: qeccode.fnl_errsz[key] = 1 for i in range(qeccode.num_anc): if qeccode.ancillas[i] in synd1: if qeccode.ancillas[i] in set(qeccode.q_flag): synd_list[i] = 1 for j in range(qeccode.num_anc): if qeccode.ancillas[j] in synd2: synd_list[j + qeccode.num_anc] = 1 if (synd_list != synd_zeros).any(): synd_list = np.reshape(synd_list, (1, 16)) pred = model.predict(synd_list) if err_tp != np.argmax(pred): total_errors += 1 print(total_errors) return (total_errors, no_error)
def run(self, trials=1): total_errors = 0 no_synd = 0 for trial in range(trials): corr = sp.Pauli() synd2 = set() err, synd1, break_point = self.run_first_round() if len(synd1): err, synd2, corr = self.run_second_round( err, synd1, break_point) else: no_synd += 1 err_fnl = err err *= corr # run another perfect round to clean the left errors synd_fnl = set() for i in range(2): subcir = self.esm_circuits[i] synd_err = circ2err(subcir, fowler_model(subcir, 0, 0, 0), err, self.ancillas) synd_fnl |= synd_err[0] err = synd_err[1] err *= self.lut_synd[tuple(sorted(synd_fnl & set(self.q_syndx)))] err *= self.lut_synd[tuple(sorted(synd_fnl & set(self.q_syndz)))] # # check logical errors err_tp = singlelogical_error(err, self.init_logs) self.errors[err_tp] += 1 return (self.errors)
def full_weight_dist(stab_gens, log_x, log_z, coset_rep): """ I revert to full Pauli calculations in order to look at depolarizing errors. """ # First get nq nq = len(reduce(union, (s.support() for s in stab_gens))) wt_counts = { 'I': np.zeros((nq + 1, ), dtype=np.int_), 'X': np.zeros((nq + 1, ), dtype=np.int_), 'Y': np.zeros((nq + 1, ), dtype=np.int_), 'Z': np.zeros((nq + 1, ), dtype=np.int_) } coset_rep_X = coset_rep * log_x coset_rep_Y = coset_rep * log_x * log_z coset_rep_Z = coset_rep * log_z for subset in powerset(stab_gens): s = reduce(mul, subset, sp.Pauli()) wt_counts['I'][(s * coset_rep).weight()] += 1 wt_counts['X'][(s * coset_rep_X).weight()] += 1 wt_counts['Y'][(s * coset_rep_Y).weight()] += 1 wt_counts['Z'][(s * coset_rep_Z).weight()] += 1 return wt_counts
def run_hld(self, trials, ds): no_error = 0 total_errors = 0 for trial in range(trials): for key in self.datas: self.fnl_errsx[key] = 0 self.fnl_errsz[key] = 0 synd_list = ['0'] * self.num_anc * 2 synd_zeros = '0' * self.num_anc * 2 corr = sp.Pauli() err, synd1, breakpoint = self.run_first_round() if len(synd1): err, synd2, corr = self.run_second_round( err, synd1, breakpoint) else: synd2 = dict() no_error += 1 err_fnl = err err *= corr # run another perfect round to clean the left errors synd_fnl = set() for i in range(len(self.esm_circuits)): subcir = self.esm_circuits[i] synd_err = circ2err(subcir, fowler_model(subcir, 0, 0, 0), err, self.ancillas) synd_fnl |= synd_err[0] err = synd_err[1] err *= self.lut_synd[tuple(sorted(synd_fnl & set(self.q_syndx)))] err *= self.lut_synd[tuple(sorted(synd_fnl & set(self.q_syndz)))] # # check logical errors err_tp = self.singlelogical_error_index(err, self.init_logs) for key in err_fnl.x_set: self.fnl_errsx[key] = 1 for key in err_fnl.z_set: self.fnl_errsz[key] = 1 synd_str = self.set_to_list_with_syndromes(synd1, synd2, synd_list) if synd_str != synd_zeros: if synd_str in ds: if err_tp != np.argmax(ds[synd_str]): total_errors += 1 elif err_tp != 0: total_errors += 1 print(synd1, 'synd1') print(synd2, 'synd2') print(synd_str, 'synd_str') print('not in dataset') return (total_errors, no_error)
def mwe(sim, syndromes, stabs, logs): """ Input: where XXXX and ZZZZ stabilisers are violated, respectively. Output: minimum-weight Pauli that matches the syndrome. """ # Pauli must be a product of a dumb correction, a logical and a # stabiliser: dumb_corr = reduce(mul, sim.dumb_correction(syndromes), sp.Pauli()) all_logs = [reduce(mul, _, sp.Pauli()) for _ in powerset(logs)] curr_corr = dumb_corr for stab_set in powerset(stabs): cs_prod = reduce(mul, stab_set, dumb_corr) for log in all_logs: test_corr = cs_prod * log if test_corr.weight() < curr_corr.weight(): curr_corr = test_corr return curr_corr
def meas(self, op): """ Here, we measure the eigenvalue of a new operator `op`, which we hope has eigenvalue +/- 1. This is Gottesman-Knill style. If the new operator is a logical, we return a random bit, effectively assuming that the state is uniformly mixed over the stabiliser subspace. """ if type(op) != sp.Pauli: raise ValueError("op must be Pauli: " "{} entered.".format(op)) acom_stabs = [_ for _ in self.stabs if _.com(op)] acom_logs = [_ for _ in self.logs if _.com(op)] if not (self.stabs) and not (self.logs): self.stabs.append(op) elif not (acom_stabs): try: aug_mat = self._augmented_matrix(op) # decomp = gf2.solve_augmented(aug_mat) decomp = _c_solve_augmented(aug_mat) s = prod([a for a, b in zip(self.stabs, decomp) if b]) comparison = s * op assert comparison.weight() == 0 return -1 if comparison.ph else 1 # UNSAFE except: # inconsistent system, we assume # stab_log_mat = np.vstack( # [pauli2vec(s, self.qubits) for s in self.stabs + self.logs] # ).T # aug_mat = np.hstack([stab_log_mat, pauli2vec(op, self.qubits).T]) # decomp = gf2.solve_augmented(aug_mat) meas_result = 2 * randint(2) - 1 # eliminate the proper logicals from self.logs if len(self.logs) > 2: raise NotImplementedError( "Logical measurements in k > 1 codes not supported.") self.logs = [] self.stabs.append(meas_result * op) return meas_result else: acom_p = (acom_stabs + acom_logs)[0] for p in acom_logs: self.logs.remove(p) if p != acom_p: self.logs.append(acom_p * p) for p in acom_stabs: self.stabs.remove(p) if p != acom_p: self.stabs.append(acom_p * p) meas_result = 2 * randint(2) - 1 self.stabs.append(sp.Pauli(op.x_set, op.z_set, meas_result - 1)) return meas_result
def path_pauli(self, crd_0, crd_1, err_type): """ Returns a minimum-length Pauli between two ancillas, given the type of error that joins the two. This function is awkward, because it works implicitly on the un-rotated surface code, first finding a "corner" (a place on the lattice for the path to turn 90 degrees), then producing two diagonal paths on the rotated lattice that go to and from this corner. """ mid_v = diag_intersection(crd_0, crd_1, self.ancillas.values()) pth_0, pth_1 = diag_pth(crd_0, mid_v), diag_pth(mid_v, crd_1) #path on lattice, uses idxs p = [self.map[crd] for crd in list(pth_0) + list(pth_1)] pl = sp.Pauli(p, []) if err_type == 'X' else sp.Pauli([], p) return pl
def int_to_pauli(intgr, lbl_lst): """ This one needs a little explaining. I convert every integer with an even number of bits to a Pauli by splitting the integer into a left (big) half and a right (small) half. Locations where the left half is 1 are elements of the `x_set`. Locations where the right half is 1 are elements of the `z_set`. """ bits = bin(intgr)[2:].zfill(2 * len(lbl_lst)) xs, zs = bits[:len(lbl_lst)], bits[len(lbl_lst):] return sp.Pauli({l for l, b in zip(lbl_lst, xs) if b == '1'}, {l for l, b in zip(lbl_lst, zs) if b == '1'})
def run_first_round(self): err = sp.Pauli() synd1 = set() for i in range(2): subcir = self.esm_circuits[i] synd_err = circ2err( subcir, fowler_model(subcir, self.p1, self.p1, self.p1, self.pI), err, self.ancillas) synd1 |= synd_err[0] err = synd_err[1] if len(synd1): # if there is a syndrome or a flag, then stop this round break return (err, synd1, i)
def stabilisers(self): """ Sometimes it's convenient to have the stabilisers of a surface code, especially when doing a 2d example. """ x_stabs, z_stabs = {}, {} #TODO: Fix Copypasta, PEP8 me. for crd_tag, shft_tag in zip(['x_sq', 'x_top', 'x_bot'], ['A', 'S', 'N']): for crd in self.ancillas[crd_tag]: pauli = sp.Pauli( x_set=[self.map[ad(crd, dx)] for dx in SHIFTS[shft_tag]]) x_stabs[self.map[crd]] = pauli for crd_tag, shft_tag in zip(['z_sq', 'z_left', 'z_right'], ['A', 'E', 'W']): for crd in self.ancillas[crd_tag]: pauli = sp.Pauli( z_set=[self.map[ad(crd, dx)] for dx in SHIFTS[shft_tag]]) z_stabs[self.map[crd]] = pauli return {'X': x_stabs, 'Z': z_stabs}
def err_synd_parallel(self): # reset all the syndromes and errs to be 0 for key in self.ancillas: self.synds1[key] = 0 self.synds2[key] = 0 for key in self.datas: self.fnl_errsx[key] = 0 self.fnl_errsz[key] = 0 corr = sp.Pauli() synd2 = set() err, synd1 = self.run_first_round() if len(synd1): err, synd2, corr = self.run_second_round(err, synd1) err_fnl = err err *= corr # run another perfect round to clean the left errors synd_fnl = set() for i in range(len(self.esm_circuits)): subcir = self.esm_circuits[i] synd_err = circ2err(subcir, fowler_model(subcir, 0, 0, 0), err, self.ancillas) synd_fnl |= synd_err[0] err = synd_err[1] err *= self.lut_synd[tuple(sorted(synd_fnl & set(self.q_syndx)))] err *= self.lut_synd[tuple(sorted(synd_fnl & set(self.q_syndz)))] err_tp = singlelogical_error_index(err, self.init_logs) for key in synd1: self.synds1[key] = 1 for key in synd2: self.synds2[key] = 1 for key in err_fnl.x_set: self.fnl_errsx[key] = 1 for key in err_fnl.z_set: self.fnl_errsz[key] = 1 fnl_synd = list(self.synds1.values()) + list(self.synds2.values()) fnl_err = list(self.fnl_errsx.values()) + list(self.fnl_errsz.values()) return np.array(fnl_synd), np.array(fnl_err), err_tp
def history(self, final_perfect_rnd=True): """ Produces a list of sparse_pauli.Paulis that track the error through the `n_meas` measurement rounds. """ #ancillas (for restricting error to data bits) ancs = {self.layout.map[anc] for anc in sum(self.layout.ancillas.values(), [])} err_hist = [] synd_hist = {'X': [], 'Z': []} #perfect (quiescent state) initialization err = sp.Pauli() for meas_dx in range(self.n_meas): #just the ones synd = {'X': set(), 'Z': set()} #run circuit for stp, mdl in zip(self.extractor, self.gate_error_model): #run timestep, then sample new_synds, err = cm.apply_step(stp, err) err *= product(_.sample() for _ in mdl) for ki in synd.keys(): synd[ki] |= new_synds[ki][1] #last round of circuit, because there are n-1 errs, n gates new_synds, err = cm.apply_step(self.extractor[-1], err) for ki in synd.keys(): synd[ki] |= new_synds[ki][1] # remove remaining errors on ancilla qubits before append # (they contain no information) err.prep(ancs) for key in 'XZ': synd_hist[key].append(synd[key]) err_hist.append(err) if final_perfect_rnd: synd = {'X': set(), 'Z': set()} for ki, val in synd.items(): for idx, stab in self.layout.stabilisers()[ki].items(): if err.com(stab) == 1: val |= {idx} for key in 'XZ': synd_hist[key].append(synd[key]) return err_hist, synd_hist
def correction(self, synds, metric=None, bdy_info=None): """ Given a set of recorded syndromes, returns a correction by minimum-weight perfect matching. In order to 'make room' for correlated decoders, X and Z syndromes are passed in as a single object, and any splitting is performed inside this method. Also, a single correction Pauli is returned. metric should be a function, so you'll have to wrap a matrix in table-lookup if you want to use one. bdy_info is a function that takes a flip and returns the distance to the closest boundary point """ n = self.layout.n x = self.layout.map.inv if metric is None: metric = lambda flp_1, flp_2: self.manhattan_metric(flp_1, flp_2) if bdy_info is None: bdy_info = lambda flp: self.manhattan_bdy_tpl(flp) flip_idxs = flat_flips(synds, n) # Note: 'X' syndromes are XXXX stabiliser measurement results. corr = sp.Pauli() for stab in 'XZ': matching = mwpm(flip_idxs[stab], metric, bdy_info, self.use_blossom, self.ffi, self.blossom) err = 'X' if stab == 'Z' else 'Z' for u, v in matching: if isinstance(u, int) & isinstance(v, int): corr *= self.layout.path_pauli(x[u % n], x[v % n], err) elif isinstance(u, int) ^ isinstance(v, int): vert = u if isinstance(u, int) else v bdy_pt = bdy_info(vert)[1] corr *= self.layout.path_pauli(bdy_pt, x[vert % n], err) else: pass #both boundary points, no correction #TODO: Optional return syndrome correction, test inferred syndrome error rate return corr
def unique_list(): distance = 5 x_anc_len = 12 test_layout = SCLayoutClass.SCLayout(distance) x_stabs = list(test_layout.stabilisers()['X'].values()) log = test_layout.logicals()[0] sim_test = dc.Sim2D( distance, distance, 0.01) # probability is irrelevant since we provide directly the error lst = [] z_ancs_keys = list(sim_test.layout.z_ancs()) cycles = 0 store_s = [] while len(lst) < 500: cycles += 1 rnd_err = sim_test.random_error() #print(rnd_err) synds = sim_test.syndromes(rnd_err) #print(synds[1]) dumb_x_corr, dumb_z_corr = sim_test.dumb_correction(synds) coset_rep = dumb_x_corr #sp.Pauli({5,13,22,26,33,42,46},{}) prob_dist = single_prob( weight_dist(x_stabs, sp.Pauli(), log, coset_rep), 0.01) list_z = [0] * len(z_ancs_keys) for k in synds[1]: key = sim_test.layout.map.inv[k] pos = z_ancs_keys.index(key) list_z[pos] = 1 s = '' for i in range(x_anc_len): s += str(list_z[i]) if s not in store_s: store_s.append(s) lst.append([s, prob_dist]) if cycles % 20 == 0: print(len(lst), cycles) #print('store_s ', store_s) for i in range(len(lst)): print(lst[i])
def run_second_round_with_synd_lut(self, err, synd1): corr = sp.Pauli() synd2 = set() for i in range(len(self.esm_circuits)): subcir = self.esm_circuits[i] synd_err = circ2err( subcir, fowler_model(subcir, self.p1, self.p2, self.pm, self.pI), err, self.ancillas) synd2 |= synd_err[0] err = synd_err[1] # Choose lut decoder based on whether there is a flag, then find corrections synd_qx = sorted(synd2 & set(self.q_syndx)) synd_qz = sorted(synd2 & set(self.q_syndz)) corr *= self.lut_synd[tuple(synd_qz)] corr *= self.lut_synd[tuple(synd_qx)] return (err, synd2, corr)
def dist_5_tabulate(): """ I only intend to run this function once, it's going to pickle all the weight_dist's for a distance-5 code correcting x errors. """ anc_lst = [0, 1, 8, 10, 18, 20, 28, 30, 38, 40, 47, 48] sim_5 = dc.Sim2D(5, 5, 0.01) log_x, log_z = sim_5.layout.logicals() x_stabs = list(sim_5.layout.stabilisers()['X'].values()) weight_dict = {} blsm_cosets = {} for subset in powerset(anc_lst): coset_rep = sim_5.dumb_correction([[], list(subset)], origin=True)[0] blsm_corr = sim_5.graphAndCorrection(subset, 'X') key = synd_to_bits(subset, anc_lst) val = weight_dist(x_stabs, sp.Pauli(), log_x, coset_rep) weight_dict[key] = val blsm_cosets[key] = (blsm_corr * coset_rep).com(log_z) with open('weight_counts_5.pkl', 'w') as phil: pkl.dump(weight_dict, phil) with open('blsm_cosets_5.pkl', 'w') as phil: pkl.dump(blsm_cosets, phil)
flp = [0] * no_anc flips_Z = [0] * (d + 1) for i in range(d + 1): flips_Z[i] = deepcopy(flp) no_samples = 10000 sim_test2d = dc2.Sim2D(d, d, p) sim_test = dc3.Sim3D(d, d, ('fowler', p), True) #err = sim_test.run(no_samples, progress=False, metric=None, bdy_info=None, final_perfect_rnd=True) #print(err) #calc_parity_3d(dist, meas_rnds, model, use_blossom) for cycl in range(1000000): err, synd = sim_test.history(True) rnd_err = err[2].x_set rd_er = sp.Pauli(rnd_err, []) corr_blossom = sim_test.correction(synd, metric=None, bdy_info=None) mwpm_log_err = sim_test.logical_error(err[-1], corr_blossom) synds_2d = sim_test2d.syndromes(rd_er) dumb_x_corr, dumb_z_corr = sim_test2d.dumb_correction(synds_2d, False) dumb_log_err = sim_test2d.logical_error(rd_er, dumb_x_corr, dumb_z_corr) for j in range(d + 1): for i in range(no_anc): if (z_ancs[i] in synd['Z'][j] and z_ancs[i] in synd['Z'][j+1]) or \ (z_ancs[i] not in synd['Z'][j] and z_ancs[i] not in synd['Z'][j+1]): flips_Z[j][i] = 0 else: flips_Z[j][i] = 1
def __init__(self, cir_index=2, steane=True): self.cir_index = cir_index if cir_index == 'c1_l1' or cir_index == 'c1_l2': # for cir_steane2,3 self.q_synd = [9, 11, 13, 80, 100, 120] self.q_flag = [8, 10, 12, 90, 110, 130] elif cir_index == 'c2_l1' or cir_index == 'c2_l2': # for cir_steane4,5,6 self.q_synd = [8, 10, 12, 80, 100, 120] if cir_index == 'c2_l1': self.q_flag = [9, 13, 14, 90, 130, 140] else: self.q_flag = [9, 13, 90, 130] elif cir_index == 'c3_l2': # for cir_steane7,8 self.q_synd = [8, 10, 12, 80, 100, 120] self.q_flag = [9, 90] self.ancillas = self.q_synd + self.q_flag self.init_stabs = [ sp.Pauli(x_set=[1, 2, 4, 5]), sp.Pauli(x_set=[4, 5, 6, 7]), sp.Pauli(x_set=[1, 3, 4, 7]), sp.Pauli(z_set=[1, 2, 4, 5]), sp.Pauli(z_set=[4, 5, 6, 7]), sp.Pauli(z_set=[1, 3, 4, 7]) ] self.init_logs = [ sp.Pauli(x_set=[1, 2, 3, 4, 5, 6, 7]), sp.Pauli(z_set=[1, 2, 3, 4, 5, 6, 7]) ] self.stabilisers = { 'X': { 8: sp.Pauli(x_set=[1, 2, 4, 5]), 10: sp.Pauli(x_set=[1, 3, 4, 7]), 12: sp.Pauli(x_set=[4, 5, 6, 7]) }, 'Z': { 80: sp.Pauli(z_set=[1, 2, 4, 5]), 100: sp.Pauli(z_set=[1, 3, 4, 7]), 120: sp.Pauli(z_set=[4, 5, 6, 7]) } } self.two_error = [ 'IX', 'IZ', 'IY', 'XI', 'ZI', 'YI', 'XX', 'XZ', 'XY', 'ZX', 'ZZ', 'ZY', 'YX', 'YZ', 'YY' ] self.one_error = ['IX', 'IZ', 'IY']
def lut_gen(self): # generate the look-up-table decocder for steane code extractor_x = flatten(self.ft_circuit(tp='X')) extractor_z = flatten(self.ft_circuit(tp='Z')) print('timesteps number', len(self.ft_circuit(tp='X')) + len(self.ft_circuit(tp='Z'))) print('gate number', len(extractor_x) + len(extractor_z)) cnot_n = 0 cnot_nd = 0 for i in extractor_z + extractor_x: if i[0] == 'CNOT': cnot_n += 1 if len(set(self.ancillas) & set(i[1:])) != 2: cnot_nd += 1 print('cnot number', cnot_n) print('cnot on data number', cnot_nd) err_synds = circuit_2err2(self.ancillas, extractor_z, extractor_x, two_err=self.two_error, one_err=None) lut_synd = dict() lut_flag = dict() for synd in err_synds.keys(): if len(synd[0]) + len(synd[1]) == 0: pass else: synd_r1 = set(synd[0]) | set(synd[1]) synd_r2 = set(synd[2]) | set(synd[3]) err_minx = self.init_logs[0] err_minz = self.init_logs[1] for qbt in err_synds[synd]: err = sp.Pauli(x_set=qbt[0]) if err.weight() < err_minx.weight(): err_minx = err for qbt in err_synds[synd]: err = sp.Pauli(z_set=qbt[1]) if err.weight() < err_minz.weight(): err_minz = err synd_r2 = [set(synd[2]), set(synd[3])] err_mins = [err_minz, err_minx] if synd_r1 & set(self.q_flag): # if flag1 flags flag1 = tuple(synd_r1 & set(self.q_flag)) if flag1 not in lut_flag.keys(): lut_flag[flag1] = dict() for i in range(len(synd_r2)): # loop through X and Z flag_key = tuple(sorted(synd_r2[i])) if flag_key not in lut_flag[flag1]: lut_flag[flag1][flag_key] = err_mins[i] else: if err_mins[i] != lut_flag[flag1][flag_key]: for log in self.init_logs: if (err_mins[i] * lut_flag[flag1][flag_key] ).com(log): print('synd is', str(flag_key)) print(err_mins[i]) print(lut_flag[flag1][flag_key]) print(err_synds) raise ( 'Possibly not distingushed syndromes' ) else: pass elif synd_r1 & set(self.q_synd): for i in range(len(synd_r2)): # print(synd_r2,'synd_r2') synd_key = tuple(sorted(synd_r2[i])) # print(synd_key,'synd_key') if synd_key not in lut_synd: lut_synd[synd_key] = err_mins[i] else: for log in self.init_logs: if err_mins[i] != lut_synd[synd_key]: if (err_mins[i] * lut_synd[synd_key]).com(log): print(err_synds) print('synd is', synd_key) print(err_mins[i]) print(lut_synd[synd_key]) raise ( 'Possibly not distingushed syndromes' ) # Add other pairs of syndrome:error for flag cases for flag in lut_flag.keys(): for synd in lut_synd.keys(): if synd not in lut_flag[flag].keys(): lut_flag[flag][synd] = lut_synd[synd] return lut_synd, lut_flag
def run(self): extractor_x = flatten(self.ft_circuit(tp='X')) extractor_z = flatten(self.ft_circuit(tp='Z')) err_synds = circuit_2err2(self.ancillas, extractor_z, extractor_x, two_err=self.two_error, one_err=None) ft_value = 0 for synd_list in err_synds: for i in err_synds[synd_list]: if len(i[0]) == 2 or len(i[1]) == 2: if (set(synd_list[0]) | set(synd_list[1])) & set( self.q_flag): pass else: print('synds are', synd_list) print('errs are', i) raise ('two errors but no flags') # print((set(synd_list[0]) | set(synd_list[1])) & set(self.q_flag)) if len(err_synds[synd_list]) > 1: for i in range(len(err_synds[synd_list]) - 1): for j in range(i + 1, len(err_synds[synd_list])): err1 = sp.Pauli() err2 = sp.Pauli() err1 *= sp.Pauli(err_synds[synd_list][i][0], err_synds[synd_list][i][1]) err2 *= sp.Pauli(err_synds[synd_list][j][0], err_synds[synd_list][j][1]) # check the fault-tolerance by checking whether the multiplication of two errors # is a logical. This should be ok for esm circuit checking, but how about logical gates for stab in self.init_stabs: if (err1 * err2).com(stab): ft_value = 0 print( 'One case that errors are going to be a logical' ) print(synd_list, err_synds[synd_list]) return ft_value else: pass if (err1 * err2).com(self.init_logs[0]): ft_value = 0 print('One case that could cause a logical error') print(synd_list, err_synds[synd_list]) return ft_value elif (err1 * err2).com(self.init_logs[1]): ft_value = 0 print('One case that could cause a logical error') print(synd_list, err_synds[synd_list]) return ft_value else: ft_value = 1 else: ft_value = 1 return ft_value
def __init__(self, cir_index=2): self.stabilisers = {'X': {8: sp.Pauli(x_set=[1,2,4,5]), 10: sp.Pauli(x_set=[1,3,4,7]), 12: sp.Pauli(x_set=[4,5,6,7])}, 'Z': {80: sp.Pauli(z_set=[1,2,4,5]), 100: sp.Pauli(z_set=[1,3,4,7]), 120: sp.Pauli(z_set=[4,5,6,7])}} if cir_index == 'IBM_11': self.q_synd = [8,9,10,80,90,100] self.q_flag = [11, 110] self.q_syndx = [80, 90, 100] self.q_syndz = [8, 9, 10] elif cir_index == 'IBM_12': self.q_synd = [8,10,12,80,100,120] self.q_flag = [9,11,90,110] self.q_syndx = [80,100,120] self.q_syndz = [8,10,12] elif cir_index == 'IBM_13': self.q_flag = [8,10,12,80,100,120] self.q_synd = [130, 110, 90, 9, 11, 13] self.q_syndx = [130,110,90] self.q_syndz = [9, 11, 13] elif cir_index == 's17_33': self.q_synd = [8,10,11, 80,100,110] self.q_flag = [90, 120, 130, 9, 12, 13] self.q_syndx = [80, 100, 110] self.q_syndz = [8, 10, 11] elif cir_index == 's17_222': self.q_synd = [9,11,13,90,110,130] self.q_flag = [80, 100, 120, 8, 10, 12] self.q_syndx = [90, 110, 130] self.q_syndz = [9, 11, 13] elif cir_index == 's17_13': self.q_synd = [9,11,13,90,110,130] self.q_flag = [80, 100, 120, 8, 10, 12] self.q_syndx = [9, 11, 13] self.q_syndz = [90, 110, 130] self.cir_index = cir_index self.ancillas = self.q_synd + self.q_flag self.init_stabs = [sp.Pauli(x_set=[1,2,4,5]), sp.Pauli(x_set=[4,5,6,7]), sp.Pauli(x_set=[1,3,4,7]), sp.Pauli(z_set=[1,2,4,5]), sp.Pauli(z_set=[4,5,6,7]), sp.Pauli(z_set=[1,3,4,7])] self.init_logs = [sp.Pauli(x_set=[1,2,3,4,5,6,7]), sp.Pauli(z_set=[1,2,3,4,5,6,7])] self.two_error = ['IX', 'IZ', 'IY', 'XI', 'ZI', 'YI', 'XX', 'XZ', 'XY','ZX', 'ZZ', 'ZY', 'YX', 'YZ', 'YY'] self.one_error = ['IX', 'IZ', 'IY'] self.flat_circuit = self.circuit_and_statistics()