Spaces:
Running
on
Zero
Running
on
Zero
Added display of full circuit Hamiltonian and reworked position of tiles
Browse files- app.py +95 -38
- quantum_utils.py +17 -2
app.py
CHANGED
@@ -69,7 +69,7 @@ for handler in logger.handlers:
|
|
69 |
handler.setLevel(logging.DEBUG)
|
70 |
|
71 |
def install_dependencies():
|
72 |
-
"""Install required packages from requirements.txt."""
|
73 |
try:
|
74 |
subprocess.check_call([sys.executable, "-m", "pip", "install", "-r", "requirements.txt"])
|
75 |
print("Dependencies installed successfully")
|
@@ -77,6 +77,16 @@ def install_dependencies():
|
|
77 |
print(f"Error installing dependencies: {e}")
|
78 |
sys.exit(1)
|
79 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
80 |
# Load molecule definitions
|
81 |
def load_molecules():
|
82 |
"""Load molecule definitions from JSON file."""
|
@@ -87,15 +97,6 @@ def load_molecules():
|
|
87 |
print(f"Error loading molecules.json: {e}", file=sys.stderr)
|
88 |
return {}
|
89 |
|
90 |
-
# Test if cudaq is already installed
|
91 |
-
try:
|
92 |
-
import cudaq
|
93 |
-
print("CUDA-Q already installed, skipping dependency installation")
|
94 |
-
except ImportError:
|
95 |
-
print("CUDA-Q not found, installing dependencies...")
|
96 |
-
# Run installation before any other imports
|
97 |
-
install_dependencies()
|
98 |
-
|
99 |
# Now import the rest of the modules
|
100 |
import gradio as gr
|
101 |
import numpy as np
|
@@ -182,11 +183,14 @@ def create_interface():
|
|
182 |
Select a molecule below, adjust its parameters, and watch as the simulation computes the lowest
|
183 |
possible energy state using quantum-inspired algorithms accelerated by NVIDIA GPUs.
|
184 |
|
|
|
|
|
185 |
Inspired by the CUDA-Q VQE Example: https://nvidia.github.io/cuda-quantum/latest/applications/python/vqe_advanced.html
|
186 |
""")
|
187 |
|
188 |
state = gr.State({}) # Store current molecule data
|
189 |
|
|
|
190 |
with gr.Row():
|
191 |
with gr.Column(scale=1):
|
192 |
# Molecule selection controls
|
@@ -195,7 +199,7 @@ def create_interface():
|
|
195 |
value=list(molecules.keys())[0],
|
196 |
label="Select Molecule",
|
197 |
interactive=True,
|
198 |
-
allow_custom_value=False
|
199 |
)
|
200 |
|
201 |
# Get scale range from selected molecule
|
@@ -208,41 +212,91 @@ def create_interface():
|
|
208 |
label="Scale Factor (1.0 = original size)",
|
209 |
interactive=True
|
210 |
)
|
211 |
-
|
212 |
-
# Split into two buttons
|
213 |
-
generate_ham_button = gr.Button("Generate Hamiltonian")
|
214 |
-
run_vqe_button = gr.Button("Run VQE Simulation", interactive=False)
|
215 |
-
|
216 |
with gr.Column(scale=1):
|
217 |
# Molecule parameters display
|
218 |
params_display = gr.HTML(
|
219 |
label="Molecule Parameters",
|
220 |
value=format_molecule_params(default_molecule)
|
221 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
222 |
|
223 |
-
#
|
224 |
-
with gr.Row():
|
225 |
-
simulation_results = gr.HTML(
|
226 |
-
label="Simulation Results",
|
227 |
-
value="<div style='padding: 10px; background-color: #f5f5f5; border-radius: 5px;'>" +
|
228 |
-
"Click 'Generate Hamiltonian' to start.</div>"
|
229 |
-
)
|
230 |
-
|
231 |
-
# Plots and visualization
|
232 |
with gr.Row():
|
233 |
with gr.Column(scale=1):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
234 |
convergence_plot = gr.Plot(
|
235 |
label="VQE Convergence",
|
236 |
show_label=True
|
237 |
)
|
238 |
-
|
239 |
-
|
240 |
-
|
241 |
-
|
242 |
-
|
243 |
-
|
244 |
-
|
245 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
246 |
def generate_hamiltonian(molecule_choice: str, scale_factor: float) -> tuple:
|
247 |
"""Generate Hamiltonian for the selected molecule."""
|
248 |
try:
|
@@ -258,25 +312,28 @@ def create_interface():
|
|
258 |
|
259 |
# Format Hamiltonian results
|
260 |
hamiltonian_html = f"""
|
261 |
-
<div style='padding: 10px; background-color: #f5f5f5; border-radius: 5px;'>
|
262 |
<h3>Hamiltonian Generated:</h3>
|
263 |
<ul style='list-style-type: none; padding-left: 0;'>
|
264 |
<li><b>Number of Qubits:</b> {results['qubit_count']}</li>
|
265 |
<li><b>Number of Electrons:</b> {results['electron_count']}</li>
|
266 |
-
<li><b>Parameter Count:</b> {results['parameter_count']}</li>
|
267 |
<li><b>Hamiltonian Terms:</b> {results['hamiltonian_terms']}</li>
|
|
|
268 |
<li><b>Scale Factor:</b> {scale_factor:.2f}</li>
|
269 |
</ul>
|
270 |
<p><i>Ready to run VQE optimization.</i></p>
|
271 |
</div>
|
272 |
"""
|
273 |
|
|
|
|
|
|
|
274 |
# Enable the VQE button now that Hamiltonian is generated
|
275 |
-
return hamiltonian_html, gr.update(interactive=True), None
|
276 |
|
277 |
except Exception as e:
|
278 |
error_msg = f"<div style='color: red;'>Error generating Hamiltonian:<br>{str(e)}</div>"
|
279 |
-
return error_msg, gr.update(interactive=False), None
|
280 |
|
281 |
def run_vqe_optimization(molecule_choice: str, scale_factor: float) -> tuple:
|
282 |
"""Run VQE optimization after Hamiltonian is generated."""
|
@@ -376,7 +433,7 @@ def create_interface():
|
|
376 |
generate_ham_button.click(
|
377 |
fn=generate_hamiltonian,
|
378 |
inputs=[molecule_choice, scale_factor],
|
379 |
-
outputs=[simulation_results, run_vqe_button, convergence_plot]
|
380 |
)
|
381 |
|
382 |
# Run VQE optimization when button is clicked
|
|
|
69 |
handler.setLevel(logging.DEBUG)
|
70 |
|
71 |
def install_dependencies():
|
72 |
+
print("""Install required packages from requirements.txt.""")
|
73 |
try:
|
74 |
subprocess.check_call([sys.executable, "-m", "pip", "install", "-r", "requirements.txt"])
|
75 |
print("Dependencies installed successfully")
|
|
|
77 |
print(f"Error installing dependencies: {e}")
|
78 |
sys.exit(1)
|
79 |
|
80 |
+
# Test if cudaq is already installed
|
81 |
+
try:
|
82 |
+
import cudaq
|
83 |
+
print("CUDA-Q already installed, skipping dependency installation")
|
84 |
+
except ImportError:
|
85 |
+
print("CUDA-Q not found, installing dependencies...")
|
86 |
+
# Run installation before any other imports
|
87 |
+
install_dependencies()
|
88 |
+
|
89 |
+
|
90 |
# Load molecule definitions
|
91 |
def load_molecules():
|
92 |
"""Load molecule definitions from JSON file."""
|
|
|
97 |
print(f"Error loading molecules.json: {e}", file=sys.stderr)
|
98 |
return {}
|
99 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
100 |
# Now import the rest of the modules
|
101 |
import gradio as gr
|
102 |
import numpy as np
|
|
|
183 |
Select a molecule below, adjust its parameters, and watch as the simulation computes the lowest
|
184 |
possible energy state using quantum-inspired algorithms accelerated by NVIDIA GPUs.
|
185 |
|
186 |
+
Even for small molecules, the needed quantum circuit can be quite large so for learning purposes we display it in all it's sacred glory.
|
187 |
+
|
188 |
Inspired by the CUDA-Q VQE Example: https://nvidia.github.io/cuda-quantum/latest/applications/python/vqe_advanced.html
|
189 |
""")
|
190 |
|
191 |
state = gr.State({}) # Store current molecule data
|
192 |
|
193 |
+
# Top section: 3 columns
|
194 |
with gr.Row():
|
195 |
with gr.Column(scale=1):
|
196 |
# Molecule selection controls
|
|
|
199 |
value=list(molecules.keys())[0],
|
200 |
label="Select Molecule",
|
201 |
interactive=True,
|
202 |
+
allow_custom_value=False
|
203 |
)
|
204 |
|
205 |
# Get scale range from selected molecule
|
|
|
212 |
label="Scale Factor (1.0 = original size)",
|
213 |
interactive=True
|
214 |
)
|
215 |
+
|
|
|
|
|
|
|
|
|
216 |
with gr.Column(scale=1):
|
217 |
# Molecule parameters display
|
218 |
params_display = gr.HTML(
|
219 |
label="Molecule Parameters",
|
220 |
value=format_molecule_params(default_molecule)
|
221 |
)
|
222 |
+
|
223 |
+
with gr.Column(scale=1):
|
224 |
+
# 3D Molecule Viewer
|
225 |
+
molecule_viz = MolGallery3D(
|
226 |
+
columns=1,
|
227 |
+
height=400, # Reduced height to match other elements
|
228 |
+
label="3D Molecule Viewer",
|
229 |
+
automatic_rotation=True
|
230 |
+
)
|
231 |
|
232 |
+
# Middle section: Buttons and results in 2 columns
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
233 |
with gr.Row():
|
234 |
with gr.Column(scale=1):
|
235 |
+
# Buttons side by side
|
236 |
+
with gr.Row():
|
237 |
+
generate_ham_button = gr.Button("Generate Hamiltonian")
|
238 |
+
run_vqe_button = gr.Button("Run VQE Simulation", interactive=False)
|
239 |
+
|
240 |
+
# Hamiltonian Information
|
241 |
+
simulation_results = gr.HTML(
|
242 |
+
label="Hamiltonian Information",
|
243 |
+
value="<div style='padding: 10px; background-color: #f5f5f5; border-radius: 5px; font-size: 0.8em;'>" +
|
244 |
+
"Click 'Generate Hamiltonian' to start.</div>"
|
245 |
+
)
|
246 |
+
|
247 |
+
with gr.Column(scale=1):
|
248 |
+
# VQE Convergence Plot
|
249 |
convergence_plot = gr.Plot(
|
250 |
label="VQE Convergence",
|
251 |
show_label=True
|
252 |
)
|
253 |
+
|
254 |
+
# Bottom section: Full width circuit visualization
|
255 |
+
circuit_latex_display = gr.Markdown(
|
256 |
+
label="Quantum Circuit Visualization",
|
257 |
+
value="Circuit will be displayed after generation.",
|
258 |
+
elem_classes="circuit-output"
|
259 |
+
)
|
260 |
+
|
261 |
+
# Add CSS styling
|
262 |
+
gr.Markdown("""
|
263 |
+
<style>
|
264 |
+
.circuit-output {
|
265 |
+
font-family: monospace;
|
266 |
+
font-size: 0.6em;
|
267 |
+
white-space: pre;
|
268 |
+
overflow-x: auto;
|
269 |
+
padding: 10px;
|
270 |
+
background-color: #f8f8f8;
|
271 |
+
width: 100%;
|
272 |
+
max-width: 100%;
|
273 |
+
box-sizing: border-box;
|
274 |
+
display: block;
|
275 |
+
}
|
276 |
+
.circuit-output pre {
|
277 |
+
margin: 0 auto;
|
278 |
+
background: none !important;
|
279 |
+
border: none !important;
|
280 |
+
padding: 0 !important;
|
281 |
+
width: 100%;
|
282 |
+
overflow-x: auto;
|
283 |
+
}
|
284 |
+
.circuit-output code {
|
285 |
+
background: none !important;
|
286 |
+
border: none !important;
|
287 |
+
padding: 0 !important;
|
288 |
+
}
|
289 |
+
.gradio-container .prose {
|
290 |
+
max-width: 100% !important;
|
291 |
+
width: 100% !important;
|
292 |
+
}
|
293 |
+
.markdown-text {
|
294 |
+
width: 100% !important;
|
295 |
+
max-width: none !important;
|
296 |
+
}
|
297 |
+
</style>
|
298 |
+
""")
|
299 |
+
|
300 |
def generate_hamiltonian(molecule_choice: str, scale_factor: float) -> tuple:
|
301 |
"""Generate Hamiltonian for the selected molecule."""
|
302 |
try:
|
|
|
312 |
|
313 |
# Format Hamiltonian results
|
314 |
hamiltonian_html = f"""
|
315 |
+
<div style='padding: 10px; background-color: #f5f5f5; border-radius: 5px; font-size: 0.8em;'>
|
316 |
<h3>Hamiltonian Generated:</h3>
|
317 |
<ul style='list-style-type: none; padding-left: 0;'>
|
318 |
<li><b>Number of Qubits:</b> {results['qubit_count']}</li>
|
319 |
<li><b>Number of Electrons:</b> {results['electron_count']}</li>
|
|
|
320 |
<li><b>Hamiltonian Terms:</b> {results['hamiltonian_terms']}</li>
|
321 |
+
<li style='margin-top: 8px; color: #2a6f97;'><b>Quantum Circuit Parameters:</b> {results['parameter_count']} <i>(parameters needed to represent the system in quantum circuit form)</i></li>
|
322 |
<li><b>Scale Factor:</b> {scale_factor:.2f}</li>
|
323 |
</ul>
|
324 |
<p><i>Ready to run VQE optimization.</i></p>
|
325 |
</div>
|
326 |
"""
|
327 |
|
328 |
+
# Format circuit output with monospace preservation and escape special characters
|
329 |
+
circuit_latex = f"""<div class='circuit-output'><pre>{results.get('circuit_latex', 'Circuit visualization not available').replace('`', '`')}</pre></div>"""
|
330 |
+
|
331 |
# Enable the VQE button now that Hamiltonian is generated
|
332 |
+
return hamiltonian_html, circuit_latex, gr.update(interactive=True), None
|
333 |
|
334 |
except Exception as e:
|
335 |
error_msg = f"<div style='color: red;'>Error generating Hamiltonian:<br>{str(e)}</div>"
|
336 |
+
return error_msg, "Error generating circuit visualization", gr.update(interactive=False), None
|
337 |
|
338 |
def run_vqe_optimization(molecule_choice: str, scale_factor: float) -> tuple:
|
339 |
"""Run VQE optimization after Hamiltonian is generated."""
|
|
|
433 |
generate_ham_button.click(
|
434 |
fn=generate_hamiltonian,
|
435 |
inputs=[molecule_choice, scale_factor],
|
436 |
+
outputs=[simulation_results, circuit_latex_display, run_vqe_button, convergence_plot]
|
437 |
)
|
438 |
|
439 |
# Run VQE optimization when button is clicked
|
quantum_utils.py
CHANGED
@@ -236,6 +236,7 @@ def generate_hamiltonian(molecule_data: Dict[str, Any],
|
|
236 |
- 'electron_count': Number of electrons in the system
|
237 |
- 'parameter_count': Number of UCCSD parameters needed
|
238 |
- 'hamiltonian_terms': Number of terms in the Hamiltonian
|
|
|
239 |
"""
|
240 |
logging.info(f"Generating Hamiltonian for {molecule_data['name']} with scale factor {scale_factor}")
|
241 |
|
@@ -267,12 +268,25 @@ def generate_hamiltonian(molecule_data: Dict[str, Any],
|
|
267 |
parameter_count = cudaq.kernels.uccsd_num_parameters(electron_count, qubit_count)
|
268 |
logging.info(f"Number of UCCSD parameters needed = {parameter_count}")
|
269 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
270 |
return {
|
271 |
'hamiltonian': spin_hamiltonian,
|
272 |
'qubit_count': qubit_count,
|
273 |
'electron_count': electron_count,
|
274 |
'parameter_count': parameter_count,
|
275 |
-
'hamiltonian_terms': term_count
|
|
|
276 |
}
|
277 |
|
278 |
@spaces.GPU
|
@@ -306,7 +320,8 @@ def run_vqe_simulation(molecule_data: Dict[str, Any],
|
|
306 |
'hamiltonian_terms': ham_info['hamiltonian_terms'],
|
307 |
'qubit_count': ham_info['qubit_count'],
|
308 |
'electron_count': ham_info['electron_count'],
|
309 |
-
'message': "Hamiltonian generated successfully"
|
|
|
310 |
}
|
311 |
|
312 |
# Otherwise continue with VQE optimization
|
|
|
236 |
- 'electron_count': Number of electrons in the system
|
237 |
- 'parameter_count': Number of UCCSD parameters needed
|
238 |
- 'hamiltonian_terms': Number of terms in the Hamiltonian
|
239 |
+
- 'circuit_latex': LaTeX representation of the quantum circuit
|
240 |
"""
|
241 |
logging.info(f"Generating Hamiltonian for {molecule_data['name']} with scale factor {scale_factor}")
|
242 |
|
|
|
268 |
parameter_count = cudaq.kernels.uccsd_num_parameters(electron_count, qubit_count)
|
269 |
logging.info(f"Number of UCCSD parameters needed = {parameter_count}")
|
270 |
|
271 |
+
# Generate LaTeX representation of the circuit
|
272 |
+
try:
|
273 |
+
logging.info("Starting circuit LaTeX generation")
|
274 |
+
thetas_draw = np.random.normal(0, 1, parameter_count)
|
275 |
+
# Draw the circuit in fancy nice stuff
|
276 |
+
circuit_latex = cudaq.draw(kernel, qubit_count, electron_count, thetas_draw)
|
277 |
+
logging.info("Successfully generated circuit LaTeX representation")
|
278 |
+
logging.info(circuit_latex)
|
279 |
+
except Exception as e:
|
280 |
+
logging.error(f"Failed to generate circuit LaTeX: {str(e)}")
|
281 |
+
circuit_latex = "Error generating circuit visualization"
|
282 |
+
|
283 |
return {
|
284 |
'hamiltonian': spin_hamiltonian,
|
285 |
'qubit_count': qubit_count,
|
286 |
'electron_count': electron_count,
|
287 |
'parameter_count': parameter_count,
|
288 |
+
'hamiltonian_terms': term_count,
|
289 |
+
'circuit_latex': circuit_latex
|
290 |
}
|
291 |
|
292 |
@spaces.GPU
|
|
|
320 |
'hamiltonian_terms': ham_info['hamiltonian_terms'],
|
321 |
'qubit_count': ham_info['qubit_count'],
|
322 |
'electron_count': ham_info['electron_count'],
|
323 |
+
'message': "Hamiltonian generated successfully",
|
324 |
+
'circuit_latex': ham_info['circuit_latex']
|
325 |
}
|
326 |
|
327 |
# Otherwise continue with VQE optimization
|