What is Qiskit?

Qiskit is an open-source Python SDK for working with quantum computers at a variety of levels—from the “metal” itself, to pulses, gates, circuits and higher-order application areas like quantum machine learning and quantum chemistry. It has “Providers” that enable support for different vendors and the various “backends” (read: quantum computers or simulators) that they offer. IonQ maintains an IonQ Provider for Qiskit that allows you to work with our trapped-ion systems and our high-performance cloud simulator, which we’ll install and use here.

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 running locally on your machine.
Run python --version from your command line if you aren’t sure which version you have running.

Set up Qiskit

First, we’ll install Qiskit and the IonQ Provider from PyPI using pip:
pip install qiskit qiskit-ionq
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.

Package versions and compatibility

You can install specific versions of qiskit and/or qiskit-ionq via:
pip install qiskit==1.3 qiskit-ionq
Or constrain the version via:
pip install qiskit-ionq<1.0
Since qiskit-ionq requires Qiskit, installing qiskit-ionq alone in an environment without Qiskit will automatically install the latest Qiskit version that’s compatible with your qiskit-ionq version. Additionally, installing Qiskit first and then installing an unspecified version of qiskit-ionq should automatically select the latest version of qiskit-ionq that’s compatible with your version of Qiskit. These are some of the most notable differences in support and compatibility for different package versions of Qiskit and the IonQ Provider for Qiskit. For more information, refer to the qiskit-ionq release notes on GitHub or reach out to [email protected]. Qiskit 2.0 support: The IonQ Provider (qiskit-ionq package) versions 1.0 and above are compatible with Qiskit v2.0. If you’re using Qiskit 1.x or 0.x, you may need to install an older version of the IonQ Provider. If you’re using Qiskit 2.x, you will need a more recent version of the IonQ Provider. IonQ API versions: The IonQ Provider versions 0.6 and above use the IonQ Quantum Cloud API v0.4, while prior versions use our API v0.3. For the most part, the IonQ Provider should handle the API changes automatically and support the same functionality across both API versions, but there may be differences in behavior or functionality in some cases. Qiskit default transpilation settings: Qiskit versions 1.3 and above also use a default optimization level of 2 for the Qiskit transpiler. If you’re using Qiskit’s transpile() function with these versions of Qiskit (specifically to convert to IonQ’s supported QIS gates before submission), we recommend passing optimization_level=1 or optimization_level=0 to avoid transpilation passes that can result in a net reduction in efficiency when combined with IonQ’s compiler. You may see an IonQTranspileLevelWarning when using the IonQ Provider with these Qiskit versions, but no action is needed unless you are using Qiskit’s transpiler to convert to IonQ’s accepted QIS gateset. You may also set the default optimization level for your local installation in Qiskit’s user configuration file. By default, this file is located at ~/.qiskit/settings.conf (if it does not already exist, you can also create a file named settings.conf at this location). To specify or change the default optimization level used for Qiskit transpilation, the file should contain at least:
[default]
transpile_optimization_level = 1
Setting this value to 0 or 1 will use that value in calls to qiskit.transpile() unless a different value is specified, as in qiskit.transpile(circuit, optimization_level=3). Circuits submitted to IonQ backends will still be optimized for IonQ systems when they pass through IonQ’s compiler. Specifying this setting in your local user configuration file will also suppress the IonQTranspileLevelWarning from the IonQ Provider.

Set up your environment

By default, Qiskit will look in your local environment for a variable named IONQ_API_KEY, so if you’ve already followed our guide on setting up and managing your API keys, Qiskit will automatically find it. Alternatively, you can set an environment variable temporarily from your command line, by running:
export IONQ_API_KEY="your_api_key_here"
While we recommend setting an environment variable so that Qiskit can find your API key, you can also pass in your API key explicitly within your Python code, when creating the IonQ Provider object that authenticates your connection to the IonQ Cloud Platform. This might be necessary if you’ve named your environment variable something other than IONQ_API_KEY, or if you are working from a Python environment where accessing environment variables is not straightforward. You can import your key explicitly or load it from a file, and pass it into the IonQProvider() object directly:
import os
from qiskit_ionq import IonQProvider

# Load your API key from an environment variable named MY_IONQ_API_KEY
my_api_key = os.getenv("MY_IONQ_API_KEY")
provider = IonQProvider(my_api_key)
In the examples below, we show IonQProvider() initialized with no arguments and assume that Qiskit will automatically find your API key, but you can always use this approach instead.

