qadence 1.9.1__py3-none-any.whl → 1.10.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 +7 -0
- qadence/backends/api.py +3 -3
- qadence/backends/horqrux/convert_ops.py +3 -1
- qadence/backends/pulser/backend.py +1 -1
- qadence/backends/pyqtorch/convert_ops.py +5 -5
- qadence/backends/utils.py +15 -1
- qadence/blocks/embedding.py +7 -6
- qadence/blocks/utils.py +5 -3
- qadence/constructors/__init__.py +5 -4
- qadence/constructors/ala.py +270 -0
- qadence/constructors/{ansatze.py → hea.py} +76 -195
- qadence/constructors/iia.py +31 -0
- qadence/engines/torch/differentiable_expectation.py +3 -1
- qadence/extensions.py +4 -8
- qadence/measurements/shadow.py +30 -10
- qadence/ml_tools/callbacks/__init__.py +10 -0
- qadence/ml_tools/callbacks/callback.py +325 -1
- qadence/ml_tools/callbacks/writer_registry.py +17 -17
- qadence/ml_tools/constructors.py +8 -6
- qadence/ml_tools/trainer.py +28 -23
- qadence/operations/control_ops.py +3 -9
- qadence/pasqal_cloud_connection.py +235 -0
- qadence/states.py +21 -0
- qadence/transpile/digitalize.py +4 -4
- qadence/transpile/flatten.py +4 -4
- qadence/transpile/invert.py +6 -12
- qadence/transpile/transpile.py +2 -4
- qadence/types.py +1 -1
- {qadence-1.9.1.dist-info → qadence-1.10.0.dist-info}/METADATA +9 -6
- {qadence-1.9.1.dist-info → qadence-1.10.0.dist-info}/RECORD +32 -30
- {qadence-1.9.1.dist-info → qadence-1.10.0.dist-info}/WHEEL +1 -1
- {qadence-1.9.1.dist-info → qadence-1.10.0.dist-info}/licenses/LICENSE +0 -0
qadence/analog/device.py
CHANGED
@@ -5,6 +5,8 @@ from dataclasses import dataclass, fields
|
|
5
5
|
from qadence.analog import AddressingPattern
|
6
6
|
from qadence.types import PI, DeviceType, Interaction
|
7
7
|
|
8
|
+
from .constants import C6_DICT
|
9
|
+
|
8
10
|
|
9
11
|
@dataclass(frozen=True, eq=True)
|
10
12
|
class RydbergDevice:
|
@@ -41,6 +43,11 @@ class RydbergDevice:
|
|
41
43
|
type: DeviceType = DeviceType.IDEALIZED
|
42
44
|
"""DeviceType.IDEALIZED or REALISTIC to convert to the Pulser backend."""
|
43
45
|
|
46
|
+
@property
|
47
|
+
def coeff_ising(self) -> float:
|
48
|
+
"""Value of C_6."""
|
49
|
+
return C6_DICT[self.rydberg_level]
|
50
|
+
|
44
51
|
def __post_init__(self) -> None:
|
45
52
|
# FIXME: Currently not supporting custom interaction functions.
|
46
53
|
if self.interaction not in [Interaction.NN, Interaction.XY]:
|
qadence/backends/api.py
CHANGED
@@ -42,9 +42,9 @@ def backend_factory(
|
|
42
42
|
|
43
43
|
# Instantiate the backend
|
44
44
|
backend_inst = BackendCls( # type: ignore[operator]
|
45
|
-
config=
|
46
|
-
|
47
|
-
|
45
|
+
config=(
|
46
|
+
configuration if configuration is not None else BackendCls.default_configuration()
|
47
|
+
)
|
48
48
|
)
|
49
49
|
set_backend_config(backend_inst, diff_mode)
|
50
50
|
# Wrap the quantum Backend in a DifferentiableBackend if a diff_mode is passed.
|
@@ -12,7 +12,7 @@ from horqrux.apply import apply_gate
|
|
12
12
|
from horqrux.parametric import RX, RY, RZ
|
13
13
|
from horqrux.primitive import NOT, SWAP, H, I, X, Y, Z
|
14
14
|
from horqrux.primitive import Primitive as Gate
|
15
|
-
from horqrux.utils import inner
|
15
|
+
from horqrux.utils import ControlQubits, TargetQubits, inner
|
16
16
|
from jax import Array
|
17
17
|
from jax.scipy.linalg import expm
|
18
18
|
from jax.tree_util import register_pytree_node_class
|
@@ -102,6 +102,8 @@ def convert_observable(
|
|
102
102
|
def convert_block(
|
103
103
|
block: AbstractBlock, n_qubits: int = None, config: Configuration = Configuration()
|
104
104
|
) -> list:
|
105
|
+
control: ControlQubits
|
106
|
+
target: TargetQubits
|
105
107
|
if n_qubits is None:
|
106
108
|
n_qubits = max(block.qubit_support) + 1
|
107
109
|
ops = []
|
@@ -259,7 +259,7 @@ class Backend(BackendInterface):
|
|
259
259
|
for i, param_values_el in enumerate(vals):
|
260
260
|
sequence = self.assign_parameters(circuit, param_values_el)
|
261
261
|
sim_result: CoherentResults = simulate_sequence(sequence, self.config, state)
|
262
|
-
final_state = sim_result.get_final_state().data.
|
262
|
+
final_state = sim_result.get_final_state().data.to_array()
|
263
263
|
batched_dm[i] = np.flip(final_state)
|
264
264
|
return torch.from_numpy(batched_dm)
|
265
265
|
|
@@ -264,7 +264,7 @@ def convert_block(
|
|
264
264
|
duration=duration,
|
265
265
|
solver=config.ode_solver,
|
266
266
|
steps=config.n_steps_hevo,
|
267
|
-
|
267
|
+
noise=noise_operators if len(noise_operators) > 0 else None,
|
268
268
|
)
|
269
269
|
]
|
270
270
|
|
@@ -351,22 +351,22 @@ def convert_block(
|
|
351
351
|
)
|
352
352
|
|
353
353
|
|
354
|
-
def convert_digital_noise(noise: NoiseHandler) -> pyq.noise.
|
354
|
+
def convert_digital_noise(noise: NoiseHandler) -> pyq.noise.DigitalNoiseProtocol | None:
|
355
355
|
"""Convert the digital noise into pyqtorch NoiseProtocol.
|
356
356
|
|
357
357
|
Args:
|
358
358
|
noise (NoiseHandler): Noise to convert.
|
359
359
|
|
360
360
|
Returns:
|
361
|
-
pyq.noise.
|
361
|
+
pyq.noise.DigitalNoiseProtocol | None: Pyqtorch native noise protocol
|
362
362
|
if there are any digital noise protocols.
|
363
363
|
"""
|
364
364
|
digital_part = noise.filter(NoiseProtocol.DIGITAL)
|
365
365
|
if digital_part is None:
|
366
366
|
return None
|
367
|
-
return pyq.noise.
|
367
|
+
return pyq.noise.DigitalNoiseProtocol(
|
368
368
|
[
|
369
|
-
pyq.noise.
|
369
|
+
pyq.noise.DigitalNoiseProtocol(proto, option.get("error_probability"))
|
370
370
|
for proto, option in zip(digital_part.protocol, digital_part.options)
|
371
371
|
]
|
372
372
|
)
|
qadence/backends/utils.py
CHANGED
@@ -110,9 +110,23 @@ def to_list_of_dicts(param_values: ParamDictType) -> list[ParamDictType]:
|
|
110
110
|
|
111
111
|
|
112
112
|
def pyqify(state: Tensor, n_qubits: int = None) -> ArrayLike:
|
113
|
-
"""Convert a state of shape (batch_size, 2**n_qubits) to [2] * n_qubits + [batch_size].
|
113
|
+
"""Convert a state of shape (batch_size, 2**n_qubits) to [2] * n_qubits + [batch_size].
|
114
|
+
|
115
|
+
Or set the batch_size of a density matrix as the last dimension for PyQTorch.
|
116
|
+
"""
|
114
117
|
if n_qubits is None:
|
115
118
|
n_qubits = int(log2(state.shape[1]))
|
119
|
+
if isinstance(state, DensityMatrix):
|
120
|
+
if (
|
121
|
+
len(state.shape) != 3
|
122
|
+
or (state.shape[1] != 2**n_qubits)
|
123
|
+
or (state.shape[1] != state.shape[2])
|
124
|
+
):
|
125
|
+
raise ValueError(
|
126
|
+
"The initial state must be composed of tensors/arrays of size "
|
127
|
+
f"(batch_size, 2**n_qubits, 2**n_qubits). Found: {state.shape = }."
|
128
|
+
)
|
129
|
+
return torch.einsum("kij->ijk", state)
|
116
130
|
if len(state.shape) != 2 or (state.shape[1] != 2**n_qubits):
|
117
131
|
raise ValueError(
|
118
132
|
"The initial state must be composed of tensors/arrays of size "
|
qadence/blocks/embedding.py
CHANGED
@@ -41,7 +41,10 @@ def unique(x: Iterable) -> List:
|
|
41
41
|
|
42
42
|
def embedding(
|
43
43
|
block: AbstractBlock, to_gate_params: bool = False, engine: Engine = Engine.TORCH
|
44
|
-
) -> tuple[
|
44
|
+
) -> tuple[
|
45
|
+
ParamDictType,
|
46
|
+
Callable[[ParamDictType, ParamDictType], ParamDictType],
|
47
|
+
]:
|
45
48
|
"""Construct embedding function which maps user-facing parameters to either *expression-level*.
|
46
49
|
|
47
50
|
parameters or *gate-level* parameters. The constructed embedding function has the signature:
|
@@ -147,11 +150,9 @@ def embedding(
|
|
147
150
|
if k not in embedded_params:
|
148
151
|
embedded_params[k] = v
|
149
152
|
out = {
|
150
|
-
stringify(k)
|
151
|
-
|
152
|
-
|
153
|
-
if as_tensor(v).ndim == 0
|
154
|
-
else v
|
153
|
+
stringify(k) if not isinstance(k, str) else k: (
|
154
|
+
as_tensor(v)[None] if as_tensor(v).ndim == 0 else v
|
155
|
+
)
|
155
156
|
for k, v in embedded_params.items()
|
156
157
|
}
|
157
158
|
return out
|
qadence/blocks/utils.py
CHANGED
@@ -305,9 +305,11 @@ def uuid_to_eigen(
|
|
305
305
|
else:
|
306
306
|
result[uuid] = (
|
307
307
|
b.eigenvalues_generator * 2.0,
|
308
|
-
|
309
|
-
|
310
|
-
|
308
|
+
(
|
309
|
+
1.0 / (b.eigenvalues_generator.item() * 2.0)
|
310
|
+
if len(b.eigenvalues_generator) == 1
|
311
|
+
else 1.0
|
312
|
+
),
|
311
313
|
)
|
312
314
|
else:
|
313
315
|
result[uuid] = (b.eigenvalues_generator, 1.0)
|
qadence/constructors/__init__.py
CHANGED
@@ -5,9 +5,9 @@ from .feature_maps import (
|
|
5
5
|
exp_fourier_feature_map,
|
6
6
|
)
|
7
7
|
|
8
|
-
from .
|
9
|
-
|
10
|
-
from .iia import identity_initialized_ansatz
|
8
|
+
from .hea import hea
|
9
|
+
from .ala import ala
|
10
|
+
from .iia import identity_initialized_ansatz, iia
|
11
11
|
|
12
12
|
from .daqc import daqc_transform
|
13
13
|
|
@@ -29,8 +29,9 @@ __all__ = [
|
|
29
29
|
"feature_map",
|
30
30
|
"exp_fourier_feature_map",
|
31
31
|
"hea",
|
32
|
-
"
|
32
|
+
"ala",
|
33
33
|
"identity_initialized_ansatz",
|
34
|
+
"iia",
|
34
35
|
"hamiltonian_factory",
|
35
36
|
"ising_hamiltonian",
|
36
37
|
"ObservableConfig",
|
@@ -0,0 +1,270 @@
|
|
1
|
+
from __future__ import annotations
|
2
|
+
|
3
|
+
import itertools
|
4
|
+
from typing import Any, Type, Union
|
5
|
+
|
6
|
+
from qadence.blocks import AbstractBlock, chain, kron, tag
|
7
|
+
from qadence.operations import CNOT, CPHASE, CRX, CRY, CRZ, CZ, RX, RY
|
8
|
+
from qadence.types import Strategy
|
9
|
+
|
10
|
+
DigitalEntanglers = Union[CNOT, CZ, CRZ, CRY, CRX]
|
11
|
+
|
12
|
+
|
13
|
+
def ala(
|
14
|
+
n_qubits: int,
|
15
|
+
m_block_qubits: int,
|
16
|
+
depth: int = 1,
|
17
|
+
param_prefix: str = "theta",
|
18
|
+
support: tuple[int, ...] | None = None,
|
19
|
+
strategy: Strategy = Strategy.DIGITAL,
|
20
|
+
**strategy_args: Any,
|
21
|
+
) -> AbstractBlock:
|
22
|
+
"""
|
23
|
+
Factory function for the alternating layer ansatz (ala).
|
24
|
+
|
25
|
+
Args:
|
26
|
+
n_qubits: number of qubits in the circuit
|
27
|
+
m_block_qubits: number of qubits in the local entangling block
|
28
|
+
depth: number of layers of the alternating layer ansatz
|
29
|
+
param_prefix: the base name of the variational parameters
|
30
|
+
support: qubit indices where the ala is applied
|
31
|
+
strategy: Strategy for the ansatz. One of the Strategy variants.
|
32
|
+
**strategy_args: see below
|
33
|
+
|
34
|
+
Keyword Arguments:
|
35
|
+
operations (list): list of operations to cycle through in the
|
36
|
+
digital single-qubit rotations of each layer. Valid for
|
37
|
+
Digital .
|
38
|
+
entangler (AbstractBlock):
|
39
|
+
- Digital: 2-qubit entangling operation. Supports CNOT, CZ,
|
40
|
+
CRX, CRY, CRZ, CPHASE. Controlled rotations will have variational
|
41
|
+
parameters on the rotation angles.
|
42
|
+
- SDAQC | BDAQC: Hamiltonian generator for the analog entangling
|
43
|
+
layer. Must be an m-qubit operator where m is the size of the
|
44
|
+
local entangling block. Defaults to a ZZ interaction.
|
45
|
+
|
46
|
+
Returns:
|
47
|
+
The Alternating Layer Ansatz (ALA) circuit.
|
48
|
+
"""
|
49
|
+
|
50
|
+
if support is None:
|
51
|
+
support = tuple(range(n_qubits))
|
52
|
+
|
53
|
+
ala_func_dict = {
|
54
|
+
Strategy.DIGITAL: ala_digital,
|
55
|
+
Strategy.SDAQC: ala_sDAQC,
|
56
|
+
Strategy.BDAQC: ala_bDAQC,
|
57
|
+
Strategy.ANALOG: ala_analog,
|
58
|
+
}
|
59
|
+
|
60
|
+
try:
|
61
|
+
ala_func = ala_func_dict[strategy]
|
62
|
+
except KeyError:
|
63
|
+
raise KeyError(f"Strategy {strategy} not recognized.")
|
64
|
+
|
65
|
+
ala_block: AbstractBlock = ala_func(
|
66
|
+
n_qubits=n_qubits,
|
67
|
+
m_block_qubits=m_block_qubits,
|
68
|
+
depth=depth,
|
69
|
+
param_prefix=param_prefix,
|
70
|
+
support=support,
|
71
|
+
**strategy_args,
|
72
|
+
) # type: ignore
|
73
|
+
|
74
|
+
return ala_block
|
75
|
+
|
76
|
+
|
77
|
+
#################
|
78
|
+
## DIGITAL ALA ##
|
79
|
+
#################
|
80
|
+
|
81
|
+
|
82
|
+
def _rotations_digital(
|
83
|
+
n_qubits: int,
|
84
|
+
depth: int,
|
85
|
+
param_prefix: str = "theta",
|
86
|
+
support: tuple[int, ...] | None = None,
|
87
|
+
operations: list[Type[AbstractBlock]] = [RX, RY, RX],
|
88
|
+
) -> list[AbstractBlock]:
|
89
|
+
"""Creates the layers of single qubit rotations in an Alternating Layer Ansatz.
|
90
|
+
|
91
|
+
Args:
|
92
|
+
n_qubits: The number of qubits in the Alternating Layer Ansatz.
|
93
|
+
depth: The number of layers of rotations.
|
94
|
+
param_prefix: The prefix for the parameter names.
|
95
|
+
support: The qubits to apply the rotations to.
|
96
|
+
operations: The operations to apply the rotations with.
|
97
|
+
|
98
|
+
Returns:
|
99
|
+
A list of digital rotation layers for the Alternating Layer Ansatz.
|
100
|
+
"""
|
101
|
+
if support is None:
|
102
|
+
support = tuple(range(n_qubits))
|
103
|
+
iterator = itertools.count()
|
104
|
+
rot_list: list[AbstractBlock] = []
|
105
|
+
for d in range(depth):
|
106
|
+
rots = [
|
107
|
+
kron(
|
108
|
+
gate(support[n], param_prefix + f"_{next(iterator)}") # type: ignore [arg-type]
|
109
|
+
for n in range(n_qubits)
|
110
|
+
)
|
111
|
+
for gate in operations
|
112
|
+
]
|
113
|
+
rot_list.append(chain(*rots))
|
114
|
+
return rot_list
|
115
|
+
|
116
|
+
|
117
|
+
def _entangler(
|
118
|
+
control: int,
|
119
|
+
target: int,
|
120
|
+
param_str: str,
|
121
|
+
op: Type[DigitalEntanglers] = CNOT,
|
122
|
+
) -> AbstractBlock:
|
123
|
+
"""
|
124
|
+
Creates the entangler for a single qubit in an Alternating Layer Ansatz.
|
125
|
+
|
126
|
+
Args:
|
127
|
+
control: The control qubit.
|
128
|
+
target: The target qubit.
|
129
|
+
param_str: The parameter string.
|
130
|
+
op: The entangler to use.
|
131
|
+
|
132
|
+
Returns:
|
133
|
+
The 2-qubit digital entangler for the Alternating Layer Ansatz.
|
134
|
+
"""
|
135
|
+
if op in [CNOT, CZ]:
|
136
|
+
return op(control, target) # type: ignore
|
137
|
+
elif op in [CRZ, CRY, CRX, CPHASE]:
|
138
|
+
return op(control, target, param_str) # type: ignore
|
139
|
+
else:
|
140
|
+
raise ValueError("Provided entangler not accepted for digital alternating layer ansatz")
|
141
|
+
|
142
|
+
|
143
|
+
def _entanglers_ala_block_digital(
|
144
|
+
n_qubits: int,
|
145
|
+
m_block_qubits: int,
|
146
|
+
depth: int,
|
147
|
+
param_prefix: str = "theta",
|
148
|
+
support: tuple[int, ...] | None = None,
|
149
|
+
entangler: Type[DigitalEntanglers] = CNOT,
|
150
|
+
) -> list[AbstractBlock]:
|
151
|
+
"""
|
152
|
+
Creates the entanglers for an Alternating Layer Ansatz.
|
153
|
+
|
154
|
+
Args:
|
155
|
+
n_qubits: The number of qubits in the Alternating Layer Ansatz.
|
156
|
+
m_block_qubits: The number of qubits in each block.
|
157
|
+
depth: The number of layers of entanglers.
|
158
|
+
param_prefix: The prefix for the parameter names.
|
159
|
+
support (tuple): qubit indices where the HEA is applied.
|
160
|
+
entangler: The entangler to use.
|
161
|
+
|
162
|
+
Returns:
|
163
|
+
The entanglers for the Alternating Layer Ansatz.
|
164
|
+
"""
|
165
|
+
if support is None:
|
166
|
+
support = tuple(range(n_qubits))
|
167
|
+
iterator = itertools.count()
|
168
|
+
ent_list: list[AbstractBlock] = []
|
169
|
+
|
170
|
+
for d in range(depth):
|
171
|
+
start_i = 0 if not d % 2 else -m_block_qubits // 2
|
172
|
+
ents = [
|
173
|
+
kron(
|
174
|
+
_entangler(
|
175
|
+
control=support[i + j],
|
176
|
+
target=support[i + j + 1],
|
177
|
+
param_str=param_prefix + f"_ent_{next(iterator)}",
|
178
|
+
op=entangler,
|
179
|
+
)
|
180
|
+
for j in range(start_j, m_block_qubits, 2)
|
181
|
+
for i in range(start_i, n_qubits, m_block_qubits)
|
182
|
+
if i + j + 1 < n_qubits and j + 1 < m_block_qubits and i + j >= 0
|
183
|
+
)
|
184
|
+
for start_j in [i for i in range(2) if m_block_qubits > 2 or i == 0]
|
185
|
+
]
|
186
|
+
|
187
|
+
ent_list.append(chain(*ents))
|
188
|
+
return ent_list
|
189
|
+
|
190
|
+
|
191
|
+
def ala_digital(
|
192
|
+
n_qubits: int,
|
193
|
+
m_block_qubits: int,
|
194
|
+
depth: int = 1,
|
195
|
+
param_prefix: str = "theta",
|
196
|
+
support: tuple[int, ...] | None = None,
|
197
|
+
operations: list[type[AbstractBlock]] = [RX, RY],
|
198
|
+
entangler: Type[DigitalEntanglers] = CNOT,
|
199
|
+
) -> AbstractBlock:
|
200
|
+
"""
|
201
|
+
Construct the digital alternating layer ansatz (ALA).
|
202
|
+
|
203
|
+
Args:
|
204
|
+
n_qubits (int): number of qubits in the circuit.
|
205
|
+
m_block_qubits (int): number of qubits in the local entangling block.
|
206
|
+
depth (int): number of layers of the ALA.
|
207
|
+
param_prefix (str): the base name of the variational parameters
|
208
|
+
support (tuple): qubit indices where the ALA is applied.
|
209
|
+
operations (list): list of operations to cycle through in the
|
210
|
+
digital single-qubit rotations of each layer.
|
211
|
+
entangler (AbstractBlock): 2-qubit entangling operation.
|
212
|
+
Supports CNOT, CZ, CRX, CRY, CRZ. Controlld rotations
|
213
|
+
will have variational parameters on the rotation angles.
|
214
|
+
|
215
|
+
Returns:
|
216
|
+
The digital Alternating Layer Ansatz (ALA) circuit.
|
217
|
+
"""
|
218
|
+
|
219
|
+
try:
|
220
|
+
if entangler not in [CNOT, CZ, CRX, CRY, CRZ, CPHASE]:
|
221
|
+
raise ValueError(
|
222
|
+
"Please provide a valid two-qubit entangler operation for digital ALA."
|
223
|
+
)
|
224
|
+
except TypeError:
|
225
|
+
raise ValueError("Please provide a valid two-qubit entangler operation for digital ALA.")
|
226
|
+
|
227
|
+
rot_list = _rotations_digital(
|
228
|
+
n_qubits=n_qubits,
|
229
|
+
depth=depth,
|
230
|
+
support=support,
|
231
|
+
param_prefix=param_prefix,
|
232
|
+
operations=operations,
|
233
|
+
)
|
234
|
+
|
235
|
+
ent_list = _entanglers_ala_block_digital(
|
236
|
+
n_qubits,
|
237
|
+
m_block_qubits,
|
238
|
+
param_prefix=param_prefix + "_ent",
|
239
|
+
depth=depth,
|
240
|
+
support=support,
|
241
|
+
entangler=entangler,
|
242
|
+
)
|
243
|
+
|
244
|
+
layers = []
|
245
|
+
for d in range(depth):
|
246
|
+
layers.append(rot_list[d])
|
247
|
+
layers.append(ent_list[d])
|
248
|
+
|
249
|
+
return tag(chain(*layers), "ALA")
|
250
|
+
|
251
|
+
|
252
|
+
#################
|
253
|
+
## sdaqc ALA ##
|
254
|
+
#################
|
255
|
+
def ala_sDAQC(*args: Any, **kwargs: Any) -> Any:
|
256
|
+
raise NotImplementedError
|
257
|
+
|
258
|
+
|
259
|
+
#################
|
260
|
+
## bdaqc ALA ##
|
261
|
+
#################
|
262
|
+
def ala_bDAQC(*args: Any, **kwargs: Any) -> Any:
|
263
|
+
raise NotImplementedError
|
264
|
+
|
265
|
+
|
266
|
+
#################
|
267
|
+
## analog ALA ##
|
268
|
+
#################
|
269
|
+
def ala_analog(*args: Any, **kwargs: Any) -> Any:
|
270
|
+
raise NotImplementedError
|