tequila-basic 1.9.9__py3-none-any.whl → 1.9.10__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. tequila/__init__.py +29 -14
  2. tequila/apps/__init__.py +14 -5
  3. tequila/apps/_unary_state_prep_impl.py +145 -112
  4. tequila/apps/adapt/__init__.py +9 -1
  5. tequila/apps/adapt/adapt.py +154 -113
  6. tequila/apps/krylov/__init__.py +1 -1
  7. tequila/apps/krylov/krylov.py +23 -21
  8. tequila/apps/robustness/helpers.py +10 -6
  9. tequila/apps/robustness/interval.py +238 -156
  10. tequila/apps/unary_state_prep.py +29 -23
  11. tequila/autograd_imports.py +8 -5
  12. tequila/circuit/__init__.py +2 -1
  13. tequila/circuit/_gates_impl.py +135 -67
  14. tequila/circuit/circuit.py +163 -79
  15. tequila/circuit/compiler.py +114 -105
  16. tequila/circuit/gates.py +288 -120
  17. tequila/circuit/gradient.py +35 -23
  18. tequila/circuit/noise.py +83 -74
  19. tequila/circuit/postselection.py +120 -0
  20. tequila/circuit/pyzx.py +10 -6
  21. tequila/circuit/qasm.py +201 -83
  22. tequila/circuit/qpic.py +63 -61
  23. tequila/grouping/binary_rep.py +148 -146
  24. tequila/grouping/binary_utils.py +84 -75
  25. tequila/grouping/compile_groups.py +334 -230
  26. tequila/grouping/ev_utils.py +77 -41
  27. tequila/grouping/fermionic_functions.py +383 -308
  28. tequila/grouping/fermionic_methods.py +170 -123
  29. tequila/grouping/overlapping_methods.py +69 -52
  30. tequila/hamiltonian/paulis.py +12 -13
  31. tequila/hamiltonian/paulistring.py +1 -1
  32. tequila/hamiltonian/qubit_hamiltonian.py +45 -35
  33. tequila/ml/__init__.py +1 -0
  34. tequila/ml/interface_torch.py +19 -16
  35. tequila/ml/ml_api.py +11 -10
  36. tequila/ml/utils_ml.py +12 -11
  37. tequila/objective/__init__.py +8 -3
  38. tequila/objective/braket.py +55 -47
  39. tequila/objective/objective.py +87 -55
  40. tequila/objective/qtensor.py +36 -27
  41. tequila/optimizers/__init__.py +31 -23
  42. tequila/optimizers/_containers.py +11 -7
  43. tequila/optimizers/optimizer_base.py +111 -83
  44. tequila/optimizers/optimizer_gd.py +258 -231
  45. tequila/optimizers/optimizer_gpyopt.py +56 -42
  46. tequila/optimizers/optimizer_scipy.py +157 -112
  47. tequila/quantumchemistry/__init__.py +66 -38
  48. tequila/quantumchemistry/chemistry_tools.py +393 -209
  49. tequila/quantumchemistry/encodings.py +121 -13
  50. tequila/quantumchemistry/madness_interface.py +170 -96
  51. tequila/quantumchemistry/orbital_optimizer.py +86 -41
  52. tequila/quantumchemistry/psi4_interface.py +166 -97
  53. tequila/quantumchemistry/pyscf_interface.py +70 -23
  54. tequila/quantumchemistry/qc_base.py +866 -414
  55. tequila/simulators/__init__.py +0 -3
  56. tequila/simulators/simulator_api.py +247 -105
  57. tequila/simulators/simulator_aqt.py +102 -0
  58. tequila/simulators/simulator_base.py +147 -53
  59. tequila/simulators/simulator_cirq.py +58 -42
  60. tequila/simulators/simulator_cudaq.py +600 -0
  61. tequila/simulators/simulator_ddsim.py +390 -0
  62. tequila/simulators/simulator_mqp.py +30 -0
  63. tequila/simulators/simulator_pyquil.py +190 -171
  64. tequila/simulators/simulator_qibo.py +95 -87
  65. tequila/simulators/simulator_qiskit.py +119 -107
  66. tequila/simulators/simulator_qlm.py +52 -26
  67. tequila/simulators/simulator_qulacs.py +74 -52
  68. tequila/simulators/simulator_spex.py +95 -60
  69. tequila/simulators/simulator_symbolic.py +6 -5
  70. tequila/simulators/test_spex_simulator.py +8 -11
  71. tequila/tools/convenience.py +4 -4
  72. tequila/tools/qng.py +72 -64
  73. tequila/tools/random_generators.py +38 -34
  74. tequila/utils/bitstrings.py +7 -7
  75. tequila/utils/exceptions.py +19 -5
  76. tequila/utils/joined_transformation.py +8 -10
  77. tequila/utils/keymap.py +0 -5
  78. tequila/utils/misc.py +6 -4
  79. tequila/version.py +1 -1
  80. tequila/wavefunction/qubit_wavefunction.py +47 -28
  81. {tequila_basic-1.9.9.dist-info → tequila_basic-1.9.10.dist-info}/METADATA +13 -16
  82. tequila_basic-1.9.10.dist-info/RECORD +93 -0
  83. {tequila_basic-1.9.9.dist-info → tequila_basic-1.9.10.dist-info}/WHEEL +1 -1
  84. tequila_basic-1.9.9.dist-info/RECORD +0 -88
  85. {tequila_basic-1.9.9.dist-info → tequila_basic-1.9.10.dist-info}/licenses/LICENSE +0 -0
  86. {tequila_basic-1.9.9.dist-info → tequila_basic-1.9.10.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,600 @@
1
+ import cudaq
2
+ import numpy as np
3
+ from cudaq import spin
4
+ import numbers
5
+ import numpy
6
+ from tequila import TequilaException
7
+ from tequila.utils.bitstrings import BitNumbering, reverse_int_bits
8
+ from tequila.wavefunction.qubit_wavefunction import QubitWaveFunction
9
+ from tequila.simulators.simulator_base import BackendCircuit, BackendExpectationValue
10
+ from typing import Union, Optional
11
+
12
+ """
13
+ Developer Note:
14
+ Cudaq does not have objects for circuits and gates. Instead it uses quantum kernels. These only allow the input of
15
+ arguments of certain types (and are therefore quite picky), i.e. ints, floats or a list[int], list[float].
16
+ Due to this restriction the circuit is a list of these primitive types and the used gates are encoded.
17
+ """
18
+
19
+
20
+ class TequilaCudaqException(TequilaException):
21
+ def __str__(self):
22
+ return "Error in cudaq (cuda-quantum) backend:" + self.message
23
+
24
+
25
+ class BackendCircuitCudaq(BackendCircuit):
26
+ """
27
+ Class representing circuits compiled to cudaq (cuda-quantum of NVIDIA).
28
+ See BackendCircuit for documentation of features and methods inherited therefrom
29
+
30
+ Attributes
31
+ ----------
32
+ counter:
33
+ counts how many distinct sympy.Symbol objects are employed in the circuit.
34
+ has_noise:
35
+ whether or not the circuit is noisy. needed by the expectationvalue to do sampling properly.
36
+ noise_lookup: dict:
37
+ dict mapping strings to lists of constructors for cirq noise channel objects.
38
+ op_lookup: dict:
39
+ dictionary mapping strings (tequila gate names) to cirq.ops objects.
40
+ variables: list:
41
+ a list of the qulacs variables of the circuit.
42
+
43
+ Methods
44
+ -------
45
+ add_noise_to_circuit:
46
+ apply a tequila NoiseModel to a qulacs circuit, by translating the NoiseModel's instructions into noise gates.
47
+ """
48
+
49
+ compiler_arguments = {
50
+ "trotterized": True,
51
+ "swap": True,
52
+ "multitarget": True,
53
+ "controlled_rotation": True, # needed for gates depending on variables
54
+ "generalized_rotation": True,
55
+ "exponential_pauli": True,
56
+ "controlled_exponential_pauli": True,
57
+ "phase": True,
58
+ "power": True,
59
+ "hadamard_power": True,
60
+ "controlled_power": True,
61
+ "controlled_phase": True,
62
+ "toffoli": True,
63
+ "phase_to_z": True,
64
+ "cc_max": True,
65
+ }
66
+ numbering = BitNumbering.LSB
67
+ supports_generic_initialization = True
68
+
69
+ def __init__(self, abstract_circuit, noise=None, *args, **kwargs):
70
+ """
71
+
72
+ Parameters
73
+ ----------
74
+ abstract_circuit: QCircuit:
75
+ the circuit to compile to cudaq
76
+ noise: optional:
77
+ noise to apply to the circuit.
78
+ args
79
+ kwargs
80
+ """
81
+
82
+ # gates encodings
83
+ self.op_lookup = {
84
+ # primitives
85
+ "I": None,
86
+ "X": 1,
87
+ "Y": 2,
88
+ "Z": 3,
89
+ "H": 4,
90
+ # simple 1-qubit rotations
91
+ "Rx": 5,
92
+ "Ry": 6,
93
+ "Rz": 7,
94
+ # phase gates
95
+ "S": 8,
96
+ "T": 9,
97
+ # controlled primitives
98
+ "CNOT": 10,
99
+ "Cx": 11,
100
+ "Cy": 12,
101
+ "Cz": 13,
102
+ # controlled rotations
103
+ "CRx": 14,
104
+ "CRy": 15,
105
+ "CRz": 16,
106
+ # other types of gates
107
+ "SWAP": 17,
108
+ "Measure": 18,
109
+ "Exp-Pauli": None,
110
+ }
111
+
112
+ # instantiate a cudaq circuit as a list
113
+ self.circuit = self.initialize_circuit()
114
+ self.measurements = None
115
+ self.variables = []
116
+ super().__init__(abstract_circuit=abstract_circuit, noise=noise, *args, **kwargs)
117
+ self.has_noise = False
118
+
119
+ @cudaq.kernel
120
+ def state_modifier_from_initial_state( ## create a state based on an EXISTING PREVIOUS STATE i.e. |1010010>
121
+ number_of_qubits: int,
122
+ gate_encodings: list[int],
123
+ target_qubits: list[int],
124
+ angles: list[float],
125
+ control_qubits: list[int],
126
+ iteration_length: int,
127
+ inital_state: cudaq.State,
128
+ ):
129
+ """
130
+ This function applies a circuit to an EXISTING QUANTUM STATE with a given number of qubits,
131
+ i.e. |10110>
132
+ - unlike "state_modifier" this function needs a special preperation of the gates to apply to the state
133
+ and therefore works with prepare_state_from_integer
134
+
135
+ The function collects the gates to apply, which are stored in a list (cudaq's circuit "object")
136
+ applies them in the given order to the state.
137
+
138
+ These circuits support:
139
+ 1. single-qubit gates like: X, Y, Z, H, S, T as well as controlled variantes of them with up
140
+ to one control-qubit
141
+ 2. parametrized single qubit gates like: Rx, Ry, Rz with a given angle
142
+ """
143
+
144
+ # create a quantum state based on a given initial state
145
+ s = cudaq.qvector(inital_state)
146
+
147
+ for index in range(iteration_length):
148
+ encoding = gate_encodings[index]
149
+ target = target_qubits[index]
150
+ angle = angles[index]
151
+ control = control_qubits[index]
152
+
153
+ # x gate
154
+ if encoding == 1:
155
+ if control != -1:
156
+ x.ctrl(s[control], s[target])
157
+ else:
158
+ x(s[target])
159
+ # y gate
160
+ elif encoding == 2:
161
+ if control != -1:
162
+ y.ctrl(s[control], s[target])
163
+ else:
164
+ y(s[target])
165
+ # z gate
166
+ elif encoding == 3:
167
+ if control != -1:
168
+ z.ctrl(s[control], s[target])
169
+ else:
170
+ z(s[target])
171
+ # h gate
172
+ elif encoding == 4:
173
+ if control != -1:
174
+ h.ctrl(s[control], s[target])
175
+ else:
176
+ h(s[target])
177
+ # Rx gate
178
+ elif encoding == 5:
179
+ if control != -1:
180
+ pass
181
+ else:
182
+ rx(angle, s[target])
183
+ # Rx gate
184
+ elif encoding == 6:
185
+ if control != -1:
186
+ pass
187
+ else:
188
+ ry(angle, s[target])
189
+ # Rx gate
190
+ elif encoding == 7:
191
+ if control != -1:
192
+ pass
193
+ else:
194
+ rz(angle, s[target])
195
+ # S gate
196
+ elif encoding == 8:
197
+ if control != -1:
198
+ s.ctrl(s[control], s[target])
199
+ else:
200
+ s(s[target])
201
+ # T gate
202
+ elif encoding == 9:
203
+ if control != -1:
204
+ t.ctrl(s[control], s[target])
205
+ else:
206
+ t(s[target])
207
+
208
+ def prepare_circuit_for_state_modifier(self):
209
+ """
210
+ this function decomposes the circuit elements for later usage in state_modifier, which uses the
211
+ cudaq annotation @cudaq.kernel.
212
+
213
+ Has to be done this way since other functions for expectation value for example like cudaq.observe have special syntax
214
+ and accept parameters and the state modifier itself
215
+ """
216
+
217
+ # prepare parameters for usage in kernal since "self. " access doesnt work within kernels
218
+ number_of_qubits = self.n_qubits
219
+
220
+ circuit = None
221
+ if isinstance(self, BackendCircuitCudaq):
222
+ circuit = self.circuit
223
+ elif isinstance(self, BackendExpectationValueCudaq):
224
+ circuit = self.U.circuit
225
+
226
+ if circuit is None:
227
+ raise ValueError("wrong attribute access in function - prepare_circuit_for_state_modifier")
228
+
229
+ # get single lists from dict
230
+ gate_encodings = circuit["gate_encodings"]
231
+ target_qubits = circuit["target_qubits"]
232
+ # Cast from FixedVariable to float to avoid CudaQ MLIR type conversion problems
233
+ angles = [float(angle) for angle in circuit["angles"]]
234
+ control_qubits = circuit["control_qubits"]
235
+
236
+ iteration_length = None
237
+
238
+ if len(gate_encodings) == len(target_qubits) == len(angles) == len(control_qubits):
239
+ iteration_length = len(gate_encodings)
240
+ else:
241
+ raise ValueError("length of params lists in prepare_circuit_for_modifier has sto match")
242
+
243
+ if iteration_length is None:
244
+ raise ValueError("iter length from prepare_circuit shall not be None")
245
+
246
+ return (number_of_qubits, gate_encodings, target_qubits, angles, control_qubits, iteration_length)
247
+
248
+ def do_simulate(self, variables, initial_state, *args, **kwargs):
249
+ """
250
+ Helper function to perform simulation.
251
+
252
+ - performs simulation in the following order:
253
+ 1. create a quantum state based on a given input integer
254
+ 2. apply a given quantum circuit on this state
255
+ 3. gate the amplitudes of the resulting wave function (wfn)
256
+ after application of the circuit
257
+
258
+
259
+ Parameters
260
+ ----------
261
+ variables: dict:
262
+ variables to supply to the circuit.
263
+ initial_state:
264
+ information indicating the initial state on which the circuit should act.
265
+ args
266
+ kwargs
267
+
268
+ Returns
269
+ -------
270
+ QubitWaveFunction:
271
+ QubitWaveFunction representing result of the simulation.
272
+ """
273
+
274
+ # prepare the circuit to apply onto the state made from an integer
275
+ (number_of_qubits, gate_encodings, target_qubits, angles, control_qubits, iteration_length) = (
276
+ BackendCircuitCudaq.prepare_circuit_for_state_modifier(self)
277
+ )
278
+
279
+ # apply state modifier (circuit) onto state and get amplitudes
280
+ vector = cudaq.get_state(
281
+ self.state_modifier_from_initial_state,
282
+ number_of_qubits,
283
+ gate_encodings,
284
+ target_qubits,
285
+ angles,
286
+ control_qubits,
287
+ iteration_length,
288
+ initialize_state(initial_state, n_qubits=self.n_qubits),
289
+ )
290
+
291
+ wfn = QubitWaveFunction.from_array(array=np.array(vector), numbering=self.numbering)
292
+
293
+ return wfn
294
+
295
+ def initialize_circuit(self, *args, **kwargs):
296
+ """
297
+ return an empty circuit.
298
+ for cudaq return an empty dict as the main data structure containing 4 key parameters:
299
+ 1. encodings of gates as integers
300
+ 2. indices of target qubits to which the gates are applied
301
+ 3. angles in case of parametrized gates
302
+ 4. index of a potential control qubit
303
+
304
+ Parameters
305
+ ----------
306
+ args
307
+ kwargs
308
+
309
+ Returns
310
+ -------
311
+ """
312
+
313
+ # as for now circuit supports only CNOT, single qubit gates and single rotations
314
+ circuit = {"gate_encodings": [], "target_qubits": [], "angles": [], "control_qubits": []}
315
+
316
+ return circuit
317
+
318
+ def add_parametrized_gate(self, gate, circuit, variables, *args, **kwargs):
319
+ """
320
+ add a parametrized gate.
321
+ - for this fetch params like 1. angle 2. gate encoding 3. target qubits
322
+ and store them in the corresponding lists within the circuit (as a dict)
323
+
324
+ Parameters
325
+ ----------
326
+ gate: QGateImpl:
327
+ the gate to add to the circuit.
328
+ circuit:
329
+ the circuit to which the gate is to be added
330
+ variables:
331
+ dict that tells values of variables; needed IFF the gate is an ExpPauli gate.
332
+ args
333
+ kwargs
334
+
335
+ Returns
336
+ -------
337
+ None
338
+ """
339
+ gate_encoding = self.op_lookup[gate.name]
340
+
341
+ # target qubits as a list
342
+ target_qubits = [self.qubit(t) for t in gate.target]
343
+
344
+ if len(target_qubits) != 1:
345
+ raise ValueError(" at most 1 target qubits is supported, NOT MORE ")
346
+
347
+ # save all control qubits into a list
348
+ control_qubits = []
349
+ if gate.is_controlled():
350
+ for control in gate.control:
351
+ control_qubits.append(self.qubit(control))
352
+
353
+ # more than one control currently not supported
354
+ if len(control_qubits) > 1:
355
+ raise ValueError("at most 1 control qubit is supported for cudaq, NOT MORE ")
356
+
357
+ # extract information about angle - one per gate
358
+ angle = gate.parameter(variables)
359
+
360
+ # if the gate has one control qubit append its index to the list of controls
361
+ if len(control_qubits) == 1:
362
+ circuit["control_qubits"].append(control_qubits[0])
363
+ elif len(control_qubits) == 0:
364
+ circuit["control_qubits"].append(-1)
365
+
366
+ # append the angle of the gate
367
+ circuit["angles"].append(angle)
368
+ circuit["gate_encodings"].append(gate_encoding)
369
+ circuit["target_qubits"].append(target_qubits[0])
370
+
371
+ def add_basic_gate(self, gate, circuit, *args, **kwargs):
372
+ """
373
+ add an unparametrized gate to the circuit.
374
+ Parameters
375
+ - add a gate with 1. control 2. target 3. encoding
376
+ since these gates are not parametrized save an angle of 0.0 into the angles list
377
+
378
+ ----------
379
+ gate: QGateImpl:
380
+ the gate to be added to the circuit.
381
+ circuit:
382
+ the circuit, to which a gate is to be added.
383
+ args
384
+ kwargs
385
+
386
+ Returns
387
+ -------
388
+ None
389
+ """
390
+
391
+ # fetch the gate encoding
392
+ gate_encoding = self.op_lookup[gate.name]
393
+
394
+ # target qubits as a list
395
+ target_qubits = [self.qubit(t) for t in gate.target]
396
+
397
+ if len(target_qubits) != 1:
398
+ raise ValueError(" at most 1 target qubits is supported, NOT MORE ")
399
+
400
+ # save all control qubits into a list
401
+ control_qubits = []
402
+ if gate.is_controlled():
403
+ for control in gate.control:
404
+ control_qubits.append(self.qubit(control))
405
+
406
+ # more than one control currently not supported
407
+ if len(control_qubits) > 1:
408
+ raise ValueError("at must 1 control qubit is supported, NOT MORE ")
409
+
410
+ # if the gate has one control qubit append its index to the list of the controls
411
+ if len(control_qubits) == 1:
412
+ circuit["control_qubits"].append(control_qubits[0])
413
+ elif len(control_qubits) == 0:
414
+ circuit["control_qubits"].append(-1)
415
+
416
+ circuit["target_qubits"].append(target_qubits[0])
417
+ circuit["gate_encodings"].append(gate_encoding)
418
+
419
+ # since primitive gates have to angle add zero to the list of angles
420
+ # so all 4 lists have the same length for iteration
421
+ circuit["angles"].append(0.0)
422
+
423
+
424
+ class BackendExpectationValueCudaq(BackendExpectationValue):
425
+ """
426
+ Class representing Expectation Values compiled for Cudaq.
427
+
428
+ Overrides some methods of BackendExpectationValue, which should be seen for details.
429
+ """
430
+
431
+ use_mapping = True
432
+ BackendCircuitType = BackendCircuitCudaq
433
+
434
+ def simulate(self, variables, initial_state: Union[int, QubitWaveFunction], *args, **kwargs) -> np.array:
435
+ """
436
+ Perform simulation of this expectationvalue.
437
+ Parameters
438
+ ----------
439
+ variables:
440
+ variables, to be supplied to the underlying circuit.
441
+ args
442
+ kwargs
443
+
444
+ Returns
445
+ -------
446
+ numpy.array:
447
+ the result of simulation as an array.
448
+ """
449
+ # fast return if possible
450
+ if self.H is None:
451
+ return np.asarray([0.0])
452
+ elif len(self.H) == 0:
453
+ return np.asarray([0.0])
454
+ elif isinstance(self.H, numbers.Number):
455
+ return np.asarray[self.H]
456
+
457
+ # update the variables for correct expectation value evaluation
458
+ self.U.update_variables(variables)
459
+
460
+ # prepare circuit to apply onto state
461
+ (number_of_qubits, gate_encodings, target_qubits, angles, control_qubits, iteration_length) = (
462
+ BackendCircuitCudaq.prepare_circuit_for_state_modifier(self)
463
+ )
464
+
465
+ # array containing results of exp. value simulation
466
+ resulting_expectation_values = []
467
+
468
+ # go over all given hamiltonians
469
+ for hamiltonian in self.H:
470
+ # compute expectation value between hamiltonian and state
471
+ expectation_value = cudaq.observe(
472
+ BackendCircuitCudaq.state_modifier_from_initial_state,
473
+ hamiltonian,
474
+ number_of_qubits,
475
+ gate_encodings,
476
+ target_qubits,
477
+ angles,
478
+ control_qubits,
479
+ iteration_length,
480
+ initialize_state(initial_state, n_qubits=self.n_qubits),
481
+ ).expectation()
482
+
483
+ # store exp. val in results array
484
+ resulting_expectation_values.append(expectation_value)
485
+
486
+ return np.asarray(resulting_expectation_values)
487
+
488
+ def XX__XX__initialize_hamiltonian_old_implementation(self, hamiltonians):
489
+ """
490
+ Convert reduced hamiltonians to native Cudaq types for efficient expectation value evaluation.
491
+
492
+ Parameters
493
+ ----------
494
+ hamiltonians:
495
+ an interable set of hamiltonian objects.
496
+
497
+ Returns
498
+ -------
499
+ list:
500
+ initialized hamiltonian objects.
501
+
502
+ """
503
+ # Map logical qubit labels to consecutive indices (0,1,2,...)
504
+ # this turns cases like [0,2,5] into [0,1,2] for cudaq
505
+ qubit_map = {}
506
+ for i, q in enumerate(self.U.abstract_circuit.qubits):
507
+ qubit_map[q] = i
508
+
509
+ list_of_initialized_hamiltonians = []
510
+ # assemble hamiltonian with cudaq "spin" objects
511
+ for hamiltonian in hamiltonians:
512
+ hamiltonian_as_spin = 0 # Initialize per Hamiltonian
513
+ for paulistring in hamiltonian.paulistrings:
514
+ term = 1 # Start with identity
515
+ for qubit, gate in paulistring.items():
516
+ mapped_qubit = qubit_map[qubit]
517
+ if gate == "X":
518
+ term *= spin.x(mapped_qubit)
519
+ elif gate == "Y":
520
+ term *= spin.y(mapped_qubit)
521
+ elif gate == "Z":
522
+ term *= spin.z(mapped_qubit)
523
+ term *= paulistring._coeff # Apply coefficient
524
+ hamiltonian_as_spin += term # Accumulate terms
525
+ list_of_initialized_hamiltonians.append(hamiltonian_as_spin)
526
+ return list_of_initialized_hamiltonians
527
+
528
+ def initialize_hamiltonian(self, hamiltonians):
529
+ """
530
+ Convert reduced hamiltonians to native Cudaq types for efficient expectation value evaluation.
531
+
532
+ Parameters
533
+ ----------
534
+ hamiltonians:
535
+ an interable set of hamiltonian objects.
536
+
537
+ Returns
538
+ -------
539
+ list:
540
+ initialized hamiltonian objects.
541
+
542
+ """
543
+ # Map logical qubit labels to consecutive indices
544
+ qubit_map = {q: i for i, q in enumerate(self.U.abstract_circuit.qubits)}
545
+ list_of_initialized_hamiltonians = []
546
+
547
+ # assemble hamiltonian with cudaq "spin" objects
548
+ for hamiltonian in hamiltonians:
549
+ hamiltonian_as_spin = None
550
+ for paulistring in hamiltonian.paulistrings:
551
+ # store the spin operators in a list
552
+ ops = []
553
+ for qubit, gate in paulistring.items():
554
+ mapped_qubit = qubit_map[qubit]
555
+ if gate == "X":
556
+ ops.append(spin.x(mapped_qubit))
557
+ elif gate == "Y":
558
+ ops.append(spin.y(mapped_qubit))
559
+ elif gate == "Z":
560
+ ops.append(spin.z(mapped_qubit))
561
+ # If no operators, skip to next paulistring
562
+ if ops:
563
+ # start with the first spin operator (instead of identity "1")
564
+ term = ops[0]
565
+ for op in ops[1:]:
566
+ term *= op
567
+ term *= paulistring._coeff
568
+ else:
569
+ # If no operators, create a "zero operator" (identity with zero coefficient)
570
+ term = spin.z(0) * 0.0 + paulistring._coeff
571
+ if hamiltonian_as_spin is None:
572
+ hamiltonian_as_spin = term
573
+ else:
574
+ hamiltonian_as_spin += term
575
+ # If no paulistrings, set to "zero operator" (not float)
576
+ if hamiltonian_as_spin is None:
577
+ hamiltonian_as_spin = spin.z(0) * 0.0
578
+ list_of_initialized_hamiltonians.append(hamiltonian_as_spin)
579
+ return list_of_initialized_hamiltonians
580
+
581
+
582
+ def initialize_state(
583
+ state: Union[int, QubitWaveFunction], n_qubits: Optional[int] = None, numbering: BitNumbering = BitNumbering.LSB
584
+ ) -> cudaq.State:
585
+ if isinstance(state, int):
586
+ if n_qubits is None:
587
+ raise ValueError("Need to specify n_qubits for initializing CudaQ basis state")
588
+ amplitudes = np.zeros(2**n_qubits, dtype=cudaq.complex())
589
+ # Reverse because Tequila uses MSB bit ordering by default, while CudaQ uses LSB
590
+ # TODO: It would be better to handle this somewhere else, e.g. by passing a BitString object
591
+ # in simulator_api (which stores its ordering), and then convert to the backend specific
592
+ # bit-ordering before passing this to the backends.
593
+ if numbering == BitNumbering.LSB:
594
+ index = reverse_int_bits(state, n_qubits)
595
+ amplitudes[index] = 1
596
+ return cudaq.State.from_data(amplitudes)
597
+ elif isinstance(state, QubitWaveFunction):
598
+ return cudaq.State.from_data(state.to_array(out_numbering=numbering).astype(cudaq.complex()))
599
+ else:
600
+ raise TypeError("State must be an int or QubitWaveFunction")