예제 #1
0
    def test_raises_error_if_position_is_larger_or_equal_to_length(
            self, length, invalid_position):
        repeated_circuit = circuits.Circuit([circuits.X(0)])
        different_circuit = circuits.Circuit([circuits.Y(0)])

        with pytest.raises(ValueError):
            _generate_circuit_sequence(repeated_circuit, different_circuit,
                                       length, invalid_position)
예제 #2
0
class TestGeneratingCircuitSequence:
    @pytest.mark.parametrize(
        "repeated_circuit, different_circuit, length, position, expected_result",
        [
            (
                circuits.Circuit([circuits.X(0), circuits.Y(1)]),
                circuits.Circuit([circuits.Z(1)]),
                5,
                1,
                circuits.Circuit([
                    *[circuits.X(0), circuits.Y(1)],
                    circuits.Z(1),
                    *([circuits.X(0), circuits.Y(1)] * 3),
                ]),
            ),
            (
                circuits.Circuit([circuits.RX(0.5)(1)]),
                circuits.Circuit([circuits.CNOT(0, 2)]),
                3,
                0,
                circuits.Circuit([
                    circuits.CNOT(0, 2),
                    circuits.RX(0.5)(1),
                    circuits.RX(0.5)(1)
                ]),
            ),
            (
                circuits.Circuit([circuits.RX(0.5)(1)]),
                circuits.Circuit([circuits.CNOT(0, 2)]),
                3,
                2,
                circuits.Circuit([
                    circuits.RX(0.5)(1),
                    circuits.RX(0.5)(1),
                    circuits.CNOT(0, 2),
                ]),
            ),
        ],
    )
    def test_produces_correct_sequence(self, repeated_circuit,
                                       different_circuit, position, length,
                                       expected_result):
        actual_result = _generate_circuit_sequence(repeated_circuit,
                                                   different_circuit, length,
                                                   position)

        assert actual_result == expected_result

    @pytest.mark.parametrize("length, invalid_position", [(5, 5), (4, 6)])
    def test_raises_error_if_position_is_larger_or_equal_to_length(
            self, length, invalid_position):
        repeated_circuit = circuits.Circuit([circuits.X(0)])
        different_circuit = circuits.Circuit([circuits.Y(0)])

        with pytest.raises(ValueError):
            _generate_circuit_sequence(repeated_circuit, different_circuit,
                                       length, invalid_position)
예제 #3
0
def _generate_circuit_sequence(
    repeated_circuit: circuits.Circuit,
    different_circuit: circuits.Circuit,
    length: int,
    position: int,
):
    """Join multiple copies of circuit, replacing one copy with a different circuit.

    Args:
        repeated_circuit: circuit which copies should be concatenated
        different_circuit: circuit that will replace one copy of `repeated_circuit
        length: total number of circuits to join
        position: which copy of repeated_circuit should be replaced by
        `different_circuit`.
    Returns:
        Concatenation of circuits C_1, ..., C_length, where C_i = `repeated_circuit`
        if i != position and C_i = `different_circuit` if i == position.
    """
    if position >= length:
        raise ValueError(f"Position {position} should be < {length}")

    return circuits.Circuit(
        list(
            chain.from_iterable(
                [
                    (
                        repeated_circuit if i != position else different_circuit
                    ).operations
                    for i in range(length)
                ]
            )
        )
    )
예제 #4
0
def _time_evolution_for_term_qubit_operator(
    term: QubitOperator, time: Union[float, sympy.Expr]
) -> circuits.Circuit:
    """Evolves a Pauli term for a given time and returns a circuit representing it.
    Based on section 4 from https://arxiv.org/abs/1001.3855 .
    Args:
        term: Pauli term to be evolved
        time: time of evolution
    Returns:
        Circuit: Circuit representing evolved pyquil term.
    """

    if len(term.terms) != 1:
        raise ValueError("This function works only on a single term.")
    term_components = list(term.terms.keys())[0]
    base_changes = []
    base_reversals = []
    cnot_gates = []
    central_gate = None
    term_types = [component[1] for component in term_components]
    qubit_indices = [component[0] for component in term_components]
    coefficient = list(term.terms.values())[0]

    for i, (term_type, qubit_id) in enumerate(zip(term_types, qubit_indices)):
        if term_type == "X":
            base_changes.append(H(qubit_id))
            base_reversals.append(H(qubit_id))
        elif term_type == "Y":
            base_changes.append(RX(np.pi / 2)(qubit_id))
            base_reversals.append(RX(-np.pi / 2)(qubit_id))
        if i == len(term_components) - 1:
            central_gate = RZ(2 * time * coefficient)(qubit_id)
        else:
            cnot_gates.append(CNOT(qubit_id, qubit_indices[i + 1]))

    circuit = circuits.Circuit()
    for gate in base_changes:
        circuit += gate

    for gate in cnot_gates:
        circuit += gate

    circuit += central_gate

    for gate in reversed(cnot_gates):
        circuit += gate

    for gate in base_reversals:
        circuit += gate

    return circuit
