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
@@ -2,37 +2,41 @@ import numpy as np
2
2
  from tequila.grouping.binary_utils import sorted_insertion_grouping, term_commutes_with_group
3
3
  from copy import deepcopy
4
4
 
5
+
5
6
  class OverlappingAuxiliary:
6
- '''
7
+ """
7
8
  Class required for passing cov_dict and number of iterations to
8
9
  OverlappingGroups. Eventually, this may also be used for building cov_dict from
9
10
  approximate wavefunction if the user provides only the latter.
10
- '''
11
+ """
12
+
11
13
  def __init__(self, cov_dict, n_iter=5):
12
14
  self.cov_dict = cov_dict
13
15
  self.n_iter = n_iter
14
16
 
17
+
15
18
  class OverlappingGroupsWoFixed:
16
- '''
17
- In the overlapping grouping methods, the same Pauli string term can be grouped into
18
- multiple different fragments. However, for this procedure to reduce the total number of
19
- required measurements, the coefficients in different fragments have to be optimized with
19
+ """
20
+ In the overlapping grouping methods, the same Pauli string term can be grouped into
21
+ multiple different fragments. However, for this procedure to reduce the total number of
22
+ required measurements, the coefficients in different fragments have to be optimized with
20
23
  the constraint that the sum of the fragments equal the original operator.
21
24
  This Class is required for this optimization process. The overlapping grouping methods
22
25
  starts from some nonoverlapping groups N_alpha; Pauli strings are then added to several
23
- compatible fragments with coefficients c_alpha. Because the nonoverlapping groups sum to
26
+ compatible fragments with coefficients c_alpha. Because the nonoverlapping groups sum to
24
27
  the original operator, the sum of coefficients c_alpha for a given Pauli string has to be
25
28
  zero. This is ensured by fixing one of these c_alpha's as -(sum over the rest of c_alpha).
26
29
  See Eq. (14) of arXiv: 2201.01471 (2022).
27
- '''
30
+ """
31
+
28
32
  def __init__(self, o_groups, o_terms, term_exists_in):
29
33
  def exclude_fixed_coeffs(o_groups, o_terms, term_exists_in):
30
- '''
34
+ """
31
35
  Remove fixed coefficients from o_groups and term_exists_in.
32
36
  These fixed coefficients are determined from other Pauli coefficients in order that
33
37
  the condition sum_alpha c_alpha = 0 is satisfied [see comment for OverlappingGroupsWoFixed
34
38
  and Eq. (14) of arXiv: 2201.01471 (2022)].
35
- '''
39
+ """
36
40
  fixed_grp = []
37
41
  o_groups_wo_fixed = deepcopy(o_groups)
38
42
  term_exists_in_wo_fixed = deepcopy(term_exists_in)
@@ -45,10 +49,10 @@ class OverlappingGroupsWoFixed:
45
49
  return fixed_grp, o_groups_wo_fixed, term_exists_in_wo_fixed, n_coeff_grp, init_idx
46
50
 
47
51
  def get_term_idxs(o_terms, o_groups_wo_fixed, term_exists_in_wo_fixed, init_idx):
48
- '''
52
+ """
49
53
  Obtain a dictionary (over i) of dictionaries (over alpha) containing
50
54
  the index corresponding to a Pauli coefficient c_{i}^{alpha}.
51
- '''
55
+ """
52
56
  term_idxs = {}
53
57
  for term_idx, term in enumerate(o_terms):
54
58
  cur_idxs = {}
@@ -57,55 +61,61 @@ class OverlappingGroupsWoFixed:
57
61
  term_idxs[term.binary_tuple()] = cur_idxs
58
62
  return term_idxs
59
63
 
60
- self.fixed_grp, self.o_groups, self.term_exists_in, self.n_coeff_grp, self.init_idx \
61
- = exclude_fixed_coeffs(o_groups, o_terms, term_exists_in)
64
+ self.fixed_grp, self.o_groups, self.term_exists_in, self.n_coeff_grp, self.init_idx = exclude_fixed_coeffs(
65
+ o_groups, o_terms, term_exists_in
66
+ )
62
67
  self.term_idxs = get_term_idxs(o_terms, self.o_groups, self.term_exists_in, self.init_idx)
63
68
  return None
64
69
 
70
+
65
71
  def get_cov(term1, term2, cov_dict):
66
- '''
72
+ """
67
73
  Return the covariance between Pauli string term 1 and 2 from a dictionary
