qadence 1.7.7__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.
Files changed (53) hide show
  1. qadence/analog/device.py +1 -1
  2. qadence/backend.py +3 -3
  3. qadence/backends/horqrux/backend.py +3 -3
  4. qadence/backends/pulser/backend.py +16 -17
  5. qadence/backends/pulser/convert_ops.py +2 -2
  6. qadence/backends/pyqtorch/backend.py +7 -7
  7. qadence/backends/pyqtorch/convert_ops.py +191 -240
  8. qadence/backends/utils.py +9 -1
  9. qadence/blocks/abstract.py +1 -1
  10. qadence/blocks/embedding.py +21 -11
  11. qadence/blocks/matrix.py +3 -1
  12. qadence/blocks/primitive.py +36 -11
  13. qadence/circuit.py +1 -1
  14. qadence/constructors/__init__.py +2 -1
  15. qadence/constructors/ansatze.py +176 -0
  16. qadence/engines/differentiable_backend.py +3 -3
  17. qadence/engines/jax/differentiable_backend.py +2 -2
  18. qadence/engines/jax/differentiable_expectation.py +2 -2
  19. qadence/engines/torch/differentiable_backend.py +2 -2
  20. qadence/engines/torch/differentiable_expectation.py +2 -2
  21. qadence/execution.py +14 -14
  22. qadence/extensions.py +1 -1
  23. qadence/measurements/shadow.py +4 -5
  24. qadence/measurements/tomography.py +2 -2
  25. qadence/measurements/utils.py +2 -2
  26. qadence/mitigations/analog_zne.py +8 -7
  27. qadence/mitigations/protocols.py +2 -2
  28. qadence/mitigations/readout.py +8 -5
  29. qadence/ml_tools/config.py +14 -0
  30. qadence/ml_tools/constructors.py +9 -4
  31. qadence/ml_tools/models.py +7 -7
  32. qadence/ml_tools/printing.py +2 -1
  33. qadence/ml_tools/train_grad.py +39 -7
  34. qadence/model.py +5 -5
  35. qadence/noise/__init__.py +2 -2
  36. qadence/noise/protocols.py +216 -29
  37. qadence/operations/control_ops.py +37 -22
  38. qadence/operations/ham_evo.py +1 -0
  39. qadence/operations/parametric.py +32 -10
  40. qadence/operations/primitive.py +61 -29
  41. qadence/overlap.py +0 -6
  42. qadence/parameters.py +3 -2
  43. qadence/transpile/__init__.py +2 -1
  44. qadence/transpile/noise.py +46 -0
  45. qadence/types.py +26 -2
  46. {qadence-1.7.7.dist-info → qadence-1.8.0.dist-info}/METADATA +5 -8
  47. {qadence-1.7.7.dist-info → qadence-1.8.0.dist-info}/RECORD +49 -52
  48. qadence/backends/braket/__init__.py +0 -4
  49. qadence/backends/braket/backend.py +0 -234
  50. qadence/backends/braket/config.py +0 -22
  51. qadence/backends/braket/convert_ops.py +0 -116
  52. {qadence-1.7.7.dist-info → qadence-1.8.0.dist-info}/WHEEL +0 -0
  53. {qadence-1.7.7.dist-info → qadence-1.8.0.dist-info}/licenses/LICENSE +0 -0
@@ -18,6 +18,7 @@ from qadence.blocks.utils import (
18
18
  chain,
19
19
  kron,
20
20
  )
