Hardware FixRecommendedDevice not working? Your driver may be the problemCheck updates for common hardware issues.Fix DriversHalloween DealsAmazon USHalloween week deals: useful picks before month-endAmazon US: current deals, useful picks and tech finds.Check DealsPC HealthRecommendedCrashes, freezes, slowdowns? Check your PC nowSpot repairable issues before they interrupt work.Check PC×
FREEDOM251

Freedom251 › Blog › Python

How to Simulate Quantum Error Correction with Qiskit Aer

By Freedom251 Team · · 10 min read

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Use Qiskit Aer to run the same circuit three ways: ideally, with a controlled noise model, and with a three-qubit repetition code that detects and corrects one bit flip. The useful workflow is to establish an unencoded baseline, encode a logical bit across three data qubits, measure two stabilizers with ancillas, apply a syndrome-controlled correction, and calculate the logical-error probability from a fixed number of shots.

This tutorial uses the current package combination documented by IBM Quantum: qiskit[all]~=2.5.2, qiskit-aer~=0.17.0, and, when runtime access is needed, qiskit-ibm-runtime~=0.47.0. Pin and record the versions installed in your own environment because circuit-control APIs and result formatting can change.

As an Amazon Associate I earn from qualifying purchases.

What this simulation demonstrates

A three-qubit repetition code stores a classical logical value as |000> or |111>. A single Pauli-X error on one data qubit changes one of those states, but the two parity checks reveal which qubit disagrees with the other two. A conditional X gate then restores the encoded value.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

The code is deliberately narrow. It corrects bit-flip errors only; it does not correct phase flips and is not a complete quantum-error-correction code. The memory experiment below protects classical information represented by the encoded state, not an arbitrary unknown qubit. Treat every measured improvement as conditional on the selected noise channel, gate placement, calibration snapshot, transpilation, decoder, and shot count.

Install Qiskit and Aer

Create an isolated Python environment, then install a pinned Qiskit distribution and Aer simulator:

python -m venv qec-qiskit
source qec-qiskit/bin/activate
python -m pip install --upgrade pip
python -m pip install "qiskit[all]~=2.5.2" "qiskit-aer~=0.17.0"

On Windows PowerShell, activate the environment with qec-qiskitScriptsActivate.ps1. If you need IBM Quantum Runtime in the same environment, install qiskit-ibm-runtime~=0.47.0 as documented for the current guide. Verify the actual versions rather than assuming that the requested constraints were resolved:

python -c "import qiskit, qiskit_aer; print(qiskit.__version__); print(qiskit_aer.__version__)"

Choose an Aer simulation method

AerSimulator supports several methods. Start with automatic; it chooses a representation based on the circuit and noise model. Select a method explicitly when you know what the experiment requires.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
Method Use it when Important trade-off
automatic You want Aer to select a suitable representation. Convenient, but the chosen method can change as the circuit or noise model changes.
stabilizer The circuit and errors are supported Clifford operations, including the repetition-code example. Usually scales much better for large Clifford circuits, but cannot represent arbitrary non-Clifford operations or unsupported channels.
statevector You need a pure-state representation or operations outside the stabilizer family. Memory grows exponentially with qubit count.
density_matrix Mixed states and general noise must be represented directly. More memory-intensive than a statevector and quickly expensive as qubits increase.
matrix_product_state The circuit has entanglement structure that an MPS representation can exploit. Performance depends strongly on entanglement and gate pattern.

For the code below, begin with AerSimulator(). To request a method explicitly, use AerSimulator(method="stabilizer"), then confirm that every operation and error in your circuit is supported.

Establish an ideal, unencoded baseline

First measure a single qubit prepared in |1>. With no noise, every one of 4,000 shots should be classified as a one, apart from a software or circuit-construction mistake.

from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator

shots = 4000
baseline = QuantumCircuit(1, 1)
baseline.x(0)
baseline.measure(0, 0)

ideal_sim = AerSimulator()
ideal_result = ideal_sim.run(baseline, shots=shots).result()
ideal_counts = ideal_result.get_counts()
print(ideal_counts)

