Time: 15-30 Minutes
Expected knowledge: Basic Python knowledge is required. Some knowledge of quantum circuits and algorithms is helpful but optional
System requirements: Internet access, Python 3.7 or later

Cirq is an open source Python framework for writing, modifying, and running programs for quantum computers. As of v0.12.0, Cirq-Ionq provides support for IonQ’s trapped-ion systems. This means that you can write quantum circuits and run them on IonQ’s trapped-ion quantum computers, all from within the Cirq framework.

In this guide we will walk you through the process of writing and executing a quantum program on an IonQ trapped-ion quantum computer via Cirq. We’ll see that this process allows you to swap out Cirq’s simulators for the IonQ device with only a few lines of code.

About Cirq

Cirq is an open-source Python framework which focuses on noisy intermediate scale quantum (NISQ) processors. It was developed by the quantum software team at Google, and is now supported by a vibrant open source community beyond Google. If you’re new to the framework, this tutorial gives a great introduction to the basics of Cirq and its philosophy.

Installing Cirq and the Cirq-IonQ Integration

For a simple install, you can install Cirq from the Python Package Index (PyPI) using pip:

pip install cirq-ionq==v0.14.1

However, to get the best experience from the framework, install recommended add-on packages, or for instructions specific to your OS, we recommend following the complete Cirq installation guide.

The IonQ Cloud API

IonQ’s quantum computers run behind the IonQ Quantum Cloud and are exposed via an API. That is to say, there are quantum computers in IonQ’s quantum data center, and you can remotely run quantum programs by sending them over the internet to the IonQ Cloud API. The IonQ Quantum Cloud will then take care of optimizing these programs for IonQ hardware and running them on the quantum computers themselves.

More specifically, quantum programs are sent to the API as jobs. Jobs are then put into a queue, where they are run in a fair, throughput-optimized way. Jobs are generally run and returned in a few minutes, but may take up to several hours depending on demand, maintenance windows, calibration, and the like. After a job has run, it is possible to query the IonQ Cloud API for the results.

So, the basic flow of running a job is:

  1. Write the quantum program
  2. Send program to the IonQ Cloud API, which creates a job
  3. Await the results of the job running to completion (or, use the job ID that is passed back to check back later for the results)
  4. Process the results

But, you don’t have to worry too much about this—the Cirq-IonQ integration takes care of most of the things that you would normally need to handle in a remote processing environment like this (for example, retrying results when the API is temporarily unavailable). All you have to do is create a program, specify IonQ as your backend, and wait for the results.

Running a Program on IonQ Hardware

Let’s walk through running a simple quantum program via the API:

Create a quantum program

First, let’s create a very simple quantum circuit for a trapped-ion quantum computer. IonQ’s 11-qubit trapped-ion system allows for two qubit gates between any of these qubits (so called all-to-all connectivity). In Cirq we represent these 11 qubits with a LineQubit object. Let’s create a quantum circuit to create an entangled pair of qubits as an example, in the process demonstrating that the gate can be between any two qubits.

# import cirq
import cirq_ionq as ionq

# In cirq_ionq <= 0.15, this helper will transpile the circuit.
qc = cirq_ionq.decompose_to_device(qc)

# In cirq_ionq >= 0.15, you can use the IonQTargetGateset and compile
# more complex custom gates!
# qc = cirq.optimize_for_target_gateset(qc, gateset=ionq_gateset.IonQTargetGateset(),

q0, q1 = cirq.LineQubit(0), cirq.LineQubit(5)
circuit = cirq.Circuit()
circuit.append([cirq.H(q0), cirq.CNOT(q0, q1), cirq.measure(q0, q1, key='x')])

Which should return:

0: ───H───@───M('x')───
          │   │
5: ───────X───M────────

Using print, we can see a visualization our little circuit that creates the entangled state (00+11)(∣00⟩+∣11⟩) and then measures it.

Set up the IonQ Cloud API Service

For this step, you’ll need an API key stored as a local environment variable as IONQ_API_KEY. Check out Managing API Keys for more information.

The basic object you will need to create to run a program is the ionq.Service object. Here we create such an object by directly feeding in the API key:

service = ionq.Service()

If you need to pass your API key to it directly (instead of calling it from the environment variable,) you can do so like this:

service = ionq.Service(api_key='your-api-key-here')

Run the job on the IonQ hardware

Now let’s run our quantum program via the IonQ Cloud API. There are two targets you can run your quantum program against. One is the IonQ cloud simulator and the other is IonQ’s 11-qubit trapped-ion system. The simulator target is called simulator, and the quantum computer is called qpu, for quantum processing unit. As new hardware gets added to the IonQ Quantum Cloud, more targets with different names will be added.

The API has a resource for retrieving a complete list of available backends.

To submit the program and await the results, run:

result = service.run(circuit=circuit, repetitions=100, target=`qpu`)

Note that this method will block waiting for the result to return. Because you have to wait for your program to get to the front of the queue to run, running a program this way can result in you waiting for a long time for the queue to clear. Below we explain a better pattern than this.

But, once the above does execute successfully, you get back a cirq.Result object, which is the basic container for results in Cirq.

# print out a histogram of the results

Which returns:

Count({3: 55, 0: 42, 1: 2, 4: 1})

Here we see that mostly the results are 0 and 3 or, in binary, 00 and 11—which is what we expect from our quantum circuit.

The asynchronous pattern for running programs

An alternative way to execute the program is to run it asynchronously. First, create a job:

job = service.create_job(circuit=circuit, repetitions=100, target='qpu')

From this job, you could just ask for the results, like so:

results = job.results()

but again, this would block until the results are done. An alternative is to get the job ID and then periodically ask for its status.


Which prints out a unique string id for the job. Once you have this ID, you can then recreate a job object at any time in the future:

same_job = service.get_job(job.job_id())

To check if the job is done running, you can query the job for its status, which is not a blocking action


If the job is completed, this will return complete, and then you can get retrieve its results:

results = same_job.results()

Additional resources

A Jupyter notebook for getting started on IonQ hardware is available here.

Full documentation for the IonQ-Cirq integration can be found on the Cirq documentation site. This site also includes numerous tutorials and documentation about writing and running quantum circuits in Cirq, many of which you’ll be able to be run on IonQ hardware with minimal modification.

One particularly useful read is the page on the IonQAPIDevice, which is a way to make sure your circuit runs with gates that are supported by the IonQ API.

Licensing for Cirq is Apache 2.0.