21
+ from qadence.noise import NoiseHandler
21
22
  from qadence.parameters import (
22
23
  Parameter,
23
24
  evaluate,
@@ -35,9 +36,9 @@ class CNOT(ControlBlock):
35
36
 
36
37
  name = OpName.CNOT
37
38
 
38
- def __init__(self, control: int, target: int) -> None:
39
+ def __init__(self, control: int, target: int, noise: NoiseHandler | None = None) -> None:
39
40
  self.generator = kron(N(control), X(target) - I(target))
40
- super().__init__((control,), X(target))
41
+ super().__init__((control,), X(target), noise=noise)
41
42
 
42
43
  @property
43
44
  def eigenvalues_generator(self) -> Tensor:
@@ -63,9 +64,11 @@ class CNOT(ControlBlock):
63
64
  class MCZ(ControlBlock):
64
65
  name = OpName.MCZ
65
66
 
66
- def __init__(self, control: tuple[int, ...], target: int) -> None:
67
+ def __init__(
68
+ self, control: tuple[int, ...], target: int, noise: NoiseHandler | None = None
69
+ ) -> None:
67
70
  self.generator = kron(*[N(qubit) for qubit in control], Z(target) - I(target))
68
- super().__init__(control, Z(target))
71
+ super().__init__(control, Z(target), noise=noise)
69
72
 
70
73
  @property
71
74
  def eigenvalues_generator(self) -> Tensor:
@@ -93,8 +96,8 @@ class CZ(MCZ):
93
96
 
94
97
  name = OpName.CZ
95
98
 
96
- def __init__(self, control: int, target: int) -> None:
97
- super().__init__((control,), target)
99
+ def __init__(self, control: int, target: int, noise: NoiseHandler | None = None) -> None:
100
+ super().__init__((control,), target, noise=noise)
98
101
 
99
102
 
100
103
  class MCRX(ParametricControlBlock):
@@ -105,9 +108,10 @@ class MCRX(ParametricControlBlock):
105
108
  control: tuple[int, ...],
106
109
  target: int,
107
110
  parameter: Parameter | TNumber | sympy.Expr | str,
111
+ noise: NoiseHandler | None = None,
108
112
  ) -> None:
109
113
  self.generator = kron(*[N(qubit) for qubit in control], X(target))
110
- super().__init__(control, RX(target, parameter))
114
+ super().__init__(control, RX(target, parameter), noise=noise)
111
115
 
112
116
  @classmethod
113
117
  def num_parameters(cls) -> int:
@@ -136,8 +140,9 @@ class CRX(MCRX):
136
140
  control: int,
137
141
  target: int,
138
142
  parameter: Parameter | TNumber | sympy.Expr | str,
143
+ noise: NoiseHandler | None = None,
139
144
  ):
140
- super().__init__((control,), target, parameter)
145
+ super().__init__((control,), target, parameter, noise=noise)
141
146
 
142
147
 
143
148
  class MCRY(ParametricControlBlock):
@@ -148,9 +153,10 @@ class MCRY(ParametricControlBlock):
148
153
  control: tuple[int, ...],
149
154
  target: int,
150
155
  parameter: Parameter | TNumber | sympy.Expr | str,
156
+ noise: NoiseHandler | None = None,
151
157
  ) -> None:
152
158
  self.generator = kron(*[N(qubit) for qubit in control], Y(target))
153
- super().__init__(control, RY(target, parameter))
159
+ super().__init__(control, RY(target, parameter), noise=noise)
154
160
 
155
161
  @classmethod
156
162
  def num_parameters(cls) -> int:
@@ -175,12 +181,9 @@ class CRY(MCRY):
175
181
  name = OpName.CRY
176
182
 
177
183
  def __init__(
178
- self,
179
- control: int,
180
- target: int,
181
- parameter: TParameter,
184
+ self, control: int, target: int, parameter: TParameter, noise: NoiseHandler | None = None
182
185
  ):
183
- super().__init__((control,), target, parameter)
186
+ super().__init__((control,), target, parameter, noise=noise)
184
187
 
185
188
 
186
189
  class MCRZ(ParametricControlBlock):
@@ -191,9 +194,10 @@ class MCRZ(ParametricControlBlock):
191
194
  control: tuple[int, ...],
192
195
  target: int,
193
196
  parameter: Parameter | TNumber | sympy.Expr | str,
197
+ noise: NoiseHandler | None = None,
194
198
  ) -> None:
195
199
  self.generator = kron(*[N(qubit) for qubit in control], Z(target))
