コード例 #1
0
ファイル: farhi_ansatz.py プロジェクト: qfizik/z-quantum-qaoa
    def _generate_circuit(self,
                          params: Optional[np.ndarray] = None) -> Circuit:
        """Returns a parametrizable circuit represention of the ansatz.
        By convention the initial state is taken to be the |+..+> state and is
        evolved first under the cost Hamiltonian and then the mixer Hamiltonian.
        Args:
            params: parameters of the circuit.
        """
        if params is not None:
            Warning(
                "This method retuns a parametrizable circuit, params will be ignored."
            )
        circuit = Circuit()
        qubits = [
            Qubit(qubit_index) for qubit_index in range(self.number_of_qubits)
        ]
        circuit.qubits = qubits

        # Prepare initial state
        circuit += create_layer_of_gates(self.number_of_qubits, "H")

        # Add time evolution layers
        pyquil_cost_hamiltonian = qubitop_to_pyquilpauli(
            change_operator_type(self._cost_hamiltonian, QubitOperator))
        pyquil_mixer_hamiltonian = qubitop_to_pyquilpauli(
            self._mixer_hamiltonian)

        for i in range(self.number_of_layers):
            circuit += time_evolution(pyquil_cost_hamiltonian,
                                      sympy.Symbol(f"gamma_{i}"))
            circuit += time_evolution(pyquil_mixer_hamiltonian,
                                      sympy.Symbol(f"beta_{i}"))

        return circuit
コード例 #2
0
    def _generate_circuit(self,
                          params: Optional[np.ndarray] = None) -> Circuit:
        """Returns a parametrizable circuit represention of the ansatz.
        By convention the initial state is taken to be the |+..+> state and is
        evolved first under the cost Hamiltonian and then the mixer Hamiltonian.
        Args:
            params: parameters of the circuit.
        """
        if params is not None:
            Warning(
                "This method retuns a parametrizable circuit, params will be ignored."
            )
        circuit = Circuit()

        # Prepare initial state
        circuit += create_layer_of_gates(self.number_of_qubits, H)

        # Add time evolution layers
        cost_circuit = time_evolution(
            change_operator_type(self._cost_hamiltonian, QubitOperator),
            sympy.Symbol(f"gamma"),
        )
        mixer_circuit = time_evolution(self._mixer_hamiltonian,
                                       sympy.Symbol(f"beta"))
        for i in range(self.number_of_layers):
            circuit += cost_circuit.bind(
                {sympy.Symbol(f"gamma"): sympy.Symbol(f"gamma_{i}")})
            circuit += mixer_circuit.bind(
                {sympy.Symbol(f"beta"): sympy.Symbol(f"beta_{i}")})

        return circuit
コード例 #3
0
    def _generate_circuit(self,
                          params: Optional[np.ndarray] = None) -> Circuit:
        """Returns a parametrizable circuit represention of the ansatz.
        Args:
            params: parameters of the circuit.
        """
        if params is not None:
            Warning(
                "This method retuns a parametrizable circuit, params will be ignored."
            )
        circuit = Circuit()

        # Prepare initial state
        circuit += create_layer_of_gates(self.number_of_qubits, RY,
                                         self._thetas)

        # Add time evolution layers
        cost_circuit = time_evolution(
            change_operator_type(self._cost_hamiltonian, QubitOperator),
            sympy.Symbol(f"gamma"),
        )
        for i in range(self.number_of_layers):
            circuit += cost_circuit.bind(
                {sympy.Symbol(f"gamma"): sympy.Symbol(f"gamma_{i}")})
            circuit += create_layer_of_gates(self.number_of_qubits, RY,
                                             -self._thetas)
            circuit += create_layer_of_gates(
                self.number_of_qubits,
                RZ,
                [-2 * sympy.Symbol(f"beta_{i}")] * self.number_of_qubits,
            )
            circuit += create_layer_of_gates(self.number_of_qubits, RY,
                                             self._thetas)

        return circuit
コード例 #4
0
    def test_evolution_with_numerical_time_produces_correct_result(
            self, hamiltonian, time, order):
        expected_zquantum_circuit = _zquantum_exponentiate_hamiltonian(
            hamiltonian, time, order)

        reference_unitary = expected_zquantum_circuit.to_unitary()
        unitary = time_evolution(hamiltonian, time,
                                 trotter_order=order).to_unitary()

        assert compare_unitary(unitary, reference_unitary, tol=1e-10)
