Example: QAOA as QiskitPattern¶
This tutorial will be demonstation of creating QAOA as QiskitPattern as well as migration guide on how you can replicate IBM Quantum QAOA custom runtime program.
Let’s first get information on what is QAOA runtime program and what inputs and outputs it has. We will not be implementing full set of input/outputs, but we will cover most important ones. Later on we can recover full functionality if needed.
Description: Qiskit Runtime QAOA program.
Inputs:
name |
type |
description |
---|---|---|
initial_point |
[array,string] |
Initial parameters of the ansatz. Can be an array or the string |
operator |
object |
The cost Hamiltonian, consisting of Pauli I and Z operators, whose smallest eigenvalue we’re trying to find. The type must be a PauliSumOp. |
method |
str |
The classical optimizer used to update the parameters in each iteration. Per default, COBYLA |
ansatz |
QuantumCircuit |
Ansatz for optimization |
Return values
name |
type |
description |
---|---|---|
optimal_point |
array |
The optimal parameter values found during the optimization. |
optimal_value |
number |
The smallest value found during the optimization. Equal to the |
We will also add optional QiskitRuntimeService
as an argument to use that to access real devices.
With that information we can start drafting our pattern implementation in qaoa.py
file.
What our pattern should do:
parse input arguments
create run_qaoa function that accepts estimator instance, creates VQE and runs calculation
decide which sampler to use and run vqe
if runtime service was passed then create a session and run
run_qaoa
functionif runtime service was not passed then use stantard qiskit sampler
save results from qaoa
Roughly our QAOA pattern will look like this. Full code can be found in qaoa.py file.
# qaoa.py
import ...
def run_qaoa(
ansatz: QuantumCircuit,
estimator: BaseEstimator,
operator: PauliSumOp,
initial_point: np.array,
method: str
):
return minimize(cost_func, initial_point, args=(ansatz, operator, estimator), method=method)
arguments = get_arguments()
service = arguments.get("service")
operator = arguments.get("operator")
initial_point = arguments.get("initial_point")
ansatz = arguments.get("ansatz", 1)
...
if service is not None:
# if we have service we need to open a session and create sampler
service = arguments.get("service")
backend = arguments.get("backend", "ibmq_qasm_simulator")
with Session(service=service, backend=backend) as session:
estimator = Estimator(session=session, options=options)
else:
# if we do not have a service let's use standart local sampler
estimator = QiskitEstimator()
result = run_qaoa(ansatz, estimator, operator, initial_point, "COBYLA")
save_result({
"optimal_point": result.x.tolist(),
"optimal_value": result.fun
})
At this point we have our pattern implemented. Now we need to actually run it. But before let’s prepare input arguments from our QAOA pattern.
[1]:
import numpy as np
from qiskit.circuit.library import QAOAAnsatz
from qiskit.quantum_info import SparsePauliOp
from qiskit_ibm_runtime import QiskitRuntimeService
operator = SparsePauliOp.from_list(
[("IIIZZ", 1), ("IIZIZ", 1), ("IZIIZ", 1), ("ZIIIZ", 1)]
)
ansatz = QAOAAnsatz(operator, reps=2)
ansatz = ansatz.decompose(reps=3)
ansatz.draw(fold=-1)
[1]:
┌─────────────┐ ┌──────────────┐ ┌──────────────┐ q_0: ┤ U3(π/2,0,π) ├─■──────────────────────────────■──────────────────────────────■──────────────────────────────■─────────────┤ Rx(2.0*β[0]) ├─■──────────────■──────────────────────────────■──────────────────────────────■─────────────┤ Rx(2.0*β[1]) ├ ├─────────────┤ │ZZ(2.0*γ[0]) ┌──────────────┐ │ │ │ └──────────────┘ │ZZ(2.0*γ[1]) │ ┌──────────────┐ │ │ └──────────────┘ q_1: ┤ U3(π/2,0,π) ├─■─────────────┤ Rx(2.0*β[0]) ├─┼──────────────────────────────┼──────────────────────────────┼──────────────────────────────■──────────────┼─────────────┤ Rx(2.0*β[1]) ├─┼──────────────────────────────┼───────────────────────────── ├─────────────┤ └──────────────┘ │ZZ(2.0*γ[0]) ┌──────────────┐ │ │ │ZZ(2.0*γ[1]) ├──────────────┤ │ │ q_2: ┤ U3(π/2,0,π) ├────────────────────────────────■─────────────┤ Rx(2.0*β[0]) ├─┼──────────────────────────────┼─────────────────────────────────────────────■─────────────┤ Rx(2.0*β[1]) ├─┼──────────────────────────────┼───────────────────────────── ├─────────────┤ └──────────────┘ │ZZ(2.0*γ[0]) ┌──────────────┐ │ └──────────────┘ │ZZ(2.0*γ[1]) ┌──────────────┐ │ q_3: ┤ U3(π/2,0,π) ├───────────────────────────────────────────────────────────────■─────────────┤ Rx(2.0*β[0]) ├─┼────────────────────────────────────────────────────────────────────────────■─────────────┤ Rx(2.0*β[1]) ├─┼───────────────────────────── ├─────────────┤ └──────────────┘ │ZZ(2.0*γ[0]) ┌──────────────┐ └──────────────┘ │ZZ(2.0*γ[1]) ┌──────────────┐ q_4: ┤ U3(π/2,0,π) ├──────────────────────────────────────────────────────────────────────────────────────────────■─────────────┤ Rx(2.0*β[0]) ├──────────────────────────────────────────────────────────────────────────────■─────────────┤ Rx(2.0*β[1]) ├ └─────────────┘ └──────────────┘ └──────────────┘
[2]:
USE_RUNTIME_SERVICE = False
service = None
if USE_RUNTIME_SERVICE:
service = QiskitRuntimeService(
channel='ibm_quantum',
instance='ibm-q/open/main',
token='<insert your token>'
)
backend = None
input_arguments = {
"initial_point": None,
"ansatz": ansatz,
"operator": operator,
"service": service,
"backend": backend,
}
input_arguments
[2]:
{'initial_point': None,
'ansatz': <qiskit.circuit.quantumcircuit.QuantumCircuit at 0x7fd0b0e45310>,
'operator': SparsePauliOp(['IIIZZ', 'IIZIZ', 'IZIIZ', 'ZIIIZ'],
coeffs=[1.+0.j, 1.+0.j, 1.+0.j, 1.+0.j]),
'service': None,
'backend': 'ibmq_qasm_simulator'}
[3]:
from qiskit_serverless import ServerlessClient
import os
[4]:
serverless = ServerlessClient(
token=os.environ.get("GATEWAY_TOKEN", "awesome_token"),
host=os.environ.get("GATEWAY_HOST", "http://localhost:8000"),
)
serverless
[4]:
<ServerlessProvider: gateway-provider>
[5]:
from qiskit_serverless import QiskitFunction
function = QiskitFunction(
title="qaoa", entrypoint="qaoa.py", working_dir="./source_files/qaoa/", dependencies=["qiskit_aer"]
)
serverless.upload(function)
[5]:
'qaoa'
[6]:
job = serverless.run("qaoa", arguments=input_arguments)
job
[6]:
<Job | a55283e5-cfc9-4c6d-8bbb-fc7216d41a47>
[7]:
job.status()
[7]:
'QUEUED'
[8]:
job.result()
[8]:
{'optimal_point': [3.6344694882728,
1.9051953195174722,
2.5515024534598467,
2.701136388288486],
'optimal_value': -3.2939248998743915}