Beispiel #1
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)]
Beispiel #2
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)]
Beispiel #3
0
    def generate(self, code, probability, rng=None):
        """
        See :meth:`qecsim.model.ErrorModel.generate`

        Notes:

        * This method delegates to :meth:`probability_distribution` to find the probability of I, X, Y, Z operators on
          each qubit, assuming an IID error model.
        """
        n_qubits = code.n_k_d[0]
        rng = np.random.default_rng() if rng is None else rng
        error_pauli = ''.join(rng.choice(
            ('I', 'X', 'Y', 'Z'),
            size=n_qubits,
            p=self.probability_distribution(probability)
        ))
        # (pI,pX,pY,pZ)=error_model.probability_distribution(probability)
        # error_Pauli=[]
        # error_Pauli.extend('X'*round(n_qubits*pX))    
        # error_Pauli.extend('Y'*round(n_qubits*pY))    
        # error_Pauli.extend('Z'*round(n_qubits*pZ))    
        # error_Pauli.extend('I'*(n_qubits-len(error_Pauli)))  
        # shuffle(error_Pauli)
        # error_Pauli=''.join(error_Pauli)

        return pt.pauli_to_bsf(error_pauli)
Beispiel #4
0
    def generate(self, code, probability, rng=None):
        """
        See :meth:`qecsim.model.ErrorModel.generate`

        Notes:

        * This method delegates to :meth:`probability_distribution` to find the probability of I, X, Y, Z operators on
          each qubit, assuming an IID error model.
        """
        rng = np.random.default_rng() if rng is None else rng
        n_qubits = code.n_k_d[0]
        error_pauli = ''.join(rng.choice(
            ('I', 'X', 'Y', 'Z'),
            size=n_qubits,
            p=self.probability_distribution(probability)
        ))
        return pt.pauli_to_bsf(error_pauli)
Beispiel #5
0
def permute_error_Pauli(error_Pauli, perm_vec):
    #XYZ,ZYX,XZY,YXZ,YZX,ZXY
    n_qubits = len(error_Pauli)
    for i in range(n_qubits):
        #if perm_vec[i]==0: XYZ
        if perm_vec[i] == 1:  #ZYX
            if error_Pauli[i] == 'X':
                error_Pauli[i] = 'Z'
            elif error_Pauli[i] == 'Z':
                error_Pauli[i] = 'X'

        elif perm_vec[i] == 2:  #XZY
            if error_Pauli[i] == 'Y':
                error_Pauli[i] = 'Z'
            elif error_Pauli[i] == 'Z':
                error_Pauli[i] = 'Y'

        elif perm_vec[i] == 3:  #YXZ
            if error_Pauli[i] == 'X':
                error_Pauli[i] = 'Y'
            elif error_Pauli[i] == 'Y':
                error_Pauli[i] = 'X'

        elif perm_vec[i] == 4:  #XYZ->YZX Schrodinger
            if error_Pauli[i] == 'X':
                error_Pauli[i] = 'Z'
            elif error_Pauli[i] == 'Y':
                error_Pauli[i] = 'X'
            elif error_Pauli[i] == 'Z':
                error_Pauli[i] = 'Y'

        elif perm_vec[i] == 5:  #XYZ->ZXY Schrodinger
            if error_Pauli[i] == 'X':
                error_Pauli[i] = 'Y'
            elif error_Pauli[i] == 'Y':
                error_Pauli[i] = 'Z'
            elif error_Pauli[i] == 'Z':
                error_Pauli[i] = 'X'

    step_error = pt.pauli_to_bsf(''.join(error_Pauli))

    return step_error
Beispiel #6
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   
Beispiel #7
0
def test_pauli_to_bsf(p, expected):
    assert np.array_equal(pt.pauli_to_bsf(p), expected)
Beispiel #8
0
    (np.array([0, 0, 0, 0, 0, 1, 1, 1, 1, 1]), 'ZZZZZ'),
    (np.array([1, 1, 1, 1, 1, 1, 1, 1, 1, 1]), 'YYYYY'),
    (np.array([[1, 0, 0, 0, 1, 0, 0, 1, 0, 1], [0, 1, 0, 1, 0, 0, 0, 1, 1, 0]
               ]), ['XIZIY', 'IXZYI']),
    (np.array([[1, 1, 1, 1, 1, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 1, 1, 1, 1, 1],
               [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]), ['XXXXX', 'ZZZZZ', 'YYYYY']),
    (np.array([1, 0]), 'X'),
    (np.array([0, 1, 0, 1, 0, 0, 1, 1]), 'IXZY'),
])
def test_bsf_to_pauli(b, expected):
    assert pt.bsf_to_pauli(b) == expected