68
74
  under the assumption that Pauli strings term1 and term2 commute.
69
- '''
75
+ """
70
76
  if (term1.binary_tuple(), term2.binary_tuple()) in cov_dict:
71
77
  return cov_dict[(term1.binary_tuple(), term2.binary_tuple())]
72
78
  if (term2.binary_tuple(), term1.binary_tuple()) in cov_dict:
73
79
  return cov_dict[(term2.binary_tuple(), term1.binary_tuple())]
74
80
 
81
+
75
82
  def cov_term_w_group(term, group, cov_dict):
76
- '''
83
+ """
77
84
  Compute covariance between a Pauli string term and a group of Pauli strings.
78
- '''
85
+ """
79
86
  cov = 0.0
80
87
  for grp_term in group:
81
88
  cov += grp_term.get_coeff() * get_cov(term, grp_term, cov_dict)
82
89
  return cov
83
90
 
91
+
84
92
  def get_opt_sample_size(groups, cov_dict):
85
- '''
93
+ """
86
94
  Allocate sample_size optimally based on variance.
87
- '''
95
+ """
88
96
  weights = np.zeros(len(groups))
89
97
  for idx, group in enumerate(groups):
90
- cur_var = 0.
98
+ cur_var = 0.0
91
99
  for term1 in group:
92
100
  for term2 in group:
93
101
  cur_var += term1.coeff * term2.coeff * get_cov(term1, term2, cov_dict)
94
102
  weights[idx] = np.sqrt(np.real(cur_var))
95
103
  return weights / np.sum(weights)
96
104
 
105
+
97
106
  class OverlappingGroups:
98
- '''
107
+ """
99
108
  Class required for performing overlapping grouping techniques:
100
- coefficient splitting (implemented), ghost paulis (todo), and iterative
101
- measurement allocation (todo). Unlike nonoverlapping grouping methods, in the overlapping methods,
109
+ coefficient splitting (implemented), ghost paulis (todo), and iterative
110
+ measurement allocation (todo). Unlike nonoverlapping grouping methods, in the overlapping methods,
102
111
  the same Pauli string term can appear in multiple compatible groups. The optimization of the associated
103
- Pauli string coefficients is performed using the estimated covariances from cov_dict.
104
- '''
112
+ Pauli string coefficients is performed using the estimated covariances from cov_dict.
113
+ """
114
+
105
115
  def __init__(self, no_groups, o_terms, term_exists_in):
106
- self.no_groups = no_groups #Non-overlapping groups: required as a starting point.
107
- self.o_terms = o_terms #List of Pauli products that are compatible in multiple groups.
108
- self.term_exists_in = term_exists_in #List of indices indicating where overlapping Pauli products appear.
116
+ self.no_groups = no_groups # Non-overlapping groups: required as a starting point.
117
+ self.o_terms = o_terms # List of Pauli products that are compatible in multiple groups.
118
+ self.term_exists_in = term_exists_in # List of indices indicating where overlapping Pauli products appear.
109
119
  self.o_groups = [[] for i in range(len(no_groups))]
110
120
  for idx, term in enumerate(o_terms):
111
121
  for grup_idx in term_exists_in[idx]:
@@ -114,20 +124,22 @@ class OverlappingGroups:
114
124
  return None
115
125
 
116
126
  @classmethod
117
- def init_from_binary_terms(cls, terms, condition='fc'):
118
- '''
127
+ def init_from_binary_terms(cls, terms, condition="fc"):
128
+ """
119
129
  Obtain a list of Pauli operators that are compatible in more than one group
120
130
  by using the overlapping version of the sorted insertion algorithm.
121
- '''
131
+ """
122
132
  nonoverlapping_groups = sorted_insertion_grouping(terms, condition=condition)
