Programming your first quantum circuitsLesson 6 of 6

Noise, and the real machine

The same Bell pair on four IonQ noise models, what a real quantum computer changes, and how to give your circuit a public home.

The Playground's Circuit panel: the Bell pair, an H gate on q0 and a CNOT from q0 to q1, then a measurement on each qubit.

Some tallies in this course carried a small tail: a stray 0 under an X gate, a few 01s and 10s from a pair that should always agree. Each time the lesson named it the noise model and moved on. This one stops and measures it.

The measuring instrument is the Bell pair from lesson 3 at four times the shots, so a one-percent tail shows up as about forty shots rather than ten. You will run it on the models of four IonQ machines, compare them, and then look at where the real machines sit in the same dialog.

What a noise model is

The simulators under Remotely Run Simulators are not four copies of the same thing. Each is IonQ's simulator with a noise_model option set to one of IonQ's machines, which the Playground passes along when you pick one.

IonQ describes these as error models representative of the named system and does not publish what is inside one, so this lesson treats each model as a box and measures its effect.

The circuit is the same on every model here, so running it on several and counting compares the models.

disagree

The tail, counted

The ideal Bell tally is 00 and 11 only, so every 01 or 10 is a wrong answer. The listing adds them up as disagree and prints the share. That is one measure of the noise, the errors that made the pair disagree; an error in a phase does not show in it.

The circuit, then the code

The Bell pair is unchanged. Three things are new around it: 4,000 shots, a line that prints which machine the run imitates, and the two counting lines at the end.

noise.py Python · PlaygroundOpen in Playground ↗
# 'backend' already exists when this runs: it is whatever you pick in the Run Experiment dialog.
from qiskit import QuantumCircuit
from qiskit.providers.jobstatus import JobStatus
import time
1shots = 4000
2print("backend:", backend.name, "| noise model:", backend.options.get("noise_model", "none"))
qc = QuantumCircuit(2, 2)
qc.h(0)
3qc.cx(0, 1)
qc.measure([0, 1], [0, 1])
job = backend.run(qc, shots=shots)
while job.status() not in (JobStatus.DONE, JobStatus.ERROR, JobStatus.CANCELLED):
    time.sleep(5)
counts = job.result().get_counts()
print(counts)
4agree = counts.get("00", 0) + counts.get("11", 0)
5disagree = shots - agree
print(f"agree:    {agree:5d}  ({100 * agree / shots:5.2f}%)")
print(f"disagree: {disagree:5d}  ({100 * disagree / shots:5.2f}%)")
  1. 1more shots than before, so a small tail is measured rather than guessed
  2. 2which backend ran it, and which machine the model imitates
  3. 3the Bell pair from lesson 3: the two qubits should always agree
  4. 4the pair's signal
  5. 5the tail: every shot where the pair disagreed

Run it on two models

Open the project, press Run on Qollab, choose IonQ Aria 1, and press Run. Before the second press, write down a guess: will Forte 1's tail be bigger or smaller than Aria 1's? Nothing so far says which; the guess is there to be checked.

Then press Run on Qollab again and choose IonQ Forte 1. Same code, different model; the first printed line tells you which.

The run console: backend ionq_simulator with noise model aria-1, the counts dict, and the lines agree 3962 (99.05%) and disagree 38 (0.95%)
The Aria 1 model's answer for the Bell pair: 38 disagreeing shots in 4,000. The first line names the model; the counts under it change with every press, model or no model.

Our two presses on 15 September 2026:

noise modelcountsdisagree
aria-1{'00': 2033, '01': 25, '10': 13, '11': 1929}38 (0.95%)
forte-1{'00': 1922, '01': 14, '10': 24, '11': 2040}38 (0.95%)

The same number twice. Before reading anything into that, work out how much one press can wander. A tail of about 1% counted over 4,000 shots has a scatter of about 6 shots, so nineteen presses in twenty land within 13 shots of the true value.

Two presses on the same model can therefore differ by close to 20 shots before anything real has changed.

Assignment: complete the table

  1. Run the listing on IonQ Aria 2 and copy the console's disagree line; the first line of each console names the model.
  2. Run it on IonQ Forte Enterprise 1 and copy that disagree line too, before the next press replaces it. You now have all four models from your own presses.
  3. Compare your four numbers against the 20-shot bar above: two numbers 8 apart are the same number, two numbers 40 apart are not.
  4. Write one sentence saying whether any two models differ.
Solution

