qadence 1.7.8__py3-none-any.whl → 1.8.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- qadence/analog/device.py +1 -1
- qadence/backend.py +3 -3
- qadence/backends/horqrux/backend.py +3 -3
- qadence/backends/pulser/backend.py +16 -17
- qadence/backends/pulser/convert_ops.py +2 -2
- qadence/backends/pyqtorch/backend.py +7 -7
- qadence/backends/pyqtorch/convert_ops.py +191 -240
- qadence/backends/utils.py +9 -1
- qadence/blocks/abstract.py +1 -1
- qadence/blocks/embedding.py +21 -11
- qadence/blocks/matrix.py +3 -1
- qadence/blocks/primitive.py +36 -11
- qadence/circuit.py +1 -1
- qadence/constructors/__init__.py +2 -1
- qadence/constructors/ansatze.py +176 -0
- qadence/engines/differentiable_backend.py +3 -3
- qadence/engines/jax/differentiable_backend.py +2 -2
- qadence/engines/jax/differentiable_expectation.py +2 -2
- qadence/engines/torch/differentiable_backend.py +2 -2
- qadence/engines/torch/differentiable_expectation.py +2 -2
- qadence/execution.py +14 -14
- qadence/extensions.py +1 -1
- qadence/measurements/shadow.py +4 -5
- qadence/measurements/tomography.py +2 -2
- qadence/measurements/utils.py +2 -2
- qadence/mitigations/analog_zne.py +8 -7
- qadence/mitigations/protocols.py +2 -2
- qadence/mitigations/readout.py +8 -5
- qadence/ml_tools/constructors.py +2 -2
- qadence/ml_tools/models.py +7 -7
- qadence/ml_tools/printing.py +2 -1
- qadence/model.py +5 -5
- qadence/noise/__init__.py +2 -2
- qadence/noise/protocols.py +216 -29
- qadence/operations/control_ops.py +37 -22
- qadence/operations/ham_evo.py +1 -0
- qadence/operations/parametric.py +32 -10
- qadence/operations/primitive.py +61 -29
- qadence/overlap.py +0 -6
- qadence/parameters.py +3 -2
- qadence/transpile/__init__.py +2 -1
- qadence/transpile/noise.py +46 -0
- qadence/types.py +26 -2
- {qadence-1.7.8.dist-info → qadence-1.8.0.dist-info}/METADATA +5 -8
- {qadence-1.7.8.dist-info → qadence-1.8.0.dist-info}/RECORD +47 -50
- qadence/backends/braket/__init__.py +0 -4
- qadence/backends/braket/backend.py +0 -234
- qadence/backends/braket/config.py +0 -22
- qadence/backends/braket/convert_ops.py +0 -116
- {qadence-1.7.8.dist-info → qadence-1.8.0.dist-info}/WHEEL +0 -0
- {qadence-1.7.8.dist-info → qadence-1.8.0.dist-info}/licenses/LICENSE +0 -0
@@ -15,7 +15,7 @@ from qadence.blocks.analog import ConstantAnalogRotation, InteractionBlock
|
|
15
15
|
from qadence.circuit import QuantumCircuit
|
16
16
|
from qadence.measurements import Measurements
|
17
17
|
from qadence.mitigations import Mitigations
|
18
|
-
from qadence.noise import
|
18
|
+
from qadence.noise import NoiseHandler
|
19
19
|
from qadence.operations import AnalogRot
|
20
20
|
from qadence.transpile import apply_fn_to_blocks
|
21
21
|
from qadence.utils import Endianness
|
@@ -44,7 +44,7 @@ def pulse_experiment(
|
|
44
44
|
circuit: QuantumCircuit,
|
45
45
|
observable: list[AbstractBlock],
|
46
46
|
param_values: dict[str, Tensor],
|
47
|
-
noise:
|
47
|
+
noise: NoiseHandler,
|
48
48
|
stretches: Tensor,
|
49
49
|
endianness: Endianness,
|
50
50
|
state: Tensor | None = None,
|
@@ -116,11 +116,12 @@ def noise_level_experiment(
|
|
116
116
|
circuit: QuantumCircuit,
|
117
117
|
observable: list[AbstractBlock],
|
118
118
|
param_values: dict[str, Tensor],
|
119
|
-
noise:
|
119
|
+
noise: NoiseHandler,
|
120
120
|
endianness: Endianness,
|
121
121
|
state: Tensor | None = None,
|
122
122
|
) -> Tensor:
|
123
|
-
|
123
|
+
protocol, options = noise.protocol[-1], noise.options[-1]
|
124
|
+
noise_probs = options.get("noise_probs")
|
124
125
|
zne_datasets: list = []
|
125
126
|
# Get noisy density matrices.
|
126
127
|
conv_circuit = backend.circuit(circuit)
|
@@ -152,7 +153,7 @@ def analog_zne(
|
|
152
153
|
param_values: dict[str, Tensor] = {},
|
153
154
|
state: Tensor | None = None,
|
154
155
|
measurement: Measurements | None = None,
|
155
|
-
noise:
|
156
|
+
noise: NoiseHandler | None = None,
|
156
157
|
mitigation: Mitigations | None = None,
|
157
158
|
endianness: Endianness = Endianness.BIG,
|
158
159
|
) -> Tensor:
|
@@ -162,7 +163,7 @@ def analog_zne(
|
|
162
163
|
backend = cast(Backend, backend)
|
163
164
|
noise_model = mitigation.options.get("noise_model", None)
|
164
165
|
if noise_model is None:
|
165
|
-
KeyError(
|
166
|
+
raise KeyError("A noise model should be specified.")
|
166
167
|
stretches = mitigation.options.get("stretches", None)
|
167
168
|
if stretches is not None:
|
168
169
|
extrapolated_exp_values = pulse_experiment(
|
@@ -195,7 +196,7 @@ def mitigate(
|
|
195
196
|
param_values: dict[str, Tensor] = {},
|
196
197
|
state: Tensor | None = None,
|
197
198
|
measurement: Measurements | None = None,
|
198
|
-
noise:
|
199
|
+
noise: NoiseHandler | None = None,
|
199
200
|
mitigation: Mitigations | None = None,
|
200
201
|
endianness: Endianness = Endianness.BIG,
|
201
202
|
) -> Tensor:
|
qadence/mitigations/protocols.py
CHANGED
@@ -5,7 +5,7 @@ from collections import Counter
|
|
5
5
|
from dataclasses import dataclass
|
6
6
|
from typing import Callable, cast
|
7
7
|
|
8
|
-
from qadence.noise.protocols import
|
8
|
+
from qadence.noise.protocols import NoiseHandler
|
9
9
|
|
10
10
|
PROTOCOL_TO_MODULE = {
|
11
11
|
"readout": "qadence.mitigations.readout",
|
@@ -45,7 +45,7 @@ class Mitigations:
|
|
45
45
|
|
46
46
|
|
47
47
|
def apply_mitigation(
|
48
|
-
noise:
|
48
|
+
noise: NoiseHandler, mitigation: Mitigations, samples: list[Counter]
|
49
49
|
) -> list[Counter]:
|
50
50
|
"""Apply mitigation to samples."""
|
51
51
|
mitigation_fn = mitigation.get_mitigation_fn()
|
qadence/mitigations/readout.py
CHANGED
@@ -11,8 +11,8 @@ from scipy.linalg import norm
|
|
11
11
|
from scipy.optimize import LinearConstraint, minimize
|
12
12
|
|
13
13
|
from qadence.mitigations.protocols import Mitigations
|
14
|
-
from qadence.noise.protocols import
|
15
|
-
from qadence.types import ReadOutOptimization
|
14
|
+
from qadence.noise.protocols import NoiseHandler
|
15
|
+
from qadence.types import NoiseProtocol, ReadOutOptimization
|
16
16
|
|
17
17
|
|
18
18
|
def corrected_probas(p_corr: npt.NDArray, T: npt.NDArray, p_raw: npt.NDArray) -> np.double:
|
@@ -69,7 +69,7 @@ def matrix_inv(K: npt.NDArray) -> npt.NDArray:
|
|
69
69
|
|
70
70
|
|
71
71
|
def mitigation_minimization(
|
72
|
-
noise:
|
72
|
+
noise: NoiseHandler,
|
73
73
|
mitigation: Mitigations,
|
74
74
|
samples: list[Counter],
|
75
75
|
) -> list[Counter]:
|
@@ -88,7 +88,10 @@ def mitigation_minimization(
|
|
88
88
|
Returns:
|
89
89
|
Mitigated counts computed by the algorithm
|
90
90
|
"""
|
91
|
-
|
91
|
+
protocol, options = noise.protocol[-1], noise.options[-1]
|
92
|
+
if protocol != NoiseProtocol.READOUT:
|
93
|
+
raise ValueError("Specify a noise source of type NoiseProtocol.READOUT.")
|
94
|
+
noise_matrices = options.get("noise_matrix", options["confusion_matrices"])
|
92
95
|
optimization_type = mitigation.options.get("optimization_type", ReadOutOptimization.MLE)
|
93
96
|
n_qubits = len(list(samples[0].keys())[0])
|
94
97
|
n_shots = sum(samples[0].values())
|
@@ -156,5 +159,5 @@ def mitigation_minimization(
|
|
156
159
|
return corrected_counters
|
157
160
|
|
158
161
|
|
159
|
-
def mitigate(noise:
|
162
|
+
def mitigate(noise: NoiseHandler, mitigation: Mitigations, samples: list[Counter]) -> list[Counter]:
|
160
163
|
return mitigation_minimization(noise=noise, mitigation=mitigation, samples=samples)
|
qadence/ml_tools/constructors.py
CHANGED
@@ -21,7 +21,7 @@ from qadence.constructors import (
|
|
21
21
|
from qadence.constructors.ansatze import hea_digital, hea_sDAQC
|
22
22
|
from qadence.constructors.hamiltonians import ObservableConfig, TDetuning
|
23
23
|
from qadence.measurements import Measurements
|
24
|
-
from qadence.noise import
|
24
|
+
from qadence.noise import NoiseHandler
|
25
25
|
from qadence.operations import CNOT, RX, RY, I, N, Z
|
26
26
|
from qadence.parameters import Parameter
|
27
27
|
from qadence.register import Register
|
@@ -740,7 +740,7 @@ def build_qnn_from_configs(
|
|
740
740
|
backend: BackendName = BackendName.PYQTORCH,
|
741
741
|
diff_mode: DiffMode = DiffMode.AD,
|
742
742
|
measurement: Measurements | None = None,
|
743
|
-
noise:
|
743
|
+
noise: NoiseHandler | None = None,
|
744
744
|
configuration: BackendConfiguration | dict | None = None,
|
745
745
|
input_diff_mode: InputDiffMode | str = InputDiffMode.AD,
|
746
746
|
) -> QNN:
|
qadence/ml_tools/models.py
CHANGED
@@ -16,7 +16,7 @@ from qadence.measurements import Measurements
|
|
16
16
|
from qadence.mitigations import Mitigations
|
17
17
|
from qadence.ml_tools.config import AnsatzConfig, FeatureMapConfig
|
18
18
|
from qadence.model import QuantumModel
|
19
|
-
from qadence.noise import
|
19
|
+
from qadence.noise import NoiseHandler
|
20
20
|
from qadence.register import Register
|
21
21
|
from qadence.types import BackendName, DiffMode, Endianness, InputDiffMode, ParamDictType
|
22
22
|
|
@@ -139,7 +139,7 @@ class QNN(QuantumModel):
|
|
139
139
|
backend: BackendName = BackendName.PYQTORCH,
|
140
140
|
diff_mode: DiffMode = DiffMode.AD,
|
141
141
|
measurement: Measurements | None = None,
|
142
|
-
noise:
|
142
|
+
noise: NoiseHandler | None = None,
|
143
143
|
configuration: BackendConfiguration | dict | None = None,
|
144
144
|
inputs: list[sympy.Basic | str] | None = None,
|
145
145
|
input_diff_mode: InputDiffMode | str = InputDiffMode.AD,
|
@@ -218,7 +218,7 @@ class QNN(QuantumModel):
|
|
218
218
|
backend: BackendName = BackendName.PYQTORCH,
|
219
219
|
diff_mode: DiffMode = DiffMode.AD,
|
220
220
|
measurement: Measurements | None = None,
|
221
|
-
noise:
|
221
|
+
noise: NoiseHandler | None = None,
|
222
222
|
configuration: BackendConfiguration | dict | None = None,
|
223
223
|
input_diff_mode: InputDiffMode | str = InputDiffMode.AD,
|
224
224
|
) -> QNN:
|
@@ -311,7 +311,7 @@ class QNN(QuantumModel):
|
|
311
311
|
values: dict[str, Tensor] | Tensor = None,
|
312
312
|
state: Tensor | None = None,
|
313
313
|
measurement: Measurements | None = None,
|
314
|
-
noise:
|
314
|
+
noise: NoiseHandler | None = None,
|
315
315
|
endianness: Endianness = Endianness.BIG,
|
316
316
|
) -> Tensor:
|
317
317
|
"""Forward pass of the model.
|
@@ -360,7 +360,7 @@ class QNN(QuantumModel):
|
|
360
360
|
values: Tensor | dict[str, Tensor] = {},
|
361
361
|
n_shots: int = 1000,
|
362
362
|
state: Tensor | None = None,
|
363
|
-
noise:
|
363
|
+
noise: NoiseHandler | None = None,
|
364
364
|
mitigation: Mitigations | None = None,
|
365
365
|
endianness: Endianness = Endianness.BIG,
|
366
366
|
) -> list[Counter]:
|
@@ -379,7 +379,7 @@ class QNN(QuantumModel):
|
|
379
379
|
observable: list[ConvertedObservable] | ConvertedObservable | None = None,
|
380
380
|
state: Tensor | None = None,
|
381
381
|
measurement: Measurements | None = None,
|
382
|
-
noise:
|
382
|
+
noise: NoiseHandler | None = None,
|
383
383
|
mitigation: Mitigations | None = None,
|
384
384
|
endianness: Endianness = Endianness.BIG,
|
385
385
|
) -> Tensor:
|
@@ -423,7 +423,7 @@ class QNN(QuantumModel):
|
|
423
423
|
backend=qm_dict["backend"],
|
424
424
|
diff_mode=qm_dict["diff_mode"],
|
425
425
|
measurement=Measurements._from_dict(qm_dict["measurement"]),
|
426
|
-
noise=
|
426
|
+
noise=NoiseHandler._from_dict(qm_dict["noise"]),
|
427
427
|
configuration=config_factory(qm_dict["backend"], qm_dict["backend_configuration"]),
|
428
428
|
inputs=qm_dict["inputs"],
|
429
429
|
)
|
qadence/ml_tools/printing.py
CHANGED
@@ -27,8 +27,9 @@ def print_metrics(loss: float | None, metrics: dict, iteration: int) -> None:
|
|
27
27
|
|
28
28
|
|
29
29
|
def write_tensorboard(
|
30
|
-
writer: SummaryWriter, loss: float = None, metrics: dict =
|
30
|
+
writer: SummaryWriter, loss: float = None, metrics: dict | None = None, iteration: int = 0
|
31
31
|
) -> None:
|
32
|
+
metrics = metrics or dict()
|
32
33
|
if loss is not None:
|
33
34
|
writer.add_scalar("loss", loss, iteration)
|
34
35
|
for key, arg in metrics.items():
|
qadence/model.py
CHANGED
@@ -24,7 +24,7 @@ from qadence.circuit import QuantumCircuit
|
|
24
24
|
from qadence.engines.differentiable_backend import DifferentiableBackend
|
25
25
|
from qadence.measurements import Measurements
|
26
26
|
from qadence.mitigations import Mitigations
|
27
|
-
from qadence.noise import
|
27
|
+
from qadence.noise import NoiseHandler
|
28
28
|
from qadence.parameters import Parameter
|
29
29
|
from qadence.types import DiffMode, Endianness
|
30
30
|
|
@@ -83,7 +83,7 @@ class QuantumModel(nn.Module):
|
|
83
83
|
backend: BackendName | str = BackendName.PYQTORCH,
|
84
84
|
diff_mode: DiffMode = DiffMode.AD,
|
85
85
|
measurement: Measurements | None = None,
|
86
|
-
noise:
|
86
|
+
noise: NoiseHandler | None = None,
|
87
87
|
mitigation: Mitigations | None = None,
|
88
88
|
configuration: BackendConfiguration | dict | None = None,
|
89
89
|
):
|
@@ -249,7 +249,7 @@ class QuantumModel(nn.Module):
|
|
249
249
|
values: dict[str, torch.Tensor] = {},
|
250
250
|
n_shots: int = 1000,
|
251
251
|
state: torch.Tensor | None = None,
|
252
|
-
noise:
|
252
|
+
noise: NoiseHandler | None = None,
|
253
253
|
mitigation: Mitigations | None = None,
|
254
254
|
endianness: Endianness = Endianness.BIG,
|
255
255
|
) -> list[Counter]:
|
@@ -287,7 +287,7 @@ class QuantumModel(nn.Module):
|
|
287
287
|
observable: list[ConvertedObservable] | ConvertedObservable | None = None,
|
288
288
|
state: Optional[Tensor] = None,
|
289
289
|
measurement: Measurements | None = None,
|
290
|
-
noise:
|
290
|
+
noise: NoiseHandler | None = None,
|
291
291
|
mitigation: Mitigations | None = None,
|
292
292
|
endianness: Endianness = Endianness.BIG,
|
293
293
|
) -> Tensor:
|
@@ -415,7 +415,7 @@ class QuantumModel(nn.Module):
|
|
415
415
|
backend=qm_dict["backend"],
|
416
416
|
diff_mode=qm_dict["diff_mode"],
|
417
417
|
measurement=Measurements._from_dict(qm_dict["measurement"]),
|
418
|
-
noise=
|
418
|
+
noise=NoiseHandler._from_dict(qm_dict["noise"]),
|
419
419
|
configuration=config_factory(qm_dict["backend"], qm_dict["backend_configuration"]),
|
420
420
|
)
|
421
421
|
|
qadence/noise/__init__.py
CHANGED
qadence/noise/protocols.py
CHANGED
@@ -1,54 +1,241 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
3
|
import importlib
|
4
|
-
from
|
5
|
-
from typing import Callable, Counter, cast
|
4
|
+
from itertools import compress
|
5
|
+
from typing import Any, Callable, Counter, cast
|
6
|
+
|
7
|
+
from qadence.types import NoiseEnum, NoiseProtocol
|
6
8
|
|
7
9
|
PROTOCOL_TO_MODULE = {
|
8
|
-
"
|
10
|
+
"Readout": "qadence.noise.readout",
|
9
11
|
}
|
10
12
|
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
READOUT
|
14
|
+
class NoiseHandler:
|
15
|
+
"""A container for multiple sources of noise.
|
16
|
+
|
17
|
+
Note `NoiseProtocol.ANALOG` and `NoiseProtocol.DIGITAL` sources cannot be both present.
|
18
|
+
Also `NoiseProtocol.READOUT` can only be present once as the last noise sources, and only
|
19
|
+
exclusively with `NoiseProtocol.DIGITAL` sources.
|
20
|
+
|
21
|
+
Args:
|
22
|
+
protocol: The protocol(s) applied. To be defined from `NoiseProtocol`.
|
23
|
+
options: A list of options defining the protocol.
|
24
|
+
For `NoiseProtocol.ANALOG`, options should contain a field `noise_probs`.
|
25
|
+
For `NoiseProtocol.DIGITAL`, options should contain a field `error_probability`.
|
26
|
+
|
27
|
+
Examples:
|
28
|
+
```
|
29
|
+
from qadence import NoiseProtocol, NoiseHandler
|
30
|
+
|
31
|
+
analog_options = {"noise_probs": 0.1}
|
32
|
+
digital_options = {"error_probability": 0.1}
|
33
|
+
readout_options = {"error_probability": 0.1, "seed": 0}
|
34
|
+
|
35
|
+
# single noise sources
|
36
|
+
analog_noise = NoiseHandler(NoiseProtocol.ANALOG.DEPOLARIZING, analog_options)
|
37
|
+
digital_depo_noise = NoiseHandler(NoiseProtocol.DIGITAL.DEPOLARIZING, digital_options)
|
38
|
+
readout_noise = NoiseHandler(NoiseProtocol.READOUT, readout_options)
|
39
|
+
|
40
|
+
# init from multiple sources
|
41
|
+
protocols: list = [NoiseProtocol.DIGITAL.DEPOLARIZING, NoiseProtocol.READOUT]
|
42
|
+
options: list = [digital_options, readout_noise]
|
43
|
+
noise_combination = NoiseHandler(protocols, options)
|
44
|
+
|
45
|
+
# Appending noise sources
|
46
|
+
noise_combination = NoiseHandler(NoiseProtocol.DIGITAL.BITFLIP, digital_options)
|
47
|
+
noise_combination.append([digital_depo_noise, readout_noise])
|
48
|
+
```
|
49
|
+
"""
|
50
|
+
|
51
|
+
def __init__(
|
52
|
+
self,
|
53
|
+
protocol: NoiseEnum | list[NoiseEnum],
|
54
|
+
options: dict | list[dict] = dict(),
|
55
|
+
) -> None:
|
56
|
+
self.protocol = protocol if isinstance(protocol, list) else [protocol]
|
57
|
+
self.options = options if isinstance(options, list) else [options] * len(self.protocol)
|
58
|
+
self.verify_all_protocols()
|
17
59
|
|
18
|
-
def
|
19
|
-
|
20
|
-
|
60
|
+
def _verify_single_protocol(self, protocol: NoiseEnum, option: dict) -> None:
|
61
|
+
if protocol != NoiseProtocol.READOUT:
|
62
|
+
name_mandatory_option = (
|
63
|
+
"noise_probs" if isinstance(protocol, NoiseProtocol.ANALOG) else "error_probability"
|
64
|
+
)
|
65
|
+
noise_probs = option.get(name_mandatory_option, None)
|
66
|
+
if noise_probs is None:
|
67
|
+
error_txt = f"A `{name_mandatory_option}` option"
|
68
|
+
error_txt += f"should be passed for protocol {protocol}."
|
69
|
+
raise KeyError(error_txt)
|
21
70
|
|
22
|
-
def
|
71
|
+
def verify_all_protocols(self) -> None:
|
72
|
+
"""Make sure all protocols are correct in terms and their combination too."""
|
73
|
+
|
74
|
+
if len(self.protocol) == 0:
|
75
|
+
raise ValueError("NoiseHandler should be specified with one valid configuration.")
|
76
|
+
|
77
|
+
if len(self.protocol) != len(self.options):
|
78
|
+
raise ValueError("Specify lists of same length when defining noises.")
|
79
|
+
|
80
|
+
for protocol, option in zip(self.protocol, self.options):
|
81
|
+
self._verify_single_protocol(protocol, option)
|
82
|
+
|
83
|
+
types = [type(p) for p in self.protocol]
|
84
|
+
unique_types = set(types)
|
85
|
+
if NoiseProtocol.DIGITAL in unique_types and NoiseProtocol.ANALOG in unique_types:
|
86
|
+
raise ValueError("Cannot define a config with both Digital and Analog noises.")
|
87
|
+
|
88
|
+
if NoiseProtocol.ANALOG in unique_types:
|
89
|
+
if NoiseProtocol.READOUT in unique_types:
|
90
|
+
raise ValueError("Cannot define a config with both READOUT and Analog noises.")
|
91
|
+
if types.count(NoiseProtocol.ANALOG) > 1:
|
92
|
+
raise ValueError("Multiple Analog Noises are not supported yet.")
|
93
|
+
|
94
|
+
if NoiseProtocol.READOUT in self.protocol:
|
95
|
+
if (
|
96
|
+
self.protocol[-1] != NoiseProtocol.READOUT
|
97
|
+
or self.protocol.count(NoiseProtocol.READOUT) > 1
|
98
|
+
):
|
99
|
+
raise ValueError("Only define a NoiseHandler with one READOUT as the last Noise.")
|
100
|
+
|
101
|
+
def __repr__(self) -> str:
|
102
|
+
return "\n".join(
|
103
|
+
[
|
104
|
+
f"Noise({protocol}, {str(option)})"
|
105
|
+
for protocol, option in zip(self.protocol, self.options)
|
106
|
+
]
|
107
|
+
)
|
108
|
+
|
109
|
+
def get_noise_fn(self, index_protocol: int) -> Callable:
|
23
110
|
try:
|
24
|
-
module = importlib.import_module(PROTOCOL_TO_MODULE[self.protocol])
|
111
|
+
module = importlib.import_module(PROTOCOL_TO_MODULE[self.protocol[index_protocol]])
|
25
112
|
except KeyError:
|
26
|
-
ImportError(
|
113
|
+
ImportError(
|
114
|
+
f"The module for the protocol {self.protocol[index_protocol]} is not found."
|
115
|
+
)
|
27
116
|
fn = getattr(module, "add_noise")
|
28
117
|
return cast(Callable, fn)
|
29
118
|
|
119
|
+
def append(self, other: NoiseHandler | list[NoiseHandler]) -> None:
|
120
|
+
"""Append noises.
|
121
|
+
|
122
|
+
Args:
|
123
|
+
other (NoiseHandler | list[NoiseHandler]): The noises to add.
|
124
|
+
"""
|
125
|
+
# To avoid overwriting the noise_sources list if an error is raised, make a copy
|
126
|
+
other_list = other if isinstance(other, list) else [other]
|
127
|
+
protocols = self.protocol[:]
|
128
|
+
options = self.options[:]
|
129
|
+
|
130
|
+
for noise in other_list:
|
131
|
+
protocols += noise.protocol
|
132
|
+
options += noise.options
|
133
|
+
|
134
|
+
# init may raise an error
|
135
|
+
temp_handler = NoiseHandler(protocols, options)
|
136
|
+
# if verify passes, replace protocols and options
|
137
|
+
self.protocol = temp_handler.protocol
|
138
|
+
self.options = temp_handler.options
|
139
|
+
|
140
|
+
def __eq__(self, other: object) -> bool:
|
141
|
+
if not isinstance(other, NoiseHandler):
|
142
|
+
raise TypeError(f"Cant compare {type(self)} to {type(other)}")
|
143
|
+
if isinstance(other, type(self)):
|
144
|
+
protocols_equal = all([p1 == p2 for p1, p2 in zip(self.protocol, other.protocol)])
|
145
|
+
options_equal = all([o1 == o2 for o1, o2 in zip(self.options, other.options)])
|
146
|
+
return protocols_equal and options_equal
|
147
|
+
|
148
|
+
return False
|
149
|
+
|
30
150
|
def _to_dict(self) -> dict:
|
31
|
-
return {
|
151
|
+
return {
|
152
|
+
"protocol": self.protocol,
|
153
|
+
"options": self.options,
|
154
|
+
}
|
32
155
|
|
33
156
|
@classmethod
|
34
|
-
def _from_dict(cls, d: dict) ->
|
35
|
-
if d:
|
36
|
-
return cls(d["protocol"],
|
157
|
+
def _from_dict(cls, d: dict | None) -> NoiseHandler | None:
|
158
|
+
if d is not None and d.get("protocol", None):
|
159
|
+
return cls(d["protocol"], d["options"])
|
37
160
|
return None
|
38
161
|
|
39
162
|
@classmethod
|
40
163
|
def list(cls) -> list:
|
41
164
|
return list(filter(lambda el: not el.startswith("__"), dir(cls)))
|
42
165
|
|
166
|
+
def filter(self, protocol: NoiseEnum) -> NoiseHandler | None:
|
167
|
+
is_protocol: list = [isinstance(p, protocol) for p in self.protocol] # type: ignore[arg-type]
|
168
|
+
return (
|
169
|
+
NoiseHandler(
|
170
|
+
list(compress(self.protocol, is_protocol)),
|
171
|
+
list(compress(self.options, is_protocol)),
|
172
|
+
)
|
173
|
+
if len(is_protocol) > 0
|
174
|
+
else None
|
175
|
+
)
|
176
|
+
|
177
|
+
def bitflip(self, *args: Any, **kwargs: Any) -> NoiseHandler:
|
178
|
+
self.append(NoiseHandler(NoiseProtocol.DIGITAL.BITFLIP, *args, **kwargs))
|
179
|
+
return self
|
180
|
+
|
181
|
+
def phaseflip(self, *args: Any, **kwargs: Any) -> NoiseHandler:
|
182
|
+
self.append(NoiseHandler(NoiseProtocol.DIGITAL.PHASEFLIP, *args, **kwargs))
|
183
|
+
return self
|
184
|
+
|
185
|
+
def digital_depolarizing(self, *args: Any, **kwargs: Any) -> NoiseHandler:
|
186
|
+
self.append(NoiseHandler(NoiseProtocol.DIGITAL.DEPOLARIZING, *args, **kwargs))
|
187
|
+
return self
|
188
|
+
|
189
|
+
def pauli_channel(self, *args: Any, **kwargs: Any) -> NoiseHandler:
|
190
|
+
self.append(NoiseHandler(NoiseProtocol.DIGITAL.PAULI_CHANNEL, *args, **kwargs))
|
191
|
+
return self
|
192
|
+
|
193
|
+
def amplitude_damping(self, *args: Any, **kwargs: Any) -> NoiseHandler:
|
194
|
+
self.append(NoiseHandler(NoiseProtocol.DIGITAL.AMPLITUDE_DAMPING, *args, **kwargs))
|
195
|
+
return self
|
196
|
+
|
197
|
+
def phase_damping(self, *args: Any, **kwargs: Any) -> NoiseHandler:
|
198
|
+
self.append(NoiseHandler(NoiseProtocol.DIGITAL.PHASE_DAMPING, *args, **kwargs))
|
199
|
+
return self
|
200
|
+
|
201
|
+
def generalized_amplitude_damping(self, *args: Any, **kwargs: Any) -> NoiseHandler:
|
202
|
+
self.append(
|
203
|
+
NoiseHandler(NoiseProtocol.DIGITAL.GENERALIZED_AMPLITUDE_DAMPING, *args, **kwargs)
|
204
|
+
)
|
205
|
+
return self
|
206
|
+
|
207
|
+
def analog_depolarizing(self, *args: Any, **kwargs: Any) -> NoiseHandler:
|
208
|
+
self.append(NoiseHandler(NoiseProtocol.ANALOG.DEPOLARIZING, *args, **kwargs))
|
209
|
+
return self
|
210
|
+
|
211
|
+
def dephasing(self, *args: Any, **kwargs: Any) -> NoiseHandler:
|
212
|
+
self.append(NoiseHandler(NoiseProtocol.ANALOG.DEPHASING, *args, **kwargs))
|
213
|
+
return self
|
214
|
+
|
215
|
+
def readout(self, *args: Any, **kwargs: Any) -> NoiseHandler:
|
216
|
+
self.append(NoiseHandler(NoiseProtocol.READOUT, *args, **kwargs))
|
217
|
+
return self
|
218
|
+
|
219
|
+
|
220
|
+
def apply_readout_noise(noise: NoiseHandler, samples: list[Counter]) -> list[Counter]:
|
221
|
+
"""Apply readout noise to samples if provided.
|
222
|
+
|
223
|
+
Args:
|
224
|
+
noise (NoiseHandler): Noise to apply.
|
225
|
+
samples (list[Counter]): Samples to alter
|
43
226
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
227
|
+
Returns:
|
228
|
+
list[Counter]: Altered samples.
|
229
|
+
"""
|
230
|
+
if noise.protocol[-1] == NoiseProtocol.READOUT:
|
231
|
+
error_fn = noise.get_noise_fn(-1)
|
232
|
+
# Get the number of qubits from the sample keys.
|
233
|
+
n_qubits = len(list(samples[0].keys())[0])
|
234
|
+
# Get the number of shots from the sample values.
|
235
|
+
n_shots = sum(samples[0].values())
|
236
|
+
noisy_samples: list = error_fn(
|
237
|
+
counters=samples, n_qubits=n_qubits, options=noise.options[-1], n_shots=n_shots
|
238
|
+
)
|
239
|
+
return noisy_samples
|
240
|
+
else:
|
241
|
+
return samples
|