Skip to content

Commit ce1e5f0

Browse files
Improve TFIM validation
1 parent 7591720 commit ce1e5f0

File tree

2 files changed

+44
-101
lines changed

2 files changed

+44
-101
lines changed

scripts/tfim_validation_qiskit.py

Lines changed: 28 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ def calc_stats(n_rows, n_cols, ideal_probs, bias, depth):
9595
diff_sqr = 0
9696
z_fidelity = 0
9797
sum_hog_counts = 0
98+
magnetization = 0
99+
sqr_magnetization = 0
98100
# total = 0
99101
# hamming_bias = [0.0] * (n + 1)
100102
for i in range(n_pow):
@@ -113,6 +115,10 @@ def calc_stats(n_rows, n_cols, ideal_probs, bias, depth):
113115
# You can make sure this still adds up to 1.0, to show the distribution is normalized:
114116
# total += count
115117

118+
mag = 1.0 - 2 * hamming_weight / n
119+
magnetization += count * mag
120+
sqr_magnetization += count * mag * mag
121+
116122
# QV / HOG
117123
if ideal > threshold:
118124
sum_hog_counts += count
@@ -141,6 +147,8 @@ def calc_stats(n_rows, n_cols, ideal_probs, bias, depth):
141147
"z_fidelity": float(z_fidelity),
142148
"hog_prob": sum_hog_counts,
143149
"xeb": xeb,
150+
"magnetization": magnetization,
151+
"sqr_magnetization": sqr_magnetization
144152
}
145153

146154

@@ -278,22 +286,9 @@ def main():
278286
job = control.run(qc_aer_sv)
279287
control_probs = Statevector(job.result().get_statevector()).probabilities()
280288

281-
n_bias = n_qubits + 1
282289
bias_0 = get_tfim_hamming_distribution(J=J, h=h, z=z, theta=theta, t=0, n_qubits=n_qubits)
283-
qrack_probs = dict(Counter(generate_tfim_samples(J=J, h=h, z=z, theta=theta, t=0, n_qubits=n_qubits, shots=shots)))
284-
for key in qrack_probs.keys():
285-
qrack_probs[key] /= shots
286-
287-
sqr_magnetization_0 = 0
288-
for key, value in qrack_probs.items():
289-
m = 0
290-
for _ in range(n_qubits):
291-
m += -1 if key & 1 else 1
292-
key >>= 1
293-
m /= n_qubits
294-
sqr_magnetization_0 += value * m * m
295290

296-
c_sqr_magnetization = 0, 0
291+
c_sqr_magnetization = 0
297292
for p in range(1 << n_qubits):
298293
perm = p
299294
m = 0
@@ -303,10 +298,6 @@ def main():
303298
m /= n_qubits
304299
c_sqr_magnetization += control_probs[p] * m * m
305300

