def merge_with_pauli_noise(prog_list: Iterable, probabilities: List, qubits: List): """ Insert pauli noise channels between each item in the list of programs. This noise channel is implemented as a single noisy identity gate acting on the provided qubits. This method does not rely on merge_programs and so avoids the inclusion of redundant Kraus Pragmas that would occur if merge_programs was called directly on programs with distinct noisy gate definitions. :param prog_list: an iterable such as a program or a list of programs. If a program is provided, a single noise gate will be applied after each gate in the program. If a list of programs is provided, the noise gate will be applied after each program. :param probabilities: The 4^num_qubits list of probabilities specifying the desired pauli channel. There should be either 4 or 16 probabilities specified in the order I, X, Y, Z or II, IX, IY, IZ, XI, XX, XY, etc respectively. :param qubits: a list of the qubits that the noisy gate should act on. :return: A single program with noisy gates inserted between each element of the program list. :rtype: Program """ p = Program() p.defgate("pauli_noise", np.eye(2 ** len(qubits))) p.define_noisy_gate("pauli_noise", qubits, pauli_kraus_map(probabilities)) for elem in prog_list: p.inst(Program(elem)) if isinstance(elem, Measurement): continue # do not apply noise after measurement p.inst(("pauli_noise", *qubits)) return p
def test_2q_general_pauli_noise(qvm, benchmarker): qvm.qam.random_seed = 1 expected_decay = .8 probs = [expected_decay + .2 / 4, .06] + [0] * 12 + [.04, .05] kraus_ops = pauli_kraus_map(probs) num_sequences_per_depth = 5 num_shots = 25 depths = [2, 10, 12, 25] depths = [ depth for depth in depths for _ in range(num_sequences_per_depth) ] qubits = (0, 1) sequences = generate_rb_experiment_sequences(benchmarker, qubits, depths) add_noise_to_sequences(sequences, qubits) expts = group_sequences_into_parallel_experiments([sequences], [qubits]) for expt in expts: add_noise_definition(expt, qubits, kraus_ops) results = acquire_rb_data(qvm, expts, num_shots) stats = get_stats_by_qubit_group([qubits], results)[qubits] fit = fit_rb_results(depths, stats['expectation'], stats['std_err'], num_shots) observed_decay = fit.params['decay'].value decay_error = fit.params['decay'].stderr np.testing.assert_allclose(expected_decay, observed_decay, atol=2.5 * decay_error)
def test_pauli_kraus_map(): probabilities = [.1, .2, .3, .4] k1, k2, k3, k4 = pauli_kraus_map(probabilities) assert np.allclose(k1, np.sqrt(.1) * np.eye(2), atol=1 * 10**-8) assert np.allclose(k2, np.sqrt(.2) * np.array([[0, 1.], [1., 0]]), atol=1 * 10**-8) assert np.allclose(k3, np.sqrt(.3) * np.array([[0, -1.j], [1.j, 0]]), atol=1 * 10**-8) assert np.allclose(k4, np.sqrt(.4) * np.array([[1, 0], [0, -1]]), atol=1 * 10**-8) two_q_pauli_kmaps = pauli_kraus_map( np.kron(probabilities, list(reversed(probabilities)))) q1_pauli_kmaps = [k1, k2, k3, k4] q2_pauli_kmaps = pauli_kraus_map(list(reversed(probabilities))) tensor_kmaps = tensor_kraus_maps(q1_pauli_kmaps, q2_pauli_kmaps) assert np.allclose(two_q_pauli_kmaps, tensor_kmaps)
def depolarizing_noise(num_qubits: int, p: float = .95): """ Generate the Kraus operators corresponding to a given unitary single qubit gate followed by a depolarizing noise channel. :params float num_qubits: either 1 or 2 qubit channel supported :params float p: parameter in depolarizing channel as defined by: p $\rho$ + (1-p)/d I :return: A list, eg. [k0, k1, k2, k3], of the Kraus operators that parametrize the map. :rtype: list """ num_of_operators = 4 ** num_qubits probabilities = [p + (1.0 - p) / num_of_operators] + [(1.0 - p) / num_of_operators] * (num_of_operators - 1) return pauli_kraus_map(probabilities)
def pauli_noise_z_gate(p_I=.7, p_x=0.1, p_y=0.1, p_z=0.1): corrupted_Z = append_kraus_to_gate(pauli_kraus_map([p_I, p_x, p_y, p_z]), np.array([[1, 0], [0, -1]])) return corrupted_Z