Esempio n. 1
0
 def __init__(self, group_generators, logical_xs, logical_zs, label=None):
     self.group_generators = pc.PauliList(*group_generators)
     if (Unspecified in logical_xs) or (Unspecified in logical_zs):
         raise ValueError("Logical operators must be specified.")
     self.logical_xs = pc.PauliList(*logical_xs)
     self.logical_zs = pc.PauliList(*logical_zs)
     self.label = label
Esempio n. 2
0
    def permute_gen_ops(self, perm):
        r"""
        Returns a stabilizer code with generators related to the 
        generators of `self`, with every instance of {X,Y,Z} replaced with
        {perm[0],perm[1],perm[2]}.
        
        :param list perm: A list containing 'X','Y',  and 'Z' in any order,
            indicating which permutation is to be applied.
        
        >>> new_stab = StabilizerCode.bit_flip_code(1).permute_gen_ops('ZYX')
        >>> assert new_stab.group_generators == StabilizerCode.phase_flip_code(1).group_generators
        """

        new_group_generators = pc.PauliList()
        for pauli in self.group_generators:
            new_group_generators.append(pauli.permute_op(perm))

        new_log_xs = pc.PauliList()
        for pauli in self.logical_xs:
            new_log_xs.append(pauli.permute_op(perm))

        new_log_zs = pc.PauliList()
        for pauli in self.logical_zs:
            new_log_zs.append(pauli.permute_op(perm))

        return StabilizerCode(new_group_generators, new_log_xs, new_log_zs)
Esempio n. 3
0
    def concatenate(self, other):
        r"""
        Returns the stabilizer for a concatenated code, given the 
        stabilizers for two codes. At this point, it only works for two
        :math:`k=1` codes.
        """

        if self.nq_logical > 1 or other.nq_logical > 1:
            raise NotImplementedError(
                "Concatenation is currently only supported for single-qubit codes."
            )

        nq_self = self.nq
        nq_other = other.nq
        nq_new = nq_self * nq_other

        # To obtain the new generators, we must apply the stabilizer generators
        # to each block of the inner code (self), as well as the stabilizer
        # generators of the outer code (other), using the inner logical Paulis
        # for the outer stabilizer generators.

        # Making the stabilizer generators from the inner (L0) code is straight-
        # forward: we repeat the code other.nq times, once on each block of the
        # outer code. We use that PauliList supports tensor products.
        new_generators = sum((p.eye_p(nq_self * k) & self.group_generators
                              & p.eye_p(nq_self * (nq_other - k - 1))
                              for k in range(nq_other)), pc.PauliList())

        # Each of the stabilizer generators due to the outer (L1) code can be
        # found by computing the block-logical operator across multiple L0
        # blocks, as implemented by StabilizerCode.block_logical_pauli.
        new_generators += list(
            map(self.block_logical_pauli, other.group_generators))

        # In the same way, the logical operators are also found by mapping L1
        # operators onto L0 qubits.

        # This completes the definition of the concatenated code, and so we are
        # done.

        return StabilizerCode(
            new_generators,
            logical_xs=list(map(self.block_logical_pauli, other.logical_xs)),
            logical_zs=list(map(self.block_logical_pauli, other.logical_zs)))
Esempio 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
Esempio n. 5
0
 def logical_ys(self):
     """Derives logical :math:`Y` operators, given logical :math:`X`
     and :math:`Z` operators."""
     return pc.PauliList(
         (ex * zed).mul_phase(1)
         for (ex, zed) in zip(self.logical_xs, self.logical_zs))