Qiskit-aer
High-performance quantum circuit simulator for Qiskit
Quick install
pip install qiskit-aer Background and History
Qiskit Aer was developed by IBM Research as the simulation component of the Qiskit ecosystem. It originated as one of Qiskit’s four original pillars: Terra (core circuits), Aer (simulation), Ignis (error mitigation), and Aqua (algorithms). The name “Aer” comes from the Latin word for air, continuing the elemental naming theme of the original Qiskit components. Aer was first released alongside the broader Qiskit framework in 2017, with the goal of providing a high-performance local simulator that could faithfully reproduce the behavior of IBM’s quantum hardware, including realistic noise models.
The early versions of Aer focused on statevector and QASM (shot-based) simulation. Over time, the simulator expanded to include multiple simulation methods: density matrix simulation for mixed states and noise, stabilizer simulation for efficient Clifford circuits, matrix product state (MPS) simulation for circuits with limited entanglement, and unitary matrix simulation for computing full circuit unitaries. The NoiseModel infrastructure, which allows users to construct custom noise models or import calibration data directly from real IBM Quantum devices, became one of Aer’s most valued features.
When IBM reorganized the Qiskit ecosystem around 2021 and 2022, deprecating the Ignis and Aqua pillars, Aer was separated into its own standalone package (qiskit-aer) with its own release cycle. This allowed Aer to evolve independently from the core Qiskit SDK. GPU acceleration through NVIDIA’s cuStateVec library (part of the cuQuantum SDK) was added to support large-scale statevector simulations that exceed CPU memory and speed constraints. Multi-GPU simulation was also made available for circuits requiring distributed state storage.
As of 2025, Qiskit Aer remains the standard local simulator for Qiskit users and is actively maintained. The FakeBackend system (now part of qiskit-ibm-runtime) uses Aer under the hood to provide realistic simulations of specific IBM Quantum processors, complete with calibrated noise models and coupling maps. Aer is widely used in quantum computing education, algorithm development, and noise-aware benchmarking, serving as the testing ground for circuits before they are submitted to real IBM Quantum hardware.
Qiskit Aer is the primary local simulator backend for Qiskit circuits. It ships several simulation methods under one unified AerSimulator interface: statevector (exact amplitudes), density matrix (mixed states), stabilizer (efficient Clifford-only circuits), matrix product state (MPS, for low-entanglement circuits), and unitary matrix. Choosing the right method lets you trade off memory against speed depending on your circuit structure.
Noise modeling is one of Aer’s strongest features. You can construct a NoiseModel by hand using depolarizing, thermal relaxation, readout, or Kraus errors, or you can pull a calibrated noise model directly from a real IBM Quantum device using NoiseModel.from_backend(). Fake backends such as FakeNairobi, FakeMontreal, and FakeSherbrooke bundle a device’s gate set, coupling map, and noise model into a single object so you can test realistic noisy simulations without hardware access.
For large statevector simulations, Aer supports GPU acceleration through NVIDIA’s cuStateVec library (part of the cuQuantum SDK). Enabling GPU execution requires only setting device='GPU' on AerSimulator. Multi-GPU and multi-node simulation is also available for circuits that exceed single-GPU memory. Aer integrates tightly with Qiskit’s transpiler so compiled circuits run directly without extra conversion steps.
Key Imports
from qiskit_aer import AerSimulator, StatevectorSimulator, UnitarySimulator
from qiskit_aer.noise import NoiseModel, depolarizing_error, thermal_relaxation_error, ReadoutError
from qiskit_aer.primitives import Sampler, Estimator
from qiskit_ibm_runtime.fake_provider import FakeNairobi, FakeSherbrooke
Simulation Methods
Aer provides several simulation methods, each suited to different circuit types and analysis goals. You select a method by passing method='...' to AerSimulator.
| Method | Description | Best For |
|---|---|---|
statevector | Tracks the full complex amplitude vector. Exponential memory in qubit count. | Exact analysis of circuits up to ~30 qubits on CPU, more with GPU. |
density_matrix | Tracks the full density matrix (2^n x 2^n). Handles mixed states natively. | Simulating open quantum systems and noise channels where you need the full density operator. |
stabilizer | Uses the Gottesman-Knill theorem for efficient Clifford circuit simulation. Polynomial time and memory. | Circuits composed entirely of Clifford gates (H, S, CNOT, Pauli). Scales to thousands of qubits. |
matrix_product_state | Decomposes the state as a tensor network (MPS). Memory depends on entanglement, not qubit count alone. | Circuits with limited or sequential entanglement structure, such as 1D nearest-neighbor circuits. |
unitary | Computes the full unitary matrix of the circuit. | Extracting the exact unitary operator for small circuits (typically under 15 qubits). |
superop | Computes the superoperator representation of a noisy circuit. | Analyzing the complete action of a noisy channel, including non-unitary effects. |
Common Patterns
Statevector simulation
Build a Bell state and extract the exact statevector.
from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator
# Build a Bell state circuit
qc = QuantumCircuit(2)
qc.h(0) # Put qubit 0 in superposition
qc.cx(0, 1) # Entangle qubit 0 and qubit 1
qc.save_statevector() # Instruct Aer to snapshot the statevector
# Run with the statevector method
sim = AerSimulator(method='statevector')
result = sim.run(qc).result()
# Extract the statevector
statevector = result.get_statevector(qc)
print(statevector)
# Expect: [0.707+0j, 0, 0, 0.707+0j] (|00> + |11>) / sqrt(2)
Shot-based simulation (QASM)
Run a circuit with measurement sampling, similar to real hardware execution.
from qiskit import QuantumCircuit
from qiskit_aer import AerSimulator
# Build a GHZ state circuit with measurements
qc = QuantumCircuit(3, 3)
qc.h(0)
qc.cx(0, 1)
qc.cx(1, 2)
qc.measure([0, 1, 2], [0, 1, 2])
# Run with 1024 shots
sim = AerSimulator()
result = sim.run(qc, shots=1024).result()
counts = result.get_counts(qc)
print(counts)
# Expect roughly: {'000': ~512, '111': ~512}
Noise model from a real device
Pull a calibrated noise model from a fake backend that mirrors a real IBM Quantum processor.
from qiskit import QuantumCircuit, transpile
from qiskit_aer import AerSimulator
from qiskit_aer.noise import NoiseModel
from qiskit_ibm_runtime.fake_provider import FakeSherbrooke
# Load the fake backend (mirrors ibm_sherbrooke calibration data)
fake_backend = FakeSherbrooke()
# Extract the noise model and coupling map
noise_model = NoiseModel.from_backend(fake_backend)
coupling_map = fake_backend.coupling_map
# Build a simple circuit
qc = QuantumCircuit(2, 2)
qc.h(0)
qc.cx(0, 1)
qc.measure([0, 1], [0, 1])
# Transpile for the target topology
qc_transpiled = transpile(qc, backend=fake_backend, optimization_level=2)
# Run on AerSimulator with the extracted noise model
sim = AerSimulator(noise_model=noise_model, coupling_map=coupling_map)
result = sim.run(qc_transpiled, shots=4096).result()
counts = result.get_counts()
print(counts)
# Expect: mostly '00' and '11', with some noise-induced bit flips
Custom noise model
Construct a noise model from scratch by attaching error channels to specific gates.
from qiskit import QuantumCircuit, transpile
from qiskit_aer import AerSimulator
from qiskit_aer.noise import NoiseModel, depolarizing_error, ReadoutError
import numpy as np
# Create an empty noise model
noise_model = NoiseModel()
# Add depolarizing error to single-qubit gates (0.1% error rate)
error_1q = depolarizing_error(0.001, 1)
noise_model.add_all_qubit_quantum_error(error_1q, ['u1', 'u2', 'u3', 'sx', 'x'])
# Add depolarizing error to two-qubit gates (1% error rate)
error_2q = depolarizing_error(0.01, 2)
noise_model.add_all_qubit_quantum_error(error_2q, ['cx'])
# Add readout (measurement) error: 2% chance of flipping the measured bit
readout_error = ReadoutError([[0.98, 0.02], [0.02, 0.98]])
noise_model.add_all_qubit_readout_error(readout_error)
# Build and run a circuit with the custom noise
qc = QuantumCircuit(2, 2)
qc.h(0)
qc.cx(0, 1)
qc.measure([0, 1], [0, 1])
sim = AerSimulator(noise_model=noise_model)
result = sim.run(qc, shots=4096).result()
print(result.get_counts())
GPU acceleration
For large circuits, offload statevector simulation to an NVIDIA GPU using cuStateVec.
from qiskit_aer import AerSimulator
# Create a GPU-accelerated simulator
# Requires qiskit-aer built with GPU support and NVIDIA cuQuantum SDK installed
sim_gpu = AerSimulator(method='statevector', device='GPU')
# For multi-GPU execution with automatic state distribution:
sim_multi_gpu = AerSimulator(
method='statevector',
device='GPU',
cuStateVec_enable=True,
blocking_enable=True,
blocking_qubits=25 # qubits per GPU chunk
)
# Usage is identical to CPU simulation after setup
result = sim_gpu.run(qc, shots=1024).result()
Note: GPU support requires installing qiskit-aer-gpu or building from source with CUDA. The cuStateVec_enable=True flag activates NVIDIA’s optimized state vector operations, which can significantly speed up simulations beyond 20 qubits.
Using with Primitives (SamplerV2 / EstimatorV2)
The Aer primitives provide the same Sampler and Estimator interface used by Qiskit Runtime, but execute locally.
from qiskit.circuit import QuantumCircuit, Parameter
from qiskit.quantum_info import SparsePauliOp
from qiskit_aer.primitives import Sampler, Estimator
# Build a parameterized circuit
theta = Parameter('θ')
qc = QuantumCircuit(2)
qc.h(0)
qc.ry(theta, 1)
qc.cx(0, 1)
qc.measure_all()
# --- Sampler: get quasi-probability distributions ---
sampler = Sampler()
# Bind parameter and sample
job = sampler.run([qc], parameter_values=[[0.5]])
result = job.result()
print(result.quasi_dists[0])
# --- Estimator: compute expectation values ---
# Remove measurements for Estimator (it handles measurement internally)
qc_no_meas = qc.remove_final_measurements(inplace=False)
observable = SparsePauliOp.from_list([("ZZ", 1.0)])
estimator = Estimator()
job = estimator.run([qc_no_meas], [observable], parameter_values=[[0.5]])
result = job.result()
print(f"<ZZ> = {result.values[0]:.4f}")
Fake Backends
Fake backends bundle calibration data, coupling maps, and noise parameters from real IBM Quantum processors. They are now imported from qiskit_ibm_runtime.fake_provider (previously qiskit.providers.fake_provider).
| Fake Backend | Real Device | Qubits | Notes |
|---|---|---|---|
FakeNairobi | ibm_nairobi | 7 | Falcon r5.11 processor. Good for small-scale noisy tests. |
FakeMontreal | ibm_montreal | 27 | Falcon r4 processor. Mid-size testing. |
FakeKolkata | ibm_kolkata | 27 | Falcon r5.11 processor. Alternative 27-qubit topology. |
FakeSherbrooke | ibm_sherbrooke | 127 | Eagle r3 processor. Large-scale noise simulation. |
FakeTorino | ibm_torino | 133 | Heron r1 processor. Newer heavy-hex topology. |
FakeBrisbane | ibm_brisbane | 127 | Eagle r3 processor. Another 127-qubit option. |
All fake backends can be used directly as a backend for transpile() and AerSimulator.from_backend(). They provide realistic coupling maps, basis gates, and gate error rates.
Learning Resources
- Qiskit Aer documentation: https://qiskit.github.io/qiskit-aer/
- GitHub repository: https://github.com/Qiskit/qiskit-aer
- Related tutorials on this site: Qiskit Noise Simulation, Qiskit Runtime Error Suppression
- Related courses: IBM Quantum Learning: Qiskit