qadence 1.9.2__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/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=configuration
46
- if configuration is not None
47
- else BackendCls.default_configuration()
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 = []
@@ -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[ParamDictType, Callable[[ParamDictType, ParamDictType], ParamDictType],]:
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
- if not isinstance(k, str)
152
- else k: as_tensor(v)[None]
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
- 1.0 / (b.eigenvalues_generator.item() * 2.0)
309
- if len(b.eigenvalues_generator) == 1
310
- else 1.0,
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)
@@ -5,9 +5,9 @@ from .feature_maps import (
5
5
  exp_fourier_feature_map,
6
6
  )
7
7
 
8
- from .ansatze import hea, alt
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
- "alt",
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