qml.add_noise¶
- add_noise(tape, noise_model, level='user')[source]¶
Insert operations according to a provided noise model.
Circuits passed through this quantum transform will be updated to apply the insertion-based
NoiseModel, which contains mappings{BooleanFn: Callable}from conditions to the corresponding noise gates for circuit operations and measurements respectively. First, each condition in the first mapping of a noise model will be evaluated on the operations contained within the given circuit. For conditions that evaluate toTrue, the noisy gates contained within theCallablewill be inserted after the operation under consideration. Similar procedure will be followed for each measurement in the circuit, in case a second mapping is present in the noise model to indicate readout errors.- Parameters:
tape (QNode or QuantumTape or Callable or pennylane.devices.Device) – the input circuit or device to be transformed.
noise_model (NoiseModel) – noise model according to which noise has to be inserted.
level (str, int, slice) –
An indication of which stage in the compile pipeline the noise model should be applied to. Only relevant when transforming a
QNode. More details on the following permissible values can be found in theget_transform_program()-str: acceptable keys are"top","user","device", and"gradient".int: how many transforms to include, starting from the front of the program.slice: a slice to select out components of the compile pipeline.
- Returns:
Transformed circuit as described in
qml.transform.- Return type:
qnode (QNode) or quantum function (Callable) or tuple[List[.QuantumTape], function] or device (pennylane.devices.Device)
- Raises:
ValueError – argument
noise_modelis not a valid noise model.
Note
For a given
model_mapandmeas_mapwithin aNoiseModel, if multiple conditionals in the given maps evaluate toTruefor an operation or measurement process, then the noise operations defined via their respective noisy quantum functions will be added in the same order in which the conditionals appear in them.Example:
The following QNode can be transformed to add noise to the circuit:
dev = qml.device("default.mixed", wires=2) fcond1 = qml.noise.op_eq(qml.RX) & qml.noise.wires_in([0, 1]) noise1 = qml.noise.partial_wires(qml.PhaseDamping, 0.4) fcond2 = qml.noise.op_in([qml.RX, qml.RZ]) def noise2(op, **kwargs): qml.ThermalRelaxationError(op.parameters[0] * 0.5, kwargs["t1"], kwargs["t2"], 0.6, op.wires) fcond3 = qml.noise.meas_eq(qml.expval) & qml.noise.wires_in([0, 1]) noise3 = qml.noise.partial_wires(qml.PhaseFlip, 0.2) noise_model = qml.NoiseModel( {fcond1: noise1, fcond2: noise2}, {fcond3: noise3}, t1=2.0, t2=0.2 ) @qml.noise.add_noise(noise_model=noise_model) @qml.qnode(dev) def circuit(w, x, y, z): qml.RX(w, wires=0) qml.RY(x, wires=1) qml.CNOT(wires=[0, 1]) qml.RY(y, wires=0) qml.RX(z, wires=1) return qml.expval(qml.Z(0) @ qml.Z(1))
Executions of this circuit will differ from the noise-free value:
>>> circuit(0.9, 0.4, 0.5, 0.6) np.float64(0.5440530007721438) >>> print(qml.draw(circuit)(0.9, 0.4, 0.5, 0.6)) 0: ──RX(0.90)──PhaseDamping(0.40)──ThermalRelaxationError(0.45,2.00,0.20,0.60)─╭●──RY(0.50) ··· 1: ──RY(0.40)──────────────────────────────────────────────────────────────────╰X──RX(0.60) ··· 0: ··· ──PhaseFlip(0.20)──────────────────────────────────────────────────────────────────┤ ╭<Z@Z> 1: ··· ──PhaseDamping(0.40)──ThermalRelaxationError(0.30,2.00,0.20,0.60)──PhaseFlip(0.20)─┤ ╰<Z@Z>
Tranform Levels
When transforming an already constructed
QNode, theadd_noisetransform will be added at the end of the “user” transforms by default, i.e., after all the transforms that have been manually applied to the QNode up to that point.dev = qml.device("default.mixed", wires=2) @qml.metric_tensor @qml.transforms.undo_swaps @qml.transforms.merge_rotations @qml.transforms.cancel_inverses @qml.qnode(dev) def circuit(w, x, y, z): qml.RX(w, wires=0) qml.RY(x, wires=1) qml.CNOT(wires=[0, 1]) qml.RY(y, wires=0) qml.RX(z, wires=1) return qml.expval(qml.Z(0) @ qml.Z(1)) noisy_circuit = qml.noise.add_noise(circuit, noise_model)
>>> qml.workflow.get_transform_program(circuit) CompilePipeline(cancel_inverses, merge_rotations, undo_swaps, defer_measurements, decompose, no_sampling, validate_device_wires, validate_measurements, validate_observables, _expand_metric_tensor, metric_tensor)
>>> qml.workflow.get_transform_program(noisy_circuit) CompilePipeline(cancel_inverses, merge_rotations, undo_swaps, add_noise, defer_measurements, decompose, no_sampling, validate_device_wires, validate_measurements, validate_observables, _expand_metric_tensor, metric_tensor)
However, one can request to insert the
add_noisetransform at any specific point in the compile pipeline. By specifying thelevelkeyword argument while transforming aQNode, this transform can be added at a designated level within the compile pipeline, as determined using theget_transform_program. For example, specifyingNonewill add it at the end, ensuring that the tape is expanded to have noAdjointandTemplates:>>> qml.noise.add_noise(circuit, noise_model, level="device").transform_program CompilePipeline(cancel_inverses, merge_rotations, undo_swaps, defer_measurements, decompose, no_sampling, validate_device_wires, validate_measurements, validate_observables, add_noise, _expand_metric_tensor, metric_tensor)
Other acceptable values for
levelare"top","user","device", and"gradient". Among these, “top” will allow addition to an empty compile pipeline, “user” will allow addition at the end of user-specified transforms, “device” will allow addition at the end of device-specific transforms, and “gradient” will allow addition at the end of transforms that expand trainable operations. For example:>>> qml.noise.add_noise(circuit, noise_model, level="top").transform_program CompilePipeline(add_noise)
>>> qml.noise.add_noise(circuit, noise_model, level="user").transform_program CompilePipeline(cancel_inverses, merge_rotations, undo_swaps, add_noise, _expand_metric_tensor, metric_tensor)
>>> qml.noise.add_noise(circuit, noise_model, level="device").transform_program CompilePipeline(cancel_inverses, merge_rotations, undo_swaps, defer_measurements, decompose, no_sampling, validate_device_wires, validate_measurements, validate_observables, add_noise, _expand_metric_tensor, metric_tensor)
Finally, more precise control over the insertion of the transform can be achieved by specifying an integer or slice for indexing when extracting the compile pipeline. For example, one can do:
>>> qml.noise.add_noise(circuit, noise_model, level=2).transform_program CompilePipeline(cancel_inverses, merge_rotations, add_noise)
>>> qml.noise.add_noise(circuit, noise_model, level=slice(1,3)).transform_program CompilePipeline(merge_rotations, undo_swaps, add_noise)