306-
# Save the sum of squares and sum of square residuals on the magnetization curve values.
307-
ss = c_sqr_magnetization**2
308-
ssr = (c_sqr_magnetization - sqr_magnetization_0) ** 2
309-
310301
result = calc_stats(
311302
n_rows,
312303
n_cols,
@@ -317,11 +308,15 @@ def main():
317308

318309
# Add up the square residuals:
319310
r_squared = result["l2_difference"] ** 2
311+
sqr_magnetization_0 = result["sqr_magnetization"]
312+
313+
# Save the sum of squares and sum of square residuals on the magnetization curve values.
314+
ss = c_sqr_magnetization**2
315+
ssr = (c_sqr_magnetization - sqr_magnetization_0) ** 2
320316

321-
r_squared = 0
322-
ss = 0
323-
ssr = 0
324317
for d in range(1, depth + 1):
318+
t = d * dt
319+
325320
# For each depth step, we append an additional Trotter step to Aer's circuit.
326321
trotter_step(qc_aer, qubits, (n_rows, n_cols), J, h, dt)
327322

@@ -335,36 +330,20 @@ def main():
335330
job = control.run(qc_aer_sv)
336331
control_probs = Statevector(job.result().get_statevector()).probabilities()
337332

338-
# This section calculates the geometric series weight per Hamming weight, with oscillating time dependence.
339-
# The mean-field ground state is encapsulated as a multiplier on the geometric series exponent.
340-
# Additionally, this same mean-field exponent is the amplitude of time-dependent oscillation (also in the geometric series exponent).
341-
t = d * dt
342-
# Determine how to weight closed-form vs. conventional simulation contributions:
343-
model = (1 - math.exp(-t / t1)) if (t1 > 0) else 1
344-
345-
# "p" is the exponent of the geometric series weighting, for (n+1) dimensions of Hamming weight.
346-
# Notice that the expected symmetries are respected under reversal of signs of J and/or h.
347-
zJ = z * J
348-
theta_c = ((np.pi if J > 0 else -np.pi) / 2) if abs(zJ) <= sys.float_info.epsilon else np.arcsin(max(-1.0, min(1.0, h / zJ)))
349-
350333
# The magnetization components are weighted by (n+1) symmetric "bias" terms over possible Hamming weights.
351334
bias = get_tfim_hamming_distribution(J=J, h=h, z=z, theta=theta, t=t, n_qubits=n_qubits)
352-
qrack_probs = dict(Counter(generate_tfim_samples(J=J, h=h, z=z, theta=theta, t=t, n_qubits=n_qubits, shots=shots)))
353-
for key in qrack_probs.keys():
354-
qrack_probs[key] /= shots
355-
356-
d_sqr_magnetization = 0
357-
for key, value in qrack_probs.items():
358-
m = 0
359-
for _ in range(n_qubits):
360-
m += -1 if key & 1 else 1
361-
key >>= 1
362-
m /= n_qubits
363-
d_sqr_magnetization += value * m * m
364335

365-
# Mix in the initial state component.
366-
bias = model * bias + (1 - model) * bias_0
367-
sqr_magnetization = model * d_sqr_magnetization + (1 - model) * sqr_magnetization_0
336+
# The full 2^n marginal probabilities will be produced in the statistics calculation,
337+
# but notice that the global magnetization value only requires (n+1) dimensions of marginal probability,
338+
# the marginal probability per each Hilbert space basis dimension is trivial to calculate by closed form,
339+
# and we simply need to be thoughtful in how to extract expectation values to maximize similar symmetries.
340+
result = calc_stats(
341+
n_rows,
342+
n_cols,
343+
control_probs,
344+
bias,
345+
d
346+
)
368347

369348
# The full 2^n marginal probabilities will be produced in the statistics calculation,
370349
# but notice that the global magnetization value only requires (n+1) dimensions of marginal probability,
@@ -380,6 +359,7 @@ def main():
380359

381360
# Add up the square residuals:
382361
r_squared += result["l2_difference"] ** 2
362+
sqr_magnetization = result["sqr_magnetization"]
383363

384364
# Calculate the "control-case" magnetization values, from Aer's samples.
385365
c_magnetization, c_sqr_magnetization = 0, 0

scripts/tfim_validation_qrack.py

Lines changed: 16 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ def calc_stats(n_rows, n_cols, ideal_probs, bias, depth):
9494
diff_sqr = 0
9595
z_fidelity = 0
9696
sum_hog_counts = 0
97+
magnetization = 0
98+
sqr_magnetization = 0
9799
# total = 0
98100
# hamming_bias = [0.0] * (n + 1)
99101
for i in range(n_pow):
@@ -112,6 +114,10 @@ def calc_stats(n_rows, n_cols, ideal_probs, bias, depth):
112114
# You can make sure this still adds up to 1.0, to show the distribution is normalized:
113115
# total += count
114116

117+
mag = 1.0 - 2 * hamming_weight / n
118+
magnetization += count * mag
119+
sqr_magnetization += count * mag * mag
120+
115121
# QV / HOG
116122
if ideal > threshold:
117123
sum_hog_counts += count
@@ -140,6 +146,8 @@ def calc_stats(n_rows, n_cols, ideal_probs, bias, depth):
140146
"z_fidelity": float(z_fidelity),
141147
"hog_prob": sum_hog_counts,
142148
"xeb": xeb,
149+
"magnetization": magnetization,
150+
"sqr_magnetization": sqr_magnetization
143151
}
144152

145153

@@ -218,8 +226,6 @@ def main():
218226
n_qubits = 8
219227
depth = 20
220228
t1 = 0
221-
t2 = 1
222-
omega = 1.5
223229

224230
# Quantinuum settings
225231
J, h, dt, z = -1.0, 2.0, 0.25, 4
@@ -255,10 +261,7 @@ def main():
255261
trials = 8 if t1 > 0 else 1
256262

257263
print("t1: " + str(t1))
258-
print("t2: " + str(t2))
259-
print("omega / pi: " + str(omega))
260264

261-
omega *= math.pi
262265
n_rows, n_cols = factor_width(n_qubits, False)
263266
qubits = list(range(n_qubits))
264267

@@ -276,20 +279,7 @@ def main():
276279
control.run_qiskit_circuit(qc_aer)
277280
control_probs = control.out_probs()
278281