123
133
  newly_added = [[] for i in range(len(nonoverlapping_groups))]
124
134
  sorted_terms = sorted(terms, key=lambda x: np.abs(x.coeff), reverse=True)
125
135
  overlapping_terms = []
126
- term_exists_in = [] #List of group indices where overlapping terms are present.
136
+ term_exists_in = [] # List of group indices where overlapping terms are present.
127
137
  for term in sorted_terms:
128
138
  grup_idxs = []
129
139
  for idx, group in enumerate(nonoverlapping_groups):
130
- commute = term_commutes_with_group(term, group, condition) and term_commutes_with_group(term, newly_added[idx], condition)
140
+ commute = term_commutes_with_group(term, group, condition) and term_commutes_with_group(
141
+ term, newly_added[idx], condition
142
+ )
131
143
  if commute:
132
144
  grup_idxs.append(idx)
133
145
  newly_added[idx].append(term)
@@ -138,31 +150,34 @@ class OverlappingGroups:
138
150
  return group
139
151
 
140
152
  def optimize_pauli_coefficients(self, cov_dict, sample_size):
141
- '''
153
+ """
142
154
  Build the equation matrix @ x = b and solve it in order to obtain the
143
- optimal overlapping coefficients for the measurable fragments
155
+ optimal overlapping coefficients for the measurable fragments
144
156
  [see Eq. (14) on arXiv:2201.01471].
145
- '''
157
+ """
158
+
146
159
  def prep_mat_single_row(term, group_index):
147
- '''
148
- Build the matrix row corresponding to Pauli term.
149
- '''
160
+ """
161
+ Build the matrix row corresponding to Pauli term.
162
+ """
150
163
  mat_single = np.zeros(np.sum(self.wo_fixed.n_coeff_grp))
151
164
  for term2_idx, term2 in enumerate(self.o_groups[group_index]):
152
165
  term2_idx_dict = self.wo_fixed.term_idxs[term2.binary_tuple()]
153
166
  cov = np.real_if_close(get_cov(term, term2, cov_dict))
154
167
  if term2 in self.wo_fixed.o_groups[group_index]:
155
- mat_single[term2_idx_dict[group_index]] -= cov/sample_size[group_index]
168
+ mat_single[term2_idx_dict[group_index]] -= cov / sample_size[group_index]
156
169
  else:
157
170
  for idx in term2_idx_dict.values():
158
- mat_single[idx] += cov/sample_size[group_index]
171
+ mat_single[idx] += cov / sample_size[group_index]
159
172
  return mat_single
160
173
 
161
174
  def prep_b_single_row(term, group_index):
162
- '''
163
- Build b row corresponding to Pauli term.
164
- '''
165
- return np.real_if_close(cov_term_w_group(term, self.no_groups[group_index], cov_dict) / sample_size[group_index])
175
+ """
176
+ Build b row corresponding to Pauli term.
177
+ """
178
+ return np.real_if_close(
179
+ cov_term_w_group(term, self.no_groups[group_index], cov_dict) / sample_size[group_index]
180
+ )
166
181
 
167
182
  mat_size = np.sum(self.wo_fixed.n_coeff_grp)
168
183
  matrix = np.zeros((mat_size, mat_size))
@@ -181,17 +196,19 @@ class OverlappingGroups:
181
196
  return sol.T[0]
182
197
 
183
198
  def overlapping_groups_from_coeff(self, coeff):
184
- '''
199
+ """
185
200
  Find fixed coefficients from the optimal coefficients and
186
201
  return the optimal overlapping groups to be measured.