The expected ideal output is a counts dictionary containing {'1': 4000}. Keep this shot count, preparation, measurement, and simulator seed policy unchanged when you compare other circuits.

Build the three-qubit repetition circuit

Logical encoding

Use data qubits data[0] through data[2]. Prepare data[0] in the chosen classical state and copy it with two CNOT gates:

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
qc.x(data[0])                 # prepare the logical value 1
qc.cx(data[0], data[1])
qc.cx(data[0], data[2])

After encoding, the logical one is represented as |111>. For a logical zero, omit the first X and the encoded state is |000>.

Measure the two stabilizers

Two ancillas measure the parities Z0 Z1 and Z1 Z2. Prepare each ancilla in |+> with H, apply CNOTs from the two data qubits into that ancilla, apply H again, and measure the ancilla. The resulting syndrome identifies a single bit flip:

Physical error Z0 Z1 result Z1 Z2 result Syndrome register value Correction
None 0 0 0 None
X on data[0] 1 0 1 X on data[0]
X on data[1] 1 1 3 X on data[1]
X on data[2] 0 1 2 X on data[2]

In this layout, syndrome bit zero stores the first parity and syndrome bit one stores the second. Qiskit treats the first bit in a classical register as the least-significant bit when a whole register is used as a condition, which produces the integer values in the table.

Complete example with a deterministic error

The following circuit inserts an X on the middle data qubit so that the syndrome and correction can be checked without relying on random noise. It uses separate syndrome and readout registers, allowing the conditional operation to test the two-bit syndrome register.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit_aer import AerSimulator

# Three data qubits, two syndrome ancillas, two syndrome bits, one logical readout bit.
data = QuantumRegister(3, "data")
anc = QuantumRegister(2, "anc")
syndrome = ClassicalRegister(2, "syndrome")
readout = ClassicalRegister(1, "readout")
qc = QuantumCircuit(data, anc, syndrome, readout)

# Prepare and encode logical |1> as |111>.
qc.x(data[0])
qc.cx(data[0], data[1])
qc.cx(data[0], data[2])

# Deterministic test error: flip the middle data qubit.
qc.x(data[1])

# Measure Z0*Z1 into syndrome[0].
qc.h(anc[0])
qc.cx(data[0], anc[0])
qc.cx(data[1], anc[0])
qc.h(anc[0])
qc.measure(anc[0], syndrome[0])

# Measure Z1*Z2 into syndrome[1].
qc.h(anc[1])
qc.cx(data[1], anc[1])
qc.cx(data[2], anc[1])
qc.h(anc[1])
qc.measure(anc[1], syndrome[1])

# Correct the qubit identified by the syndrome.
with qc.if_test((syndrome, 1)):
    qc.x(data[0])
with qc.if_test((syndrome, 3)):
    qc.x(data[1])
with qc.if_test((syndrome, 2)):
    qc.x(data[2])

# All three data qubits now agree; measure one as the decoded logical value.
qc.measure(data[0], readout[0])

sim = AerSimulator()
result = sim.run(qc, shots=4000).result()
print(result.get_counts())

The exact conditional syntax and the way multiple classical registers are printed in result keys depend on the installed Qiskit version. If your version rejects if_test, consult that version’s circuit-control API and keep the syndrome mapping unchanged. With the deterministic middle-qubit X and an ideal simulator, the syndrome should be 11, the correction should run on data[1], and the logical readout should be one in every shot.

Add a controlled noise model with Aer

Aer noise is explicit: define a channel, attach it to the operations it represents, and record those choices with your result. The example below applies a one-qubit bit-flip channel to X gates and a two-qubit depolarizing channel to CNOT gates.

from qiskit_aer.noise import NoiseModel, pauli_error, depolarizing_error

noise = NoiseModel()
bit_flip = pauli_error([("X", 0.01), ("I", 0.99)])
noise.add_all_qubit_quantum_error(bit_flip, ["x"])

# A two-qubit channel is required for a two-qubit gate.
cx_error = depolarizing_error(0.01, 2)
noise.add_all_qubit_quantum_error(cx_error, ["cx"])

