tequila-basic 1.9.8__py3-none-any.whl → 1.9.9__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.
@@ -9,6 +9,7 @@ import warnings
9
9
 
10
10
  from .qpic import export_to
11
11
 
12
+
12
13
  class QCircuit():
13
14
  """
14
15
  Fundamental class representing an abstract circuit.
@@ -254,8 +255,8 @@ class QCircuit():
254
255
  the gates to add at the corresponding positions
255
256
  replace: list of bool: (Default value: None)
256
257
  Default is None which corresponds to all true
257
- decide if gates shall be replaces or if the new parts shall be inserted without replacement
258
- if replace[i] = true: gate at position [i] will be replaces by gates[i]
258
+ decide if gates shall be replaced or if the new parts shall be inserted without replacement
259
+ if replace[i] = true: gate at position [i] will be replaced by gates[i]
259
260
  if replace[i] = false: gates[i] circuit will be inserted at position [i] (beaming before gate previously at position [i])
260
261
  Returns
261
262
  -------
@@ -271,8 +272,9 @@ class QCircuit():
271
272
  dataset = zip(positions, circuits, replace)
272
273
  dataset = sorted(dataset, key=lambda x: x[0])
273
274
 
274
- offset = 0
275
- new_gatelist = self.gates
275
+ new_gatelist = []
276
+ last_idx = -1
277
+
276
278
  for idx, circuit, do_replace in dataset:
277
279
 
278
280
  # failsafe
@@ -283,13 +285,14 @@ class QCircuit():
283
285
  else:
284
286
  gatelist = [circuit]
285
287
 
286
- pos = idx + offset
287
- if do_replace:
288
- new_gatelist = new_gatelist[:pos] + gatelist + new_gatelist[pos + 1:]
289
- offset += len(gatelist) - 1
290
- else:
291
- new_gatelist = new_gatelist[:pos] + gatelist + new_gatelist[pos:]
292
- offset += len(gatelist)
288
+ new_gatelist += self.gates[last_idx + 1:idx]
289
+ new_gatelist += gatelist
290
+ if not do_replace:
291
+ new_gatelist.append(self.gates[idx])
292
+
293
+ last_idx = idx
294
+
295
+ new_gatelist += self.gates[last_idx + 1:]
293
296
 
294
297
  result = QCircuit(gates=new_gatelist)
295
298
  result.n_qubits = max(result.n_qubits, self.n_qubits)
@@ -386,7 +389,7 @@ class QCircuit():
386
389
  for k, v in other._parameter_map.items():
387
390
  self._parameter_map[k] += [(x[0] + offset, x[1]) for x in v]
388
391
 
389
- self._gates += copy.deepcopy(other.gates)
392
+ self._gates += other.gates
390
393
  self._min_n_qubits = max(self._min_n_qubits, other._min_n_qubits)
391
394
 
392
395
  return self
@@ -593,7 +596,7 @@ class QCircuit():
593
596
 
594
597
  This is an in-place method, so it mutates self and doesn't return any value.
595
598
 
596
- Raise TequilaWarning if there any qubits in common between self and control.
599
+ Raise TequilaWarning if there are any qubits in common between self and control.
597
600
  """
598
601
  gates = self.gates
599
602
  control = list_assignment(control)
@@ -610,8 +613,10 @@ class QCircuit():
610
613
  if len(control_lst) < len(gate.control) + len(control):
611
614
  # warnings.warn("Some of the controls {} were already included in the control "
612
615
  # "of a gate {}.".format(control, gate), TequilaWarning)
613
- raise TequilaWarning(f'Some of the controls {control} were already included '
614
- f'in the control of a gate {gate}.')
616
+ raise TequilaWarning(f"Some of the controls {control} were already included "
617
+ f"in the control of a gate {gate}. "
618
+ f"This might be because the same instance of a gate is used in multiple places, "
619
+ f"e.g. because the same circuit was appended twice.")
615
620
  else:
616
621
  control_lst, not_control = list(control), list()
617
622
 
@@ -1,4 +1,5 @@
1
1
  import typing, copy, numbers
2
+ from typing import Union
2
3
  from tequila.grouping.compile_groups import compile_commuting_parts
3
4
  from tequila import TequilaException
4
5
  from tequila.utils import JoinedTransformation
@@ -545,7 +546,7 @@ class Objective:
545
546
  "variables = {}\n" \
546
547
  "types = {}".format(unique, measurements, variables, types)
547
548
 
548
- def __call__(self, variables=None, *args, **kwargs):
549
+ def __call__(self, variables=None, initial_state = 0, *args, **kwargs):
549
550
  """
550
551
  Return the output of the calculation the objective represents.
551
552
 
@@ -553,6 +554,8 @@ class Objective:
553
554
  ----------
554
555
  variables: dict:
555
556
  dictionary instantiating all variables that may appear within the objective.
557
+ initial_state: int or QubitWaveFunction:
558
+ the initial state of the circuit
556
559
  args
557
560
  kwargs
558
561
 
@@ -579,7 +582,7 @@ class Objective:
579
582
  ev_array = []
580
583
  for E in self.args:
581
584
  if E not in evaluated: #
582
- expval_result = E(variables=variables, *args, **kwargs)
585
+ expval_result = E(variables=variables, initial_state=initial_state, *args, **kwargs)
583
586
  evaluated[E] = expval_result
584
587
  else:
585
588
  expval_result = evaluated[E]
@@ -442,7 +442,14 @@ class ParametersQC:
442
442
  for i in range(natoms):
443
443
  coord += content[2 + i]
444
444
  return coord, comment
445
-
445
+ def get_xyz(self)->str:
446
+ geom = self.parameters.get_geometry()
447
+ f = ''
448
+ f += f'{len(geom)}\n'
449
+ f += f'{self.parameters.name}\n'
450
+ for at in geom:
451
+ f += f'{at[0]} {at[1][0]} {at[1][1]} {at[1][2]}\n'
452
+ return f
446
453
 
447
454
  @dataclass
448
455
  class ClosedShellAmplitudes:
@@ -217,7 +217,8 @@ class PySCFVQEWrapper:
217
217
  vqe_solver_arguments = self.vqe_solver_arguments
218
218
  result = self.vqe_solver(H=H, circuit=self.circuit, molecule=molecule, **vqe_solver_arguments)
219
219
  if hasattr(self.vqe_solver, "compute_rdms"):
220
- rdm1, rdm2 = self.vqe_solver.compute_rdms(U=self.circuit, variables=result.variables, molecule=molecule, use_hcb=restrict_to_hcb)
220
+ rdm1,rdm2 = self.vqe_solver.compute_rdms(U=self.circuit, variables=result.variables, molecule=molecule, use_hcb=restrict_to_hcb)
221
+ rdm2 = self.reorder(rdm2, 'dirac', 'mulliken')
221
222
  elif self.circuit is None:
222
223
  raise Exception("Orbital Optimizer: Either provide a callable vqe_solver or a circuit")
223
224
  else:
@@ -11,8 +11,9 @@ from tequila.simulators.simulator_base import BackendCircuit, BackendExpectation
11
11
  from tequila.circuit.noise import NoiseModel
12
12
  from tequila.wavefunction.qubit_wavefunction import QubitWaveFunction
13
13
 
14
- SUPPORTED_BACKENDS = ["qulacs", "qulacs_gpu", "qibo", "qiskit", "qiskit_gpu", "cirq", "pyquil", "symbolic", "qlm"]
15
- SUPPORTED_NOISE_BACKENDS = ["qiskit", "qiskit_gpu", "cirq", "pyquil"] # qulacs removed in v.1.9
14
+ SUPPORTED_BACKENDS = ["qulacs", "qulacs_gpu", "qibo", "qiskit", "qiskit_gpu", "cirq", "pyquil", "symbolic", "qlm", "spex"]
15
+ # TODO: Reenable noise for Qiskit
16
+ SUPPORTED_NOISE_BACKENDS = ["cirq", "pyquil"] # qulacs removed in v.1.9
16
17
  BackendTypes = namedtuple('BackendTypes', 'CircType ExpValueType')
17
18
  INSTALLED_SIMULATORS = {}
18
19
  INSTALLED_SAMPLERS = {}
@@ -30,6 +31,15 @@ We are distinguishing two classes of simulators: Samplers and full wavefunction
30
31
  """
31
32
 
32
33
 