187
- '''
202
+ """
203
+
188
204
  def add_coeff_times_term(coeff, term, group_index):
189
205
  added = False
190
206
  for igrp, group_term in enumerate(final_overlapping_groups[group_index]):
191
207
  if group_term.binary_tuple() == term.binary_tuple():
192
- final_overlapping_groups[group_index][igrp].set_coeff( coeff + group_term.get_coeff() )
208
+ final_overlapping_groups[group_index][igrp].set_coeff(coeff + group_term.get_coeff())
193
209
  added = True
194
- if not(added): final_overlapping_groups[group_index].append( term.term_w_coeff(coeff) )
210
+ if not (added):
211
+ final_overlapping_groups[group_index].append(term.term_w_coeff(coeff))
195
212
  return None
196
213
 
197
214
  final_overlapping_groups = deepcopy(self.no_groups)
@@ -207,9 +224,9 @@ class OverlappingGroups:
207
224
  return final_overlapping_groups
208
225
 
209
226
  def optimal_overlapping_groups(self, overlap_aux):
210
- '''
227
+ """
211
228
  Find a set of overlapping groups with optimized coefficients.
212
- '''
229
+ """
213
230
  cur_groups = self.no_groups
214
231
  for i in range(overlap_aux.n_iter):
215
232
  cur_sample_size = get_opt_sample_size(cur_groups, overlap_aux.cov_dict)
@@ -3,6 +3,7 @@ Convenience initialization
3
3
  of Pauli Operators. Resulting structures can be added and multiplied together.
4
4
  Currently uses OpenFermion as backend (QubitOperators)
5
5
  """
6
+
6
7
  import typing
7
8
  from tequila.hamiltonian import QubitHamiltonian
8
9
  from tequila import BitString, TequilaException
@@ -10,9 +11,11 @@ from tequila.wavefunction.qubit_wavefunction import QubitWaveFunction
10
11
  from tequila.tools import list_assignment
11
12
  import numpy
12
13
 
14
+
13
15
  def from_string(string, openfermion_format=False):
14
16
  return QubitHamiltonian.from_string(string=string, openfermion_format=openfermion_format)
15
17
 
18
+
16
19
  def pauli(qubit, type) -> QubitHamiltonian:
17
20
  """
18
21
  Parameters
@@ -198,7 +201,7 @@ def Sp(qubit) -> QubitHamiltonian:
198
201
  qubit = list_assignment(qubit)
199
202
  result = I()
200
203
  for q in qubit:
201
- result *= 0.5 * (X(qubit=q) + 1.j * Y(qubit=q))
204
+ result *= 0.5 * (X(qubit=q) + 1.0j * Y(qubit=q))
202
205
  return result
203
206
 
204
207
 
@@ -224,7 +227,7 @@ def Sm(qubit) -> QubitHamiltonian:
224
227
  qubit = list_assignment(qubit)
225
228
  result = I()
226
229
  for q in qubit:
227
- result *= 0.5 * (X(qubit=q) - 1.j * Y(qubit=q))
230
+ result *= 0.5 * (X(qubit=q) - 1.0j * Y(qubit=q))
228
231
  return result
229
232
 
230
233
 
@@ -264,12 +267,13 @@ def Projector(wfn, threshold=0.0, n_qubits=None) -> QubitHamiltonian:
264
267
  c = v1.conjugate() * v2
265
268
  if not numpy.isclose(c, 0.0, atol=threshold):
266
269
  H += c * decompose_transfer_operator(bra=k1, ket=k2)
267
- assert (H.is_hermitian())
270
+ assert H.is_hermitian()
268
271
  return H
269
272
 
270
273
 
271
- def KetBra(ket: QubitWaveFunction, bra: QubitWaveFunction, hermitian: bool = False, threshold: float = 1.e-6,
272
- n_qubits=None):
274
+ def KetBra(
275
+ ket: QubitWaveFunction, bra: QubitWaveFunction, hermitian: bool = False, threshold: float = 1.0e-6, n_qubits=None
276
+ ):
273
277
  """
274
278
  Notes
275
279
  ----------
@@ -348,12 +352,7 @@ def decompose_transfer_operator(ket: BitString, bra: BitString, qubits: typing.L
348
352
 
349
353
  """
350
354
 
351
- opmap = {
352
- (0, 0): Qp,
353
- (0, 1): Sp,
354
- (1, 0): Sm,
355
- (1, 1): Qm
356
- }
355
+ opmap = {(0, 0): Qp, (0, 1): Sp, (1, 0): Sm, (1, 1): Qm}
357
356
 
