Adding noise to a QuantumProgram or Circuit
Noise is an inherent part of real quantum systems, and simulating its effects is crucial for understanding the behavior of quantum algorithms in real-world conditions. The open source package qoqo
provides users with several ways to define noise models precisely. The noise operations can be directly added to a quantum circuit and then be simulated by compatible backends.
Adding Decoherence
As decoherence noise does not translate into unitary quantum gates, the noise operations are defined as Pragma
operations in qoqo
. The strength of the noise is determined by defining a gate_time
and a rate
. The noise Pragma
operations affect the system as a Lindblad type noise acting on the system with the rate rate
for the time gate_time
.
The Noise Models
Struqture
lets users represent the decoherence part of the Lindblad equation using the struqture.spins.PlusMinusLindbladNoiseOperator
class. The available noise models include:
-
ContinousDecoherenceModel
: The noise model representing a continuous decoherence process on qubits. This noise model assumes that all qubits are constantly experiencing decoherence over time (e.g. due to coupling to the environment). The noise for each qubit can be different but only single-qubit noise is included in the model -
DecoherenceOnGateModel
: This noise model for noise that is only present on gate executions. It adds noise when specific gates (identified by the name of the gate and qubits it acts on) are executed. -
DecoherenceOnIdleModel
: The noise model which adds noise to qubits not involved in an operation, when that gate operation is executed (i.e. to the qubits which remain idle during the operation).
The NoiseInserter
Converter
The NoiseInserter
allows users to add noise Pragma
operations to a given circuit or QuantumProgram, where the noise operations are derived from the input noise models. The noise model specifies the type of noise operations to be inserted, e.g. PragmaDamping
, PragmaDephasing
or PragmaDepolarizing
as well as the rate of the noise. The gate time is inferred from the device being used.
The noise is added either before, after or symmetrical around gate operations present in the circuit. This is defined by the user when initializing the NoiseInserter
.
Initializing the NoiseInserter
Class
The NoiseInserter
class is imported from the pyqonvert.noise
module. The following values are needed to initialize it.
noise_mode
: specifies the type of noise insertion. Allowed values includeactive_qubits_only
- noisePragma
s are added to the qubits involved in gate operations in the circuit.all_qubits
- noisePragma
s are added on all the qubits involved in the circuit.parallelization_blocks
- noisePragma
s are added to the qubits involved in the a parallel block marked byPragmaStopParallelBlock
operations in the circuit.
Additionally, there is the noise placement mode
property of the NoiseInserter, which can be modified by the user. This setting determines whether noise is applied after, before, or symmetrically around (both before and after) a gate operation. By default, the noise placement mode is set to "After"
, meaning that all noise Pragma operations are sequentially added after gate operations in a circuit. You can modify this behavior using the functions provided below.
from pyqonvert.noise import NoiseInserter
noise_mode = "all_qubits"
# initialize noise inserter
noise_inserter = NoiseInserter(noise_mode)
# Ready to use for noise addition.
Noise placement modes
The following functions help in updating the noise placement mode:
noise_before_gate
- noisePragma
s are placed before each gate operation.
noise_inserter = NoiseInserter(noise_mode)
noise_inserter.noise_before_gate()
noise_symmetric_around_gate
- noisePragma
s are placed symmetrically around each gate operation.
noise_inserter = NoiseInserter(noise_mode)
noise_inserter.noise_symmetric_around_gate()
noise_after_gate
- noisePragma
s are placed after each gate operation.
noise_inserter = NoiseInserter(noise_mode)
noise_inserter.noise_after_gate()
Examples for each type of noise insertion
The NoiseInserter
Class provides the convert
method to insert noise into quantum circuits and QuantumPrograms.
The following example shows how one can insert noise according to a DecoherenceOnGateModel
into a circuit with the "all_qubits"
mode.
from pyqonvert.noise import NoiseInserter
from qoqo import devices, noise_models as nm, Circuit
from qoqo import operations as ops
from struqture_py.spins import PlusMinusLindbladNoiseOperator, PlusMinusProduct
noise_inserter = NoiseInserter("all_qubits").noise_before_gate()
decoherence_rate = 0.011
number_qubits = 2
# Initialize device and noise_model
device = devices.AllToAllDevice(
number_qubits,
["Hadamard"],
["CNOT"],
0.5
)
noise = PlusMinusLindbladNoiseOperator()
noise_product = PlusMinusProduct().z(0).plus(0)
noise.add_operator_product((noise_product, noise_product), 2.5)
noise_model = nm.DecoherenceOnGateModel().set_single_qubit_gate_error(
"Hadamard",
0,
noise
)
# initialize circuit
circuit = Circuit()
circuit += ops.Hadamard(0)
circuit += ops.CNOT(0,1)
# Add noise
circuit_noise = noise_inserter.convert(circuit, device, [noise_model])
Figure 1: Adding decoherence noise "before" Hadamard gates on qubit 0
The convert
function is applied to a QuantumProgram in a similar fashion.
from pyqonvert.noise import NoiseInserter
from qoqo import devices, noise_models as nm, Circuit, QuantumProgram
from qoqo import operations as ops
from qoqo.measurements import PauliZProduct, PauliZProductInput
noise_inserter = NoiseInserter("active_qubits_only")
decoherence_rate = 0.011
number_qubits = 2
# Initialize device and noise_model
device = devices.AllToAllDevice(
number_qubits,
["Hadamard"],
["CNOT"],
0.5
)
noise_model = nm.ContinuousDecoherenceModel().add_damping_rate(
[0, 1],
decoherence_rate
)
# initialize program
circuit = Circuit()
circuit += ops.Hadamard(0)
circuit += ops.CNOT(0,1)
z_circuit = Circuit()
z_circuit += ops.DefinitionBit("ro_z", 1, is_output=True)
z_circuit += ops.PragmaRepeatedMeasurement("ro_z", 1000, None)
measurement_input = PauliZProductInput(1, False)
measurement = PauliZProduct(
constant_circuit = circuit,
circuits=[z_circuit],
input=measurement_input,
)
qp = QuantumProgram(measurement, [])
# Add noise
program_noise = noise_inserter.convert(qp, device, [noise_model])
Figure 2: Adding continous decoherence noise on active qubits
A circuit that has been optimized using the CircuitParallelizer
converter is interspersed with PragmaStopParallelBlock
instances. Noise can be added to a parallelized circuit or QuantumProgram using the "parallelization_blocks"
mode. More information about the CircuitParallelizer
converter can be found here and in the Python API documentation.
from pyqonvert.noise import NoiseInserter
from qoqo import devices, Circuit, noise_models as nm
from qoqo import operations as ops
noise_inserter = NoiseInserter("parallelization_blocks")
decoherence_rate = 0.011
number_qubits = 2
# Initialize device
device = devices.AllToAllDevice(
number_qubits,
["Hadamard"],
[],
0.5
)
noise_model = nm.DecoherenceOnIdleModel().add_damping_rate(
[0, 1],
decoherence_rate
)
# initialize circuit
circuit = Circuit()
circuit += ops.Hadamard(0)
circuit += ops.PragmaStopParallelBlock([0], 0.5)
circuit += ops.Hadamard(1)
circuit += ops.PragmaStopParallelBlock([1], 0.5)
# Add noise
circuit_noise = noise_inserter.convert(circuit, device, [noise_model])
Adding Overrotation Noise
Overrotation noise refers to the arbitrary rotation of qubits due to background factors. Pyqonvert
facilitates the addition of such noise using the following noise model:
SingleQubitOverrotationOnGate
: The noise model which places a rotation gate with a randomly distributed rotation angle after a specified gate operation. Through theSingleQubitOverrotationDescription
class, one can define the parameters of the Gaussian distribution (mean and standard deviation) from which the overrotation angle is sampled.
The SingleQubitOverrotationAdder
converter
The purpose of SingleQubitOverrotationAdder
is to add single-qubit rotation gates that simulate overrotation noise informed by a SingleQubitOverrotationOnGate
model.
Initializing the SingleQubitOverrotationAdder
Class
The SingleQubitOverrotationAdder
class is imported from the pyqonvert.noise
module.
seed
: the(optional) seed for the random number generator. If a seed is not provided, a machine-generated seed is used.
from pyqonvert.noise import SingleQubitOverrotationAdder
# initialize SingleQubitOverrotationAdder without a seed
overrotation_adder = SingleQubitOverrotationAdder()
# initialize SingleQubitOverrotationAdder with a seed
overrotation_adder_seeded = SingleQubitOverrotationAdder(seed=42)
# Ready to use for noise addition.
Example
Like NoiseInserter
, the SingleQubitOverrotationAdder
Class has the convert
function which adds overrotation noise to quantum circuits and QuantumPrograms.
from pyqonvert.noise import SingleQubitOverrotationAdder
from qoqo import devices, Circuit, noise_models as nm
from qoqo import operations as ops
# Initializing the converter
noise_inserter = SingleQubitOverrotationAdder(seed=1)
# noise model
rotation_gate = "RotateZ"
# Parameters for Gaussian distribution single-qubit gates
mean_single = 0.0
std_single = 1.0
overrotation_noise_description_single = nm.SingleQubitOverrotationDescription("RotateZ", mean_single, std_single)
# Parameters for Gaussian distribution two qubit gates
mean_two = 1.0
std_two = 2.0
overrotation_noise_description_two = nm.SingleQubitOverrotationDescription("RotateX", mean_two, std_two)
overrotation_noise = nm.SingleQubitOverrotationOnGate()
overrotation_noise = overrotation_noise.set_single_qubit_overrotation(
"Hadamard",
0,
overrotation_noise_description_single
)
overrotation_noise = overrotation_noise.set_two_qubit_overrotation(
"CNOT",
0,
1,
(overrotation_noise_description_two, overrotation_noise_description_two)
)
# Initialize device and noise_model
number_qubits = 2
device = devices.AllToAllDevice(
number_qubits,
["Hadamard"],
["CNOT"],
0.5
)
# initialize circuit
circuit = Circuit()
circuit += ops.Hadamard(0)
circuit += ops.CNOT(0,1)
# Add noise
circuit_noise = noise_inserter.convert(circuit, device, [overrotation_noise])
The usage in the case of QuantumPrograms is similar:
program_noise = noise_inserter.convert(quantum_program, device, [overrotation_noise])
Figure 3: Adding overrotation noise