Simulate Noisy Circuits
Quantum computations are susceptible to noise from the environment, which can introduce errors and alter results. pyrauli
provides a comprehensive NoiseModel
class to simulate the effects of various noise channels on your quantum circuits. This guide demonstrates how to apply noise models when working with Circuit
objects directly, or when using pyrauli
as a backend within Qiskit via PBackend
and PyrauliEstimator
.
Apply Noise to a pyrauli
Circuit
You can introduce noise into a simulation by creating a NoiseModel
and associating it with a Circuit
. The noise model is applied automatically.
# 1. Create a Noise Model that adds depolarizing noise after every H gate and amplitude damping noise after each X gate
p = 0.1 # Noise strength
noise_model = NoiseModel()
noise_model.add_unital_noise_on_gate(QGate.H, UnitalNoise.Depolarizing, p)
noise_model.add_amplitude_damping_on_gate(QGate.X, p)
# 2. Create a noisy circuit using the noise model
qc_noisy = Circuit(1, noise_model=noise_model)
qc_noisy.add_operation("H", 0)
# 3. Run, everything is taken care of automatically
res_noisy = qc_noisy.run(Observable("X"))
The Importance of Truncation with Noise
Noise models, especially those that split a single Pauli term into multiple terms (like amplitude damping), can cause the number of terms in an Observable
to grow rapidly. Without management, this can quickly make the simulation intractable.
To counteract this, it is crucial to use a Truncator
with an appropriate SchedulingPolicy
when running noisy simulations. This will prune terms that are unlikely to contribute significantly to the final expectation value, keeping the simulation feasible.
The example below shows how to configure a circuit to apply a CoefficientTruncator
after every gate that splits the observable, ensuring the complexity remains under control.
qc = Circuit(
nb_qubits=4,
truncator=CoefficientTruncator(0.1),
# Crucially, the policy must be set to run the truncator
truncate_policy=AlwaysAfterSplittingPolicy()
)
Note
See Manage Simulation Complexity for more information on using Truncator
.
Use Noise Models with Qiskit Backends
When using pyrauli
as a backend within the Qiskit ecosystem, you can specify noise models at two levels: either when initializing the backend for a default noise setting, or on a per-run basis for specific experiments.
With PBackend
You can initialize a PBackend
with a default noise model.
# Define a default noise model
p = 0.1
default_noise = NoiseModel()
default_noise.add_unital_noise_on_gate(QGate.H, UnitalNoise.Depolarizing, p)
# Initialize the backend with this model
backend = PBackend(noise_model=default_noise)
However, you can easily override this default by passing a different NoiseModel
directly to the run()
method. This provides the flexibility to compare different noise scenarios without creating multiple backend instances.
job = backend.run(
[simple_pub],
truncator=override_trunc,
merge_policy=override_policy,
truncate_policy=override_policy,
noise_model=override_nm
)
With PyrauliEstimator
The same principle applies to the PyrauliEstimator
. You can configure a default noise model during initialization or override it in the run()
call for specific Program-Backend-Unification (PUB) executions.
This example demonstrates how to set an initial NoiseModel
on the estimator and then override it in a run()
call to apply a different noise channel for a specific job.
estimator = PyrauliEstimator(
truncator=initial_trunc,
merge_policy=initial_policy,
truncate_policy=initial_policy,
noise_model=initial_nm
)
# 3. Run with overriding options
job = estimator.run(
[simple_pub],
truncator=override_trunc,
merge_policy=override_policy,
truncate_policy=override_policy,
noise_model=override_nm
)
result = job.result()