Ejemplo n.º 1
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 = 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]
Ejemplo n.º 2
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]
Ejemplo n.º 3
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
Ejemplo n.º 4
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 = 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