What is Cirq?

Cirq is an open source Python framework for writing, modifying, and running programs for quantum computers. 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.

The cirq-ionq module provides support for IonQ’s trapped-ion systems. This means that you can write quantum circuits and run them on IonQ’s simulators and trapped-ion quantum computers, all from within the Cirq framework.

Before you begin

You’ll need an account on the IonQ Quantum Cloud, and you’ll need to create an API key. We also have a guide about setting up and managing your API keys if you need some help.

You’ll also need Python 3.11 or higher running locally on your machine.

Run python --version from your command line if you aren’t sure which version you have running.


Set up Cirq

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

pip install cirq-ionq

This will also install the core functionality of cirq. For more details and advanced features, we recommend following the complete Cirq installation guide.

Note: We encourage doing this inside an environment management system like virtualenv or conda so as to avoid this fate, but do what makes the most sense for you.


Set up the IonQ Service

We’ll walk through each step of the code examples for different job submissions here, but full, copy-paste-friendly code examples are provided below.

Open a file up in whatever IDE or notebook you prefer, and add the following:

import cirq_ionq
service = cirq_ionq.Service()

Cirq will automatically look for your API key in the environment variable IONQ_API_KEY when setting up the cirq_ionq.Service(). If you need to pass in your API key directly, you can instead use:

service = cirq_ionq.Service(api_key="YOUR API KEY HERE")

Build a circuit

Next, construct a circuit. Each qubit is represented by a cirq.LineQubit with a unique integer identifier. While we use LineQubit objects to represent the chain of trapped-ion qubits in an IonQ system, the qubit identifier does not indicate the position of a particular qubit in the chain. Also note that all IonQ systems have all-to-all connectivity, so two-qubit gates can be performed directly on any pair of qubits, whether or not their LineQubit identifiers are adjacent.

import cirq

q0, q1 = cirq.LineQubit.range(2)
circuit = cirq.Circuit(
    cirq.H(q0),
    cirq.CNOT(q0, q1),
    cirq.measure(q0, q1, key='x') 
)

print(circuit)

Here’s a basic Bell state circuit:

0: ---H---@---M('x')---
          |   |
1: -------X---M--------

Submit a circuit to the ideal simulator

After we’ve built a quantum circuit, we can submit it to IonQ’s ideal simulator. Here, we’ll use repetitions to request 1000 shots and name to give the circuit a name that will show up in the IonQ Cloud Console.

result = service.run(
    circuit=circuit,
    target="simulator",
    repetitions=1000,
    name="Hello simulator, Cirq"
)

print(result.histogram(key='x'))

This returns:

Counter({0: 502, 3: 498})

The cirq.Result object shows 502 counts for the 0 state (00) and 498 for the 3 state (11).

Submit a circuit to the noisy simulator

This process is almost the same as the ideal simulator example above, except when calling service.run(), we’ll use the extra_query_params to add {"noise": {"model": "aria-1"}} for the Aria 1 noise model.

The available noise models are harmony (legacy), aria-1, aria-2, and forte-1. You can read more about these noise models here.

result = service.run(
    circuit=circuit,
    target="simulator",
    repetitions=1000,
    extra_query_params={"noise": {"model": "aria-1"}}
)

Or, to supply both a circuit name and a noise model:

result = service.run(
    circuit=circuit,
    target="simulator",
    repetitions=1000,
    name="Hello noisy simulator, Cirq",
    extra_query_params={"noise": {"model": "aria-1"}}
)

Submit a circuit to a QPU

For the QPU, we’ll use service.create_job() instead of service.run(). This will submit the job, but won’t wait for it to finish, so our code won’t be blocked waiting for the result to return. In most cases, a QPU job will wait in the queue for some time before running.

The available QPU targets may include qpu.aria-1, qpu.aria-2, and qpu.forte-1. You can view your access to these systems on the “Backends” tab of the IonQ Cloud Console.

Before submitting to any QPU, we recommend testing your code on a simulator (including with noise model) and following the other steps on this list to confirm your access and the QPU availability.
job = service.create_job(
    circuit=circuit,
    target="qpu.aria-1",
    repetitions=1000,
    name="Hello QPU, Cirq"
)

You can check the status of your job:

print(job.status())

If the job is waiting in the queue, this will print 'ready'. If the job is finished, this will print 'completed'.

You can also print and record the job’s unique ID, which can be used to retrieve the job (including its status and results) at a later time.

print(job.job_id())

Once the job is completed, you can retrieve its results:

result = job.results().to_cirq_result()
print(result.histogram(key='x'))

You can cancel a job while it’s waiting in the queue:

job.cancel()

Retrieve a job

You can retrieve results for a previously run job using its job ID. You can save the job ID after submitting a job (as in the QPU example above) or copy it from the “ID” column in the “My Jobs” tab on the IonQ Cloud Console.

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

result = job.results().to_cirq_result()
print(result.histogram(key='x'))

Supported gates

Note that some gates supported by Cirq aren’t accepted by IonQ backends. The supported gates include:

  • cirq.XPowGate, cirq.YPowGate, cirq.ZPowGate
    • This includes cirq.rx, cirq.ry, and cirq.rz and Pauli gates cirq.X, cirq.Y, and cirq.Z.
  • cirq.H
  • cirq.XXPowGate, cirq.YYPowGate, cirq.ZZPowGate
  • cirq.CNOT, cirq.SWAP
  • cirq.MeasurementGate: usually via cirq.measure

Other gates available in Cirq, such as the Toffoli gate, are not directly supported by IonQ systems. However, you can decompose a circuit to IonQ-supported gates.

Construct a circuit containing an unsupported gate:

q0, q1, q2 = cirq.LineQubit.range(3)
circuit = cirq.Circuit(
    cirq.TOFFOLI(q0, q1, q2),
    cirq.measure(q0, q1, q2, key='x') 
)

print(circuit)

This circuit looks like:

0: ---H---@---@---M('x')---
          |   |   |
1: -------X---@---M--------
              |   |
2: -----------X---M--------

Get the IonQ target gateset and decompose the unsupported gates in the circuit:

target_gateset = cirq_ionq.ionq_gateset.IonQTargetGateset()
circuit2 = cirq.optimize_for_target_gateset(
    circuit,
    gateset=target_gateset
    )

print(circuit2)

The resulting circuit has more gates, but all of these gates can be submitted to an IonQ backend:

0: ---H---@------------------@------------------@---@---T------@---M('x')---
          |                  |                  |   |          |   |
1: -------X-------@----------+-------@---T------+---X---T^-1---X---M--------
                  |          |       |          |                  |
2: -----------H---X---T^-1---X---T---X---T^-1---X---T---H----------M--------

From here, we can run circuit2 using service.run() or service.create_job() as shown in the previous examples.

Note that prior to cirq-ionq v0.15, this step was performed using cirq_ionq.decompose_to_device(qc) instead.

IonQ’s native gates (GPi, GPi2, and MS) are also supported in Cirq. See our native gates introduction and guide to using native gates in Cirq for more details and examples.


Full code examples

Here’s full, copy-paste-friendly code examples for building a circuit and submitting it to the simulator, noisy simulator, or QPU.


Additional resources

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

Full documentation for the Cirq-IonQ 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.

The IonQ hardware section of Cirq’s documentation includes additional topics and resources specific to Cirq-IonQ.

You can find the source code for the cirq-ionq package on GitHub.

Licensing for Cirq is Apache 2.0.