| """ |
| GQE Multi-Backend Comparison for H2 Molecule |
| Compares Rust (qhybrid) vs Python implementations using different simulators |
| """ |
| import time |
| import numpy as np |
| import json |
| import matplotlib.pyplot as plt |
| import random |
| from qiskit import QuantumCircuit, transpile |
| from qiskit_aer import AerSimulator |
| from qiskit.quantum_info import Statevector |
|
|
| |
| H2_TERMS = [ |
| (-1.052373245772859, "II"), |
| (0.39793742484318045, "IZ"), |
| (-0.39793742484318045, "ZI"), |
| (-0.01128010425623538, "ZZ"), |
| (0.18093119978423156, "XX"), |
| ] |
|
|
| EXACT_GROUND = -1.8572750302023795 |
|
|
| def pauli_expectation_qiskit(circuit, pauli_string, backend): |
| """Compute Pauli expectation using Qiskit backend.""" |
| from qiskit.quantum_info import SparsePauliOp |
| |
| sv = Statevector.from_instruction(circuit) |
| |
| |
| op = SparsePauliOp.from_list([(pauli_string, 1.0)]) |
| expectation = sv.expectation_value(op).real |
| |
| return expectation |
|
|
| def evaluate_hamiltonian(circuit, backend_type='statevector'): |
| """Evaluate H2 Hamiltonian for a circuit.""" |
| total = 0.0 |
| |
| if backend_type == 'statevector': |
| sv = Statevector.from_instruction(circuit) |
| for coeff, pauli in H2_TERMS: |
| from qiskit.quantum_info import SparsePauliOp |
| op = SparsePauliOp.from_list([(pauli, 1.0)]) |
| exp_val = sv.expectation_value(op).real |
| total += coeff * exp_val |
| |
| return total |
|
|
| def generate_random_circuit(n_qubits, max_depth): |
| """Generate a random quantum circuit.""" |
| qc = QuantumCircuit(n_qubits) |
| n_gates = random.randint(1, max_depth) |
| |
| gate_types = ['h', 'x', 'y', 'z', 's', 't', 'rx', 'ry', 'rz', 'cx'] |
| |
| for _ in range(n_gates): |
| gate = random.choice(gate_types) |
| |
| if gate == 'cx': |
| ctrl = random.randint(0, n_qubits-1) |
| targ = random.randint(0, n_qubits-1) |
| while targ == ctrl: |
| targ = random.randint(0, n_qubits-1) |
| qc.cx(ctrl, targ) |
| elif gate in ['rx', 'ry', 'rz']: |
| qubit = random.randint(0, n_qubits-1) |
| angle = random.uniform(-np.pi, np.pi) |
| getattr(qc, gate)(angle, qubit) |
| else: |
| qubit = random.randint(0, n_qubits-1) |
| getattr(qc, gate)(qubit) |
| |
| return qc |
|
|
| def mutate_circuit(qc, mutation_type='add'): |
| """Mutate a circuit.""" |
| new_qc = qc.copy() |
| |
| if mutation_type == 'add' and len(new_qc.data) < 10: |
| gate_types = ['h', 'x', 'y', 'z', 's'] |
| gate = random.choice(gate_types) |
| qubit = random.randint(0, new_qc.num_qubits-1) |
| getattr(new_qc, gate)(qubit) |
| elif mutation_type == 'remove' and len(new_qc.data) > 0: |
| idx = random.randint(0, len(new_qc.data)-1) |
| new_qc.data.pop(idx) |
| |
| return new_qc |
|
|
| def python_gqe(backend_name='statevector', use_gpu=False): |
| """Python GQE implementation.""" |
| n_qubits = 2 |
| population_size = 50 |
| n_generations = 200 |
| mutation_rate = 0.5 |
| |
| history = [] |
| best_energy = float('inf') |
| best_circuit = None |
| |
| |
| population = [generate_random_circuit(n_qubits, 10) for _ in range(population_size)] |
| |
| for gen in range(n_generations): |
| |
| fitness = [] |
| for circ in population: |
| try: |
| energy = evaluate_hamiltonian(circ, backend_type='statevector') |
| fitness.append(energy) |
| except: |
| fitness.append(float('inf')) |
| |
| |
| min_idx = np.argmin(fitness) |
| if fitness[min_idx] < best_energy: |
| best_energy = fitness[min_idx] |
| best_circuit = population[min_idx].copy() |
| |
| history.append(best_energy) |
| |
| |
| new_population = [] |
| for _ in range(population_size): |
| tournament = random.sample(list(zip(population, fitness)), 3) |
| winner = min(tournament, key=lambda x: x[1])[0] |
| new_population.append(winner.copy()) |
| |
| |
| for i in range(len(new_population)): |
| if random.random() < mutation_rate: |
| mut_type = random.choice(['add', 'remove']) |
| new_population[i] = mutate_circuit(new_population[i], mut_type) |
| |
| |
| new_population[0] = best_circuit.copy() |
| |
| population = new_population |
| |
| return { |
| 'backend': f'Python-{backend_name}', |
| 'energy': best_energy, |
| 'history': history, |
| 'generations': n_generations |
| } |
|
|
| def rust_gqe(): |
| """Load Rust GQE results.""" |
| with open('gqe_history.json', 'r') as f: |
| history = json.load(f) |
| |
| return { |
| 'backend': 'qhybrid (Rust)', |
| 'energy': min(history), |
| 'history': history, |
| 'generations': len(history) |
| } |
|
|
| def run_gqe_comparison(): |
| print("Running GQE on multiple backends...") |
| print("="*60) |
| |
| results = [] |
| |
| |
| print("1. Loading Rust GQE results...") |
| results.append(rust_gqe()) |
| |
| |
| print("2. Running Python/NumPy GQE...") |
| start = time.perf_counter() |
| py_result = python_gqe('statevector') |
| py_result['time'] = time.perf_counter() - start |
| results.append(py_result) |
| |
| |
| print("\n" + "="*60) |
| print(f"{'Backend':<25} | {'Energy':<15} | {'Error':<12} | {'Time (s)':<10}") |
| print("-"*60) |
| |
| for r in results: |
| err = abs(r['energy'] - EXACT_GROUND) |
| time_str = f"{r.get('time', 0):.3f}" if 'time' in r else "N/A" |
| print(f"{r['backend']:<25} | {r['energy']:<15.8f} | {err:<12.2e} | {time_str:<10}") |
| |
| |
| plt.figure(figsize=(12, 6)) |
| |
| for r in results: |
| if 'history' in r: |
| plt.plot(r['history'], label=r['backend'], marker='.', markersize=3, alpha=0.7) |
| |
| plt.axhline(y=EXACT_GROUND, color='red', linestyle='--', linewidth=2, label='Exact Ground State') |
| |
| plt.xlabel('Generation') |
| plt.ylabel('Energy (Hartree)') |
| plt.title('GQE Convergence Comparison Across Backends (H2 Molecule)') |
| plt.legend() |
| plt.grid(True, which="both", ls="-", alpha=0.3) |
| |
| import os |
| os.makedirs('docs/assets', exist_ok=True) |
| plt.savefig('docs/assets/gqe_backends_comparison.png', dpi=300) |
| print("\nGQE backend comparison saved to docs/assets/gqe_backends_comparison.png") |
| |
| |
| with open('gqe_backends_results.json', 'w') as f: |
| json.dump(results, f, indent=2, default=str) |
| print("Results saved to gqe_backends_results.json") |
|
|
| if __name__ == "__main__": |
| run_gqe_comparison() |
|
|