196
- super().__init__(control, RZ(target, parameter))
200
+ super().__init__(control, RZ(target, parameter), noise=noise)
197
201
 
198
202
  @classmethod
199
203
  def num_parameters(cls) -> int:
@@ -222,8 +226,9 @@ class CRZ(MCRZ):
222
226
  control: int,
223
227
  target: int,
224
228
  parameter: Parameter | TNumber | sympy.Expr | str,
229
+ noise: NoiseHandler | None = None,
225
230
  ):
226
- super().__init__((control,), target, parameter)
231
+ super().__init__((control,), target, parameter, noise=noise)
227
232
 
228
233
 
229
234
  class MCPHASE(ParametricControlBlock):
@@ -234,9 +239,10 @@ class MCPHASE(ParametricControlBlock):
234
239
  control: tuple[int, ...],
235
240
  target: int,
236
241
  parameter: Parameter | TNumber | sympy.Expr | str,
242
+ noise: NoiseHandler | None = None,
237
243
  ) -> None:
238
244
  self.generator = kron(*[N(qubit) for qubit in control], Z(target) - I(target))
239
- super().__init__(control, PHASE(target, parameter))
245
+ super().__init__(control, PHASE(target, parameter), noise=noise)
240
246
 
241
247
  @classmethod
242
248
  def num_parameters(cls) -> int:
@@ -276,8 +282,9 @@ class CPHASE(MCPHASE):
276
282
  control: int,
277
283
  target: int,
278
284
  parameter: Parameter | TNumber | sympy.Expr | str,
285
+ noise: NoiseHandler | None = None,
279
286
  ):
280
- super().__init__((control,), target, parameter)
287
+ super().__init__((control,), target, parameter, noise=noise)
281
288
 
282
289
 
283
290
  class CSWAP(ControlBlock):
@@ -285,7 +292,13 @@ class CSWAP(ControlBlock):
285
292
 
286
293
  name = OpName.CSWAP
287
294
 
288
- def __init__(self, control: int | tuple[int, ...], target1: int, target2: int) -> None:
295
+ def __init__(
296
+ self,
297
+ control: int | tuple[int, ...],
298
+ target1: int,
299
+ target2: int,
300
+ noise: NoiseHandler | None = None,
301
+ ) -> None:
289
302
  if isinstance(control, tuple):
290
303
  control = control[0]
291
304
 
@@ -303,7 +316,7 @@ class CSWAP(ControlBlock):
303
316
  + kron(a00p, a21, a12)
304
317
  )
305
318
  self.generator = no_effect + swap_effect
306
- super().__init__((control,), SWAP(target1, target2))
319
+ super().__init__((control,), SWAP(target1, target2), noise=noise)
307
320
 
308
321
  @property
309
322
  def eigenvalues_generator(self) -> Tensor:
@@ -321,9 +334,11 @@ class CSWAP(ControlBlock):
321
334
  class Toffoli(ControlBlock):
322
335
  name = OpName.TOFFOLI
323
336
 
324
- def __init__(self, control: tuple[int, ...], target: int) -> None:
337
+ def __init__(
338
+ self, control: tuple[int, ...], target: int, noise: NoiseHandler | None = None
339
+ ) -> None:
325
340
  self.generator = kron(*[N(qubit) for qubit in control], X(target) - I(target))
326
- super().__init__(control, X(target))
341
+ super().__init__(control, X(target), noise=noise)
327
342
 
328
343
  @property
329
344
  def n_qubits(self) -> int:
@@ -104,6 +104,7 @@ class HamEvo(TimeEvolutionBlock):
104
104
  )
105
105
  ps = {"parameter": Parameter(parameter), **gen_exprs}
106
106
  self.parameters = ParamMap(**ps)
107
+ self.time_param = parameter
107
108
  self.generator = generator
108
109
  self.duration = duration
109
110
 
@@ -15,6 +15,7 @@ from qadence.blocks.utils import (
15
15
  add, # noqa
16
16
  chain,
17
17
  )
