Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
NVIDIA
GitHub Repository: NVIDIA/cuda-q-academic
Path: blob/main/chemistry-simulations/aux_files/krylov/qchem/uccsd.py
583 views
1
import cudaq
2
from cudaq import spin
3
4
5
def uccsd_get_excitation_list(n_electrons: int, n_qubits: int, spin_mult=0):
6
7
if n_qubits % 2 != 0:
8
raise ValueError(
9
"Total number of spin molecular orbitals (number of qubits) should be even."
10
)
11
else:
12
n_spatial_orbitals = n_qubits // 2
13
14
singles_alpha = []
15
singles_beta = []
16
doubles_mixed = []
17
doubles_alpha = []
18
doubles_beta = []
19
20
if spin_mult > 0:
21
n_occupied_beta = (n_electrons - spin_mult) // 2
22
n_occupied_alpha = n_electrons - n_occupied_beta
23
n_virtual_alpha = n_spatial_orbitals - n_occupied_alpha
24
n_virtual_beta = n_spatial_orbitals - n_occupied_beta
25
26
occupied_alpha_indices = [i * 2 for i in range(n_occupied_alpha)]
27
virtual_alpha_indices = [
28
i * 2 + n_electrons + 1 for i in range(n_virtual_alpha)
29
]
30
31
occupied_beta_indices = [i * 2 + 1 for i in range(n_occupied_beta)]
32
virtual_beta_indices = [
33
i * 2 + 1 + n_electrons - 1 for i in range(n_virtual_beta)
34
]
35
36
elif n_electrons % 2 == 0 and spin_mult == 0:
37
n_occupied = n_electrons // 2
38
n_virtual = n_spatial_orbitals - n_occupied
39
40
occupied_alpha_indices = [i * 2 for i in range(n_occupied)]
41
virtual_alpha_indices = [i * 2 + n_electrons for i in range(n_virtual)]
42
43
occupied_beta_indices = [i * 2 + 1 for i in range(n_occupied)]
44
virtual_beta_indices = [
45
i * 2 + 1 + n_electrons for i in range(n_virtual)
46
]
47
48
else:
49
raise ValueError("spin should be zero or larger than zero.")
50
51
#occupied orbital is alpha, virtual orbital is alpha
52
for p in occupied_alpha_indices:
53
for q in virtual_alpha_indices:
54
singles_alpha.append((p, q))
55
56
# occupied orbital is beta and virtual orbital is beta
57
for p in occupied_beta_indices:
58
for q in virtual_beta_indices:
59
singles_beta.append((p, q))
60
61
#Mixed spin double excitation
62
for p in occupied_alpha_indices:
63
for q in occupied_beta_indices:
64
for r in virtual_beta_indices:
65
for s in virtual_alpha_indices:
66
doubles_mixed.append((p, q, r, s))
67
68
# same spin double excitation
69
n_occ_alpha = len(occupied_alpha_indices)
70
n_occ_beta = len(occupied_beta_indices)
71
n_virt_alpha = len(virtual_alpha_indices)
72
n_virt_beta = len(virtual_beta_indices)
73
74
for p in range(n_occ_alpha - 1):
75
for q in range(p + 1, n_occ_alpha):
76
for r in range(n_virt_alpha - 1):
77
for s in range(r + 1, n_virt_alpha):
78
79
# Same spin: all alpha
80
doubles_alpha.append((occupied_alpha_indices[p],occupied_alpha_indices[q],\
81
virtual_alpha_indices[r],virtual_alpha_indices[s]))
82
83
for p in range(n_occ_beta - 1):
84
for q in range(p + 1, n_occ_beta):
85
for r in range(n_virt_beta - 1):
86
for s in range(r + 1, n_virt_beta):
87
88
# Same spin: all beta
89
doubles_beta.append((occupied_beta_indices[p],occupied_beta_indices[q],\
90
virtual_beta_indices[r],virtual_beta_indices[s]))
91
92
return singles_alpha, singles_beta, doubles_mixed, doubles_alpha, doubles_beta
93
94
95
#######################################
96
97
98
def uccsd_parameter_size(n_electrons: int, n_qubits: int, spin_mult=0):
99
# Compute the size of theta parameters for all UCCSD excitation.
100
101
singles_alpha, singles_beta, doubles_mixed, doubles_alpha, doubles_beta = \
102
uccsd_get_excitation_list(n_electrons, n_qubits, spin_mult)
103
104
length_alpha_singles = len(singles_alpha)
105
length_beta_singles = len(singles_beta)
106
length_mixed_doubles = len(doubles_mixed)
107
length_alpha_doubles = len(doubles_alpha)
108
length_beta_doubles = len(doubles_beta)
109
110
singles = length_alpha_singles + length_beta_singles
111
doubles = length_mixed_doubles + length_alpha_doubles + length_beta_doubles
112
total = singles + doubles
113
114
return singles, doubles, total
115
116
117
###################################################
118
119
120
def add_single_excitation(op, p_occ, q_virt):
121
122
parity = 1.0
123
for i in range(p_occ + 1, q_virt):
124
parity *= spin.z(i)
125
126
c = 0.5
127
op.append(c * spin.y(p_occ) * parity * spin.x(q_virt) -
128
c * spin.x(p_occ) * parity * spin.y(q_virt))
129
130
131
##################################
132
133
134
def add_double_excitation(op, p_occ, q_occ, r_virt, s_virt):
135
136
temp = []
137
if (p_occ < q_occ) and (r_virt < s_virt):
138
i_occ, j_occ = p_occ, q_occ
139
a_virt, b_virt = r_virt, s_virt
140
141
elif (p_occ > q_occ) and (r_virt > s_virt):
142
i_occ, j_occ = q_occ, p_occ
143
a_virt, b_virt = s_virt, r_virt
144
145
elif (p_occ < q_occ) and (r_virt > s_virt):
146
i_occ, j_occ = p_occ, q_occ
147
a_virt, b_virt = s_virt, r_virt
148
149
elif (p_occ > q_occ) and (r_virt < s_virt):
150
i_occ, j_occ = q_occ, p_occ
151
a_virt, b_virt = r_virt, s_virt
152
153
parity_a = 1.0
154
parity_b = 1.0
155
156
for i in range(i_occ + 1, j_occ):
157
parity_a *= spin.z(i)
158
159
for i in range(a_virt + 1, b_virt):
160
parity_b *= spin.z(i)
161
162
c = 1.0 / 8.0
163
temp_op = c * spin.x(i_occ) * parity_a * spin.x(j_occ) * spin.x(
164
a_virt) * parity_b * spin.y(b_virt)
165
temp_op += c * spin.x(i_occ) * parity_a * spin.x(j_occ) * spin.y(
166
a_virt) * parity_b * spin.x(b_virt)
167
temp_op += c * spin.x(i_occ) * parity_a * spin.y(j_occ) * spin.y(
168
a_virt) * parity_b * spin.y(b_virt)
169
temp_op += c * spin.y(i_occ) * parity_a * spin.x(j_occ) * spin.y(
170
a_virt) * parity_b * spin.y(b_virt)
171
temp_op -= c * spin.x(i_occ) * parity_a * spin.y(j_occ) * spin.x(
172
a_virt) * parity_b * spin.x(b_virt)
173
temp_op -= c * spin.y(i_occ) * parity_a * spin.x(j_occ) * spin.x(
174
a_virt) * parity_b * spin.x(b_virt)
175
temp_op -= c * spin.y(i_occ) * parity_a * spin.y(j_occ) * spin.x(
176
a_virt) * parity_b * spin.y(b_virt)
177
temp_op -= c * spin.y(i_occ) * parity_a * spin.y(j_occ) * spin.y(
178
a_virt) * parity_b * spin.x(b_virt)
179
180
op.append(temp_op)
181
182
183
########################################
184
185
186
def get_uccsd_op(nelectrons,
187
n_qubits,
188
spin_mult=0,
189
only_singles=False,
190
only_doubles=False):
191
192
singles_alpha, singles_beta, doubles_mixed, doubles_alpha, doubles_beta = \
193
uccsd_get_excitation_list(nelectrons, n_qubits, spin_mult)
194
195
n_alpha_singles = len(singles_alpha)
196
n_beta_singles = len(singles_beta)
197
n_mixed_doubles = len(doubles_mixed)
198
n_alpha_doubles = len(doubles_alpha)
199
n_beta_doubles = len(doubles_beta)
200
201
pool_op_single = []
202
pool_op_double = []
203
204
if not only_singles and not only_doubles:
205
206
for i in range(n_alpha_singles):
207
p_alpha_occ, q_alpha_virt = singles_alpha[i]
208
add_single_excitation(pool_op_single, p_alpha_occ, q_alpha_virt)
209
210
for i in range(n_beta_singles):
211
p_beta_occ, q_beta_virt = singles_beta[i]
212
add_single_excitation(pool_op_single, p_beta_occ, q_beta_virt)
213
214
for i in range(n_mixed_doubles):
215
p_alpha_occ, q_beta_occ, r_beta_virt, s_alpha_virt = doubles_mixed[
216
i]
217
add_double_excitation(pool_op_double, p_alpha_occ, q_beta_occ,
218
r_beta_virt, s_alpha_virt)
219
220
for i in range(n_alpha_doubles):
221
p_alpha_occ, q_alpha_occ, r_alpha_virt, s_alpha_virt = doubles_alpha[
222
i]
223
add_double_excitation(pool_op_double, p_alpha_occ, q_alpha_occ,
224
r_alpha_virt, s_alpha_virt)
225
226
for i in range(n_beta_doubles):
227
p_beta_occ, q_beta_occ, r_beta_virt, s_beta_virt = doubles_beta[i]
228
229
add_double_excitation(pool_op_double, p_beta_occ, q_beta_occ,
230
r_beta_virt, s_beta_virt)
231
232
elif only_singles and not only_doubles:
233
pool_op = []
234
235
for i in range(n_alpha_singles):
236
p_alpha_occ, q_alpha_virt = singles_alpha[i]
237
add_single_excitation(pool_op_single, p_alpha_occ, q_alpha_virt)
238
239
for i in range(n_beta_singles):
240
p_beta_occ, q_beta_virt = singles_beta[i]
241
add_single_excitation(pool_op_single, p_beta_occ, q_beta_virt)
242
243
elif not only_singles and only_doubles:
244
pool_op = []
245
246
for i in range(n_mixed_doubles):
247
p_alpha_occ, q_beta_occ, r_beta_virt, s_alpha_virt = doubles_mixed[
248
i]
249
add_double_excitation(pool_op_double, p_alpha_occ, q_beta_occ,
250
r_beta_virt, s_alpha_virt)
251
252
for i in range(n_alpha_doubles):
253
p_alpha_occ, q_alpha_occ, r_alpha_virt, s_alpha_virt = doubles_alpha[
254
i]
255
add_double_excitation(pool_op_double, p_alpha_occ, q_alpha_occ,
256
r_alpha_virt, s_alpha_virt)
257
258
for i in range(n_beta_doubles):
259
p_beta_occ, q_beta_occ, r_beta_virt, s_beta_virt = doubles_beta[i]
260
261
add_double_excitation(pool_op_double, p_beta_occ, q_beta_occ,
262
r_beta_virt, s_beta_virt)
263
264
# Convert the list of operators to cudaq.`pauli_word`
265
word_single = []
266
word_double = []
267
coef_single = []
268
coef_double = []
269
270
for i in range(len(pool_op_single)):
271
272
op_i = pool_op_single[i]
273
for term in op_i:
274
coef_single.append(term.evaluate_coefficient().real)
275
word_single.append(term.get_pauli_word(n_qubits))
276
277
for i in range(len(pool_op_double)):
278
279
op_i = pool_op_double[i]
280
for term in op_i:
281
coef_double.append(term.evaluate_coefficient().real)
282
word_double.append(term.get_pauli_word(n_qubits))
283
284
# Return the list of operators
285
if only_singles and not only_doubles:
286
return word_single, coef_single
287
288
elif not only_singles and only_doubles:
289
return word_double, coef_double
290
291
elif not only_singles and not only_doubles:
292
return word_single, word_double, coef_single, coef_double
293
294
295
###################################################
296
297
298
@cudaq.kernel
299
def uccsd_circuit_single(qubits: cudaq.qview, theta: list[float],
300
words_single: list[cudaq.pauli_word],
301
coef_single: list[float]):
302
"""
303
UCCSD circuit implementation for only single excitation.
304
"""
305
306
n_qubits = qubits.size()
307
308
count = 0
309
for i in range(0, len(words_single), 2):
310
exp_pauli(coef_single[i] * theta[count], qubits, words_single[i])
311
exp_pauli(coef_single[i + 1] * theta[count], qubits,
312
words_single[i + 1])
313
count += 1
314
315
316
@cudaq.kernel
317
def uccsd_circuit_double(qubits: cudaq.qview, theta: list[float],
318
words_double: list[cudaq.pauli_word],
319
coef_double: list[float]):
320
"""
321
UCCSD circuit implementation for only double excitation.
322
"""
323
324
n_qubits = qubits.size()
325
326
count = 0
327
for i in range(0, len(words_double), 8):
328
exp_pauli(coef_double[i] * theta[count], qubits, words_double[i])
329
exp_pauli(coef_double[i + 1] * theta[count], qubits,
330
words_double[i + 1])
331
exp_pauli(coef_double[i + 2] * theta[count], qubits,
332
words_double[i + 2])
333
exp_pauli(coef_double[i + 3] * theta[count], qubits,
334
words_double[i + 3])
335
exp_pauli(coef_double[i + 4] * theta[count], qubits,
336
words_double[i + 4])
337
exp_pauli(coef_double[i + 5] * theta[count], qubits,
338
words_double[i + 5])
339
exp_pauli(coef_double[i + 6] * theta[count], qubits,
340
words_double[i + 6])
341
exp_pauli(coef_double[i + 7] * theta[count], qubits,
342
words_double[i + 7])
343
count += 1
344
345
346
@cudaq.kernel
347
def uccsd_circuit(qubits: cudaq.qview, theta: list[float],
348
words_single: list[cudaq.pauli_word],
349
coef_single: list[float],
350
words_double: list[cudaq.pauli_word],
351
coef_double: list[float]):
352
"""
353
UCCSD circuit implementation for both single and double excitation.
354
"""
355
356
n_qubits = qubits.size()
357
358
count = 0
359
for i in range(0, len(words_single), 2):
360
exp_pauli(coef_single[i] * theta[count], qubits, words_single[i])
361
exp_pauli(coef_single[i + 1] * theta[count], qubits,
362
words_single[i + 1])
363
count += 1
364
365
for i in range(0, len(words_double), 8):
366
exp_pauli(coef_double[i] * theta[count], qubits, words_double[i])
367
exp_pauli(coef_double[i + 1] * theta[count], qubits,
368
words_double[i + 1])
369
exp_pauli(coef_double[i + 2] * theta[count], qubits,
370
words_double[i + 2])
371
exp_pauli(coef_double[i + 3] * theta[count], qubits,
372
words_double[i + 3])
373
exp_pauli(coef_double[i + 4] * theta[count], qubits,
374
words_double[i + 4])
375
exp_pauli(coef_double[i + 5] * theta[count], qubits,
376
words_double[i + 5])
377
exp_pauli(coef_double[i + 6] * theta[count], qubits,
378
words_double[i + 6])
379
exp_pauli(coef_double[i + 7] * theta[count], qubits,
380
words_double[i + 7])
381
count += 1
382
383