279-
n_bias = n_qubits + 1
280282
bias_0 = get_tfim_hamming_distribution(J=J, h=h, z=z, theta=theta, t=0, n_qubits=n_qubits)
281-
qrack_probs = dict(Counter(generate_tfim_samples(J=J, h=h, z=z, theta=theta, t=0, n_qubits=n_qubits, shots=shots)))
282-
for key in qrack_probs.keys():
283-
qrack_probs[key] /= shots
284-
285-
sqr_magnetization_0 = 0
286-
for key, value in qrack_probs.items():
287-
m = 0
288-
for _ in range(n_qubits):
289-
m += -1 if key & 1 else 1
290-
key >>= 1
291-
m /= n_qubits
292-
sqr_magnetization_0 += value * m * m
293283

294284
c_sqr_magnetization = 0
295285
for p in range(1 << n_qubits):
@@ -301,10 +291,6 @@ def main():
301291
m /= n_qubits
302292
c_sqr_magnetization += control_probs[p] * m * m
303293

304-
# Save the sum of squares and sum of square residuals on the magnetization curve values.
305-
ss = c_sqr_magnetization**2
306-
ssr = (c_sqr_magnetization - sqr_magnetization_0) ** 2
307-
308294
result = calc_stats(
309295
n_rows,
310296
n_cols,
@@ -315,6 +301,11 @@ def main():
315301

316302
# Add up the square residuals:
317303
r_squared = result["l2_difference"] ** 2
304+
sqr_magnetization_0 = result["sqr_magnetization"]
305+
306+
# Save the sum of squares and sum of square residuals on the magnetization curve values.
307+
ss = c_sqr_magnetization**2
308+
ssr = (c_sqr_magnetization - sqr_magnetization_0) ** 2
318309

319310
qc_aer = QuantumCircuit(n_qubits)
320311
trotter_step(qc_aer, qubits, (n_rows, n_cols), J, h, dt)
@@ -325,44 +316,15 @@ def main():
325316
basis_gates=basis_gates
326317
)
327318

328-
r_squared = 0
329-
ss = 0
330-
ssr = 0
331319
for d in range(1, depth + 1):
320+
t = d * dt
321+
332322
# For each depth step, we append an additional Trotter step to Aer's circuit.
333323
control.run_qiskit_circuit(qc_aer)
334324
control_probs = control.out_probs()
335325

336-
# This section calculates the geometric series weight per Hamming weight, with oscillating time dependence.
337-
# The mean-field ground state is encapsulated as a multiplier on the geometric series exponent.
338-
# Additionally, this same mean-field exponent is the amplitude of time-dependent oscillation (also in the geometric series exponent).
339-
t = d * dt
340-
# Determine how to weight closed-form vs. conventional simulation contributions:
341-
model = (1 - math.exp(-t / t1)) if (t1 > 0) else 1
342-
343-
# "p" is the exponent of the geometric series weighting, for (n+1) dimensions of Hamming weight.
344-
# Notice that the expected symmetries are respected under reversal of signs of J and/or h.
345-
zJ = z * J
346-
theta_c = ((np.pi if J > 0 else -np.pi) / 2) if abs(zJ) <= sys.float_info.epsilon else np.arcsin(max(-1.0, min(1.0, h / zJ)))
347-
348326
# The magnetization components are weighted by (n+1) symmetric "bias" terms over possible Hamming weights.
349327
bias = get_tfim_hamming_distribution(J=J, h=h, z=z, theta=theta, t=t, n_qubits=n_qubits)
350-
qrack_probs = dict(Counter(generate_tfim_samples(J=J, h=h, z=z, theta=theta, t=t, n_qubits=n_qubits, shots=shots)))
351-
for key in qrack_probs.keys():
352-
qrack_probs[key] /= shots
353-
354-
d_sqr_magnetization = 0
355-
for key, value in qrack_probs.items():
356-
m = 0
357-
for _ in range(n_qubits):
358-
m += -1 if key & 1 else 1
359-
key >>= 1
360-
m /= n_qubits
361-
d_sqr_magnetization += value * m * m
362-
363-
# Mix in the initial state component.
364-
bias = model * bias + (1 - model) * bias_0
365-
sqr_magnetization = model * d_sqr_magnetization + (1 - model) * sqr_magnetization_0
366328

367329
# The full 2^n marginal probabilities will be produced in the statistics calculation,
368330
# but notice that the global magnetization value only requires (n+1) dimensions of marginal probability,
@@ -378,6 +340,7 @@ def main():
378340

379341
# Add up the square residuals:
380342
r_squared += result["l2_difference"] ** 2
343+
sqr_magnetization = result["sqr_magnetization"]
381344

382345
# Calculate the "control-case" magnetization values, from Aer's samples.
383346
c_magnetization, c_sqr_magnetization = 0, 0

0 commit comments

Comments
 (0)