Example #1
0
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)
Example #2
0
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)
Example #3
0
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}
Example #4
0
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)
Example #5
0
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
Example #6
0
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
Example #7
0
 def __init__(self, single_qubit_noise_gate: cirq.Gate,
              two_qubit_noise_gate: cirq.Gate):
     if single_qubit_noise_gate.num_qubits() != 1:
         raise ValueError(
             'The noise gate provided to single_qubit_noise_gate has number of qubits != 1.'
         )
     if two_qubit_noise_gate.num_qubits() != 2:
         raise ValueError(
             'The noise gate provided to two_qubit_noise_gate has number of qubits != 2.'
         )
     self.single_qubit_noise_gate = single_qubit_noise_gate
     self.two_qubit_noise_gate = two_qubit_noise_gate
Example #8
0
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)
Example #9
0
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)]
Example #10
0
def assert_unitary_gate_converts_correctly(gate: cirq.Gate):
    n = gate.num_qubits()
    for pre, post in solve_tableau(gate).items():
        # Create a circuit that measures pre before the gate times post after the gate.
        # If the gate is translated correctly, the measurement will always be zero.

        c = stim.Circuit()
        c.append_operation("H", range(n))
        for i in range(n):
            c.append_operation("CNOT", [i, i + n])
        c.append_operation("H", [2 * n])
        for q, p in pre.items():
            c.append_operation(f"C{p}", [2 * n, q.x])
        qs = cirq.LineQubit.range(n)
        conv_gate, _ = cirq_circuit_to_stim_data(cirq.Circuit(gate(*qs)),
                                                 q2i={q: q.x
                                                      for q in qs})
        c += conv_gate
        for q, p in post.items():
            c.append_operation(f"C{p}", [2 * n, q.x])
        if post.coefficient == -1:
            c.append_operation("Z", [2 * n])
        c.append_operation("H", [2 * n])
        c.append_operation("M", [2 * n])
        correct = not np.any(c.compile_sampler().sample_bit_packed(10))
        assert correct, f"{gate!r} failed to turn {pre} into {post}.\nConverted to:\n{conv_gate}\n"
Example #11
0
def test_noisy_gate_conversions_compiled_sampler(gate: cirq.Gate):
    # Create test circuit that uses superdense coding to quantify arbitrary Pauli error mixtures.
    n = gate.num_qubits()
    qs = cirq.LineQubit.range(n)
    circuit = cirq.Circuit(
        cirq.H.on_each(qs),
        [cirq.CNOT(q, q + n) for q in qs],
        gate(*qs),
        [cirq.CNOT(q, q + n) for q in qs],
        cirq.H.on_each(qs),
    )
    expected_rates = cirq.final_density_matrix(circuit).diagonal().real

    # Convert test circuit to Stim and sample from it.
    stim_circuit, _ = cirq_circuit_to_stim_data(circuit + cirq.measure(
        *sorted(circuit.all_qubits())[::-1]))
    sample_count = 10000
    samples = stim_circuit.compile_sampler().sample_bit_packed(
        sample_count).flat
    unique, counts = np.unique(samples, return_counts=True)

    # Compare sample rates to expected rates.
    for value, count in zip(unique, counts):
        expected_rate = expected_rates[value]
        actual_rate = count / sample_count
        allowed_variation = 5 * (expected_rate *
                                 (1 - expected_rate) / sample_count)**0.5
        if not 0 <= expected_rate - allowed_variation <= 1:
            raise ValueError(
                "Not enough samples to bound results away from extremes.")
        assert abs(expected_rate - actual_rate) < allowed_variation, (
            f"Sample rate {actual_rate} is over 5 standard deviations away from {expected_rate}.\n"
            f"Gate: {gate}\n"
            f"Test circuit:\n{circuit}\n"
            f"Converted circuit:\n{stim_circuit}\n")