18
+ from qadence.noise import NoiseHandler
18
19
  from qadence.parameters import (
19
20
  Parameter,
20
21
  ParamMap,
@@ -32,10 +33,15 @@ class PHASE(ParametricBlock):
32
33
 
33
34
  name = OpName.PHASE
34
35
 
35
- def __init__(self, target: int, parameter: Parameter | TNumber | sympy.Expr | str):
36
+ def __init__(
37
+ self,
38
+ target: int,
39
+ parameter: Parameter | TNumber | sympy.Expr | str,
40
+ noise: NoiseHandler | None = None,
41
+ ) -> None:
36
42
  self.parameters = ParamMap(parameter=parameter)
37
43
  self.generator = I(target) - Z(target)
38
- super().__init__((target,))
44
+ super().__init__((target,), noise=noise)
39
45
 
40
46
  @classmethod
41
47
  def num_parameters(cls) -> int:
@@ -56,13 +62,18 @@ class RX(ParametricBlock):
56
62
 
57
63
  name = OpName.RX
58
64
 
59
- def __init__(self, target: int, parameter: Parameter | TParameter | ParamMap):
65
+ def __init__(
66
+ self,
67
+ target: int,
68
+ parameter: Parameter | TParameter | ParamMap,
69
+ noise: NoiseHandler | None = None,
70
+ ) -> None:
60
71
  # TODO: should we give them more meaningful names? like 'angle'?
61
72
  self.parameters = (
62
73
  parameter if isinstance(parameter, ParamMap) else ParamMap(parameter=parameter)
63
74
  )
64
75
  self.generator = X(target)
65
- super().__init__((target,))
76
+ super().__init__((target,), noise=noise)
66
77
 
67
78
  @classmethod
68
79
  def num_parameters(cls) -> int:
@@ -84,12 +95,17 @@ class RY(ParametricBlock):
84
95
 
85
96
  name = OpName.RY
86
97
 
87
- def __init__(self, target: int, parameter: Parameter | TParameter | ParamMap):
98
+ def __init__(
99
+ self,
100
+ target: int,
101
+ parameter: Parameter | TParameter | ParamMap,
102
+ noise: NoiseHandler | None = None,
103
+ ) -> None:
88
104
  self.parameters = (
89
105
  parameter if isinstance(parameter, ParamMap) else ParamMap(parameter=parameter)
90
106
  )
91
107
  self.generator = Y(target)
92
- super().__init__((target,))
108
+ super().__init__((target,), noise=noise)
93
109
 
94
110
  @classmethod
95
111
  def num_parameters(cls) -> int:
@@ -111,12 +127,17 @@ class RZ(ParametricBlock):
111
127
 
112
128
  name = OpName.RZ
113
129
 
114
- def __init__(self, target: int, parameter: Parameter | TParameter | ParamMap):
130
+ def __init__(
131
+ self,
132
+ target: int,
133
+ parameter: Parameter | TParameter | ParamMap,
134
+ noise: NoiseHandler | None = None,
135
+ ) -> None:
115
136
  self.parameters = (
116
137
  parameter if isinstance(parameter, ParamMap) else ParamMap(parameter=parameter)
117
138
  )
118
139
  self.generator = Z(target)
119
- super().__init__((target,))
140
+ super().__init__((target,), noise=noise)
120
141
 
121
142
  @classmethod
122
143
  def num_parameters(cls) -> int:
@@ -147,10 +168,11 @@ class U(ParametricBlock):
147
168
  phi: Parameter | TParameter,
148
169
  theta: Parameter | TParameter,
149
170
  omega: Parameter | TParameter,
150
- ):
171
+ noise: NoiseHandler | None = None,
172
+ ) -> None:
151
173
  self.parameters = ParamMap(phi=phi, theta=theta, omega=omega)
152
174
  self.generator = chain(Z(target), Y(target), Z(target))
153
- super().__init__((target,))
175
+ super().__init__((target,), noise=noise)
154
176
 
155
177
  @classmethod
