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
@@ -1,7 +1,17 @@
1
1
  import numpy as np
2
2
  import tequila as tq
3
3
  import openfermion as of
4
- from openfermion import FermionOperator, QubitOperator, expectation, get_sparse_operator, jordan_wigner, reverse_jordan_wigner, normal_ordered, count_qubits, variance
4
+ from openfermion import (
5
+ FermionOperator,
6
+ QubitOperator,
7
+ expectation,
8
+ get_sparse_operator,
9
+ jordan_wigner,
10
+ reverse_jordan_wigner,
11
+ normal_ordered,
12
+ count_qubits,
13
+ variance,
14
+ )
5
15
  from itertools import product
6
16
  import scipy as sp
7
17
  import tequila.grouping.ev_utils as evu
@@ -9,8 +19,9 @@ from functools import partial
9
19
  import multiprocessing as mp
10
20
  from tequila import TequilaException
11
21
 
22
+
12
23
  def get_obt_tbt(h_ferm, spin_orb=True):
13
- '''
24
+ """
14
25
  Parameters
15
26
  ----------
16
27
  mol_name -
@@ -27,56 +38,57 @@ def get_obt_tbt(h_ferm, spin_orb=True):
27
38
  (obt, tbt) Tuple of one- and two-body integrals.
28
39
  h_ferm Fermionic Hamiltonian of the molecular system.
29
40
  num_elecs Number of electrons in the molecular system.
30
- '''
41
+ """
31
42
  no_h_ferm = normal_ordered(h_ferm)
32
- tbt = get_tbt(no_h_ferm, spin_orb = spin_orb)
43
+ tbt = get_tbt(no_h_ferm, spin_orb=spin_orb)
33
44
  h1b = no_h_ferm - tbt_to_ferm(tbt, spin_orb)
34
45
  h1b = normal_ordered(of_simplify(h1b))
35
46
  obt = get_obt(h1b, spin_orb=spin_orb)
36
47
  return (obt, tbt)
37
48
 
49
+
38
50
  def of_simplify(op):
39
- '''
51
+ """
40
52
  Simplifies fermionic operator by converting to Qubit and back again.
41
- '''
53
+ """
42
54
  return reverse_jordan_wigner(jordan_wigner(op))
43
55
 
44
- def get_spin_orbitals(H : FermionOperator):
45
- '''
56
+
57
+ def get_spin_orbitals(H: FermionOperator):
58
+ """
46
59
  Obtain the number of spin orbitals of H
47
- '''
60
+ """
48
61
  n = -1
49
62
  for term, val in H.terms.items():
50
63
  if len(term) == 4:
51
- n = max([
52
- n, term[0][0], term[1][0],
53
- term[2][0], term[3][0]
54
- ])
64
+ n = max([n, term[0][0], term[1][0], term[2][0], term[3][0]])
55
65
  elif len(term) == 2:
56
- n = max([
57
- n, term[0][0], term[1][0]])
66
+ n = max([n, term[0][0], term[1][0]])
58
67
  n += 1
59
68
  return n
60
69
 
61
- def get_tbt(H : FermionOperator, n = None, spin_orb=False):
62
- '''
70
+
71
+ def get_tbt(H: FermionOperator, n=None, spin_orb=False):
72
+ """
63
73
  Obtain the 4-rank tensor that represents two body interaction in H.
64
74
  In chemist ordering a^ a a^ a.
65
75
  In addition, simplify tensor assuming symmetry between alpha/beta coefficients
66
- '''
76
+ """
67
77
  if n is None:
68
78
  n = get_spin_orbitals(H)
69
79
 
70
80
  phy_tbt = np.zeros((n, n, n, n))
71
81
  for term, val in H.terms.items():
72
82
  if len(term) == 4:
73
- phy_tbt[
74
- term[0][0], term[1][0],
75
- term[2][0], term[3][0]
76
- ] = np.real_if_close(val)
83
+ phy_tbt[term[0][0], term[1][0], term[2][0], term[3][0]] = np.real_if_close(val)
77
84
 
78
85
  chem_tbt = np.transpose(phy_tbt, [0, 3, 1, 2])
79
- chem_tbt_sym = (chem_tbt - np.transpose(chem_tbt, [0,3,2,1]) + np.transpose(chem_tbt, [2,3,0,1]) - np.transpose(chem_tbt, [2,1,0,3]) ) / 4.0
86
+ chem_tbt_sym = (
87
+ chem_tbt
88
+ - np.transpose(chem_tbt, [0, 3, 2, 1])
89
+ + np.transpose(chem_tbt, [2, 3, 0, 1])
90
+ - np.transpose(chem_tbt, [2, 1, 0, 3])
91
+ ) / 4.0
80
92
 
81
93
  # Spin-orbital to orbital
82
94
  n_orb = phy_tbt.shape[0]
@@ -84,30 +96,30 @@ def get_tbt(H : FermionOperator, n = None, spin_orb=False):
84
96
  alpha_indices = list(range(0, n_orb * 2, 2))
85
97
  beta_indices = list(range(1, n_orb * 2, 2))
86
98
 
87
- chem_tbt_orb = (chem_tbt_sym[np.ix_(alpha_indices, alpha_indices, beta_indices, beta_indices)]
88
- - np.transpose(chem_tbt_sym[np.ix_(alpha_indices, beta_indices, beta_indices, alpha_indices)], [0,3,2,1]))
99
+ chem_tbt_orb = chem_tbt_sym[np.ix_(alpha_indices, alpha_indices, beta_indices, beta_indices)] - np.transpose(
100
+ chem_tbt_sym[np.ix_(alpha_indices, beta_indices, beta_indices, alpha_indices)], [0, 3, 2, 1]
101
+ )
89
102
  if spin_orb:
90
103
  chem_tbt = np.zeros_like(chem_tbt_sym)
91
104
  n = chem_tbt_orb.shape[0]
92
105
  for i, j, k, l in product(range(n), repeat=4):
93
106
  for a, b in product(range(2), repeat=2):
94
- chem_tbt[(2*i+a, 1), (2*j+a, 0), (2*k+b, 1), (2*l+b, 0)] = chem_tbt_orb[i,j,k,l]
107
+ chem_tbt[(2 * i + a, 1), (2 * j + a, 0), (2 * k + b, 1), (2 * l + b, 0)] = chem_tbt_orb[i, j, k, l]
95
108
  return chem_tbt
96
109
  else:
97
110
  return chem_tbt_orb
98
111
 
99
112
 
100
-
101
- def get_obt(H : FermionOperator, n = None, spin_orb=False, tiny=1e-12):
102
- '''
113
+ def get_obt(H: FermionOperator, n=None, spin_orb=False, tiny=1e-12):
114
+ """
103
115
  Obtain the 2-rank tensor that represents one body interaction in H.
104
116
  In addition, simplify tensor assuming symmetry between alpha/beta coefficients
