Skip to content

Commit 1da4b8c

Browse files
rcs_nn_tn
1 parent 3b39bf3 commit 1da4b8c

File tree

1 file changed

+221
-0
lines changed

1 file changed

+221
-0
lines changed
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
# How good are Google's own "patch circuits" and "elided circuits" as a direct XEB approximation to full Sycamore circuits?
2+
# (Are they better than the 2019 Sycamore hardware?)
3+
4+
import math
5+
import operator
6+
import random
7+
import statistics
8+
import sys
9+
10+
from collections import Counter
11+
12+
from scipy.stats import binom
13+
14+
from pyqrack import QrackSimulator
15+
16+
from qiskit import QuantumCircuit
17+
from qiskit_aer.backends import AerSimulator
18+
from qiskit.quantum_info import Statevector
19+
20+
import quimb.tensor as tn
21+
from qiskit_quimb import quimb_circuit
22+
23+
24+
# Function by Google search AI
25+
def int_to_bitstring(integer, length):
26+
return (bin(integer)[2:].zfill(length))[::-1]
27+
28+
29+
def factor_width(width):
30+
col_len = math.floor(math.sqrt(width))
31+
while ((width // col_len) * col_len) != width:
32+
col_len -= 1
33+
row_len = width // col_len
34+
if col_len == 1:
35+
raise Exception("ERROR: Can't simulate prime number width!")
36+
37+
return (row_len, col_len)
38+
39+
40+
def cx(sim, q1, q2):
41+
sim.cx(q1, q2)
42+
43+
44+
def cy(sim, q1, q2):
45+
sim.cy(q1, q2)
46+
47+
48+
def cz(sim, q1, q2):
49+
sim.cz(q1, q2)
50+
51+
52+
def acx(sim, q1, q2):
53+
sim.x(q1)
54+
sim.cx(q1, q2)
55+
sim.x(q1)
56+
57+
58+
def acy(sim, q1, q2):
59+
sim.x(q1)
60+
sim.cy(q1, q2)
61+
sim.x(q1)
62+
63+
64+
def acz(sim, q1, q2):
65+
sim.x(q1)
66+
sim.cz(q1, q2)
67+
sim.x(q1)
68+
69+
70+
def bench_qrack(width, depth, sdrp):
71+
lcv_range = range(width)
72+
all_bits = list(lcv_range)
73+
retained = width * width
74+
shots = retained * width
75+
gateSequence = [0, 3, 2, 1, 2, 1, 0, 3]
76+
two_bit_gates = cx, cy, cz, acx, acy, acz
77+
row_len, col_len = factor_width(width)
78+
79+
rcs = QuantumCircuit(width)
80+
for d in range(depth):
81+
# Single-qubit gates
82+
for i in lcv_range:
83+
for b in range(3):
84+
rcs.h(i)
85+
rcs.rz(random.uniform(0, 2 * math.pi), i)
86+
87+
# Nearest-neighbor couplers:
88+
############################
89+
gate = gateSequence.pop(0)
90+
gateSequence.append(gate)
91+
for row in range(1, row_len, 2):
92+
for col in range(col_len):
93+
temp_row = row
94+
temp_col = col
95+
temp_row = temp_row + (1 if (gate & 2) else -1)
96+
temp_col = temp_col + (1 if (gate & 1) else 0)
97+
98+
if temp_row < 0:
99+
temp_row = temp_row + row_len
100+
if temp_col < 0:
101+
temp_col = temp_col + col_len
102+
if temp_row >= row_len:
103+
temp_row = temp_row - row_len
104+
if temp_col >= col_len:
105+
temp_col = temp_col - col_len
106+
107+
b1 = row * row_len + col
108+
b2 = temp_row * row_len + temp_col
109+
110+
if (b1 >= width) or (b2 >= width):
111+
continue
112+
113+
if d & 1:
114+
t = b1
115+
b1 = b2
116+
b2 = t
117+
118+
g = random.choice(two_bit_gates)
119+
g(rcs, b1, b2)
120+
121+
experiment = QrackSimulator(width, isTensorNetwork=False, isSparse=True, isOpenCL=False)
122+
if sdrp > 0:
123+
experiment.set_sdrp(sdrp)
124+
experiment.run_qiskit_circuit(rcs)
125+
experiment_counts = dict(Counter(experiment.measure_shots(all_bits, shots)))
126+
experiment_counts = sorted(experiment_counts.items(), key=operator.itemgetter(1))
127+
128+
quimb_rcs = quimb_circuit(rcs)
129+
n_pow = 1 << width
130+
u_u = 1 / n_pow
131+
idx = 0
132+
ideal_probs = {}
133+
sum_probs = 0
134+
for count_tuple in experiment_counts:
135+
key = count_tuple[0]
136+
prob = abs(quimb_rcs.amplitude(int_to_bitstring(key, width), backend="jax")) ** 2
137+
if prob <= u_u:
138+
continue
139+
val = count_tuple[1]
140+
ideal_probs[key] = val
141+
sum_probs += val
142+
if len(ideal_probs) >= retained:
143+
break
144+
145+
for key in ideal_probs.keys():
146+
ideal_probs[key] = ideal_probs[key] / sum_probs
147+
148+
rcs.save_statevector()
149+
control = AerSimulator(method="statevector")
150+
job = control.run(rcs)
151+
control_probs = Statevector(job.result().get_statevector()).probabilities()
152+
153+
return calc_stats(control_probs, ideal_probs, shots, depth)
154+
155+
156+
def calc_stats(ideal_probs, exp_probs, shots, depth):
157+
# For QV, we compare probabilities of (ideal) "heavy outputs."
158+
# If the probability is above 2/3, the protocol certifies/passes the qubit width.
159+
n_pow = len(ideal_probs)
160+
n = int(round(math.log2(n_pow)))
161+
mean_guess = 1 / n_pow
162+
model = 1 / math.sqrt(n)
163+
threshold = statistics.median(ideal_probs)
164+
u_u = statistics.mean(ideal_probs)
165+
numer = 0
166+
denom = 0
167+
sum_hog_counts = 0
168+
sqr_diff = 0
169+
for i in range(n_pow):
170+
exp = (1 - model) * (exp_probs[i] if i in exp_probs else 0) + model * mean_guess
171+
ideal = ideal_probs[i]
172+
173+
# XEB / EPLG
174+
denom += (ideal - u_u) ** 2
175+
numer += (ideal - u_u) * (exp - u_u)
176+
177+
# L2 norm
178+
sqr_diff += (ideal - exp) ** 2
179+
180+
# QV / HOG
181+
if ideal > threshold:
182+
sum_hog_counts += exp * shots
183+
184+
hog_prob = sum_hog_counts / shots
185+
xeb = numer / denom
186+
# p-value of heavy output count, if method were actually 50/50 chance of guessing
187+
p_val = (
188+
(1 - binom.cdf(sum_hog_counts - 1, shots, 1 / 2)) if sum_hog_counts > 0 else 1
189+
)
190+
rss = math.sqrt(sqr_diff)
191+
192+
return {
193+
"qubits": n,
194+
"depth": depth,
195+
"xeb": float(xeb),
196+
"hog_prob": float(hog_prob),
197+
"l2_diff": float(rss),
198+
"p-value": float(p_val),
199+
}
200+
201+
202+
def main():
203+
if len(sys.argv) < 3:
204+
raise RuntimeError(
205+
"Usage: python3 fc_qiskit_validation.py [width] [depth] [trials]"
206+
)
207+
208+
width = int(sys.argv[1])
209+
depth = int(sys.argv[2])
210+
sdrp = 0
211+
if len(sys.argv) > 3:
212+
sdrp = float(sys.argv[3])
213+
214+
# Run the benchmarks
215+
print(bench_qrack(width, depth, sdrp))
216+
217+
return 0
218+
219+
220+
if __name__ == "__main__":
221+
sys.exit(main())

0 commit comments

Comments
 (0)