noisy_sim = AerSimulator(noise_model=noise, seed_simulator=1234)
noisy_result = noisy_sim.run(qc, shots=4000).result()
print(noisy_result.get_counts())

The probabilities above are illustrative settings for a controlled experiment, not a hardware benchmark. You can also model measurement errors with Aer’s ReadoutError, and use helper channels for Pauli, depolarizing, bit-flip, and thermal-relaxation-style behavior. Attach each error to the gate or measurement that it is intended to model; adding an error to every operation without a physical rationale makes the result difficult to interpret.

Optional readout error

If the experiment is intended to include classical measurement mistakes, define a readout confusion matrix and attach it to the measured qubits. Keep readout noise separate from gate noise in your notes so you can identify which mechanism changed the logical-error rate.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Run comparable ideal and noisy experiments

Use at least three comparison arms:

  1. Unencoded: prepare and measure one physical qubit.
  2. Encoded with end correction: run the three-qubit circuit, measure the syndrome once, correct once, and then decode.
  3. Repeated correction cycles: insert idle or gate cycles, measure the syndrome repeatedly, and correct after each cycle or defer corrections according to the experiment being tested.

Use the same shot count, logical-state preparation, final measurement convention, random-seed policy, and decoder rule wherever a comparison is intended. A minimal driver for the first two arms looks like this:

shots = 4000
seed = 1234

ideal_sim = AerSimulator(seed_simulator=seed)
noisy_sim = AerSimulator(noise_model=noise, seed_simulator=seed)

# baseline is the one-qubit circuit created earlier
ideal_baseline = ideal_sim.run(baseline, shots=shots).result()
noisy_baseline = noisy_sim.run(baseline, shots=shots).result()

ideal_encoded = ideal_sim.run(qc, shots=shots).result()
noisy_encoded = noisy_sim.run(qc, shots=shots).result()

print("ideal baseline", ideal_baseline.get_counts())
print("noisy baseline", noisy_baseline.get_counts())
print("ideal encoded", ideal_encoded.get_counts())
print("noisy encoded", noisy_encoded.get_counts())

For a fair test of stochastic noise, remove the deterministic qc.x(data[1]) test error or run separate experiments. The inserted X is useful for proving that the decoder works, but leaving it in changes the experiment from a random-noise comparison to a noise-plus-known-error comparison.

Calculate logical-error probability

For an encoded logical one, a shot is a logical failure when the decoded readout is zero. For a logical zero, reverse the classification. If the readout bit is classical bit index 2 in the register construction above, Qiskit’s marginal-count helper can isolate it:

from qiskit.result import marginal_counts

def logical_error_probability(result, shots, expected_logical_bit="1"):
    marginal = marginal_counts(result, indices=[2])
    wrong = marginal.get("0" if expected_logical_bit == "1" else "1", 0)
    return wrong / shots

p_ideal = logical_error_probability(ideal_encoded, shots)
p_noisy = logical_error_probability(noisy_encoded, shots)
print("ideal logical error", p_ideal)
print("noisy logical error", p_noisy)

Check the index against your circuit and Qiskit version by inspecting result.get_counts(); multiple classical registers can be displayed with spaces and in register order that is not obvious from a printed key. If the readout bit is not index 2 in your construction, pass its actual classical-bit index.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Report shot uncertainty

A measured error rate is an estimate, not an exact probability. If k logical failures occur in N shots, the observed rate is k/N. For a rough standard error when the rate is not near zero or one, use sqrt(p(1-p)/N). For small counts, especially zero failures, report the shot count and use a binomial confidence interval rather than claiming that the true error probability is zero.

Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Support on Ko-Fi

Model repeated syndrome-and-correction cycles

A single correction at the end answers whether one encoded preparation survives the selected circuit. A memory-style experiment inserts a defined number of idle or gate cycles and measures the syndrome after each cycle. For every cycle:

  1. Apply the chosen idle, delay, or gate sequence to the data qubits.
  2. Measure Z0 Z1 and Z1 Z2 with fresh or reset ancillas.
  3. Decode the syndrome using the same mapping shown earlier.
  4. Apply the corresponding conditional X, or record the Pauli frame and defer the physical correction.
  5. Repeat for the requested cycle count.
  6. Measure one data qubit and classify the final logical value.