105
- '''
117
+ """
106
118
  # getting N^2 phy_tbt and then (N/2)^2 chem_tbt
107
119
  if n is None:
108
120
  n = get_spin_orbitals(H)
109
121
 
110
- obt = np.zeros((n,n))
122
+ obt = np.zeros((n, n))
111
123
  for term, val in H.terms.items():
112
124
  if len(term) == 2:
113
125
  if term[0][1] == 1 and term[1][1] == 0:
@@ -131,13 +143,15 @@ def get_obt(H : FermionOperator, n = None, spin_orb=False, tiny=1e-12):
131
143
  obt_red_du = np.zeros((n_orb, n_orb))
132
144
  for i in range(n_orb):
133
145
  for j in range(n_orb):
134
- obt_red_uu[i,j] = obt[2*i, 2*j]
135
- obt_red_dd[i,j] = obt[2*i+1, 2*j+1]
136
- obt_red_ud = obt[2*i, 2*j+1]
137
- obt_red_du = obt[2*i+1, 2*j]
146
+ obt_red_uu[i, j] = obt[2 * i, 2 * j]
147
+ obt_red_dd[i, j] = obt[2 * i + 1, 2 * j + 1]
148
+ obt_red_ud = obt[2 * i, 2 * j + 1]
149
+ obt_red_du = obt[2 * i + 1, 2 * j]
138
150
 
139
151
  if np.sum(np.abs(obt_red_du)) + np.sum(np.abs(obt_red_ud)) != 0:
140
- print("Warning, operator to one-body transformation ran with spin_orb=false, but spin-orbit couplings are not 0!")
152
+ print(
153
+ "Warning, operator to one-body transformation ran with spin_orb=false, but spin-orbit couplings are not 0!"
154
+ )
141
155
  if np.sum(np.abs(obt_red_uu - obt_red_dd)) > tiny:
142
156
  print("Warning, operator to one-body transformation ran with spin_orb=false, but isn't symmetric to spin-flips")
143
157
  print("obt_uu - obt_dd = {}".format(obt_red_uu - obt_red_dd))
@@ -146,11 +160,12 @@ def get_obt(H : FermionOperator, n = None, spin_orb=False, tiny=1e-12):
146
160
 
147
161
  return obt
148
162
 
163
+
149
164
  def get_cartan_ferm_op(tsr, spin_orb=False):
150
- '''
165
+ """
151
166
  Return the corresponding fermionic operators in Cartan subalgebra
152
167
  based on the tensor. This tensor can index over spin-orbtals or orbitals
153
- '''
168
+ """
154
169
  if len(tsr.shape) == 4:
155
170
  n = tsr.shape[0]
156
171
  op = FermionOperator.zero()
@@ -158,18 +173,11 @@ def get_cartan_ferm_op(tsr, spin_orb=False):
158
173
  if not spin_orb:
159
174
  for a, b in product(range(2), repeat=2):
160
175
  op += FermionOperator(
161
- term = (
162
- (2*i+a, 1), (2*i+a, 0),
163
- (2*j+b, 1), (2*j+b, 0)
164
- ), coefficient=tsr[i, i, j, j]
176
+ term=((2 * i + a, 1), (2 * i + a, 0), (2 * j + b, 1), (2 * j + b, 0)),
177
+ coefficient=tsr[i, i, j, j],
165
178
  )
166
179
  else:
167
- op += FermionOperator(
168
- term=(
169
- (i, 1), (i, 0),
170
- (j, 1), (j, 0)
171
- ), coefficient=tsr[i, i, j, j]
172
- )
180
+ op += FermionOperator(term=((i, 1), (i, 0), (j, 1), (j, 0)), coefficient=tsr[i, i, j, j])
173
181
  return op
174
182
  elif len(tsr.shape) == 2:
175
183
  n = tsr.shape[0]
@@ -177,24 +185,17 @@ def get_cartan_ferm_op(tsr, spin_orb=False):
177
185
  for i in range(n):
178
186
  if not spin_orb:
179
187
  for a in range(2):
180
- op += FermionOperator(
181
- term = (
182
- (2*i+a, 1), (2*i+a, 0)
183
- ), coefficient=tsr[i, i]
184
- )
188
+ op += FermionOperator(term=((2 * i + a, 1), (2 * i + a, 0)), coefficient=tsr[i, i])
185
189
  else:
186
- op += FermionOperator(
187
- term = (
188
- (i, 1), (i, 0)
189
- ), coefficient=tsr[i, i]
190
- )
190
+ op += FermionOperator(term=((i, 1), (i, 0)), coefficient=tsr[i, i])
191
191
  return op
192
192
 
193
+
193
194
  def get_ferm_op(tsr, spin_orb=False):
194
- '''
195
+ """
195
196
  Return the corresponding fermionic operators based on the tensor
196
197
  This tensor can index over spin-orbtals or orbitals
197
- '''
198
+ """
198
199
  if len(tsr.shape) == 4:
199
200
  n = tsr.shape[0]
200
201
  op = FermionOperator.zero()
@@ -202,18 +203,11 @@ def get_ferm_op(tsr, spin_orb=False):
202
203
  if not spin_orb:
203
204
  for a, b in product(range(2), repeat=2):
204
205
  op += FermionOperator(
205
- term = (
206
- (2*i+a, 1), (2*j+a, 0),
207
- (2*k+b, 1), (2*l+b, 0)
208
- ), coefficient=tsr[i, j, k, l]
206
+ term=((2 * i + a, 1), (2 * j + a, 0), (2 * k + b, 1), (2 * l + b, 0)),
207
+ coefficient=tsr[i, j, k, l],
209
208
  )
210
209
  else:
211
- op += FermionOperator(
212
- term=(
213
- (i, 1), (j, 0),
214
- (k, 1), (l, 0)
215
- ), coefficient=tsr[i, j, k, l]
216
- )
210
+ op += FermionOperator(term=((i, 1), (j, 0), (k, 1), (l, 0)), coefficient=tsr[i, j, k, l])
217
211
  return op
218
212
  elif len(tsr.shape) == 2:
219
213
  n = tsr.shape[0]
@@ -222,154 +216,161 @@ def get_ferm_op(tsr, spin_orb=False):
222
216
  for j in range(n):
223
217
  if not spin_orb:
224
218
  for a in range(2):
225
- op += FermionOperator(
226
- term = (
227
- (2*i+a, 1), (2*j+a, 0)
228
- ), coefficient=tsr[i, j]
229
- )
219
+ op += FermionOperator(term=((2 * i + a, 1), (2 * j + a, 0)), coefficient=tsr[i, j])
230
220
  else:
231
- op += FermionOperator(
232
- term = (
233
- (i, 1), (j, 0)
234
- ), coefficient=tsr[i, j]
235
- )
221
+ op += FermionOperator(term=((i, 1), (j, 0)), coefficient=tsr[i, j])
236
222
  return op
237
223
 
238
- def tbt_to_ferm(tbt : np.array, spin_orb, norm_ord = False):
239
- '''
224
+
225
+ def tbt_to_ferm(tbt: np.array, spin_orb, norm_ord=False):
226
+ """
240
227
  Converts two body tensor into Fermion Operator.
241
- '''
242
- if norm_ord == True:
228
+ """
229
+ if norm_ord:
243
230
  return normal_ordered(get_ferm_op(tbt, spin_orb))
244
231
  else:
245
232
  return get_ferm_op(tbt, spin_orb)
246
233
 
247
- def cartan_tbt_to_ferm(ctbt : np.array, spin_orb, norm_ord = False):
248
- '''
234
+
235
+ def cartan_tbt_to_ferm(ctbt: np.array, spin_orb, norm_ord=False):
236
+ """
249
237
  Converts two body tensor into Fermion Operator.
250
- '''
251
- if norm_ord == True:
238
+ """
239
+ if norm_ord:
252
240
  return normal_ordered(get_cartan_ferm_op(ctbt, spin_orb))
253
241
  else:
254
242
  return get_cartan_ferm_op(ctbt, spin_orb)
255
243
 
256
- def obt_to_ferm(obt : np.array, spin_orb = False, norm_ord = False):
257
- '''
244
+
245
+ def obt_to_ferm(obt: np.array, spin_orb=False, norm_ord=False):
246
+ """
258
247
  Converts one body tensor into Fermion Operator.
259
- '''
260
- if norm_ord == True:
248
+ """
249
+ if norm_ord:
261
250
  return of.normal_ordered(get_ferm_op(obt, spin_orb))
262
251
  else:
263
252
  return get_ferm_op(obt, spin_orb)
264
253
 
254
+
265
255
  def tbt_orb_to_so(tbt):
266
- '''
256
+ """
267
257
  Converts two body term to spin orbitals.
