def get_possible_decoding_strats(self): num_qubits = len(self.gstate) # print("num_qubits", num_qubits) # get all possible 2^N stabilizers. # TODO: to be improved checking only "smart" stabilizers. all_stabs = list(q.from_generators(self.gstate.stab_gens)) poss_stabs_list = [] for stab_ix1 in range(1, (2 ** num_qubits) - 1): for stab_ix2 in range(stab_ix1, 2 ** num_qubits): stab1 = all_stabs[stab_ix1].op stab2 = all_stabs[stab_ix2].op ## checks which qubits have anticommuting Paulis anticomm_qbts = [qbt for qbt in range(num_qubits) if single_qubit_commute(stab1, stab2, qbt)] ## checks that there are exactly two qubits with anticommuting Paulis: the input and an output if len(anticomm_qbts) == 2 and self.in_qubit in anticomm_qbts: measurement = [stab1[qbt] if stab1[qbt] != 'I' else stab2[qbt] for qbt in range(num_qubits)] other_meas_qubits = [qbt for qbt in range(num_qubits) if measurement[qbt] != 'I' and qbt not in anticomm_qbts] meas_weight = num_qubits - measurement.count('I') Z_weight = measurement.count('Z') / (meas_weight + 1) poss_stabs_list.append( [anticomm_qbts, other_meas_qubits, [stab1, stab2], measurement, meas_weight, Z_weight]) # print(stab1, stab2, anticomm_qbts, other_meas_qubits, measurement, meas_weight) # ## order them such that we always prefer mstrategies with smaller weight, and with more Zs in the # non-trivial paulis. poss_stabs_list.sort(key=lambda x: x[4] - x[5]) return poss_stabs_list
def get_possible_stabs_meas(gstate, in_qubit=0): num_qubits = len(gstate) # print("num_qubits", num_qubits) # get all possible 2^N stabilizers. # TODO: to be improved checking only "smart" stabilizers. all_stabs = list(q.from_generators(gstate.stab_gens)) poss_stabs_list = [] for stab_ix1 in range(1, (2 ** num_qubits) - 1): for stab_ix2 in range(stab_ix1, 2 ** num_qubits): stab1 = all_stabs[stab_ix1].op stab2 = all_stabs[stab_ix2].op ## checks which qubits have anticommuting Paulis anticomm_qbts = [qbt for qbt in range(num_qubits) if single_qubit_commute(stab1, stab2, qbt)] ## checks that there are exactly two qubits with anticommuting Paulis: the input and an output if len(anticomm_qbts) == 2 and in_qubit in anticomm_qbts: measurement = [stab1[qbt] if stab1[qbt] is not 'I' else stab2[qbt] for qbt in range(num_qubits)] other_meas_qubits = [qbt for qbt in range(num_qubits) if measurement[qbt] is not 'I' and qbt not in anticomm_qbts] meas_weight = num_qubits - measurement.count('I') poss_stabs_list.append([anticomm_qbts, other_meas_qubits, [stab1, stab2], measurement, meas_weight]) # print(stab1, stab2, anticomm_qbts, other_meas_qubits, measurement, meas_weight) poss_stabs_list.sort(key=lambda x: x[4]) return poss_stabs_list
def ind_meas_EC_decoder(graph_state, ind_meas_op, in_qubit, include_direct_meas=False, max_error_num=None): if ind_meas_op not in ('X', 'Y', 'Z'): raise ValueError('The indirect measument Pauli operator needs to be one of (X, Y, Z)') ### gets all stabs for a graph all_stabs = [this_op.op for this_op in q.from_generators(graph_state.stab_gens)] all_stabs.remove('I' * len(all_stabs[0])) ### filter stabilizers to get only those for indirect measurement filtered_stabs_ind_meas = filter_stabs_input_op_compatible(all_stabs, ind_meas_op, in_qubit) ### identify all possible indirect measurement families meas_families = find_single_op_ind_meas_EC_families(filtered_stabs_ind_meas, ind_meas_op, in_qubit) # print(meas_families) ## select best family best_meas = max(meas_families, key=lambda x: len(meas_families[x][1])) best_fam = meas_families[best_meas] ## run decoder: build syndromes lookup table and return it return calculate_syndromes_dictionary_single_ind_meas(best_meas, best_fam[0], best_fam[1], in_qubit, include_direct_meas=include_direct_meas, max_error_num=max_error_num)
def get_indirect_meas_stabs(self): num_qubits = len(self.gstate) # print("num_qubits", num_qubits) # get all possible 2^N stabilizers. # TODO: to be improved checking only "smart" stabilizers. all_stabs = q.from_generators(self.gstate.stab_gens) poss_stabs_list = [] for this_stab0 in all_stabs: this_stab = this_stab0.op if this_stab[self.in_qubit] == self.indmeas_pauli: meas_weight = num_qubits - this_stab.count('I') Z_weight = this_stab.count('Z') / (meas_weight + 1) meas_qubits = [qbt for qbt in range(num_qubits) if ((qbt != self.in_qubit) and (this_stab[qbt] != 'I'))] ### order them such that we always prefer mstrategies with smaller weight, and with more Zs in the non-trivial paulis. poss_stabs_list.append([this_stab, meas_qubits, meas_weight, Z_weight]) poss_stabs_list.sort(key=lambda x: x[2] - x[3]) return poss_stabs_list
def teleportation_EC_decoder(graph_state, in_qubit, pref_pauli='Z', max_error_num=None): num_qbts = len(graph_state) ### gets all stabs for a graph all_stabs = [this_op.op for this_op in q.from_generators(graph_state.stab_gens)] all_stabs.remove('I' * len(all_stabs[0])) ### identify all possible teleportation measurement families meas_families = find_all_ind_meas_strats(all_stabs, in_qubit, pref_pauli=pref_pauli) # print('All families:') # print(meas_families, len(meas_families)) ## select best family if meas_families: best_meas = max(meas_families, key=lambda x: len(meas_families[x][1]) + ( 0 if len(meas_families[x][1]) == 0 else count_target_pauli_in_stabs(meas_families[x][1], pref_pauli) / ( num_qbts * len(meas_families[x][1])))) best_fam = meas_families[best_meas] # print('best_meas', best_meas) # print('best_fam', best_fam) return calculate_syndromes_dictionary_teleport(best_meas, best_fam[0][1][0], best_fam[0][1][1], best_fam[1], best_fam[0][0][0], best_fam[0][0][1], max_error_num=max_error_num) else: return False
# gstate = graphstate_from_nodes_and_edges(graph_nodes, graph_edges) ### Generate random graph # graph = gen_random_connected_graph(6) # gstate = GraphState(graph) ######################################################################################### ################################### SINGLE TEST - DECODING ############################## ######################################################################################### gstate.image(input_qubits=[in_qubit]) plt.show() ind_meas_op = 'Y' ### gets all stabs for a graph all_stabs = [this_op.op for this_op in q.from_generators(gstate.stab_gens)] all_stabs.remove('I' * len(all_stabs[0])) print(all_stabs) ### filter stabilizers to get only those for indirect measurement # filtered_stabs_ind_meas = filter_stabs_input_op_only(all_stabs, ind_meas_op, in_qubit) filtered_stabs_ind_meas = filter_stabs_input_op_compatible( all_stabs, ind_meas_op, in_qubit) print(filtered_stabs_ind_meas) ### identify all possible indirect measurement families meas_families = find_ind_meas_EC_families(filtered_stabs_ind_meas, ind_meas_op, in_qubit) print(meas_families) ## select best family
def get_teleportation_strats(self): ### gets all stabs for a graph all_stabs = [ this_op.op for this_op in q.from_generators(self.gstate.stab_gens) ] all_stabs.remove('I' * self.n_qbts) all_meas = {} for stab_ix1 in range((2**self.n_qbts) - 1): for stab_ix2 in range(stab_ix1 + 1, (2**self.n_qbts) - 1): stab1 = all_stabs[stab_ix1] stab2 = all_stabs[stab_ix2] ## checks which qubits have anticommuting Paulis anticomm_qbts = [ qbt for qbt in range(self.n_qbts) if single_qubit_commute(stab1, stab2, qbt) ] # print() # print(stab_ix1, stab1, stab_ix2, stab2, anticomm_qbts) # checks that there are exactly two qubits with anticommuting Paulis: the input and an output if len(anticomm_qbts) == 2 and self.in_qubit in anticomm_qbts: out_qubit = [ x for x in anticomm_qbts if x != self.in_qubit ][0] compatible_stabs = filter_stabs_given_qubits_ops( all_stabs, { anticomm_qbts[0]: 'I', anticomm_qbts[1]: 'I' }) non_inout_qubits = [ ix for ix in range(self.n_qbts) if ix not in anticomm_qbts ] non_inout_paulis = [ stab1[ix] if stab1[ix] != 'I' else stab2[ix] for ix in non_inout_qubits ] # non_inout_measlist = dict((x, non_inout_paulis[ix]) for x, ix in non_inout_qubits) compatible_stabs = filter_stabs_compatible_qubits_ops( compatible_stabs, dict(zip(non_inout_qubits, non_inout_paulis))) # print('good inout, compatible_stabs:', compatible_stabs) poss_ops_list = [['I'] if (ix in anticomm_qbts) else [] for ix in range(self.n_qbts)] free_qubits = [] for ix in range(self.n_qbts): if ix not in anticomm_qbts: if (stab1[ix] != 'I' or stab2[ix] != 'I'): poss_ops_list[ix] = [ stab1[ix] if stab1[ix] != 'I' else stab2[ix] ] else: poss_ops_list[ix] = ['I'] free_qubits.append(ix) # print('free_qubits', free_qubits) for this_stab in compatible_stabs: for ix, this_op in enumerate(this_stab): if ix in free_qubits: if this_op not in poss_ops_list[ix]: poss_ops_list[ix].append(this_op) # print('poss_ops_list', poss_ops_list) this_strat_all_meas = (''.join(ops) for ops in product(*poss_ops_list)) # this_strat_all_meas = list(this_strat_all_meas) # print(this_strat_all_meas) for this_meas in this_strat_all_meas: meas_comp_stabs = filter_stabs_measurement_compatible( compatible_stabs, this_meas) ## Uses these stabilizers for this measurement if this_meas is not already included tele_strat_weight = self.n_qbts - this_meas.count('I') tele_strat_prefpauli_weight = this_meas.count( self.pref_pauli) / (tele_strat_weight + 1) tele_strat_val = tele_strat_weight - tele_strat_prefpauli_weight if this_meas not in all_meas: all_meas[this_meas] = ((out_qubit, (stab1, stab2)), meas_comp_stabs, tele_strat_val) else: # If this_meas was already included, updated its strategy to this measurement if there are equal or # more compatible stabilizers than the existing case (from EC decoders), and if the weight of the # logical operator is smaller (from LT decoders). if len(meas_comp_stabs) >= len( all_meas[this_meas][1]): if tele_strat_val < all_meas[this_meas][2]: all_meas[this_meas] = ((out_qubit, (stab1, stab2)), meas_comp_stabs, tele_strat_val) return all_meas
def get_indirect_meas_strats(self): if self.indmeas_pauli not in ('X', 'Y', 'Z'): raise ValueError( 'The indirect measument Pauli operator needs to be one of (X, Y, Z)' ) ### gets all stabs for a graph all_stabs = [ this_op.op for this_op in q.from_generators(self.gstate.stab_gens) ] all_stabs.remove('I' * self.n_qbts) ### filter stabilizers to get those compatible with the indirect measurement filtered_stabs_ind_meas_comp = filter_stabs_input_op_compatible( all_stabs, self.indmeas_pauli, self.in_qubit) # print(filtered_stabs_ind_meas_comp) ### further filter all stabs to get only those forming possible valid logical operators filtered_stabs_ind_meas_only = filter_stabs_input_op_only( filtered_stabs_ind_meas_comp, self.indmeas_pauli, self.in_qubit) ### further filter all stabs get only those forming possible valid syndromes filtered_stabs_ind_meas_syndr = filter_stabs_input_op_only( filtered_stabs_ind_meas_comp, 'I', self.in_qubit) # print(filtered_stabs_ind_meas_only) # print(filtered_stabs_ind_meas_syndr) all_meas = {} for log_op_stab in filtered_stabs_ind_meas_only: # print('\nlog_op_stab:', log_op_stab) log_op_weight = self.n_qbts - log_op_stab.count('I') log_op_prefpauli_weight = log_op_stab.count( self.pref_pauli) / (log_op_weight + 1) log_op_val = log_op_weight - log_op_prefpauli_weight ### Get only the stabilizers that are also compatible with this logical operator compatible_syndromes = filter_stabs_indmeas_compatible( filtered_stabs_ind_meas_syndr, log_op_stab, self.in_qubit) # print('compatible_stabs', compatible_syndromes) poss_ops_list = [ [self.indmeas_pauli] if ix == self.in_qubit else [] for ix in range(self.n_qbts) ] free_qubits = [] ### Populate the measurement bases that are fixed and given by logical operator stabilizer for ix in range(self.n_qbts): # print('qbt_ix:', ix) if ix != self.in_qubit: if log_op_stab[ix] != 'I': # print('occupied qbt') poss_ops_list[ix] = [log_op_stab[ix]] else: # print('free qubt') free_qubits.append(ix) ### For all free qubits, include 'I' (loss) as a possible compatible measurement for ix in free_qubits: poss_ops_list[ix].append('I') # print('free_qubits:', free_qubits) ### Find all possible measurement bases for qubits with bases not fixed by the logical operator considered for this_comp_stab in compatible_syndromes: for ix, this_op in enumerate(this_comp_stab): if ix in free_qubits: if this_op not in poss_ops_list[ix]: poss_ops_list[ix].append(this_op) ### Find all possible measurements this_strat_all_meas = (''.join(ops) for ops in product(*poss_ops_list)) this_strat_all_meas = list(this_strat_all_meas) # print('this_strat_all_meas:', this_strat_all_meas) ### Update the all_meas dictionary using the measurements compatible with this logical operator. for this_meas in this_strat_all_meas: meas_comp_stabs = filter_stabs_measurement_compatible( compatible_syndromes, this_meas) ## Uses these stabilizers for this measurement if this_meas is not already included if this_meas not in all_meas: all_meas[this_meas] = (log_op_stab, meas_comp_stabs, log_op_val) # If this_meas was already included, updated its strategy to this measurement if there are equal or # more compatible stabilizers than the existing case (from EC decoders), and if the weight of the # logical operator is smaller (from LT decoders). else: if len(meas_comp_stabs) >= len(all_meas[this_meas][1]): if log_op_val < all_meas[this_meas][2]: all_meas[this_meas] = (log_op_stab, meas_comp_stabs, log_op_val) return all_meas