358
357
  nbits = None
359
358
  if qubits is not None:
@@ -366,13 +365,13 @@ def decompose_transfer_operator(ket: BitString, bra: BitString, qubits: typing.L
366
365
 
367
366
  b_arr = bra.array
368
367
  k_arr = ket.array
369
- assert (len(b_arr) == len(k_arr))
368
+ assert len(b_arr) == len(k_arr)
370
369
  n_qubits = len(k_arr)
371
370
 
372
371
  if qubits is None:
373
372
  qubits = range(n_qubits)
374
373
 
375
- assert (n_qubits <= len(qubits))
374
+ assert n_qubits <= len(qubits)
376
375
 
377
376
  result = QubitHamiltonian.unit()
378
377
  for q, b in enumerate(b_arr):
@@ -1,7 +1,7 @@
1
1
  from tequila.hamiltonian import PauliString
2
2
 
3
3
  if __name__ == "__main__":
4
- testdata = {0: 'x', 1: 'y', 2: 'z'}
4
+ testdata = {0: "x", 1: "y", 2: "z"}
5
5
  test = PauliString(data=testdata, coeff=2)
6
6
  print("test=", test)
7
7
  print("test: openfermion_key = ", test.key_openfermion())
@@ -1,3 +1,9 @@
1
+ """
2
+ Explicit matrix forms for the Pauli operators for the tomatrix method
3
+ For sparse matrices use the openfermion tool
4
+ get the openfermion object with hamiltonian.hamiltonian
5
+ """
6
+
1
7
  import numbers
2
8
  import typing
3
9
 
@@ -10,20 +16,15 @@ from functools import reduce
10
16
 
11
17
  from collections import namedtuple
12
18
 
13
- BinaryPauli = namedtuple("BinaryPauli", "coeff, binary")
14
-
15
- """
16
- Explicit matrix forms for the Pauli operators for the tomatrix method
17
- For sparse matrices use the openfermion tool
18
- get the openfermion object with hamiltonian.hamiltonian
19
- """
20
19
  import numpy as np
21
20
 
21
+ BinaryPauli = namedtuple("BinaryPauli", "coeff, binary")
22
+
22
23
  pauli_matrices = {
23
- 'I': np.array([[1, 0], [0, 1]], dtype=complex),
24
- 'Z': np.array([[1, 0], [0, -1]], dtype=complex),
25
- 'X': np.array([[0, 1], [1, 0]], dtype=complex),
26
- 'Y': np.array([[0, -1j], [1j, 0]], dtype=complex)
24
+ "I": np.array([[1, 0], [0, 1]], dtype=complex),
25
+ "Z": np.array([[1, 0], [0, -1]], dtype=complex),
26
+ "X": np.array([[0, 1], [1, 0]], dtype=complex),
27
+ "Y": np.array([[0, -1j], [1j, 0]], dtype=complex),
27
28
  }
28
29
 
29
30
 
@@ -98,12 +99,19 @@ class PauliString:
98
99
  # <psi|op|psi> = |a|**2<0|op|0> + (a*)*b<1|op|0> + (b*)*a<0|op|1> + |b|**2<1|op|1>
99
100
 
100
101
  if states is None:
101
- states = [(1.0, 0.0)]*len(qubits)
102
+ states = [(1.0, 0.0)] * len(qubits)
102
103
 
103
104
  def make_coeff_vec(state_tuple):
104
- return np.asarray([np.abs(state_tuple[0])**2, state_tuple[0].conjugate()*state_tuple[1], state_tuple[1].conjugate()*state_tuple[0] ,np.abs(state_tuple[1])**2])
105
-
106
- factor=1.0
105
+ return np.asarray(
106
+ [
107
+ np.abs(state_tuple[0]) ** 2,
108
+ state_tuple[0].conjugate() * state_tuple[1],
109
+ state_tuple[1].conjugate() * state_tuple[0],
110
+ np.abs(state_tuple[1]) ** 2,
111
+ ]
112
+ )
113
+
114
+ factor = 1.0
107
115
  for q, state in zip(qubits, states):
108
116
  if q in self.keys():
109
117
  matrix = pauli_matrices[self[q].upper()].reshape([4])
@@ -112,9 +120,8 @@ class PauliString:
112
120
  if factor == 0.0:
113
121
  break
114
122
 
115
- new_data = {k:v for k,v in self.items() if k not in qubits}
116
- return PauliString(data=new_data, coeff=self.coeff*factor)
117
-
123
+ new_data = {k: v for k, v in self.items() if k not in qubits}
124
+ return PauliString(data=new_data, coeff=self.coeff * factor)
118
125
 
119
126
  def map_qubits(self, qubit_map: dict):
120
127
  """
@@ -153,13 +160,13 @@ class PauliString:
153
160
  """
154
161
  data = dict()
155
162
  string = string.strip()
156
- for part in string.split(')'):
163
+ for part in string.split(")"):
157
164
  part = part.strip()
158
165
  if part == "":
159
166
  break
160
- pauli_dim = part.split('(')
167
+ pauli_dim = part.split("(")
161
168
  string = pauli_dim[0].upper()
162
- if not string in ['X', 'Y', 'Z']:
169
+ if string not in ["X", "Y", "Z"]:
163
170
  raise TequilaException("PauliString.from_string initialization failed, unknown pauliterm: " + string)
164
171
  data[int(pauli_dim[1])] = string
165
172
 
@@ -223,8 +230,11 @@ class PauliString:
223
230
 
224
231
  if n_qubits < maxq:
225
232
  raise TequilaException(
226
- "PauliString acts on qubit number larger than n_qubits given\n PauliString=" + self.__repr__() + ", n_qubits=" + str(
227
- n_qubits))
233
+ "PauliString acts on qubit number larger than n_qubits given\n PauliString="
234
+ + self.__repr__()
235
+ + ", n_qubits="
236
+ + str(n_qubits)
237
+ )
228
238
 
