Esempio n. 1
0
def _max_ent_state_circuit(num_qubits: int) -> Circuit:
    r"""Generates a circuits which prepares the maximally entangled state
    |\omega\rangle = U |0\rangle  = \sum_i |i\rangle \otimes |i\rangle .

    Args:
        num_qubits: The number of qubits on which the circuit is applied.
            Only 2 or 4 qubits are supported.

    Returns:
        The circuits which prepares the state |\omega\rangle.
    """

    qreg = LineQubit.range(num_qubits)
    circ = Circuit()
    if num_qubits == 2:
        circ.append(H.on(qreg[0]))
        circ.append(CNOT.on(*qreg))
    elif num_qubits == 4:
        # Prepare half of the qubits in a uniform superposition
        circ.append(H.on(qreg[0]))
        circ.append(H.on(qreg[1]))
        # Create a perfect correlation between the two halves of the qubits.
        circ.append(CNOT.on(qreg[0], qreg[2]))
        circ.append(CNOT.on(qreg[1], qreg[3]))
    else:
        raise NotImplementedError(
            "Only 2- or 4-qubit maximally entangling circuits are supported."
        )
    return circ
Esempio n. 2
0
def test_simplify_circuit_exponents():
    qreg = LineQubit.range(2)
    circuit = Circuit([H.on(qreg[0]), CNOT.on(*qreg), Z.on(qreg[1])])

    # Invert circuit
    inverse_circuit = cirq.inverse(circuit)
    inverse_repr = inverse_circuit.__repr__()
    inverse_qasm = inverse_circuit._to_qasm_output().__str__()

    # Expected circuit after simplification
    expected_inv = Circuit([Z.on(qreg[1]), CNOT.on(*qreg), H.on(qreg[0])])
    expected_repr = expected_inv.__repr__()
    expected_qasm = expected_inv._to_qasm_output().__str__()

    # Check inverse_circuit is logically equivalent to expected_inverse
    # but they have a different representation
    assert inverse_circuit == expected_inv
    assert inverse_repr != expected_repr
    assert inverse_qasm != expected_qasm

    # Simplify the circuit
    _simplify_circuit_exponents(inverse_circuit)

    # Check inverse_circuit has the expected simplified representation
    simplified_repr = inverse_circuit.__repr__()
    simplified_qasm = inverse_circuit._to_qasm_output().__str__()
    assert inverse_circuit == expected_inv
    assert simplified_repr == expected_repr
    assert simplified_qasm == expected_qasm
Esempio n. 3
0
def test_simple_pauli_deco_dict_CNOT():
    """Tests that the _simple_pauli_deco_dict function returns a decomposition
    dicitonary which is consistent with a local depolarizing noise model.

    The channel acting on the state each qubit is assumed to be:
    D(rho) = = (1 - epsilon) rho + epsilon I/2
    = (1 - p) rho + p/3 (X rho X + Y rho Y^dag + Z rho Z)
    """

    # Deduce epsilon from BASE_NOISE
    epsilon = BASE_NOISE * 4.0 / 3.0
    c_neg = -(1 / 4) * epsilon / (1 - epsilon)
    c_pos = 1 - 3 * c_neg
    qreg = LineQubit.range(2)

    # Get the decomposition of a CNOT gate
    deco = DECO_DICT[CNOT.on(*qreg)]

    # The first term of 'deco' corresponds to no error occurring
    first_coefficient, first_imp_seq = deco[0]
    assert np.isclose(c_pos * c_pos, first_coefficient)
    assert first_imp_seq == [CNOT.on(*qreg)]
    # The second term corresponds to a Pauli X error on one qubit
    second_coefficient, second_imp_seq = deco[1]
    assert np.isclose(c_pos * c_neg, second_coefficient)
    assert second_imp_seq == [CNOT.on(*qreg), X.on(qreg[0])]
    # The last term corresponds to two Pauli Z errors on both qubits
    last_coefficient, last_imp_seq = deco[-1]
    assert np.isclose(c_neg * c_neg, last_coefficient)
    assert last_imp_seq == [CNOT.on(*qreg), Z.on(qreg[0]), Z.on(qreg[1])]