コード例 #5
0
    def test_evolution_with_numerical_time_produces_correct_result(
        self, hamiltonian, time, order
    ):
        cirq_qubits = cirq.LineQubit(0), cirq.LineQubit(1)
        expected_cirq_circuit = _cirq_exponentiate_hamiltonian(
            hamiltonian, cirq_qubits, time, order
        )

        reference_unitary = cirq.unitary(expected_cirq_circuit)
        unitary = time_evolution(hamiltonian, time, trotter_order=order).to_unitary()
        assert compare_unitary(unitary, reference_unitary, tol=1e-10)
コード例 #6
0
def build_qaoa_circuit(params, hamiltonians):
    """Generates a circuit for QAOA. This is not only for QAOA proposed by Farhi
    et al., but also general ansatz where alternating layers of time evolution under 
    two different Hamiltonians H1 and H2 are considered.

    Args:
        hamiltonians (list):
            A list of dict or zquantum.core.qubitoperator.QubitOperator objects representing Hamiltonians
            H1, H2, ..., Hk which forms one layer of the ansatz
                    exp(-i Hk tk) ... exp(-i H2 t2) exp(-i H1 t1)
            For example, in the case of QAOA proposed by Farhi et al, the list the list is then
            [H1, H2] where
                H1 is the Hamiltonian for which the ground state is sought, and
                H2 is the Hamiltonian for which the time evolution act as a diffuser 
                    in the search space.
        params (numpy.ndarray): 
            A list of sets of parameters. Each parameter in a set specifies the time
            duration of evolution under each of the Hamiltonians H1, H2, ... Hk.
            
    Returns:
        zquantum.core.circuit.Circuit: the ansatz circuit
    """

    if mod(len(params), len(hamiltonians)) != 0:
        raise Warning('There are {} input parameters and {} Hamiltonians. Since {} does not divide {} the last layer will be incomplete.'.\
            format(len(params), len(hamiltonians), len(params), len(hamiltonians)))

    # Convert qubit operators from dicts to QubitOperator objects, if needed
    for index, hamiltonian in enumerate(hamiltonians):
        if isinstance(hamiltonian, dict):
            hamiltonians[index] = convert_dict_to_qubitop(hamiltonian)

    output = Circuit()

    # Start with a layer of Hadarmard gates
    n_qubits = count_qubits(hamiltonians[0])
    qubits = [Qubit(qubit_index) for qubit_index in range(n_qubits)]
    output.qubits = qubits
    for qubit_index in range(n_qubits):
        output.gates.append(Gate('H', (qubits[qubit_index], )))

    # Add time evolution layers
    for i in range(params.shape[0]):
        hamiltonian_index = mod(i, len(hamiltonians))
        current_hamiltonian = qubitop_to_pyquilpauli(
            hamiltonians[hamiltonian_index])
        output += time_evolution(current_hamiltonian, params[i])

    return output
コード例 #7
0
    def test_time_evolution_with_symbolic_time_produces_correct_unitary(
            self, hamiltonian, time_value, order):
        time_symbol = sympy.Symbol("t")
        symbols_map = {time_symbol: time_value}

        expected_zquantum_circuit = _zquantum_exponentiate_hamiltonian(
            hamiltonian, time_value, order)

        reference_unitary = expected_zquantum_circuit.to_unitary()

        unitary = (time_evolution(
            hamiltonian, time_symbol,
            trotter_order=order).bind(symbols_map).to_unitary())

        assert compare_unitary(unitary, reference_unitary, tol=1e-10)
コード例 #8
0
    def test_time_evolution_with_symbolic_time_produces_correct_unitary(
        self, hamiltonian, time_value, order
    ):
        time_symbol = sympy.Symbol("t")
        symbols_map = [(time_symbol, time_value)]

        cirq_qubits = cirq.LineQubit(0), cirq.LineQubit(1)

        expected_cirq_circuit = _cirq_exponentiate_hamiltonian(
            hamiltonian, cirq_qubits, time_value, order
        )

        reference_unitary = cirq.unitary(expected_cirq_circuit)

        unitary = (
            time_evolution(hamiltonian, time_symbol, trotter_order=order)
            .evaluate(symbols_map)
            .to_unitary()
        )
        assert compare_unitary(unitary, reference_unitary, tol=1e-10)
