1+ #!/usr/bin/env python3
2+ """Example running VQE algorithm on IBM Quantum hardware."""
3+
4+ import torch
5+ import numpy as np
6+ import sys
7+ import os
8+
9+ # Add project root to path
10+ sys .path .insert (0 , os .path .abspath ('..' ))
11+
12+ from torchquantum .backend import ParameterizedQuantumCircuit , QuantumExpectation
13+ from torchquantum .backend .qiskit_backend import QiskitBackend , HardwareManager
14+ from torchquantum .operator .standard_gates import RY , RZ , CNOT
15+
16+
17+ def create_vqe_ansatz (n_qubits = 2 , n_layers = 2 ):
18+ """Create a hardware-efficient VQE ansatz."""
19+ n_params = n_qubits * n_layers * 2
20+ circuit = ParameterizedQuantumCircuit (n_wires = n_qubits , n_trainable_params = n_params )
21+
22+ # Initialize parameters near ground state
23+ circuit .set_trainable_params (torch .randn (n_params ) * 0.1 )
24+
25+ param_idx = 0
26+ for layer in range (n_layers ):
27+ # Rotation layer
28+ for q in range (n_qubits ):
29+ circuit .append_gate (RY , wires = q , trainable_idx = param_idx )
30+ param_idx += 1
31+ circuit .append_gate (RZ , wires = q , trainable_idx = param_idx )
32+ param_idx += 1
33+
34+ # Entangling layer
35+ for q in range (n_qubits - 1 ):
36+ circuit .append_gate (CNOT , wires = [q , q + 1 ])
37+
38+ return circuit
39+
40+
41+ def select_backend ():
42+ """Select an appropriate backend for VQE."""
43+ print ("🔍 Finding suitable quantum backend..." )
44+
45+ try :
46+ # Try to connect to IBM Quantum
47+ from qiskit_ibm_runtime import QiskitRuntimeService
48+ service = QiskitRuntimeService ()
49+ backends = service .backends ()
50+
51+ print (f"✅ Connected to IBM Quantum Runtime" )
52+ print (f"📋 Found { len (backends )} available backends" )
53+
54+ # Prefer simulators for reliable results, but show real hardware options
55+ simulators = []
56+ real_devices = []
57+
58+ for backend in backends :
59+ if backend .num_qubits >= 2 : # Need at least 2 qubits for our VQE
60+ if backend .simulator :
61+ simulators .append (backend )
62+ else :
63+ try :
64+ status = backend .status ()
65+ if status .operational :
66+ real_devices .append ((backend , status .pending_jobs ))
67+ except :
68+ pass
69+
70+ print ("\n 🎯 Available options:" )
71+
72+ # Show simulators
73+ if simulators :
74+ print ("\n 🖥️ Simulators (recommended for VQE):" )
75+ for i , sim in enumerate (simulators [:3 ]):
76+ print (f" { i + 1 } . { sim .name } : { sim .num_qubits } qubits" )
77+
78+ # Show real devices
79+ if real_devices :
80+ real_devices .sort (key = lambda x : x [1 ]) # Sort by queue length
81+ print ("\n 🔬 Real Quantum Devices:" )
82+ for i , (device , queue ) in enumerate (real_devices [:3 ]):
83+ print (f" { i + 1 + len (simulators )} . { device .name } : { device .num_qubits } qubits (Queue: { queue } )" )
84+
85+ # Let user choose
86+ total_options = len (simulators ) + len (real_devices )
87+ if total_options == 0 :
88+ print ("❌ No suitable backends found" )
89+ return None
90+
91+ print (f"\n 🔢 Select backend (1-{ total_options } ), or 0 for local simulator:" )
92+ choice = input ("Choice: " ).strip ()
93+
94+ try :
95+ choice = int (choice )
96+ if choice == 0 :
97+ return "local"
98+ elif 1 <= choice <= len (simulators ):
99+ return simulators [choice - 1 ]
100+ elif len (simulators ) < choice <= total_options :
101+ device , _ = real_devices [choice - len (simulators ) - 1 ]
102+ return device
103+ else :
104+ print ("❌ Invalid choice, using local simulator" )
105+ return "local"
106+ except ValueError :
107+ print ("❌ Invalid choice, using local simulator" )
108+ return "local"
109+
110+ except Exception as e :
111+ print (f"⚠️ Could not connect to IBM Quantum: { e } " )
112+ print ("Using local simulator instead" )
113+ return "local"
114+
115+
116+ def run_vqe (backend_choice , max_iterations = 50 ):
117+ """Run VQE algorithm on the selected backend."""
118+ print (f"\n 🚀 Running VQE Algorithm" )
119+ print ("=" * 40 )
120+
121+ # Create VQE circuit for H2 molecule (simplified)
122+ circuit = create_vqe_ansatz (n_qubits = 2 , n_layers = 2 )
123+
124+ # H2 Hamiltonian (simplified, 2-qubit version)
125+ hamiltonian = {
126+ 'ZZ' : - 1.0523732 , # Main interaction
127+ 'ZI' : - 0.39793742 , # Single qubit terms
128+ 'IZ' : - 0.39793742 ,
129+ 'XX' : - 0.01128010 , # Exchange terms
130+ 'YY' : 0.01128010
131+ }
132+
133+ print (f"🧬 Optimizing H2 molecule ground state" )
134+ print (f"🔬 Hamiltonian: { len (hamiltonian )} terms" )
135+
136+ # Create backend
137+ if backend_choice == "local" :
138+ backend = QiskitBackend (device = 'qasm_simulator' , shots = 8192 )
139+ print (f"🖥️ Using local QASM simulator" )
140+ else :
141+ backend = QiskitBackend (device = backend_choice , shots = 4096 ) # Lower shots for hardware
142+ print (f"🔬 Using { backend_choice .name } : { backend_choice .num_qubits } qubits" )
143+
144+ # Create VQE model
145+ vqe_model = QuantumExpectation (circuit , backend , hamiltonian )
146+
147+ # Optimizer
148+ optimizer = torch .optim .Adam ([circuit .trainable_params ], lr = 0.1 )
149+
150+ print (f"\n ⚙️ Starting optimization ({ max_iterations } iterations)..." )
151+
152+ best_energy = float ('inf' )
153+ energies = []
154+
155+ try :
156+ for iteration in range (max_iterations ):
157+ optimizer .zero_grad ()
158+
159+ # Compute energy
160+ energy_tensor = vqe_model ()
161+ total_energy = energy_tensor .sum ()
162+
163+ # Backward pass
164+ total_energy .backward ()
165+ optimizer .step ()
166+
167+ current_energy = total_energy .item ()
168+ energies .append (current_energy )
169+
170+ if current_energy < best_energy :
171+ best_energy = current_energy
172+
173+ # Print progress
174+ if iteration % 10 == 0 or iteration == max_iterations - 1 :
175+ print (f" Iter { iteration :3d} : Energy = { current_energy :.6f} Ha" )
176+
177+ print (f"\n ✅ Optimization complete!" )
178+ print (f"🎯 Best energy: { best_energy :.6f} Ha" )
179+ print (f"📊 Theoretical H2 ground state: ≈ -1.857 Ha" )
180+
181+ error = abs (best_energy - (- 1.857 ))
182+ if error < 0.5 :
183+ print (f"✅ Good agreement! Error: { error :.3f} Ha" )
184+ else :
185+ print (f"⚠️ Large error: { error :.3f} Ha (hardware noise expected)" )
186+
187+ return best_energy , energies
188+
189+ except Exception as e :
190+ print (f"❌ VQE optimization failed: { e } " )
191+ return None , []
192+
193+
194+ def plot_convergence (energies ):
195+ """Plot VQE convergence (if matplotlib available)."""
196+ try :
197+ import matplotlib .pyplot as plt
198+
199+ plt .figure (figsize = (10 , 6 ))
200+ plt .plot (energies , 'b-' , linewidth = 2 , label = 'VQE Energy' )
201+ plt .axhline (y = - 1.857 , color = 'r' , linestyle = '--' , label = 'Theoretical Ground State' )
202+ plt .xlabel ('Iteration' )
203+ plt .ylabel ('Energy (Ha)' )
204+ plt .title ('VQE Convergence on Quantum Hardware' )
205+ plt .legend ()
206+ plt .grid (True , alpha = 0.3 )
207+ plt .tight_layout ()
208+
209+ plt .savefig ('vqe_convergence.png' , dpi = 150 , bbox_inches = 'tight' )
210+ print ("📊 Convergence plot saved as 'vqe_convergence.png'" )
211+
212+ except ImportError :
213+ print ("⚠️ Matplotlib not available, skipping plot" )
214+
215+
216+ def main ():
217+ """Main VQE hardware demo."""
218+ print ("🧪 VQE on IBM Quantum Hardware" )
219+ print ("=" * 50 )
220+
221+ print ("This example demonstrates running the Variational Quantum Eigensolver" )
222+ print ("algorithm to find the ground state of the H2 molecule using real" )
223+ print ("quantum hardware or high-fidelity simulators." )
224+
225+ # Select backend
226+ backend_choice = select_backend ()
227+ if backend_choice is None :
228+ print ("❌ No backend available" )
229+ return False
230+
231+ # Ask for number of iterations
232+ print (f"\n ⏱️ How many optimization iterations?" )
233+ print (" Simulators: 50-100 iterations recommended" )
234+ print (" Real hardware: 20-30 iterations (due to queue time)" )
235+
236+ try :
237+ max_iter = int (input ("Iterations (press Enter for 30): " ).strip () or "30" )
238+ max_iter = max (1 , min (max_iter , 200 )) # Reasonable bounds
239+ except ValueError :
240+ max_iter = 30
241+
242+ # Run VQE
243+ best_energy , energies = run_vqe (backend_choice , max_iter )
244+
245+ if best_energy is not None :
246+ # Plot results if we have data
247+ if len (energies ) > 1 :
248+ plot_convergence (energies )
249+
250+ print ("\n 🎉 VQE Demo Complete!" )
251+ print ("\n 📋 Summary:" )
252+ print (f" Backend: { backend_choice if isinstance (backend_choice , str ) else backend_choice .name } " )
253+ print (f" Iterations: { len (energies )} " )
254+ print (f" Final energy: { best_energy :.6f} Ha" )
255+ print (f" Target energy: -1.857 Ha" )
256+ print (f" Error: { abs (best_energy - (- 1.857 )):.3f} Ha" )
257+
258+ return True
259+ else :
260+ print ("❌ VQE demo failed" )
261+ return False
262+
263+
264+ if __name__ == "__main__" :
265+ success = main ()
266+ sys .exit (0 if success else 1 )
0 commit comments