@pytest.mark.parametrize(
    'b, expected',
    [(pt.pauli_to_bsf('IIIII'), 0), (pt.pauli_to_bsf('XIZIY'), 3),
     (pt.pauli_to_bsf('XXXXX'), 5), (pt.pauli_to_bsf('ZZZZZ'), 5),
     (pt.pauli_to_bsf('ZZZZZ'), 5),
     (np.array([pt.pauli_to_bsf('XIZIY'),
                pt.pauli_to_bsf('XXXXX')]), 8)])
def test_bsf_weight(b, expected):
    assert pt.bsf_wt(b) == expected


@pytest.mark.parametrize(
    'a, b, expected',
    [
        (np.array([0, 0, 0, 0, 0, 0]), np.array([0, 0, 0, 0, 0, 0
                                                 ]), 0),  # III bsp III commute
        (np.array([1, 0, 0, 0, 0, 0]), np.array([1, 0, 0, 0, 0, 0
                                                 ]), 0),  # XII bsp XII commute
Beispiel #9
0

@pytest.mark.parametrize('max_qubits', [
    (-1),
    (12.5),
    ('asdf'),
])
def test_naive_decoder_new_invalid_parameters(max_qubits):
    with pytest.raises((ValueError, TypeError),
                       match=r"^NaiveDecoder") as exc_info:
        NaiveDecoder(max_qubits)
    print(exc_info)


@pytest.mark.parametrize('error', [
    pt.pauli_to_bsf('IIIII'),
    pt.pauli_to_bsf('IXIII'),
    pt.pauli_to_bsf('IIYII'),
    pt.pauli_to_bsf('IIIIZ'),
    pt.pauli_to_bsf('IZYXI'),
    pt.pauli_to_bsf('YZYXX'),
])
def test_naive_decoder_decode(error):
    code = FiveQubitCode()
    decoder = NaiveDecoder()
    syndrome = pt.bsp(error, code.stabilizers.T)
    recovery = decoder.decode(code, syndrome)
    assert np.array_equal(pt.bsp(recovery, code.stabilizers.T), syndrome), (
        'recovery {} does not give the same syndrome as the error {}'.format(
            recovery, error))
    assert np.all(pt.bsp(recovery ^ error, code.stabilizers.T) == 0), (
Beispiel #10
0
def _run_once_defN(mode, code,hadamard_mat, time_steps, error_model, decoder, n_errors_code, measurement_error_probability, rng):
    """Implements run_once and run_once_ftp functions"""
    # assumptions
    assert (mode == 'ideal' and time_steps == 1) or mode == 'ftp'

    # generate step_error, step_syndrome and step_measurement_error for each time step
    
    error_probability_sample=0.1
    (pI,pX,pY,pZ)=error_model.probability_distribution(error_probability_sample)
    error_Pauli=[]
    error_Pauli.extend('X'*round(n_errors_code*pX/error_probability_sample))    
    error_Pauli.extend('Y'*round(n_errors_code*pY/error_probability_sample))    
    error_Pauli.extend('Z'*round(n_errors_code*pZ/error_probability_sample))    
    error_Pauli.extend('I'*(n_qubits-len(error_Pauli)))

    n_qubits = code.n_k_d[0]
    hadamard_vec=np.zeros(n_qubits)

    Nx=code.site_bounds[0]+1
    Ny=code.site_bounds[1]+1
    for i,j in np.ndindex(hadamard_mat.shape):
        if hadamard_mat[i,j]==1:
            hadamard_vec[j+(Ny-1-i)*Nx]=1

    for _ in range(time_steps):
        step_errors, step_syndromes, step_measurement_errors = [], [], []

        # step_error: random error based on error probability
        shuffle(error_Pauli)
        # for i in range(n_qubits):
        #     if hadamard_vec[i]==1:
        #         if error_Pauli[i]=='X':
        #             error_Pauli[i]='Z'
        #         elif error_Pauli[i]=='Z':
        #             error_Pauli[i]='X'

        step_error=pt.pauli_to_bsf(''.join(error_Pauli))

        # step_error = error_model.generate(code, error_probability, rng)
        for i in range(n_qubits):
            if hadamard_vec[i]==1:
                step_error_temp=step_error[i]
                step_error[i]=step_error[n_qubits+i]
                step_error[n_qubits+i]=step_error_temp

        step_errors.append(step_error)
        # step_syndrome: stabilizers that do not commute with the error
        step_syndrome = pt.bsp(step_error, code.stabilizers.T)
        step_syndromes.append(step_syndrome)
        # step_measurement_error: random syndrome bit flips based on measurement_error_probability
        if measurement_error_probability:
            step_measurement_error = rng.choice(
                (0, 1),
                size=step_syndrome.shape,
                p=(1 - measurement_error_probability, measurement_error_probability)
            )
        else:
            step_measurement_error = np.zeros(step_syndrome.shape, dtype=int)
        step_measurement_errors.append(step_measurement_error)
    if logger.isEnabledFor(logging.DEBUG):
        logger.debug('run: step_errors={}'.format(step_errors))
        logger.debug('run: step_syndromes={}'.format(step_syndromes))
        logger.debug('run: step_measurement_errors={}'.format(step_measurement_errors))

    # error: sum of errors at each time step
    error = np.bitwise_xor.reduce(step_errors)
    if logger.isEnabledFor(logging.DEBUG):
        logger.debug('run: error={}'.format(error))

    # syndrome: apply measurement_error at times t-1 and t to syndrome at time t
    syndrome = []
    for t in range(time_steps):
        syndrome.append(step_measurement_errors[t - 1] ^ step_syndromes[t] ^ step_measurement_errors[t])
    # convert syndrome to 2d numpy array
    syndrome = np.array(syndrome)
    if logger.isEnabledFor(logging.DEBUG):
        logger.debug('run: syndrome={}'.format(syndrome))

    # decoding: boolean or best match recovery operation based on decoder
    ctx = {'error_model': error_model, 'n_errors_code': n_errors_code, 'error': error,
           'step_errors': step_errors, 'measurement_error_probability': measurement_error_probability,
           'step_measurement_errors': step_measurement_errors}
    # convert syndrome to 1d if mode is 'ideal'
    if mode == 'ideal':  # convert syndrome to 1d and call decode
        decoding = decoder.decode(code,hadamard_mat, syndrome[0], **ctx)
    if mode == 'ftp':  # call decode_ftp
        decoding = decoder.decode_ftp(code, time_steps, syndrome, **ctx)

    if logger.isEnabledFor(logging.DEBUG):
        logger.debug('run: decoding={}'.format(decoding))

    # if decoding is not DecodeResult, convert to DecodeResult
    if not isinstance(decoding, DecodeResult):
        # decoding is recovery, so wrap in DecodeResult
        decoding = DecodeResult(recovery=decoding)  # raises error if recovery is None
    # extract outcomes from decoding
    success = decoding.success
    logical_commutations = decoding.logical_commutations
    custom_values = decoding.custom_values
    # if recovery specified, resolve success and logical_commutations
    if decoding.recovery is not None:
        # recovered code
        recovered = decoding.recovery ^ error
        # success checks
        commutes_with_stabilizers = np.all(pt.bsp(recovered, code.stabilizers.T) == 0)
        if not commutes_with_stabilizers:
            log_data = {  # enough data to recreate issue
                # models
                'code': repr(code), 'error_model': repr(error_model), 'decoder': repr(decoder),
                # variables
                'error': pt.pack(error), 'recovery': pt.pack(decoding.recovery),
                # step variables
                'step_errors': [pt.pack(v) for v in step_errors],
                'step_measurement_errors': [pt.pack(v) for v in step_measurement_errors],
            }
            logger.warning('RECOVERY DOES NOT RETURN TO CODESPACE: {}'.format(json.dumps(log_data, sort_keys=True)))
        resolved_logical_commutations = pt.bsp(recovered, code.logicals.T)
        commutes_with_logicals = np.all(resolved_logical_commutations == 0)
        resolved_success = commutes_with_stabilizers and commutes_with_logicals
        # fill in unspecified outcomes
        success = resolved_success if success is None else success
        logical_commutations = resolved_logical_commutations if logical_commutations is None else logical_commutations

    if logger.isEnabledFor(logging.DEBUG):
        logger.debug('run: success={}'.format(success))
        logger.debug('run: logical_commutations={!r}'.format(logical_commutations))
        logger.debug('run: custom_values={!r}'.format(custom_values))

    data = {
        'error_weight': pt.bsf_wt(np.array(step_errors)),
        'success': bool(success),
        'logical_commutations': logical_commutations,
        'custom_values': custom_values,
    }

    return data
Beispiel #11
0
 def logical_zs(self):
     """See :meth:`qecsim.model.StabilizerCode.logical_zs`"""
     return pt.pauli_to_bsf(self._pauli_logical_zs)
Beispiel #12
0
 def stabilizers(self):
     """See :meth:`qecsim.model.StabilizerCode.stabilizers`"""
     return pt.pauli_to_bsf(self._pauli_stabilizers)