Gyanateet Dutta
Fix Space loading: direct Streamlit, lazy imports, ReNova page, fix deps
9f8cf99
Raw
History Blame Contribute Delete
6.06 kB
import time
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
from qiskit import QuantumCircuit, transpile
from qiskit_aer import Aer
import qhybrid_kernels
import os
def numpy_statevector_simulator(qc: QuantumCircuit):
"""A very simple pure NumPy statevector simulator for comparison."""
n_qubits = qc.num_qubits
state = np.zeros(2**n_qubits, dtype=complex)
state[0] = 1.0
# Transpile to basic gates
qc_decomposed = transpile(qc, basis_gates=['u', 'cx'], optimization_level=0)
for instruction in qc_decomposed.data:
gate = instruction.operation
qubits = [qc_decomposed.find_bit(q).index for q in instruction.qubits]
if gate.name == 'u':
theta, phi, lam = gate.params
u_mat = np.array([
[np.cos(theta/2), -np.exp(1j*lam)*np.sin(theta/2)],
[np.exp(1j*phi)*np.sin(theta/2), np.exp(1j*(phi+lam))*np.cos(theta/2)]
])
state = apply_gate(state, u_mat, qubits[0], n_qubits)
elif gate.name == 'cx':
cx_mat = np.array([
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 0, 1],
[0, 0, 1, 0]
])
state = apply_2q_gate(state, cx_mat, qubits[0], qubits[1], n_qubits)
return state
def apply_gate(state, gate_mat, qubit, n_qubits):
state = state.reshape([2]*(n_qubits))
target_axis = n_qubits - 1 - qubit
state = np.tensordot(gate_mat, state, axes=([1], [target_axis]))
state = np.moveaxis(state, 0, target_axis)
return state.flatten()
def apply_2q_gate(state, gate_mat, q_ctrl, q_trgt, n_qubits):
state = state.reshape([2]*(n_qubits))
ctrl_axis = n_qubits - 1 - q_ctrl
trgt_axis = n_qubits - 1 - q_trgt
gate_mat = gate_mat.reshape((2, 2, 2, 2))
state = np.tensordot(gate_mat, state, axes=((2, 3), (ctrl_axis, trgt_axis)))
state = np.moveaxis(state, [0, 1], [ctrl_axis, trgt_axis])
return state.flatten()
def run_benchmark():
qubit_range = range(4, 27) # Stress test up to 26 qubits
results = []
print(f"{'Qubits':<10} | {'Rust (ms)':<12} | {'NumPy (ms)':<12} | {'Aer-CPU (ms)':<12} | {'Aer-GPU (ms)':<12}")
print("-" * 75)
numpy_too_slow = False
# Backends
backend_cpu = Aer.get_backend('statevector_simulator')
backend_gpu = Aer.get_backend('statevector_simulator')
backend_gpu.set_options(device='GPU')
# We'll try to enable cuStateVec if possible
backend_custatevec = Aer.get_backend('statevector_simulator')
try:
backend_custatevec.set_options(device='GPU', cuStateVec_enable=True)
custatevec_works = True
except Exception:
custatevec_works = False
for n_qubits in qubit_range:
qc = QuantumCircuit(n_qubits)
for i in range(n_qubits): qc.h(i)
for i in range(n_qubits - 1): qc.cx(i, i+1)
# 1. Rust Benchmark
circuit_json = qhybrid_kernels.qiskit_to_qhybrid_json(qc)
start = time.perf_counter()
qhybrid_kernels.execute_quantum_circuit(circuit_json)
rust_time = (time.perf_counter() - start) * 1000
# 2. NumPy Benchmark
numpy_time = None
if not numpy_too_slow:
try:
start = time.perf_counter()
numpy_statevector_simulator(qc)
numpy_time = (time.perf_counter() - start) * 1000
if numpy_time > 15000:
numpy_too_slow = True
except Exception:
numpy_too_slow = True
# 3. Aer CPU Benchmark
start = time.perf_counter()
backend_cpu.run(transpile(qc, backend_cpu)).result()
aer_cpu_time = (time.perf_counter() - start) * 1000
# 4. Aer GPU Benchmark
start = time.perf_counter()
backend_gpu.run(transpile(qc, backend_gpu)).result()
aer_gpu_time = (time.perf_counter() - start) * 1000
# 5. Aer cuStateVec Benchmark (Optional)
aer_cusv_time = None
if custatevec_works:
start = time.perf_counter()
backend_custatevec.run(transpile(qc, backend_custatevec)).result()
aer_cusv_time = (time.perf_counter() - start) * 1000
numpy_str = f"{numpy_time:.2f}" if numpy_time else "SKIP"
print(f"{n_qubits:<10} | {rust_time:<12.2f} | {numpy_str:<12} | {aer_cpu_time:<12.2f} | {aer_gpu_time:<12.2f}")
results.append({
'qubits': n_qubits,
'rust': rust_time,
'numpy': numpy_time,
'aer_cpu': aer_cpu_time,
'aer_gpu': aer_gpu_time,
'aer_cusv': aer_cusv_time
})
df = pd.DataFrame(results)
# Plotting
plt.figure(figsize=(12, 8))
# Filter valid results for plotting
valid_numpy = df.dropna(subset=['numpy'])
plt.plot(valid_numpy['qubits'], valid_numpy['numpy'], label='NumPy (Naive)', marker='o', linestyle='--')
plt.plot(df['qubits'], df['aer_cpu'], label='Qiskit Aer (CPU)', marker='s', linestyle='-')
plt.plot(df['qubits'], df['aer_gpu'], label='Qiskit Aer (GPU)', marker='d', linestyle='-')
if custatevec_works:
valid_cusv = df.dropna(subset=['aer_cusv'])
plt.plot(valid_cusv['qubits'], valid_cusv['aer_cusv'], label='Qiskit Aer (cuStateVec)', marker='x', linestyle='-.')
plt.plot(df['qubits'], df['rust'], label='qhybrid (Rust - CPU)', marker='^', linestyle='-', linewidth=2, color='green')
plt.yscale('log')
plt.xlabel('Number of Qubits')
plt.ylabel('Execution Time (ms) - Log Scale')
plt.title('HPC Quantum Simulator Comparison (CPU vs GPU vs Rust)')
plt.legend()
plt.grid(True, which="both", ls="-", alpha=0.5)
os.makedirs('docs/assets', exist_ok=True)
plt.savefig('docs/assets/speedup_hpc.png', dpi=300)
print(f"\nHPC benchmark chart saved to docs/assets/speedup_hpc.png")
if __name__ == "__main__":
run_benchmark()