Esempio n. 4
0
def _max_ent_state_circuit(num_qubits: int) -> Circuit:
    r"""Generates a circuit which prepares the maximally entangled state
    |\omega\rangle = U |0\rangle  = \sum_i |i\rangle \otimes |i\rangle .

    Args:
        num_qubits: The number of qubits on which the circuit is applied.
            It must be an even number because of the structure of a
            maximally entangled state.

    Returns:
        The circuits which prepares the state |\omega\rangle.

    Raises:
        Value error: if num_qubits is not an even positive integer.
    """

    if not isinstance(num_qubits, int) or num_qubits % 2 or num_qubits == 0:
        raise ValueError(
            "The argument 'num_qubits' must be an even and positive integer.")

    alice_reg = LineQubit.range(num_qubits // 2)
    bob_reg = LineQubit.range(num_qubits // 2, num_qubits)

    return Circuit(
        # Prepare alice_register in a uniform superposition
        H.on_each(*alice_reg),
        # Correlate alice_register with bob_register
        [CNOT.on(alice_reg[i], bob_reg[i]) for i in range(num_qubits // 2)],
    )
Esempio n. 5
0
    return noisy_simulation(
        circuit,
        BASE_NOISE,
        obs,
    )


# Simple identity 1-qubit circuit for testing
q = LineQubit(1)
oneq_circ = Circuit(Z.on(q), Z.on(q))

# Simple identity 2-qubit circuit for testing
qreg = LineQubit.range(2)
twoq_circ = Circuit(
    Y.on(qreg[1]),
    CNOT.on(*qreg),
    Y.on(qreg[1]),
)


@mark.parametrize("circuit", [oneq_circ, twoq_circ])
@mark.parametrize("decomposition_dict",
                  [NOISELESS_DECO_DICT, DECO_DICT_SIMP, DECO_DICT])
def test_execute_with_pec_one_qubit(circuit: Circuit,
                                    decomposition_dict: DecompositionDict):
    """Tests that execute_with_pec mitigates the error of a noisy
    expectation value.
    """
    unmitigated = executor(circuit)
    mitigated = execute_with_pec(circuit,
                                 executor,
Esempio n. 6
0
        obs = np.array(
            [[1, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
        )
    else:
        raise ValueError("The input must be a circuit with 1 or 2 qubits.")

    return noisy_simulation(circuit, BASE_NOISE, obs,)


# Simple identity 1-qubit circuit for testing
q = LineQubit(1)
oneq_circ = Circuit(Z.on(q), Z.on(q))

# Simple identity 2-qubit circuit for testing
qreg = LineQubit.range(2)
twoq_circ = Circuit(Y.on(qreg[1]), CNOT.on(*qreg), Y.on(qreg[1]),)


@mark.parametrize("circuit", [oneq_circ, twoq_circ])
@mark.parametrize(
    "decomposition_dict", [NOISELESS_DECO_DICT, DECO_DICT_SIMP, DECO_DICT]
)
def test_execute_with_pec_one_qubit(
    circuit: Circuit, decomposition_dict: DecompositionDict
):
    """Tests that execute_with_pec mitigates the error of a noisy
    expectation value.
    """
    unmitigated = executor(circuit)
    mitigated = execute_with_pec(
        circuit, executor, decomposition_dict=decomposition_dict
Esempio n. 7
0
def _simple_pauli_deco_dict(base_noise: float,
                            simplify_paulis: bool = False
                            ) -> DecompositionDict:
    """Returns a simple hard-coded decomposition
    dictionary to be used for testing and protoptyping.

    The decomposition is compatible with one-qubit or
    two-qubit circuits involving only Pauli and CNOT gates.

    The keys of the output dictionary are Pauli and CNOT operations.

    The decomposition assumes that Pauli and CNOT operations,
    followed by local depolarizing noise, are implementable.

    Args:
        base_noise: The depolarizing noise level.
        simplify_paulis: If True, products of Paulis are simplified to a
            single Pauli. If False, Pauli sequences are not simplified.

    Returns:
        decomposition_dict: The decomposition dictionary.

    """
    # Initialize two qubits
    qreg = LineQubit.range(2)

    # Single-qubit Pauli operations
    i0 = I.on(qreg[0])
    x0 = X.on(qreg[0])
    y0 = Y.on(qreg[0])
    z0 = Z.on(qreg[0])
    i1 = I.on(qreg[1])
    x1 = X.on(qreg[1])
    y1 = Y.on(qreg[1])
    z1 = Z.on(qreg[1])
    single_paulis = [x0, y0, z0, x1, y1, z1]

    # Single-qubit decomposition coefficients
    epsilon = base_noise * 4 / 3
    c_neg = -(1 / 4) * epsilon / (1 - epsilon)
    c_pos = 1 - 3 * c_neg
    assert np.isclose(c_pos + 3 * c_neg, 1.0)

    # Single-qubit decomposition dictionary
    decomposition_dict = {}
    if simplify_paulis:
        # Hard-coded simplified gates
        decomposition_dict = {
            x0: [(c_pos, [x0]), (c_neg, [i0]), (c_neg, [z0]), (c_neg, [y0])],
            y0: [(c_pos, [y0]), (c_neg, [z0]), (c_neg, [i0]), (c_neg, [x0])],
            z0: [(c_pos, [z0]), (c_neg, [y0]), (c_neg, [x0]), (c_neg, [i0])],
            x1: [(c_pos, [x1]), (c_neg, [i1]), (c_neg, [z1]), (c_neg, [y1])],
            y1: [(c_pos, [y1]), (c_neg, [z1]), (c_neg, [i1]), (c_neg, [x1])],
            z1: [(c_pos, [z1]), (c_neg, [y1]), (c_neg, [x1]), (c_neg, [i1])],
        }
    else:
        for local_paulis in [[x0, y0, z0], [x1, y1, z1]]:
            for key in local_paulis:
                key_deco_pos = [(c_pos, [key])]
                key_deco_neg = [(c_neg, [key, op]) for op in local_paulis]
                decomposition_dict[key] = (
                    key_deco_pos + key_deco_neg  # type: ignore
                )

    # Two-qubit Paulis
    xx = [x0, x1]
    xy = [x0, y1]
    xz = [x0, z1]
    yx = [y0, x1]
    yy = [y0, y1]
    yz = [y0, z1]
    zx = [z0, x1]
    zy = [z0, y1]
    zz = [z0, z1]
    double_paulis = [xx, xy, xz, yx, yy, yz, zx, zy, zz]

    # Two-qubit decomposition coefficients (assuming local noise)
    c_pos_pos = c_pos * c_pos
    c_pos_neg = c_neg * c_pos
    c_neg_neg = c_neg * c_neg
    assert np.isclose(c_pos_pos + 6 * c_pos_neg + 9 * c_neg_neg, 1.0)

    cnot = CNOT.on(qreg[0], qreg[1])
    cnot_decomposition = [(c_pos_pos, [cnot])]
    for p in single_paulis:
        cnot_decomposition.append((c_pos_neg, [cnot] + [p]))
    for pp in double_paulis:
        cnot_decomposition.append((c_neg_neg, [cnot] + pp))  # type: ignore

    decomposition_dict[cnot] = cnot_decomposition  # type: ignore

    return decomposition_dict  # type: ignore