Skip to content
Pasqal Documentation

Constructors for common quantum circuits

exp_fourier_feature_map(n_qubits, support=None, param='x', feature_range=None)

Section titled “ exp_fourier_feature_map(n_qubits, support=None, param='x', feature_range=None) ”

Exponential fourier feature map.

PARAMETER DESCRIPTION
n_qubits

number of qubits in the feature

TYPE: int

support

qubit support

TYPE: tuple[int, ...] DEFAULT: None

param

name of feature Parameter

TYPE: str DEFAULT: 'x'

feature_range

min and max value of the feature, as floats in a Tuple

TYPE: tuple[float, float] DEFAULT: None

Source code in qadence/constructors/feature_maps.py
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234def exp_fourier_feature_map(
n_qubits: int,
support: tuple[int, ...] = None,
param: str = "x",
feature_range: tuple[float, float] = None,
) -> AbstractBlock:
"""
Exponential fourier feature map.
Args:
n_qubits: number of qubits in the feature
support: qubit support
param: name of feature `Parameter`
feature_range: min and max value of the feature, as floats in a Tuple
"""
if feature_range is None:
feature_range = (0.0, 2.0**n_qubits)
support = tuple(range(n_qubits)) if support is None else support
hlayer = kron(H(qubit) for qubit in support)
rlayer = feature_map(
n_qubits,
support=support,
param=param,
op=RZ,
fm_type=BasisSet.FOURIER,
reupload_scaling=ReuploadScaling.EXP,
feature_range=feature_range,
target_range=(0.0, 2 * PI),
)
rlayer.tag = None
return tag(chain(hlayer, rlayer), f"ExpFourierFM({param})")

feature_map(n_qubits, support=None, param='phi', op=RX, fm_type=BasisSet.FOURIER, reupload_scaling=ReuploadScaling.CONSTANT, feature_range=None, target_range=None, multiplier=None, param_prefix=None)

Section titled “ feature_map(n_qubits, support=None, param='phi', op=RX, fm_type=BasisSet.FOURIER, reupload_scaling=ReuploadScaling.CONSTANT, feature_range=None, target_range=None, multiplier=None, param_prefix=None) ”

Construct a feature map of a given type.

PARAMETER DESCRIPTION
n_qubits

Number of qubits the feature map covers. Results in support=range(n_qubits).

TYPE: int

support

Puts one feature-encoding rotation gate on every qubit in support. n_qubits in this case specifies the total overall qubits of the circuit, which may be wider than the support itself, but not narrower.

TYPE: tuple[int, ...] | None DEFAULT: None

param

Parameter of the feature map; you can pass a string or Parameter; it will be set as non-trainable (FeatureParameter) regardless.

TYPE: Parameter | str DEFAULT: 'phi'

op

Rotation operation of the feature map; choose from RX, RY, RZ or PHASE.

TYPE: RotationTypes DEFAULT: RX

fm_type

Basis set for data encoding; choose from BasisSet.FOURIER for Fourier encoding, or BasisSet.CHEBYSHEV for Chebyshev polynomials of the first kind.

TYPE: BasisSet | Callable | str DEFAULT: FOURIER

reupload_scaling

how the feature map scales the data that is re-uploaded for each qubit. choose from ReuploadScaling enumeration or provide your own function with a single int as input and int or float as output.

TYPE: ReuploadScaling | Callable | str DEFAULT: CONSTANT

feature_range

range of data that the input data provided comes from. Used to map input data to the correct domain of the feature-encoding function.

TYPE: tuple[float, float] | None DEFAULT: None

target_range

range of data the data encoder assumes as the natural range. For example, in Chebyshev polynomials it is (-1, 1), while for Fourier it may be chosen as (0, 2*PI). Used to map data to the correct domain of the feature-encoding function.

TYPE: tuple[float, float] | None DEFAULT: None

multiplier

overall multiplier; this is useful for reuploading the feature map serially with different scalings; can be a number or parameter/expression.

TYPE: Parameter | TParameter | None DEFAULT: None

param_prefix

string prefix to create trainable parameters multiplying the feature parameter inside the feature-encoding function. Note that currently this does not take into account the domain of the feature-encoding function.

TYPE: str | None DEFAULT: None

Example:

from qadence import feature_map, BasisSet, ReuploadScaling
fm = feature_map(3, fm_type=BasisSet.FOURIER)
print(f"{fm = }")
fm = feature_map(3, fm_type=BasisSet.CHEBYSHEV)
print(f"{fm = }")
fm = feature_map(3, fm_type=BasisSet.FOURIER, reupload_scaling = ReuploadScaling.TOWER)
print(f"{fm = }")
fm = KronBlock(0,1,2) [tag: Constant Fourier FM]
├── RX(0) [params: ['phi']]
├── RX(1) [params: ['phi']]
└── RX(2) [params: ['phi']]
fm = KronBlock(0,1,2) [tag: Constant Chebyshev FM]
├── RX(0) [params: ['acos(phi)']]
├── RX(1) [params: ['acos(phi)']]
└── RX(2) [params: ['acos(phi)']]
fm = KronBlock(0,1,2) [tag: Tower Fourier FM]
├── RX(0) [params: ['1_0*phi']]
├── RX(1) [params: ['2_0*phi']]
└── RX(2) [params: ['3_0*phi']]
Source code in qadence/constructors/feature_maps.py
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199def feature_map(
n_qubits: int,
support: tuple[int, ...] | None = None,
param: Parameter | str = "phi",
op: RotationTypes = RX,
fm_type: BasisSet | Callable | str = BasisSet.FOURIER,
reupload_scaling: ReuploadScaling | Callable | str = ReuploadScaling.CONSTANT,
feature_range: tuple[float, float] | None = None,
target_range: tuple[float, float] | None = None,
multiplier: Parameter | TParameter | None = None,
param_prefix: str | None = None,
) -> KronBlock:
"""Construct a feature map of a given type.
Arguments:
n_qubits: Number of qubits the feature map covers. Results in `support=range(n_qubits)`.
support: Puts one feature-encoding rotation gate on every qubit in `support`. n_qubits in
this case specifies the total overall qubits of the circuit, which may be wider than the
support itself, but not narrower.
param: Parameter of the feature map; you can pass a string or Parameter;
it will be set as non-trainable (FeatureParameter) regardless.
op: Rotation operation of the feature map; choose from RX, RY, RZ or PHASE.
fm_type: Basis set for data encoding; choose from `BasisSet.FOURIER` for Fourier
encoding, or `BasisSet.CHEBYSHEV` for Chebyshev polynomials of the first kind.
reupload_scaling: how the feature map scales the data that is re-uploaded for each qubit.
choose from `ReuploadScaling` enumeration or provide your own function with a single
int as input and int or float as output.
feature_range: range of data that the input data provided comes from. Used to map input data
to the correct domain of the feature-encoding function.
target_range: range of data the data encoder assumes as the natural range. For example,
in Chebyshev polynomials it is (-1, 1), while for Fourier it may be chosen as (0, 2*PI).
Used to map data to the correct domain of the feature-encoding function.
multiplier: overall multiplier; this is useful for reuploading the feature map serially with
different scalings; can be a number or parameter/expression.
param_prefix: string prefix to create trainable parameters multiplying the feature parameter
inside the feature-encoding function. Note that currently this does not take into
account the domain of the feature-encoding function.
Example:
```python exec="on" source="material-block" result="json"
from qadence import feature_map, BasisSet, ReuploadScaling
fm = feature_map(3, fm_type=BasisSet.FOURIER)
print(f"{fm = }")
fm = feature_map(3, fm_type=BasisSet.CHEBYSHEV)
print(f"{fm = }")
fm = feature_map(3, fm_type=BasisSet.FOURIER, reupload_scaling = ReuploadScaling.TOWER)
print(f"{fm = }")
```
"""
# Process input
if support is None:
support = tuple(range(n_qubits))
elif len(support) != n_qubits:
raise ValueError("Wrong qubit support supplied")
if op not in ROTATIONS:
raise ValueError(
f"Operation {op} not supported. "
f"Please provide one from {[rot.__name__ for rot in ROTATIONS]}."
)
scaled_fparam = fm_parameter_scaling(
fm_type, param, feature_range=feature_range, target_range=target_range
)
transform_func = fm_parameter_func(fm_type)
basis_tag = fm_type.value if isinstance(fm_type, BasisSet) else str(fm_type)
rs_func, rs_tag = fm_reupload_scaling_fn(reupload_scaling)
# Set overall multiplier
multiplier = 1 if multiplier is None else Parameter(multiplier)
# Build feature map
op_list = []
fparam = scaled_fparam
for i, qubit in enumerate(support):
if param_prefix is not None:
train_param = VariationalParameter(param_prefix + f"_{i}")
fparam = train_param * scaled_fparam
op_list.append(op(qubit, multiplier * rs_func(i) * transform_func(fparam)))
fm = kron(*op_list)
fm.tag = rs_tag + " " + basis_tag + " FM"
return fm