268
- '''
258
+ """
269
259
  n = tbt.shape[0]
270
260
 
271
- tbt_so = np.zeros([2*n,2*n,2*n,2*n])
261
+ tbt_so = np.zeros([2 * n, 2 * n, 2 * n, 2 * n])
272
262
  for i1, i2, i3, i4 in product(range(n), repeat=4):
273
- for a in [0,1]:
274
- for b in [0,1]:
275
- tbt_so[2*i1+a,2*i2+a,2*i3+b,2*i4+b] = tbt[i1,i2,i3,i4]
263
+ for a in [0, 1]:
264
+ for b in [0, 1]:
265
+ tbt_so[2 * i1 + a, 2 * i2 + a, 2 * i3 + b, 2 * i4 + b] = tbt[i1, i2, i3, i4]
276
266
  return tbt_so
277
267
 
268
+
278
269
  def obt_orb_to_so(obt):
279
- '''
270
+ """
280
271
  Converts one body term to spin orbitals.
281
- '''
272
+ """
282
273
  n = obt.shape[0]
283
274
 
284
- obt_so = np.zeros([2*n,2*n])
275
+ obt_so = np.zeros([2 * n, 2 * n])
285
276
  for i1, i2 in product(range(n), repeat=2):
286
- for a in [0,1]:
287
- obt_so[2*i1+a,2*i2+a] = obt[i1,i2]
277
+ for a in [0, 1]:
278
+ obt_so[2 * i1 + a, 2 * i2 + a] = obt[i1, i2]
288
279
 
289
280
  return obt_so
290
281
 
282
+
291
283
  def convert_u_to_so(U):
292
- '''
284
+ """
293
285
  Converts unitary matrix to spin orbitals
294
- '''
286
+ """
295
287
  nf = U.shape[0]
296
288
  n = 2 * U.shape[1]
297
289
  U_so = np.zeros([nf, n, n])
298
290
  for i in range(nf):
299
- U_so[i,:,:] = obt_orb_to_so(U[i,:,:])
291
+ U_so[i, :, :] = obt_orb_to_so(U[i, :, :])
300
292
  return U_so
301
293
 
302
- def convert_tbts_to_frags(tbts, spin_orb = False):
303
- '''
294
+
295
+ def convert_tbts_to_frags(tbts, spin_orb=False):
296
+ """
304
297
  Converts two body terms to fermionic fragments.
305
- '''
298
+ """
306
299
  ops = []
307
300
  nf = tbts.shape[0]
308
301
  for i in range(nf):
309
- ops.append(tbt_to_ferm(tbts[i,:,:,:,:],spin_orb))
302
+ ops.append(tbt_to_ferm(tbts[i, :, :, :, :], spin_orb))
310
303
  return ops
311
304
 
312
- def symmetric(M, tol = 1e-8, ret_op = False):
313
- '''
305
+
306
+ def symmetric(M, tol=1e-8, ret_op=False):
307
+ """
314
308
  if ret_op = False, checks whether a given matrix is symmetric.
315
309
  if ret_op = True, returns the symmetrc form of the given matrix.
316
- '''
310
+ """
317
311
  M_res = np.tril(M) + np.triu(M.T, 1)
318
- if ret_op == False:
312
+ if not ret_op:
319
313
  if np.sum(np.abs(M - M_res)) > tol:
320
314
  return False
321
315
  return True
322
316
  else:
323
317
  return M_res
324
318
 
319
+
325
320
  def check_tbt_symmetry(tbt):
326
- '''
321
+ """
327
322
  Returns symmetric form of tbt.
328
- '''
323
+ """
329
324
  N = tbt.shape[0] ** 2
330
- tbt_res = np.reshape(tbt, (N,N))
325
+ tbt_res = np.reshape(tbt, (N, N))
331
326
  if not symmetric(tbt_res):
332
327
  print("Non-symmetric two-body tensor as input for SVD routine, calculations might have errors...")
333
328
  else:
334
- tbt_res = symmetric(tbt_res, ret_op = True)
329
+ tbt_res = symmetric(tbt_res, ret_op=True)
335
330
  return tbt_res
336
331
 
332
+
337
333
  def diagonalizing_tbt(tbt_res):
338
- '''
334
+ """
339
335
  Returns diagonal form and unitary rotation to diagonalize tbt_res
340
- '''
336
+ """
341
337
  print("Diagonalizing two-body tensor")
342
338
  lamda, U = np.linalg.eig(tbt_res)
343
339
  ind = np.argsort(np.abs(lamda))[::-1]
344
340
  lamda = lamda[ind]
345
- U = U[:,ind]
341
+ U = U[:, ind]
346
342
  return lamda, U
347
343
 
348
- def fragmentization(lamda, U, n, tol = 1e-8):
349
- '''
344
+
345
+ def fragmentization(lamda, U, n, tol=1e-8):
346
+ """
350
347
  Returns fragments of tbt
351
- '''
348
+ """
352
349
  N = n**2
353
350
  L_mats = []
354
351
  flags = np.zeros(N)
355
352
 
356
353
  for i in range(N):
357
354
  if abs(lamda[i]) < tol:
358
- print("Truncating SVD for coefficients with magnitude smaller or equal to {}, using {} fragments".format(abs(lamda[i]), (i)))
355
+ print(
356
+ "Truncating SVD for coefficients with magnitude smaller or equal to {}, using {} fragments".format(
357
+ abs(lamda[i]), (i)
358
+ )
359
+ )
359
360
  break
360
- cur_l = np.reshape(U[:, i], (n,n))
361
+ cur_l = np.reshape(U[:, i], (n, n))
361
362
  if not symmetric(cur_l):
362
363
  flags[i] = 1
363
364
  else:
364
- cur_l = symmetric(cur_l, ret_op = True)
365
+ cur_l = symmetric(cur_l, ret_op=True)
365
366
  L_mats.append(cur_l)
366
367
  return L_mats, flags
367
368
 
368
369
 