156
178
  def num_parameters(cls) -> int:
@@ -18,6 +18,7 @@ from qadence.blocks.utils import (
18
18
  chain,
19
19
  kron,
20
20
  )
21
+ from qadence.noise import NoiseHandler
21
22
  from qadence.parameters import (
22
23
  Parameter,
23
24
  )
@@ -31,8 +32,8 @@ class X(PrimitiveBlock):
31
32
 
32
33
  name = OpName.X
33
34
 
34
- def __init__(self, target: int):
35
- super().__init__((target,))
35
+ def __init__(self, target: int, noise: NoiseHandler | None = None) -> None:
36
+ super().__init__((target,), noise=noise)
36
37
 
37
38
  @property
38
39
  def generator(self) -> AbstractBlock:
@@ -52,8 +53,8 @@ class Y(PrimitiveBlock):
52
53
 
53
54
  name = OpName.Y
54
55
 
55
- def __init__(self, target: int):
56
- super().__init__((target,))
56
+ def __init__(self, target: int, noise: NoiseHandler | None = None) -> None:
57
+ super().__init__((target,), noise=noise)
57
58
 
58
59
  @property
59
60
  def generator(self) -> AbstractBlock:
@@ -73,8 +74,8 @@ class Z(PrimitiveBlock):
73
74
 
74
75
  name = OpName.Z
75
76
 
76
- def __init__(self, target: int):
77
- super().__init__((target,))
77
+ def __init__(self, target: int, noise: NoiseHandler | None = None) -> None:
78
+ super().__init__((target,), noise=noise)
78
79
 
79
80
  @property
80
81
  def generator(self) -> AbstractBlock:
@@ -94,8 +95,8 @@ class I(PrimitiveBlock):
94
95
 
95
96
  name = OpName.I
96
97
 
97
- def __init__(self, target: int):
98
- super().__init__((target,))
98
+ def __init__(self, target: int, noise: NoiseHandler | None = None) -> None:
99
+ super().__init__((target,), noise=noise)
99
100
 
100
101
  def __ixor__(self, other: AbstractBlock | int) -> AbstractBlock:
101
102
  if not isinstance(other, AbstractBlock):
@@ -113,7 +114,7 @@ class I(PrimitiveBlock):
113
114
 
114
115
  @property
115
116
  def generator(self) -> AbstractBlock:
116
- return I(*self.qubit_support)
117
+ return I(self.qubit_support[0])
117
118
 
118
119
  @property
119
120
  def eigenvalues_generator(self) -> Tensor:
@@ -137,8 +138,9 @@ class Projector(ProjectorBlock):
137
138
  ket: str,
138
139
  bra: str,
139
140
  qubit_support: int | tuple[int, ...],
140
- ):
141
- super().__init__(ket=ket, bra=bra, qubit_support=qubit_support)
141
+ noise: NoiseHandler | None = None,
142
+ ) -> None:
143
+ super().__init__(ket=ket, bra=bra, qubit_support=qubit_support, noise=noise)
142
144
 
143
145
  @property
144
146
  def generator(self) -> None:
@@ -154,8 +156,13 @@ class N(Projector):
154
156
 
155
157
  name = OpName.N
156
158
 
157
- def __init__(self, target: int, state: str = "1"):
158
- super().__init__(ket=state, bra=state, qubit_support=(target,))
159
+ def __init__(
160
+ self,
161
+ target: int,
162
+ state: str = "1",
163
+ noise: NoiseHandler | None = None,
164
+ ) -> None:
165
+ super().__init__(ket=state, bra=state, qubit_support=(target,), noise=noise)
159
166
 
160
167
  @property
161
168
  def generator(self) -> None:
@@ -175,9 +182,13 @@ class S(PrimitiveBlock):
175
182
 
176
183
  name = OpName.S
177
184
 
178
- def __init__(self, target: int):
185
+ def __init__(
186
+ self,
187
+ target: int,
188
+ noise: NoiseHandler | None = None,
189
+ ) -> None:
179
190
  self.generator = I(target) - Z(target)