Example #12
0
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])
Example #13
0
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)
Example #14
0
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)]
Example #15
0
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)
Example #16
0
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)))
Example #17
0
def test_tableau_simulator_error_mechanisms(gate: cirq.Gate):
    # Technically this be a test of the `stim` package itself, but it's so convenient to compare to cirq.

    # Create test circuit that uses superdense coding to quantify arbitrary Pauli error mixtures.
    n = gate.num_qubits()
    qs = cirq.LineQubit.range(n)
    circuit = cirq.Circuit(
        cirq.H.on_each(qs),
        [cirq.CNOT(q, q + n) for q in qs],
        gate(*qs),
        [cirq.CNOT(q, q + n) for q in qs],
        cirq.H.on_each(qs),
    )
    expected_rates = cirq.final_density_matrix(circuit).diagonal().real

    # Convert test circuit to Stim and sample from it.
    stim_circuit, _ = cirq_circuit_to_stim_data(circuit + cirq.measure(
        *sorted(circuit.all_qubits())[::-1]))
    sample_count = 10000
    samples = []
    for _ in range(sample_count):
        sim = stim.TableauSimulator()
        sim.do(stim_circuit)
        s = 0
        for k, v in enumerate(sim.current_measurement_record()):
            s |= v << k
        samples.append(s)

    unique, counts = np.unique(samples, return_counts=True)

    # Compare sample rates to expected rates.
    for value, count in zip(unique, counts):
        expected_rate = expected_rates[value]
        actual_rate = count / sample_count
        allowed_variation = 5 * (expected_rate *
                                 (1 - expected_rate) / sample_count)**0.5
        if not 0 <= expected_rate - allowed_variation <= 1:
            raise ValueError(
                "Not enough samples to bound results away from extremes.")
        assert abs(expected_rate - actual_rate) < allowed_variation, (
            f"Sample rate {actual_rate} is over 5 standard deviations away from {expected_rate}.\n"
            f"Gate: {gate}\n"
            f"Test circuit:\n{circuit}\n"
            f"Converted circuit:\n{stim_circuit}\n")
Example #18
0
def solve_tableau(gate: cirq.Gate) -> Dict[cirq.PauliString, cirq.PauliString]:
    """Computes a stabilizer tableau for the given gate."""

    result = {}

    n = gate.num_qubits()
    qs = cirq.LineQubit.range(n)
    for inp in [g(q) for g in [cirq.X, cirq.Z] for q in qs]:
        # Use superdense coding to extract X and Z flips from the generator conjugated by the gate.
        c = cirq.Circuit(
            cirq.H.on_each(qs),
            [cirq.CNOT(q, q + n) for q in qs],
            gate(*qs)**-1,
            inp,
            gate(*qs),
            [cirq.CNOT(q, q + n) for q in qs],
            cirq.H.on_each(qs),
            [cirq.measure(q, q + n, key=str(q)) for q in qs],
        )

        # Extract X/Y/Z data from sample result (which should be deterministic).
        s = cirq.Simulator().sample(c)
        out: cirq.PauliString = cirq.PauliString(
            {q: "IXZY"[s[str(q)][0]]
             for q in qs})

        # Use phase kickback to determine the sign of the output stabilizer.
        sign = cirq.NamedQubit('a')
        c = cirq.Circuit(
            cirq.H(sign),
            inp.controlled_by(sign),
            gate(*qs),
            out.controlled_by(sign),
            cirq.H(sign),
            cirq.measure(sign, key='sign'),
        )
        if cirq.Simulator().sample(c)['sign'][0]:
            out *= -1

        result[inp] = out
    return result
Example #19
0
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)
Example #20
0
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
Example #21
0
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)
Example #22
0
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)
Example #23
0
 def __init__(self, u: cirq.Gate, v: cirq.Gate, n=1):
     self.u = u
     self.v = v
     self.n_phys_qubits = n
     self.bond_dim = int(2**(u.num_qubits() - 1))