369
- def lr_decomp(tbt : np.array, spin_orb=True, tiny=1e-8):
370
- '''
370
+ def lr_decomp(tbt: np.array, spin_orb=True, tiny=1e-8):
371
+ """
371
372
  Singular Value Decomposition of the two-body tensor term
372
- '''
373
+ """
373
374
  print("Starting SVD routine")
374
375
  n = tbt.shape[0]
375
376
 
@@ -379,46 +380,49 @@ def lr_decomp(tbt : np.array, spin_orb=True, tiny=1e-8):
379
380
  num_ops = len(L_mats)
380
381
 
381
382
  TBTS = np.zeros((num_ops, n, n, n, n))
382
- CARTAN_TBTS = np.zeros(( num_ops, n, n, n, n))
383
- U_ARR = np.zeros((num_ops, n,n))
383
+ CARTAN_TBTS = np.zeros((num_ops, n, n, n, n))
384
+ U_ARR = np.zeros((num_ops, n, n))
384
385
 
385
386
  for i in range(num_ops):
386
387
  if flags[i] == 0:
387
388
  omega_l, U_l = np.linalg.eigh(L_mats[i])
388
389
 
389
- tbt_svd_CSA = np.zeros((n,n,n,n))
390
+ tbt_svd_CSA = np.zeros((n, n, n, n))
390
391
 
391
392
  for j in range(n):
392
- for k in range(j,n):
393
- tbt_svd_CSA[j,j,k,k] = omega_l[j]*omega_l[k]
394
- tbt_svd_CSA[k,k,j,j] = tbt_svd_CSA[j,j,k,k]
395
-
393
+ for k in range(j, n):
394
+ tbt_svd_CSA[j, j, k, k] = omega_l[j] * omega_l[k]
395
+ tbt_svd_CSA[k, k, j, j] = tbt_svd_CSA[j, j, k, k]
396
396
 
397
397
  tbt_svd_CSA = lamda[i] * tbt_svd_CSA
398
398
  tbt_svd = unitary_cartan_rotation_from_matrix(U_l, tbt_svd_CSA)
399
- TBTS[i,:,:,:,:] = tbt_svd
400
- CARTAN_TBTS[i,:,:,:,:] = tbt_svd_CSA
399
+ TBTS[i, :, :, :, :] = tbt_svd
400
+ CARTAN_TBTS[i, :, :, :, :] = tbt_svd_CSA
401
401
  else:
402
402
  if np.sum(np.abs(L_mats[i] + L_mats[i].T)) > tiny:
403
- raise TequilaException("SVD operator {} if neither Hermitian or anti-Hermitian, cannot do double factorization into Hermitian fragment!".format(i))
403
+ raise TequilaException(
404
+ "SVD operator {} if neither Hermitian or anti-Hermitian, cannot do double factorization into Hermitian fragment!".format(
405
+ i
406
+ )
407
+ )
404
408
 
405
409
  temp_l = 1j * L_mats[i]
406
- cur_l = (temp_l + temp_l.conj().T)/2
410
+ cur_l = (temp_l + temp_l.conj().T) / 2
407
411
  omega_l, U_l = np.linalg.eigh(cur_l)
408
412
 
409
- tbt_svd_CSA = np.zeros((n,n,n,n))
413
+ tbt_svd_CSA = np.zeros((n, n, n, n))
410
414
 
411
415
  for j in range(n):
412
- for k in range(j,n):
413
- tbt_svd_CSA[j,j,k,k] = -omega_l[j]*omega_l[k]
414
- tbt_svd_CSA[k,k,j,j] = tbt_svd_CSA[j,j,k,k]
416
+ for k in range(j, n):
417
+ tbt_svd_CSA[j, j, k, k] = -omega_l[j] * omega_l[k]
418
+ tbt_svd_CSA[k, k, j, j] = tbt_svd_CSA[j, j, k, k]
415
419
 
416
420
  tbt_svd_CSA = lamda[i] * tbt_svd_CSA
417
421
  tbt_svd = unitary_cartan_rotation_from_matrix(U_l, tbt_svd_CSA)
418
- TBTS[i,:,:,:,:] = tbt_svd
419
- CARTAN_TBTS[i,:,:,:,:] = tbt_svd_CSA
422
+ TBTS[i, :, :, :, :] = tbt_svd
423
+ CARTAN_TBTS[i, :, :, :, :] = tbt_svd_CSA
420
424
 
421
- U_ARR[i,:,:] = U_l
425
+ U_ARR[i, :, :] = U_l
422
426
  print("Finished SVD routine")
423
427
 
424
428
  L_ops = []
@@ -428,19 +432,22 @@ def lr_decomp(tbt : np.array, spin_orb=True, tiny=1e-8):
428
432
  L_ops.append(lamda[i] * op_1d * op_1d)
429
433
  return CARTAN_TBTS, TBTS, L_ops, U_ARR
430
434
 
431
- def unitary_cartan_rotation_from_matrix(Umat, tbt : np.array):
432
- '''
435
+
436
+ def unitary_cartan_rotation_from_matrix(Umat, tbt: np.array):
437
+ """
433
438
  Rotates Cartan tbt using orbital rotation matrix U_mat
434
- '''
435
- rotated_tbt = np.einsum('al,bl,cm,dm,llmm',Umat,Umat,Umat,Umat,tbt)
439
+ """
440
+ rotated_tbt = np.einsum("al,bl,cm,dm,llmm", Umat, Umat, Umat, Umat, tbt)
436
441
  return rotated_tbt
437
442
 
443
+
438
444
  def qubit_number(op):
439
- '''
445
+ """
440
446
  Returns number of qubits in the operator
441
- '''
447
+ """
442
448
  return count_qubits(op)
443
449
 
450
+
444
451
  def get_occ_no(n_elec, n_qubits):
445
452
  """
446
453
  Given some molecule, find the reference occupation number state.
@@ -449,21 +456,46 @@ def get_occ_no(n_elec, n_qubits):
449
456
  Returns:
450
457
  occ_no (str): Occupation no. vector.
451
458
  """
452
- occ_no = '1'*n_elec + '0'*(n_qubits - n_elec)
459
+ occ_no = "1" * n_elec + "0" * (n_qubits - n_elec)
453
460
 
454
461
  return occ_no
455
462
 
463
+
456
464
  def n_elec(mol):
457
- '''
465
+ """
458
466
  Given some molecule, find the reference occupation number state.
459
467
  Args:
460
468
  mol (str): H2, LiH, BeH2, H2O, NH3
461
469
  Returns:
462
470
  Number of electrons (int)
463
- '''
464
- n_electrons = {'h2': 2, 'lih': 4, 'beh2': 6, 'h2o': 10, 'nh3': 10, 'n2': 14, 'hf':10, 'ch4':10, 'co':14, 'h4':4, 'ch2':8, 'heh':2, 'h6':6, 'nh':8, 'h3':2, 'h4sq':4, 'h2ost':10, 'beh2st':6, 'h2ost2':10, 'beh2st2':6, 'li2frco':2, 'beh2frco':4}
471
+ """
472
+ n_electrons = {
473
+ "h2": 2,
474
+ "lih": 4,
475
+ "beh2": 6,
476
+ "h2o": 10,
477
+ "nh3": 10,
478
+ "n2": 14,
479
+ "hf": 10,
480
+ "ch4": 10,
481
+ "co": 14,
482
+ "h4": 4,
483
+ "ch2": 8,
484
+ "heh": 2,
485
+ "h6": 6,
486
+ "nh": 8,
487
+ "h3": 2,
488
+ "h4sq": 4,
489
+ "h2ost": 10,
490
+ "beh2st": 6,
491
+ "h2ost2": 10,
492
+ "beh2st2": 6,
493
+ "li2frco": 2,
494
+ "beh2frco": 4,
495
+ }
465
496
  return n_electrons[mol.lower()]
466
497
 
498
+
467
499
  def create_hamiltonian_in_subspace(indices, Hq, n_qubits):
468
500
  """
469
501
  Given some basis states, create the Hamiltonian within the span of those basis states.
@@ -479,11 +511,11 @@ def create_hamiltonian_in_subspace(indices, Hq, n_qubits):
479
511
  row_idx = []
480
512
  col_idx = []
481
513
  H_mat_elements = []
482
- elements_sum = np.zeros((len(indices),len(indices)))
514
+ elements_sum = np.zeros((len(indices), len(indices)))
483
515
  op_sum = QubitOperator.zero()
484
516
  for prog, op in enumerate(Hq):
485
517
  op_sum += op
486
- if (prog + 1)%350 == 0 or prog == len(Hq.terms) - 1:
518
+ if (prog + 1) % 350 == 0 or prog == len(Hq.terms) - 1:
487
519
  opspar = of.get_sparse_operator(op_sum, n_qubits)
488
520
  op_sum = of.QubitOperator.zero()
489
521
  for iidx, iindx in enumerate(indices):
@@ -495,9 +527,10 @@ def create_hamiltonian_in_subspace(indices, Hq, n_qubits):
495
527
  row_idx.append(iidx)
496
528
  col_idx.append(jidx)
497
529
  H_mat_elements.append(elements_sum[iidx, jidx])
