def test_biased_noise_representation_with_choi( gate: Gate, epsilon: float, eta: float ): """Tests the representation by comparing exact Choi matrices.""" qreg = LineQubit.range(gate.num_qubits()) ideal_choi = _operation_to_choi(gate.on(*qreg)) op_rep = represent_operation_with_local_biased_noise( Circuit(gate.on(*qreg)), epsilon, eta ) choi_components = [] # Define biased noise channel a = 1 - epsilon b = epsilon * (3 * eta + 1) / (3 * (eta + 1)) c = epsilon / (3 * (eta + 1)) mix = [ (a, unitary(I)), (b, unitary(Z)), (c, unitary(X)), (c, unitary(Y)), ] for noisy_op, coeff in op_rep.basis_expansion.items(): implementable_circ = noisy_op.circuit() # Apply noise after each sequence. # NOTE: noise is not applied after each operation. biased_op = ops.MixedUnitaryChannel(mix).on_each(*qreg) implementable_circ.append(biased_op) sequence_choi = _circuit_to_choi(implementable_circ) choi_components.append(coeff * sequence_choi) combination_choi = np.sum(choi_components, axis=0) assert np.allclose(ideal_choi, combination_choi, atol=10**-6)
def test_amplitude_damping_representation_with_choi( gate: Gate, noise: float, circuit_type: str, ): """Tests the representation by comparing exact Choi matrices.""" q = LineQubit(0) ideal_circuit = convert_from_mitiq(Circuit(gate.on(q)), circuit_type) ideal_choi = _circuit_to_choi(Circuit(gate.on(q))) op_rep = _represent_operation_with_amplitude_damping_noise( ideal_circuit, noise, ) choi_components = [] for noisy_op, coeff in op_rep.basis_expansion.items(): implementable_circ = noisy_op.circuit() depolarizing_op = AmplitudeDampingChannel(noise).on(q) # Apply noise after each sequence. # NOTE: noise is not applied after each operation. implementable_circ.append(depolarizing_op) sequence_choi = _operation_to_choi(implementable_circ) choi_components.append(coeff * sequence_choi) combination_choi = np.sum(choi_components, axis=0) assert np.allclose(ideal_choi, combination_choi, atol=10**-8)
def test_single_qubit_gates(qasm_gate: str, cirq_gate: cirq.Gate): qasm = """OPENQASM 2.0; include "qelib1.inc"; qreg q[2]; {0} q[0]; {0} q; """.format(qasm_gate) parser = QasmParser() q0 = cirq.NamedQubit('q_0') q1 = cirq.NamedQubit('q_1') expected_circuit = Circuit( [cirq_gate.on(q0), cirq_gate.on(q0), cirq_gate.on(q1)]) parsed_qasm = parser.parse(qasm) assert parsed_qasm.supportedFormat assert parsed_qasm.qelib1Include ct.assert_same_circuits(parsed_qasm.circuit, expected_circuit) assert parsed_qasm.qregs == {'q': 2}
def test_get_imp_sequences_no_simplify(gate: Gate): q = LineQubit(0) expected_imp_sequences = [ [gate.on(q)], [gate.on(q), X.on(q)], [gate.on(q), Y.on(q)], [gate.on(q), Z.on(q)], ] assert get_imp_sequences(gate.on(q), DECO_DICT) == expected_imp_sequences
def test_simplify_paulis_in_simple_pauli_deco_dict(gate: Gate): """Tests DECO_DICT_SIMP which is initialized using the 'simplify_paulis' option. This should produce decomposition dictionary in which Pauli sequences are simplified to single Pauli gates. """ qreg = LineQubit.range(2) decomposition_dict = DECO_DICT_SIMP for q in qreg: deco = decomposition_dict[gate.on(q)] _, first_imp_seq = deco[0] assert first_imp_seq == [gate.on(q)] _, second_imp_seq = deco[1] input_times_x = {X: I, Y: Z, Z: Y} assert second_imp_seq == [input_times_x[gate].on(q)]
def test_simple_pauli_deco_dict_with_Choi(gate: Gate): """Tests the decomposition by comparing the exact Choi matrices.""" qreg = LineQubit.range(gate.num_qubits()) ideal_choi = _operation_to_choi(gate.on(*qreg)) op_decomp = DECO_DICT[gate.on(*qreg)] choi_components = [] for coeff, imp_seq in op_decomp: # Apply noise after each sequence. # NOTE: noise is not applied after each operation. noisy_sequence = [imp_seq] + [depolarize(BASE_NOISE)(q) for q in qreg] sequence_choi = _operation_to_choi(noisy_sequence) choi_components.append(coeff * sequence_choi) combination_choi = np.sum(choi_components, axis=0) assert np.allclose(ideal_choi, combination_choi)
def test_sample_sequence_choi(gate: cirq.Gate): """Tests the sample_sequence by comparing the exact Choi matrices.""" qreg = cirq.LineQubit.range(gate.num_qubits()) ideal_op = gate.on(*qreg) ideal_circ = cirq.Circuit(ideal_op) noisy_op_tree = [ideal_op] + [cirq.depolarize(BASE_NOISE)(q) for q in qreg] ideal_choi = _operation_to_choi(ideal_op) noisy_choi = _operation_to_choi(noisy_op_tree) representation = represent_operation_with_local_depolarizing_noise( ideal_circ, BASE_NOISE, ) choi_unbiased_estimates = [] rng = np.random.RandomState(1) for _ in range(500): imp_seqs, signs, norm = sample_sequence(ideal_circ, [representation], random_state=rng) noisy_sequence = imp_seqs[0].with_noise(cirq.depolarize(BASE_NOISE)) sequence_choi = _circuit_to_choi(noisy_sequence) choi_unbiased_estimates.append(norm * signs[0] * sequence_choi) choi_pec_estimate = np.average(choi_unbiased_estimates, axis=0) noise_error = np.linalg.norm(ideal_choi - noisy_choi) pec_error = np.linalg.norm(ideal_choi - choi_pec_estimate) assert pec_error < noise_error assert np.allclose(ideal_choi, choi_pec_estimate, atol=0.05)
def test_get_coefficients(gate: Gate): q = LineQubit(0) coeffs = get_coefficients(gate.on(q), DECO_DICT) epsilon = BASE_NOISE * 4.0 / 3.0 c_neg = -(1 / 4) * epsilon / (1 - epsilon) c_pos = 1 - 3 * c_neg assert np.isclose(np.sum(coeffs), 1.0) assert np.allclose(coeffs, [c_pos, c_neg, c_neg, c_neg])
def test_sample_sequence_types(gate: Gate): num_qubits = gate.num_qubits() qreg = LineQubit.range(num_qubits) for _ in range(1000): imp_seq, sign, norm = sample_sequence(gate.on(*qreg), DECO_DICT) assert all([isinstance(op, Operation) for op in imp_seq]) assert sign in {1, -1} assert norm > 1
def test_depolarizing_representation_with_choi(gate: Gate, noise: float): """Tests the representation by comparing exact Choi matrices.""" qreg = LineQubit.range(gate.num_qubits()) ideal_choi = _operation_to_choi(gate.on(*qreg)) op_rep = represent_operation_with_global_depolarizing_noise( Circuit(gate.on(*qreg)), noise, ) choi_components = [] for noisy_op, coeff in op_rep.basis_expansion.items(): implementable_circ = noisy_op.circuit() # Apply noise after each sequence. # NOTE: noise is not applied after each operation. depolarizing_op = DepolarizingChannel(noise, len(qreg))(*qreg) implementable_circ.append(depolarizing_op) sequence_choi = _circuit_to_choi(implementable_circ) choi_components.append(coeff * sequence_choi) combination_choi = np.sum(choi_components, axis=0) assert np.allclose(ideal_choi, combination_choi, atol=10 ** -6)
def test_simple_pauli_deco_dict_single_qubit(gate: Gate): """Tests that the _simple_pauli_deco_dict function returns a decomposition dicitonary which is consistent with a local depolarizing noise model. This is similar to test_simple_pauli_deco_dict_CNOT but applied to single-qubit gates. """ epsilon = BASE_NOISE * 4.0 / 3.0 c_neg = -(1 / 4) * epsilon / (1 - epsilon) c_pos = 1 - 3 * c_neg qreg = LineQubit.range(2) for q in qreg: deco = DECO_DICT[gate.on(q)] first_coefficient, first_imp_seq = deco[0] assert np.isclose(c_pos, first_coefficient) assert first_imp_seq == [gate.on(q)] second_coefficient, second_imp_seq = deco[1] assert np.isclose(c_neg, second_coefficient) assert second_imp_seq == [gate.on(q), X.on(q)]
def test_sample_sequence_choi(gate: Gate): """Tests the sample_sequence by comparing the exact Choi matrices.""" qreg = LineQubit.range(gate.num_qubits()) ideal_op = gate.on(*qreg) noisy_op_tree = [ideal_op] + [depolarize(BASE_NOISE)(q) for q in qreg] ideal_choi = _operation_to_choi(gate.on(*qreg)) noisy_choi = _operation_to_choi(noisy_op_tree) choi_unbiased_estimates = [] for _ in range(500): imp_seq, sign, norm = sample_sequence(gate.on(*qreg), DECO_DICT) # Apply noise after each sequence. # NOTE: noise is not applied after each operation. noisy_sequence = [imp_seq] + [depolarize(BASE_NOISE)(q) for q in qreg] sequence_choi = _operation_to_choi(noisy_sequence) choi_unbiased_estimates.append(norm * sign * sequence_choi) choi_pec_estimate = np.average(choi_unbiased_estimates, axis=0) noise_error = np.linalg.norm(ideal_choi - noisy_choi) pec_error = np.linalg.norm(ideal_choi - choi_pec_estimate) assert pec_error < noise_error assert np.allclose(ideal_choi, choi_pec_estimate, atol=0.05)
def test_gate_decomposition(gate: cirq.Gate, testcase: unittest.TestCase, print_circuit=True, expected_unitary=None): qubits = cirq.LineQubit.range(2) control, target = qubits circuit_compressed = cirq.Circuit() circuit_decomposed = cirq.Circuit() circuit_compressed.append(gate.on(control, target)) circuit_decomposed.append( cirq.decompose(gate.on(control, target), keep=is_a_basic_operation)) if print_circuit: print("Compressed circuit: \n{}".format(circuit_compressed)) print("Decomposed circuit: \n{}".format(circuit_decomposed)) print(cirq.unitary(circuit_compressed).round(3)) print(cirq.unitary(circuit_decomposed).round(3)) testcase.assertTrue( np.allclose((cirq.unitary(circuit_compressed) if expected_unitary is None else expected_unitary), cirq.unitary(circuit_decomposed)))
def random_cliffords( connectivity_graph: nx.Graph, random_state: random.RandomState, two_qubit_gate: cirq.Gate = cirq.CNOT, ) -> cirq.Circuit: """Returns a circuit with a two-qubit Clifford gate applied to each edge in edges, and a random single-qubit Clifford gate applied to every other qubit. Args: connectivity_graph: A graph with the edges for which the two-qubit Clifford gate is to be applied. random_state: Random state to choose Cliffords (uniformly at random). two_qubit_gate: Two-qubit gate to use. """ gates = [ two_qubit_gate.on(cirq.LineQubit(a), cirq.LineQubit(b)) for a, b in list(connectivity_graph.edges) ] qubits = nx.Graph() qubits.add_nodes_from(nx.isolates(connectivity_graph)) gates.extend( list(random_single_cliffords(qubits, random_state).all_operations())) return cirq.Circuit(gates)
def add_hadamard_test(state: typing.Union[cirq.Qid, typing.Iterable[cirq.Qid]], gate_v: cirq.Gate, gate_a: cirq.Gate, is_imaginary_part: bool, circuit: cirq.Circuit) -> str: """ Add an Hadamard test for `state` |ψ0> to estimate <ψ0|V+ A V|ψ0>, the circuit of which is: 999: ───H─────S/I─────@──────H──────M('Hadamard test measure 0')─── │ 0: ─────V─────────────A──────────────────────────────────────────── where S/I is set to be the identity gate / the Clifford S gate when `is_imaginary_part` is False / True (i.e. when estimating the real / imaginary part of <ψ0|V+ A V|ψ0>). :param state: The input state |ψ0>. :param gate_v: :param gate_a: :param is_imaginary_part: :param circuit: :return: """ if isinstance(state, cirq.Qid): state = [state] if gate_a.num_qubits() == gate_v.num_qubits() == len(state): pass else: raise ValueError( "The number of qubits acted on by gate `V` and `A` must equal to " "that of `state`, but they are found to be {}, {} and {}.".format( gate_v.num_qubits(), gate_a.num_qubits(), len(state))) auxiliary_qubit = generate_auxiliary_qubit(circuit) measurement_name = "Hadamard test measure " existed_hadamard_measurement_ids = { int(key[len(measurement_name):]) for key in get_all_measurement_keys(circuit) if key.startswith(measurement_name) } hadamard_measurement_id = (max(existed_hadamard_measurement_ids) + 1 if len(existed_hadamard_measurement_ids) != 0 else 0) measurement_name += str(hadamard_measurement_id) circuit.append([cirq.H(auxiliary_qubit), gate_v.on(*state)]) if is_imaginary_part is False: pass else: circuit.append(cirq.S(auxiliary_qubit)) circuit.append([cirq.ControlledGate(gate_a).on(auxiliary_qubit, *state)]) circuit.append(cirq.H(auxiliary_qubit)) circuit.append(cirq.measure(auxiliary_qubit, key=measurement_name)) return measurement_name
def test_get_one_norm(gate: Gate): q = LineQubit(0) epsilon = BASE_NOISE * 4.0 / 3.0 expected_one_norm = (1.0 + 0.5 * epsilon) / (1.0 - epsilon) assert np.isclose(get_one_norm(gate.on(q), DECO_DICT), expected_one_norm)
def test_get_probabilities(gate: Gate): q = LineQubit(0) probs = get_probabilities(gate.on(q), DECO_DICT) assert all([p >= 0 for p in probs]) assert np.isclose(sum(probs), 1.0)