Start a script

For this exercise, we’ll create a Python file and run it as a script. If you’re comfortable and familiar with Python, you can approach this any number of ways—our getting-started repository includes Jupyter notebooks that can be downloaded or run in Google Colab. Open a file up in whatever IDE you prefer, and add the following:
from qiskit_ionq import IonQProvider
provider = IonQProvider()

print(provider.backends())
Running this script should print the results below—something like this:
[<IonQSimulatorBackend('ionq_simulator')>, <IonQQPUBackend('ionq_qpu')>]
If this runs correctly then Qiskit and the IonQ Provider were installed successfully. Note that this command does not currently check your API key credentials or permissions for specific backends. Because certain backend information is built into qiskit-ionq and prepopulated without actually connecting to IonQ systems, some provider and backend setup steps will work even if the provider does not have a valid API key.

Submit a circuit to the simulator

For all circuits run on IonQ backends, make sure to explicitly include measurement in your circuit definition via .measure() or measure_all(). Without measurement, you may get unexpected or incorrect result values.

Running a simple Bell state circuit

First, let’s try running a simple Bell state circuit on the ideal quantum simulator. Try running this script:
from qiskit import QuantumCircuit
from qiskit_ionq import IonQProvider

provider = IonQProvider()
simulator_backend = provider.get_backend("simulator")

# Create a basic Bell State circuit:
qc = QuantumCircuit(2, name="IonQ Qiskit guide - simulator example")
qc.h(0)
qc.cx(0, 1)
qc.measure_all()

# Run the circuit on IonQ's platform:
job = simulator_backend.run(qc, shots=10000)

# Print the counts
print(job.get_counts())
When you run it, you should see something like:
{'00': 4984, '11': 5016}
The ideal simulator is simulating the circuit we defined, calculating the probabilities for each quantum state, and sampling from those probabilities 10,000 times. In this case, the circuit evaluated to a “00” state 5,016 times, and a “11” state 4,984 times.
While the ideal simulator creates a quantum state with a 50-50 probability of being measured as “00” or “11”, the .get_counts() method samples from this probability distribution, so we didn’t end up with exactly 5,000 counts for each state. You can use job.get_probabilities() to see the calculated probabilities for a circuit that was run on the simulator.

Submitting multiple circuits in a single job

To submit multiple circuits in a single job submission, pass all of the circuits to the run() function in a list instead:
from qiskit import QuantumCircuit
from qiskit_ionq import IonQProvider

provider = IonQProvider()
simulator_backend = provider.get_backend("simulator")

# Define two quantum circuits
qc1 = QuantumCircuit(2, name="IonQ Qiskit guide - Bell state")
qc1.h(0)
qc1.cx(0, 1)
qc1.measure_all()

qc2 = QuantumCircuit(3, name="IonQ Qiskit guide - GHZ state")
qc2.h(0)
qc2.cx(0, 1)
qc2.cx(0, 2)
qc2.measure_all()

# Submit both circuits as a single job
job = simulator_backend.run([qc1, qc2])

# Print the results
print(job.get_counts())

# Or a specific job
print(job.get_counts(qc1))
This script submits two quantum circuits in a single job: a Bell state circuit and a GHZ state circuit. When the job completes, it prints the counts for each circuit:
[{'00': 519, '11': 505}, {'000': 505, '111': 519}]
{'00': 519, '11': 505}

Submit a circuit to the noisy simulator

To run the circuit (or circuits) using the simulator with a noise model, set the backend noise_model option like: noise_model="aria-1". The available noise models are harmony (legacy), aria-1, aria-2, and forte-1. You can read more about these noise models here.
from qiskit import QuantumCircuit
from qiskit_ionq import IonQProvider

provider = IonQProvider()
simulator_backend = provider.get_backend("simulator")
simulator_backend.set_options(noise_model="aria-1")

# Create a basic Bell State circuit:
qc = QuantumCircuit(2, name="IonQ Qiskit guide - noisy sim example")
qc.h(0)
qc.cx(0, 1)
qc.measure_all()

# Run the circuit on IonQ's platform:
job = simulator_backend.run(qc, shots=10000)

