qadence 1.7.8__py3-none-any.whl → 1.9.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/__init__.py +1 -1
- qadence/analog/device.py +1 -1
- qadence/analog/parse_analog.py +1 -2
- qadence/backend.py +3 -3
- qadence/backends/gpsr.py +8 -2
- qadence/backends/horqrux/backend.py +3 -3
- qadence/backends/pulser/backend.py +21 -38
- qadence/backends/pulser/convert_ops.py +2 -2
- qadence/backends/pyqtorch/backend.py +85 -10
- qadence/backends/pyqtorch/config.py +10 -3
- qadence/backends/pyqtorch/convert_ops.py +245 -233
- 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 +37 -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 -16
- qadence/extensions.py +1 -1
- qadence/log_config.yaml +10 -0
- qadence/measurements/shadow.py +101 -133
- qadence/measurements/tomography.py +2 -2
- qadence/measurements/utils.py +4 -4
- qadence/mitigations/analog_zne.py +8 -7
- qadence/mitigations/protocols.py +2 -2
- qadence/mitigations/readout.py +14 -5
- qadence/ml_tools/__init__.py +4 -8
- qadence/ml_tools/callbacks/__init__.py +30 -0
- qadence/ml_tools/callbacks/callback.py +451 -0
- qadence/ml_tools/callbacks/callbackmanager.py +214 -0
- qadence/ml_tools/{saveload.py → callbacks/saveload.py} +11 -11
- qadence/ml_tools/callbacks/writer_registry.py +430 -0
- qadence/ml_tools/config.py +132 -258
- qadence/ml_tools/constructors.py +2 -2
- qadence/ml_tools/data.py +7 -3
- qadence/ml_tools/loss/__init__.py +10 -0
- qadence/ml_tools/loss/loss.py +87 -0
- qadence/ml_tools/models.py +7 -7
- qadence/ml_tools/optimize_step.py +45 -10
- qadence/ml_tools/stages.py +46 -0
- qadence/ml_tools/train_utils/__init__.py +7 -0
- qadence/ml_tools/train_utils/base_trainer.py +548 -0
- qadence/ml_tools/train_utils/config_manager.py +184 -0
- qadence/ml_tools/trainer.py +692 -0
- qadence/model.py +6 -6
- qadence/noise/__init__.py +2 -2
- qadence/noise/protocols.py +188 -36
- qadence/operations/control_ops.py +37 -22
- qadence/operations/ham_evo.py +88 -26
- 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 +53 -0
- qadence/types.py +39 -3
- {qadence-1.7.8.dist-info → qadence-1.9.0.dist-info}/METADATA +5 -9
- {qadence-1.7.8.dist-info → qadence-1.9.0.dist-info}/RECORD +67 -63
- {qadence-1.7.8.dist-info → qadence-1.9.0.dist-info}/WHEEL +1 -1
- 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/ml_tools/printing.py +0 -153
- qadence/ml_tools/train_grad.py +0 -395
- qadence/ml_tools/train_no_grad.py +0 -199
- qadence/noise/readout.py +0 -218
- {qadence-1.7.8.dist-info → qadence-1.9.0.dist-info}/licenses/LICENSE +0 -0
qadence/measurements/shadow.py
CHANGED
@@ -1,40 +1,37 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from collections import Counter
|
4
|
-
from functools import reduce
|
5
|
-
|
6
3
|
import numpy as np
|
7
4
|
import torch
|
8
5
|
from torch import Tensor
|
9
6
|
|
10
7
|
from qadence.backend import Backend
|
11
8
|
from qadence.backends.pyqtorch import Backend as PyQBackend
|
12
|
-
from qadence.blocks import AbstractBlock, chain, kron
|
13
|
-
from qadence.blocks.block_to_tensor import HMAT, IMAT, SDAGMAT
|
9
|
+
from qadence.blocks import AbstractBlock, KronBlock, chain, kron
|
10
|
+
from qadence.blocks.block_to_tensor import HMAT, IMAT, SDAGMAT
|
14
11
|
from qadence.blocks.composite import CompositeBlock
|
15
12
|
from qadence.blocks.primitive import PrimitiveBlock
|
16
13
|
from qadence.blocks.utils import get_pauli_blocks, unroll_block_with_scaling
|
17
14
|
from qadence.circuit import QuantumCircuit
|
18
15
|
from qadence.engines.differentiable_backend import DifferentiableBackend
|
19
|
-
from qadence.
|
20
|
-
from qadence.
|
16
|
+
from qadence.measurements.utils import get_qubit_indices_for_op
|
17
|
+
from qadence.noise import NoiseHandler
|
18
|
+
from qadence.operations import H, I, SDagger, X, Y, Z
|
21
19
|
from qadence.types import Endianness
|
22
|
-
from qadence.utils import P0_MATRIX, P1_MATRIX
|
23
20
|
|
24
21
|
pauli_gates = [X, Y, Z]
|
25
|
-
|
22
|
+
pauli_rotations = [
|
23
|
+
lambda index: H(index),
|
24
|
+
lambda index: SDagger(index) * H(index),
|
25
|
+
lambda index: None,
|
26
|
+
]
|
26
27
|
|
27
28
|
UNITARY_TENSOR = [
|
28
|
-
|
29
|
-
|
29
|
+
HMAT,
|
30
|
+
HMAT @ SDAGMAT,
|
30
31
|
IMAT,
|
31
32
|
]
|
32
33
|
|
33
34
|
|
34
|
-
def identity(n_qubits: int) -> Tensor:
|
35
|
-
return torch.eye(2**n_qubits, dtype=torch.complex128)
|
36
|
-
|
37
|
-
|
38
35
|
def _max_observable_weight(observable: AbstractBlock) -> int:
|
39
36
|
"""
|
40
37
|
Get the maximal weight for the given observable.
|
@@ -88,27 +85,40 @@ def number_of_samples(
|
|
88
85
|
return N, K
|
89
86
|
|
90
87
|
|
91
|
-
def
|
88
|
+
def nested_operator_indexing(
|
89
|
+
idx_array: np.ndarray,
|
90
|
+
) -> list:
|
91
|
+
"""Obtain the list of rotation operators from indices.
|
92
|
+
|
93
|
+
Args:
|
94
|
+
idx_array (np.ndarray): Indices for obtaining the operators.
|
95
|
+
|
96
|
+
Returns:
|
97
|
+
list: Map of rotations.
|
92
98
|
"""
|
93
|
-
|
99
|
+
if idx_array.ndim == 1:
|
100
|
+
return [pauli_rotations[int(ind_pauli)](i) for i, ind_pauli in enumerate(idx_array)] # type: ignore[abstract]
|
101
|
+
return [nested_operator_indexing(sub_array) for sub_array in idx_array]
|
102
|
+
|
103
|
+
|
104
|
+
def kron_if_non_empty(list_operations: list) -> KronBlock | None:
|
105
|
+
filtered_op: list = list(filter(None, list_operations))
|
106
|
+
return kron(*filtered_op) if len(filtered_op) > 0 else None
|
94
107
|
|
95
|
-
See https://arxiv.org/pdf/2002.08953.pdf
|
96
|
-
Supplementary Material 1 and Eqs. (S17,S44).
|
97
108
|
|
98
|
-
|
109
|
+
def extract_operators(unitary_ids: np.ndarray, n_qubits: int) -> list:
|
110
|
+
"""Sample `shadow_size` rotations of `n_qubits`.
|
111
|
+
|
112
|
+
Args:
|
113
|
+
unitary_ids (np.ndarray): Indices for obtaining the operators.
|
114
|
+
n_qubits (int): Number of qubits
|
115
|
+
Returns:
|
116
|
+
list: Pauli strings.
|
99
117
|
"""
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
unitary_tensor = UNITARY_TENSOR[unitary_id].squeeze(dim=0)
|
105
|
-
local_density_matrices.append(
|
106
|
-
3 * (unitary_tensor.adjoint() @ proj_mat @ unitary_tensor) - identity(1)
|
107
|
-
)
|
108
|
-
if len(local_density_matrices) == 1:
|
109
|
-
return local_density_matrices[0]
|
110
|
-
else:
|
111
|
-
return reduce(torch.kron, local_density_matrices)
|
118
|
+
operations = nested_operator_indexing(unitary_ids)
|
119
|
+
if n_qubits > 1:
|
120
|
+
operations = [kron_if_non_empty(ops) for ops in operations]
|
121
|
+
return operations
|
112
122
|
|
113
123
|
|
114
124
|
def classical_shadow(
|
@@ -117,28 +127,23 @@ def classical_shadow(
|
|
117
127
|
param_values: dict,
|
118
128
|
state: Tensor | None = None,
|
119
129
|
backend: Backend | DifferentiableBackend = PyQBackend(),
|
120
|
-
|
121
|
-
noise: Noise | None = None,
|
130
|
+
noise: NoiseHandler | None = None,
|
122
131
|
endianness: Endianness = Endianness.BIG,
|
123
|
-
) -> list:
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
132
|
+
) -> tuple[np.ndarray, list[Tensor]]:
|
133
|
+
unitary_ids = np.random.randint(0, 3, size=(shadow_size, circuit.n_qubits))
|
134
|
+
shadow: list = list()
|
135
|
+
all_rotations = extract_operators(unitary_ids, circuit.n_qubits)
|
136
|
+
|
137
|
+
for i in range(shadow_size):
|
138
|
+
if all_rotations[i]:
|
139
|
+
rotated_circuit = QuantumCircuit(
|
140
|
+
circuit.register, chain(circuit.block, all_rotations[i])
|
141
|
+
)
|
133
142
|
else:
|
134
|
-
|
135
|
-
rotated_circuit = QuantumCircuit(
|
136
|
-
circuit.n_qubits,
|
137
|
-
chain(circuit.block, random_unitary_block),
|
138
|
-
)
|
143
|
+
rotated_circuit = circuit
|
139
144
|
# Reverse endianness to get sample bitstrings in ILO.
|
140
145
|
conv_circ = backend.circuit(rotated_circuit)
|
141
|
-
|
146
|
+
batch_samples = backend.sample(
|
142
147
|
circuit=conv_circ,
|
143
148
|
param_values=param_values,
|
144
149
|
n_shots=1,
|
@@ -146,97 +151,61 @@ def classical_shadow(
|
|
146
151
|
noise=noise,
|
147
152
|
endianness=endianness,
|
148
153
|
)
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
def reconstruct_state(shadow: list) -> Tensor:
|
160
|
-
"""Reconstruct the state density matrix for the given shadow."""
|
161
|
-
return reduce(torch.add, shadow) / len(shadow)
|
162
|
-
|
163
|
-
|
164
|
-
def compute_traces(
|
165
|
-
qubit_support: tuple,
|
166
|
-
N: int,
|
167
|
-
K: int,
|
168
|
-
shadow: list,
|
169
|
-
observable: AbstractBlock,
|
170
|
-
endianness: Endianness = Endianness.BIG,
|
171
|
-
) -> list:
|
172
|
-
floor = int(np.floor(N / K))
|
173
|
-
traces = []
|
174
|
-
# TODO: Parallelize embarrassingly parallel loop.
|
175
|
-
for k in range(K):
|
176
|
-
reconstructed_state = reconstruct_state(shadow=shadow[k * floor : (k + 1) * floor])
|
177
|
-
# Reshape the observable matrix to fit the density matrix dimensions
|
178
|
-
# by filling indentites.
|
179
|
-
# Please note the endianness is also flipped to get results in LE.
|
180
|
-
# FIXME: Changed below from Little to Big, double-check when Roland is back
|
181
|
-
# FIXME: Correct these comments.
|
182
|
-
trace = (
|
183
|
-
(
|
184
|
-
block_to_tensor(
|
185
|
-
block=observable,
|
186
|
-
qubit_support=qubit_support,
|
187
|
-
endianness=Endianness.BIG,
|
188
|
-
).squeeze(dim=0)
|
189
|
-
@ reconstructed_state
|
190
|
-
)
|
191
|
-
.trace()
|
192
|
-
.real
|
193
|
-
)
|
194
|
-
traces.append(trace)
|
195
|
-
return traces
|
154
|
+
shadow.append(batch_samples)
|
155
|
+
bitstrings = list()
|
156
|
+
batchsize = len(batch_samples)
|
157
|
+
for b in range(batchsize):
|
158
|
+
bitstrings.append([list(batch[b].keys())[0] for batch in shadow])
|
159
|
+
bitstrings_torch = [
|
160
|
+
1 - 2 * torch.stack([torch.tensor([int(b_i) for b_i in sample]) for sample in batch])
|
161
|
+
for batch in bitstrings
|
162
|
+
]
|
163
|
+
return unitary_ids, bitstrings_torch
|
196
164
|
|
197
165
|
|
198
166
|
def estimators(
|
199
|
-
qubit_support: tuple,
|
200
167
|
N: int,
|
201
168
|
K: int,
|
202
|
-
|
169
|
+
unitary_shadow_ids: np.ndarray,
|
170
|
+
shadow_samples: Tensor,
|
203
171
|
observable: AbstractBlock,
|
204
|
-
endianness: Endianness = Endianness.BIG,
|
205
172
|
) -> Tensor:
|
206
173
|
"""
|
207
|
-
Return estimators
|
208
|
-
|
209
|
-
for K equally-sized shadow partitions.
|
174
|
+
Return trace estimators from the samples for K equally-sized shadow partitions.
|
210
175
|
|
211
176
|
See https://arxiv.org/pdf/2002.08953.pdf
|
212
177
|
Algorithm 1.
|
213
178
|
"""
|
214
|
-
|
215
|
-
|
179
|
+
|
180
|
+
obs_qubit_support = observable.qubit_support
|
216
181
|
if isinstance(observable, PrimitiveBlock):
|
217
|
-
if
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
K=K,
|
222
|
-
shadow=shadow,
|
223
|
-
observable=observable,
|
224
|
-
endianness=endianness,
|
225
|
-
)
|
226
|
-
else:
|
227
|
-
traces = [torch.tensor(0.0)]
|
182
|
+
if isinstance(observable, I):
|
183
|
+
return torch.tensor(1.0, dtype=torch.get_default_dtype())
|
184
|
+
obs_to_pauli_index = [pauli_gates.index(type(observable))]
|
185
|
+
|
228
186
|
elif isinstance(observable, CompositeBlock):
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
187
|
+
obs_to_pauli_index = [
|
188
|
+
pauli_gates.index(type(p)) for p in observable.blocks if not isinstance(p, I) # type: ignore[arg-type]
|
189
|
+
]
|
190
|
+
ind_I = set(get_qubit_indices_for_op((observable, 1.0), I(0)))
|
191
|
+
obs_qubit_support = tuple([ind for ind in observable.qubit_support if ind not in ind_I])
|
192
|
+
|
193
|
+
floor = int(np.floor(N / K))
|
194
|
+
traces = []
|
195
|
+
for k in range(K):
|
196
|
+
indices_match = np.all(
|
197
|
+
unitary_shadow_ids[k * floor : (k + 1) * floor, obs_qubit_support]
|
198
|
+
== obs_to_pauli_index,
|
199
|
+
axis=1,
|
200
|
+
)
|
201
|
+
if indices_match.sum() > 0:
|
202
|
+
trace = torch.prod(
|
203
|
+
shadow_samples[k * floor : (k + 1) * floor][indices_match][:, obs_qubit_support],
|
204
|
+
axis=-1,
|
205
|
+
).sum() / sum(indices_match)
|
206
|
+
traces.append(trace)
|
238
207
|
else:
|
239
|
-
traces
|
208
|
+
traces.append(torch.tensor(0.0))
|
240
209
|
return torch.tensor(traces, dtype=torch.get_default_dtype())
|
241
210
|
|
242
211
|
|
@@ -249,7 +218,7 @@ def estimations(
|
|
249
218
|
confidence: float = 0.1,
|
250
219
|
state: Tensor | None = None,
|
251
220
|
backend: Backend | DifferentiableBackend = PyQBackend(),
|
252
|
-
noise:
|
221
|
+
noise: NoiseHandler | None = None,
|
253
222
|
endianness: Endianness = Endianness.BIG,
|
254
223
|
) -> Tensor:
|
255
224
|
"""Compute expectation values for all local observables using median of means."""
|
@@ -259,7 +228,7 @@ def estimations(
|
|
259
228
|
N, K = number_of_samples(observables=observables, accuracy=accuracy, confidence=confidence)
|
260
229
|
if shadow_size is not None:
|
261
230
|
N = shadow_size
|
262
|
-
|
231
|
+
unitaries_ids, batch_shadow_samples = classical_shadow(
|
263
232
|
shadow_size=N,
|
264
233
|
circuit=circuit,
|
265
234
|
param_values=param_values,
|
@@ -272,18 +241,17 @@ def estimations(
|
|
272
241
|
for observable in observables:
|
273
242
|
pauli_decomposition = unroll_block_with_scaling(observable)
|
274
243
|
batch_estimations = []
|
275
|
-
for batch in
|
244
|
+
for batch in batch_shadow_samples:
|
276
245
|
pauli_term_estimations = []
|
277
246
|
for pauli_term in pauli_decomposition:
|
278
247
|
# Get the estimators for the current Pauli term.
|
279
248
|
# This is a tensor<float> of size K.
|
280
249
|
estimation = estimators(
|
281
|
-
qubit_support=circuit.block.qubit_support,
|
282
250
|
N=N,
|
283
251
|
K=K,
|
284
|
-
|
252
|
+
unitary_shadow_ids=unitaries_ids,
|
253
|
+
shadow_samples=batch,
|
285
254
|
observable=pauli_term[0],
|
286
|
-
endianness=endianness,
|
287
255
|
)
|
288
256
|
# Compute the median of means for the current Pauli term.
|
289
257
|
# Weigh the median by the Pauli term scaling.
|
@@ -302,7 +270,7 @@ def compute_expectation(
|
|
302
270
|
options: dict,
|
303
271
|
state: Tensor | None = None,
|
304
272
|
backend: Backend | DifferentiableBackend = PyQBackend(),
|
305
|
-
noise:
|
273
|
+
noise: NoiseHandler | None = None,
|
306
274
|
endianness: Endianness = Endianness.BIG,
|
307
275
|
) -> Tensor:
|
308
276
|
"""
|
@@ -10,7 +10,7 @@ from qadence.blocks.utils import unroll_block_with_scaling
|
|
10
10
|
from qadence.circuit import QuantumCircuit
|
11
11
|
from qadence.engines.differentiable_backend import DifferentiableBackend
|
12
12
|
from qadence.measurements.utils import iterate_pauli_decomposition
|
13
|
-
from qadence.noise import
|
13
|
+
from qadence.noise import NoiseHandler
|
14
14
|
from qadence.utils import Endianness
|
15
15
|
|
16
16
|
|
@@ -21,7 +21,7 @@ def compute_expectation(
|
|
21
21
|
options: dict,
|
22
22
|
state: Tensor | None = None,
|
23
23
|
backend: Backend | DifferentiableBackend = PyQBackend(),
|
24
|
-
noise:
|
24
|
+
noise: NoiseHandler | None = None,
|
25
25
|
endianness: Endianness = Endianness.BIG,
|
26
26
|
) -> Tensor:
|
27
27
|
"""Basic tomography protocol with rotations.
|
qadence/measurements/utils.py
CHANGED
@@ -14,8 +14,8 @@ from qadence.backends.pyqtorch import Backend as PyQBackend
|
|
14
14
|
from qadence.blocks import AbstractBlock, PrimitiveBlock, chain
|
15
15
|
from qadence.circuit import QuantumCircuit
|
16
16
|
from qadence.engines.differentiable_backend import DifferentiableBackend
|
17
|
-
from qadence.noise import
|
18
|
-
from qadence.operations import H, SDagger, X, Y
|
17
|
+
from qadence.noise import NoiseHandler
|
18
|
+
from qadence.operations import H, I, SDagger, X, Y
|
19
19
|
from qadence.parameters import evaluate
|
20
20
|
from qadence.utils import Endianness
|
21
21
|
|
@@ -113,7 +113,7 @@ def rotate(circuit: QuantumCircuit, pauli_term: Tuple[AbstractBlock, Basic]) ->
|
|
113
113
|
|
114
114
|
rotations = []
|
115
115
|
|
116
|
-
for op, gate in [(X(0),
|
116
|
+
for op, gate in [(X(0), I), (Y(0), SDagger)]:
|
117
117
|
qubit_indices = get_qubit_indices_for_op(pauli_term, op=op)
|
118
118
|
for index in qubit_indices:
|
119
119
|
rotations.append(gate(index) * H(index))
|
@@ -128,7 +128,7 @@ def iterate_pauli_decomposition(
|
|
128
128
|
n_shots: int,
|
129
129
|
state: Tensor | None = None,
|
130
130
|
backend: Backend | DifferentiableBackend = PyQBackend(),
|
131
|
-
noise:
|
131
|
+
noise: NoiseHandler | None = None,
|
132
132
|
endianness: Endianness = Endianness.BIG,
|
133
133
|
) -> Tensor:
|
134
134
|
"""Estimate total expectation value by averaging all Pauli terms.
|
@@ -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
@@ -10,8 +10,9 @@ from numpy.linalg import inv, matrix_rank, pinv
|
|
10
10
|
from scipy.linalg import norm
|
11
11
|
from scipy.optimize import LinearConstraint, minimize
|
12
12
|
|
13
|
+
from qadence.backends.pyqtorch.convert_ops import convert_readout_noise
|
13
14
|
from qadence.mitigations.protocols import Mitigations
|
14
|
-
from qadence.noise.protocols import
|
15
|
+
from qadence.noise.protocols import NoiseHandler
|
15
16
|
from qadence.types import ReadOutOptimization
|
16
17
|
|
17
18
|
|
@@ -69,7 +70,7 @@ def matrix_inv(K: npt.NDArray) -> npt.NDArray:
|
|
69
70
|
|
70
71
|
|
71
72
|
def mitigation_minimization(
|
72
|
-
noise:
|
73
|
+
noise: NoiseHandler,
|
73
74
|
mitigation: Mitigations,
|
74
75
|
samples: list[Counter],
|
75
76
|
) -> list[Counter]:
|
@@ -88,10 +89,18 @@ def mitigation_minimization(
|
|
88
89
|
Returns:
|
89
90
|
Mitigated counts computed by the algorithm
|
90
91
|
"""
|
91
|
-
|
92
|
-
optimization_type = mitigation.options.get("optimization_type", ReadOutOptimization.MLE)
|
92
|
+
|
93
93
|
n_qubits = len(list(samples[0].keys())[0])
|
94
|
+
readout_noise = convert_readout_noise(n_qubits, noise)
|
95
|
+
if readout_noise is None:
|
96
|
+
raise ValueError("Specify a noise source of type NoiseProtocol.READOUT.")
|
94
97
|
n_shots = sum(samples[0].values())
|
98
|
+
noise_matrices = readout_noise.confusion_matrix
|
99
|
+
if noise_matrices.numel() == 0:
|
100
|
+
readout_noise.create_noise_matrix(n_shots)
|
101
|
+
noise_matrices = readout_noise.confusion_matrix
|
102
|
+
optimization_type = mitigation.options.get("optimization_type", ReadOutOptimization.MLE)
|
103
|
+
|
95
104
|
corrected_counters: list[Counter] = []
|
96
105
|
|
97
106
|
if optimization_type == ReadOutOptimization.CONSTRAINED:
|
@@ -156,5 +165,5 @@ def mitigation_minimization(
|
|
156
165
|
return corrected_counters
|
157
166
|
|
158
167
|
|
159
|
-
def mitigate(noise:
|
168
|
+
def mitigate(noise: NoiseHandler, mitigation: Mitigations, samples: list[Counter]) -> list[Counter]:
|
160
169
|
return mitigation_minimization(noise=noise, mitigation=mitigation, samples=samples)
|
qadence/ml_tools/__init__.py
CHANGED
@@ -1,16 +1,14 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
|
3
|
-
from .
|
3
|
+
from .callbacks.saveload import load_checkpoint, load_model, write_checkpoint
|
4
|
+
from .config import AnsatzConfig, FeatureMapConfig, TrainConfig
|
4
5
|
from .constructors import create_ansatz, create_fm_blocks, observable_from_config
|
5
6
|
from .data import DictDataLoader, InfiniteTensorDataset, OptimizeResult, to_dataloader
|
6
7
|
from .models import QNN
|
7
8
|
from .optimize_step import optimize_step as default_optimize_step
|
8
9
|
from .parameters import get_parameters, num_parameters, set_parameters
|
9
|
-
from .printing import print_metrics, write_tensorboard
|
10
|
-
from .saveload import load_checkpoint, load_model, write_checkpoint
|
11
10
|
from .tensors import numpy_to_tensor, promote_to, promote_to_tensor
|
12
|
-
from .
|
13
|
-
from .train_no_grad import train as train_gradient_free
|
11
|
+
from .trainer import Trainer
|
14
12
|
|
15
13
|
# Modules to be automatically added to the qadence namespace
|
16
14
|
__all__ = [
|
@@ -24,8 +22,6 @@ __all__ = [
|
|
24
22
|
"QNN",
|
25
23
|
"TrainConfig",
|
26
24
|
"OptimizeResult",
|
27
|
-
"
|
28
|
-
"train_with_grad",
|
29
|
-
"train_gradient_free",
|
25
|
+
"Trainer",
|
30
26
|
"write_checkpoint",
|
31
27
|
]
|
@@ -0,0 +1,30 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
from .callback import (
|
4
|
+
Callback,
|
5
|
+
LoadCheckpoint,
|
6
|
+
LogHyperparameters,
|
7
|
+
LogModelTracker,
|
8
|
+
PlotMetrics,
|
9
|
+
PrintMetrics,
|
10
|
+
SaveBestCheckpoint,
|
11
|
+
SaveCheckpoint,
|
12
|
+
WriteMetrics,
|
13
|
+
)
|
14
|
+
from .callbackmanager import CallbacksManager
|
15
|
+
from .writer_registry import get_writer
|
16
|
+
|
17
|
+
# Modules to be automatically added to the qadence.ml_tools.callbacks namespace
|
18
|
+
__all__ = [
|
19
|
+
"CallbacksManager",
|
20
|
+
"Callback",
|
21
|
+
"LoadCheckpoint",
|
22
|
+
"LogHyperparameters",
|
23
|
+
"LogModelTracker",
|
24
|
+
"PlotMetrics",
|
25
|
+
"PrintMetrics",
|
26
|
+
"SaveBestCheckpoint",
|
27
|
+
"SaveCheckpoint",
|
28
|
+
"WriteMetrics",
|
29
|
+
"get_writer",
|
30
|
+
]
|