Our four presses gave 38, 38, 36 and 44 shots. Aria 2 came back {'00': 2018, '01': 19, '10': 17, '11': 1946} and Forte Enterprise 1 {'00': 2007, '01': 22, '10': 22, '11': 1949}.

The largest gap is 8, well under 20, so no pair of models differs by more than a fresh press of the same model would.

On this two-gate circuit, then, one press per model cannot tell the four models apart: each put the pair wrong about once in a hundred shots. Whatever separates an Aria from a Forte does not show in this measurement.

Your four numbers will differ from ours and, by the 20-shot bar, will most likely say the same thing.

Does the model charge for depth?

On a real machine every gate is a physical operation with its own small chance of going wrong, so a deeper circuit, more gates executed one after another, is expected to come back with a bigger tail. That is a reason algorithms are counted in gates.

Whether the model does the same is a question you can try to put to it directly: make the circuit deeper without changing what it computes, and watch the tail. One catch to keep in mind: IonQ compiles what you submit before it runs, and gates that cancel can be removed on the way.

Assignment: pad the circuit and watch the tail

  1. Fork the project, as in lesson 1.
  2. In your fork, add qc.cx(0, 1) twice, directly under the existing one, so the circuit runs three CNOTs where the last two undo each other. The ideal tally does not change: two CNOTs in a row are the identity.
  3. Write down what you expect from a model that charges per gate, and from one that does not.
  4. Run on the same model as your plain-circuit press, and compare the two disagree lines against the 20-shot bar before you call a difference real.
Solution
qc.h(0)
qc.cx(0, 1)
qc.cx(0, 1)
qc.cx(0, 1)
qc.measure([0, 1], [0, 1])

On our presses the padded circuit's tail was 32 shots against 38 for the plain one: no growth beyond the scatter.

The likely reason is that the padding never ran. IonQ compiles what you submit, and removing gates that cancel is part of that, so two CNOTs in a row can be gone before the model sees them. Every CNOT you wrote is submitted; the console does not say what the compiler kept.

So this experiment cannot tell a model that does not charge for depth from extra gates that were compiled away. A circuit that is deeper in the editor is not always deeper on the machine, and the tally alone cannot say which.

Where the real machines sit

Scroll to the bottom of the Run Experiment dialog and the last group is Quantum Computer: the physical IonQ machines, in the same list as their models.

The bottom of the Run Experiment dialog: the last free simulator, then the Quantum Computer group with IonQ Forte 1 marked Offline and IonQ Forte Enterprise 1 marked Credits apply and highlighted, and the Run button
The models above the line are free. The machines below it are queued behind other people's jobs and cost credits, shown before you confirm. On the day we wrote this, Forte 1 was offline and Forte Enterprise 1 was taking jobs.

The code does not change at all. The backend global from lesson 1 is what allows that: the same listing runs on a local simulator, on a machine's model, or on the machine.

On the machine the first line names a different backend, ionq_qpu.forte-1 in place of ionq_simulator, and the model field shows its default, ideal, because on the machine there is no model to set. The tail is then the machine's own, made of every gate that actually ran.

Nothing in this course needs a paid run. When you want one, Course 2 walks through the account, the estimate and the queue, and every option in the dialog is listed with its qubit count and cost under compute backends.

Give your work a public home

Seven circuits, each one you have run, changed and run again. The last step is to make one of them yours in public: a public project on Qollab is runnable by anyone, forkable by anyone, and carries a page that says what it does and how to run it.

The procedure is short. In your fork, edit the circuit into something of your own. Under the Project Card tab, write what it does and how to run it, and say which licence it carries; each course project has a line reading MIT license. Fork it, change it, keep it.

Then press Publish Project in the header: one click, no dialog. The Draft label clears, the project is public at qollab.xyz/u/<your username>/<your project>, and this first release is tagged v1.0.

How later versions work is in the docs, and so is the project page, which is the part people read before they press Run.

Assignment: publish a circuit of your own

  1. Take any circuit from this course and change it into something that is yours. The smallest honest change is a real one: a Bell pair with a rotation on one qubit before the CNOT, a search that marks a different answer, an X gate on the qubit nobody measured.
  2. On the project page, say what you changed and what the tally shows because of it.
  3. Publish it by the procedure above, then open the URL in a private window.
Solution

There is no single answer here. The check is the URL: qollab.xyz/u/<your username>/<your project>, which opens in a private window where you are not signed in. If the page also lets a stranger predict the tally before they press Run, it is a good project page.

That is Programming your first quantum circuits, start to finish.

Stay in the loop.

Get the latest tutorials, demos, and project showcases straight to your inbox. No noise, just the good stuff.