hea(n_qubits, depth=1, param_prefix='theta', support=None, strategy=Strategy.DIGITAL, **strategy_args)

Section titled “ hea(n_qubits, depth=1, param_prefix='theta', support=None, strategy=Strategy.DIGITAL, **strategy_args) ”

Factory function for the Hardware Efficient Ansatz (HEA).

PARAMETER DESCRIPTION
n_qubits

number of qubits in the circuit

TYPE: int

depth

number of layers of the HEA

TYPE: int DEFAULT: 1

param_prefix

the base name of the variational parameters

TYPE: str DEFAULT: 'theta'

support

qubit indices where the HEA is applied

TYPE: tuple[int, ...] | None DEFAULT: None

strategy

Strategy for the ansatz. One of the Strategy variants.

TYPE: Strategy DEFAULT: DIGITAL

**strategy_args

see below

TYPE: Any DEFAULT: {}

PARAMETER DESCRIPTION
operations

list of operations to cycle through in the digital single-qubit rotations of each layer. Valid for Digital and DigitalAnalog HEA.

TYPE: list

periodic

if the qubits should be linked periodically. periodic=False is not supported in emu-c. Valid for only for Digital HEA.

TYPE: bool

entangler
  • Digital: 2-qubit entangling operation. Supports CNOT, CZ, CRX, CRY, CRZ, CPHASE. Controlled rotations will have variational parameters on the rotation angles.
  • SDAQC | Analog: Hamiltonian generator for the analog entangling layer. Defaults to global ZZ Hamiltonian. Time parameter is considered variational.

TYPE: AbstractBlock

RETURNS DESCRIPTION
AbstractBlock

The Hardware Efficient Ansatz (HEA) circuit.

Examples:

from qadence import RZ, RX
from qadence import hea
# create the circuit
n_qubits, depth = 2, 4
ansatz = hea(
n_qubits=n_qubits,
depth=depth,
strategy="sDAQC",
operations=[RZ,RX,RZ]
)
Source code in qadence/constructors/hea.py
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90def hea(
n_qubits: int,
depth: int = 1,
param_prefix: str = "theta",
support: tuple[int, ...] | None = None,
strategy: Strategy = Strategy.DIGITAL,
**strategy_args: Any,
) -> AbstractBlock:
"""
Factory function for the Hardware Efficient Ansatz (HEA).
Args:
n_qubits: number of qubits in the circuit
depth: number of layers of the HEA
param_prefix: the base name of the variational parameters
support: qubit indices where the HEA is applied
strategy: Strategy for the ansatz. One of the Strategy variants.
**strategy_args: see below
Keyword Arguments:
operations (list): list of operations to cycle through in the
digital single-qubit rotations of each layer. Valid for
Digital and DigitalAnalog HEA.
periodic (bool): if the qubits should be linked periodically.
periodic=False is not supported in emu-c. Valid for only
for Digital HEA.
entangler (AbstractBlock):
- Digital: 2-qubit entangling operation. Supports CNOT, CZ,
CRX, CRY, CRZ, CPHASE. Controlled rotations will have variational
parameters on the rotation angles.
- SDAQC | Analog: Hamiltonian generator for the
analog entangling layer. Defaults to global ZZ Hamiltonian.
Time parameter is considered variational.
Returns:
The Hardware Efficient Ansatz (HEA) circuit.
Examples:
```python exec="on" source="material-block" result="json"
from qadence import RZ, RX
from qadence import hea
# create the circuit
n_qubits, depth = 2, 4
ansatz = hea(
n_qubits=n_qubits,
depth=depth,
strategy="sDAQC",
operations=[RZ,RX,RZ]
)
```
"""
if support is None:
support = tuple(range(n_qubits))
hea_func_dict = {
Strategy.DIGITAL: hea_digital,
Strategy.SDAQC: hea_sDAQC,
Strategy.BDAQC: hea_bDAQC,
Strategy.ANALOG: hea_analog,
}
try:
hea_func = hea_func_dict[strategy]
except KeyError:
raise KeyError(f"Strategy {strategy} not recognized.")
hea_block: AbstractBlock = hea_func(
n_qubits=n_qubits,
depth=depth,
param_prefix=param_prefix,
support=support,
**strategy_args,
) # type: ignore
return hea_block

hea_digital(n_qubits, depth=1, param_prefix='theta', support=None, periodic=False, operations=[RX, RY, RX], entangler=CNOT)

Section titled “ hea_digital(n_qubits, depth=1, param_prefix='theta', support=None, periodic=False, operations=[RX, RY, RX], entangler=CNOT) ”

Construct the Digital Hardware Efficient Ansatz (HEA).

PARAMETER DESCRIPTION
n_qubits

number of qubits in the cricuit.

TYPE: int

depth

number of layers of the HEA.

TYPE: int DEFAULT: 1

param_prefix

the base name of the variational parameters

TYPE: str DEFAULT: 'theta'

support

qubit indices where the HEA is applied.

TYPE: tuple DEFAULT: None

periodic

if the qubits should be linked periodically. periodic=False is not supported in emu-c.

TYPE: bool DEFAULT: False

operations

list of operations to cycle through in the digital single-qubit rotations of each layer.

TYPE: list DEFAULT: [RX, RY, RX]

entangler

2-qubit entangling operation. Supports CNOT, CZ, CRX, CRY, CRZ. Controlld rotations will have variational parameters on the rotation angles.

TYPE: AbstractBlock DEFAULT: CNOT

RETURNS DESCRIPTION
AbstractBlock

The digital Hardware Efficient Ansatz (HEA) circuit.

Source code in qadence/constructors/hea.py
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275def hea_digital(
n_qubits: int,
depth: int = 1,
param_prefix: str = "theta",
support: tuple[int, ...] | None = None,
periodic: bool = False,
operations: list[type[AbstractBlock]] = [RX, RY, RX],
entangler: Type[DigitalEntanglers] = CNOT,
) -> AbstractBlock:
"""
Construct the Digital Hardware Efficient Ansatz (HEA).
Args:
n_qubits (int): number of qubits in the cricuit.
depth (int): number of layers of the HEA.
param_prefix (str): the base name of the variational parameters
support (tuple): qubit indices where the HEA is applied.
periodic (bool): if the qubits should be linked periodically.
periodic=False is not supported in emu-c.
operations (list): list of operations to cycle through in the
digital single-qubit rotations of each layer.
entangler (AbstractBlock): 2-qubit entangling operation.
Supports CNOT, CZ, CRX, CRY, CRZ. Controlld rotations
will have variational parameters on the rotation angles.
Returns:
The digital Hardware Efficient Ansatz (HEA) circuit.
"""
try:
if entangler not in [CNOT, CZ, CRX, CRY, CRZ, CPHASE]:
raise ValueError(
"Please provide a valid two-qubit entangler operation for digital HEA."
)
except TypeError:
raise ValueError("Please provide a valid two-qubit entangler operation for digital HEA.")
rot_list = _rotations_digital(
n_qubits=n_qubits,
depth=depth,
param_prefix=param_prefix,
support=support,
operations=operations,
)
ent_list = _entanglers_digital(
n_qubits=n_qubits,
depth=depth,
param_prefix=param_prefix,
support=support,
periodic=periodic,
entangler=entangler,
)
layers = []
for d in range(depth):
layers.append(rot_list[d])
layers.append(ent_list[d])
return tag(chain(*layers), "HEA")

hea_sDAQC(n_qubits, depth=1, param_prefix='theta', support=None, operations=[RX, RY, RX], entangler=None)

Section titled “ hea_sDAQC(n_qubits, depth=1, param_prefix='theta', support=None, operations=[RX, RY, RX], entangler=None) ”

Construct the Hardware Efficient Ansatz (HEA) with analog entangling layers.

It uses step-wise digital-analog computation.

PARAMETER DESCRIPTION
n_qubits

number of qubits in the circuit.

TYPE: int

depth

number of layers of the HEA.

TYPE: int DEFAULT: 1

param_prefix

the base name of the variational parameters

TYPE: str DEFAULT: 'theta'

support

qubit indices where the HEA is applied.

TYPE: tuple DEFAULT: None

operations

list of operations to cycle through in the digital single-qubit rotations of each layer.

TYPE: list DEFAULT: [RX, RY, RX]

entangler

Hamiltonian generator for the analog entangling layer. Defaults to global ZZ Hamiltonian. Time parameter is considered variational.

TYPE: AbstractBlock DEFAULT: None

RETURNS DESCRIPTION
AbstractBlock

The step-wise digital-analog Hardware Efficient Ansatz (sDA HEA) circuit.

