Пример #1
0
def test_basic_code_calculated_properties(stabilizers, logical_xs, logical_zs,
                                          n_k_d):
    code = BasicCode(stabilizers, logical_xs, logical_zs)
    assert tuple(pt.bsf_to_pauli(code.stabilizers)) == stabilizers
    assert tuple(pt.bsf_to_pauli(code.logical_xs)) == logical_xs
    assert tuple(pt.bsf_to_pauli(code.logical_zs)) == logical_zs
    assert tuple(pt.bsf_to_pauli(code.logicals)) == logical_xs + logical_zs
    assert code.n_k_d == n_k_d
    assert isinstance(code.label, str)
    assert isinstance(repr(code), str)
Пример #2
0
def test_ibsf(n_qubits, min_weight, max_weight):
    pauli_iter = pt.ipauli(n_qubits, min_weight, max_weight)
    bsf_iter = pt.ibsf(n_qubits, min_weight, max_weight)
    assert all(
        p == pt.bsf_to_pauli(b)
        for p, b in itertools.zip_longest(pauli_iter, bsf_iter)), (
            'BSF iterator does not correspond to equivalent Pauli iterator.')
Пример #3
0
 def h_node_value(self, prob_dist, f, n, e, s, w):
     """Return horizontal edge tensor element value."""
     paulis = ('I', 'X', 'Y', 'Z')
     op_to_pr = dict(zip(paulis, prob_dist))
     f = pt.pauli_to_bsf(f)
     I, X, Y, Z = pt.pauli_to_bsf(paulis)
     # n,e,s,w are in {0,1} so multiply op to turn on or off
     op = (f + (n * Z) + (e * X) + (s * Z) + (w * X)) % 2
     return op_to_pr[pt.bsf_to_pauli(op)]
Пример #4
0
 def q_node_value(self, prob_dist, f, n, e, s, w):
     """Return qubit tensor element value."""
     paulis = ('I', 'X', 'Y', 'Z')
     op_to_pr = dict(zip(paulis, prob_dist))
     f = pt.pauli_to_bsf(f)
     # n, e, s, w are in {0, 1, 2, 3} so create dict from index to op
     index_to_op = dict(zip((0, 1, 2, 3), pt.pauli_to_bsf(paulis)))
     # apply ops from indices to f
     op = (f + index_to_op[n] + index_to_op[e] + index_to_op[s] +
           index_to_op[w]) % 2
     # return probability of op
     return op_to_pr[pt.bsf_to_pauli(op)]