コード例 #9
0
    def _generate_circuit(self,
                          params: Optional[np.ndarray] = None) -> Circuit:
        """Returns a parametrizable circuit represention of the ansatz.
        Args:
            params: parameters of the circuit.
        """
        if params is not None:
            Warning(
                "This method retuns a parametrizable circuit, params will be ignored."
            )
        circuit = Circuit()
        qubits = [
            Qubit(qubit_index) for qubit_index in range(self.number_of_qubits)
        ]
        circuit.qubits = qubits

        # Prepare initial state
        circuit += create_layer_of_gates(self.number_of_qubits, "Ry",
                                         self._thetas)

        pyquil_cost_hamiltonian = qubitop_to_pyquilpauli(
            change_operator_type(self._cost_hamiltonian, QubitOperator))

        # Add time evolution layers
        for i in range(self.number_of_layers):
            circuit += time_evolution(pyquil_cost_hamiltonian,
                                      sympy.Symbol(f"gamma_{i}"))
            circuit += create_layer_of_gates(self.number_of_qubits, "Ry",
                                             -self._thetas)
            circuit += create_layer_of_gates(
                self.number_of_qubits,
                "Rz",
                [-2 * sympy.Symbol(f"beta_{i}")] * self.number_of_qubits,
            )
            circuit += create_layer_of_gates(self.number_of_qubits, "Ry",
                                             self._thetas)

        return circuit
コード例 #10
0
def build_qaoa_circuit_grads(params, hamiltonians):
    """ Generates gradient circuits and corresponding factors for the QAOA ansatz
        defined in the function build_qaoa_circuit.

    Args:
        hamiltonians (list):
            A list of dict or zquantum.core.qubitoperator.QubitOperator objects representing Hamiltonians
            H1, H2, ..., Hk which forms one layer of the ansatz
                    exp(-i Hk tk) ... exp(-i H2 t2) exp(-i H1 t1)
            For example, in the case of QAOA proposed by Farhi et al, the list the list is then
            [H1, H2] where
                H1 is the Hamiltonian for which the ground state is sought, and
                H2 is the Hamiltonian for which the time evolution act as a diffuser 
                    in the search space.
        params (numpy.ndarray): 
            A list of sets of parameters. Each parameter in a set specifies the time
            duration of evolution under each of the Hamiltonians H1, H2, ... Hk.
            
    Returns:
        gradient_circuits (list of lists of zquantum.core.circuit.Circuit: the circuits)
        circuit_factors (list of lists of floats): combination coefficients for the expectation
            values of the list of circuits.
    """
    if mod(len(params), len(hamiltonians)) != 0:
        raise Warning('There are {} input parameters and {} Hamiltonians. Since {} does not divide {} the last layer will be incomplete.'.\
            format(len(params), len(hamiltonians), len(params), len(hamiltonians)))

    # Convert qubit operators from dicts to QubitOperator objects, if needed
    for index, hamiltonian in enumerate(hamiltonians):
        if isinstance(hamiltonian, dict):
            hamiltonians[index] = convert_dict_to_qubitop(hamiltonian)

    hadamard_layer = Circuit()

    # Start with a layer of Hadarmard gates
    n_qubits = count_qubits(hamiltonians[0])
    qubits = [Qubit(qubit_index) for qubit_index in range(n_qubits)]
    hadamard_layer.qubits = qubits
    for qubit_index in range(n_qubits):
        hadamard_layer.gates.append(Gate('H', (qubits[qubit_index], )))

    # Add time evolution layers
    gradient_circuits = []
    circuit_factors = []

    for index1 in range(params.shape[0]):

        hamiltonian_index1 = mod(index1, len(hamiltonians))
        current_hamiltonian = qubitop_to_pyquilpauli(
            hamiltonians[hamiltonian_index1])
        derivative_circuits_for_index1, factors = time_evolution_derivatives(
            current_hamiltonian, params[index1])
        param_circuits = []

        for derivative_circuit in derivative_circuits_for_index1:

            output_circuit = Circuit()
            output_circuit.qubits = qubits
            output_circuit += hadamard_layer

            for index2 in range(params.shape[0]):
                hamiltonian_index2 = mod(index2, len(hamiltonians))
                if index2 == index1:
                    output_circuit += derivative_circuit
                else:
                    current_hamiltonian = qubitop_to_pyquilpauli(
                        hamiltonians[hamiltonian_index2])
                    output_circuit += time_evolution(current_hamiltonian,
                                                     params[index2])
            param_circuits.append(output_circuit)

        circuit_factors.append(factors)
        gradient_circuits.append(param_circuits)

    return gradient_circuits, circuit_factors