498
- H_mat_sub = sp.sparse.coo_matrix((H_mat_elements, (row_idx, col_idx)), shape = (subspace_dim, subspace_dim))
530
+ H_mat_sub = sp.sparse.coo_matrix((H_mat_elements, (row_idx, col_idx)), shape=(subspace_dim, subspace_dim))
499
531
  return H_mat_sub
500
532
 
533
+
501
534
  def get_jw_cisd_basis_states(n_elec, n_qubits):
502
535
  """
503
536
  Given some molecule, find the all BK basis vectors that correspond to occupation numbers that are achieved by single and double excitations.
@@ -511,6 +544,7 @@ def get_jw_cisd_basis_states(n_elec, n_qubits):
511
544
  indices = get_jw_cisd_basis_states_wrap(ref_occ_nos, n_qubits)
512
545
  return indices
513
546
 
547
+
514
548
  def get_jw_cisd_basis_states_wrap(ref_occ_nos, n_qubits):
515
549
  """
516
550
  Given some occupation number, find the all other occupation numbers that are achieved by single and double excitations.
@@ -522,27 +556,28 @@ def get_jw_cisd_basis_states_wrap(ref_occ_nos, n_qubits):
522
556
 
523
557
  indices = [find_index(get_jw_basis_states(ref_occ_nos))]
524
558
  for occidx, occ_orbitals in enumerate(ref_occ_nos):
525
- if occ_orbitals == '1':
559
+ if occ_orbitals == "1":
526
560
  annihilated_state = list(ref_occ_nos)
527
- annihilated_state[occidx] = '0'
528
- #Singles
561
+ annihilated_state[occidx] = "0"
562
+ # Singles
529
563
  for virtidx, virtual_orbs in enumerate(ref_occ_nos):
530
- if virtual_orbs == '0':
564
+ if virtual_orbs == "0":
531
565
  new_state = annihilated_state[:]
532
- new_state[virtidx] = '1'
533
- indices.append(find_index(get_jw_basis_states(''.join(new_state))))
534
- #Doubles
535
- for occ2idx in range(occidx +1, n_qubits):
536
- if ref_occ_nos[occ2idx] == '1':
566
+ new_state[virtidx] = "1"
567
+ indices.append(find_index(get_jw_basis_states("".join(new_state))))
568
+ # Doubles
569
+ for occ2idx in range(occidx + 1, n_qubits):
570
+ if ref_occ_nos[occ2idx] == "1":
537
571
  annihilated_state_double = new_state[:]
538
- annihilated_state_double[occ2idx] = '0'
539
- for virt2idx in range(virtidx +1, n_qubits):
540
- if ref_occ_nos[virt2idx] == '0':
572
+ annihilated_state_double[occ2idx] = "0"
573
+ for virt2idx in range(virtidx + 1, n_qubits):
574
+ if ref_occ_nos[virt2idx] == "0":
541
575
  new_state_double = annihilated_state_double[:]
542
- new_state_double[virt2idx] = '1'
543
- indices.append(find_index(get_jw_basis_states(''.join(new_state_double))))
576
+ new_state_double[virt2idx] = "1"
577
+ indices.append(find_index(get_jw_basis_states("".join(new_state_double))))
544
578
  return indices
545
579
 
580
+
546
581
  def find_index(basis_state):
547
582
  """
548
583
  Given some qubit/fermionic basis state, find the index of the a wavefunction that corresponds to that array.
@@ -554,10 +589,11 @@ def find_index(basis_state):
554
589
  index = 0
555
590
  n_qubits = len(basis_state)
556
591
  for j in range(n_qubits):
557
- index += int(basis_state[j])*2**(n_qubits - j - 1)
592
+ index += int(basis_state[j]) * 2 ** (n_qubits - j - 1)
558
593
 
559
594
  return index
560
595
 
596
+
561
597
  def get_jw_basis_states(occ_no_list):
562
598
  """
563
599
  Implementation from arXiv:quant-ph/0003137 and https://doi.org/10.1021/acs.jctc.8b00450. Given some reference occupation no's in the fermionic space, find the corresponding BK basis state in the qubit space.
@@ -568,14 +604,15 @@ def get_jw_basis_states(occ_no_list):
568
604
  """
569
605
  jw_list = []
570
606
  for occ_no in occ_no_list:
571
- qubit_state = np.array(list(occ_no), dtype = int)
607
+ qubit_state = np.array(list(occ_no), dtype=int)
572
608
  jw_list.append(qubit_state)
573
609
  return jw_list
574
610
 
611
+
575
612
  def expectation_value(op, psi, n, trunc=False):
576
- '''
613
+ """
577
614
  Calculates expectation of op over psi.
578
- '''
615
+ """
579
616
  opq = jordan_wigner(op)
580
617
  if trunc is False:
581
618
  e_val = expectation(get_sparse_operator(opq, n_qubits=n), psi)
@@ -583,86 +620,103 @@ def expectation_value(op, psi, n, trunc=False):
583
620
  e_val = evu.op_ev_multiple_bases(opq, psi[0], psi[1], n)
584
621
  return real_round(e_val)
585
622
 
586
- def variance_value(op, psi, n, trunc=False, neg_tol = 1e-7):
587
- '''
623
+
624
+ def variance_value(op, psi, n, trunc=False, neg_tol=1e-7):
625
+ """
588
626
  Calculates variance of op over psi.
589
- '''
627
+ """
590
628
  opq = jordan_wigner(op)
591
629
  if trunc is False:
592
630
  var_val = variance(get_sparse_operator(opq, n_qubits=n), psi)
593
631
  else:
594
- var_val = evu.op_ev_multiple_bases(opq * opq, psi[0], psi[1], n) - evu.op_ev_multiple_bases(opq, psi[0], psi[1], n) ** 2
632
+ var_val = (
633
+ evu.op_ev_multiple_bases(opq * opq, psi[0], psi[1], n)
634
+ - evu.op_ev_multiple_bases(opq, psi[0], psi[1], n) ** 2
635
+ )
595
636
  var_val = real_round(var_val)
596
637
  if -neg_tol <= var_val < 0:
597
638
  var_val = 0
598
639
  if var_val < -neg_tol:
599
- raise TequilaException('Variance of an observable should not be negative')
640
+ raise TequilaException("Variance of an observable should not be negative")
600
641
  return var_val
601
642
 
643
+
602
644
  def real_round(x, tol=1e-10):
603
- '''
645
+ """
604
646
  Returns real part of complex numbers if the imaginary part is negligible.
605
- '''
606
- if abs(x.imag)/abs(x) >= tol:
607
- print("Warning, rounding x={} to abs(x). Complex component is above tolerance (relative magnitude: {:.2f})".format(x, abs(x.imag)/abs(x)))
647
+ """
648
+ if abs(x.imag) / abs(x) >= tol:
649
+ print(
650
+ "Warning, rounding x={} to abs(x). Complex component is above tolerance (relative magnitude: {:.2f})".format(
651
+ x, abs(x.imag) / abs(x)
652
+ )
653
+ )
608
654
  return abs(x)
609
655
  return x.real
610
656
 
657
+
611
658
  def get_E(psi, n, n_qubits, trunc=False):
612
- '''
659
+ """
613
660
  Returns the dictionary of one electron excitations over psi
614
- '''
661
+ """
615
662
  gEd = partial(get_E_dummy, psi, n, n_qubits, trunc)
616
663
  with mp.Pool(mp.cpu_count()) as pool:
617
- sq = pool.map(gEd, range(n ** 2))
664
+ sq = pool.map(gEd, range(n**2))
618
665
  pool.close()
619
666
  pool.join()
620
667
  return np.array(sq)
621
668
 
669
+
622
670
  def get_E_dummy(psi, n, n_qubits, trunc, ind):
623
- '''
671
+ """
624
672
  Returns the expectation value of one electron excitation over psi
625
- '''
673
+ """
626
674
  j = ind % n