예제 #5
0
def _time_evolution_for_term_pyquil(
    term: pyquil.paulis.PauliTerm, time: Union[float, sympy.Expr]
) -> circuits.Circuit:
    """Construct a circuit simulating evolution under a given Pauli hamiltonian.

    Args:
        term: Pauli term describing evolution
        time: time of evolution
    Returns:
        Circuit simulating time evolution.
    """
    if isinstance(time, sympy.Expr):
        circuit = circuits.import_from_pyquil(pyquil.paulis.exponentiate(term))

        new_circuit = circuits.Circuit(
            [_adjust_gate_angle(operation, time) for operation in circuit.operations]
        )
    else:
        exponent = term * time
        new_circuit = circuits.import_from_pyquil(pyquil.paulis.exponentiate(exponent))

    return new_circuit
예제 #6
0
def _make_old_circuit_with_inactive_qubits(x_qubit, cnot_qubits, n_qubits):
    circuit = old_circuit.Circuit(
        pyquil.Program(pyquil.gates.X(x_qubit),
                       pyquil.gates.CNOT(*cnot_qubits)))
    circuit.qubits = [old_circuit.Qubit(i) for i in range(n_qubits)]
    return circuit


@pytest.mark.parametrize(
    "old,new",
    [
        *[(_old_circuit_from_pyquil(program),
           _new_circuit_from_pyquil(program)) for program in PYQUIL_PROGRAMS],
        (
            _make_old_parametric_circuit(),
            new_circuits.Circuit([new_circuits.RX(THETA_1)(0)]),
        ),
        *[(
            _make_old_circuit_with_inactive_qubits(x_qubit, cnot_qubits,
                                                   n_qubits),
            new_circuits.Circuit(
                [new_circuits.X(x_qubit),
                 new_circuits.CNOT(*cnot_qubits)],
                n_qubits=n_qubits,
            ),
        ) for x_qubit, cnot_qubits, n_qubits in [
            (0, (1, 2), 4),
            (1, (3, 4), 5),
            (0, (2, 3), 4),
        ]],
    ],
예제 #7
0
def time_evolution_derivatives(
    hamiltonian: pyquil.paulis.PauliSum,
    time: float,
    method: str = "Trotter",
    trotter_order: int = 1,
) -> Tuple[List[circuits.Circuit], List[float]]:
    """Generates derivative circuits for the time evolution operator defined in
    function time_evolution

    Args:
        hamiltonian: The Hamiltonian to be evolved under. It should contain numeric
            coefficients, symbolic expressions aren't supported.
        time: time duration of the evolution.
        method: time evolution method. Currently the only option is 'Trotter'.
        trotter_order: order of Trotter evolution

    Returns:
        A Circuit simulating time evolution.
    """
    if method != "Trotter":
        raise ValueError(f"The method {method} is currently not supported.")

    single_trotter_derivatives = []
    factors = [1.0, -1.0]
    output_factors = []
    if isinstance(hamiltonian, QubitOperator):
        terms = list(hamiltonian.get_operators())
    elif isinstance(hamiltonian, pyquil.paulis.PauliSum):
        warnings.warn(
            "PauliSum as an input to time_evolution_derivatives will be depreciated, "
            "please change to QubitOperator instead.",
            DeprecationWarning,
        )
        terms = hamiltonian.terms

    for i, term_1 in enumerate(terms):
        for factor in factors:
            output = circuits.Circuit()

            try:
                if isinstance(term_1, QubitOperator):
                    r = list(term_1.terms.values())[0] / trotter_order
                else:
                    r = complex(term_1.coefficient).real / trotter_order
            except TypeError:
                raise ValueError(
                    "Term coefficients need to be numerical. "
                    f"Offending term: {term_1}"
                )
            output_factors.append(r * factor)
            shift = factor * (np.pi / (4.0 * r))

            for j, term_2 in enumerate(terms):
                output += time_evolution_for_term(
                    term_2,
                    (time + shift) / trotter_order if i == j else time / trotter_order,
                )

            single_trotter_derivatives.append(output)

    if trotter_order > 1:
        output_circuits = []
        final_factors = []

        repeated_circuit = time_evolution(
            hamiltonian, time, method="Trotter", trotter_order=1
        )

        for position in range(trotter_order):
            for factor, different_circuit in zip(
                output_factors, single_trotter_derivatives
            ):
                output_circuits.append(
                    _generate_circuit_sequence(
                        repeated_circuit, different_circuit, trotter_order, position
                    )
                )
                final_factors.append(factor)
        return output_circuits, final_factors
    else:
        return single_trotter_derivatives, output_factors