229
239
  binary = np.zeros(2 * n_qubits)
230
240
  for k, v in self._data.items():
@@ -310,9 +320,9 @@ class QubitHamiltonian:
310
320
  else:
311
321
  self._qubit_operator = qubit_operator
312
322
 
313
- assert (isinstance(self._qubit_operator, QubitOperator))
323
+ assert isinstance(self._qubit_operator, QubitOperator)
314
324
 
315
- def trace_out_qubits(self, qubits, states: list=None, *args, **kwargs):
325
+ def trace_out_qubits(self, qubits, states: list = None, *args, **kwargs):
316
326
  """
317
327
  Tracing out qubits with the assumption that they are in the |0> (default) or |1> state
318
328
 
@@ -328,7 +338,7 @@ class QubitHamiltonian:
328
338
  """
329
339
 
330
340
  if states is None:
331
- states = [(1.0,0.0)]*len(qubits)
341
+ states = [(1.0, 0.0)] * len(qubits)
332
342
  else:
333
343
  assert len(states) == len(qubits)
334
344
  # states should be given as list of individual tq.QubitWaveFunctions
@@ -400,14 +410,14 @@ class QubitHamiltonian:
400
410
  string = string.replace("Y", " Y")
401
411
  string = string.replace("Z", " Z")
402
412
  string += " "
403
- terms = string.split('+')
413
+ terms = string.split("+")
404
414
  for term in terms:
405
415
  if term.strip() == "":
406
416
  continue
407
417
 
408
418
  coeff = term.split(" ")[0]
409
419
  if coeff.strip() == "" or coeff[0] in ["X", "Y", "Z"]:
410
- coeff = '1.0'
420
+ coeff = "1.0"
411
421
  ps = term
412
422
  else:
413
423
  ps = term.replace(coeff, " ").replace(" ", "")
@@ -418,7 +428,7 @@ class QubitHamiltonian:
418
428
  if "@" in coeff:
419
429
  coeff = coeff.replace("@", "e-")
420
430
  coeff = complex(coeff)