34
+ HAS_SPEX = True
35
+ try:
36
+ from tequila.simulators.simulator_spex import BackendCircuitSpex, BackendExpectationValueSpex
37
+
38
+ INSTALLED_SIMULATORS["spex"] = BackendTypes(BackendCircuitSpex, BackendExpectationValueSpex)
39
+ except ImportError:
40
+ HAS_SPEX = False
41
+
42
+
33
43
  HAS_QISKIT = True
34
44
  try:
35
45
  from tequila.simulators.simulator_qiskit import BackendCircuitQiskit, BackendExpectationValueQiskit
@@ -431,7 +431,8 @@ class BackendCircuit():
431
431
  return self.do_sample(samples=samples, circuit=circuit, read_out_qubits=read_out_qubits,
432
432
  initial_state=initial_state, *args, **kwargs)
433
433
 
434
- def sample_all_z_hamiltonian(self, samples: int, hamiltonian, variables, *args, **kwargs):
434
+ def sample_all_z_hamiltonian(self, samples: int, hamiltonian, variables,
435
+ initial_state: Union[int, QubitWaveFunction] = 0, *args, **kwargs):
435
436
  """
436
437
  Sample from a Hamiltonian which only consists of Pauli-Z and unit operators
437
438
  Parameters
@@ -440,6 +441,8 @@ class BackendCircuit():
440
441
  number of samples to take
441
442
  hamiltonian
442
443
  the tequila hamiltonian
444
+ initial_state
445
+ the initial state of the circuit
443
446
  args
444
447
  arguments for do_sample
445
448
  kwargs
@@ -458,7 +461,7 @@ class BackendCircuit():
458
461
  self.n_qubits))
459
462
 
460
463
  # run simulators
461
- counts = self.sample(samples=samples, read_out_qubits=abstract_qubits_H, variables=variables, *args, **kwargs)
464
+ counts = self.sample(samples=samples, read_out_qubits=abstract_qubits_H, variables=variables, initial_state=initial_state, *args, **kwargs)
462
465
  read_out_map = {q: i for i, q in enumerate(abstract_qubits_H)}
463
466
 
464
467
  # compute energy
@@ -481,8 +484,8 @@ class BackendCircuit():
481
484
  assert n_samples == samples
482
485
  return E
483
486
 
484
- def sample_paulistring(self, samples: int, paulistring, variables, *args,
485
- **kwargs) -> numbers.Real:
487
+ def sample_paulistring(self, samples: int, paulistring, variables, initial_state: Union[int, QubitWaveFunction] = 0,
488
+ *args, **kwargs) -> numbers.Real:
486
489
  """
487
490
  Sample an individual pauli word (pauli string) and return the average result thereof.
488
491
  Parameters
@@ -520,8 +523,8 @@ class BackendCircuit():
520
523
  # on construction: tq.ExpectationValue(H=H, U=U, optimize_measurements=True)
521
524
  circuit = self.create_circuit(circuit=copy.deepcopy(self.circuit), abstract_circuit=basis_change)
522
525
  # run simulators
523
- counts = self.sample(samples=samples, circuit=circuit, read_out_qubits=qubits, variables=variables, *args,
524
- **kwargs)
526
+ counts = self.sample(samples=samples, circuit=circuit, read_out_qubits=qubits, variables=variables,
527
+ initial_state=initial_state, *args, **kwargs)
525
528
  # compute energy
526
529
  E = 0.0
527
530
  n_samples = 0
@@ -792,7 +795,7 @@ class BackendExpectationValue:
792
795
  def __deepcopy__(self, memodict={}):
793
796
  return type(self)(self.abstract_expectationvalue, **self._input_args)
794
797
 
795
- def __call__(self, variables, samples: int = None, *args, **kwargs):
798
+ def __call__(self, variables, samples: int = None, initial_state: Union[int, QubitWaveFunction] = 0, *args, **kwargs):
796
799
 
797
800
  variables = format_variable_dictionary(variables=variables)
798
801
  if self._variables is not None and len(self._variables) > 0:
@@ -802,9 +805,9 @@ class BackendExpectationValue:
802
805
  self._variables, variables))
803
806
 
804
807
  if samples is None:
805
- data = self.simulate(variables=variables, *args, **kwargs)
808
+ data = self.simulate(variables=variables, initial_state=initial_state, *args, **kwargs)
806
809
  else:
807
- data = self.sample(variables=variables, samples=samples, *args, **kwargs)
810
+ data = self.sample(variables=variables, samples=samples, initial_state=initial_state, *args, **kwargs)
808
811
 
809
812
  if self._shape is None and self._contraction is None:
810
813
  # this is the default
@@ -852,7 +855,7 @@ class BackendExpectationValue:
852
855
  """wrapper over circuit update_variables"""
853
856
  self._U.update_variables(variables=variables)
854
857
 
855
- def sample(self, variables, samples, *args, **kwargs) -> numpy.array:
858
+ def sample(self, variables, samples, initial_state: Union[int, QubitWaveFunction] = 0, *args, **kwargs) -> numpy.array:
856
859
  """
857
860
  sample the expectationvalue.
858
861
 
@@ -862,6 +865,8 @@ class BackendExpectationValue:
862
865
  variables to supply to the unitary.
863
866
  samples: int:
864
867
  number of samples to perform.
868
+ initial_state: int or QubitWaveFunction:
869
+ the initial state of the circuit
865
870
  args
866
871
  kwargs
867
872
 
@@ -891,16 +896,16 @@ class BackendExpectationValue:
891
896
  if len(H.qubits) == 0:
892
897
  E = sum([ps.coeff for ps in H.paulistrings])
893
898
  elif H.is_all_z():
894
- E = self.U.sample_all_z_hamiltonian(samples=samples, hamiltonian=H, variables=variables, *args,
895
- **kwargs)
899
+ E = self.U.sample_all_z_hamiltonian(samples=samples, hamiltonian=H, variables=variables, initial_state=initial_state,
900
+ *args, **kwargs)
896
901
  else:
897
902
  for ps in H.paulistrings:
898
- E += self.U.sample_paulistring(samples=samples, paulistring=ps, variables=variables, *args,
899
- **kwargs)
903
+ E += self.U.sample_paulistring(samples=samples, paulistring=ps, variables=variables, initial_state=initial_state,
904
+ *args, **kwargs)
900
905
  result.append(to_float(E))
901
906
  return numpy.asarray(result)
902
907
 
903
- def simulate(self, variables, *args, **kwargs):
908
+ def simulate(self, variables, initial_state: Union[int, QubitWaveFunction], *args, **kwargs):
904
909
  """
905
910
  Simulate the expectationvalue.
906
911
 
@@ -908,6 +913,8 @@ class BackendExpectationValue:
908
913
  ----------
909
914
  variables:
910
915
  variables to supply to the unitary.
916
+ initial_state: int or QubitWaveFunction:
917
+ the initial state of the circuit
911
918
  args
912
919
  kwargs
913
920
 
@@ -922,7 +929,7 @@ class BackendExpectationValue:
922
929
  final_E = 0.0
923
930
  # TODO inefficient,
924
931
  # Always better to overwrite this function
925
- wfn = self.U.simulate(variables=variables, *args, **kwargs)
932
+ wfn = self.U.simulate(variables=variables, initial_state=initial_state, *args, **kwargs)
926
933
  final_E += wfn.compute_expectationvalue(operator=H)
927
934
  result.append(to_float(final_E))
928
935
  return numpy.asarray(result)
@@ -9,7 +9,7 @@ from typing import Union
9
9
  import warnings
10
10
  import numpy as np
11
11
  import qiskit, qiskit_aer, qiskit.providers.fake_provider
12
- from qiskit import QuantumCircuit
12
+ from qiskit import QuantumCircuit, transpile
13
13
 
14
14
  HAS_NOISE = True
15
15
  try:
@@ -316,11 +316,9 @@ class BackendCircuitQiskit(BackendCircuit):
316
316
  optimization_level = kwargs['optimization_level']
317
317
 
318
318
  circuit = self.circuit.assign_parameters(self.resolver)
319
-
320
319
  circuit = self.add_state_init(circuit, initial_state)
321
-
322
320
  circuit.save_statevector()
323
-
321
+ circuit = transpile(circuit, qiskit_backend)
324
322
  backend_result = qiskit_backend.run(circuit, optimization_level=optimization_level).result()
325
323
 
326
324
  return QubitWaveFunction.from_array(array=backend_result.get_statevector(circuit).data, numbering=self.numbering)
@@ -349,11 +347,11 @@ class BackendCircuitQiskit(BackendCircuit):
349
347
  if 'optimization_level' in kwargs:
350
348
  optimization_level = kwargs['optimization_level']