Source code in qadence/constructors/hea.py
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361def hea_sDAQC(
n_qubits: int,
depth: int = 1,
param_prefix: str = "theta",
support: tuple[int, ...] | None = None,
operations: list[type[AbstractBlock]] = [RX, RY, RX],
entangler: AbstractBlock | None = None,
) -> AbstractBlock:
"""
Construct the Hardware Efficient Ansatz (HEA) with analog entangling layers.
It uses step-wise digital-analog computation.
Args:
n_qubits (int): number of qubits in the circuit.
depth (int): number of layers of the HEA.
param_prefix (str): the base name of the variational parameters
support (tuple): qubit indices where the HEA is applied.
operations (list): list of operations to cycle through in the
digital single-qubit rotations of each layer.
entangler (AbstractBlock): Hamiltonian generator for the
analog entangling layer. Defaults to global ZZ Hamiltonian.
Time parameter is considered variational.
Returns:
The step-wise digital-analog Hardware Efficient Ansatz (sDA HEA) circuit.
"""
# TODO: Add qubit support
if entangler is None:
entangler = hamiltonian_factory(n_qubits, interaction=Interaction.NN)
try:
if not block_is_qubit_hamiltonian(entangler):
raise ValueError(
"Please provide a valid Pauli Hamiltonian generator for digital-analog HEA."
)
except NotImplementedError:
raise ValueError(
"Please provide a valid Pauli Hamiltonian generator for digital-analog HEA."
)
rot_list = _rotations_digital(
n_qubits=n_qubits,
depth=depth,
param_prefix=param_prefix,
support=support,
operations=operations,
)
ent_list = _entanglers_analog(
depth=depth,
param_prefix=param_prefix,
entangler=entangler,
)
layers = []
for d in range(depth):
layers.append(rot_list[d])
layers.append(ent_list[d])
return tag(chain(*layers), "HEA-sDA")

identity_initialized_ansatz(n_qubits, depth=1, param_prefix='iia', strategy=Strategy.DIGITAL, rotations=[RX, RY], entangler=None, periodic=False)

Section titled “ identity_initialized_ansatz(n_qubits, depth=1, param_prefix='iia', strategy=Strategy.DIGITAL, rotations=[RX, RY], entangler=None, periodic=False) ”

Identity block for barren plateau mitigation.

The initial configuration of this block is equal to an identity unitary but can be trained in the same fashion as other ansatzes, reaching same level of expressivity.

PARAMETER DESCRIPTION
n_qubits

number of qubits in the block

TYPE: int

depth

number of layers of the HEA

TYPE: int DEFAULT: 1

param_prefix

The base name of the variational parameter. Defaults to "iia".

TYPE: str DEFAULT: 'iia'

strategy

(Strategy) Strategy.DIGITAL for fully digital or Strategy.SDAQC for digital-analog.

TYPE: Strategy DEFAULT: DIGITAL

rotations

single-qubit rotations with trainable parameters

TYPE: list of AbstractBlocks DEFAULT: [RX, RY]

entangler

For Digital: 2-qubit entangling operation. Supports CNOT, CZ, CRX, CRY, CRZ, CPHASE. Controlled rotations will have variational parameters on the rotation angles. Defaults to CNOT. For Digital-analog: Hamiltonian generator for the analog entangling layer. Time parameter is considered variational. Defaults to a global NN Hamiltonain.

TYPE: AbstractBlock DEFAULT: None

periodic

if the qubits should be linked periodically. Valid only for digital.

TYPE: bool DEFAULT: False

RETURNS DESCRIPTION
AbstractBlock

The identity initialized ansatz circuit.

Source code in qadence/constructors/iia.py
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242def identity_initialized_ansatz(
n_qubits: int,
depth: int = 1,
param_prefix: str = "iia",
strategy: Strategy = Strategy.DIGITAL,
rotations: Any = [RX, RY],
entangler: Any = None,
periodic: bool = False,
) -> AbstractBlock:
"""
Identity block for barren plateau mitigation.
The initial configuration of this block is equal to an identity unitary
but can be trained in the same fashion as other ansatzes, reaching same level
of expressivity.
Args:
n_qubits: number of qubits in the block
depth: number of layers of the HEA
param_prefix (str):
The base name of the variational parameter. Defaults to "iia".
strategy: (Strategy)
Strategy.DIGITAL for fully digital or Strategy.SDAQC for digital-analog.
rotations (list of AbstractBlocks):
single-qubit rotations with trainable parameters
entangler (AbstractBlock):
For Digital:
2-qubit entangling operation. Supports CNOT, CZ, CRX, CRY, CRZ, CPHASE.
Controlled rotations will have variational parameters on the rotation angles.
Defaults to CNOT.
For Digital-analog:
Hamiltonian generator for the analog entangling layer.
Time parameter is considered variational.
Defaults to a global NN Hamiltonain.
periodic (bool): if the qubits should be linked periodically. Valid only for digital.
Returns:
The identity initialized ansatz circuit.
"""
initialized_layers = []
for layer in range(depth):
alpha = 2 * PI * torch.rand(n_qubits * len(rotations))
gamma = torch.zeros(n_qubits)
beta = -alpha
left_rotations = _rotations(
n_qubits=n_qubits,
layer=layer,
side="left",
param_str=f"{param_prefix}_α",
values=alpha,
ops=rotations,
)
if strategy == Strategy.DIGITAL:
if entangler is None:
entangler = CNOT
if entangler not in [CNOT, CZ, CRZ, CRY, CRX, CPHASE]:
raise ValueError(
"Please provide a valid two-qubit entangler operation for digital IIA."
)
ent_param_prefix = f"{param_prefix}_θ_ent_"
if not periodic:
left_entanglers = [
chain(
_entangler(
control=n,
target=n + 1,
param_str=ent_param_prefix + f"_{layer}{n}",
entangler=entangler,
)
for n in range(n_qubits - 1)
)
]
else:
left_entanglers = [
chain(
_entangler(
control=n,
target=(n + 1) % n_qubits,
param_str=ent_param_prefix + f"_{layer}{n}",
entangler=entangler,
)
for n in range(n_qubits)
)
]
elif strategy == Strategy.SDAQC:
if entangler is None:
entangler = hamiltonian_factory(n_qubits, interaction=Interaction.NN)
if not block_is_qubit_hamiltonian(entangler):
raise ValueError(
"Please provide a valid Pauli Hamiltonian generator for digital-analog IIA."
)
ent_param_prefix = f"{param_prefix}_ent_t"
left_entanglers = [
chain(
_entangler_analog(
param_str=f"{ent_param_prefix}_{layer}",
generator=entangler,
)
)
]
else:
raise NotImplementedError
centre_rotations = [
kron(
RX(
target=n,
parameter=Parameter(name=f"{param_prefix}_γ" + f"_{layer}{n}", value=gamma[n]),
)
for n in range(n_qubits)
)
]
right_entanglers = reversed(*left_entanglers)
right_rotations = _rotations(
n_qubits=n_qubits,
layer=layer,
side="right",
param_str=f"{param_prefix}_β",
values=beta,
ops=rotations,
)
krons = [
*left_rotations,
*left_entanglers,
*centre_rotations,
*right_entanglers,
*right_rotations,
]
initialized_layers.append(tag(chain(*krons), tag=f"BPMA-{layer}"))
return chain(*initialized_layers)

ala(n_qubits, m_block_qubits, depth=1, param_prefix='theta', support=None, strategy=Strategy.DIGITAL, **strategy_args)

Section titled “ ala(n_qubits, m_block_qubits, depth=1, param_prefix='theta', support=None, strategy=Strategy.DIGITAL, **strategy_args) ”

Factory function for the alternating layer ansatz (ala).

PARAMETER DESCRIPTION
n_qubits

number of qubits in the circuit

TYPE: int

m_block_qubits

number of qubits in the local entangling block

TYPE: int

depth

number of layers of the alternating layer ansatz

TYPE: int DEFAULT: 1

param_prefix

the base name of the variational parameters

TYPE: str DEFAULT: 'theta'

support

qubit indices where the ala is applied

TYPE: tuple[int, ...] | None DEFAULT: None

strategy

Strategy for the ansatz. One of the Strategy variants.

TYPE: Strategy DEFAULT: DIGITAL

**strategy_args

see below

TYPE: Any DEFAULT: {}

PARAMETER DESCRIPTION
operations

list of operations to cycle through in the digital single-qubit rotations of each layer. Valid for Digital .

TYPE: list

entangler
  • Digital: 2-qubit entangling operation. Supports CNOT, CZ, CRX, CRY, CRZ, CPHASE. Controlled rotations will have variational parameters on the rotation angles.
  • SDAQC | BDAQC: Hamiltonian generator for the analog entangling layer. Must be an m-qubit operator where m is the size of the local entangling block. Defaults to a ZZ interaction.

TYPE: AbstractBlock

RETURNS DESCRIPTION
AbstractBlock

The Alternating Layer Ansatz (ALA) circuit.