627
675
  i = ind // n
628
676
  op = FermionOperator(((i, 1), (j, 0)), coefficient=1.0)
629
677
  return expectation_value(op, psi, n_qubits, trunc)
630
678
 
679
+
631
680
  def get_EE(psi, n, n_qubits, trunc=False):
632
- '''
681
+ """
633
682
  Returns the dictionary value of two electron excitations over psi
634
- '''
683
+ """
635
684
  gEEd = partial(get_EE_dummy, psi, n, n_qubits, trunc)
636
685
  with mp.Pool(mp.cpu_count()) as pool:
637
- sq = pool.map(gEEd, range(n ** 4))
686
+ sq = pool.map(gEEd, range(n**4))
638
687
  pool.close()
639
688
  pool.join()
640
689
  return np.array(sq)
641
690
 
691
+
642
692
  def get_EE_dummy(psi, n, n_qubits, trunc, ind):
643
- '''
693
+ """
644
694
  Returns the expectation value of two electron excitation over psi
645
- '''
695
+ """
646
696
  l = ind % n
647
- k = ind % n ** 2 // n
648
- j = ind % n ** 3 // n ** 2
649
- i = ind // n ** 3
697
+ k = ind % n**2 // n
698
+ j = ind % n**3 // n**2
699
+ i = ind // n**3
650
700
  op = FermionOperator(((i, 1), (j, 0), (k, 1), (l, 0)), coefficient=1.0)
651
701
  return expectation_value(op, psi, n_qubits, trunc=trunc)
652
702
 
703
+
653
704
  def reorganize(n, ev_dict_E, ev_dict_EE):
654
- '''
705
+ """
655
706
  -----------
656
- '''
657
- ev_dict_ob_ob = np.zeros([n,n,n,n])
707
+ """
708
+ ev_dict_ob_ob = np.zeros([n, n, n, n])
658
709
  for i, j, k, l in product(range(n), repeat=4):
659
- ev_dict_ob_ob[i, j, k, l] = ev_dict_EE[(n**3) * (i) + (n**2) * (j) + n * (k) + l] - ev_dict_E[n * (i) + j] * ev_dict_E[n * (k) + l]
710
+ ev_dict_ob_ob[i, j, k, l] = (
711
+ ev_dict_EE[(n**3) * (i) + (n**2) * (j) + n * (k) + l] - ev_dict_E[n * (i) + j] * ev_dict_E[n * (k) + l]
712
+ )
660
713
  return ev_dict_ob_ob
661
714
 
715
+
662
716
  def compute_covk(op1, op2, psi, n_qubits, trunc=False):
663
- '''
717
+ """
664
718
  ---------------------
665
- '''
719
+ """
666
720
  cko = partial(covk_one, op1, op2, psi, n_qubits, trunc)
667
721
  length = len(op1)
668
722
  with mp.Pool(mp.cpu_count()) as pool:
@@ -671,17 +725,19 @@ def compute_covk(op1, op2, psi, n_qubits, trunc=False):
671
725
  pool.join()
672
726
  return np.array(covs)
673
727
 
728
+
674
729
  def covk_one(op1, op2, psi, n_qubits, trunc, ind):
675
- '''
730
+ """
676
731
  ----------------------
677
- '''
732
+ """
678
733
  cov = covariance(op1[ind], op2, psi, n_qubits, trunc) + covariance(op2, op1[ind], psi, n_qubits, trunc)
679
734
  return cov
680
735
 
736
+
681
737
  def covariance(op1, op2, psi, n_qubits, trunc):
682
- '''
738
+ """
683
739
  Returns covariance between op1 and op2
684
- '''
740
+ """
685
741
  op1q = of.jordan_wigner(op1)
686
742
  op2q = of.jordan_wigner(op2)
687
743
 
@@ -698,18 +754,18 @@ def covariance(op1, op2, psi, n_qubits, trunc):
698
754
  exp_op2 = evu.op_ev_multiple_bases(op2q, psi[0], psi[1], n_qubits)
699
755
  return cross - exp_op1 * exp_op2
700
756
 
701
- def covariance_ob_ob(obt1, obt2, ev_dict_ob_ob):
702
- '''
703
757
 
704
- '''
758
+ def covariance_ob_ob(obt1, obt2, ev_dict_ob_ob):
759
+ """ """
705
760
  my_cov = 0.0 + 0.0j
706
- my_cov = np.einsum('ij,kl,ijkl',obt1, obt2, ev_dict_ob_ob)
761
+ my_cov = np.einsum("ij,kl,ijkl", obt1, obt2, ev_dict_ob_ob)
707
762
  return my_cov
708
763
 
764
+
709
765
  def fff_1_iter(obt, tbts, var, c0, ck, fff_var):
710
- '''
766
+ """
711
767
  Computes a single iteration of FFF optimization (arXiv:2208.14490v3 - Section 2.2)
712
- '''
768
+ """
713
769
  m0 = compute_meas_alloc(var, obt, tbts, fff_var.nq, fff_var.mix)
714
770
  lambda_opt = compute_lambda_optimum(fff_var.coo, c0, ck, m0, fff_var.nf, fff_var.n)
715
771
  new_obt, new_tbts = modify_ops(obt, tbts, lambda_opt, fff_var.o_t, fff_var.nf, fff_var.n, fff_var.uops)
@@ -717,10 +773,11 @@ def fff_1_iter(obt, tbts, var, c0, ck, fff_var):
717
773
  new_c0, new_ck = modify_c(fff_var.coo, c0, ck, lambda_opt, fff_var.nf, fff_var.n)
718
774
  return new_obt, new_tbts, new_var, new_c0, new_ck
719
775
 
776
+
720
777
  def compute_lambda_optimum(Coo, C0, Ck, m0, nf, n):
721
- '''
778
+ """
722
779
  Computes the optimum value of lamda
