ZenkuIshigami09's picture
Upload 21 files
9fc3c5b verified
Raw
History Blame Contribute Delete
53.5 kB
"""
Quantum Catalyst Discovery Platform - Main Application
======================================================
A comprehensive platform for quantum-powered catalyst discovery using:
- Real VQE simulations
- Quantum Machine Learning (QSVM, VQC, QGAN)
- Classical algorithm comparisons
- Interactive educational tools
Author: Baratam Pranneth Gupta
Date: March 2026
"""
import streamlit as st
import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
import json
from datetime import datetime
# Import our modules
from modules.molecule_validator import process_molecule_input
from modules.molecule_generation import generate_3d_molecule
from modules.visualization import show_molecule_3d, mol_to_xyz
from modules.quantum_simulation import run_vqe_simulation, compare_methods, get_supported_molecules, HAS_PYSCF
from modules.reaction_pathway import (
simulate_reaction_pathway,
compute_catalyst_score,
get_supported_reactions,
get_reaction_info,
REACTION_DATABASE,
parse_dynamic_reaction,
)
from modules.quantum_ml import (
discover_catalysts,
score_user_catalyst,
QuantumCatalystScorer
)
from modules.classical_baselines import (
compare_quantum_vs_classical_chemistry,
compare_quantum_vs_classical_ml,
run_full_comparison
)
from modules.export_utils import export_discovery_batch_to_csv
# ========================================================================
# PAGE CONFIGURATION
# ========================================================================
st.set_page_config(
page_title="Quantum Catalyst Platform",
page_icon="⚛️",
layout="wide",
initial_sidebar_state="expanded"
)
# Custom CSS for better visuals
st.markdown("""
<style>
.main-header {
font-size: 3rem;
font-weight: bold;
color: #1f77b4;
text-align: center;
margin-bottom: 2rem;
}
.sub-header {
font-size: 1.5rem;
color: #2ca02c;
margin-top: 2rem;
}
.metric-card {
background-color: #f0f2f6;
padding: 1rem;
border-radius: 0.5rem;
border-left: 4px solid #1f77b4;
}
.success-box {
background-color: #d4edda;
padding: 1rem;
border-radius: 0.5rem;
border-left: 4px solid #28a745;
}
.warning-box {
background-color: #fff3cd;
padding: 1rem;
border-radius: 0.5rem;
border-left: 4px solid #ffc107;
}
.error-box {
background-color: #f8d7da;
padding: 1rem;
border-radius: 0.5rem;
border-left: 4px solid #dc3545;
}
</style>
""", unsafe_allow_html=True)
# ========================================================================
# SESSION STATE INITIALIZATION
# ========================================================================
if 'results_history' not in st.session_state:
st.session_state.results_history = []
if 'current_page' not in st.session_state:
st.session_state.current_page = "Home"
if 'discovered_catalysts' not in st.session_state:
st.session_state.discovered_catalysts = []
# ========================================================================
# SIDEBAR NAVIGATION
# ========================================================================
st.sidebar.title("⚛️ Quantum Catalyst Platform")
st.sidebar.markdown("---")
page = st.sidebar.radio(
"Navigation",
[
"🏠 Home",
"🔬 Feature 1: AI Discovery",
"🎮 Feature 2: Learning Game",
"📊 Quantum vs Classical",
"🧪 Molecule Explorer",
"📈 Results & Export"
]
)
st.sidebar.markdown("---")
st.sidebar.info("""
**About this Platform:**
This platform demonstrates quantum computing's advantage in catalyst discovery using:
- Real VQE simulations
- Quantum Machine Learning
- Chemistry-based models
Built with Qiskit, RDKit, and Streamlit.
""")
if HAS_PYSCF:
st.sidebar.success("🟢 Dynamic PySCF Engine: Active")
else:
st.sidebar.warning("🟡 Dynamic Engine Offline (Using Static Fallback)")
# ========================================================================
# HELPER FUNCTIONS
# ========================================================================
def plot_energy_landscape(states, energies, title="Energy Landscape"):
"""Create beautiful energy landscape plot."""
fig, ax = plt.subplots(figsize=(10, 6))
x_pos = np.arange(len(states))
colors = ['#3498db', '#2ecc71', '#e74c3c', '#f39c12', '#9b59b6']
# Plot lines
ax.plot(x_pos, energies, 'o-', linewidth=2, markersize=10, color='#2c3e50')
# Fill area
ax.fill_between(x_pos, energies, min(energies), alpha=0.3, color='#3498db')
# Annotate energies
for i, (state, energy) in enumerate(zip(states, energies)):
ax.annotate(f'{energy:.4f} Ha',
xy=(i, energy),
xytext=(0, 10),
textcoords='offset points',
ha='center',
fontsize=9,
bbox=dict(boxstyle='round,pad=0.5', fc='yellow', alpha=0.7))
# Styling
ax.set_xticks(x_pos)
ax.set_xticklabels(states, rotation=45, ha='right')
ax.set_ylabel('Energy (Hartree)', fontsize=12, fontweight='bold')
ax.set_title(title, fontsize=14, fontweight='bold')
ax.grid(True, alpha=0.3, linestyle='--')
ax.axhline(y=0, color='k', linestyle='-', linewidth=0.5)
plt.tight_layout()
return fig
def plot_convergence(convergence_data, title="VQE Convergence"):
"""Plot VQE convergence."""
fig, ax = plt.subplots(figsize=(10, 5))
iterations = range(1, len(convergence_data) + 1)
ax.plot(iterations, convergence_data, 'o-', linewidth=2, markersize=6, color='#e74c3c')
# Mark minimum
min_idx = np.argmin(convergence_data)
min_energy = convergence_data[min_idx]
ax.plot(min_idx + 1, min_energy, 'g*', markersize=20, label=f'Minimum: {min_energy:.6f} Ha')
ax.set_xlabel('Iteration', fontsize=12, fontweight='bold')
ax.set_ylabel('Energy (Hartree)', fontsize=12, fontweight='bold')
ax.set_title(title, fontsize=14, fontweight='bold')
ax.grid(True, alpha=0.3)
ax.legend()
plt.tight_layout()
return fig
def plot_comparison_bar(data_dict, title="Comparison", ylabel="Score"):
"""Create comparison bar chart."""
fig, ax = plt.subplots(figsize=(10, 6))
methods = list(data_dict.keys())
values = list(data_dict.values())
colors = ['#3498db', '#e74c3c', '#2ecc71', '#f39c12', '#9b59b6'][:len(methods)]
bars = ax.bar(methods, values, color=colors, alpha=0.7, edgecolor='black', linewidth=1.5)
# Add value labels on bars
for bar in bars:
height = bar.get_height()
ax.text(bar.get_x() + bar.get_width()/2., height,
f'{height:.2f}',
ha='center', va='bottom', fontweight='bold')
ax.set_ylabel(ylabel, fontsize=12, fontweight='bold')
ax.set_title(title, fontsize=14, fontweight='bold')
ax.grid(True, alpha=0.3, axis='y')
plt.tight_layout()
return fig
def save_result_to_history(result_data):
"""Save analysis result to session history."""
result_data['timestamp'] = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
st.session_state.results_history.append(result_data)
def show_simulation_provenance(sim_result: dict):
"""Display Hamiltonian provenance for VQE/HF results when available."""
generation_mode = sim_result.get("generation_mode", "Static Database")
st.metric("Hamiltonian Source", generation_mode)
active_electrons = int(sim_result.get("active_electrons", 0) or 0)
frozen_orbitals = int(sim_result.get("frozen_orbitals", 0) or 0)
if generation_mode == "Dynamic":
active_orbitals = max(1, int(sim_result.get("num_qubits", 0) // 2))
st.caption(f"Active Space: [{active_electrons}e, {active_orbitals}o]")
st.caption(f"Core Orbitals Frozen: {frozen_orbitals}")
noise_model = sim_result.get("noise_model", "None")
if noise_model and noise_model != "None":
st.caption(f"Noise Model: {noise_model}")
source = sim_result.get("hamiltonian_source")
if source == "approximate_fallback":
st.warning("Using approximate fallback Hamiltonian for an unsupported molecule. Interpret results as exploratory.")
elif source == "dynamic_pyscf":
st.success("Dynamic PySCF Hamiltonian generated successfully.")
elif source == "database":
st.caption("Hamiltonian source: curated static database")
def _map_custom_reaction_to_qml_key(parsed_reaction: dict) -> str:
"""Map parsed custom reactions to the closest trained QSVM reaction key."""
if not parsed_reaction:
return "H2_O2"
reactants = parsed_reaction.get("reactants", [])
if "N#N" in reactants:
return "N2_H2"
reaction_type = parsed_reaction.get("type", "")
if reaction_type == "oxidation":
return "H2_O2"
return "CO2_reduction"
def catalyst_input_widget(label: str, key_prefix: str, placeholder: str = "e.g., Pt, Fe, NiO") -> str:
"""Render catalyst input as discovered-candidate select + custom entry."""
discovered = st.session_state.get("discovered_catalysts", [])
if discovered:
mode = st.selectbox(
f"{label} Input Mode:",
options=["Select Discovered Catalyst", "Type Custom SMILES"],
key=f"{key_prefix}_mode",
)
if mode == "Select Discovered Catalyst":
return st.selectbox(
label,
options=discovered,
key=f"{key_prefix}_selected",
)
return st.text_input(
label,
placeholder=placeholder,
key=f"{key_prefix}_custom",
)
# ========================================================================
# PAGE: HOME
# ========================================================================
if page == "🏠 Home":
st.markdown('<p class="main-header">⚛️ Quantum Catalyst Discovery Platform</p>', unsafe_allow_html=True)
st.markdown("""
### Welcome to the Future of Catalyst Discovery!
This platform harnesses **quantum computing** and **machine learning** to revolutionize
how we discover and optimize catalysts for chemical reactions.
""")
# Key Features
col1, col2 = st.columns(2)
with col1:
st.markdown("""
#### 🔬 Feature 1: AI-Powered Discovery
- **QGAN**: Generate novel catalyst candidates
- **VQC**: Classify catalyst effectiveness
- **VQE**: Validate with quantum simulations
Perfect for researchers discovering new materials!
""")
if st.button("🚀 Try AI Discovery", key="home_discovery"):
st.session_state.current_page = "🔬 Feature 1: AI Discovery"
st.rerun()
with col2:
st.markdown("""
#### 🎮 Feature 2: Interactive Learning
- **QSVM**: Score your catalyst choices
- **Real-time feedback**: Learn chemistry principles
- **Gamified experience**: Challenge yourself!
Perfect for students and educators!
""")
if st.button("🎯 Try Learning Game", key="home_learning"):
st.session_state.current_page = "🎮 Feature 2: Learning Game"
st.rerun()
st.markdown("---")
# Platform Capabilities
st.markdown("### 🎯 Platform Capabilities")
cap_col1, cap_col2, cap_col3 = st.columns(3)
with cap_col1:
st.metric("Molecules Supported", f"{len(get_supported_molecules())}")
st.metric("Reactions Available", f"{len(get_supported_reactions())}")
with cap_col2:
st.metric("Quantum Algorithms", "4+")
st.caption("VQE, QSVM, VQC, QGAN")
with cap_col3:
st.metric("Classical Baselines", "6+")
st.caption("HF, DFT, RF, SVM, GB")
st.markdown("---")
# Quick Start Guide
with st.expander("📖 Quick Start Guide"):
st.markdown("""
### How to Use This Platform
1. **AI Discovery Mode**:
- Select a target reaction
- Let the QGAN generate catalyst candidates
- Review quantum simulation results
- Compare with classical methods
2. **Learning Game Mode**:
- Choose a reaction
- Guess the best catalyst
- Get scored by quantum ML
- Learn from detailed feedback
3. **Comparison Mode**:
- Explore quantum vs classical algorithms
- See side-by-side energy calculations
- Understand quantum advantage
4. **Molecule Explorer**:
- Validate any molecule
- Run VQE simulations
- View 3D structures
- Analyze properties
""")
# Technical Details
with st.expander("🔧 Technical Details"):
st.markdown("""
### Technology Stack
**Quantum Computing:**
- **Qiskit**: IBM's quantum framework
- **VQE**: Variational Quantum Eigensolver
- **QSVM**: Quantum Support Vector Machine
- **Real quantum circuits** (not simulated data!)
**Chemistry:**
- **RDKit**: Molecular validation and properties
- **Custom Hamiltonians**: Pre-computed for 26+ molecules
- **D-band model**: Catalyst activity prediction
- **BEP relation**: Activation energy estimation
**Machine Learning:**
- **Qiskit ML**: Quantum machine learning algorithms
- **Scikit-learn**: Classical ML baselines
- **Feature engineering**: 16D descriptors (physicochemical + Coulomb-like + fingerprint)
**Visualization:**
- **Streamlit**: Interactive web interface
- **Matplotlib**: Scientific plotting
- **Py3Dmol**: 3D molecular visualization
""")
# ========================================================================
# PAGE: FEATURE 1 - AI DISCOVERY
# ========================================================================
elif page == "🔬 Feature 1: AI Discovery":
st.markdown('<p class="main-header">🔬 AI-Powered Catalyst Discovery</p>', unsafe_allow_html=True)
st.markdown("""
### Discover Novel Catalysts with Quantum AI
This feature uses **QGAN** to generate catalyst candidates, **VQC** to classify them,
and **VQE** to validate their effectiveness through quantum simulations.
""")
# User inputs
st.markdown("### ⚙️ Configuration")
col1, col2 = st.columns([2, 1])
with col1:
reaction_options = {
"H2_O2": "H₂ + O₂ → H₂O (Fuel Cell / Water Formation)",
"N2_H2": "N₂ + 3H₂ → 2NH₃ (Haber Process / Ammonia)",
"CO2_reduction": "CO₂ + H₂ → CO + H₂O (Carbon Capture)",
"CUSTOM": "Custom Reaction (Enter Equation)",
}
selected_reaction = st.selectbox(
"Select Target Reaction:",
options=list(reaction_options.keys()),
format_func=lambda x: reaction_options[x]
)
custom_reaction_equation = ""
parsed_custom_reaction = None
if selected_reaction == "CUSTOM":
custom_reaction_equation = st.text_input(
"Custom Reaction Equation:",
placeholder="e.g., CO + H2O -> CO2 + H2",
key="custom_discovery_equation",
)
if custom_reaction_equation:
parsed_custom_reaction = parse_dynamic_reaction(custom_reaction_equation)
if parsed_custom_reaction.get("error"):
st.error(parsed_custom_reaction["error"])
else:
st.info(f"**Reaction:** {parsed_custom_reaction['equation']}")
else:
reaction_info = get_reaction_info(selected_reaction)
st.info(f"**Reaction:** {reaction_info['equation']}")
with col2:
num_candidates = st.slider("Number of Candidates:", 3, 10, 5)
discover_btn = st.button("🚀 Discover Catalysts", type="primary", use_container_width=True)
if discover_btn:
st.info("Generating novel catalytic structures via RDKit mutation engine...")
with st.spinner("🧬 Sampling Statevector probabilities and generating stochastic catalyst mutations..."):
if selected_reaction == "CUSTOM":
parsed_custom_reaction = parse_dynamic_reaction(custom_reaction_equation) if custom_reaction_equation else {"error": "Please enter a custom reaction equation."}
if parsed_custom_reaction.get("error"):
st.error(parsed_custom_reaction["error"])
st.stop()
discovery_reaction_key = _map_custom_reaction_to_qml_key(parsed_custom_reaction)
pathway_reaction_input = parsed_custom_reaction
else:
discovery_reaction_key = selected_reaction
pathway_reaction_input = selected_reaction
# Discover catalysts
candidates = discover_catalysts(discovery_reaction_key, num_candidates)
if candidates:
st.success(f"✅ Generated {len(candidates)} catalyst candidates!")
# Persist discovered catalysts across tabs for pipeline flow.
discovered = st.session_state.discovered_catalysts
for cand in candidates:
smiles = cand.get('smiles')
if smiles and smiles not in discovered:
discovered.append(smiles)
# Display candidates
st.markdown("### 📋 Catalyst Candidates")
# Create DataFrame
df = pd.DataFrame([
{
"Rank": i+1,
"Catalyst": cand['smiles'],
"Metal": cand.get('metal_type', 'N/A'),
"Score": f"{cand['catalyst_score']:.2f}",
"Classification": cand['classification'],
"Generation Score": f"{cand['generation_score']:.2f}"
}
for i, cand in enumerate(candidates)
])
st.dataframe(df, use_container_width=True)
# Visualize top 3
st.markdown("### 🏆 Top 3 Detailed Analysis")
for i, cand in enumerate(candidates[:3]):
with st.expander(f"#{i+1}: {cand['smiles']} - Score: {cand['catalyst_score']:.2f}/100"):
col_a, col_b = st.columns(2)
with col_a:
st.markdown("**Catalyst Information:**")
st.write(f"- **SMILES:** `{cand['smiles']}`")
st.write(f"- **Metal:** {cand.get('metal_type', 'N/A')}")
st.write(f"- **Classification:** {cand['classification']}")
st.write(f"- **Feedback:** {cand['feedback']}")
with col_b:
st.markdown("**Run Full Simulation:**")
apply_noise = st.toggle(
"🎛️ Simulate Quantum Hardware Noise (NISQ)",
key=f"noise_discovery_{i}",
value=False,
)
if st.button(f"Run VQE + Pathway", key=f"sim_{i}"):
with st.spinner("Running quantum simulation..."):
# Run VQE
vqe_result = run_vqe_simulation(cand['smiles'], apply_noise=apply_noise)
if not vqe_result.get('error'):
st.metric("Ground State Energy", f"{vqe_result['energy']:.6f} Ha")
show_simulation_provenance(vqe_result)
# Plot convergence
fig_conv = plot_convergence(
vqe_result['convergence'],
f"VQE Convergence: {cand['smiles']}"
)
st.pyplot(fig_conv)
plt.close()
# Run pathway
pathway = simulate_reaction_pathway(cand['smiles'], pathway_reaction_input)
if not pathway.get('error'):
fig_path = plot_energy_landscape(
pathway['states'],
pathway['energies'],
f"Reaction Pathway: {cand['smiles']}"
)
st.pyplot(fig_path)
plt.close()
st.metric(
"Predicted Turnover Frequency (TOF)",
f"{pathway.get('turnover_frequency_s', 0.0):.2e} s^-1"
)
else:
st.error(f"Error: {vqe_result['error']}")
# Comparison chart
st.markdown("### 📊 Candidate Comparison")
score_dict = {f"#{i+1} {c['smiles']}": c['catalyst_score'] for i, c in enumerate(candidates[:5])}
fig_comp = plot_comparison_bar(score_dict, "Catalyst Scores", "Score (0-100)")
st.pyplot(fig_comp)
plt.close()
csv_report = export_discovery_batch_to_csv(candidates)
st.download_button(
label="📥 Download Catalyst Discovery Report (CSV)",
data=csv_report,
file_name="quantum_discovery_report.csv",
mime="text/csv",
)
# Save to history
save_result_to_history({
'type': 'AI Discovery',
'reaction': custom_reaction_equation if selected_reaction == "CUSTOM" else selected_reaction,
'candidates': candidates
})
else:
st.error("❌ Failed to generate candidates. Please try again.")
# ========================================================================
# PAGE: FEATURE 2 - LEARNING GAME
# ========================================================================
elif page == "🎮 Feature 2: Learning Game":
st.markdown('<p class="main-header">🎮 Interactive Catalyst Learning Game</p>', unsafe_allow_html=True)
st.markdown("""
### Test Your Chemistry Knowledge!
Choose a catalyst for the reaction and see how it compares to the ideal choice.
Get scored by **quantum machine learning** algorithms!
""")
# Game configuration
st.markdown("### 🎯 Challenge Configuration")
col1, col2 = st.columns(2)
with col1:
game_options = list(REACTION_DATABASE.keys()) + ["CUSTOM"]
game_reaction = st.selectbox(
"Select Reaction:",
options=game_options,
format_func=lambda x: REACTION_DATABASE[x]['name'] if x in REACTION_DATABASE else "Custom Reaction (Enter Equation)"
)
custom_game_equation = ""
parsed_game_reaction = None
if game_reaction == "CUSTOM":
custom_game_equation = st.text_input(
"Custom Reaction Equation:",
placeholder="e.g., CO + H2O -> CO2 + H2",
key="custom_game_equation",
)
if custom_game_equation:
parsed_game_reaction = parse_dynamic_reaction(custom_game_equation)
if parsed_game_reaction.get("error"):
st.error(parsed_game_reaction["error"])
else:
st.info(f"**Equation:** {parsed_game_reaction['equation']}")
else:
reaction_data = REACTION_DATABASE[game_reaction]
st.info(f"**Equation:** {reaction_data['equation']}")
# Show ideal catalysts hint
if st.checkbox("Show Hint") and game_reaction != "CUSTOM":
st.warning(f"💡 Ideal catalysts: {', '.join(reaction_data['ideal_catalysts'])}")
with col2:
user_catalyst = catalyst_input_widget(
"Your Catalyst Guess:",
key_prefix="learning",
placeholder="e.g., Pt, Fe, NiO, iron, platinum",
)
st.caption("Enter: element name, formula, or SMILES")
submit_guess = st.button("🎯 Submit & Get Scored", type="primary", use_container_width=True)
if submit_guess and user_catalyst:
# Validate input
validation = process_molecule_input(user_catalyst, max_atoms=6)
if validation['valid']:
user_smiles = validation['smiles']
st.success(f"✅ Valid catalyst: **{validation['formula']}** ({validation['atom_count']} atoms)")
with st.spinner("🧠 Quantum ML is evaluating your choice..."):
if game_reaction == "CUSTOM":
parsed_game_reaction = parse_dynamic_reaction(custom_game_equation) if custom_game_equation else {"error": "Please enter a custom reaction equation."}
if parsed_game_reaction.get("error"):
st.error(parsed_game_reaction["error"])
st.stop()
ideal_catalyst = parsed_game_reaction.get('ideal_catalysts', ['[Pt]'])[0]
scoring_reaction_key = _map_custom_reaction_to_qml_key(parsed_game_reaction)
pathway_reaction_input = parsed_game_reaction
else:
ideal_catalyst = reaction_data['ideal_catalysts'][0]
scoring_reaction_key = game_reaction
pathway_reaction_input = game_reaction
# Score user's choice
scoring_result = score_user_catalyst(user_smiles, ideal_catalyst, scoring_reaction_key)
if not scoring_result.get('error'):
st.markdown("---")
st.markdown("### 📊 Your Results")
# Overall score with colored background
score = scoring_result['overall_score']
if score >= 80:
st.markdown('<div class="success-box">', unsafe_allow_html=True)
st.markdown(f"## 🏆 Excellent! Score: {score:.1f}/100")
st.markdown("</div>", unsafe_allow_html=True)
elif score >= 60:
st.markdown('<div class="warning-box">', unsafe_allow_html=True)
st.markdown(f"## 👍 Good! Score: {score:.1f}/100")
st.markdown("</div>", unsafe_allow_html=True)
else:
st.markdown('<div class="error-box">', unsafe_allow_html=True)
st.markdown(f"## 💡 Keep Learning! Score: {score:.1f}/100")
st.markdown("</div>", unsafe_allow_html=True)
# Detailed breakdown
col_a, col_b, col_c = st.columns(3)
with col_a:
st.metric("QSVM Score", f"{scoring_result['qsvm_score']:.2f}/100")
with col_b:
st.metric("Feature Similarity", f"{scoring_result['feature_similarity']:.2f}%")
with col_c:
st.metric("Classification", scoring_result['classification'].upper())
# Feedback
st.markdown("### 💬 Feedback")
st.info(scoring_result['qsvm_feedback'])
# VQC Category
st.markdown(f"**Catalyst Category:** {scoring_result['vqc_category']}")
# Run full analysis
st.markdown("---")
st.markdown("### 🔬 Deep Dive Analysis")
if st.button("Run Full Quantum Simulation"):
with st.spinner("Running comprehensive analysis..."):
# VQE simulation
vqe_result = run_vqe_simulation(user_smiles)
col_vqe1, col_vqe2 = st.columns(2)
with col_vqe1:
if not vqe_result.get('error'):
st.metric("Ground State Energy", f"{vqe_result['energy']:.6f} Ha")
st.metric("VQE Iterations", vqe_result['iterations'])
st.metric("Qubits Used", vqe_result['num_qubits'])
show_simulation_provenance(vqe_result)
else:
st.error(f"VQE Error: {vqe_result['error']}")
with col_vqe2:
if not vqe_result.get('error'):
fig_conv = plot_convergence(vqe_result['convergence'])
st.pyplot(fig_conv)
plt.close()
# Reaction pathway
pathway = simulate_reaction_pathway(user_smiles, pathway_reaction_input)
if not pathway.get('error'):
st.markdown("### 🛤️ Reaction Energy Pathway")
col_path1, col_path2 = st.columns([2, 1])
with col_path1:
fig_path = plot_energy_landscape(
pathway['states'],
pathway['energies'],
"Reaction Pathway"
)
st.pyplot(fig_path)
plt.close()
with col_path2:
st.metric("Activation Barrier",
f"{pathway['activation_barrier_forward']*27.211:.2f} eV")
st.metric("Reaction Enthalpy",
f"{pathway['reaction_enthalpy']*27.211:.2f} eV")
st.metric("Pathway Score", f"{pathway['catalyst_score']:.2f}/100")
st.metric(
"Predicted Turnover Frequency (TOF)",
f"{pathway.get('turnover_frequency_s', 0.0):.2e} s^-1"
)
if pathway['is_ideal_catalyst']:
st.success("✨ This IS an ideal catalyst!")
else:
st.info("Try one of the ideal catalysts for comparison!")
# Save to history
save_result_to_history({
'type': 'Learning Game',
'reaction': custom_game_equation if game_reaction == "CUSTOM" else game_reaction,
'user_catalyst': user_catalyst,
'score': score
})
else:
st.error(f"❌ Scoring error: {scoring_result.get('error')}")
if "feature" in str(scoring_result.get('error', '')).lower():
st.info("Try a simpler catalyst input (single metal or small oxide), for example [Pt], [Fe], or [Ni]=O.")
else:
st.error(f"❌ Invalid catalyst: {validation['error']}")
# Show suggestions
from modules.molecule_validator import get_similar_molecules
suggestions = get_similar_molecules(user_catalyst, limit=5)
if suggestions:
st.info(f"💡 Did you mean: {', '.join(suggestions)}?")
# ========================================================================
# PAGE: QUANTUM VS CLASSICAL
# ========================================================================
elif page == "📊 Quantum vs Classical":
st.markdown('<p class="main-header">📊 Quantum vs Classical Comparison</p>', unsafe_allow_html=True)
st.markdown("""
### Demonstrate Quantum Advantage
Compare quantum algorithms (VQE, QSVM) against classical methods (HF, DFT, ML)
to see the power of quantum computing in action!
""")
# Comparison type
comparison_type = st.radio(
"Comparison Type:",
["Chemistry Methods (VQE vs HF/DFT)", "Machine Learning (QSVM vs Classical ML)", "Full Comparison"]
)
# Molecule/Catalyst input
test_molecule = catalyst_input_widget(
"Enter Molecule/Catalyst:",
key_prefix="comparison",
placeholder="e.g., H2, H2O, Pt, Fe",
)
if comparison_type in ["Machine Learning (QSVM vs Classical ML)", "Full Comparison"]:
ml_reaction = st.selectbox(
"Reaction for ML Comparison:",
options=list(REACTION_DATABASE.keys()),
format_func=lambda x: REACTION_DATABASE[x]['name']
)
run_comparison = st.button("⚡ Run Comparison", type="primary", use_container_width=True)
if run_comparison and test_molecule:
# Validate molecule
validation = process_molecule_input(test_molecule, max_atoms=6)
if validation['valid']:
smiles = validation['smiles']
st.success(f"✅ Analyzing: **{validation['formula']}**")
# Chemistry Comparison
if comparison_type in ["Chemistry Methods (VQE vs HF/DFT)", "Full Comparison"]:
st.markdown("---")
st.markdown("### ⚛️ Quantum Chemistry Comparison")
with st.spinner("Running quantum and classical simulations..."):
chem_comp = compare_quantum_vs_classical_chemistry(smiles)
if not chem_comp.get('error'):
col1, col2, col3 = st.columns(3)
with col1:
st.markdown("#### VQE (Quantum)")
st.metric("Energy", f"{chem_comp['vqe']['energy']:.6f} Ha")
st.metric("Iterations", chem_comp['vqe']['iterations'])
st.caption(chem_comp['vqe']['method'])
show_simulation_provenance(chem_comp['vqe'])
with col2:
st.markdown("#### Hartree-Fock")
st.metric("Energy", f"{chem_comp['hf']['energy']:.6f} Ha")
st.caption(chem_comp['hf']['description'])
with col3:
st.markdown("#### DFT (B3LYP)")
st.metric("Energy", f"{chem_comp['dft']['energy']:.6f} Ha")
st.caption(chem_comp['dft']['description'])
# Comparison visualization
st.markdown("#### Energy Comparison")
energy_dict = {
"VQE\n(Quantum)": chem_comp['vqe']['energy'],
"HF\n(Classical)": chem_comp['hf']['energy'],
"DFT\n(Classical)": chem_comp['dft']['energy']
}
fig_energy = plot_comparison_bar(energy_dict, "Energy Comparison", "Energy (Hartree)")
st.pyplot(fig_energy)
plt.close()
# Quantum advantage analysis
st.markdown("#### 🎯 Quantum Advantage Analysis")
comparison = chem_comp['comparison']
correlation_energy = chem_comp['correlation_energy']
st.metric(
label="Electron Correlation Energy Captured",
value=f"{correlation_energy:.4f} Ha"
)
st.info(
"Hartree-Fock (Classical) ignores electron-electron correlation. "
"The VQE (Quantum) captures this correlation, which is critical for "
"accurate catalyst binding energies."
)
adv_col1, adv_col2 = st.columns(2)
with adv_col1:
st.markdown("**VQE vs HF:**")
st.metric("Energy Difference",
f"{comparison['vs_hf']['energy_difference']:.6f} Ha")
st.metric("Improvement",
f"{comparison['vs_hf']['percent_improvement']:.4f}%")
if comparison['vs_hf']['quantum_is_better']:
st.success("✅ VQE is more accurate!")
else:
st.info("HF approximation sufficient")
with adv_col2:
st.markdown("**VQE vs DFT:**")
st.metric("Energy Difference",
f"{comparison['vs_dft']['energy_difference']:.6f} Ha")
st.metric("Improvement",
f"{comparison['vs_dft']['percent_improvement']:.4f}%")
if comparison['vs_dft']['quantum_is_better']:
st.success("✅ VQE is more accurate!")
else:
st.info("DFT approximation sufficient")
# Summary
summary = chem_comp['summary']
if summary['quantum_advantage_demonstrated']:
st.success(f"🏆 **Quantum Advantage Demonstrated!** Most accurate method: {summary['most_accurate']}")
else:
st.info(f"Most accurate: {summary['most_accurate']}")
# VQE Convergence
if st.checkbox("Show VQE Convergence Details"):
fig_conv = plot_convergence(chem_comp['vqe']['convergence'])
st.pyplot(fig_conv)
plt.close()
else:
st.error(f"Error: {chem_comp.get('error')}")
# ML Comparison
if comparison_type in ["Machine Learning (QSVM vs Classical ML)", "Full Comparison"]:
st.markdown("---")
st.markdown("### 🤖 Machine Learning Comparison")
with st.spinner("Running quantum and classical ML..."):
ml_comp = compare_quantum_vs_classical_ml(smiles, ml_reaction)
if not ml_comp.get('error'):
col1, col2 = st.columns(2)
with col1:
st.markdown("#### Quantum ML (QSVM)")
qsvm = ml_comp['quantum_ml']
st.metric("Score", f"{qsvm['score']:.2f}/100")
st.metric("Classification", qsvm['classification'].upper())
st.metric("Confidence", f"{qsvm['confidence']:.1f}%")
st.caption(qsvm['method'])
with col2:
st.markdown("#### Classical ML (Average)")
avg_score = ml_comp['comparison']['avg_classical_score']
st.metric("Average Score", f"{avg_score:.2f}/100")
st.markdown("**Individual Methods:**")
classical = ml_comp['classical_ml']
st.write(f"- RF: {classical['random_forest']['score']:.2f}")
st.write(f"- SVM: {classical['svm']['score']:.2f}")
st.write(f"- GB: {classical['gradient_boosting']['score']:.2f}")
# ML Comparison visualization
st.markdown("#### Scoring Comparison")
ml_dict = {
"QSVM\n(Quantum)": qsvm['score'],
"Random\nForest": classical['random_forest']['score'],
"SVM\n(Classical)": classical['svm']['score'],
"Gradient\nBoosting": classical['gradient_boosting']['score']
}
fig_ml = plot_comparison_bar(ml_dict, "ML Method Comparison", "Score (0-100)")
st.pyplot(fig_ml)
plt.close()
# Quantum ML advantage
advantage = ml_comp['comparison']['quantum_advantage']
if advantage > 5:
st.success(f"🎯 **Quantum ML Advantage: {advantage:.2f} points!**")
else:
st.info(f"Methods are comparable (difference: {advantage:.2f} points)")
else:
st.error(f"Error: {ml_comp.get('error')}")
# Save comparison
save_result_to_history({
'type': 'Comparison',
'molecule': test_molecule,
'comparison_type': comparison_type
})
else:
st.error(f"❌ Invalid molecule: {validation['error']}")
# ========================================================================
# PAGE: MOLECULE EXPLORER
# ========================================================================
elif page == "🧪 Molecule Explorer":
st.markdown('<p class="main-header">🧪 Molecule Explorer</p>', unsafe_allow_html=True)
st.markdown("""
### Explore Molecular Properties & Quantum Simulations
Validate any molecule, visualize its structure, run VQE simulations,
and analyze its properties.
""")
# Input section
col1, col2 = st.columns([2, 1])
with col1:
explore_input = st.text_input(
"Enter Molecule:",
placeholder="e.g., water, H2O, methane, Pt, benzene",
help="Accepts: common names, formulas, or SMILES"
)
with col2:
max_atoms = st.number_input("Max Atoms:", 1, 20, 6)
explore_btn = st.button("🔍 Analyze Molecule", type="primary", use_container_width=True)
# Show supported molecules
with st.expander("📚 View All Supported Molecules"):
supported = get_supported_molecules()
st.write(f"Total: {len(supported)} molecules")
# Group by type
diatomic = [s for s in supported if len(s) <= 7 and '[' in s and 'H' in s]
metals = [s for s in supported if '[' in s and len(s) <= 5]
organic = [s for s in supported if 'C' in s or 'O' in s and len(s) > 1]
col_a, col_b, col_c = st.columns(3)
with col_a:
st.markdown("**Diatomic:**")
st.write(", ".join(diatomic[:10]))
with col_b:
st.markdown("**Metals:**")
st.write(", ".join(metals[:10]))
with col_c:
st.markdown("**Organic:**")
st.write(", ".join(organic[:10]))
if explore_btn and explore_input:
# Validate
validation = process_molecule_input(explore_input, max_atoms=max_atoms)
if validation['valid']:
smiles = validation['smiles']
st.success("✅ Valid Molecule!")
# Display properties
st.markdown("### 📋 Molecular Properties")
prop_col1, prop_col2, prop_col3, prop_col4 = st.columns(4)
with prop_col1:
st.metric("Formula", validation['formula'])
with prop_col2:
st.metric("Atoms", validation['atom_count'])
with prop_col3:
st.metric("Molecular Weight", f"{validation['mol_weight']:.2f}")
with prop_col4:
st.metric("Heavy Atoms", validation['heavy_atom_count'])
st.write(f"**Elements:** {', '.join(validation['elements'])}")
st.write(f"**SMILES:** `{smiles}`")
# 3D Visualization
st.markdown("---")
st.markdown("### 🔬 3D Structure")
try:
mol_3d = generate_3d_molecule(smiles)
if mol_3d:
show_molecule_3d(mol_3d)
else:
st.warning("3D generation not available for this molecule")
except:
st.warning("3D visualization not available for this molecule")
# Quantum Simulation
st.markdown("---")
st.markdown("### ⚛️ Quantum Simulation")
explorer_noise = st.toggle(
"🎛️ Simulate Quantum Hardware Noise (NISQ)",
key="noise_explorer",
value=False,
)
if st.button("Run VQE Simulation"):
with st.spinner("Running VQE..."):
vqe_result = run_vqe_simulation(smiles, method="VQE", apply_noise=explorer_noise)
if not vqe_result.get('error'):
sim_col1, sim_col2 = st.columns([1, 2])
with sim_col1:
st.metric("Ground State Energy", f"{vqe_result['energy']:.6f} Ha")
st.metric("Iterations", vqe_result['iterations'])
st.metric("Qubits", vqe_result['num_qubits'])
st.caption(vqe_result['method'])
show_simulation_provenance(vqe_result)
# Compare with HF
if st.checkbox("Compare with HF"):
comp = compare_methods(smiles)
if not comp.get('error'):
st.metric("HF Energy", f"{comp['hf']['energy']:.6f} Ha")
st.metric("Difference", f"{comp['energy_difference']:.6f} Ha")
with sim_col2:
fig_conv = plot_convergence(
vqe_result['convergence'],
f"VQE Convergence: {validation['formula']}"
)
st.pyplot(fig_conv)
plt.close()
else:
st.error(f"VQE Error: {vqe_result['error']}")
# Test in reactions
st.markdown("---")
st.markdown("### 🧬 Test as Catalyst")
test_reaction = st.selectbox(
"Test in Reaction:",
options=list(REACTION_DATABASE.keys()),
format_func=lambda x: REACTION_DATABASE[x]['name']
)
if st.button("Run Catalyst Test"):
with st.spinner("Testing catalyst..."):
pathway = simulate_reaction_pathway(smiles, test_reaction)
if not pathway.get('error'):
cat_col1, cat_col2 = st.columns([2, 1])
with cat_col1:
fig_path = plot_energy_landscape(
pathway['states'],
pathway['energies'],
f"Pathway: {validation['formula']}"
)
st.pyplot(fig_path)
plt.close()
with cat_col2:
st.metric("Catalyst Score", f"{pathway['catalyst_score']:.2f}/100")
st.metric("Activation Barrier",
f"{pathway['activation_barrier_forward']*27.211:.2f} eV")
st.metric("Reaction Enthalpy",
f"{pathway['reaction_enthalpy']*27.211:.2f} eV")
if pathway['is_ideal_catalyst']:
st.success("⭐ Ideal Catalyst!")
st.write(f"**Type:** {pathway['reaction_type']}")
st.write(f"**Method:** {pathway['method']}")
else:
st.error(f"Error: {pathway['error']}")
else:
st.error(f"❌ {validation['error']}")
# Suggestions
from modules.molecule_validator import get_similar_molecules
suggestions = get_similar_molecules(explore_input)
if suggestions:
st.info(f"💡 Did you mean: {', '.join(suggestions[:5])}?")
# ========================================================================
# PAGE: RESULTS & EXPORT
# ========================================================================
elif page == "📈 Results & Export":
st.markdown('<p class="main-header">📈 Results & Export</p>', unsafe_allow_html=True)
st.markdown("""
### Session History & Data Export
Review your session history and export results for further analysis.
""")
if st.session_state.results_history:
st.success(f"📊 {len(st.session_state.results_history)} results in session")
# Display history
st.markdown("### 📋 Session History")
for i, result in enumerate(reversed(st.session_state.results_history)):
with st.expander(f"#{len(st.session_state.results_history)-i}: {result['type']} - {result['timestamp']}"):
st.json(result, expanded=False)
# Export options
st.markdown("---")
st.markdown("### 💾 Export Options")
export_col1, export_col2, export_col3 = st.columns(3)
with export_col1:
if st.button("📄 Export JSON"):
json_data = json.dumps(st.session_state.results_history, indent=2)
st.download_button(
label="Download JSON",
data=json_data,
file_name=f"quantum_catalyst_results_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json",
mime="application/json"
)
with export_col2:
if st.button("📊 Export CSV"):
# Flatten data for CSV
csv_data = []
for result in st.session_state.results_history:
csv_data.append({
'Timestamp': result['timestamp'],
'Type': result['type'],
'Details': str(result)
})
df = pd.DataFrame(csv_data)
csv = df.to_csv(index=False)
st.download_button(
label="Download CSV",
data=csv,
file_name=f"quantum_catalyst_results_{datetime.now().strftime('%Y%m%d_%H%M%S')}.csv",
mime="text/csv"
)
with export_col3:
if st.button("🗑️ Clear History"):
st.session_state.results_history = []
st.rerun()
else:
st.info("No results yet. Run some analyses first!")
# ========================================================================
# FOOTER
# ========================================================================
st.markdown("---")
st.markdown("""
<div style='text-align: center; color: gray;'>
<p><strong>Quantum Catalyst Discovery Platform</strong> | Built with Qiskit, RDKit, and Streamlit</p>
<p>Powered by Real Quantum Computing & Machine Learning</p>
</div>
""", unsafe_allow_html=True)