Source code in qadence/constructors/ala.py
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74def ala(
n_qubits: int,
m_block_qubits: int,
depth: int = 1,
param_prefix: str = "theta",
support: tuple[int, ...] | None = None,
strategy: Strategy = Strategy.DIGITAL,
**strategy_args: Any,
) -> AbstractBlock:
"""
Factory function for the alternating layer ansatz (ala).
Args:
n_qubits: number of qubits in the circuit
m_block_qubits: number of qubits in the local entangling block
depth: number of layers of the alternating layer ansatz
param_prefix: the base name of the variational parameters
support: qubit indices where the ala is applied
strategy: Strategy for the ansatz. One of the Strategy variants.
**strategy_args: see below
Keyword Arguments:
operations (list): list of operations to cycle through in the
digital single-qubit rotations of each layer. Valid for
Digital .
entangler (AbstractBlock):
- Digital: 2-qubit entangling operation. Supports CNOT, CZ,
CRX, CRY, CRZ, CPHASE. Controlled rotations will have variational
parameters on the rotation angles.
- SDAQC | BDAQC: Hamiltonian generator for the analog entangling
layer. Must be an m-qubit operator where m is the size of the
local entangling block. Defaults to a ZZ interaction.
Returns:
The Alternating Layer Ansatz (ALA) circuit.
"""
if support is None:
support = tuple(range(n_qubits))
ala_func_dict = {
Strategy.DIGITAL: ala_digital,
Strategy.SDAQC: ala_sDAQC,
Strategy.BDAQC: ala_bDAQC,
Strategy.ANALOG: ala_analog,
}
try:
ala_func = ala_func_dict[strategy]
except KeyError:
raise KeyError(f"Strategy {strategy} not recognized.")
ala_block: AbstractBlock = ala_func(
n_qubits=n_qubits,
m_block_qubits=m_block_qubits,
depth=depth,
param_prefix=param_prefix,
support=support,
**strategy_args,
) # type: ignore
return ala_block

ala_digital(n_qubits, m_block_qubits, depth=1, param_prefix='theta', support=None, operations=[RX, RY], entangler=CNOT)

Section titled “ ala_digital(n_qubits, m_block_qubits, depth=1, param_prefix='theta', support=None, operations=[RX, RY], entangler=CNOT) ”

Construct the digital alternating layer ansatz (ALA).

PARAMETER DESCRIPTION
n_qubits

number of qubits in the circuit.

TYPE: int

m_block_qubits

number of qubits in the local entangling block.

TYPE: int

depth

number of layers of the ALA.

TYPE: int DEFAULT: 1

param_prefix

the base name of the variational parameters

TYPE: str DEFAULT: 'theta'

support

qubit indices where the ALA is applied.

TYPE: tuple DEFAULT: None

operations

list of operations to cycle through in the digital single-qubit rotations of each layer.

TYPE: list DEFAULT: [RX, RY]

entangler

2-qubit entangling operation. Supports CNOT, CZ, CRX, CRY, CRZ. Controlld rotations will have variational parameters on the rotation angles.

TYPE: AbstractBlock DEFAULT: CNOT

RETURNS DESCRIPTION
AbstractBlock

The digital Alternating Layer Ansatz (ALA) circuit.

Source code in qadence/constructors/ala.py
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249def ala_digital(
n_qubits: int,
m_block_qubits: int,
depth: int = 1,
param_prefix: str = "theta",
support: tuple[int, ...] | None = None,
operations: list[type[AbstractBlock]] = [RX, RY],
entangler: Type[DigitalEntanglers] = CNOT,
) -> AbstractBlock:
"""
Construct the digital alternating layer ansatz (ALA).
Args:
n_qubits (int): number of qubits in the circuit.
m_block_qubits (int): number of qubits in the local entangling block.
depth (int): number of layers of the ALA.
param_prefix (str): the base name of the variational parameters
support (tuple): qubit indices where the ALA is applied.
operations (list): list of operations to cycle through in the
digital single-qubit rotations of each layer.
entangler (AbstractBlock): 2-qubit entangling operation.
Supports CNOT, CZ, CRX, CRY, CRZ. Controlld rotations
will have variational parameters on the rotation angles.
Returns:
The digital Alternating Layer Ansatz (ALA) circuit.
"""
try:
if entangler not in [CNOT, CZ, CRX, CRY, CRZ, CPHASE]:
raise ValueError(
"Please provide a valid two-qubit entangler operation for digital ALA."
)
except TypeError:
raise ValueError("Please provide a valid two-qubit entangler operation for digital ALA.")
rot_list = _rotations_digital(
n_qubits=n_qubits,
depth=depth,
support=support,
param_prefix=param_prefix,
operations=operations,
)
ent_list = _entanglers_ala_block_digital(
n_qubits,
m_block_qubits,
param_prefix=param_prefix + "_ent",
depth=depth,
support=support,
entangler=entangler,
)
layers = []
for d in range(depth):
layers.append(rot_list[d])
layers.append(ent_list[d])
return tag(chain(*layers), "ALA")

ObservableConfig(interaction=None, detuning=None, scale=1.0, shift=0.0, trainable_transform=None, tag=None) dataclass

Section titled “ ObservableConfig(interaction=None, detuning=None, scale=1.0, shift=0.0, trainable_transform=None, tag=None) dataclass ”

ObservableConfig is a configuration class for defining the parameters of an observable Hamiltonian.

detuning = None class-attribute instance-attribute

Section titled “ detuning = None class-attribute instance-attribute ”

Single qubit detuning of the observable Hamiltonian.

Accepts single-qubit operator N, X, Y, or Z.

interaction = None class-attribute instance-attribute

Section titled “ interaction = None class-attribute instance-attribute ”

The type of interaction.

Available options from the Interaction enum are

Alternatively, a custom interaction function can be defined. Example:

def custom_int(i: int, j: int):
return X(i) @ X(j) + Y(i) @ Y(j)
n_qubits = 2
observable_config = ObservableConfig(interaction=custom_int, scale = 1.0, shift = 0.0)
observable = create_observable(register=4, config=observable_config)

scale = 1.0 class-attribute instance-attribute

Section titled “ scale = 1.0 class-attribute instance-attribute ”

The scale by which to multiply the output of the observable.

shift = 0.0 class-attribute instance-attribute

Section titled “ shift = 0.0 class-attribute instance-attribute ”

The shift to add to the output of the observable.

tag = None class-attribute instance-attribute

Section titled “ tag = None class-attribute instance-attribute ”

String to indicate the name tag of the observable.

Defaults to None, in which case no tag will be applied.

trainable_transform = None class-attribute instance-attribute

Section titled “ trainable_transform = None class-attribute instance-attribute ”

Whether to have a trainable transformation on the output of the observable.

If None, the scale and shift are numbers. If True, the scale and shift are VariationalParameter. If False, the scale and shift are FeatureParameter.

hamiltonian_factory(register, interaction=None, detuning=None, interaction_strength=None, detuning_strength=None, random_strength=False, use_all_node_pairs=False)

Section titled “ hamiltonian_factory(register, interaction=None, detuning=None, interaction_strength=None, detuning_strength=None, random_strength=False, use_all_node_pairs=False) ”

General Hamiltonian creation function.

Can be used to create Hamiltonians with 2-qubit interactions and single-qubit detunings, both with arbitrary strength or parameterized.

PARAMETER DESCRIPTION
register

register of qubits with a specific graph topology, or number of qubits. When passing a number of qubits a register with all-to-all connectivity is created.

TYPE: Register | int

interaction

Interaction.ZZ, Interaction.NN, Interaction.XY, or Interacton.XYZ.

TYPE: Interaction | Callable | None DEFAULT: None

detuning

single-qubit operator N, X, Y, or Z.

TYPE: TDetuning | None DEFAULT: None

interaction_strength

list of values to be used as the interaction strength for each pair of qubits. Should be ordered following the order of Register(n_qubits).edges. Alternatively, some string "x" can be passed, which will create a parameterized interactions for each pair of qubits, each labelled as "x_ij".

TYPE: TArray | str | None DEFAULT: None

detuning_strength

list of values to be used as the detuning strength for each qubit. Alternatively, some string "x" can be passed, which will create a parameterized detuning for each qubit, each labelled as "x_i".

TYPE: TArray | str | None DEFAULT: None

random_strength

set random interaction and detuning strengths between -1 and 1.

TYPE: bool DEFAULT: False

use_all_node_pairs

computes an interaction term for every pair of nodes in the graph, independent of the edge topology in the register. Useful for defining Hamiltonians where the interaction strength decays with the distance.

TYPE: bool DEFAULT: False

Examples:

from qadence import hamiltonian_factory, Interaction, Register, Z
n_qubits = 3
# Constant total magnetization observable:
observable = hamiltonian_factory(n_qubits, detuning = Z)
# Parameterized total magnetization observable:
observable = hamiltonian_factory(n_qubits, detuning = Z, detuning_strength = "z")
# Random all-to-all XY Hamiltonian generator:
generator = hamiltonian_factory(
n_qubits,
interaction = Interaction.XY,
random_strength = True,
)
# Parameterized NN Hamiltonian generator with a square grid interaction topology:
register = Register.square(qubits_side = n_qubits)
generator = hamiltonian_factory(
register,
interaction = Interaction.NN,
interaction_strength = "theta"
)
Source code in qadence/constructors/hamiltonians.py
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166def hamiltonian_factory(
register: Register | int,
interaction: Interaction | Callable | None = None,
detuning: TDetuning | None = None,
interaction_strength: TArray | str | None = None,
detuning_strength: TArray | str | None = None,
random_strength: bool = False,
use_all_node_pairs: bool = False,
) -> AbstractBlock:
"""
General Hamiltonian creation function.
Can be used to create Hamiltonians with 2-qubit
interactions and single-qubit detunings, both with arbitrary strength or parameterized.
Arguments:
register: register of qubits with a specific graph topology, or number of qubits.
When passing a number of qubits a register with all-to-all connectivity
is created.
interaction: Interaction.ZZ, Interaction.NN, Interaction.XY, or Interacton.XYZ.
detuning: single-qubit operator N, X, Y, or Z.
interaction_strength: list of values to be used as the interaction strength for each
pair of qubits. Should be ordered following the order of `Register(n_qubits).edges`.
Alternatively, some string "x" can be passed, which will create a parameterized
interactions for each pair of qubits, each labelled as `"x_ij"`.
detuning_strength: list of values to be used as the detuning strength for each qubit.
Alternatively, some string "x" can be passed, which will create a parameterized
detuning for each qubit, each labelled as `"x_i"`.
random_strength: set random interaction and detuning strengths between -1 and 1.
use_all_node_pairs: computes an interaction term for every pair of nodes in the graph,
independent of the edge topology in the register. Useful for defining Hamiltonians
where the interaction strength decays with the distance.
Examples:
```python exec="on" source="material-block" result="json"
from qadence import hamiltonian_factory, Interaction, Register, Z
n_qubits = 3
# Constant total magnetization observable:
observable = hamiltonian_factory(n_qubits, detuning = Z)
# Parameterized total magnetization observable:
observable = hamiltonian_factory(n_qubits, detuning = Z, detuning_strength = "z")
# Random all-to-all XY Hamiltonian generator:
generator = hamiltonian_factory(
n_qubits,
interaction = Interaction.XY,
random_strength = True,
)
# Parameterized NN Hamiltonian generator with a square grid interaction topology:
register = Register.square(qubits_side = n_qubits)
generator = hamiltonian_factory(
register,
interaction = Interaction.NN,
interaction_strength = "theta"
)
```
"""
if interaction is None and detuning is None:
raise ValueError("Please provide an interaction and/or detuning for the Hamiltonian.")
# If number of qubits is given, creates all-to-all register
register = Register(register) if isinstance(register, int) else register
# Get interaction function
if interaction is not None:
if callable(interaction):
int_fn = interaction
try:
if not block_is_qubit_hamiltonian(interaction(0, 1)):
raise ValueError("Custom interactions must be composed of Pauli operators.")
except TypeError:
raise TypeError(
"Please use a custom interaction function signed with two integer parameters."
)
else:
int_fn = INTERACTION_DICT.get(interaction, None) # type: ignore [arg-type]
if int_fn is None:
raise KeyError(f"Interaction {interaction} not supported.")
# Check single-qubit detuning
if (detuning is not None) and (detuning not in DETUNINGS):
raise TypeError(f"Detuning of type {type(detuning)} not supported.")
# Pre-process detuning and interaction strengths and update register
detuning_strength_array = _preprocess_strengths(
register, detuning_strength, "nodes", random_strength
)
edge_str = "all_node_pairs" if use_all_node_pairs else "edges"
interaction_strength_array = _preprocess_strengths(
register, interaction_strength, edge_str, random_strength
)
# Create single-qubit detunings:
single_qubit_terms: List[AbstractBlock] = []
if detuning is not None:
for strength, node in zip(detuning_strength_array, register.nodes):
single_qubit_terms.append(strength * detuning(node))
# Create two-qubit interactions:
two_qubit_terms: List[AbstractBlock] = []
edge_data = register.all_node_pairs if use_all_node_pairs else register.edges
if interaction is not None and int_fn is not None:
for strength, edge in zip(interaction_strength_array, edge_data):
two_qubit_terms.append(strength * int_fn(*edge))
return add(*single_qubit_terms, *two_qubit_terms)

Ising NN interaction.

Source code in qadence/constructors/hamiltonians.py
25
26
27def interaction_nn(i: int, j: int) -> AbstractBlock:
"""Ising NN interaction."""
return N(i) @ N(j)

XY interaction.

Source code in qadence/constructors/hamiltonians.py
30
31
32def interaction_xy(i: int, j: int) -> AbstractBlock:
"""XY interaction."""
return X(i) @ X(j) + Y(i) @ Y(j)

Heisenberg XYZ interaction.

Source code in qadence/constructors/hamiltonians.py
35
36
37def interaction_xyz(i: int, j: int) -> AbstractBlock:
"""Heisenberg XYZ interaction."""
return X(i) @ X(j) + Y(i) @ Y(j) + Z(i) @ Z(j)

Ising ZZ interaction.

Source code in qadence/constructors/hamiltonians.py
20
21
22def interaction_zz(i: int, j: int) -> AbstractBlock:
"""Ising ZZ interaction."""
return Z(i) @ Z(j)

qft(n_qubits, support=None, inverse=False, reverse_in=False, swaps_out=False, strategy=Strategy.DIGITAL, gen_build=None)

Section titled “ qft(n_qubits, support=None, inverse=False, reverse_in=False, swaps_out=False, strategy=Strategy.DIGITAL, gen_build=None) ”

The Quantum Fourier Transform.

Depending on the application, user should be careful with qubit ordering in the input and output. This can be controlled with reverse_in and swaps_out arguments.

PARAMETER DESCRIPTION
n_qubits

number of qubits in the QFT

TYPE: int

support

qubit support to use

TYPE: tuple[int, ...] DEFAULT: None

inverse

True performs the inverse QFT

TYPE: bool DEFAULT: False

reverse_in

Reverses the input qubits to account for endianness

TYPE: bool DEFAULT: False

swaps_out

Performs swaps on the output qubits to match the "textbook" QFT.

TYPE: bool DEFAULT: False

strategy

Strategy.Digital or Strategy.sDAQC

TYPE: Strategy DEFAULT: DIGITAL

gen_build

building block Ising Hamiltonian for the DAQC transform. Defaults to constant all-to-all Ising.

TYPE: AbstractBlock | None DEFAULT: None

Examples:

