-
Notifications
You must be signed in to change notification settings - Fork 81
/
Copy pathvqeh2o_benchmark.py
158 lines (123 loc) · 3.91 KB
/
vqeh2o_benchmark.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
"""
Time comparison for different evaluation approach on molecule VQE
"""
import os
os.environ["TF_FORCE_GPU_ALLOW_GROWTH"] = "true"
# one need this for jax+gpu combination in some cases
import time
import numpy as np
from openfermion.chem import MolecularData
from openfermion.transforms import (
get_fermion_operator,
binary_code_transform,
checksum_code,
reorder,
)
from openfermion.chem import geometry_from_pubchem
from openfermion.utils import up_then_down
from openfermionpyscf import run_pyscf
import tensorcircuit as tc
K = tc.set_backend("tensorflow")
n = 12
nlayers = 2
multiplicity = 1
basis = "sto-3g"
# 14 spin orbitals for H2O
geometry = geometry_from_pubchem("h2o")
description = "h2o"
molecule = MolecularData(geometry, basis, multiplicity, description=description)
molecule = run_pyscf(molecule, run_mp2=True, run_cisd=True, run_ccsd=True, run_fci=True)
mh = molecule.get_molecular_hamiltonian()
fh = get_fermion_operator(mh)
b = binary_code_transform(reorder(fh, up_then_down), 2 * checksum_code(7, 1))
lsb, wb = tc.templates.chems.get_ps(b, 12)
print("%s terms in H2O qubit Hamiltonian" % len(wb))
mb = tc.quantum.PauliStringSum2COO_numpy(lsb, wb)
mbd = mb.todense()
mb = K.coo_sparse_matrix(
np.transpose(np.stack([mb.row, mb.col])), mb.data, shape=(2**n, 2**n)
)
mbd = tc.array_to_tensor(mbd)
def ansatz(param):
c = tc.Circuit(n)
for i in [0, 1, 2, 3, 4, 6, 7, 8, 9, 10]:
c.X(i)
for j in range(nlayers):
for i in range(n - 1):
c.cz(i, i + 1)
for i in range(n):
c.rx(i, theta=param[j, i])
return c
def benchmark(vqef, tries=3):
if tries < 0:
vagf = K.value_and_grad(vqef)
else:
vagf = K.jit(K.value_and_grad(vqef))
time0 = time.time()
v, g = vagf(K.zeros([nlayers, n]))
time1 = time.time()
for _ in range(tries):
v, g = vagf(K.zeros([nlayers, n]))
time2 = time.time()
print("staging time: ", time1 - time0)
if tries > 0:
print("running time: ", (time2 - time1) / tries)
return (v, g), (time1 - time0, (time2 - time1) / tries)
# 1. plain Pauli sum
def vqe1(param):
c = ansatz(param)
loss = 0.0
for ps, w in zip(lsb, wb):
obs = []
for i, p in enumerate(ps):
if p == 1:
obs.append([tc.gates.x(), [i]])
elif p == 2:
obs.append([tc.gates.y(), [i]])
elif p == 3:
obs.append([tc.gates.z(), [i]])
loss += w * c.expectation(*obs)
return K.real(loss)
# 2. vmap the Pauli sum
def measurement(s, structure):
c = tc.Circuit(n, inputs=s)
return tc.templates.measurements.parameterized_measurements(
c, structure, onehot=True
)
measurement = K.jit(K.vmap(measurement, vectorized_argnums=1))
structures = tc.array_to_tensor(lsb)
weights = tc.array_to_tensor(wb, dtype="float32")
def vqe2(param):
c = ansatz(param)
s = c.state()
ms = measurement(s, structures)
return K.sum(ms * weights)
# 3. dense matrix
def vqe3(param):
c = ansatz(param)
return tc.templates.measurements.operator_expectation(c, mbd)
# 4. sparse matrix
def vqe4(param):
c = ansatz(param)
return tc.templates.measurements.operator_expectation(c, mb)
# 5. mpo (ommited, since it is not that applicable for molecule/long range Hamiltonian
# due to large bond dimension)
if __name__ == "__main__":
r0 = None
des = [
"plain Pauli sum",
"vmap Pauli sum",
"dense Hamiltonian matrix",
"sparse Hamiltonian matrix",
]
vqef = [vqe1, vqe2, vqe3, vqe4]
tries = [-1, 2, 5, 5]
for i in range(4):
print(des[i])
r1, _ = benchmark(vqef[i], tries=tries[i])
# plain approach takes too long to jit
if r0 is not None:
np.testing.assert_allclose(r0[0], r1[0], atol=1e-5)
np.testing.assert_allclose(r0[1], r1[1], atol=1e-5)
r0 = r1
print("------------------")