Пример #5
0
    def _coset_probabilities(self, prob_dist, hadamard_vec,hadamard_mat, sample_pauli):
        r"""
        Return the (approximate) probability and sample Pauli for the left coset :math:`fG` of the stabilizer group
        :math:`G` of the planar code with respect to the given sample Pauli :math:`f`, as well as for the cosets
        :math:`f\bar{X}G`, :math:`f\bar{Y}G` and :math:`f\bar{Z}G`.

        :param prob_dist: Tuple of probability distribution in the format (P(I), P(X), P(Y), P(Z)).
        :type prob_dist: 4-tuple of float
        :param sample_pauli: Sample planar Pauli.
        :type sample_pauli: PlanarPauli
        :return: Coset probabilities, Sample Paulis (both in order I, X, Y, Z)
            E.g. (0.20, 0.10, 0.05, 0.10), (PlanarPauli(...), PlanarPauli(...), PlanarPauli(...), PlanarPauli(...))
        :rtype: 4-tuple of mp.mpf, 4-tuple of PlanarPauli
        """
        # NOTE: all list/tuples in this method are ordered (i, x, y, z)
        # empty log warnings
        log_warnings = []
        # sample paulis
        sample_paulis = (
            sample_pauli,
            sample_pauli.copy().logical_x(),
            sample_pauli.copy().logical_x().logical_z(),
            sample_pauli.copy().logical_z()
        )
        # tensor networks: tns are common to both contraction by column and by row (after transposition)
        tns = [self._tnc.create_tn(prob_dist, sp) for sp in sample_paulis]







        tns= [self._tnc.modify_tn(tn, had_prob_dist, hadamard_mat,sample_pauli)

        # probabilities
        coset_ps = (0.0, 0.0, 0.0, 0.0)  # default coset probabilities
        coset_ps_col = coset_ps_row = None  # undefined coset probabilities by column and row
        # N.B. After multiplication by mult, coset_ps will be of type mp.mpf so don't process with numpy!
        if self._mode in ('c', 'a'):
            # evaluate coset probabilities by column
            coset_ps_col = [0.0, 0.0, 0.0, 0.0]  # default coset probabilities
            for i in range(len(tns)):
                try:
                    coset_ps_col[i] = tt.mps2d.contract(tns[i], chi=self._chi, tol=self._tol)
                except (ValueError, np.linalg.LinAlgError) as ex:
                    log_warnings.append('CONTRACTION BY COL FOR {} COSET FAILED: {!r}'.format('IXYZ'[i], ex))
            # treat nan as inf so it doesn't get lost
            coset_ps_col = [mp.inf if mp.isnan(coset_p) else coset_p for coset_p in coset_ps_col]
        if self._mode in ('r', 'a'):
            # evaluate coset probabilities by row
            coset_ps_row = [0.0, 0.0, 0.0, 0.0]  # default coset probabilities
            # transpose tensor networks
            tns = [tt.mps2d.transpose(tn) for tn in tns]
            for i in range(len(tns)):
                try:
                    coset_ps_row[i] = tt.mps2d.contract(tns[i], chi=self._chi, tol=self._tol)
                except (ValueError, np.linalg.LinAlgError) as ex:
                    log_warnings.append('CONTRACTION BY ROW FOR {} COSET FAILED: {!r}'.format('IXYZ'[i], ex))
            # treat nan as inf so it doesn't get lost
            coset_ps_row = [mp.inf if mp.isnan(coset_p) else coset_p for coset_p in coset_ps_row]
        if self._mode == 'c':
            coset_ps = coset_ps_col
        elif self._mode == 'r':
            coset_ps = coset_ps_row
        elif self._mode == 'a':
            # average coset probabilities
            coset_ps = [sum(coset_p) / len(coset_p) for coset_p in zip(coset_ps_col, coset_ps_row)]
        # logging
        if log_warnings:
            log_data = {
                # instance
                'decoder': repr(self),
                # method parameters
                'prob_dist': prob_dist,
                'sample_pauli': pt.pack(sample_pauli.to_bsf()),
                # variables (convert to string because mp.mpf)
                'coset_ps_col': [repr(p) for p in coset_ps_col] if coset_ps_col else None,
                'coset_ps_row': [repr(p) for p in coset_ps_row] if coset_ps_row else None,
                'coset_ps': [repr(p) for p in coset_ps],
            }
            logger.warning('{}: {}'.format(' | '.join(log_warnings), json.dumps(log_data, sort_keys=True)))
        # results
        return tuple(coset_ps), sample_paulis

    def decode(self, code, hadamard_vec,hadamard_mat, syndrome,
               error_model=DepolarizingErrorModel(),  # noqa: B008
               error_probability=0.1, **kwargs):
        """
        See :meth:`qecsim.model.Decoder.decode`

        Note: The optional keyword parameters ``error_model`` and ``error_probability`` are used to determine the prior
        probability distribution for use in the decoding algorithm. Any provided error model must implement
        :meth:`~qecsim.model.ErrorModel.probability_distribution`.

        :param code: Rotated planar code.
        :type code: RotatedPlanarCode
        :param syndrome: Syndrome as binary vector.
        :type syndrome: numpy.array (1d)
        :param error_model: Error model. (default=DepolarizingErrorModel())
        :type error_model: ErrorModel
        :param error_probability: Overall probability of an error on a single qubit. (default=0.1)
        :type error_probability: float
        :return: Recovery operation as binary symplectic vector.
        :rtype: numpy.array (1d)
        """
        # any recovery
        any_recovery = self.sample_recovery(code, syndrome)
        # probability distribution
        prob_dist = error_model.probability_distribution(error_probability)
        # coset probabilities, recovery operations
        coset_ps, recoveries = self._coset_probabilities(prob_dist, hadamard_vec,hadamard_mat, any_recovery)
        # most likely recovery operation
        max_coset_p, max_recovery = max(zip(coset_ps, recoveries), key=lambda coset_p_recovery: coset_p_recovery[0])
        # logging
        if not (mp.isfinite(max_coset_p) and max_coset_p > 0):
            log_data = {
                # instance
                'decoder': repr(self),
                # method parameters
                'code': repr(code),
                'syndrome': pt.pack(syndrome),
                'error_model': repr(error_model),
                'error_probability': error_probability,
                # variables
                'prob_dist': prob_dist,
                'coset_ps': [repr(p) for p in coset_ps],  # convert to string because mp.mpf
                # context
                'error': pt.pack(kwargs['error']) if 'error' in kwargs else None,
            }
            logger.warning('NON-POSITIVE-FINITE MAX COSET PROBABILITY: {}'.format(json.dumps(log_data, sort_keys=True)))
        # return most likely recovery operation as bsf
        return max_recovery.to_bsf()

    @property
    def label(self):
        """See :meth:`qecsim.model.Decoder.label`"""
        params = [('chi', self._chi), ('mode', self._mode), ('tol', self._tol), ]
        return 'Rotated planar MPS ({})'.format(', '.join('{}={}'.format(k, v) for k, v in params if v))

    def __repr__(self):
        return '{}({!r}, {!r}, {!r})'.format(type(self).__name__, self._chi, self._mode, self._tol)





    class TNC:
        """Tensor network creator"""

        @functools.lru_cache()
        def h_node_value(self, prob_dist, f, n, e, s, w):
            """Return horizontal edge tensor element value."""
            paulis = ('I', 'X', 'Y', 'Z')
            op_to_pr = dict(zip(paulis, prob_dist))
            f = pt.pauli_to_bsf(f)
            I, X, Y, Z = pt.pauli_to_bsf(paulis)
            # n, e, s, w are in {0, 1} so multiply op to turn on or off
            op = (f + (n * Z) + (e * X) + (s * Z) + (w * X)) % 2
            return op_to_pr[pt.bsf_to_pauli(op)]

        @functools.lru_cache()
        def v_node_value(self, prob_dist, f, n, e, s, w):
            """Return vertical edge tensor element value."""
            # N.B. for v_node order of nesw is rotated relative to h_node
            return self.h_node_value(prob_dist, f, e, s, w, n)

        @functools.lru_cache()
        def create_h_node(self, prob_dist, f, compass_direction=None):
            """Return horizontal qubit tensor, i.e. has X plaquettes to left/right and Z plaquettes above/below."""

            def _shape(compass_direction=None):
                """Return shape of tensor including dummy indices."""
                return {  # (ne, se, sw, nw)
                    'n': (2, 2, 2, 1),
                    'ne': (1, 2, 2, 1),
                    'e': (1, 2, 2, 2),
                    'se': (1, 1, 2, 2),
                    's': (2, 1, 2, 2),
                    'sw': (2, 1, 1, 2),
                    'w': (2, 2, 1, 2),
                    'nw': (2, 2, 1, 1),
                }.get(compass_direction, (2, 2, 2, 2))

            # create bare h_node
            node = np.empty(_shape(compass_direction), dtype=np.float64)
            # fill values
            for n, e, s, w in np.ndindex(node.shape):
                node[(n, e, s, w)] = self.h_node_value(prob_dist, f, n, e, s, w)
            return node

        @functools.lru_cache()
        def create_v_node(self, prob_dist, f, compass_direction=None):
            """Return vertical qubit tensor, i.e. has Z plaquettes to left/right and X plaquettes above/below."""

            def _shape(compass_direction=None):
                """Return shape of tensor including dummy indices."""
                return {  # (ne, se, sw, nw)
                    'n': (1, 2, 2, 2),
                    'ne': (1, 1, 2, 2),
                    'e': (2, 1, 2, 2),
                    'se': (2, 1, 1, 2),
                    's': (2, 2, 1, 2),
                    # 'sw': (2, 2, 1, 1),  # cannot happen
                    'w': (2, 2, 2, 1),
                    'nw': (1, 2, 2, 1),
                }.get(compass_direction, (2, 2, 2, 2))

            # create bare v_node
            node = np.empty(_shape(compass_direction), dtype=np.float64)
            # fill values
            for n, e, s, w in np.ndindex(node.shape):
                node[(n, e, s, w)] = self.v_node_value(prob_dist, f, n, e, s, w)
            return node

        @functools.lru_cache()
        def create_s_node(self, compass_direction=None):
            """Return stabilizer tensor."""

            def _shape(compass_direction=None):
                """Return shape of tensor including dummy indices."""
                return {  # (ne, se, sw, nw)
                    'n': (1, 2, 2, 1),
                    'e': (1, 1, 2, 2),
                    's': (2, 1, 1, 2),
                    'w': (2, 2, 1, 1),
                }.get(compass_direction, (2, 2, 2, 2))

            node = tt.tsr.delta(_shape(compass_direction))
            return node

        def create_tn(self, prob_dist, sample_pauli):
            """Return a network (numpy.array 2d) of tensors (numpy.array 4d).
            Note: The network contracts to the coset probability of the given sample_pauli.
            """
            def _rotate_q_index(index, code):
                """Convert code site index in format (x, y) to tensor network q-node index in format (r, c)"""
                site_x, site_y = index  # qubit index in (x, y)
                site_r, site_c = code.site_bounds[1] - site_y, site_x  # qubit index in (r, c)
                return code.site_bounds[0] - site_c + site_r, site_r + site_c  # q-node index in (r, c)

            def _rotate_p_index(index, code):
                """Convert code plaquette index in format (x, y) to tensor network s-node index in format (r, c)"""
                q_node_r, q_node_c = _rotate_q_index(index, code)  # q-node index in (r, c)
                return q_node_r - 1, q_node_c  # s-node index in (r, c)

            def _compass_q_direction(index, code):
                """if the code site index lies on border of lattice then give that direction, else empty string."""
                direction = {code.site_bounds[1]: 'n', 0: 's'}.get(index[1], '')
                direction += {0: 'w', code.site_bounds[0]: 'e'}.get(index[0], '')
                return direction

            def _compass_p_direction(index, code):
                """if the code plaquette index lies on border of lattice then give that direction, else empty string."""
                direction = {code.site_bounds[1]: 'n', -1: 's'}.get(index[1], '')
                direction += {-1: 'w', code.site_bounds[0]: 'e'}.get(index[0], '')
                return direction

            
            pi,px,py,pz=prob_dist
            had_prob_dist= pi,pz,py,px

            # extract code
            code = sample_pauli.code
            # initialise empty tn
            tn_max_r, _ = _rotate_q_index((0, 0), code)
            _, tn_max_c = _rotate_q_index((code.site_bounds[0], 0), code)
            tn = np.empty((tn_max_r + 1, tn_max_c + 1), dtype=object)
            # iterate over
            max_site_x, max_site_y = code.site_bounds
            for code_index in itertools.product(range(-1, max_site_x + 1), range(-1, max_site_y + 1)):
                is_z_plaquette = code.is_z_plaquette(code_index)
                if code.is_in_site_bounds(code_index):
                    q_node_index = _rotate_q_index(code_index, code)
                    q_pauli = sample_pauli.operator(code_index)
                    if is_z_plaquette:
                        #print(code_index)
                        q_node = self.create_h_node(prob_dist, q_pauli, _compass_q_direction(code_index, code))
                    else:
                        q_node = self.create_v_node(prob_dist, q_pauli, _compass_q_direction(code_index, code))
                    tn[q_node_index] = q_node
                if code.is_in_plaquette_bounds(code_index):
                    s_node_index = _rotate_p_index(code_index, code)
                    s_node = self.create_s_node(_compass_p_direction(code_index, code))
                    tn[s_node_index] = s_node
            return tn


        def modify_tn(tn, had_prob_dist, hadamard_mat,sample_pauli):
            """Return a network (numpy.array 2d) of tensors (numpy.array 4d).
            Note: The network contracts to the coset probability of the given sample_pauli.
            """

            def _rotate_q_index(index, code):
                """Convert code site index in format (x, y) to tensor network q-node index in format (r, c)"""
                site_x, site_y = index  # qubit index in (x, y)
                site_r, site_c = code.site_bounds[1] - site_y, site_x  # qubit index in (r, c)
                return code.site_bounds[0] - site_c + site_r, site_r + site_c  # q-node index in (r, c)
                
            def _compass_q_direction(index, code):
                """if the code site index lies on border of lattice then give that direction, else empty string."""
                direction = {code.site_bounds[1]: 'n', 0: 's'}.get(index[1], '')
                direction += {0: 'w', code.site_bounds[0]: 'e'}.get(index[0], '')
                return direction

            code = sample_pauli.code
            max_site_x, max_site_y = code.site_bounds
            for code_index in itertools.product(range(-1, max_site_x + 1), range(-1, max_site_y + 1)):
                is_z_plaquette = code.is_z_plaquette(code_index)
                if code.is_in_site_bounds(code_index):
                    q_node_index = _rotate_q_index(code_index, code)
                    q_pauli = sample_pauli.operator(code_index)
                    if is_z_plaquette:
                        #print(code_index)
                        if hadamard_mat[code_index]:
                            q_node = self.create_h_node(had_prob_dist, q_pauli, _compass_q_direction(code_index, code))
                    else:
                        if hadamard_mat[code_index]:
                            q_node = self.create_v_node(had_prob_dist, q_pauli, _compass_q_direction(code_index, code))
                    tn[q_node_index] = q_node
            return tn   
Пример #6
0
def test_bsf_to_pauli(b, expected):
    assert pt.bsf_to_pauli(b) == expected
Пример #7
0
def test_rotated_planar_pauli_logical(planar_pauli, op_counts, message):
    pauli = pt.bsf_to_pauli(planar_pauli.to_bsf())
    for op, count in op_counts.items():
        assert pauli.count(op) == count, message
Пример #8
0
def test_planar_pauli_plaquette(planar_pauli, op_counts, message):
    pauli = pt.bsf_to_pauli(planar_pauli.to_bsf())
    for op, count in op_counts.items():
        assert pauli.count(op) == count, message
Пример #9
0
def test_toric_pauli_path(toric_pauli, op_counts, message):
    pauli = pt.bsf_to_pauli(toric_pauli.to_bsf())
    for op, count in op_counts.items():
        assert pauli.count(op) == count, message
Пример #10
0
def test_color666_pauli_logical(pauli, op_counts, message):
    pauli = pt.bsf_to_pauli(pauli.to_bsf())
    for op, count in op_counts.items():
        assert pauli.count(op) == count, message