421
- except Exception as E:
431
+ except Exception:
422
432
  raise Exception("failed to convert coefficient : {}".format(coeff))
423
433
 
424
434
  if coeff.imag == 0.0:
@@ -490,7 +500,7 @@ class QubitHamiltonian:
490
500
  return self.__neg__().__add__(other=other)
491
501
 
492
502
  def __pow__(self, power):
493
- return QubitHamiltonian(qubit_operator=self.qubit_operator ** power)
503
+ return QubitHamiltonian(qubit_operator=self.qubit_operator**power)
494
504
 
495
505
  def __neg__(self):
496
506
  return self.__mul__(other=-1.0)
@@ -524,7 +534,7 @@ class QubitHamiltonian:
524
534
  anti_hermitian = QubitHamiltonian.zero()
525
535
  for k, v in self.qubit_operator.terms.items():
526
536
  hermitian.qubit_operator.terms[k] = v.real
527
- anti_hermitian.qubit_operator.terms[k] = 1.j * v.imag
537
+ anti_hermitian.qubit_operator.terms[k] = 1.0j * v.imag
528
538
 
529
539
  return hermitian.simplify(), anti_hermitian.simplify()
530
540
 
@@ -575,10 +585,10 @@ class QubitHamiltonian:
575
585
 
576
586
  Returns a dense 2**N x 2**N matrix representation of this
577
587
  QubitHamiltonian. Watch for memory usage when N is >12!
578
-
588
+
579
589
  Args:
580
590
  ignore_unused_qubits: If no non-trivial operator is defined on a qubits this qubit will be ignored in the matrix construction.
581
- Take for example X(1).
591
+ Take for example X(1).
582
592
  If False the operator X(1) will get mapped to X(0)
583
593
  and the function will return the matrix for X(0)
584
594
  otherwise the function will return the matrix 1 \otimes X(1)
@@ -590,10 +600,10 @@ class QubitHamiltonian:
590
600
  if ignore_unused_qubits:
591
601
  nq = len(qubits)
592
602
  else:
593
- nq = max(qubits)+1
603
+ nq = max(qubits) + 1
594
604
 
595
605
  I = np.eye(2, dtype=complex)
596
- Hm = np.zeros((2 ** nq, 2 ** nq), dtype=complex)
606
+ Hm = np.zeros((2**nq, 2**nq), dtype=complex)
597
607
 
598
608
  for key, val in self.items():
599
609
  term = [I] * nq
tequila/ml/__init__.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from .ml_api import to_platform
2
2
  from .ml_api import HAS_TORCH
3
+
3
4
  if HAS_TORCH:
4
5
  from .interface_torch import TorchLayer