180
- super().__init__((target,))
191
+ super().__init__((target,), noise=noise)
181
192
 
182
193
  @property
183
194
  def eigenvalues_generator(self) -> Tensor:
@@ -188,7 +199,7 @@ class S(PrimitiveBlock):
188
199
  return tensor([1, 1j], dtype=cdouble)
189
200
 
190
201
  def dagger(self) -> SDagger:
191
- return SDagger(*self.qubit_support)
202
+ return SDagger(self.qubit_support[0])
192
203
 
193
204
 
194
205
  class SDagger(PrimitiveBlock):
@@ -196,9 +207,13 @@ class SDagger(PrimitiveBlock):
196
207
 
197
208
  name = OpName.SDAGGER
198
209
 
199
- def __init__(self, target: int):
210
+ def __init__(
211
+ self,
212
+ target: int,
213
+ noise: NoiseHandler | None = None,
214
+ ) -> None:
200
215
  self.generator = I(target) - Z(target)
201
- super().__init__((target,))
216
+ super().__init__((target,), noise=noise)
202
217
 
203
218
  @property
204
219
  def eigenvalues_generator(self) -> Tensor:
@@ -209,7 +224,7 @@ class SDagger(PrimitiveBlock):
209
224
  return tensor([1, -1j], dtype=cdouble)
210
225
 
211
226
  def dagger(self) -> S:
212
- return S(*self.qubit_support)
227
+ return S(self.qubit_support[0])
213
228
 
214
229
 
215
230
  class H(PrimitiveBlock):
@@ -217,9 +232,13 @@ class H(PrimitiveBlock):
217
232
 
218
233
  name = OpName.H
219
234
 
220
- def __init__(self, target: int):
235
+ def __init__(
236
+ self,
237
+ target: int,
238
+ noise: NoiseHandler | None = None,
239
+ ) -> None:
221
240
  self.generator = (1 / np.sqrt(2)) * (X(target) + Z(target) - np.sqrt(2) * I(target))
222
- super().__init__((target,))
241
+ super().__init__((target,), noise=noise)
223
242
 
224
243
  @property
225
244
  def eigenvalues_generator(self) -> Tensor:
@@ -275,9 +294,13 @@ class T(PrimitiveBlock):
275
294
 
276
295
  name = OpName.T
277
296
 
278
- def __init__(self, target: int):
297
+ def __init__(
298
+ self,
299
+ target: int,
300
+ noise: NoiseHandler | None = None,
301
+ ) -> None:
279
302
  self.generator = I(target) - Z(target)
280
- super().__init__((target,))
303
+ super().__init__((target,), noise)
281
304
 
282
305
  @property
283
306
  def eigenvalues_generator(self) -> Tensor:
@@ -292,7 +315,7 @@ class T(PrimitiveBlock):
292
315
  return 1
293
316
 
294
317
  def dagger(self) -> TDagger:
295
- return TDagger(*self.qubit_support)
318
+ return TDagger(self.qubit_support[0])
296
319
 
297
320
 
298
321
  class TDagger(PrimitiveBlock):
@@ -301,9 +324,13 @@ class TDagger(PrimitiveBlock):
301
324
  # FIXME: this gate is not support by any backend
302
325
  name = "T_dagger"
303
326
 
304
- def __init__(self, target: int):
327
+ def __init__(
328
+ self,
329
+ target: int,
330
+ noise: NoiseHandler | None = None,
331
+ ) -> None:
305
332
  self.generator = I(target) - Z(target)
306
- super().__init__((target,))
333
+ super().__init__((target,), noise)
307
334
 
308
335
  @property
309
336
  def eigenvalues_generator(self) -> Tensor:
@@ -318,7 +345,7 @@ class TDagger(PrimitiveBlock):
318
345
  return 1
319
346
 
320
347
  def dagger(self) -> T:
321
- return T(*self.qubit_support)
348
+ return T(self.qubit_support[0])
322
349
 
323
350
 