723
- '''
780
+ """
724
781
  symCoo = np.zeros_like(Coo)
725
782
  diagCoo = np.zeros_like(Coo)
726
783
  mat = np.zeros_like(Coo)
@@ -728,63 +785,65 @@ def compute_lambda_optimum(Coo, C0, Ck, m0, nf, n):
728
785
  nall = Coo.shape[0]
729
786
  for i in range(nall):
730
787
  for j in range(i, nall):
731
- symCoo[i,j] = Coo[i,j] + Coo[j,i]
732
- symCoo[j,i] = symCoo[i,j]
788
+ symCoo[i, j] = Coo[i, j] + Coo[j, i]
789
+ symCoo[j, i] = symCoo[i, j]
733
790
  for k in range(nf):
734
791
  for p in range(n):
735
792
  for q in range(n):
736
793
  ind1 = n * k + p
737
794
  ind2 = n * k + q
738
- diagCoo[ind1, ind2] = symCoo[ind1, ind2]/m0[k + 1]
739
- mat = (1/m0[0]) * symCoo + diagCoo
795
+ diagCoo[ind1, ind2] = symCoo[ind1, ind2] / m0[k + 1]
796
+ mat = (1 / m0[0]) * symCoo + diagCoo
740
797
  for k in range(nf):
741
798
  for p in range(n):
742
799
  ind = n * k + p
743
- vec[ind] = Ck[ind]/m0[k + 1]
744
- vec = vec - (1/m0[0]) * C0
800
+ vec[ind] = Ck[ind] / m0[k + 1]
801
+ vec = vec - (1 / m0[0]) * C0
745
802
  lam = solve_Axb(mat, vec)
746
803
  return lam
747
804
 
805
+
748
806
  def var_avg(n_qubits):
749
- """ Haar average variance of a Pauli product.
750
- """
751
- return 1. - 1. / ( 2 ** n_qubits + 1 )
807
+ """Haar average variance of a Pauli product."""
808
+ return 1.0 - 1.0 / (2**n_qubits + 1)
809
+
752
810
 
753
811
  def get_avg_variances(ops, n_qubits):
754
- """ Compute Haar average variances of ops.
755
- """
812
+ """Compute Haar average variances of ops."""
756
813
  return np.array(list(map(lambda x: get_avg_variance(x, n_qubits), ops)))
757
814
 
815
+
758
816
  def get_avg_variance(op, n_qubits):
759
- """ Compute Haar average variance of op.
760
- """
817
+ """Compute Haar average variance of op."""
761
818
  pws = get_pauliword_list(jordan_wigner(op))
762
819
  c_sq = np.sum(list(map(lambda x: np.abs(evu.get_pauli_word_coefficient(x)) ** 2, pws)))
763
820
  return c_sq * var_avg(n_qubits)
764
821
 
822
+
765
823
  def get_pauliword_list(H: QubitOperator):
766
- """Obtain a list of pauli words in H.
767
- """
824
+ """Obtain a list of pauli words in H."""
768
825
  pws = []
769
826
  for pw, val in H.terms.items():
770
- if len(pw) != 0: pws.append(QubitOperator(term=pw, coefficient=val))
827
+ if len(pw) != 0:
828
+ pws.append(QubitOperator(term=pw, coefficient=val))
771
829
  return pws
772
830
 
773
831
 
774
832
  def solve_Axb(A, b):
775
- '''
833
+ """
776
834
  Provides asolution frox x, Ax = b.
777
- '''
835
+ """
778
836
  b_tmp = np.zeros((len(b), 1))
779
- b_tmp[:,0] = b
837
+ b_tmp[:, 0] = b
780
838
  sol = np.linalg.lstsq(A, b_tmp, rcond=None)
781
839
  x0 = sol[0]
782
840
  return x0.T[0]
783
841
 
842
+
784
843
  def modify_ops(obt, tbts, lambda_opt, O_t, nf, n, uops):
785
- '''
844
+ """
786
845
  Computes new values of obt and tbt after a single FFF iteration
787
- '''
846
+ """
788
847
  ntmp = obt.shape[0]
789
848
  new_tbts = np.zeros([nf, ntmp, ntmp, ntmp, ntmp])
790
849
  sig_o = np.zeros([ntmp, ntmp])
@@ -794,37 +853,40 @@ def modify_ops(obt, tbts, lambda_opt, O_t, nf, n, uops):
794
853
  ind = n * i + j
795
854
  obt_tmp += lambda_opt[ind] * O_t[i, j, :, :]
796
855
  sig_o += obt_tmp
797
- new_tbts[i,:,:,:,:] = tbts[i,:,:,:,:] - my_obt_to_tbt(obt_tmp,uops[i,:,:])
856
+ new_tbts[i, :, :, :, :] = tbts[i, :, :, :, :] - my_obt_to_tbt(obt_tmp, uops[i, :, :])
798
857
  new_obt = obt + sig_o
799
858
  return new_obt, new_tbts
800
859
 
860
+
801
861
  def my_obt_to_tbt(obt, uop):
802
- '''
862
+ """
803
863
  Converts the repartioned one body term into two body terms.
804
- '''
864
+ """
805
865
  nsize = obt.shape[0]
806
866
  rot_obt = np.zeros([nsize, nsize])
807
867
  tbt_from_obt = np.zeros([nsize, nsize, nsize, nsize])
808
- rot_obt = np.einsum('pa,kb,pk',np.conjugate(uop),uop,obt)
809
- if np.sum(np.abs(np.diag(np.diag(rot_obt)) - rot_obt)) > 1e-5:
868
+ rot_obt = np.einsum("pa,kb,pk", np.conjugate(uop), uop, obt)
869
+ if np.sum(np.abs(np.diag(np.diag(rot_obt)) - rot_obt)) > 1e-5:
810
870
  print("Warning:", np.sum(np.abs(np.diag(np.diag(rot_obt)) - rot_obt)))
811
- tbt_from_obt = np.einsum('al,bl,cl,dl,ll',uop, np.conjugate(uop), uop, np.conjugate(uop), rot_obt)
871
+ tbt_from_obt = np.einsum("al,bl,cl,dl,ll", uop, np.conjugate(uop), uop, np.conjugate(uop), rot_obt)
812
872
  return tbt_from_obt
813
873
 
874
+
814
875
  def modify_var(var, coo, c0, ck, lam, n, tol=1e-10):
815
- '''
876
+ """
816
877
  Computes the variances of the repartitioned fragments
817
- '''
878
+ """
818
879
  nvar = len(var)
819
880
  new_var = np.zeros(len(var))
820
881
  delta_var1 = 0.0
821
882
  for l1 in range(len(lam)):
822
883
  for l2 in range(len(lam)):
823
- delta_var1 += lam[l1] * lam[l2] * coo[l1,l2]
884
+ delta_var1 += lam[l1] * lam[l2] * coo[l1, l2]
824
885
  delta_var1 += lam[l1] * c0[l1]
825
886
  new_var[0] = var[0] + delta_var1
826
- if new_var[0] < tol: new_var[0] = 0.
827
- for k in range(nvar-1):
887
+ if new_var[0] < tol:
888
+ new_var[0] = 0.0
889
+ for k in range(nvar - 1):
828
890
  delta_var_k = 0.0
829
891
  for p in range(n):
830
892
  ind1 = n * k + p
@@ -832,14 +894,16 @@ def modify_var(var, coo, c0, ck, lam, n, tol=1e-10):
832
894
  ind2 = n * k + q
833
895
  delta_var_k += lam[ind1] * lam[ind2] * coo[ind1, ind2]
834
896
  delta_var_k -= lam[ind1] * ck[ind1]
835
- new_var[k+1] = var[k+1] + delta_var_k
836
- if new_var[k+1] < tol: new_var[k+1] = 0.
897
+ new_var[k + 1] = var[k + 1] + delta_var_k
898
+ if new_var[k + 1] < tol:
899
+ new_var[k + 1] = 0.0
837
900
  return new_var
838
901
 
902
+
839
903
  def modify_c(coo, c0, ck, lam, nf, n):
840
- '''
904
+ """
841
905
  Computes the covariances of the repartitioned fragments
842
- '''
906
+ """
843
907
  new_c0 = np.copy(c0)
844
908
  new_ck = np.copy(ck)
845
909
  for ind1 in range(len(lam)):
@@ -853,28 +917,30 @@ def modify_c(coo, c0, ck, lam, nf, n):
853
917
  new_ck[ind1] -= coo[ind1, ind2] * lam[ind2] + coo[ind2, ind1] * lam[ind2]
854
918
  return new_c0, new_ck
855
919
 
920
+
856
921
  def compute_meas_alloc(varbs, obt=None, tbts=None, n_qubits=None, mix=0.0):
857
- '''
922
+ """
858
923
  Computes the measurement allocations based on the variances of repartitioned fragments.
