Quickstart: Submit a circuit with Cirq to Azure Quantum
Note
The examples in this topic use the Microsoft Quantum Development Kit (Classic QDK) and are not yet compatible with the Azure Quantum Development Kit Preview (Modern QDK). For more information about the Modern QDK, see the QSharp GitHub Wiki.
Learn how to use the azure-quantum
Python package to submit Cirq quantum circuits to an IonQ or Quantinuum quantum computing target via the Azure Quantum service. For more information, see Quantum circuits.
For information on how to submit a circuit with Qiskit, see Quickstart: Submit a circuit with Qiskit to Azure Quantum.
Prerequisites
An Azure account with an active subscription. Create an account for free.
Create an Azure Quantum workspace and enable your preferred provider, Quantinuum or IonQ (or both), for this scenario. For more information, see Create an Azure Quantum workspace.
Install the latest
azure-quantum
Python package using the [cirq] tag.Tip
If you are using Miniconda or Anaconda, you can optionally create a new environment by downloading environment.yml and running the following:
conda env create -f environment.yml
This creates a new conda environment that you can activate with the following:
conda activate azurequantum
Start your favorite code editor or interactive Python tool, such as VS Code, Jupyter or iPython.
Load the required imports
First, run the following cell to load the required imports:
from azure.quantum.cirq import AzureQuantumService
Connect to the Azure Quantum service
To connect to the Azure Quantum service, your program will need the resource ID and the location of your Azure Quantum workspace. Log in to your Azure account, https://portal.azure.com, navigate to your Azure Quantum workspace, and copy the values from the header.
Paste the values into the following AzureQuantumService
constructor to
create a service
object that connects to your Azure Quantum workspace.
Optionally, you can specify a default target, in this case the IonQ simulator:
service = AzureQuantumService(
resource_id="",
location="",
default_target="ionq.simulator"
)
List all targets
Note
The target names for the Quantinuum Syntax Checkers, Emulators, and QPUs have recently changed. The updated names are used in this topic. For details, see the Quantinuum provider topic.
Use the targets()
method to list all the targets in your workspace that can run your circuit, including the
current queue time and availability.
Note
All the targets in your workspace may not be listed - only the targets that can accept a Cirq or OpenQASM circuit will be listed here.
print(service.targets())
[<Target name="quantinuum.qpu.h1-1", avg. queue time=0 s, Degraded>,
<Target name="quantinuum.sim.h1-1sc", avg. queue time=1 s, Available>,
<Target name="quantinuum.qpu.h1-2", avg. queue time=217300 s, Unavailable>,
<Target name="quantinuum.sim.h1-2sc", avg. queue time=0 s, Available>,
<Target name="quantinuum.sim.h1-1e", avg. queue time=40 s, Available>,
<Target name="quantinuum.sim.h1-2e", avg. queue time=64 s, Available>,
<Target name="ionq.qpu", avg. queue time=229 s, Available>,
<Target name="ionq.simulator", avg. queue time=3 s, Available>,
<Target name="ionq.qpu.aria-1", avg. queue time=1136774 s, Available>]
Run a simple circuit
Next, create a simple Cirq circuit to run. This circuit uses the square root of X gate, native to the IonQ hardware system.
import cirq
q0, q1 = cirq.LineQubit.range(2)
circuit = cirq.Circuit(
cirq.X(q0)**0.5, # Square root of X
cirq.CX(q0, q1), # CNOT
cirq.measure(q0, q1, key='b') # Measure both qubits
)
print(circuit)
0: ───X^0.5───@───M────────
│ │
1: ───────────X───M────────
You can now run the program via the Azure Quantum service and get the result. The following cell submits a job (to the default IonQ simulator) that runs the circuit with 100 shots, waits until the job is complete, and returns the results.
result = service.run(program=circuit, repetitions=100)
This returns a cirq.Result
object.
print(result)
b=1001100101100001000011011101000011010100010111100011001000100100010000001110010010101110110000011010, 1001100101100001000011011101000011010100010111100011001000100100010000001110010010101110110000011010
Estimate job cost
Before running a job on the QPU, you can estimate how much it will cost to run. To estimate the cost of running a job on the QPU, you can use the estimate_cost
method:
cost = service.estimate_cost(
program=circuit,
repetitions=100,
target="ionq.qpu"
)
print(f"Estimated cost: {cost.estimated_total}")
This prints the estimated cost in USD.
For the most current pricing details, see IonQ Pricing, or find your workspace and view pricing options in the "Provider" tab of your workspace via: aka.ms/aq/myworkspaces.
Run on IonQ QPU
The previous job ran on the default simulator, "ionq.simulator"
. However, you can also run it on IonQ's hardware processor (a Quantum Processor Unit (QPU)). To run on the IonQ QPU, provide "ionq.qpu"
as the
target
argument:
result = service.run(
program=circuit,
repetitions=100,
target="ionq.qpu",
timeout_seconds=500 # Set timeout to accommodate queue time on QPU
)
Again, this returns a cirq.Result
object.
print(result)
b=0101011011011111100001011101101011011110100010000000011110111000100100110110101100110001001111101111, 0101011011011111100001011101101011011110100010000000011110111000100100110110101100110001001111101111
Asynchronous model using Jobs
For long-running circuits, it can be useful to run them asynchronously.
The service.create_job
method returns a Job
object, which you can use to
get the results after the job has run successfully.
job = service.create_job(
program=circuit,
repetitions=100,
target="ionq.simulator"
)
To check on the job status, use job.status()
:
print(job.status())
'completed'
To wait for the job to complete and then get the results, use the blocking
call job.results()
:
result = job.results()
print(result)
00: 0.5
11: 0.5
Note that this does not return a cirq.Result
object. Instead it
returns a result object that is specific to the IonQ simulator and uses
state probabilities instead of shot data.
type(result)
cirq_ionq.results.SimulatorResult
To convert this to a cirq.Result
object, use result.to_cirq_result()
:
print(result.to_cirq_result())
b=1110101111111110111000011101011111001100010000001011011101001111001111001101100111010000001100011100, 1110101111111110111000011101011111001100010000001011011101001111001111001101100111010000001100011100
Load the required imports
First, run the following cell to load the required imports:
from azure.quantum.cirq import AzureQuantumService
Connect to the Azure Quantum service
To connect to the Azure Quantum service, your program will need the resource ID and the location of your Azure Quantum workspace. Log in to your Azure account, https://portal.azure.com, navigate to your Azure Quantum workspace, and copy the values from the header.
Paste the values into the following AzureQuantumService
constructor to
create a service
object that connects to your Azure Quantum workspace.
Optionally, specify a default target:
from azure.quantum.cirq import AzureQuantumService
service = AzureQuantumService(
resource_id="",
location="",
default_target="quantinuum.sim.h1-1sc"
)
List all targets
Note
The target names for the Quantinuum Syntax Checkers, Emulators, and QPUs have recently changed. The updated names are used in this topic. For details, see the Quantinuum provider topic.
Use the targets()
method to list all the targets in your workspace that can run your circuit, including the
current queue time and availability.
Note
All the targets in your workspace may not be listed - only the targets that can accept a Cirq or OpenQASM circuit will be listed here.
print(service.targets())
[<Target name="quantinuum.qpu.h1-1", avg. queue time=0 s, Degraded>,
<Target name="quantinuum.sim.h1-1sc", avg. queue time=1 s, Available>,
<Target name="quantinuum.qpu.h1-2", avg. queue time=217300 s, Unavailable>,
<Target name="quantinuum.sim.h1-2sc", avg. queue time=0 s, Available>,
<Target name="quantinuum.sim.h1-1e", avg. queue time=40 s, Available>,
<Target name="quantinuum.sim.h1-2e", avg. queue time=64 s, Available>,
<Target name="ionq.qpu", avg. queue time=229 s, Available>,
<Target name="ionq.simulator", avg. queue time=3 s, Available>,
<Target name="ionq.qpu.aria-1", avg. queue time=1136774 s, Available>]
Run a simple circuit on the API validator
Next, create a simple Cirq circuit to run.
import cirq
q0, q1 = cirq.LineQubit.range(2)
circuit = cirq.Circuit(
cirq.H(q0), # Hadamard
cirq.CNOT(q0, q1), # CNOT
cirq.measure(q0, q1, key='b') # Measure both qubits
)
print(circuit)
0: ───H───@───M────────
│ │
1: ───────X───M────────
You can now run the program via the Azure Quantum service and get the result. The following cell submits a job that runs the circuit with 100 shots, waits until the job is complete, and returns the results.
result = service.run(program=circuit, repetitions=100)
This returns a cirq.Result
object.
print(result)
b=0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
You can plot the results in a histogram:
import pylab as pl
pl.hist(result.data)
pl.ylabel("Counts")
pl.xlabel("Result")
Looking at the histogram, you may notice that the random number generator returned 0 every time, which is not very random. This is because that, while the API Validator ensures that your code will run successfully on Quantinuum hardware, it also returns 0 for every quantum measurement. For a true random number generator, you need to run your circuit on quantum hardware.
Estimate job cost
Before running a job on the QPU, you can estimate how much it will cost to run. To estimate the cost of running a job on the QPU, you can use the estimate_cost
method.
Note
To run a cost estimate against a Quantinuum target, you must first reload the azure-quantum Python package with the [qiskit] parameter. For more information, see Update the azure-quantum Python package.
cost = service.estimate_cost(
program=circuit,
repetitions=100,
target="quantinuum.qpu.h1-1"
)
print(f"Estimated cost: {cost.estimated_total}")
This prints the estimated cost in H-System Credits (HQCs).
For the most current pricing details, see Azure Quantum pricing, or find your workspace and view pricing options in the "Provider" tab of your workspace via: aka.ms/aq/myworkspaces.
Asynchronous workflow using Jobs
For long-running circuits, it can be useful to run them asynchronously.
The service.create_job
method returns a Job
object, which you can use to
get the results after the job has run successfully.
job = service.create_job(
program=circuit,
repetitions=100,
target="quantinuum.sim.h1-1sc"
)
To check on the job status, use job.status()
:
print(job.status())
'Waiting'
To wait for the job to complete and then get the results, use the blocking
call job.results()
:
result = job.results()
print(result)
{'m_b': ['00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00', '00']}
Note that this does not return a cirq.Result
object. Instead, it
returns a dictionary of bitstring measurement results indexed by measurement key.
Important
Submitting multiple circuits on a single job is currently not supported. As a workaround you can call the backend.run
method to submit each circuit asynchronously, then fetch the results of each job. For example:
jobs = []
for circuit in circuits:
jobs.append(backend.run(circuit, shots=N))
results = []
for job in jobs:
results.append(job.result())
Feedback
Submit and view feedback for