Skip to main contentIBM Quantum Documentation Preview
This is a preview build of IBM Quantum® documentation. Refer to quantum.cloud.ibm.com/docs for the official documentation.

Learn the noise model of a gate

This tutorial demonstrates learning a noise model for a unitary gate with a standard learning protocol accessible through the high-level protocol functions prepare_vanilla_program() and process_vanilla_result().

  1. Define the gate
  2. Set up local simulation
  3. Prepare the quantum program
  4. Run the quantum program
  5. Read the results
Running on real hardware

The circuits below are simulated locally, so this walkthrough needs no IBM Quantum® credentials. To alternatively run it on a real device:

  • Step 1: replace FakeMarrakesh with a real backend.
  • Step 2: pass that backend as the executor’s mode, and drop the simulator options.

1. Define the gate

The gate is a BoxOp holding a twirled layer of six disjoint CZ gates.

from qiskit.circuit import QuantumCircuit
from qiskit_ibm_runtime.fake_provider import FakeMarrakesh
from samplomatic import InjectNoise, Tag, Twirl

backend = FakeMarrakesh()

cz_pairs = [(91, 92), (93, 94), (95, 99), (98, 111), (112, 113), (114, 115)]

circuit = QuantumCircuit(backend.num_qubits)
with circuit.box([Twirl(), Tag("cz_gate"), InjectNoise("cz_gate")]):
    for pair in cz_pairs:
        circuit.cz(*pair)
Running on real hardware
from qiskit_ibm_runtime import QiskitRuntimeService

backend = QiskitRuntimeService().backend("ibm_marrakesh")

2. Set up local simulation

The Pauli indices inside each map are local to the gate, in ascending physical-qubit order:

cz_qubits = sorted({qubit for pair in cz_pairs for qubit in pair})
local = {qubit: index for index, qubit in enumerate(cz_qubits)}
local
{91: 0,
 92: 1,
 93: 2,
 94: 3,
 95: 4,
 98: 5,
 99: 6,
 111: 7,
 112: 8,
 113: 9,
 114: 10,
 115: 11}

Define the noise with a correlated ZZ term and a weaker XX term on each CZ pair, plus a single-qubit Z term everywhere; measurement gets a bit-flip term per qubit.

from qiskit.quantum_info import PauliLindbladMap

num_qubits = len(cz_qubits)

cz_noise = PauliLindbladMap.from_sparse_list(
    [("ZZ", [local[a], local[b]], 8e-4) for a, b in cz_pairs]
    + [("XX", [local[a], local[b]], 4e-4) for a, b in cz_pairs]
    + [("Z", [index], 3e-4) for index in range(num_qubits)],
    num_qubits=num_qubits,
)

readout_noise = PauliLindbladMap.from_sparse_list(
    [("X", [index], 5e-3) for index in range(num_qubits)], num_qubits=num_qubits
)

Build a box for measurement to learn.

measurement = QuantumCircuit(backend.num_qubits, num_qubits)
with measurement.box([Twirl(), Tag("readout"), InjectNoise("readout")]):
    for clbit, qubit in enumerate(cz_qubits):
        measurement.measure(qubit, clbit)

Each entry of layer_noise_model pairs one of those boxes with the map to apply to it. Use the stabilizer simulation method, and set seed_simulator to make the simulated data reproducible.

from qiskit_aer import AerSimulator
from qiskit_ibm_runtime import Executor

executor = Executor(
    mode=AerSimulator(method="stabilizer"),
    options={
        "simulator": {
            "layer_noise_model": [
                (circuit[0], cz_noise),
                (measurement[0], readout_noise),
            ],
            "seed_simulator": 1234,
        }
    },
)
Running on real hardware
executor = Executor(mode=backend)

3. Prepare the quantum program

Use prepare_vanilla_program() to build the quantum program containing the learning experiments.

from qiskit_noise_learning.protocols import prepare_vanilla_program

program = prepare_vanilla_program(
    backend,
    [circuit[0], measurement[0]],
    fragment_depths=[2, 16, 64, 128],
    num_randomizations=50,
    shots_per_randomization=20,
)

print(f"Number of template circuits: {len(program.items)}")
Number of template circuits: 5

4. Run the quantum program

Submit the quantum program. This step is the same whether the executor is pointed at a simulator or at a real device.

result = executor.run(program).result()

5. Read the results

Process the result with process_vanilla_result().

from qiskit_noise_learning.protocols import process_vanilla_result

noise_maps, gate_names, fit = process_vanilla_result(result)

Plot per-qubit-pair fidelity decays: both the data and the exponential fit.

fit.plot_qubit_pair_decays(
    pairs=cz_pairs,
    observable_type="means",
    exponential_fit=True,
)