859
- '''
924
+ """
860
925
  if mix > 1e-6:
861
926
  all_ops = [obt_to_ferm(obt, True)]
862
927
  ops = convert_tbts_to_frags(tbts, True)
863
928
  for i in range(len(ops)):
864
929
  all_ops.append(ops[i])
865
930
  avg_vars = get_avg_variances(all_ops, n_qubits)
866
- vtmp = mix * avg_vars + (1-mix) * varbs
931
+ vtmp = mix * avg_vars + (1 - mix) * varbs
867
932
  else:
868
933
  vtmp = varbs
869
934
  sqrt_vars = np.sqrt(vtmp)
870
- meas_alloc = sqrt_vars/np.sum(sqrt_vars)
935
+ meas_alloc = sqrt_vars / np.sum(sqrt_vars)
871
936
  for i in range(len(meas_alloc)):
872
937
  if meas_alloc[i] < 1e-6:
873
938
  meas_alloc[i] = 1e-6
874
- return np.real( meas_alloc/np.sum(meas_alloc))
939
+ return np.real(meas_alloc / np.sum(meas_alloc))
940
+
875
941
 
876
942
  def depth_eff_order_mf(N):
877
- '''
943
+ """
878
944
  Returns index ordering for linear depth circuit
879
945
 
880
946
  For example N = 6 gives elimination order
@@ -884,51 +950,52 @@ def depth_eff_order_mf(N):
884
950
  [ 3. 8. 12. 0. 0. 0.]
885
951
  [ 2. 6. 11. 14. 0. 0.]
886
952
  [ 1. 4. 9. 13. 15. 0.]
887
- '''
953
+ """
888
954
  l = []
889
- for c in range(0, N-1):
955
+ for c in range(0, N - 1):
890
956
  for r in range(1, N):
891
957
  if r - c > 0:
892
- l.append([r, c, 2*c - r + N])
958
+ l.append([r, c, 2 * c - r + N])
893
959
  l.sort(key=lambda x: x[2])
894
960
  return [(a[0], a[1]) for a in l]
895
961
 
896
- def get_orb_rot(U, qubit_list = [], method = 'short', tol = 1e-12):
897
- '''
962
+
963
+ def get_orb_rot(U, qubit_list=[], method="short", tol=1e-12):
964
+ """
898
965
  Construct sequence of orbital rotations that implement mean-field unitary given by NxN unitary U
899
966
  Currently supported only for real U
900
- '''
901
-
967
+ """
968
+
902
969
  N = len(U)
903
970
  C = tq.QCircuit()
904
-
971
+
905
972
  if qubit_list == []:
906
973
  qubit_list = list(range(N))
907
-
908
- assert len(qubit_list) >= len(U), 'Insufficient qubits for orbital rotation' #check if sufficient qubits
909
-
974
+
975
+ assert len(qubit_list) >= len(U), "Insufficient qubits for orbital rotation" # check if sufficient qubits
976
+
910
977
  U[abs(U) < tol] = 0
911
978
 
912
- if method == 'naive':
979
+ if method == "naive":
913
980
  theta_list, phi_list = given_rotation(U, tol)
914
- elif method == 'short':
981
+ elif method == "short":
915
982
  ordering = depth_eff_order_mf(N)
916
983
  theta_list, phi_list = given_rotation(U, tol, ordering)
917
-
918
- #filter
984
+
985
+ # filter
919
986
  theta_list_new = []
920
987
  for i, theta in enumerate(theta_list):
921
- if abs(theta[0] % (2*np.pi)) > tol:
988
+ if abs(theta[0] % (2 * np.pi)) > tol:
922
989
  theta_list_new.append(theta)
923
-
990
+
924
991
  phi_list_new = []
925
992
  for i, phi in enumerate(phi_list):
926
993
  if abs(phi[0]) > tol:
927
994
  phi_list_new.append(phi)
928
-
995
+
929
996
  for phi in phi_list_new:
930
997
  C += n_rotation(qubit_list[phi[1]], phi[0])
931
-
998
+
932
999
  gates = []
933
1000
  for theta in theta_list_new:
934
1001
  gates.append(orbital_rotation(qubit_list[theta[1]], qubit_list[theta[2]], -theta[0]))
@@ -938,25 +1005,32 @@ def get_orb_rot(U, qubit_list = [], method = 'short', tol = 1e-12):
938
1005
  C += gate
939
1006
  return C
940
1007
 
1008
+
941
1009
  def orbital_rotation(i, j, theta):
942
- '''
1010
+ """
943
1011
  Implements exp(theta(a^_i a_j - a^_j a_i))
944
1012
  Right now restricted to |i-j| <= 1 and jordan wigner transform.
945
- '''
946
- if abs(i-j) <= 1:
947
- return tq.gates.CNOT(control=i, target=j) + tq.gates.Ry(angle=2*theta, target=i, control=j) + tq.gates.CNOT(control=i, target=j)
1013
+ """
1014
+ if abs(i - j) <= 1:
1015
+ return (
1016
+ tq.gates.CNOT(control=i, target=j)
1017
+ + tq.gates.Ry(angle=2 * theta, target=i, control=j)
1018
+ + tq.gates.CNOT(control=i, target=j)
1019
+ )
1020
+
948
1021
 
949
1022
  def n_rotation(i, phi):
950
- return tq.gates.Rz(angle = phi, target=i)
1023
+ return tq.gates.Rz(angle=phi, target=i)
951
1024
 
952
- def given_rotation(U, tol = 1e-12, ordering = None):
953
- '''
1025
+
1026
+ def given_rotation(U, tol=1e-12, ordering=None):
1027
+ """
954
1028
  Decomposes the Unitary into a set of Rz by angle phi and Givens Rotations by angle theta.
955
1029
  Input:
956
1030
  U (np.array): Rotation matrix
957
1031
  tol: tolerance for U elements
958
- '''
959
-
1032
+ """
1033
+
960
1034
  U[abs(U) < tol] = 0
961
1035
  n = U.shape[0]
962
1036
 
@@ -964,33 +1038,34 @@ def given_rotation(U, tol = 1e-12, ordering = None):
964
1038
  phi = []
965
1039
  if ordering is None:
966
1040
  for c in range(n):
967
- for r in range(n-1, c, -1):
968
- t = np.arctan2(-U[r,c], U[r-1,c])
969
- theta.append((t, r, r-1))
970
-
971
- g = givens_matrix(n,r,r-1,t)
1041
+ for r in range(n - 1, c, -1):
1042
+ t = np.arctan2(-U[r, c], U[r - 1, c])
1043
+ theta.append((t, r, r - 1))
1044
+
1045
+ g = givens_matrix(n, r, r - 1, t)
972
1046
  U = np.dot(g, U)
973
1047
  else:
974
1048
  for r, c in ordering:
975
- t = np.arctan2(-U[r,c], U[r-1,c])
976
- theta.append((t, r, r-1))
977
-
978
- g = givens_matrix(n,r,r-1,t)
1049
+ t = np.arctan2(-U[r, c], U[r - 1, c])
1050
+ theta.append((t, r, r - 1))
1051
+
1052
+ g = givens_matrix(n, r, r - 1, t)
979
1053
  U = np.dot(g, U)
980
-
1054
+
981
1055
  for i in range(n):
982
- ph = np.angle(U[i,i])
1056
+ ph = np.angle(U[i, i])
983
1057
  phi.append((ph, i))
984
-
1058
+
985
1059
  return theta, phi
986
1060
 
987
- def givens_matrix(n, p, q, theta): #verified
988
- '''
1061
+
1062
+ def givens_matrix(n, p, q, theta): # verified
1063
+ """
989
1064
  Returns the n dimension givens rotation matrix by theta between rows p and q.
990
- '''
1065
+ """
991
1066
  g = np.eye(n)
992
- g[p,p] = np.cos(theta)
993
- g[q,q] = np.cos(theta)
994
- g[p,q] = np.sin(theta)
995
- g[q,p] = - np.sin(theta)
1067
+ g[p, p] = np.cos(theta)
1068
+ g[q, q] = np.cos(theta)
1069
+ g[p, q] = np.sin(theta)
1070
+ g[q, p] = -np.sin(theta)
996
1071
  return g