tequila-basic 1.9.8__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 +177 -88
  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 +91 -56
  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 +394 -203
  49. tequila/quantumchemistry/encodings.py +121 -13
  50. tequila/quantumchemistry/madness_interface.py +170 -96
  51. tequila/quantumchemistry/orbital_optimizer.py +86 -40
  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 +258 -106
  57. tequila/simulators/simulator_aqt.py +102 -0
  58. tequila/simulators/simulator_base.py +156 -55
  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 +124 -114
  66. tequila/simulators/simulator_qlm.py +52 -26
  67. tequila/simulators/simulator_qulacs.py +85 -59
  68. tequila/simulators/simulator_spex.py +464 -0
  69. tequila/simulators/simulator_symbolic.py +6 -5
  70. tequila/simulators/test_spex_simulator.py +208 -0
  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 +13 -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 +52 -30
  81. {tequila_basic-1.9.8.dist-info → tequila_basic-1.9.10.dist-info}/METADATA +23 -17
  82. tequila_basic-1.9.10.dist-info/RECORD +93 -0
  83. {tequila_basic-1.9.8.dist-info → tequila_basic-1.9.10.dist-info}/WHEEL +1 -1
  84. tequila_basic-1.9.8.dist-info/RECORD +0 -86
  85. {tequila_basic-1.9.8.dist-info → tequila_basic-1.9.10.dist-info/licenses}/LICENSE +0 -0
  86. {tequila_basic-1.9.8.dist-info → tequila_basic-1.9.10.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,464 @@
1
+ from tequila.simulators.simulator_base import BackendExpectationValue, BackendCircuit
2
+ from tequila.wavefunction.qubit_wavefunction import QubitWaveFunction
3
+ from tequila.utils import TequilaException
4
+ from tequila.hamiltonian import PauliString
5
+ from tequila.circuit._gates_impl import ExponentialPauliGateImpl, QGateImpl, RotationGateImpl, QubitHamiltonian
6
+ from tequila.circuit.gates import QubitExcitationImpl
7
+ from tequila import BitNumbering
8
+
9
+
10
+ import hashlib
11
+ import numpy
12
+ import os
13
+ import spex_tequila
14
+ import gc
15
+
16
+ numbering = BitNumbering.MSB
17
+
18
+
19
+ class TequilaSpexException(TequilaException):
20
+ """Custom exception for SPEX simulator errors"""
21
+
22
+ pass
23
+
24
+
25
+ def extract_pauli_dict(ps):
26
+ """
27
+ Extract qubit:operator mapping from PauliString/QubitHamiltonian
28
+ Args:
29
+ ps: PauliString or single-term QubitHamiltonian
30
+ Returns:
31
+ dict: {qubit: 'X'/'Y'/'Z'}
32
+ """
33
+
34
+ if isinstance(ps, PauliString):
35
+ return dict(ps.items())
36
+ if isinstance(ps, QubitHamiltonian) and len(ps.paulistrings) == 1:
37
+ return dict(ps.paulistrings[0].items())
38
+ raise TequilaSpexException("Unsupported generator type")
39
+
40
+
41
+ def circuit_hash(abstract_circuit, variables=None):
42
+ """
43
+ Create MD5 hash for circuit caching
44
+ Uses gate types, targets, controls and generators for uniqueness
45
+ """
46
+ sha = hashlib.md5()
47
+ if abstract_circuit is None:
48
+ return None
49
+ for g in abstract_circuit.gates:
50
+ gate_str = f"{type(g).__name__}:{g.name}:{g.target}:{g.control}:{g.generator}:{getattr(g, 'parameter', None)}\n"
51
+ sha.update(gate_str.encode("utf-8"))
52
+ if variables:
53
+ for key, value in sorted(variables.items()):
54
+ sha.update(f"{key}:{value}\n".encode("utf-8"))
55
+ return sha.hexdigest()
56
+
57
+
58
+ def copy_exp_pauli_term(term):
59
+ """Create a copy of a ExpPauliTerm."""
60
+ new_term = spex_tequila.ExpPauliTerm()
61
+ if hasattr(term, "pauli_map"):
62
+ new_term.pauli_map = dict(term.pauli_map)
63
+ if hasattr(term, "angle"):
64
+ new_term.angle = term.angle
65
+ return new_term
66
+
67
+
68
+ class BackendCircuitSpex(BackendCircuit):
69
+ """SPEX circuit implementation using sparse state representation"""
70
+
71
+ # Circuit compilation configuration
72
+ compiler_arguments = {
73
+ "multitarget": True,
74
+ "multicontrol": True,
75
+ "trotterized": True,
76
+ "generalized_rotation": True,
77
+ "exponential_pauli": False,
78
+ "controlled_exponential_pauli": True,
79
+ "hadamard_power": True,
80
+ "controlled_power": True,
81
+ "power": True,
82
+ "toffoli": True,
83
+ "controlled_phase": True,
84
+ "phase": True,
85
+ "phase_to_z": True,
86
+ "controlled_rotation": True,
87
+ "swap": True,
88
+ "cc_max": True,
89
+ "ry_gate": True,
90
+ "y_gate": True,
91
+ "ch_gate": True,
92
+ }
93
+
94
+ def __init__(
95
+ self,
96
+ abstract_circuit=None,
97
+ variables=None,
98
+ num_threads=-1,
99
+ amplitude_threshold=1e-14,
100
+ angle_threshold=1e-14,
101
+ compress_qubits=True,
102
+ *args,
103
+ **kwargs,
104
+ ):
105
+ # Circuit chaching
106
+ self.circuit_cache = {}
107
+
108
+ # Performance parameters
109
+ self.num_threads = num_threads
110
+ self.amplitude_threshold = amplitude_threshold
111
+ self.angle_threshold = angle_threshold
112
+
113
+ # State compression
114
+ self.compress_qubits = compress_qubits
115
+ self.n_qubits_compressed = None
116
+ self.hamiltonians = None
117
+
118
+ super().__init__(abstract_circuit=abstract_circuit, variables=variables, *args, **kwargs)
119
+
120
+ @property
121
+ def n_qubits(self):
122
+ """Get number of qubits after compression (if enabled)"""
123
+ used = set()
124
+ if hasattr(self, "circuit") and self.circuit:
125
+ for term in self.circuit:
126
+ used.update(term.pauli_map.keys())
127
+
128
+ if self.abstract_circuit is not None and hasattr(self.abstract_circuit, "gates"):
129
+ for gate in self.abstract_circuit.gates:
130
+ if hasattr(gate, "target"):
131
+ if isinstance(gate.target, (list, tuple)):
132
+ used.update(gate.target)
133
+ else:
134
+ used.add(gate.target)
135
+ if hasattr(gate, "control") and gate.control:
136
+ if isinstance(gate.control, (list, tuple)):
137
+ used.update(gate.control)
138
+ else:
139
+ used.add(gate.control)
140
+ computed = max(used) + 1 if used else 0
141
+ return max(super().n_qubits, computed)
142
+
143
+ def initialize_circuit(self, *args, **kwargs):
144
+ return []
145
+
146
+ def create_circuit(self, abstract_circuit=None, variables=None, *args, **kwargs):
147
+ """Compile circuit with caching using MD5 hash"""
148
+ if abstract_circuit is None:
149
+ abstract_circuit = self.abstract_circuit
150
+
151
+ key = circuit_hash(abstract_circuit, variables)
152
+
153
+ if key in self.circuit_cache:
154
+ return self.circuit_cache[key]
155
+
156
+ circuit = super().create_circuit(abstract_circuit=abstract_circuit, variables=variables, *args, **kwargs)
157
+
158
+ self.circuit_cache[key] = circuit
159
+
160
+ return circuit
161
+
162
+ def compress_qubit_indices(self):
163
+ """
164
+ Optimize qubit indices by mapping used qubits to contiguous range
165
+ Reduces memory usage by eliminating unused qubit dimensions
166
+ """
167
+ if not self.compress_qubits or not (hasattr(self, "circuit") and self.circuit):
168
+ return self.circuit, self.hamiltonians, self.n_qubits
169
+
170
+ new_circuit = [copy_exp_pauli_term(term) for term in self.circuit]
171
+ for term in new_circuit:
172
+ if hasattr(term, "pauli_map"):
173
+ term.pauli_map = dict(term.pauli_map)
174
+
175
+ new_hamiltonians = []
176
+ if self.hamiltonians is not None:
177
+ for ham in self.hamiltonians:
178
+ new_ham = []
179
+ for term, coeff in ham:
180
+ cloned = copy_exp_pauli_term(term)
181
+ cloned.pauli_map = dict(cloned.pauli_map)
182
+ new_ham.append((cloned, coeff))
183
+ new_hamiltonians.append(new_ham)
184
+
185
+ # Collect all qubits used in circuit and Hamiltonians
186
+ used_qubits = set()
187
+ for term in new_circuit:
188
+ used_qubits.update(term.pauli_map.keys())
189
+
190
+ if new_hamiltonians is not None:
191
+ for ham in new_hamiltonians:
192
+ for term, _ in ham:
193
+ used_qubits.update(term.pauli_map.keys())
194
+
195
+ if not used_qubits:
196
+ self.n_qubits_compressed = 0
197
+ return new_circuit, new_hamiltonians, 0
198
+
199
+ # Create qubit mapping and remap all terms
200
+ qubit_map = {old: new for new, old in enumerate(sorted(used_qubits))}
201
+
202
+ for term in new_circuit:
203
+ term.pauli_map = {qubit_map[old]: op for old, op in term.pauli_map.items()}
204
+
205
+ if new_hamiltonians is not None:
206
+ for ham in new_hamiltonians:
207
+ for term, _ in ham:
208
+ term.pauli_map = {qubit_map[old]: op for old, op in term.pauli_map.items()}
209
+
210
+ self.n_qubits_compressed = len(used_qubits)
211
+
212
+ return new_circuit, new_hamiltonians, self.n_qubits_compressed
213
+
214
+ def update_variables(self, variables, *args, **kwargs):
215
+ if variables is None:
216
+ variables = {}
217
+ super().update_variables(variables)
218
+ self.circuit = self.create_circuit(abstract_circuit=self.abstract_circuit, variables=variables)
219
+
220
+ def assign_parameter(self, param, variables):
221
+ if isinstance(param, (int, float, complex)):
222
+ return float(param)
223
+ if isinstance(param, str):
224
+ if param in variables:
225
+ return float(variables[param])
226
+ else:
227
+ raise TequilaSpexException(f"Variable '{param}' not found in variables")
228
+ if callable(param):
229
+ result = param(variables)
230
+ return float(result)
231
+
232
+ raise TequilaSpexException(f"Can't assign parameter '{param}'.")
233
+
234
+ def add_basic_gate(self, gate, circuit, *args, **kwargs):
235
+ """Convert Tequila gates to SPEX exponential Pauli terms"""
236
+ exp_term = spex_tequila.ExpPauliTerm()
237
+ if isinstance(gate, ExponentialPauliGateImpl):
238
+ if self.angle_threshold is not None and abs(gate.parameter) < self.angle_threshold:
239
+ return
240
+ exp_term.pauli_map = extract_pauli_dict(gate.paulistring)
241
+ exp_term.angle = gate.parameter
242
+ circuit.append(exp_term)
243
+
244
+ elif isinstance(gate, RotationGateImpl):
245
+ if self.angle_threshold is not None and abs(gate.parameter) < self.angle_threshold:
246
+ return
247
+ exp_term.pauli_map = extract_pauli_dict(gate.generator)
248
+ exp_term.angle = gate.parameter
249
+ circuit.append(exp_term)
250
+
251
+ elif isinstance(gate, QubitExcitationImpl):
252
+ compiled_gate = gate.compile(exponential_pauli=True)
253
+ for sub_gate in compiled_gate.abstract_circuit.gates:
254
+ self.add_basic_gate(sub_gate, circuit, *args, **kwargs)
255
+
256
+ elif isinstance(gate, QGateImpl):
257
+ if gate.name.lower() in ["x", "y", "z"]:
258
+ # Convert standard gates to Pauli rotations
259
+ for ps in gate.make_generator(include_controls=True).paulistrings:
260
+ angle = numpy.pi * ps.coeff
261
+ if self.angle_threshold is not None and abs(angle) < self.angle_threshold:
262
+ continue
263
+ exp_term = spex_tequila.ExpPauliTerm()
264
+ exp_term.pauli_map = dict(ps.items())
265
+ exp_term.angle = angle
266
+ circuit.append(exp_term)
267
+ elif gate.name.lower() in ["h", "hadamard"]:
268
+ assert len(gate.target) == 1
269
+ target = gate.target[0]
270
+ for ps in ["-0.25*Y({q})", "Z({q})", "0.25*Y({q})"]:
271
+ ps = QubitHamiltonian(ps.format(q=gate.target[0])).paulistrings[0]
272
+ angle = numpy.pi * ps.coeff
273
+ exp_term = spex_tequila.ExpPauliTerm()
274
+ exp_term.pauli_map = dict(ps.items())
275
+ exp_term.angle = angle
276
+ circuit.append(exp_term)
277
+ else:
278
+ raise TequilaSpexException("{} not supported. Only x,y,z,h".format(gate.name.lower()))
279
+
280
+ else:
281
+ raise TequilaSpexException(
282
+ f"Unsupported gate object type: {type(gate)}. "
283
+ "All gates should be compiled to exponential pauli or rotation gates."
284
+ )
285
+
286
+ def add_parametrized_gate(self, gate, circuit, *args, **kwargs):
287
+ """Convert Tequila parametrized gates to SPEX exponential Pauli terms"""
288
+ exp_term = spex_tequila.ExpPauliTerm()
289
+ if isinstance(gate, ExponentialPauliGateImpl):
290
+ angle = self.assign_parameter(gate.parameter, kwargs.get("variables", {}))
291
+ if self.angle_threshold is not None and abs(angle) < self.angle_threshold:
292
+ return
293
+ exp_term.pauli_map = extract_pauli_dict(gate.paulistring)
294
+ exp_term.angle = angle
295
+ circuit.append(exp_term)
296
+
297
+ elif isinstance(gate, RotationGateImpl):
298
+ angle = self.assign_parameter(gate.parameter, kwargs.get("variables", {}))
299
+ if self.angle_threshold is not None and abs(angle) < self.angle_threshold:
300
+ return
301
+ exp_term.pauli_map = extract_pauli_dict(gate.generator)
302
+ exp_term.angle = angle
303
+ circuit.append(exp_term)
304
+
305
+ elif isinstance(gate, QubitExcitationImpl):
306
+ compiled_gate = gate.compile(exponential_pauli=True)
307
+ for sub_gate in compiled_gate.abstract_circuit.gates:
308
+ self.add_parametrized_gate(sub_gate, circuit, *args, **kwargs)
309
+
310
+ elif isinstance(gate, QGateImpl):
311
+ for ps in gate.make_generator(include_controls=True).paulistrings:
312
+ if self.angle_threshold is not None and abs(gate.parameter) < self.angle_threshold:
313
+ continue
314
+ exp_term = spex_tequila.ExpPauliTerm()
315
+ exp_term.pauli_map = dict(ps.items())
316
+ exp_term.angle = gate.parameter
317
+ circuit.append(exp_term)
318
+
319
+ else:
320
+ raise TequilaSpexException(
321
+ f"Unsupported gate type: {type(gate)}. "
322
+ "Only Exponential Pauli and Rotation gates are allowed after compilation."
323
+ )
324
+
325
+ def do_simulate(self, variables, initial_state=0, *args, **kwargs) -> QubitWaveFunction:
326
+ """
327
+ Simulate circuit and return final state
328
+ Args:
329
+ initial_state: Starting state (int or QubitWaveFunction)
330
+ Returns:
331
+ QubitWaveFunction: Sparse state representation
332
+ """
333
+
334
+ if self.compress_qubits and self.n_qubits_compressed is not None and self.n_qubits_compressed > 0:
335
+ n_qubits = self.n_qubits_compressed
336
+ else:
337
+ n_qubits = self.n_qubits
338
+
339
+ # Initialize state
340
+ if isinstance(initial_state, (int, numpy.integer)):
341
+ if initial_state == 0:
342
+ state = spex_tequila.initialize_zero_state(n_qubits)
343
+ else:
344
+ state = {initial_state: 1.0 + 0j}
345
+ else:
346
+ # initial_state is already a QubitWaveFunction
347
+ state = {k: v for k, v in initial_state.raw_items()}
348
+
349
+ # Apply circuit with amplitude thresholding, -1.0 disables threshold in spex_tequila
350
+ threshold = self.amplitude_threshold if self.amplitude_threshold is not None else -1.0
351
+ final_state = spex_tequila.apply_U(self.circuit, state, threshold, n_qubits)
352
+
353
+ wfn_MSB = QubitWaveFunction(n_qubits=n_qubits, numbering=BitNumbering.MSB)
354
+ for state, amplitude in final_state.items():
355
+ wfn_MSB[state] = amplitude
356
+
357
+ del final_state
358
+ gc.collect()
359
+
360
+ return wfn_MSB
361
+
362
+ def simulate(self, variables, initial_state=0, *args, **kwargs) -> QubitWaveFunction:
363
+ """Override simulate to avoid automatic mapping by KeyMapSubregisterToRegister"""
364
+ self.update_variables(variables)
365
+ result = self.do_simulate(variables=variables, initial_state=initial_state, *args, **kwargs)
366
+ return result
367
+
368
+
369
+ class BackendExpectationValueSpex(BackendExpectationValue):
370
+ """SPEX expectation value calculator using sparse simulations"""
371
+
372
+ BackendCircuitType = BackendCircuitSpex
373
+
374
+ def __init__(
375
+ self, *args, num_threads=-1, amplitude_threshold=1e-14, angle_threshold=1e-14, compress_qubits=True, **kwargs
376
+ ):
377
+ super().__init__(*args, **kwargs)
378
+
379
+ self.num_threads = num_threads
380
+ self.amplitude_threshold = amplitude_threshold
381
+ self.angle_threshold = angle_threshold
382
+
383
+ # Configure circuit parameters
384
+ if isinstance(self.U, BackendCircuitSpex):
385
+ self.U.num_threads = num_threads
386
+ self.U.amplitude_threshold = amplitude_threshold
387
+ self.U.angle_threshold = angle_threshold
388
+ self.U.compress_qubits = compress_qubits
389
+
390
+ def initialize_hamiltonian(self, hamiltonians):
391
+ """
392
+ Initializes the Hamiltonian terms for the simulation.
393
+ Args:
394
+ hamiltonians: A list of Hamiltonian objects.
395
+ Returns:
396
+ tuple: A converted list of (pauli_string, coefficient) tuples.
397
+ """
398
+ # Convert Tequila Hamiltonians into a list of (pauli_string, coeff) tuples for spex_tequila.
399
+ converted = []
400
+ for H in hamiltonians:
401
+ terms = []
402
+ for ps in H.paulistrings:
403
+ # Construct Pauli string like "X(0)Y(1)"
404
+ pauli_map = dict(ps.items())
405
+ term = spex_tequila.ExpPauliTerm()
406
+ term.pauli_map = pauli_map
407
+ terms.append((term, ps.coeff))
408
+ converted.append(terms)
409
+
410
+ if isinstance(self.U, BackendCircuitSpex):
411
+ self.U.hamiltonians = converted
412
+
413
+ return tuple(converted)
414
+
415
+ def simulate(self, variables, initial_state=0, *args, **kwargs):
416
+ """
417
+ Calculate expectation value through sparse simulation
418
+ Returns:
419
+ numpy.ndarray: Expectation values for each Hamiltonian term
420
+ """
421
+
422
+ # Prepare simulation
423
+ self.update_variables(variables)
424
+
425
+ if self.U.compress_qubits:
426
+ circuit, comp_hams, n_qubits = self.U.compress_qubit_indices()
427
+ else:
428
+ circuit = self.U.circuit
429
+ comp_hams = self.H
430
+ n_qubits = self.U.n_qubits
431
+
432
+ # Prepare the initial state
433
+ if isinstance(initial_state, int):
434
+ if initial_state == 0:
435
+ state = spex_tequila.initialize_zero_state(n_qubits)
436
+ else:
437
+ state = {initial_state: 1.0 + 0j}
438
+ else:
439
+ # initial_state is a QubitWaveFunction
440
+ state = {k: v for k, v in initial_state.raw_items()}
441
+
442
+ circuit = [t for t in circuit if abs(t.angle) >= self.U.angle_threshold]
443
+
444
+ threshold = self.amplitude_threshold if self.amplitude_threshold is not None else -1.0
445
+ final_state = spex_tequila.apply_U(circuit, state, threshold, n_qubits)
446
+
447
+ del state
448
+
449
+ if "SPEX_NUM_THREADS" in os.environ:
450
+ self.num_threads = int(os.environ["SPEX_NUM_THREADS"])
451
+ elif "OMP_NUM_THREADS" in os.environ:
452
+ self.num_threads = int(os.environ["OMP_NUM_THREADS"])
453
+
454
+ # Calculate the expectation value for each Hamiltonian
455
+ results = []
456
+
457
+ for H_terms in comp_hams:
458
+ val = spex_tequila.expectation_value_parallel(final_state, final_state, H_terms, n_qubits, num_threads=-1)
459
+ results.append(val.real)
460
+
461
+ del final_state
462
+ gc.collect()
463
+
464
+ return numpy.array(results)
@@ -11,8 +11,8 @@ import sympy
11
11
  Simple Symbolic Simulator for debugging purposes
12
12
  """
13
13
 
14
- class BackendCircuitSymbolic(BackendCircuit):
15
14
 
15
+ class BackendCircuitSymbolic(BackendCircuit):
16
16
  # compiler instructions
17
17
  compiler_arguments = {
18
18
  "trotterized": True,
@@ -29,7 +29,7 @@ class BackendCircuitSymbolic(BackendCircuit):
29
29
  "controlled_phase": True,
30
30
  "toffoli": True,
31
31
  "phase_to_z": True,
32
- "cc_max": True
32
+ "cc_max": True,
33
33
  }
34
34
 
35
35
  convert_to_numpy = True
@@ -49,7 +49,7 @@ class BackendCircuitSymbolic(BackendCircuit):
49
49
  return result
50
50
 
51
51
  @classmethod
52
- def apply_on_standard_basis(cls, gate: QGate, basis_state: BitString, qubits:dict, variables) -> QubitWaveFunction:
52
+ def apply_on_standard_basis(cls, gate: QGate, basis_state: BitString, qubits: dict, variables) -> QubitWaveFunction:
53
53
  n_qubits = len(qubits.keys())
54
54
  basis_array = basis_state.array
55
55
  if gate.is_controlled() and not all(basis_array[qubits[c]] == 1 for c in gate.control):
@@ -70,8 +70,8 @@ class BackendCircuitSymbolic(BackendCircuit):
70
70
  fac1 = None
71
71
  fac2 = None
72
72
  if gate.name == "H":
73
- fac1 = (sympy.Integer(-1) ** qt * sympy.sqrt(sympy.Rational(1 / 2)))
74
- fac2 = (sympy.sqrt(sympy.Rational(1 / 2)))
73
+ fac1 = sympy.Integer(-1) ** qt * sympy.sqrt(sympy.Rational(1 / 2))
74
+ fac2 = sympy.sqrt(sympy.Rational(1 / 2))
75
75
  elif gate.name.upper() == "CNOT" or gate.name.upper() == "X":
76
76
  fac2 = sympy.Integer(1)
77
77
  elif gate.name.upper() == "Y":
@@ -129,5 +129,6 @@ class BackendCircuitSymbolic(BackendCircuit):
129
129
 
130
130
  return wfn
131
131
 
132
+
132
133
  class BackendExpectationValueSymbolic(BackendExpectationValue):
133
134
  BackendCircuitType = BackendCircuitSymbolic