When comparing dynamic correction with correction deferred until final measurement, keep the preparation, measurement, number of cycles, shot count, and decoder identical. Plot logical-error probability against physical error probability or idle-cycle count. The resulting curve describes the selected simulation model; it is not a universal threshold for quantum error correction.

Use a device-informed noise model carefully

Aer can construct an approximate noise model from reported backend calibration properties, including gate-error and relaxation parameters. This is useful for emulating selected characteristics of a noisy processor, but it is not a complete replica of hardware behavior. Calibration data change over time, and the generated model does not automatically capture every correlated, crosstalk, scheduling, control, leakage, or compilation effect.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Record the backend and calibration snapshot, the gates included, the transpilation settings, the simulator method, and the decoder whenever you publish a result. A result from a device-derived model should be labeled as a simulation based on reported calibration data, not as a measurement performed on that device.

Common failures and fixes

The conditional gate is rejected

  • Confirm the installed Qiskit version and use its supported dynamic-circuit syntax. Current versions use the if_test context shown above.
  • Condition on the two-bit syndrome register, not on a register that also contains the final logical readout.
  • Make sure the syndrome measurements occur before the conditional block.

The syndrome appears reversed

  • Check which parity was measured into each classical bit.
  • Remember that a whole classical register is interpreted with its first bit as the least-significant bit.
  • Inspect a deterministic test: inject X on data[0], data[1], and data[2] separately and verify the values 1, 3, and 2.

The noise model has no visible effect

  • Confirm that the error is attached to an operation actually present in the circuit after transpilation.
  • Use enough shots to resolve the expected rate and print the noisy counts.
  • Do not assume that adding a one-qubit error to a two-qubit gate is valid; use a two-qubit channel such as depolarizing_error(probability, 2).
  • If you expect measurement failures, add a readout channel explicitly; gate noise alone does not create readout errors.

The logical result is worse after correction

  • Include the extra ancilla gates, measurements, and conditional operations in the noise model. Syndrome extraction can introduce more opportunities for failure.
  • Separate the deterministic-error validation run from the stochastic-noise run.
  • Compare against an unencoded circuit with equivalent preparation and final measurement, and report all circuit and noise-model choices.

Simulation runs out of memory

  • Try method="stabilizer" for supported Clifford circuits.
  • Use statevector or density_matrix only when the circuit or channel requires it.
  • Reduce qubit count, shots, or cycle count while debugging, then restore the planned experiment.

What a defensible result includes

  • Installed Qiskit and Aer versions.
  • Simulator method and whether it was selected automatically.
  • Logical-state preparation and the exact encoding and decoder.
  • Noise channels, probabilities, and the gates or measurements to which they were attached.
  • Whether a deterministic error was inserted.
  • Shot count, random-seed policy, and logical-error definition.
  • For device-informed models, the backend calibration snapshot and transpilation choices.
  • Uncertainty information or, at minimum, the number of failures and total shots.

With those details, Aer provides a reproducible way to test how a repetition-code decoder behaves under a specified model. Without them, an apparent improvement can reflect a changed circuit, measurement convention, or noise placement rather than error correction itself.

Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

PythonQiskitQiskit AerQuantum Computingquantum error correctionquantum simulation

Leave a Reply

Your email address will not be published. Required fields are marked *

What’s actually slowing this PC down?

Pick the symptom - the matching free tool is one click away.

Special offer. See more information about Outbyte and uninstall instructions. Please review EULA and Privacy policy.

Recommended PC Tool
Recommended PC Tool
Windows Errors? Fix Them Before They SpreadFree repair scan
Outdated Drivers Are Slowing You DownFree scan - exact matches

Two free Windows tools

One Free Minute Could Fix That PC

Before you go - each of these free tools takes about a minute and tackles what quietly slows a Windows PC down.

Special offer. View Outbyte info, uninstall instructions, EULA, and Privacy Policy.