def syndrome_to_recovery_operator(self, synd): r""" Returns a Pauli operator which corrects an error on the stabilizer code ``self``, given the syndrome ``synd``, a bitstring indicating which generators the implied error commutes with and anti-commutes with. :param synd: a string, list, tuple or other sequence type with entries consisting only of 0 or 1. This parameter will be certified before use. """ # If the syndrome is an integer, change it to a bitstring by # using string formatting. if isinstance(synd, int): fmt = "{{0:0>{n}b}}".format(n=self.n_constraints) synd = fmt.format(synd) # Ensures synd is a list of integers by mapping int onto the list. synd = list(map(int, synd)) # Check that the syndrome is all zeros and ones. acceptable_syndrome = all([bit == 0 or bit == 1 for bit in synd]) if not acceptable_syndrome: raise ValueError( "Please input a syndrome which is an iterable onto 0 and 1.") if len(synd) != self.nq - self.nq_logical: raise ValueError( "Syndrome must account for n-k bits of syndrome data.") # We produce commutation and anti_commutation constraints from synd. anti_coms = list(it.compress(self.group_generators, synd)) coms = list( it.compress(self.group_generators, [1 - bit for bit in synd])) for op_weight in range(self.nq + 1): #We loop over all possible weights. As soon as we find an operator #that satisfies the commutation and anti-commutation constraints, #we return it: low_weight_ops = list( map( p.remove_phase, cs.solve_commutation_constraints( coms, anti_coms, search_in_set=p.paulis_by_weight(self.nq, op_weight)))) if low_weight_ops: break return low_weight_ops[0]
def syndrome_to_recovery_operator(self,synd): r""" Returns a Pauli operator which corrects an error on the stabilizer code ``self``, given the syndrome ``synd``, a bitstring indicating which generators the implied error commutes with and anti-commutes with. :param synd: a string, list, tuple or other sequence type with entries consisting only of 0 or 1. This parameter will be certified before use. """ # If the syndrome is an integer, change it to a bitstring by # using string formatting. if isinstance(synd,int): fmt = "{{0:0>{n}b}}".format(n=self.n_constraints) synd = fmt.format(synd) # Ensures synd is a list of integers by mapping int onto the list. synd=map(int, synd) # Check that the syndrome is all zeros and ones. acceptable_syndrome = all([bit == 0 or bit == 1 for bit in synd]) if not acceptable_syndrome: raise ValueError("Please input a syndrome which is an iterable onto 0 and 1.") if len(synd) != self.nq - self.nq_logical: raise ValueError("Syndrome must account for n-k bits of syndrome data.") # We produce commutation and anti_commutation constraints from synd. anti_coms = list(it.compress(self.group_generators,synd)) coms = list(it.compress(self.group_generators,[1-bit for bit in synd])) for op_weight in range(self.nq+1): #We loop over all possible weights. As soon as we find an operator #that satisfies the commutation and anti-commutation constraints, #we return it: low_weight_ops=map(p.remove_phase, cs.solve_commutation_constraints(coms,anti_coms, search_in_set=p.paulis_by_weight(self.nq, op_weight))) if low_weight_ops: break return low_weight_ops[0]
def star_decoder(self, for_enc=None, as_dict=False): r""" Returns a tuple of a decoding Clifford and a :class:`qecc.PauliList` specifying the recovery operation to perform as a function of the result of a :math:`Z^{\otimes{n - k}}` measurement on the ancilla register. For syndromes corresponding to errors of weight greater than the distance, the relevant element of the recovery list will be set to :obj:`qecc.Unspecified`. :param for_enc: If not ``None``, specifies to use a given Clifford operator as the encoder, instead of the first element yielded by :meth:`encoding_cliffords`. :param bool as_dict: If ``True``, returns a dictionary from recovery operators to syndromes that indicate that recovery. """ def error_to_pauli(error): if error == p.I.as_clifford(): return "I" if error == p.X.as_clifford(): return "X" if error == p.Y.as_clifford(): return "Y" if error == p.Z.as_clifford(): return "Z" if for_enc is None: encoder = self.encoding_cliffords().next() else: encoder = for_enc decoder = encoder.inv() errors = pc.PauliList(p.eye_p(self.nq)) + pc.PauliList(p.paulis_by_weight(self.nq, self.n_correctable)) syndrome_dict = defaultdict(lambda: Unspecified) syndrome_meas = [p.elem_gen(self.nq, idx, 'Z') for idx in range(self.nq_logical, self.nq)] for error in errors: effective_gate = decoder * error.as_clifford() * encoder # FIXME: the following line emulates measurement until we have a real # measurement simulation method. syndrome = tuple([effective_gate(meas).ph / 2 for meas in syndrome_meas]) recovery = "".join([ # FIXME: the following is a broken hack to get the phases on the logical qubit register. error_to_pauli(c.Clifford([effective_gate.xout[idx][idx]], [effective_gate.zout[idx][idx]])) for idx in range(self.nq_logical) ]) # For degenerate codes, the syndromes can collide, so long as we # correct the same way for each. if syndrome in syndrome_dict and syndrome_dict[syndrome] != recovery: raise RuntimeError('Syndrome {} has collided.'.format(syndrome)) syndrome_dict[syndrome] = recovery if as_dict: outdict = dict() keyfn = lambda (syndrome, recovery): recovery data = sorted(syndrome_dict.items(), key=keyfn) for recovery, syndrome_group in it.groupby(data, keyfn): outdict[recovery] = [syn[0] for syn in syndrome_group] return decoder, outdict else: recovery_list = pc.PauliList(syndrome_dict[syndrome] for syndrome in it.product(range(2), repeat=self.n_constraints)) return decoder, recovery_list
def star_decoder(self, for_enc=None, as_dict=False): r""" Returns a tuple of a decoding Clifford and a :class:`qecc.PauliList` specifying the recovery operation to perform as a function of the result of a :math:`Z^{\otimes{n - k}}` measurement on the ancilla register. For syndromes corresponding to errors of weight greater than the distance, the relevant element of the recovery list will be set to :obj:`qecc.Unspecified`. :param for_enc: If not ``None``, specifies to use a given Clifford operator as the encoder, instead of the first element yielded by :meth:`encoding_cliffords`. :param bool as_dict: If ``True``, returns a dictionary from recovery operators to syndromes that indicate that recovery. """ def error_to_pauli(error): if error == p.I.as_clifford(): return "I" if error == p.X.as_clifford(): return "X" if error == p.Y.as_clifford(): return "Y" if error == p.Z.as_clifford(): return "Z" if for_enc is None: encoder = next(self.encoding_cliffords()) else: encoder = for_enc decoder = encoder.inv() errors = pc.PauliList(p.eye_p(self.nq)) + pc.PauliList( p.paulis_by_weight(self.nq, self.n_correctable)) syndrome_dict = defaultdict(lambda: Unspecified) syndrome_meas = [ p.elem_gen(self.nq, idx, 'Z') for idx in range(self.nq_logical, self.nq) ] for error in errors: effective_gate = decoder * error.as_clifford() * encoder # FIXME: the following line emulates measurement until we have a real # measurement simulation method. syndrome = tuple( [effective_gate(meas).ph / 2 for meas in syndrome_meas]) recovery = "".join([ # FIXME: the following is a broken hack to get the phases on the logical qubit register. error_to_pauli( c.Clifford([effective_gate.xout[idx][idx]], [effective_gate.zout[idx][idx]])) for idx in range(self.nq_logical) ]) # For degenerate codes, the syndromes can collide, so long as we # correct the same way for each. if syndrome in syndrome_dict and syndrome_dict[ syndrome] != recovery: raise RuntimeError( 'Syndrome {} has collided.'.format(syndrome)) syndrome_dict[syndrome] = recovery if as_dict: outdict = dict() keyfn = lambda syndrome_recovery: syndrome_recovery[1] data = sorted(list(syndrome_dict.items()), key=keyfn) for recovery, syndrome_group in it.groupby(data, keyfn): outdict[recovery] = [syn[0] for syn in syndrome_group] return decoder, outdict else: recovery_list = pc.PauliList( syndrome_dict[syndrome] for syndrome in it.product(list(range(2)), repeat=self.n_constraints)) return decoder, recovery_list