# Print the counts
print(job.get_counts())
When this simulation includes the effects of noise, we would expect to see results that are similar to the ideal simulation shown above, but with a few instances of measuring the “01” and “10” states. For example:
{'00': 4919, '01': 37, '10': 33, '11': 5011}

Submit a circuit to a QPU

To run the same circuit on IonQ’s quantum hardware (QPU), we need to define a different backend at the beginning of the script and submit the circuit to that backend. Available QPU backend options may include ionq_qpu.aria-1, ionq_qpu.aria-2, ionq_qpu.forte-1, or ionq_qpu.forte-enterprise-1. You can view which of these systems you can access in the /v0.3/backends resource in the API and 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.
# Set up the Aria-1 QPU backend:
qpu_backend = provider.get_backend("qpu.aria-1")

# Create a basic Bell State circuit:
qc = QuantumCircuit(2, name="IonQ Qiskit guide - Aria 1 example")
qc.h(0)
qc.cx(0, 1)
qc.measure_all()

# Run the circuit on IonQ's platform:
job = qpu_backend.run(qc, shots=10000)

# Print the job ID
print(job.job_id())
When submitting jobs to a QPU, your job may need to wait in the queue, so you probably won’t get the results right away. Next, we’ll show how to check a previously submitted job’s status and retrieve its results.

Viewing job status and results

On the “My Jobs” tab in the IonQ Quantum Cloud application, you can always view the status of all of your jobs, and you can view and download their results. "My Jobs" in the IonQ Cloud Console You can also get the job status and results within Qiskit. You’ll need the job ID, which you can save after submitting a job (as in the QPU example above) or copy from the “ID” column in the “My Jobs” tab. Then, you’ll need to set up a backend of the same type that was used to submit the job (in this case, we’ll set up an Aria 1 backend).
from qiskit_ionq import IonQProvider

# Set up the IonQ provider and backend
provider = IonQProvider()
backend = provider.get_backend("qpu.aria-1")

# Specify a job ID
job_id = "..."

# Retrieve the job
job = backend.retrieve_job(job_id)

# Print the job status
print(job.status())

# Print the job result
print(job.get_counts())

The behavior of job.get_counts() is different depending on the type of backend (ideal simulator, or noisy simulator or QPU) used to retrieve the job. We recommend always retrieving a job with the same backend type and settings that were used to run the job. In particular, a job retrieved using an ideal simulator backend will get counts by sampling the stored distribution rather than reproducibly returning the counts that were actually recorded.
Once you’ve retrieved a job, you can use the same methods as in the above examples to print counts, probabilities, and other information.

Troubleshooting

If you encounter an IonQCredentialsError, it’s likely that your IonQProvider did not find anything to use as an API key. You can run provider.credentials() to print the API key (token) associated with an IonQProvider object; if the returned token is missing, this indicates that the provider did not successfully find your key. You will need to either set up an environment variable named IONQ_API_KEY or pass the key directly into the provider as shown above. If you encounter an IonQAPIError mentioning “Insufficient scope” in the error message, it’s likely that your IonQProvider found something to use as a credential, but it’s not a valid API key. You may need to generate a new API key, or change your environment variable or code setup to ensure that the provider finds a valid key. If you encounter an IonQAPIError with a message starting with “Unable to run jobs on IonQ QPU”, your organization or project does not have permissions for this QPU backend. If you see a backend that is not listed as “Out of Plan” at cloud.ionq.com/backends, your organization owner has the option to enable that backend for your project in the IonQ Cloud Console. For backends that are listed as “Out of Plan”, your organization owner may request access from the IonQ team at [email protected]. If you encounter an IonQAPIError with message “Not found” and error code 404 when retrieving a job, you may have tried to access a job that does not exist, or a job in a project that is not accessible to the API key being used. Check the input job ID, and confirm that the backend was set up using an IonQProvider whose API key is linked to the project where the job was run. Please direct additional questions, support requests, and bug reports to [email protected] or the IonQ Community Slack!

Learning more

Great work! You successfully ran your first quantum circuits—now what? For additional resources on using Qiskit, we recommend their getting started page and learning resources. For more detailed information on Qiskit, we recommend the Qiskit documentation. For advanced features on IonQ systems with Qiskit, refer to our guides on using native gates and error mitigation with debiasing and sharpening. For examples using different SDKs, more complex circuits, and in other languages, check out our IonQ Samples library on GitHub. Finally (and maybe most importantly), you can also request access to IonQ Quantum Computers here.