from qadence import qft
n_qubits = 3
qft_circuit = qft(n_qubits, strategy = "sDAQC")
Source code in qadence/constructors/qft.py
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84def qft(
n_qubits: int,
support: tuple[int, ...] = None,
inverse: bool = False,
reverse_in: bool = False,
swaps_out: bool = False,
strategy: Strategy = Strategy.DIGITAL,
gen_build: AbstractBlock | None = None,
) -> AbstractBlock:
"""
The Quantum Fourier Transform.
Depending on the application, user should be careful with qubit ordering
in the input and output. This can be controlled with reverse_in and swaps_out
arguments.
Args:
n_qubits: number of qubits in the QFT
support: qubit support to use
inverse: True performs the inverse QFT
reverse_in: Reverses the input qubits to account for endianness
swaps_out: Performs swaps on the output qubits to match the "textbook" QFT.
strategy: Strategy.Digital or Strategy.sDAQC
gen_build: building block Ising Hamiltonian for the DAQC transform.
Defaults to constant all-to-all Ising.
Examples:
```python exec="on" source="material-block" result="json"
from qadence import qft
n_qubits = 3
qft_circuit = qft(n_qubits, strategy = "sDAQC")
```
"""
if support is None:
support = tuple(range(n_qubits))
assert len(support) <= n_qubits, "Wrong qubit support supplied"
if reverse_in:
support = support[::-1]
qft_layer_dict = {
Strategy.DIGITAL: _qft_layer_digital,
Strategy.SDAQC: _qft_layer_sDAQC,
Strategy.BDAQC: _qft_layer_bDAQC,
Strategy.ANALOG: _qft_layer_analog,
}
try:
layer_func = qft_layer_dict[strategy]
except KeyError:
raise KeyError(f"Strategy {strategy} not recognized.")
qft_layers = reversed(range(n_qubits)) if inverse else range(n_qubits)
qft_circ = chain(
layer_func(
n_qubits=n_qubits, support=support, layer=layer, inverse=inverse, gen_build=gen_build
) # type: ignore
for layer in qft_layers
)
if swaps_out:
swap_ops = [SWAP(support[i], support[n_qubits - i - 1]) for i in range(n_qubits // 2)]
qft_circ = chain(*swap_ops, qft_circ) if inverse else chain(qft_circ, *swap_ops)
return tag(qft_circ, tag="iQFT") if inverse else tag(qft_circ, tag="QFT")

Hardware efficient ansatz for Rydberg atom arrays

Section titled “Hardware efficient ansatz for Rydberg atom arrays”

rydberg_hea(register, n_layers=1, addressable_detuning=True, addressable_drive=False, tunable_phase=False, additional_prefix=None)

Section titled “ rydberg_hea(register, n_layers=1, addressable_detuning=True, addressable_drive=False, tunable_phase=False, additional_prefix=None) ”

Hardware efficient ansatz for neutral atom (Rydberg) platforms.

This constructor implements a variational ansatz which is very close to what is implementable on 2nd generation PASQAL quantum devices. In particular, it implements evolution over a specific Hamiltonian which can be realized on the device. This Hamiltonian contains:

  • an interaction term given by the standard NN interaction and determined starting from the positions in the input register: Hᵢₙₜ = ∑ᵢⱼ C₆/rᵢⱼ⁶ nᵢnⱼ

  • a detuning term which corresponding to a n_i = (1+sigma_i^z)/2 applied to all the qubits. If the addressable_detuning flag is set to True, the routine effectively a local n_i = (1+sigma_i^z)/2 term in the evolved Hamiltonian with a different coefficient for each atom. These coefficients determine a local addressing pattern for the detuning on a subset of the qubits. In this routine, the coefficients are variational parameters and they will therefore be optimized at each optimizer step

  • a drive term which corresponding to a sigma^x evolution operation applied to all the qubits. If the addressable_drive flag is set to True, the routine effectively a local sigma_i^x term in the evolved Hamiltonian with a different coefficient for each atom. These coefficients determine a local addressing pattern for the drive on a subset of the qubits. In this routine, the coefficients are variational parameters and they will therefore be optimized at each optimizer step

  • if the tunable_phase flag is set to True, the drive term is modified in the following way: drive = cos(phi) * sigma^x - sin(phi) * sigma^y The addressable pattern above is maintained and the phase is considered just as an additional variational parameter which is optimized with the rest

Notice that, on real devices, the coefficients assigned to each qubit in both the detuning and drive patterns should be non-negative and they should always sum to 1. This is not the case for the implementation in this routine since the coefficients (weights) do not have any constraint. Therefore, this HEA is not completely realizable on neutral atom devices.

PARAMETER DESCRIPTION
register

the input atomic register with Cartesian coordinates.

TYPE: Register

n_layers

number layers in the HEA, each layer includes a drive, detuning and pure interaction pulses whose is a variational parameter

TYPE: int DEFAULT: 1

addressable_detuning

whether to turn on the trainable semi-local addressing pattern on the detuning (n_i terms in the Hamiltonian)

TYPE: bool DEFAULT: True

addressable_drive

whether to turn on the trainable semi-local addressing pattern on the drive (sigma_i^x terms in the Hamiltonian)

TYPE: bool DEFAULT: False

tunable_phase

whether to have a tunable phase to get both sigma^x and sigma^y rotations in the drive term. If False, only a sigma^x term will be included in the drive part of the Hamiltonian generator

TYPE: bool DEFAULT: False

additional_prefix

an additional prefix to attach to the parameter names

TYPE: str DEFAULT: None

RETURNS DESCRIPTION
ChainBlock

The Rydberg HEA block

Source code in qadence/constructors/rydberg_hea.py
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191def rydberg_hea(
register: qd.Register,
n_layers: int = 1,
addressable_detuning: bool = True,
addressable_drive: bool = False,
tunable_phase: bool = False,
additional_prefix: str = None,
) -> qd.blocks.ChainBlock:
"""Hardware efficient ansatz for neutral atom (Rydberg) platforms.
This constructor implements a variational ansatz which is very close to
what is implementable on 2nd generation PASQAL quantum devices. In particular,
it implements evolution over a specific Hamiltonian which can be realized on
the device. This Hamiltonian contains:
* an interaction term given by the standard NN interaction and determined starting
from the positions in the input register: Hᵢₙₜ = ∑ᵢⱼ C₆/rᵢⱼ⁶ nᵢnⱼ
* a detuning term which corresponding to a n_i = (1+sigma_i^z)/2 applied to
all the qubits. If the `addressable_detuning` flag is set to True, the routine
effectively a local n_i = (1+sigma_i^z)/2 term in the
evolved Hamiltonian with a different coefficient for each atom. These
coefficients determine a local addressing pattern for the detuning on a subset
of the qubits. In this routine, the coefficients are variational parameters
and they will therefore be optimized at each optimizer step
* a drive term which corresponding to a sigma^x evolution operation applied to
all the qubits. If the `addressable_drive` flag is set to True, the routine
effectively a local sigma_i^x term in the evolved Hamiltonian with a different
coefficient for each atom. These coefficients determine a local addressing pattern
for the drive on a subset of the qubits. In this routine, the coefficients are
variational parameters and they will therefore be optimized at each optimizer step
* if the `tunable_phase` flag is set to True, the drive term is modified in the following
way: drive = cos(phi) * sigma^x - sin(phi) * sigma^y
The addressable pattern above is maintained and the phase is considered just as an
additional variational parameter which is optimized with the rest
Notice that, on real devices, the coefficients assigned to each qubit in both the detuning
and drive patterns should be non-negative and they should always sum to 1. This is not the
case for the implementation in this routine since the coefficients (weights) do not have any
constraint. Therefore, this HEA is not completely realizable on neutral atom devices.
Args:
register: the input atomic register with Cartesian coordinates.
n_layers: number layers in the HEA, each layer includes a drive, detuning and
pure interaction pulses whose is a variational parameter
addressable_detuning: whether to turn on the trainable semi-local addressing pattern
on the detuning (n_i terms in the Hamiltonian)
addressable_drive: whether to turn on the trainable semi-local addressing pattern
on the drive (sigma_i^x terms in the Hamiltonian)
tunable_phase: whether to have a tunable phase to get both sigma^x and sigma^y rotations
in the drive term. If False, only a sigma^x term will be included in the drive part
of the Hamiltonian generator
additional_prefix: an additional prefix to attach to the parameter names
Returns:
The Rydberg HEA block
"""
n_qubits = register.n_qubits
prefix = "" if additional_prefix is None else "_" + additional_prefix
detunings = None
# add a detuning pattern locally addressing the atoms
if addressable_detuning:
detunings = [qd.VariationalParameter(f"detmap_{j}") for j in range(n_qubits)]
drives = None
# add a drive pattern locally addressing the atoms
if addressable_drive:
drives = [qd.VariationalParameter(f"drivemap_{j}") for j in range(n_qubits)]
phase = None
if tunable_phase:
phase = qd.VariationalParameter("phase")
return chain(
rydberg_hea_layer(
register,
VariationalParameter(f"At{prefix}_{layer}"),
VariationalParameter(f"Omega{prefix}_{layer}"),
VariationalParameter(f"wait{prefix}_{layer}"),
detunings=detunings,
drives=drives,
phase=phase,
)
for layer in range(n_layers)
)

rydberg_hea_layer(register, tevo_drive, tevo_det, tevo_wait, phase=None, detunings=None, drives=None, drive_scaling=1.0)

Section titled “ rydberg_hea_layer(register, tevo_drive, tevo_det, tevo_wait, phase=None, detunings=None, drives=None, drive_scaling=1.0) ”

A single layer of the Rydberg hardware efficient ansatz.

PARAMETER DESCRIPTION
register

the input register with atomic coordinates needed to build the interaction.

TYPE: Register

tevo_drive

a variational parameter for the duration of the drive term of the Hamiltonian generator, including optional semi-local addressing

TYPE: Parameter | float

tevo_det

a variational parameter for the duration of the detuning term of the Hamiltonian generator, including optional semi-local addressing

TYPE: Parameter | float

tevo_wait

a variational parameter for the duration of the waiting time with interaction only

TYPE: Parameter | float

phase

a variational parameter representing the global phase. If None, the global phase is set to 0 which results in a drive term in sigma^x only. Otherwise both sigma^x and sigma^y terms will be present

TYPE: Parameter | float | None DEFAULT: None

detunings

a list of parameters with the weights of the locally addressed detuning terms. These are variational parameters which are tuned by the optimizer

TYPE: list[Parameter] | list[float] | None DEFAULT: None

drives

a list of parameters with the weights of the locally addressed drive terms. These are variational parameters which are tuned by the optimizer

TYPE: list[Parameter] | list[float] | None DEFAULT: None

drive_scaling

a scaling term to be added to the drive Hamiltonian generator

TYPE: float DEFAULT: 1.0

RETURNS DESCRIPTION
ChainBlock

A block with a single layer of Rydberg HEA

Source code in qadence/constructors/rydberg_hea.py
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101def rydberg_hea_layer(
register: qd.Register,
tevo_drive: Parameter | float,
tevo_det: Parameter | float,
tevo_wait: Parameter | float,
phase: Parameter | float | None = None,
detunings: list[Parameter] | list[float] | None = None,
drives: list[Parameter] | list[float] | None = None,
drive_scaling: float = 1.0,
) -> ChainBlock:
"""A single layer of the Rydberg hardware efficient ansatz.
Args:
register: the input register with atomic coordinates needed to build the interaction.
tevo_drive: a variational parameter for the duration of the drive term of
the Hamiltonian generator, including optional semi-local addressing
tevo_det: a variational parameter for the duration of the detuning term of the
Hamiltonian generator, including optional semi-local addressing
tevo_wait: a variational parameter for the duration of the waiting
time with interaction only
phase: a variational parameter representing the global phase. If None, the
global phase is set to 0 which results in a drive term in sigma^x only. Otherwise
both sigma^x and sigma^y terms will be present
detunings: a list of parameters with the weights of the locally addressed
detuning terms. These are variational parameters which are tuned by the optimizer
drives: a list of parameters with the weights of the locally addressed
drive terms. These are variational parameters which are tuned by the optimizer
drive_scaling: a scaling term to be added to the drive Hamiltonian generator
Returns:
A block with a single layer of Rydberg HEA
"""
n_qubits = register.n_qubits
drive_x = _amplitude_map(n_qubits, qd.X, weights=drives)
drive_y = _amplitude_map(n_qubits, qd.Y, weights=drives)
detuning = _amplitude_map(n_qubits, qd.N, weights=detunings)
interaction = hamiltonian_factory(register, qd.Interaction.NN)
# drive and interaction are not commuting thus they need to be
# added directly into the final Hamiltonian generator
if phase is not None:
generator = (
drive_scaling * sympy.cos(phase) * drive_x
- drive_scaling * sympy.sin(phase) * drive_y
+ interaction
)
else:
generator = drive_scaling * drive_x + interaction
return chain(
qd.HamEvo(generator, tevo_drive),
# detuning and interaction are commuting, so they
# can be ordered arbitrarily and treated separately
qd.HamEvo(interaction, tevo_wait),
qd.HamEvo(detuning, tevo_det),
)

daqc_transform(n_qubits, gen_target, t_f, gen_build=None, zero_tol=1e-08, strategy=Strategy.SDAQC, ignore_global_phases=False)

Section titled “ daqc_transform(n_qubits, gen_target, t_f, gen_build=None, zero_tol=1e-08, strategy=Strategy.SDAQC, ignore_global_phases=False) ”

Implements the DAQC transform for representing an arbitrary 2-body Hamiltonian.

The result is another fixed 2-body Hamiltonian.

Reference for universality of 2-body Hamiltonians:

-- https://arxiv.org/abs/quant-ph/0106064

Based on the transformation for Ising (ZZ) interactions, as described in the paper

-- https://arxiv.org/abs/1812.03637

The transform translates a target weighted generator of the type:

`gen_target = add(g_jk * kron(op(j), op(k)) for j < k)`

To a circuit using analog evolutions with a fixed building block generator:

`gen_build = add(f_jk * kron(op(j), op(k)) for j < k)`

where op = Z or op = N.

PARAMETER DESCRIPTION
n_qubits

total number of qubits to use.

TYPE: int

gen_target

target generator built with the structure above. The type of the generator will be automatically evaluated when parsing.

TYPE: AbstractBlock

t_f

total time for the gen_target evolution.

TYPE: float

gen_build

fixed generator to act as a building block. Defaults to constant NN: add(1.0 * kron(N(j), N(k)) for j < k). The type of the generator will be automatically evaluated when parsing.

TYPE: AbstractBlock | None DEFAULT: None

zero_tol

default "zero" for a missing interaction. Included for numerical reasons, see notes below.

TYPE: float DEFAULT: 1e-08

strategy

sDAQC or bDAQC, following definitions in the reference paper.

TYPE: Strategy DEFAULT: SDAQC

ignore_global_phases

if True the transform does not correct the global phases coming from the mapping between ZZ and NN interactions.

TYPE: bool DEFAULT: False

Notes:

The paper follows an index convention of running from 1 to N. A few functions
here also use that convention to be consistent with the paper. However, for qadence
related things the indices are converted to [0, N-1].
The case for `n_qubits = 4` is an edge case where the sign matrix is not invertible.
There is a workaround for this described in the paper, but it is currently not implemented.
The current implementation may result in evolution times that are both positive or
negative. In practice, both can be represented by simply changing the signs of the
interactions. However, for a real implementation where the interactions should remain
fixed, the paper discusses a workaround that is not currently implemented.
The transformation works by representing each interaction in the target hamiltonian by
a set of evolutions using the build hamiltonian. As a consequence, some care must be
taken when choosing the build hamiltonian. Some cases:
- The target hamiltonian can have any interaction, as long as it is sufficiently
represented in the build hamiltonian. E.g., if the interaction `g_01 * kron(Z(0), Z(1))`
is in the target hamiltonian, the corresponding interaction `f_01 * kron(Z(0), Z(1))`
needs to be in the build hamiltonian. This is checked when the generators are parsed.
- The build hamiltonian can have any interaction, irrespectively of it being needed
for the target hamiltonian. This is especially useful for designing local operations
through the repeated evolution of a "global" hamiltonian.
- The parameter `zero_tol` controls what it means for an interaction to be "missing".
Any interaction strength smaller than `zero_tol` in the build hamiltonian will not be
considered, and thus that interaction is missing.
- The various ratios `g_jk / f_jk` will influence the time parameter for the various
evolution slices, meaning that if there is a big discrepancy in the interaction strength
for a given qubit pair (j, k), the output circuit may require the usage of hamiltonian
evolutions with very large times.
- A warning will be issued for evolution times larger than `1/sqrt(zero_tol)`. Evolution
times smaller than `zero_tol` will not be represented.

Examples:

from qadence import Z, N, daqc_transform
n_qubits = 3
gen_build = 0.5 * (N(0)@N(1)) + 0.7 * (N(1)@N(2)) + 0.2 * (N(0)@N(2))
gen_target = 0.1 * (Z(1)@Z(2))
t_f = 2.0
transformed_circuit = daqc_transform(
n_qubits = n_qubits,
gen_target = gen_target,
t_f = t_f,
gen_build = gen_build,
)
Source code in qadence/constructors/daqc/daqc.py
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234def daqc_transform(
n_qubits: int,
gen_target: AbstractBlock,
t_f: float,
gen_build: AbstractBlock | None = None,
zero_tol: float = 1e-08,
strategy: Strategy = Strategy.SDAQC,
ignore_global_phases: bool = False,
) -> AbstractBlock:
"""
Implements the DAQC transform for representing an arbitrary 2-body Hamiltonian.
The result is another fixed 2-body Hamiltonian.
Reference for universality of 2-body Hamiltonians:
-- https://arxiv.org/abs/quant-ph/0106064
Based on the transformation for Ising (ZZ) interactions, as described in the paper
-- https://arxiv.org/abs/1812.03637
The transform translates a target weighted generator of the type:
`gen_target = add(g_jk * kron(op(j), op(k)) for j < k)`
To a circuit using analog evolutions with a fixed building block generator:
`gen_build = add(f_jk * kron(op(j), op(k)) for j < k)`
where `op = Z` or `op = N`.
Args:
n_qubits: total number of qubits to use.
gen_target: target generator built with the structure above. The type
of the generator will be automatically evaluated when parsing.
t_f (float): total time for the gen_target evolution.
gen_build: fixed generator to act as a building block. Defaults to
constant NN: add(1.0 * kron(N(j), N(k)) for j < k). The type
of the generator will be automatically evaluated when parsing.
zero_tol: default "zero" for a missing interaction. Included for
numerical reasons, see notes below.
strategy: sDAQC or bDAQC, following definitions in the reference paper.
ignore_global_phases: if `True` the transform does not correct the global
phases coming from the mapping between ZZ and NN interactions.
Notes:
The paper follows an index convention of running from 1 to N. A few functions
here also use that convention to be consistent with the paper. However, for qadence
related things the indices are converted to [0, N-1].
The case for `n_qubits = 4` is an edge case where the sign matrix is not invertible.
There is a workaround for this described in the paper, but it is currently not implemented.
The current implementation may result in evolution times that are both positive or
negative. In practice, both can be represented by simply changing the signs of the
interactions. However, for a real implementation where the interactions should remain
fixed, the paper discusses a workaround that is not currently implemented.
The transformation works by representing each interaction in the target hamiltonian by
a set of evolutions using the build hamiltonian. As a consequence, some care must be
taken when choosing the build hamiltonian. Some cases:
- The target hamiltonian can have any interaction, as long as it is sufficiently
represented in the build hamiltonian. E.g., if the interaction `g_01 * kron(Z(0), Z(1))`
is in the target hamiltonian, the corresponding interaction `f_01 * kron(Z(0), Z(1))`
needs to be in the build hamiltonian. This is checked when the generators are parsed.
- The build hamiltonian can have any interaction, irrespectively of it being needed
for the target hamiltonian. This is especially useful for designing local operations
through the repeated evolution of a "global" hamiltonian.
- The parameter `zero_tol` controls what it means for an interaction to be "missing".
Any interaction strength smaller than `zero_tol` in the build hamiltonian will not be
considered, and thus that interaction is missing.
- The various ratios `g_jk / f_jk` will influence the time parameter for the various
evolution slices, meaning that if there is a big discrepancy in the interaction strength
for a given qubit pair (j, k), the output circuit may require the usage of hamiltonian
evolutions with very large times.
- A warning will be issued for evolution times larger than `1/sqrt(zero_tol)`. Evolution
times smaller than `zero_tol` will not be represented.
Examples:
```python exec="on" source="material-block" result="json"
from qadence import Z, N, daqc_transform
n_qubits = 3
gen_build = 0.5 * (N(0)@N(1)) + 0.7 * (N(1)@N(2)) + 0.2 * (N(0)@N(2))
gen_target = 0.1 * (Z(1)@Z(2))
t_f = 2.0
transformed_circuit = daqc_transform(
n_qubits = n_qubits,
gen_target = gen_target,
t_f = t_f,
gen_build = gen_build,
)
```
"""
##################
# Input controls #
##################
if strategy != Strategy.SDAQC:
raise NotImplementedError("Currently only the sDAQC transform is implemented.")
if n_qubits == 4:
raise NotImplementedError("DAQC transform 4-qubit edge case not implemented.")
if gen_build is None:
gen_build = hamiltonian_factory(n_qubits, interaction=Interaction.NN)
try:
if (not block_is_qubit_hamiltonian(gen_target)) or (
not block_is_qubit_hamiltonian(gen_build)
):
raise ValueError(
"Generator block is not a qubit Hamiltonian. Only ZZ or NN interactions allowed."
)
except NotImplementedError:
# Happens when block_is_qubit_hamiltonian is called on something that is not a block.
raise TypeError(
"Generator block is not a qubit Hamiltonian. Only ZZ or NN interactions allowed."
)
#####################
# Generator parsing #
#####################
g_jk_target, mat_jk_target, target_type = _parse_generator(n_qubits, gen_target, 0.0)
g_jk_build, mat_jk_build, build_type = _parse_generator(n_qubits, gen_build, zero_tol)
# Get the global phase hamiltonian and single-qubit detuning hamiltonian
if build_type == GenDAQC.NN:
h_phase_build, h_sq_build = _nn_phase_and_detunings(n_qubits, mat_jk_build)
if target_type == GenDAQC.NN:
h_phase_target, h_sq_target = _nn_phase_and_detunings(n_qubits, mat_jk_target)
# Time re-scalings
if build_type == GenDAQC.ZZ and target_type == GenDAQC.NN:
t_star = t_f / 4.0
elif build_type == GenDAQC.NN and target_type == GenDAQC.ZZ:
t_star = 4.0 * t_f
else:
t_star = t_f
# Check if target Hamiltonian can be mapped with the build Hamiltonian
assert _check_compatibility(g_jk_target, g_jk_build, zero_tol)
##################
# DAQC Transform #
##################
# Section III A of https://arxiv.org/abs/1812.03637:
# Matrix M for the linear system, exemplified in Table I:
matrix_M = _build_matrix_M(n_qubits)
# Linear system mapping interaction ratios -> evolution times.
t_slices = torch.linalg.solve(matrix_M, g_jk_target / g_jk_build) * t_star
# ZZ-DAQC with ZZ or NN build Hamiltonian
daqc_slices = []
for m in range(2, n_qubits + 1):
for n in range(1, m):
alpha = _ix_map(n_qubits, n, m)
t = t_slices[alpha - 1]
if abs(t) > zero_tol:
if abs(t) > (1 / (zero_tol**0.5)):
logger.warning(
"""
Transformed circuit with very long evolution time.
Make sure your target interactions are sufficiently
represented in the build Hamiltonian."""
)
x_gates = kron(X(n - 1), X(m - 1))
analog_evo = HamEvo(gen_build, t)
# TODO: Fix repeated X-gates
if build_type == GenDAQC.NN:
# Local detuning at each DAQC layer for NN build Hamiltonian
sq_detuning_build = HamEvo(h_sq_build, t)
daqc_slices.append(chain(x_gates, sq_detuning_build, analog_evo, x_gates))
elif build_type == GenDAQC.ZZ:
daqc_slices.append(chain(x_gates, analog_evo, x_gates))
daqc_circuit = chain(*daqc_slices)
########################
# Phases and Detunings #
########################
if target_type == GenDAQC.NN:
# Local detuning given a NN target Hamiltonian
sq_detuning_target = HamEvo(h_sq_target, t_f).dagger()
daqc_circuit = chain(sq_detuning_target, daqc_circuit)
if not ignore_global_phases:
if build_type == GenDAQC.NN:
# Constant global phase given a NN build Hamiltonian
global_phase_build = HamEvo(h_phase_build, t_slices.sum())
daqc_circuit = chain(global_phase_build, daqc_circuit)
if target_type == GenDAQC.NN:
# Constant global phase and given a NN target Hamiltonian
global_phase_target = HamEvo(h_phase_target, t_f).dagger()
daqc_circuit = chain(global_phase_target, daqc_circuit)
return daqc_circuit

build_idx_fms(basis, fm_pauli, multivariate_strategy, n_features, n_qubits, reupload_scaling)

Section titled “ build_idx_fms(basis, fm_pauli, multivariate_strategy, n_features, n_qubits, reupload_scaling) ”

Builds the index feature maps based on the given parameters.

PARAMETER DESCRIPTION
basis

Type of basis chosen for the feature map.

TYPE: BasisSet

fm_pauli

The chosen Pauli rotation type.

TYPE: PrimitiveBlock type

multivariate_strategy

The strategy used for encoding the multivariate feature map.

TYPE: MultivariateStrategy

n_features

The number of features.

TYPE: int

n_qubits

The number of qubits.

TYPE: int

reupload_scaling

The chosen scaling for the reupload.

TYPE: ReuploadScaling

RETURNS DESCRIPTION
list[KronBlock]

List[KronBlock]: The list of index feature maps.

Source code in qadence/constructors/utils.py
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68def build_idx_fms(
basis: BasisSet,
fm_pauli: Type[RY],
multivariate_strategy: MultivariateStrategy,
n_features: int,
n_qubits: int,
reupload_scaling: ReuploadScaling,
) -> list[KronBlock]:
"""Builds the index feature maps based on the given parameters.
Args:
basis (BasisSet): Type of basis chosen for the feature map.
fm_pauli (PrimitiveBlock type): The chosen Pauli rotation type.
multivariate_strategy (MultivariateStrategy): The strategy used for encoding
the multivariate feature map.
n_features (int): The number of features.
n_qubits (int): The number of qubits.
reupload_scaling (ReuploadScaling): The chosen scaling for the reupload.
Returns:
List[KronBlock]: The list of index feature maps.
"""
idx_fms = []
for i in range(n_features):
target_qubits = get_fm_qubits(multivariate_strategy, i, n_qubits, n_features)
param = FeatureParameter(f"x{i}")
block = kron(
*[
fm_pauli(qubit, generator_prefactor(reupload_scaling, j) * basis_func(basis, param))
for j, qubit in enumerate(target_qubits)
]
)
idx_fm = block
idx_fms.append(idx_fm)
return idx_fms

generator_prefactor(reupload_scaling, qubit_index)

Section titled “ generator_prefactor(reupload_scaling, qubit_index) ”

Converts a spectrum string, e.g. tower or exponential.

The result is the correct generator prefactor.

Source code in qadence/constructors/utils.py
13
14
15
16
17
18
19
20
21
22
23def generator_prefactor(reupload_scaling: ReuploadScaling, qubit_index: int) -> float | int:
"""Converts a spectrum string, e.g. tower or exponential.
The result is the correct generator prefactor.
"""
conversion_dict: dict[str, float | int] = {
ReuploadScaling.CONSTANT: 1,
ReuploadScaling.TOWER: qubit_index + 1,
ReuploadScaling.EXP: 2 * PI / (2 ** (qubit_index + 1)),
}
return conversion_dict[reupload_scaling]

get_fm_qubits(multivariate_strategy, i, n_qubits, n_features)

Section titled “ get_fm_qubits(multivariate_strategy, i, n_qubits, n_features) ”

Returns the list of target qubits for the given feature map strategy and feature index.

PARAMETER DESCRIPTION
multivariate_strategy

The strategy used for encoding the multivariate feature map.

TYPE: MultivariateStrategy

i

The feature index.

TYPE: int

n_qubits

The number of qubits.

TYPE: int

n_features

The number of features.

TYPE: int

RETURNS DESCRIPTION
Iterable

List[int]: The list of target qubits.

RAISES DESCRIPTION
ValueError

If the feature map strategy is not implemented.

Source code in qadence/constructors/utils.py
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96def get_fm_qubits(
multivariate_strategy: MultivariateStrategy, i: int, n_qubits: int, n_features: int
) -> Iterable:
"""Returns the list of target qubits for the given feature map strategy and feature index.
Args:
multivariate_strategy (MultivariateStrategy): The strategy used for encoding
the multivariate feature map.
i (int): The feature index.
n_qubits (int): The number of qubits.
n_features (int): The number of features.
Returns:
List[int]: The list of target qubits.
Raises:
ValueError: If the feature map strategy is not implemented.
"""
if multivariate_strategy == MultivariateStrategy.PARALLEL:
n_qubits_per_feature = int(n_qubits / n_features)
target_qubits = range(i * n_qubits_per_feature, (i + 1) * n_qubits_per_feature)
elif multivariate_strategy == MultivariateStrategy.SERIES:
target_qubits = range(0, n_qubits)
else:
raise ValueError(f"Multivariate strategy {multivariate_strategy} not implemented.")
return target_qubits