351
349
  if self.device is None:
352
- qiskit_backend = self.retrieve_device('aer_simulator')
350
+ qiskit_backend = self.retrieve_device(self.STATEVECTOR_DEVICE_NAME)
353
351
  else:
354
352
  qiskit_backend = self.retrieve_device(self.device)
355
353
 
356
- if isinstance(qiskit_backend, IBMBackend):
354
+ if HAS_IBMQ and isinstance(qiskit_backend, IBMBackend):
357
355
  if self.noise_model is not None:
358
356
  raise TequilaException(
359
357
  'Cannot combine backend {} with custom noise models.'.format(str(qiskit_backend)))
@@ -370,7 +368,7 @@ class BackendCircuitQiskit(BackendCircuit):
370
368
  if self.noise_model is not None:
371
369
  from_back = self.noise_model
372
370
  basis = from_back.basis_gates
373
- use_backend = self.retrieve_device('aer_simulator')
371
+ use_backend = self.retrieve_device(self.STATEVECTOR_DEVICE_NAME)
374
372
  use_backend.set_options(noise_model=from_back)
375
373
  circuit = qiskit.transpile(circuit, backend=use_backend,
376
374
  basis_gates=basis,
@@ -429,13 +429,15 @@ class BackendExpectationValueQulacs(BackendExpectationValue):
429
429
  use_mapping = True
430
430
  BackendCircuitType = BackendCircuitQulacs
431
431
 
432
- def simulate(self, variables, *args, **kwargs) -> numpy.array:
432
+ def simulate(self, variables, initial_state: Union[int, QubitWaveFunction] = 0, *args, **kwargs) -> numpy.array:
433
433
  """
434
434
  Perform simulation of this expectationvalue.
435
435
  Parameters
436
436
  ----------
437
437
  variables:
438
438
  variables, to be supplied to the underlying circuit.
439
+ initial_state: int or QubitWaveFunction:
440
+ the initial state of the circuit
439
441
  args
440
442
  kwargs
441
443
 
@@ -453,7 +455,7 @@ class BackendExpectationValueQulacs(BackendExpectationValue):
453
455
  return numpy.asarray[self.H]
454
456
 
455
457
  self.U.update_variables(variables)
456
- state = self.U.initialize_state(self.n_qubits)
458
+ state = self.U.initialize_state(self.n_qubits, initial_state)
457
459
  self.U.circuit.update_quantum_state(state)
458
460
  result = []
459
461
  for H in self.H:
@@ -495,7 +497,7 @@ class BackendExpectationValueQulacs(BackendExpectationValue):
495
497
  result.append(qulacs_H)
496
498
  return result
497
499
 
498
- def sample(self, variables, samples, *args, **kwargs) -> numpy.array:
500
+ def sample(self, variables, samples, initial_state: Union[int, QubitWaveFunction] = 0, *args, **kwargs) -> numpy.array:
499
501
  """
500
502
  Sample this Expectation Value.
501
503
  Parameters
@@ -504,6 +506,8 @@ class BackendExpectationValueQulacs(BackendExpectationValue):
504
506
  variables, to supply to the underlying circuit.
505
507
  samples: int:
506
508
  the number of samples to take.
509
+ initial_state: int or QubitWaveFunction:
510
+ the initial state of the circuit
507
511
  args
508
512
  kwargs
509
513
 
@@ -513,13 +517,13 @@ class BackendExpectationValueQulacs(BackendExpectationValue):
513
517
  the result of sampling as a number.
514
518
  """
515
519
  self.update_variables(variables)
516
- state = self.U.initialize_state(self.n_qubits)
520
+ state = self.U.initialize_state(self.n_qubits, initial_state)
517
521
  self.U.circuit.update_quantum_state(state)
518
522
  result = []
519
- for H in self._reduced_hamiltonians: # those are the hamiltonians which where non-used qubits are already traced out
523
+ for H in self._reduced_hamiltonians: # those are the hamiltonians which where non-used qubits are already traced out
520
524
  E = 0.0
521
525
  if H.is_all_z() and not self.U.has_noise:
522
- E = super().sample(samples=samples, variables=variables, *args, **kwargs)
526
+ E = super().sample(samples=samples, variables=variables, initial_state=initial_state, *args, **kwargs)
523
527
  else:
524
528
  for ps in H.paulistrings:
525
529
  # change basis, measurement is destructive so the state will be copied
@@ -530,8 +534,8 @@ class BackendExpectationValueQulacs(BackendExpectationValue):
530
534
  qbc = self.U.create_circuit(abstract_circuit=bc, variables=None)
531
535
  Esamples = []
532
536
  for sample in range(samples):
533
- if self.U.has_noise and sample>0:
534
- state = self.U.initialize_state(self.n_qubits)
537
+ if self.U.has_noise and sample > 0:
538
+ state = self.U.initialize_state(self.n_qubits, initial_state)
535
539
  self.U.circuit.update_quantum_state(state)
536
540
  state_tmp = state
537
541
  else:
@@ -540,7 +544,7 @@ class BackendExpectationValueQulacs(BackendExpectationValue):
540
544
  qbc.update_quantum_state(state_tmp)
541
545
  ps_measure = 1.0
542
546
  for idx in ps.keys():
543
- assert idx in self.U.abstract_qubits # assert that the hamiltonian was really reduced
547
+ assert idx in self.U.abstract_qubits # assert that the hamiltonian was really reduced
544
548
  M = qulacs.gate.Measurement(self.U.qubit(idx), self.U.qubit(idx))
545
549
  M.update_quantum_state(state_tmp)
546
550
  measured = state_tmp.get_classical_value(self.U.qubit(idx))
@@ -0,0 +1,429 @@
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
+ class TequilaSpexException(TequilaException):
19
+ """Custom exception for SPEX simulator errors"""
20
+ pass
21
+
22
+ def extract_pauli_dict(ps):
23
+ """
24
+ Extract qubit:operator mapping from PauliString/QubitHamiltonian
25
+ Args:
26
+ ps: PauliString or single-term QubitHamiltonian
27
+ Returns:
28
+ dict: {qubit: 'X'/'Y'/'Z'}
29
+ """
30
+
31
+ if isinstance(ps, PauliString):
32
+ return dict(ps.items())
33
+ if isinstance(ps, QubitHamiltonian) and len(ps.paulistrings) == 1:
34
+ return dict(ps.paulistrings[0].items())
35
+ raise TequilaSpexException("Unsupported generator type")
36
+
37
+ def circuit_hash(abstract_circuit, variables=None):
38
+ """
39
+ Create MD5 hash for circuit caching
40
+ Uses gate types, targets, controls and generators for uniqueness
41
+ """
42
+ sha = hashlib.md5()
43
+ if abstract_circuit is None:
44
+ return None
45
+ for g in abstract_circuit.gates:
46
+ gate_str = f"{type(g).__name__}:{g.name}:{g.target}:{g.control}:{g.generator}:{getattr(g, 'parameter', None)}\n"
47
+ sha.update(gate_str.encode('utf-8'))
48
+ if variables:
49
+ for key, value in sorted(variables.items()):
50
+ sha.update(f"{key}:{value}\n".encode('utf-8'))
51
+ return sha.hexdigest()
52
+
53
+ class BackendCircuitSpex(BackendCircuit):
54
+ """SPEX circuit implementation using sparse state representation"""
55
+
56
+ # Circuit compilation configuration
57
+ compiler_arguments = {
58
+ "multitarget": True,
59
+ "multicontrol": True,
60
+ "trotterized": True,
61
+ "generalized_rotation": True,
62
+ "exponential_pauli": False,
63
+ "controlled_exponential_pauli": True,
64
+ "hadamard_power": True,
65
+ "controlled_power": True,
66
+ "power": True,
67
+ "toffoli": True,
68
+ "controlled_phase": True,
69
+ "phase": True,
70
+ "phase_to_z": True,
71
+ "controlled_rotation": True,
72
+ "swap": True,
73
+ "cc_max": True,
74
+ "ry_gate": True,
75
+ "y_gate": True,
76
+ "ch_gate": True
77
+ }
78
+
79
+ def __init__(self,
80
+ abstract_circuit=None,
81
+ variables=None,
82
+ num_threads=-1,
83
+ amplitude_threshold=1e-14,
84
+ angle_threshold=1e-14,
85
+ compress_qubits=True,
86
+ *args, **kwargs):
87
+
88
+ # Circuit chaching
89
+ self.circuit_cache = {}
90
+
91
+ # Performance parameters
92
+ self.num_threads = num_threads
93
+ self.amplitude_threshold = amplitude_threshold
94
+ self.angle_threshold = angle_threshold
95
+
96
+ # State compression
97
+ self.compress_qubits = compress_qubits
98
+ self.n_qubits_compressed = None
99
+ self.hamiltonians = None
100
+
101
+ super().__init__(abstract_circuit=abstract_circuit, variables=variables, *args, **kwargs)
102
+
103
+ @property
104
+ def n_qubits(self):
105
+ """Get number of qubits after compression (if enabled)"""
106
+ used = set()
107
+ if hasattr(self, "circuit") and self.circuit:
108
+ for term in self.circuit:
109
+ used.update(term.pauli_map.keys())
110
+
111
+ if self.abstract_circuit is not None and hasattr(self.abstract_circuit, "gates"):
112
+ for gate in self.abstract_circuit.gates:
113
+ if hasattr(gate, "target"):
114
+ if isinstance(gate.target, (list, tuple)):
115
+ used.update(gate.target)
116
+ else:
117
+ used.add(gate.target)
118
+ if hasattr(gate, "control") and gate.control:
119
+ if isinstance(gate.control, (list, tuple)):
120
+ used.update(gate.control)
121
+ else:
122
+ used.add(gate.control)
123
+ computed = max(used) + 1 if used else 0
124
+ return max(super().n_qubits, computed)
125
+
126
+ def initialize_circuit(self, *args, **kwargs):
127
+ return []
128
+
129
+ def create_circuit(self, abstract_circuit=None, variables=None, *args, **kwargs):
130
+ """Compile circuit with caching using MD5 hash"""
131
+ if abstract_circuit is None:
132
+ abstract_circuit = self.abstract_circuit
133
+
134
+ key = circuit_hash(abstract_circuit, variables)
135
+
136
+ if key in self.circuit_cache:
137
+ return self.circuit_cache[key]
138
+
139
+ circuit = super().create_circuit(abstract_circuit=abstract_circuit, variables=variables, *args, **kwargs)
140
+
141
+ self.circuit_cache[key] = circuit
142
+
143
+ return circuit
144
+
145
+ def compress_qubit_indices(self):
146
+ """
147
+ Optimize qubit indices by mapping used qubits to contiguous range
148
+ Reduces memory usage by eliminating unused qubit dimensions
149
+ """
150
+ if not self.compress_qubits or not (hasattr(self, "circuit") and self.circuit):
151
+ return
152
+
153
+ # Collect all qubits used in circuit and Hamiltonians
154
+ used_qubits = set()
155
+ for term in self.circuit:
156
+ used_qubits.update(term.pauli_map.keys())
157
+ for ham in self.hamiltonians:
158
+ for term, _ in ham:
159
+ used_qubits.update(term.pauli_map.keys())
160
+
161
+ if not used_qubits:
162
+ self.n_qubits_compressed = 0
163
+ return
164
+
165
+ # Create qubit mapping and remap all terms
166
+ qubit_map = {old: new for new, old in enumerate(sorted(used_qubits))}
167
+
168
+ for term in self.circuit:
169
+ term.pauli_map = {qubit_map[old]: op for old, op in term.pauli_map.items()}
170
+
171
+ if self.hamiltonians is not None:
172
+ for ham in self.hamiltonians:
173
+ for term, _ in ham:
174
+ term.pauli_map = {qubit_map[old]: op for old, op in term.pauli_map.items()}
175
+
176
+ self.n_qubits_compressed = len(used_qubits)
177
+
178
+ def update_variables(self, variables, *args, **kwargs):
179
+ if variables is None:
180
+ variables = {}
181
+ super().update_variables(variables)
182
+ self.circuit = self.create_circuit(abstract_circuit=self.abstract_circuit, variables=variables)
183
+
184
+ def assign_parameter(self, param, variables):
185
+ if isinstance(param, (int, float, complex)):
186
+ return float(param)
187
+ if isinstance(param, str):
188
+ if param in variables:
189
+ return float(variables[param])
190
+ else:
191
+ raise TequilaSpexException(f"Variable '{param}' not found in variables")
192
+ if callable(param):
193
+ result = param(variables)
194
+ return float(result)
195
+
196
+ raise TequilaSpexException(f"Can't assign parameter '{param}'.")
197
+
198
+
199
+ def add_basic_gate(self, gate, circuit, *args, **kwargs):
200
+ """Convert Tequila gates to SPEX exponential Pauli terms"""
201
+ exp_term = spex_tequila.ExpPauliTerm()
202
+ if isinstance(gate, ExponentialPauliGateImpl):
203
+ if self.angle_threshold is not None and abs(gate.parameter) < self.angle_threshold:
204
+ return
205
+ exp_term.pauli_map = extract_pauli_dict(gate.paulistring)
206
+ exp_term.angle = gate.parameter
207
+ circuit.append(exp_term)
208
+
209
+ elif isinstance(gate, RotationGateImpl):
210
+ if self.angle_threshold is not None and abs(gate.parameter) < self.angle_threshold:
211
+ return
212
+ exp_term.pauli_map = extract_pauli_dict(gate.generator)
213
+ exp_term.angle = gate.parameter
214
+ circuit.append(exp_term)
215
+
216
+ elif isinstance(gate, QubitExcitationImpl):
217
+ compiled_gate = gate.compile(exponential_pauli=True)
218
+ for sub_gate in compiled_gate.abstract_circuit.gates:
219
+ self.add_basic_gate(sub_gate, circuit, *args, **kwargs)
220
+
221
+ elif isinstance(gate, QGateImpl):
222
+ if gate.name.lower() in ["x","y","z"]:
223
+ # Convert standard gates to Pauli rotations
224
+ for ps in gate.make_generator(include_controls=True).paulistrings:
225
+ angle = numpy.pi * ps.coeff
226
+ if self.angle_threshold is not None and abs(angle) < self.angle_threshold:
227
+ continue
228
+ exp_term = spex_tequila.ExpPauliTerm()
229
+ exp_term.pauli_map = dict(ps.items())
230
+ exp_term.angle = angle
231
+ circuit.append(exp_term)
232
+ elif gate.name.lower() in ["h", "hadamard"]:
233
+ assert len(gate.target)==1
234
+ target = gate.target[0]
235
+ for ps in ["-0.25*Y({q})", "Z({q})", "0.25*Y({q})"]:
236
+ ps = QubitHamiltonian(ps.format(q=gate.target[0])).paulistrings[0]
237
+ angle = numpy.pi * ps.coeff
238
+ exp_term = spex_tequila.ExpPauliTerm()
239
+ exp_term.pauli_map = dict(ps.items())
240
+ exp_term.angle = angle
241
+ circuit.append(exp_term)
242
+ else:
243
+ raise TequilaSpexException("{} not supported. Only x,y,z,h".format(gate.name.lower()))
244
+
245
+ else:
246
+ raise TequilaSpexException(f"Unsupported gate object type: {type(gate)}. "
247
+ "All gates should be compiled to exponential pauli or rotation gates.")
248
+
249
+
250
+
251
+ def add_parametrized_gate(self, gate, circuit, *args, **kwargs):
252
+ """Convert Tequila parametrized gates to SPEX exponential Pauli terms"""
253
+ exp_term = spex_tequila.ExpPauliTerm()
254
+ if isinstance(gate, ExponentialPauliGateImpl):
255
+ angle = self.assign_parameter(gate.parameter, kwargs.get("variables", {}))
256
+ if self.angle_threshold is not None and abs(angle) < self.angle_threshold:
257
+ return
258
+ exp_term.pauli_map = extract_pauli_dict(gate.paulistring)
259
+ exp_term.angle = angle
260
+ circuit.append(exp_term)
261
+
262
+ elif isinstance(gate, RotationGateImpl):
263
+ angle = self.assign_parameter(gate.parameter, kwargs.get("variables", {}))
264
+ if self.angle_threshold is not None and abs(angle) < self.angle_threshold:
265
+ return
266
+ exp_term.pauli_map = extract_pauli_dict(gate.generator)
267
+ exp_term.angle = angle
268
+ circuit.append(exp_term)
269
+
270
+ elif isinstance(gate, QubitExcitationImpl):
271
+ compiled_gate = gate.compile(exponential_pauli=True)
272
+ for sub_gate in compiled_gate.abstract_circuit.gates:
273
+ self.add_parametrized_gate(sub_gate, circuit, *args, **kwargs)
274
+
275
+ elif isinstance(gate, QGateImpl):
276
+ for ps in gate.make_generator(include_controls=True).paulistrings:
277
+ if self.angle_threshold is not None and abs(gate.parameter) < self.angle_threshold:
278
+ continue
279
+ exp_term = spex_tequila.ExpPauliTerm()
280
+ exp_term.pauli_map = dict(ps.items())
281
+ exp_term.angle = gate.parameter
282
+ circuit.append(exp_term)
283
+
284
+ else:
285
+ raise TequilaSpexException(f"Unsupported gate type: {type(gate)}. "
286
+ "Only Exponential Pauli and Rotation gates are allowed after compilation.")
287
+
288
+
289
+ def do_simulate(self, variables, initial_state=0, *args, **kwargs) -> QubitWaveFunction:
290
+ """
291
+ Simulate circuit and return final state
292
+ Args:
293
+ initial_state: Starting state (int or QubitWaveFunction)
294
+ Returns:
295
+ QubitWaveFunction: Sparse state representation
296
+ """
297
+
298
+ if self.compress_qubits and self.n_qubits_compressed is not None and self.n_qubits_compressed > 0:
299
+ n_qubits = self.n_qubits_compressed
300
+ else:
301
+ n_qubits = self.n_qubits
302
+
303
+ # Initialize state
304
+ if isinstance(initial_state, (int, numpy.integer)):
305
+ if initial_state == 0:
306
+ state = spex_tequila.initialize_zero_state(n_qubits)
307
+ else:
308
+ state = {initial_state: 1.0 + 0j}
309
+ else:
310
+ # initial_state is already a QubitWaveFunction
311
+ state = {k: v for k, v in initial_state.raw_items()}
312
+
313
+ # Apply circuit with amplitude thresholding, -1.0 disables threshold in spex_tequila
314
+ threshold = self.amplitude_threshold if self.amplitude_threshold is not None else -1.0
315
+ final_state = spex_tequila.apply_U(self.circuit, state, threshold, n_qubits)
316
+
317
+ wfn_MSB = QubitWaveFunction(n_qubits=n_qubits, numbering=BitNumbering.MSB)
318
+ for state, amplitude in final_state.items():
319
+ wfn_MSB[state] = amplitude
320
+
321
+ del final_state
322
+ gc.collect()
323
+
324
+ return wfn_MSB
325
+
326
+ def simulate(self, variables, initial_state=0, *args, **kwargs) -> QubitWaveFunction:
327
+ """Override simulate to avoid automatic mapping by KeyMapSubregisterToRegister"""
328
+ self.update_variables(variables)
329
+ result = self.do_simulate(variables=variables, initial_state=initial_state, *args, **kwargs)
330
+ return result
331
+
332
+
333
+ class BackendExpectationValueSpex(BackendExpectationValue):
334
+ """SPEX expectation value calculator using sparse simulations"""
335
+ BackendCircuitType = BackendCircuitSpex
336
+
337
+ def __init__(self, *args,
338
+ num_threads=-1,
339
+ amplitude_threshold=1e-14,
340
+ angle_threshold=1e-14,
341
+ compress_qubits=True,
342
+ **kwargs):
343
+ super().__init__(*args, **kwargs)
344
+
345
+ self.num_threads = num_threads
346
+ self.amplitude_threshold = amplitude_threshold
347
+ self.angle_threshold = angle_threshold
348
+
349
+ # Configure circuit parameters
350
+ if isinstance(self.U, BackendCircuitSpex):
351
+ self.U.num_threads = num_threads
352
+ self.U.amplitude_threshold = amplitude_threshold
353
+ self.U.angle_threshold = angle_threshold
354
+ self.U.compress_qubits = compress_qubits
355
+
356
+ def initialize_hamiltonian(self, hamiltonians):
357
+ """
358
+ Initializes the Hamiltonian terms for the simulation.
359
+ Args:
360
+ hamiltonians: A list of Hamiltonian objects.
361
+ Returns:
362
+ tuple: A converted list of (pauli_string, coefficient) tuples.
363
+ """
364
+ # Convert Tequila Hamiltonians into a list of (pauli_string, coeff) tuples for spex_tequila.
365
+ converted = []
366
+ for H in hamiltonians:
367
+ terms = []
368
+ for ps in H.paulistrings:
369
+ # Construct Pauli string like "X(0)Y(1)"
370
+ pauli_map = dict(ps.items())
371
+ term = spex_tequila.ExpPauliTerm()
372
+ term.pauli_map = pauli_map
373
+ terms.append((term, ps.coeff))
374
+ converted.append(terms)
375
+
376
+ if isinstance(self.U, BackendCircuitSpex):
377
+ self.U.hamiltonians = converted
378
+
379
+ return tuple(converted)
380
+
381
+
382
+ def simulate(self, variables, initial_state=0, *args, **kwargs):
383
+ """
384
+ Calculate expectation value through sparse simulation
385
+ Returns:
386
+ numpy.ndarray: Expectation values for each Hamiltonian term
387
+ """
388
+
389
+ # Prepare simulation
390
+ self.update_variables(variables)
391
+ if self.U.compress_qubits:
392
+ self.U.compress_qubit_indices()
393
+
394
+ if self.U.compress_qubits and self.U.n_qubits_compressed is not None and self.U.n_qubits_compressed > 0:
395
+ n_qubits = self.U.n_qubits_compressed
396
+ else:
397
+ n_qubits = self.U.n_qubits
398
+
399
+ # Prepare the initial state
400
+ if isinstance(initial_state, int):
401
+ if initial_state == 0:
402
+ state = spex_tequila.initialize_zero_state(n_qubits)
403
+ else:
404
+ state = {initial_state: 1.0 + 0j}
405
+ else:
406
+ # initial_state is a QubitWaveFunction
407
+ state = {k: v for k, v in initial_state.raw_items()}
408
+
409
+ self.U.circuit = [t for t in self.U.circuit if abs(t.angle) >= self.U.angle_threshold]
410
+
411
+ threshold = self.amplitude_threshold if self.amplitude_threshold is not None else -1.0
412
+ final_state = spex_tequila.apply_U(self.U.circuit, state, threshold, n_qubits)
413
+ del state
414
+
415
+ if "SPEX_NUM_THREADS" in os.environ:
416
+ self.num_threads = int(os.environ["SPEX_NUM_THREADS"])
417
+ elif "OMP_NUM_THREADS" in os.environ:
418
+ self.num_threads = int(os.environ["OMP_NUM_THREADS"])
419
+
420
+ # Calculate the expectation value for each Hamiltonian
421
+ results = []
422
+ for H_terms in self.H:
423
+ val = spex_tequila.expectation_value_parallel(final_state, final_state, H_terms, n_qubits, num_threads=-1)
424
+ results.append(val.real)
425
+
426
+ del final_state
427
+ gc.collect()
428
+
429
+ return numpy.array(results)
@@ -0,0 +1,211 @@
1
+ import tequila as tq
2
+ import numpy as np
3
+ import time
4
+ import os
5
+ import csv
6
+ from tequila.hamiltonian import PauliString
7
+ import pytest
8
+
9
+
10
+ #!/usr/bin/env python
11
+ import tequila as tq
12
+ import numpy as np
13
+ import time
14
+
15
+ print("Test: Circuit und Hamiltonian mit Variablen (SPEX vs. Qulacs)")
16
+
17
+ # --- Parameterdefinition ---
18
+ # Definiere mehrere Variablen für die Rotation
19
+ a = tq.Variable("a")
20
+ b = tq.Variable("b")
21
+ c = tq.Variable("c")
22
+ variables = {"a": np.pi/3, "b": np.pi/4, "c": np.pi/6}
23
+
24
+ # --- Circuitaufbau ---
25
+ # Erzeuge einen Circuit, der auf 3 Qubits operiert:
26
+ # - Eine Rx-Rotation auf Qubit 0 (Winkel "a")
27
+ # - Eine Ry-Rotation auf Qubit 1 (Winkel "b")
28
+ # - Eine Rz-Rotation auf Qubit 2 (Winkel "c")
29
+ # - Zusätzlich eine parametrische exponentielle Pauli-Rotation (ExpPauli) auf Qubit 0 und 2 (Pauli-String "X(0)Z(2)")
30
+ U = tq.gates.Rx(angle="a", target=(0,)) \
31
+ + tq.gates.Ry(angle="b", target=(1,)) \
32
+ + tq.gates.Rz(angle="c", target=(2,))
33
+ U += tq.gates.ExpPauli(angle="a", paulistring="X(0)Z(2)")
34
+
35
+ print("\nCircuit U:")
36
+ print(U)
37
+
38
+ # --- Hamiltonianaufbau ---
39
+ # Erstelle einen zusammengesetzten Hamiltonian auf 3 Qubits,
40
+ # z.B.: H = Z(0) + X(1)Y(2) + Z(0)Z(1)
41
+ H = tq.QubitHamiltonian("Z(0) + X(1)Y(2) + Z(0)Z(1)")
42
+ print("\nHamiltonian H:")
43
+ print(H)
44
+
45
+ # Erzeuge ein Erwartungswertobjekt
46
+ E = tq.ExpectationValue(U=U, H=H)
47
+
48
+ # --- Simulation mit SPEX ---
49
+ start = time.time()
50
+ wfn_spex = tq.simulate(U, variables, backend='spex')
51
+ exp_spex = tq.simulate(E, variables, backend='spex')
52
+ end = time.time()
53
+ time_spex = end - start
54
+
55
+ # --- Simulation mit Qulacs ---
56
+ start = time.time()
57
+ wfn_qulacs = tq.simulate(U, variables, backend='qulacs')
58
+ exp_qulacs = tq.simulate(E, variables, backend='qulacs')
59
+ end = time.time()
60
+ time_qulacs = end - start
61
+
62
+ # --- Ergebnisse ausgeben ---
63
+ print("\nSimulationsergebnisse:")
64
+ print("Wellenfunktion (SPEX backend):")
65
+ print(wfn_spex)
66
+ print("\nWellenfunktion (Qulacs backend):")
67
+ print(wfn_qulacs)
68
+
69
+ print("\nErwartungswert (SPEX backend):", exp_spex, f"(Simulationszeit: {time_spex:.3f}s)")
70
+ print("Erwartungswert (Qulacs backend):", exp_qulacs, f"(Simulationszeit: {time_qulacs:.3f}s)")
71
+
72
+ # Optional: Vergleiche das innere Produkt der beiden Wavefunctions (Quadrat des Betrags)
73
+ inner_prod = np.abs(wfn_spex.inner(wfn_qulacs))**2
74
+ print("\nInneres Produkt (Quadrat) zwischen SPEX und Qulacs:", inner_prod)
75
+
76
+
77
+
78
+ """
79
+ print("\nTest: 1")
80
+ U = tq.gates.ExpPauli(paulistring=PauliString({0: "X", 1: "Z"}), angle=np.pi / 2)
81
+ H = tq.QubitHamiltonian("Z(0)")
82
+ E = tq.ExpectationValue(U=U, H=H)
83
+
84
+ print(U)
85
+ print("spex-U:", tq.simulate(U, backend='spex'))
86
+ print("qulacs-U:", tq.simulate(U, backend='qulacs'))
87
+ print("spex:", tq.simulate(E, backend='spex'))
88
+ print("qulacs:", tq.simulate(E, backend='qulacs'))
89
+
90
+
91
+ print("\nTest: 2")
92
+ H = tq.QubitHamiltonian("Z(0)X(1)")
93
+
94
+ U = tq.gates.Rx(angle=np.pi / 4, target=(0,))
95
+ U += tq.gates.Ry(angle=np.pi / 3, target=(1,))
96
+ E = tq.ExpectationValue(U=U, H=H)
97
+
98
+ print(U)
99
+ print("spex-U:", tq.simulate(U, backend='spex'))
100
+ print("qulacs-U:", tq.simulate(U, backend='qulacs'))
101
+ print("spex:", tq.simulate(E, backend='spex'))
102
+ print("qulacs:", tq.simulate(E, backend='qulacs'))
103
+
104
+
105
+ print("\nTest: 3")
106
+ H = tq.QubitHamiltonian("Z(0)")
107
+
108
+ U = tq.gates.X(target=(0,))
109
+ U += tq.gates.Y(target=(1,))
110
+ E = tq.ExpectationValue(U=U, H=H)
111
+
112
+ print(U)
113
+ print("spex-U:", tq.simulate(U, backend='spex'))
114
+ print("qulacs-U:", tq.simulate(U, backend='qulacs'))
115
+ print("spex:", tq.simulate(E, backend='spex'))
116
+ print("qulacs:", tq.simulate(E, backend='qulacs'))
117
+
118
+
119
+ print("\nTest: 4")
120
+ U = (
121
+ tq.gates.Rx(angle=np.pi / 2, target=(0,)) +
122
+ tq.gates.Rz(angle=np.pi / 2, target=(1,)) +
123
+ tq.gates.Ry(angle=np.pi / 3, target=(2,)) +
124
+ tq.gates.ExpPauli(paulistring=PauliString({0: "X", 1: "X", 2: "X"}), angle=np.pi / 4)
125
+ )
126
+
127
+ H = tq.QubitHamiltonian("X(0)X(1)X(2) + Z(0) + Z(1) + Z(2)")
128
+ E = tq.ExpectationValue(U=U, H=H)
129
+
130
+ print(U)
131
+ print("spex-U:", tq.simulate(U, backend='spex'))
132
+ print("qulacs-U:", tq.simulate(U, backend='qulacs'))
133
+ print("spex:", tq.simulate(E, backend='spex'))
134
+ print("qulacs:", tq.simulate(E, backend='qulacs'))
135
+
136
+ print("\nTest: 5")
137
+ os.environ["OMP_NUM_THREADS"] = "6"
138
+
139
+ results = []
140
+
141
+ for n in range(1, 15):
142
+ # 1) Geometrie aufbauen
143
+ geom = ""
144
+ for k in range(2*n):
145
+ geom += f"h 0.0 0.0 {1.5*k}\n"
146
+
147
+ # 2) Molekül + Hamiltonian
148
+ mol = tq.Molecule(geometry=geom, basis_set="sto-3g")
149
+ H = mol.make_hardcore_boson_hamiltonian()
150
+
151
+ # 3) Ansatz U
152
+ edges = [(2*i, 2*i+1) for i in range(n)]
153
+ U = mol.make_ansatz(name="HCB-SPA", edges=edges)
154
+ # Alle im Ansatz auftretenden Variablen auf 1.0 setzen
155
+ U = U.map_variables({var: 1.0 for var in U.extract_variables()})
156
+
157
+ # 4) Erwartungswert-Objekt
158
+ E_obj = tq.ExpectationValue(H=H, U=U)
159
+
160
+ # -- SPEX-Berechnung --
161
+ start_spex = time.time()
162
+ E_spex = tq.simulate(E_obj, backend='spex', num_threads=6)
163
+ end_spex = time.time()
164
+ time_spex = end_spex - start_spex
165
+
166
+ # -- Qulacs-Berechnung --
167
+ if n <= 10:
168
+ start_qulacs = time.time()
169
+ E_qulacs = tq.simulate(E_obj, backend='qulacs')
170
+ end_qulacs = time.time()
171
+ time_qulacs = end_qulacs - start_qulacs
172
+
173
+ total_measurements = E_obj.count_measurements()
174
+
175
+ # Speichern der Daten
176
+ results.append({
177
+ 'n': n,
178
+ 'total_measurements' : total_measurements,
179
+ 'E_spex': E_spex,
180
+ 'time_spex': time_spex,
181
+ 'E_qulacs': E_qulacs,
182
+ 'time_qulacs': time_qulacs
183
+ })
184
+
185
+ if E_qulacs is not None:
186
+ print(f"n={n:2d} | total_measurements={total_measurements} | "
187
+ f"E_spex={E_spex:.6f} (dt={time_spex:.2f}s) | "
188
+ f"E_qulacs={E_qulacs:.6f} (dt={time_qulacs:.2f}s)")
189
+ else:
190
+ print(f"n={n:2d} | total_measurements={total_measurements} | "
191
+ f"E_spex={E_spex:.6f} (dt={time_spex:.2f}s) | "
192
+ f"E_qulacs=--- (dt=---) (für n>13 nicht berechnet)")
193
+
194
+ with open("spex_qulacs_comparison.csv", mode="w", newline="") as f:
195
+ writer = csv.writer(f)
196
+ # Kopfzeile
197
+ writer.writerow(["n", "total_measurements", "E_spex", "time_spex", "E_qulacs", "time_qulacs"])
198
+ # Datenzeilen
199
+ for entry in results:
200
+ writer.writerow([
201
+ entry["n"],
202
+ entry["total_measurements"],
203
+ entry["E_spex"],
204
+ entry["time_spex"],
205
+ entry["E_qulacs"] if entry["E_qulacs"] is not None else "NA",
206
+ entry["time_qulacs"] if entry["time_qulacs"] is not None else "NA"
207
+ ])
208
+
209
+ E_qulacs = None
210
+
211
+ """
@@ -69,6 +69,12 @@ class BitString:
69
69
  self.update_nbits()
70
70
  return self
71
71
 
72
+ def to_integer(self, numbering: BitNumbering):
73
+ if numbering == self.numbering:
74
+ return self.integer
75
+ else:
76
+ return reverse_int_bits(self.integer, self.nbits)
77
+
72
78
  @property
73
79
  def array(self):
74
80
  return [int(i) for i in self.binary]
tequila/version.py CHANGED
@@ -1,2 +1,2 @@
1
- __version__ = "1.9.8"
1
+ __version__ = "1.9.9"
2
2
  __author__ = "Tequila Developers "
@@ -365,7 +365,7 @@ class QubitWaveFunction:
365
365
  # because the __mul__ implementation of the number tries to perform some sort of array
366
366
  # operation.
367
367
  def length(self):
368
- return sum(1 for _ in self.raw_items())
368
+ return sum(1 for (k, v) in self.raw_items() if abs(v) > 1e-6)
369
369
 
370
370
  def __repr__(self):
371
371
  result = str()
@@ -373,7 +373,10 @@ class QubitWaveFunction:
373
373
  index = index.integer
374
374
  if self.numbering == BitNumbering.LSB:
375
375
  index = reverse_int_bits(index, self._n_qubits)
376
- result += f"{coeff} |{index:0{self._n_qubits}b}> "
376
+ if np.isclose(coeff.imag, 0.0):
377
+ result += f"{coeff.real:+2.4f} |{index:0{self._n_qubits}b}> "
378
+ else:
379
+ result += f"({coeff.real:+2.4f} + {coeff.imag:+2.4f}i) |{index:0{self._n_qubits}b}> "
377
380
  # If the wavefunction contains no states
378
381
  if not result:
379
382
  result = "empty wavefunction"
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: tequila-basic
3
- Version: 1.9.8
3
+ Version: 1.9.9
4
4
  Summary: A High-Level Abstraction Framework for Quantum Algorithms
5
5
  Home-page: https://github.com/tequilahub/tequila
6
6
  Author: Tequila Developers
@@ -15,6 +15,15 @@ Requires-Dist: setuptools
15
15
  Requires-Dist: pytest
16
16
  Requires-Dist: openfermion~=1.0
17
17
  Requires-Dist: dataclasses; python_version < "3.7"
18
+ Dynamic: author
19
+ Dynamic: author-email
20
+ Dynamic: description
21
+ Dynamic: description-content-type
22
+ Dynamic: home-page
23
+ Dynamic: license-file
24
+ Dynamic: provides-extra
25
+ Dynamic: requires-dist
26
+ Dynamic: summary
18
27
 
19
28
  [![License: MIT](https://img.shields.io/badge/License-MIT-lightgrey.svg)](LICENCE) [![DOI](https://zenodo.org/badge/259718912.svg)](https://zenodo.org/badge/latestdoi/259718912) [![PyPI version](https://badge.fury.io/py/tequila-basic.svg)](https://badge.fury.io/py/tequila-basic) ![CI](https://github.com/tequilahub/tequila/actions/workflows/ci_basic.yml/badge.svg)
20
29
 
@@ -1,6 +1,6 @@
1
1
  tequila/__init__.py,sha256=FV8-j7GEw_VYadsZUp3M2mGRQxVUYYG3W1jiI6in3CY,1959
2
2
  tequila/autograd_imports.py,sha256=t7V5uYaI0GzjD7pSjkYtiaj3BzSvkm_RL2KcYfNwNhM,1529
3
- tequila/version.py,sha256=sZDtaWTr_VlllD_RWU9Qeudj0ybXPEgH4_O65N0Myp8,57
3
+ tequila/version.py,sha256=YND9gLv_BQrt_NeUjOzGd6JYX47UlW1-UsIm5a71jLs,57
4
4
  tequila/apps/__init__.py,sha256=GJb04napv8AAx5EHxS5C1CMv9kxQeu7aA-ZMWk6X_eQ,1623
5
5
  tequila/apps/_unary_state_prep_impl.py,sha256=SzRtI0Nx29ODygvYYdC1NnSTCL70wY7NTAvqhiwpMDs,21757
6
6
  tequila/apps/unary_state_prep.py,sha256=X6NTvni5BYzSNv1w-etohh1WwLAZpC06Y3TrSlGKc8U,9367
@@ -13,7 +13,7 @@ tequila/apps/robustness/helpers.py,sha256=t8VgwMfgrhVaa0sNYaxfCIHsEQXL4WAEaT-UJG
13
13
  tequila/apps/robustness/interval.py,sha256=m3D1wBRTLRpGQdnabzcDrlxWqN0uCf80NG-enbZrcoY,24735
14
14
  tequila/circuit/__init__.py,sha256=ZgoUEjU_NggxjKYkJjh5aO-6VdUjnO5CLxKB44rHzQc,301
15
15
  tequila/circuit/_gates_impl.py,sha256=iB40Sg9l9UoCCayu0l4vGjgvEHUVVbXMaGdUtXIHa1w,17821
16
- tequila/circuit/circuit.py,sha256=5XqOddRAQMgjO_zyRDHQvtGyWt8gBW0seorejsV9KK4,31874
16
+ tequila/circuit/circuit.py,sha256=HASLPspbg6bxqQffm6dkJEUAKgKM9m5HCuwlB03ETnI,32007
17
17
  tequila/circuit/compiler.py,sha256=fsHnnNZo43VWUl_n34P21b4GAi7k2kwoYdx0xlbAuZY,32685
18
18
  tequila/circuit/gates.py,sha256=buNzNZaflRzVFe1oihCprUBZVL5KcZXyd_kqiWkNbTo,35923
19
19
  tequila/circuit/gradient.py,sha256=Y4dNL6nkZUEkKJvaA3hxaSEa8_b_3XZwxy3j8tGsOmA,10465
@@ -39,7 +39,7 @@ tequila/ml/ml_api.py,sha256=T-HEojuiiO3TzeHN_mMX5GheywK2P4pJH7C_9D7HDaM,846
39
39
  tequila/ml/utils_ml.py,sha256=uTif6aiNtyJZWTql0uXbxkkKzutAX2w7vbpA7xb9CIw,5533
40
40
  tequila/objective/__init__.py,sha256=_BS3tVabs0zdon46nKpKmk3gI40z6KaWsjynIBMnPuc,231
41
41
  tequila/objective/braket.py,sha256=10jEj7F3VWAj-fVrD4nhptrgRfrGvij84mVYcJWq4ZU,6019
42
- tequila/objective/objective.py,sha256=8MzzTousgJf7w7rEdqGr8MU0PRF7S4kwh3BgsMMjnEg,31763
42
+ tequila/objective/objective.py,sha256=SunUH9KdPGV2pWQ2lXKrTJBbDIdNKYsqAknZABviVGg,31930
43
43
  tequila/objective/qtensor.py,sha256=eSjFOEVW-kLoM8jn1RD2_E_kHVAm579Ji3_lMcJg7cE,7957
44
44
  tequila/optimizers/__init__.py,sha256=1AhFyARYIfdyFGwnjOX0RDDa_kGrhiLJyjEbEm4NgZI,5086
45
45
  tequila/optimizers/_containers.py,sha256=CXbWWQory_aADxlaMw4deIr03K_el-0E12GmpA0vX10,7161
@@ -48,39 +48,41 @@ tequila/optimizers/optimizer_gd.py,sha256=QF84K2XDn_-0w9htDlEpIaaGGtfudNamIvVCgx
48
48
  tequila/optimizers/optimizer_gpyopt.py,sha256=-aVuutZUScyo6_4q4PyvMMa_OVd3L8ev9Ge2fXyWGV8,11280
49
49
  tequila/optimizers/optimizer_scipy.py,sha256=zqRVQ-Whr74KCGP7Zg1jQkl9S3j9s1kS4oCrCtX30gY,18949
50
50
  tequila/quantumchemistry/__init__.py,sha256=KP99PNJYVwBcfQGwL-MB9IBLSBJNRPf-5WN9NLqT-Rg,7424
51
- tequila/quantumchemistry/chemistry_tools.py,sha256=thHsovI-pMJJbEFBuYUAXg2ptitnGe9CeD0mdsh3iV8,46989
51
+ tequila/quantumchemistry/chemistry_tools.py,sha256=PwqDciSHUNeu5J3kxo4koThGIMe2UxBcKE1Ymk912ME,47252
52
52
  tequila/quantumchemistry/encodings.py,sha256=GoQrEdzPO0EfvEtVu7_p18TG9VfcMYaqSnyjCmz49hE,11571
53
53
  tequila/quantumchemistry/madness_interface.py,sha256=ivwDoaUDyAB6lJoFri0pZpvOZbihGavIuybcc40xTD0,36372
54
- tequila/quantumchemistry/orbital_optimizer.py,sha256=plaMs77qbVOtrAVDVCwIqQdMOs3dGzC6-fIjm-LugUo,12670
54
+ tequila/quantumchemistry/orbital_optimizer.py,sha256=nxxQ1ILDnJy1EN0PAzltTcQjYYxCdpNXsb5mMFMx-PE,12732
55
55
  tequila/quantumchemistry/psi4_interface.py,sha256=syNaDvlSmCsyB4f7idn3VGbMKyKo83vJHD5y5LpHwaM,29953
56
56
  tequila/quantumchemistry/pyscf_interface.py,sha256=XgecUnKKg2rGiKqsYCJnDQ89ekkDkKuwc3qHULLMYck,6152
57
57
  tequila/quantumchemistry/qc_base.py,sha256=IqPQ-FylaOsKtiLur8QWnvMqFBa_X_i3rdjMHSgQ9fA,107121
58
58
  tequila/simulators/__init__.py,sha256=VFw4sJIt4Zc0-__eYnksN8Ku9qMhbPpHJEkXMWUiD30,4
59
- tequila/simulators/simulator_api.py,sha256=qz5dXCv-C4JXz09WK6eF1jBSMRuIqFIMAfPvNoapOrs,25394
60
- tequila/simulators/simulator_base.py,sha256=j0v00qA4WFE8agOmcoKGCDH85s40UJAX6gbjYrYamI8,34838
59
+ tequila/simulators/simulator_api.py,sha256=VCKdz4kxB5QX1c4ncHuB9y5SJxwMkkK25SVon3AuHJs,25672
60
+ tequila/simulators/simulator_base.py,sha256=gEbX7MgA09qGYXVA_j1p7xJ_izuiFvEKNB13PEpdMYI,35571
61
61
  tequila/simulators/simulator_cirq.py,sha256=j2noXfhqcyPJoramWik_PLwKxNNq_rB794PkIZt8ktA,16552
62
62
  tequila/simulators/simulator_pyquil.py,sha256=1n6bQqoDUiXulBJX_9t8knl439aKpIeepRBwcyU72QU,24264
63
63
  tequila/simulators/simulator_qibo.py,sha256=8A2YZBmAi23tErfR28eqdWWuwulgblFGZ9Hbrwy7Ht8,25220
64
- tequila/simulators/simulator_qiskit.py,sha256=8go0PehReRtmdk-onJQ7hKhbM7bKLwhr7YCaSTow2Go,25046
64
+ tequila/simulators/simulator_qiskit.py,sha256=28l18iL3VI3kT0kUSqcizwJjnImyUf1VyERLVCGpWnk,25146
65
65
  tequila/simulators/simulator_qiskit_gpu.py,sha256=EXlHtpl5w1bqlYOR1aPIZXFO_Jei5JJTWTESnRli9cc,339
66
66
  tequila/simulators/simulator_qlm.py,sha256=OQPZEdbM0UkfzoTx2pa0SM14cN7r6PsGS1cDlfJqTl8,15431
67
- tequila/simulators/simulator_qulacs.py,sha256=L7pWxSmbUkFEbTffikvnzXLhqjhFbjIy6O0YhZXlWXc,19829
67
+ tequila/simulators/simulator_qulacs.py,sha256=9Q1LlmUIcYcw9KGBfQ5j09sAJSinof6U7hCqWWLDkOk,20195
68
68
  tequila/simulators/simulator_qulacs_gpu.py,sha256=3yVIACmQYQp_6ntvjvaGiYoHXbc1mKtb_Oa6Mo-2bSc,557
69
+ tequila/simulators/simulator_spex.py,sha256=DWmHDTnQdIExRwQnt-xOD62wNOuFucXC_tk9UtC22wU,16980
69
70
  tequila/simulators/simulator_symbolic.py,sha256=nyS1lwk_dxcsTWKQ2YL4kvdl-kTdC0NoB-EmH6seP-M,5074
71
+ tequila/simulators/test_spex_simulator.py,sha256=Y9jWLxDDEPeTrnCZr017liqA0kJvLANk6wcuqkuXPB8,6480
70
72
  tequila/tools/__init__.py,sha256=mJChgkme6v6Ohj2s3tJ96Mq4U0kmVmc6d8Y6_1pEZMg,72
71
73
  tequila/tools/convenience.py,sha256=9WYQJvx5i2QwQG8c7lssYTUv5hzW8h16JTr1GHxYf18,1291
72
74
  tequila/tools/qng.py,sha256=p19A1rcKo06DAyBVUCQzNhwIGni6eAT6IGPnEPhKa4Y,21326
73
75
  tequila/tools/random_generators.py,sha256=UwkDMmNxxSPgaH1LNBswZUiHnLDd8arI3F5QshfBtBY,3417
74
76
  tequila/utils/__init__.py,sha256=nlulDMHcq-XyBZf3NPx2bn_tcixi2mvhRc6MhU9TBWI,317
75
- tequila/utils/bitstrings.py,sha256=81U4Mg6voNzWfQVrrhqaqcOsQnPPkFI2UWQDSAlsMdo,5941
77
+ tequila/utils/bitstrings.py,sha256=e3kptMWj6Nv6h4QsX41H-b_S-t8OBHjqnxmRXOLhzFQ,6141
76
78
  tequila/utils/exceptions.py,sha256=QK7AcDO2-7Itr8Aw6rLP_WtSkH6rfT8PHWcvNM6sQnc,1188
77
79
  tequila/utils/joined_transformation.py,sha256=R2-aMTi6h7ZnjYUnX6cvFuPEQwgf49W0w33q6018qdc,2013
78
80
  tequila/utils/keymap.py,sha256=RgQzeHEfRVee0-uoH-QsLYsGsXyMhEp3n33KCH-EV2k,3735
79
81
  tequila/utils/misc.py,sha256=e62ASkFReaLJQXnBXzyYukzXZnXNoURsM1luoMeIXiE,919
80
82
  tequila/wavefunction/__init__.py,sha256=q4DVL0lGFg03PogRMYA6S8MQqqmLYQiU9VNOF-YQxfQ,50
81
- tequila/wavefunction/qubit_wavefunction.py,sha256=2S3vUMwbuUOuFrZ_-ST6s7CU9nSjKTql7rCoc39UPQ8,17529
82
- tequila_basic-1.9.8.dist-info/LICENSE,sha256=oG1FtUav5_xrym9ByiG5emJDQRcbnAfTB08fRV9TCiE,1114
83
- tequila_basic-1.9.8.dist-info/METADATA,sha256=RRoQh2vohcUKQGx-Iysn7qOHZHqY5WhHma2eLM5uuDQ,19170
84
- tequila_basic-1.9.8.dist-info/WHEEL,sha256=R06PA3UVYHThwHvxuRWMqaGcr-PuniXahwjmQRFMEkY,91
85
- tequila_basic-1.9.8.dist-info/top_level.txt,sha256=VBH0gl6mDMbcLHKlO0yEAqtcq08DqBHz4gRJ9jafl5w,8
86
- tequila_basic-1.9.8.dist-info/RECORD,,
83
+ tequila/wavefunction/qubit_wavefunction.py,sha256=6-Cgg8I6zIi0tfYMrI_jqds1_2vRO5alRSA13tl944Y,17730
84
+ tequila_basic-1.9.9.dist-info/licenses/LICENSE,sha256=oG1FtUav5_xrym9ByiG5emJDQRcbnAfTB08fRV9TCiE,1114
85
+ tequila_basic-1.9.9.dist-info/METADATA,sha256=Pf7NfCw-bJlFp9Z5LQeA0sOOhyxHaUJh0iUMIRHMkRc,19368
86
+ tequila_basic-1.9.9.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
87
+ tequila_basic-1.9.9.dist-info/top_level.txt,sha256=VBH0gl6mDMbcLHKlO0yEAqtcq08DqBHz4gRJ9jafl5w,8
88
+ tequila_basic-1.9.9.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (75.5.0)
2
+ Generator: setuptools (78.1.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5