Source code for squadds.ui.app

import json
import os

import numpy as np
import plotly.express as px
import streamlit as st

from squadds import Analyzer, SQuADDS_DB
from squadds.interpolations.physics import ScalingInterpolator
from squadds.ui.utils_query import (extract_cavity_only_params,
                                    find_closest_cached)

# SQuADDS logo and links
LOGO_PATH = "https://raw.githubusercontent.com/LFL-Lab/SQuADDS/master/docs/_static/images/squadds_logo_transparent.png"
HF_LINK = "https://huggingface.co/datasets/SQuADDS/SQuADDS_DB"
GITHUB_LINK = "https://github.com/LFL-Lab/SQuADDS"
DOCSITE_LINK = "https://lfl-lab.github.io/SQuADDS/"
PAPER_LINK = "https://quantum-journal.org/papers/q-2024-09-09-1465/"
DEEPWIKI_LINK = "https://deepwiki.com/LFL-Lab/SQuADDS/1-overview"
PORTAL_LINK = "https://squadds-portal.vercel.app"


[docs] def convert_numpy_to_list(obj): if isinstance(obj, np.ndarray): return obj.tolist() elif isinstance(obj, dict): return {k: convert_numpy_to_list(v) for k, v in obj.items()} elif isinstance(obj, list): return [convert_numpy_to_list(v) for v in obj] else: return obj
[docs] def initialize_session_state(): """Initialize session state variables""" if 'db' not in st.session_state: st.session_state.db = SQuADDS_DB() if 'supported_qubits' not in st.session_state: qubits = st.session_state.db.get_component_names("qubit") st.session_state.supported_qubits = qubits if 'supported_cavities' not in st.session_state: cavities = st.session_state.db.get_component_names("cavity_claw") st.session_state.supported_cavities = cavities
[docs] def main(): st.set_page_config( page_title="SQuADDS WebUI", page_icon="🔍", layout="wide" ) initialize_session_state() db = st.session_state.db # SQuADDS logo and title st.markdown(f""" <div style='display: flex; align-items: center; gap: 2rem;'> <img src='{LOGO_PATH}' alt='SQuADDS Logo' style='height: 80px;'> <h1 style='margin-bottom: 0;'>SQuADDS WebUI</h1> </div> """, unsafe_allow_html=True) # Links row st.markdown(f""" <div style='margin-bottom: 1.5rem;'> <a href='{HF_LINK}' target='_blank' style='margin-right: 1.5rem;'>🤗 HuggingFace</a> <a href='{GITHUB_LINK}' target='_blank' style='margin-right: 1.5rem;'>🐙 GitHub</a> <a href='{DOCSITE_LINK}' target='_blank' style='margin-right: 1.5rem;'>📖 Docsite</a> <a href='{PAPER_LINK}' target='_blank' style='margin-right: 1.5rem;'>📄 Paper</a> <a href='{DEEPWIKI_LINK}' target='_blank' style='margin-right: 1.5rem;'>🧠 DeepWiki</a> <a href='{PORTAL_LINK}' target='_blank'>🌐 Portal</a> </div> """, unsafe_allow_html=True) st.markdown(""" Find the closest superconducting device designs based on your target parameters. """) # Sidebar for system selection with st.sidebar: st.header("System Configuration") # Track previous selections prev_system_type = st.session_state.get('prev_system_type', None) prev_qubit_type = st.session_state.get('prev_qubit_type', None) prev_cavity_type = st.session_state.get('prev_cavity_type', None) prev_coupling_type = st.session_state.get('prev_coupling_type', None) prev_resonator_type = st.session_state.get('prev_resonator_type', None) system_type = st.selectbox( "Select System Type", ["Qubit-Cavity", "Qubit Only", "Cavity Only"], index=0 ) # Qubit selection if system_type in ["Qubit-Cavity", "Qubit Only"]: qubit_type = st.selectbox( "Select Qubit Type", st.session_state.supported_qubits, index=st.session_state.supported_qubits.index("TransmonCross") if "TransmonCross" in st.session_state.supported_qubits else 0 ) if qubit_type == "CLT": st.warning("⚠️ 'CLT' is not a valid qubit type for design search. Please select another qubit type.") else: qubit_type = None # Cavity selection if system_type in ["Qubit-Cavity", "Cavity Only"]: cavity_type = st.selectbox( "Select Cavity Type", st.session_state.supported_cavities, index=st.session_state.supported_cavities.index("RouteMeander") if "RouteMeander" in st.session_state.supported_cavities else 0 ) if cavity_type == "CLT": st.warning("⚠️ 'CLT' is not a valid cavity type for design search. Please select another cavity type.") else: cavity_type = None # Coupling type for Qubit-Cavity show_num_cpu = False num_cpu = "auto" if system_type == "Qubit-Cavity": coupling_type = st.selectbox( "Select Coupling Type", ["Capacitive"], index=0 ) resonator_type = st.selectbox( "Select Resonator Type", ["quarter", "half"], index=0 ) if resonator_type == "half": show_num_cpu = True elif system_type == "Cavity Only": coupling_type = None resonator_type = st.selectbox( "Select Resonator Type", ["quarter", "half"], index=0 ) else: coupling_type = None resonator_type = None # Dynamic num_cpu for Qubit-Cavity + half if show_num_cpu: cpu_options = ["auto"] + [str(i) for i in range(1, os.cpu_count() + 1)] num_cpu = st.selectbox("Number of CPUs", cpu_options, index=0) # Reset results if any major config changes if ( prev_system_type != system_type or prev_qubit_type != qubit_type or prev_cavity_type != cavity_type or prev_coupling_type != coupling_type or prev_resonator_type != resonator_type ): for k in ["results", "params", "analyzer", "data_qubit", "data_cpw", "data_coupler", "LJs"]: if k in st.session_state: del st.session_state[k] # Update previous selections st.session_state.prev_system_type = system_type st.session_state.prev_qubit_type = qubit_type st.session_state.prev_cavity_type = cavity_type st.session_state.prev_coupling_type = coupling_type st.session_state.prev_resonator_type = resonator_type st.divider() st.markdown("### Search Settings") num_results = st.slider("Number of results", 1, 10, 1) # Default to 1 # Main content area col1, col2 = st.columns([2, 3]) with col1: st.subheader("Target Hamiltonian Parameters") # Parameter input fields based on system type params = {} if system_type in ["Qubit-Cavity", "Qubit Only"]: params["qubit_frequency_GHz"] = st.number_input("Qubit Frequency (GHz)", 3.0, 8.0, 4.0) params["anharmonicity_MHz"] = st.number_input("Anharmonicity (MHz)", -500.0, -50.0, -200.0) if system_type in ["Qubit-Cavity", "Cavity Only"]: params["cavity_frequency_GHz"] = st.number_input("Cavity Frequency (GHz)", 5.0, 12.0, 9.2) params["kappa_kHz"] = st.number_input("Kappa (kHz)", 10.0, 1000.0, 80.0) if system_type == "Qubit-Cavity": params["g_MHz"] = st.number_input("Coupling Strength g (MHz)", 10.0, 200.0, 70.0) params["resonator_type"] = resonator_type elif system_type == "Cavity Only": params["resonator_type"] = resonator_type # No resonator_type for Qubit Only if st.button("Find Designs", type="primary"): # Show message for half-wave Qubit-Cavity search if system_type == "Qubit-Cavity" and resonator_type == "half": st.info("Half-wave Qubit-Cavity searches may take a while due to the size of the dataset and parallel processing. If you'd like to help speed this up, please consider contributing code! 🙏🏽 [Contribute on GitHub](https://github.com/LFL-Lab/SQuADDS)") # Block Cavity Only + half-wave and show info message if system_type == "Cavity Only" and resonator_type == "half": st.info("Half-wave Cavity Only search is not yet supported. We're working on this feature!") return # Track last-used system config for smart skip logic last_config = st.session_state.get('last_system_config', None) current_config = { 'system_type': system_type, 'qubit_type': qubit_type, 'cavity_type': cavity_type, 'resonator_type': resonator_type, 'num_cpu': num_cpu, 'num_results': num_results } skip_df_gen = False if last_config is not None: # If only params changed, skip df gen config_keys = ['system_type', 'qubit_type', 'cavity_type', 'resonator_type', 'num_cpu', 'num_results'] if all(current_config[k] == last_config[k] for k in config_keys): skip_df_gen = True with st.spinner("Searching for closest designs..."): try: results, analyzer = find_closest_cached(system_type, qubit_type, cavity_type, resonator_type, params, num_results, num_cpu, skip_df_gen) st.session_state.results = results st.session_state.params = params st.session_state.analyzer = analyzer st.session_state.last_system_config = current_config if system_type == "Qubit-Cavity": st.session_state.data_qubit = convert_numpy_to_list(analyzer.get_qubit_options(results)) st.session_state.data_cpw = convert_numpy_to_list(analyzer.get_cpw_options(results)) st.session_state.data_coupler = convert_numpy_to_list(analyzer.get_coupler_options(results)) st.session_state.LJs = convert_numpy_to_list(analyzer.get_Ljs(results)) # --- Interpolated Results for Qubit-Cavity --- try: interpolator = ScalingInterpolator(analyzer, params) interpolated_df = interpolator.get_design() st.session_state.interpolated_df = interpolated_df except Exception as e: st.session_state.interpolated_df = None st.session_state.interpolated_error = str(e) elif system_type == "Qubit Only": st.session_state.data_qubit = convert_numpy_to_list(analyzer.get_qubit_options(results)) st.session_state.data_cpw = None st.session_state.data_coupler = None st.session_state.LJs = None st.session_state.interpolated_df = None elif system_type == "Cavity Only": # Use utility function for extraction cpw_vals, coupler_vals = extract_cavity_only_params(results) st.session_state.data_qubit = None st.session_state.data_cpw = cpw_vals st.session_state.data_coupler = coupler_vals st.session_state.LJs = None st.session_state.interpolated_df = None except Exception as e: st.error(f"Error: {str(e)}") st.error("Please check your system configuration and parameters.") with col2: if 'results' in st.session_state: st.subheader("Design Space Results") # Create tabs for different visualizations tab_labels = ["Design Parameters", "ℋ Parameter Space Plots", "`design` Options"] if system_type == "Qubit-Cavity": tab_labels.append("Interpolated Results") tabs = st.tabs(tab_labels) tab1, tab2, tab3 = tabs[:3] if system_type == "Qubit-Cavity": tab4 = tabs[3] with tab1: # Design Parameters Tab (was previously Extracted Data) analyzer = st.session_state.analyzer results = st.session_state.results if system_type == "Qubit Only": keys_cross = ["cross_gap", "cross_length", "cross_width"] keys_claw = ["claw_gap", "claw_length", "claw_width", "ground_spacing"] cross_vals = {k: [] for k in keys_cross} claw_vals = {k: [] for k in keys_claw} for idx, row in results.iterrows(): opts = convert_numpy_to_list(row['design_options']) # Cross parameters for k in keys_cross: v = opts.get(k) if v is not None: cross_vals[k].append(v) # Claw parameters (nested in connection_pads.readout) claw = opts.get('connection_pads', {}).get('readout', {}) for k in keys_claw: v = claw.get(k) if v is not None: claw_vals[k].append(v) LJs = convert_numpy_to_list(analyzer.get_Ljs(results)) st.markdown("**TransmonCross (Qubit) Values**") st.code(json.dumps(cross_vals, indent=2)) st.markdown("**Claw Values**") st.code(json.dumps(claw_vals, indent=2)) st.markdown("**Josephson Inductances (LJs, nH)**") st.code(json.dumps(LJs, indent=2)) elif system_type == "Cavity Only": # Use extracted values from session state cpw_vals = st.session_state.data_cpw coupler_vals = st.session_state.data_coupler st.markdown("**RouteMeander (CPW) Values**") st.code(json.dumps(cpw_vals, indent=2)) st.markdown("**CoupledLineTee (Coupler) Values**") st.code(json.dumps(coupler_vals, indent=2)) else: # Qubit-Cavity: show with QComponent names and 'Values' if st.session_state.data_qubit is not None: st.markdown("**TransmonCross (Qubit) Values**") st.code(json.dumps(st.session_state.data_qubit, indent=2)) if st.session_state.data_cpw is not None: st.markdown("**RouteMeander (CPW) Values**") st.code(json.dumps(st.session_state.data_cpw, indent=2)) if st.session_state.data_coupler is not None: # Try to get coupler type from results coupler_type = results.iloc[0]['coupler_type'] if 'coupler_type' in results.iloc[0] else 'CoupledLineTee' coupler_qcomp = "CapNInterdigital" if coupler_type in ["CapNInterdigital", "CapNInterdigitalTee"] else "CoupledLineTee" st.markdown(f"**{coupler_qcomp} (Coupler) Values**") st.code(json.dumps(st.session_state.data_coupler, indent=2)) if st.session_state.LJs is not None: st.markdown("**Josephson Inductances (LJs, nH)**") st.code(json.dumps(st.session_state.LJs, indent=2)) with tab2: # Parameter Space Plots (was previously tab1, now tab2) try: results = st.session_state.results target = st.session_state.params if system_type in ["Qubit-Cavity", "Qubit Only"]: fig1 = px.scatter( results, x="qubit_frequency_GHz", y="anharmonicity_MHz", title="Qubit Frequency vs Anharmonicity", labels={ "qubit_frequency_GHz": "Qubit Frequency (GHz)", "anharmonicity_MHz": "Anharmonicity (MHz)" } ) fig1.add_scatter( x=[target["qubit_frequency_GHz"]], y=[target["anharmonicity_MHz"]], mode="markers", marker=dict(size=15, symbol="x", color="red"), name="Target" ) st.plotly_chart(fig1, use_container_width=True) if system_type in ["Qubit-Cavity", "Cavity Only"]: fig2 = px.scatter( results, x="cavity_frequency_GHz", y="kappa_kHz", title="Cavity Frequency vs Kappa", labels={ "cavity_frequency_GHz": "Cavity Frequency (GHz)", "kappa_kHz": "Kappa (kHz)" } ) fig2.add_scatter( x=[target["cavity_frequency_GHz"]], y=[target["kappa_kHz"]], mode="markers", marker=dict(size=15, symbol="x", color="red"), name="Target" ) st.plotly_chart(fig2, use_container_width=True) except Exception as e: st.error(f"Error plotting results: {str(e)}") with tab3: # Design Options (was previously tab2, now tab3) for i, design in st.session_state.results.iterrows(): with st.expander(f"Design {i+1}"): c1, c2 = st.columns(2) if system_type == "Qubit Only": with c1: st.markdown("#### Qubit Parameters") st.write(f"Frequency: {design['qubit_frequency_GHz']:.2f} GHz") st.write(f"Anharmonicity: {design['anharmonicity_MHz']:.2f} MHz") if 'EJ' in design and 'EC' in design: st.write(f"EJ/EC: {design['EJ']/design['EC']:.2f}") qubit_options = convert_numpy_to_list(design.get('design_options', {})) st.code(json.dumps(qubit_options, indent=2)) if st.button(f"Copy Qubit Design {i+1}", key=f"copy_qubit_{i}"): st.write("Qubit design copied to clipboard! ✅") st.session_state[f"clipboard_qubit_{i}"] = json.dumps(qubit_options) elif system_type == "Cavity Only": with c2: st.markdown("#### Cavity Parameters") st.write(f"Frequency: {design['cavity_frequency_GHz']:.2f} GHz") st.write(f"Kappa: {design['kappa_kHz']:.2f} kHz") if 'g_MHz' in design: st.write(f"Coupling g: {design['g_MHz']:.2f} MHz") cavity_options = convert_numpy_to_list(design.get('design_options', {})) st.code(json.dumps(cavity_options, indent=2)) if st.button(f"Copy Cavity Design {i+1}", key=f"copy_cavity_{i}"): st.write("Cavity design copied to clipboard! ✅") st.session_state[f"clipboard_cavity_{i}"] = json.dumps(cavity_options) elif system_type == "Qubit-Cavity": with c1: st.markdown("#### Qubit Parameters") st.write(f"Frequency: {design['qubit_frequency_GHz']:.2f} GHz") st.write(f"Anharmonicity: {design['anharmonicity_MHz']:.2f} MHz") if 'EJ' in design and 'EC' in design: st.write(f"EJ/EC: {design['EJ']/design['EC']:.2f}") qubit_options = convert_numpy_to_list(design['design_options'].get('qubit_options', {})) st.code(json.dumps(qubit_options, indent=2)) if st.button(f"Copy Qubit Design {i+1}", key=f"copy_qubit_{i}"): st.write("Qubit design copied to clipboard! ✅") st.session_state[f"clipboard_qubit_{i}"] = json.dumps(qubit_options) with c2: st.markdown("#### Cavity Parameters") st.write(f"Frequency: {design['cavity_frequency_GHz']:.2f} GHz") st.write(f"Kappa: {design['kappa_kHz']:.2f} kHz") if 'g_MHz' in design: st.write(f"Coupling g: {design['g_MHz']:.2f} MHz") cavity_options = convert_numpy_to_list(design['design_options'].get('cavity_claw_options', {})) st.code(json.dumps(cavity_options, indent=2)) if st.button(f"Copy Cavity Design {i+1}", key=f"copy_cavity_{i}"): st.write("Cavity design copied to clipboard! ✅") st.session_state[f"clipboard_cavity_{i}"] = json.dumps(cavity_options) # Show full design options with copy button if st.checkbox(f"Show Full Design Options {i+1}", key=f"design_{i}"): st.code(json.dumps(convert_numpy_to_list(design['design_options']), indent=2)) if st.button(f"Copy Full Design {i+1}", key=f"copy_full_{i}"): st.write("Full design copied to clipboard! ✅") st.session_state[f"clipboard_full_{i}"] = json.dumps(convert_numpy_to_list(design['design_options'])) if system_type == "Qubit-Cavity": with tab4: # Interpolated Results Tab interpolated_df = st.session_state.get("interpolated_df", None) if interpolated_df is None: st.warning(st.session_state.get("interpolated_error", "No interpolated results available.")) else: for i, row in interpolated_df.iterrows(): # Extract values for Design Parameters (mirroring main tab, but from interpolated data) # Qubit keys_cross = ["cross_gap", "cross_length", "cross_width"] keys_claw = ["claw_gap", "claw_length", "claw_width", "ground_spacing"] cross_vals = {k: [] for k in keys_cross} claw_vals = {k: [] for k in keys_claw} qubit_opts = convert_numpy_to_list(row['design_options'].get('qubit_options', {})) for k in keys_cross: v = qubit_opts.get(k) if v is not None: cross_vals[k].append(v) claw = qubit_opts.get('connection_pads', {}).get('readout', {}) for k in keys_claw: v = claw.get(k) if v is not None: claw_vals[k].append(v) # CPW cpw_keys = ["total_length", "trace_gap", "trace_width"] cpw_vals = {k: [] for k in cpw_keys} cpw_opts = convert_numpy_to_list(row['design_options'].get('cavity_claw_options', {}).get('cpw_opts', {}).get('left_options', {})) for k in cpw_keys: v = cpw_opts.get(k) if v is not None: cpw_vals[k].append(v) # Coupler coupler_keys = ["coupling_length", "coupling_space", "down_length", "orientation", "prime_gap", "prime_width", "second_gap", "second_width", "cap_distance", "cap_gap", "cap_gap_ground", "cap_width", "finger_count", "finger_length"] coupler_vals = {k: [] for k in coupler_keys} coupler_opts = convert_numpy_to_list(row['design_options'].get('cavity_claw_options', {}).get('coupler_options', {})) for k in coupler_keys: v = coupler_opts.get(k) if v is not None: coupler_vals[k].append(v) with st.expander(f"Interpolated Design {i+1} - Design Parameters"): st.markdown("**TransmonCross (Qubit) Values**") st.code(json.dumps(cross_vals, indent=2)) st.markdown("**Claw Values**") st.code(json.dumps(claw_vals, indent=2)) st.markdown("**RouteMeander (CPW) Values**") st.code(json.dumps(cpw_vals, indent=2)) coupler_type = row['design_options'].get('cavity_claw_options', {}).get('coupler_type', 'CoupledLineTee') coupler_qcomp = "CapNInterdigital" if coupler_type in ["CapNInterdigital", "CapNInterdigitalTee"] else "CoupledLineTee" st.markdown(f"**{coupler_qcomp} (Coupler) Values**") st.code(json.dumps(coupler_vals, indent=2)) if 'LJ' in row: st.markdown("**Josephson Inductance (LJ, nH)**") st.code(json.dumps(row['LJ'], indent=2)) with st.expander(f"Interpolated Design {i+1} - `design` Options"): st.markdown("#### Qubit Parameters") st.code(json.dumps(qubit_opts, indent=2)) st.markdown("#### Cavity Parameters") cavity_opts = convert_numpy_to_list(row['design_options'].get('cavity_claw_options', {})) st.code(json.dumps(cavity_opts, indent=2)) st.markdown("#### Coupler Parameters") st.code(json.dumps(coupler_opts, indent=2)) st.markdown("#### CPW Parameters") st.code(json.dumps(cpw_opts, indent=2)) # Simple feedback link in the bottom right corner st.markdown( """ <div style="position: fixed; bottom: 16px; right: 24px; z-index: 9999;"> <a href="mailto:shanto@usc.edu?subject=SQuADDS%20WebUI%20Feedback%20or%20Feature%20Request&body=Please%20describe%20your%20bug%2C%20feature%20request%2C%20or%20feedback%20below%3A%0A%0A" style="color: #3578e5; font-size: 0.95em; text-decoration: underline; background: rgba(255,255,255,0.85); padding: 2px 10px; border-radius: 1em; box-shadow: 0 2px 8px rgba(0,0,0,0.04);"> 💬 Feedback </a> </div> """, unsafe_allow_html=True, )
if __name__ == "__main__": main()