324
351
  class SWAP(PrimitiveBlock):
@@ -326,7 +353,12 @@ class SWAP(PrimitiveBlock):
326
353
 
327
354
  name = OpName.SWAP
328
355
 
329
- def __init__(self, control: int, target: int) -> None:
356
+ def __init__(
357
+ self,
358
+ control: int,
359
+ target: int,
360
+ noise: NoiseHandler | None = None,
361
+ ) -> None:
330
362
  a11 = 0.5 * (Z(control) - I(control))
331
363
  a22 = -0.5 * (Z(target) + I(target))
332
364
  a12 = 0.5 * (chain(X(control), Z(control)) + X(control))
@@ -334,7 +366,7 @@ class SWAP(PrimitiveBlock):
334
366
  self.generator = (
335
367
  kron(-1.0 * a22, a11) + kron(-1.0 * a11, a22) + kron(a12, a21) + kron(a21, a12)
336
368
  )
337
- super().__init__((control, target))
369
+ super().__init__((control, target), noise=noise)
338
370
 
339
371
  @property
340
372
  def eigenvalues_generator(self) -> Tensor:
qadence/overlap.py CHANGED
@@ -102,9 +102,6 @@ def _select_overlap_method(
102
102
  return overlap
103
103
 
104
104
  elif method == OverlapMethod.SWAP_TEST:
105
- if backend == BackendName.BRAKET:
106
- raise ValueError("SWAP test method is not supported by the Braket backend.")
107
-
108
105
  n_qubits = bra_circuit.n_qubits
109
106
 
110
107
  # shift qubit support of bra and ket circuit blocks
@@ -128,9 +125,6 @@ def _select_overlap_method(
128
125
  return overlap
129
126
 
130
127
  elif method == OverlapMethod.HADAMARD_TEST:
131
- if backend == BackendName.BRAKET:
132
- raise ValueError("Hadamard test method is not supported by the Braket backend.")
133
-
134
128
  n_qubits = bra_circuit.n_qubits
135
129
 
136
130
  # construct controlled bra and ket blocks
qadence/parameters.py CHANGED
@@ -228,7 +228,7 @@ def sympy_to_numeric(expr: Basic) -> TNumber:
228
228
  return float(expr)
229
229
 
230
230
 
231
- def evaluate(expr: Expr, values: dict = {}, as_torch: bool = False) -> TNumber | Tensor:
231
+ def evaluate(expr: Expr, values: dict | None = None, as_torch: bool = False) -> TNumber | Tensor:
232
232
  """
233
233
  Arguments:
234
234
 
@@ -260,7 +260,8 @@ def evaluate(expr: Expr, values: dict = {}, as_torch: bool = False) -> TNumber |
260
260
  """
261
261
  res: Basic
262
262
  res_value: TNumber | Tensor
263
- query: dict[Parameter, TNumber | Tensor] = {}
263
+ query: dict[Parameter, TNumber | Tensor] = dict()
264
+ values = values or dict()
264
265
  if isinstance(expr, Array):
265
266
  return Tensor(expr.tolist())
266
267
  else:
@@ -12,6 +12,7 @@ from .circuit import fill_identities
12
12
  from .digitalize import digitalize
13
13
  from .flatten import flatten
14
14
  from .invert import invert_endianness, reassign
15
+ from .noise import set_noise
15
16
  from .transpile import blockfn_to_circfn, transpile
16
17
 
17
- __all__ = ["set_trainable", "invert_endianness"]
18
+ __all__ = ["set_trainable", "invert_endianness", "set_noise"]
@@ -0,0 +1,46 @@
1
+ from __future__ import annotations
2
+
3
+ from qadence.blocks.abstract import AbstractBlock
4
+ from qadence.circuit import QuantumCircuit
5
+ from qadence.noise.protocols import NoiseHandler
6
+
7
+ from .apply_fn import apply_fn_to_blocks
8
+
9
+
10
+ def _set_noise(
11
+ block: AbstractBlock,
12
+ noise: NoiseHandler | None,
13
+ target_class: type[AbstractBlock] | None = None,
14
+ ) -> AbstractBlock:
15
+ """Changes the noise protocol of a given block in place."""
16
+ if target_class is not None:
17
+ if isinstance(block, target_class):
18
+ block._noise = noise # type: ignore [attr-defined]
19
+ else:
20
+ block._noise = noise # type: ignore [attr-defined]
21
+
22
+ return block
23
+
24
+
25
+ def set_noise(
26
+ circuit: QuantumCircuit | AbstractBlock,
27
+ noise: NoiseHandler | None,
28
+ target_class: AbstractBlock | None = None,
29
+ ) -> QuantumCircuit | AbstractBlock:
30
+ """
31
+ Parses a `QuantumCircuit` or `CompositeBlock` to add noise to specific gates.
32
+
33
+ Changes the input in place.
34
+
35
+ Arguments:
36
+ circuit: the circuit or block to parse.
37
+ noise: the NoiseHandler protocol to change to, or `None` to remove the noise.
38
+ target_class: optional class to selectively add noise to.
39
+ """
40
+ is_circuit_input = isinstance(circuit, QuantumCircuit)
41
+
42
+ input_block: AbstractBlock = circuit.block if is_circuit_input else circuit # type: ignore
43
+
44
+ output_block = apply_fn_to_blocks(input_block, _set_noise, noise, target_class)
45
+
46
+ return circuit
qadence/types.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import importlib
4
+ from dataclasses import dataclass
4
5
  from enum import Enum
5
6
  from typing import Callable, Iterable, Tuple, Union
6
7
 
@@ -8,6 +9,7 @@ import numpy as np
8
9
  import sympy
9
10
  from matplotlib.figure import Figure
10
11
  from numpy.typing import ArrayLike
12
+ from pyqtorch.noise import NoiseType as DigitalNoise
11
13
  from pyqtorch.utils import SolverType
12
14
  from torch import Tensor, pi
13
15
  from torch.nn import Module
@@ -26,6 +28,7 @@ TArray = Union[Iterable, Tensor, np.ndarray]
26
28
  TGenerator = Union[Tensor, sympy.Array, sympy.Basic]
27
29
  """Union of torch tensors and numpy arrays."""
28
30
 
31
+
29
32
  PI = pi
30
33
 
31
34
  # Modules to be automatically added to the qadence namespace
@@ -51,6 +54,7 @@ __all__ = [
51
54
  "SerializationFormat",
52
55
  "PI",
53
56
  "SolverType",
57
+ "NoiseProtocol",
54
58
  ] # type: ignore
55
59
 
56
60
 
@@ -221,8 +225,6 @@ class _BackendName(StrEnum):
221
225
 
222
226
  PYQTORCH = "pyqtorch"
223
227
  """The Pyqtorch backend."""
224
- BRAKET = "braket"
225
- """The Braket backend."""
226
228
  PULSER = "pulser"
227
229
  """The Pulser backend."""
228
230
  HORQRUX = "horqrux"
@@ -457,3 +459,25 @@ class ExperimentTrackingTool(StrEnum):
457
459
 
458
460
 
459
461
  LoggablePlotFunction = Callable[[Module, int], tuple[str, Figure]]
462
+
463
+
464
+ class AnalogNoise(StrEnum):
465
+ """Type of noise protocol."""
466
+
467
+ DEPOLARIZING = "Depolarizing"
468
+ DEPHASING = "Dephasing"
469
+
470
+
471
+ @dataclass
472
+ class NoiseProtocol:
473
+ """Type of noise protocol."""
474
+
475
+ ANALOG = AnalogNoise
476
+ """Noise applied in analog blocks."""
477
+ READOUT = "Readout"
478
+ """Noise applied on outputs of quantum programs."""
479
+ DIGITAL = DigitalNoise
480
+ """Noise applied to digital blocks."""
481
+
482
+
483
+ NoiseEnum = Union[DigitalNoise, AnalogNoise, str]