@@ -24,11 +24,12 @@ def get_torch_function(objective: Objective, compile_args: dict = None, input_va
24
24
  the requisite pytorch autograd function, alongside necessary information for higher level classes.
25
25
  """
26
26
 
27
- if isinstance(objective,tuple) or isinstance(objective,list) or isinstance(objective,Objective):
27
+ if isinstance(objective, tuple) or isinstance(objective, list) or isinstance(objective, Objective):
28
28
  objective = vectorize(list_assignment(objective))
29
- comped_objective, compile_args, input_vars, weight_vars, i_grads, w_grads, first, second \
30
- = preamble(objective, compile_args, input_vars)
31
- samples = compile_args['samples']
29
+ comped_objective, compile_args, input_vars, weight_vars, i_grads, w_grads, first, second = preamble(
30
+ objective, compile_args, input_vars
31
+ )
32
+ samples = compile_args["samples"]
32
33
 
33
34
  def tensor_fix(tensor, angles, first, second):
34
35
  """
@@ -94,6 +95,7 @@ def get_torch_function(objective: Objective, compile_args: dict = None, input_va
94
95
  takes a tensor and an (int: Variable) dict and returns a (Variable: float) dict.
95
96
 
96
97
  """
98
+
97
99
  @staticmethod
98
100
  def forward(ctx, inputs, angles):
99
101
  """
@@ -106,8 +108,8 @@ def get_torch_function(objective: Objective, compile_args: dict = None, input_va
106
108
  if not isinstance(result, np.ndarray):
107
109
  # this happens if the Objective is a scalar since that's usually more convenient for pure quantum stuff.
108
110
  result = np.array(result)
109
- if hasattr(inputs,'device'):
110
- if inputs.device == 'cuda':
111
+ if hasattr(inputs, "device"):
112
+ if inputs.device == "cuda":
111
113
  r = torch.from_numpy(result).to(inputs.device)
112
114
  else:
113
115
  r = torch.from_numpy(result)
@@ -122,7 +124,7 @@ def get_torch_function(objective: Objective, compile_args: dict = None, input_va
122
124
  call_args = tensor_fix(inputs, angles, first, second)
123
125
  back_d = grad_backward.get_device()
124
126
  # build up weight and input gradient matrices... see what needs to be done to them.
125
- grad_outs = [None,None]
127
+ grad_outs = [None, None]
126
128
  for i, grads in enumerate([i_grads, w_grads]):
127
129
  if grads != {}:
128
130
  g_keys = [j for j in grads.keys()]
@@ -138,7 +140,7 @@ def get_torch_function(objective: Objective, compile_args: dict = None, input_va
138
140
  else:
139
141
  g_tensor = torch.as_tensor(arr, dtype=grad_backward.dtype)
140
142
 
141
- b = grad_backward.reshape(-1,1)
143
+ b = grad_backward.reshape(-1, 1)
142
144
  jvp = torch.matmul(g_tensor, b)
143
145
  jvp_out = jvp.flatten()
144
146
  jvp_out.requires_grad_(True)
@@ -186,9 +188,9 @@ class TorchLayer(torch.nn.Module):
186
188
  super().__init__()
187
189
 
188
190
  self._objective = objective
189
- self.function, weight_vars, compile_args = get_torch_function(objective, compile_args, input_vars)
191
+ self.function, weight_vars, compile_args = get_torch_function(objective, compile_args, input_vars)
190
192
  self._input_len = len(objective.extract_variables()) - len(weight_vars)
191
- inits = compile_args['initial_values']
193
+ inits = compile_args["initial_values"]
192
194
  self.weights = {}
193
195
  if inits is not None:
194
196
  for v in weight_vars:
@@ -197,7 +199,7 @@ class TorchLayer(torch.nn.Module):
197
199
  self.register_parameter(str(v), self.weights[str(v)])
198
200
  else:
199
201
  for v in weight_vars:
200
- self.weights[str(v)] = torch.nn.Parameter(torch.nn.init.uniform(torch.Tensor(1), a=0.0, b=2*np.pi)[0])
202
+ self.weights[str(v)] = torch.nn.Parameter(torch.nn.init.uniform(torch.Tensor(1), a=0.0, b=2 * np.pi)[0])
201
203
  self.register_parameter(str(v), self.weights[str(v)])
202
204
 
203
205
  def forward(self, x=None):
@@ -231,7 +233,9 @@ class TorchLayer(torch.nn.Module):
231
233
  f = None
232
234
  if x is not None:
233
235
  if len(x) != self._input_len:
234
- raise TequilaMLException('Received input of len {} when Objective takes {} inputs.'.format(len(x),self._input_len))
236
+ raise TequilaMLException(
237
+ "Received input of len {} when Objective takes {} inputs.".format(len(x), self._input_len)
238
+ )
235
239
  return self.function.apply(x, f)
236
240
 
237
241
  def extra_repr(self) -> str:
@@ -241,8 +245,7 @@ class TorchLayer(torch.nn.Module):
241
245
  str:
242
246
  Information used by print(TorchLayer).
243
247
  """
244
- string = 'Tequila TorchLayer. Represents: \n'
245
- string += '{} \n'.format(str(self._objective))
246
- string += 'Current Weights: {}'.format(self.weights)
248
+ string = "Tequila TorchLayer. Represents: \n"
249
+ string += "{} \n".format(str(self._objective))
250
+ string += "Current Weights: {}".format(self.weights)
247
251
  return string
248
-