Basics¶
Quantum Inspire supports hybrid classical-quantum algorithms, and allows execution of both the quantum and the classical part fully on the Qauntum Inspire servers.
Script format¶
Any script that will be executed on the QI platform is required to have at least the execute()
and finalize()
functions.
Execute¶
The execute function is the function that gets called by Quantum Inspire first. As an argument it gets a QuantumInterface
, which is what allows you to actually execute your circuit. In the example below, the same circuit is executed 5 times in a row as a simple illustration of the interchanging control between the hybrid and classical domains. Normally, you might want to change your QNode
(circuit) between each execution based on your results. Refer to this example to see how you can dynamically update your quantum circuit.
from typing import Any, Dict, List
import pennylane as qml
from pennylane import numpy as np
from qi2_shared.hybrid.quantum_interface import QuantumInterface
from qiskit import QuantumCircuit
from qiskit_quantuminspire.hybrid.hybrid_backend import QIHybridBackend
from pennylane_quantuminspire import cqasm
def generate_circuit(device: QIDevice):
@qml.qnode(device)
def circuit(circuit_params): # type: ignore
qml.RX(circuit_params[0], wires=0)
qml.RY(circuit_params[1], wires=1)
qml.CNOT(wires=[0, 1])
return qml.expval(qml.PauliZ(0))
return circuit
def execute(qi: QuantumInterface) -> None:
"""Run the classical part of the Hybrid Quantum/Classical Algorithm.
Args:
qi: A QuantumInterface instance that can be used to execute quantum circuits
The qi object has a single method called execute_circuit, its interface is described below:
qi.execute_circuit args:
circuit: a string representation of a quantum circuit
number_of_shots: how often to execute the circuit
raw_data_enabled (default: False): report measurement per shot (if supported by backend type)
qi.execute_circuit return value:
The results of executing the quantum circuit, this is an object with the following attributes
results: The results from iteration n-1.
raw_data: Measurement per shot as a list of strings (or None if disabled).
shots_requested: The number of shots requested by the user for the previous iteration.
shots_done: The number of shots actually run.
"""
backend = QIHybridBackend(qi)
device = QIDevice(backend=backend)
circuit_params = np.array([0.1, 0.2], requires_grad=True)
circuit = generate_circuit(device)
for _ in range(1, 5):
circuit_str = cqasm.dumps(circuit, circuit_params)
result = qi.execute_circuit(circuit_str, 1024)
Finalize¶
The finalize()
function allows you to aggregate your results. It should return a dictionary which will be stored as the final result. By default it takes a list of all measurements as an argument, but through the use of globals you could also export other data.
def finalize(list_of_measurements: List[Dict[str, Any]]) -> Dict[str, Any]:
"""Aggregate the results from all iterations into one final result.
Args:
list_of_measurements: List of all results from the previous iterations.
Returns:
A free-form result, with a `property`: `value` structure. Value can
be everything serializable.
"""
return {"results": list_of_measurements}
Execution¶
A complete script with both example functions can be found here. This can be uploaded to QI as follows (using the CLI), where <backend_id>
is the id number of the selected quantum backend, which can be retrieved using the QIProvider.
provider = QIProvider()
for backend in provider.backends():
print(backend)
qi files upload ./simple.py <backend_id>
The final results can be retrieved as follows, where <job_id>
is the job id returned by the previous command.
qi final_results get <job_id>