import streamlit as st import subprocess import os import json import numpy as np import plotly.graph_objects as go from plotly.subplots import make_subplots from PIL import Image import time import io # Set page config with wider layout st.set_page_config( page_title="Eigenvalue Analysis Dashboard", page_icon="📊", layout="wide", initial_sidebar_state="expanded" ) # Apply custom CSS for a dashboard-like appearance st.markdown(""" """, unsafe_allow_html=True) # Dashboard Header st.markdown('

Eigenvalue Analysis Dashboard

', unsafe_allow_html=True) # Create output directory in the current working directory current_dir = os.getcwd() output_dir = os.path.join(current_dir, "output") os.makedirs(output_dir, exist_ok=True) # Compile the C++ code at runtime cpp_file = os.path.join(current_dir, "app.cpp") executable = os.path.join(current_dir, "eigen_analysis") # Two-column layout for the dashboard left_column, right_column = st.columns([1, 3]) with left_column: st.markdown('
', unsafe_allow_html=True) st.markdown('
Control Panel
', unsafe_allow_html=True) # Check if cpp file exists and compile if necessary if not os.path.exists(cpp_file): st.error(f"C++ source file not found at: {cpp_file}") st.stop() # Compile the C++ code with the right OpenCV libraries if not os.path.exists(executable) or st.button("Recompile C++ Code"): with st.spinner("Compiling C++ code..."): compile_commands = [ f"g++ -o {executable} {cpp_file} `pkg-config --cflags --libs opencv4` -std=c++11", f"g++ -o {executable} {cpp_file} `pkg-config --cflags --libs opencv` -std=c++11", f"g++ -o {executable} {cpp_file} -I/usr/include/opencv4 -lopencv_core -lopencv_imgproc -std=c++11" ] compiled = False for cmd in compile_commands: compile_result = subprocess.run( cmd, shell=True, capture_output=True, text=True ) if compile_result.returncode == 0: compiled = True break if not compiled: st.error("All compilation attempts failed. Please check the system requirements.") st.stop() # Make sure the executable is executable os.chmod(executable, 0o755) st.success("C++ code compiled successfully") # Parameter inputs with defaults and validation st.markdown("### Matrix Parameters") n = st.number_input("Sample size (n)", min_value=5, max_value=1000, value=100, step=5, help="Number of samples") p = st.number_input("Dimension (p)", min_value=5, max_value=1000, value=50, step=5, help="Dimensionality") a = st.number_input("Value for a", min_value=1.1, max_value=10.0, value=2.0, step=0.1, help="Parameter a > 1") # Automatically calculate y = p/n (as requested) y = p/n st.info(f"Value for y = p/n: {y:.4f}") st.markdown("### Calculation Controls") fineness = st.slider( "Beta points", min_value=20, max_value=500, value=100, step=10, help="Number of points to calculate along the β axis (0 to 1)" ) with st.expander("Advanced Settings"): # Add controls for theoretical calculation precision theory_grid_points = st.slider( "Theoretical grid points", min_value=100, max_value=1000, value=200, step=50, help="Number of points in initial grid search for theoretical calculations" ) theory_tolerance = st.number_input( "Theoretical tolerance", min_value=1e-12, max_value=1e-6, value=1e-10, format="%.1e", help="Convergence tolerance for golden section search" ) # Generate button generate_button = st.button("Generate Analysis", type="primary", use_container_width=True) st.markdown('
', unsafe_allow_html=True) # About section with st.expander("About Eigenvalue Analysis"): st.markdown(""" ## Theory This application visualizes the relationship between empirical and theoretical eigenvalues for matrices with specific properties. The analysis examines: - **Empirical Max/Min Eigenvalues**: The maximum and minimum eigenvalues calculated from the generated matrices - **Theoretical Max/Min Functions**: The theoretical bounds derived from mathematical analysis ### Key Parameters - **n**: Sample size - **p**: Dimension - **a**: Value > 1 that affects the distribution of eigenvalues - **y**: Value calculated as p/n that affects scaling ### Calculation Controls - **Beta points**: Number of points calculated along the β range (0 to 1) - **Theoretical grid points**: Number of points in initial grid search for finding theoretical max/min - **Theoretical tolerance**: Convergence tolerance for golden section search algorithm ### Mathematical Formulas Max Function: max{k ∈ (0,∞)} [yβ(a-1)k + (ak+1)((y-1)k-1)]/[(ak+1)(k²+k)] Min Function: min{t ∈ (-1/a,0)} [yβ(a-1)t + (at+1)((y-1)t-1)]/[(at+1)(t²+t)] """) with right_column: # Main visualization area st.markdown('
', unsafe_allow_html=True) st.markdown('
Eigenvalue Analysis Visualization
', unsafe_allow_html=True) # Container for the analysis results results_container = st.container() # Process when generate button is clicked if generate_button: with results_container: # Show progress progress_container = st.container() with progress_container: progress_bar = st.progress(0) status_text = st.empty() try: # Run the C++ executable with the parameters in JSON output mode data_file = os.path.join(output_dir, "eigenvalue_data.json") # Delete previous output if exists if os.path.exists(data_file): os.remove(data_file) # Execute the C++ program cmd = [ executable, str(n), str(p), str(a), str(y), str(fineness), str(theory_grid_points), str(theory_tolerance), data_file ] process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True ) # Show output in a status area status_text.text("Starting calculations...") last_progress = 0 while process.poll() is None: output = process.stdout.readline() if output: if output.startswith("PROGRESS:"): try: # Update progress bar progress_value = float(output.split(":")[1].strip()) progress_bar.progress(progress_value) last_progress = progress_value status_text.text(f"Calculating... {int(progress_value * 100)}% complete") except: pass else: status_text.text(output.strip()) time.sleep(0.1) return_code = process.poll() if return_code != 0: error = process.stderr.read() st.error(f"Error executing the analysis: {error}") else: progress_bar.progress(1.0) status_text.text("Calculations complete! Generating visualization...") # Load the results from the JSON file with open(data_file, 'r') as f: data = json.load(f) # Extract data beta_values = np.array(data['beta_values']) max_eigenvalues = np.array(data['max_eigenvalues']) min_eigenvalues = np.array(data['min_eigenvalues']) theoretical_max = np.array(data['theoretical_max']) theoretical_min = np.array(data['theoretical_min']) # Create an interactive plot using Plotly fig = go.Figure() # Add traces for each line fig.add_trace(go.Scatter( x=beta_values, y=max_eigenvalues, mode='lines+markers', name='Empirical Max Eigenvalue', line=dict(color='rgb(220, 60, 60)', width=3), marker=dict( symbol='circle', size=8, color='rgb(220, 60, 60)', line=dict(color='white', width=1) ), hovertemplate='β: %{x:.3f}
Value: %{y:.6f}Empirical Max' )) fig.add_trace(go.Scatter( x=beta_values, y=min_eigenvalues, mode='lines+markers', name='Empirical Min Eigenvalue', line=dict(color='rgb(60, 60, 220)', width=3), marker=dict( symbol='circle', size=8, color='rgb(60, 60, 220)', line=dict(color='white', width=1) ), hovertemplate='β: %{x:.3f}
Value: %{y:.6f}Empirical Min' )) fig.add_trace(go.Scatter( x=beta_values, y=theoretical_max, mode='lines+markers', name='Theoretical Max Function', line=dict(color='rgb(30, 180, 30)', width=3), marker=dict( symbol='diamond', size=8, color='rgb(30, 180, 30)', line=dict(color='white', width=1) ), hovertemplate='β: %{x:.3f}
Value: %{y:.6f}Theoretical Max' )) fig.add_trace(go.Scatter( x=beta_values, y=theoretical_min, mode='lines+markers', name='Theoretical Min Function', line=dict(color='rgb(180, 30, 180)', width=3), marker=dict( symbol='diamond', size=8, color='rgb(180, 30, 180)', line=dict(color='white', width=1) ), hovertemplate='β: %{x:.3f}
Value: %{y:.6f}Theoretical Min' )) # Configure layout for better appearance fig.update_layout( title={ 'text': f'Eigenvalue Analysis: n={n}, p={p}, a={a}, y={y:.4f}', 'font': {'size': 24, 'color': '#1E88E5'}, 'y': 0.95, 'x': 0.5, 'xanchor': 'center', 'yanchor': 'top' }, xaxis={ 'title': 'β Parameter', 'titlefont': {'size': 18, 'color': '#424242'}, 'tickfont': {'size': 14}, 'gridcolor': 'rgba(220, 220, 220, 0.5)', 'showgrid': True }, yaxis={ 'title': 'Eigenvalues', 'titlefont': {'size': 18, 'color': '#424242'}, 'tickfont': {'size': 14}, 'gridcolor': 'rgba(220, 220, 220, 0.5)', 'showgrid': True }, plot_bgcolor='rgba(240, 240, 240, 0.8)', paper_bgcolor='rgba(249, 249, 249, 0.8)', hovermode='closest', legend={ 'font': {'size': 14}, 'bgcolor': 'rgba(255, 255, 255, 0.9)', 'bordercolor': 'rgba(200, 200, 200, 0.5)', 'borderwidth': 1 }, margin={'l': 60, 'r': 30, 't': 100, 'b': 60}, height=600, annotations=[ { 'text': f"Max Function: max{{k ∈ (0,∞)}} [yβ(a-1)k + (ak+1)((y-1)k-1)]/[(ak+1)(k²+k)]", 'xref': 'paper', 'yref': 'paper', 'x': 0.02, 'y': 0.02, 'showarrow': False, 'font': {'size': 12, 'color': 'rgb(30, 180, 30)'}, 'bgcolor': 'rgba(255, 255, 255, 0.9)', 'bordercolor': 'rgb(30, 180, 30)', 'borderwidth': 1, 'borderpad': 4 }, { 'text': f"Min Function: min{{t ∈ (-1/a,0)}} [yβ(a-1)t + (at+1)((y-1)t-1)]/[(at+1)(t²+t)]", 'xref': 'paper', 'yref': 'paper', 'x': 0.55, 'y': 0.02, 'showarrow': False, 'font': {'size': 12, 'color': 'rgb(180, 30, 180)'}, 'bgcolor': 'rgba(255, 255, 255, 0.9)', 'bordercolor': 'rgb(180, 30, 180)', 'borderwidth': 1, 'borderpad': 4 } ] ) # Add custom modebar buttons fig.update_layout( modebar_add=[ 'drawline', 'drawopenpath', 'drawclosedpath', 'drawcircle', 'drawrect', 'eraseshape' ], modebar_remove=['lasso2d', 'select2d'], dragmode='zoom' ) # Clear progress container progress_container.empty() # Display the interactive plot in Streamlit st.plotly_chart(fig, use_container_width=True) # Generate static image for download output_file = os.path.join(output_dir, "eigenvalue_analysis.png") fig.write_image(output_file, scale=2) # Provide download button col1, col2, col3 = st.columns([1, 2, 1]) with col2: with open(output_file, "rb") as file: btn = st.download_button( label="Download Plot", data=file, file_name=f"eigenvalue_analysis_n{n}_p{p}_a{a}_y{y:.4f}.png", mime="image/png", use_container_width=True ) # Add statistics section with cards st.markdown("### Results Summary") # Calculate key statistics emp_max = max(max_eigenvalues) emp_min = min(min_eigenvalues) theo_max = max(theoretical_max) theo_min = min(theoretical_min) max_diff = abs(emp_max - theo_max) min_diff = abs(emp_min - theo_min) # Display statistics in a card layout col1, col2, col3, col4 = st.columns(4) with col1: st.markdown('
', unsafe_allow_html=True) st.markdown(f'
{emp_max:.4f}
', unsafe_allow_html=True) st.markdown('
Empirical Maximum
', unsafe_allow_html=True) st.markdown('
', unsafe_allow_html=True) with col2: st.markdown('
', unsafe_allow_html=True) st.markdown(f'
{emp_min:.4f}
', unsafe_allow_html=True) st.markdown('
Empirical Minimum
', unsafe_allow_html=True) st.markdown('
', unsafe_allow_html=True) with col3: st.markdown('
', unsafe_allow_html=True) st.markdown(f'
{theo_max:.4f}
', unsafe_allow_html=True) st.markdown('
Theoretical Maximum
', unsafe_allow_html=True) st.markdown('
', unsafe_allow_html=True) with col4: st.markdown('
', unsafe_allow_html=True) st.markdown(f'
{theo_min:.4f}
', unsafe_allow_html=True) st.markdown('
Theoretical Minimum
', unsafe_allow_html=True) st.markdown('
', unsafe_allow_html=True) st.markdown("
", unsafe_allow_html=True) col1, col2 = st.columns(2) with col1: st.markdown('
', unsafe_allow_html=True) st.markdown(f'
{max_diff:.4f}
', unsafe_allow_html=True) st.markdown('
Max Difference
', unsafe_allow_html=True) st.markdown('
', unsafe_allow_html=True) with col2: st.markdown('
', unsafe_allow_html=True) st.markdown(f'
{min_diff:.4f}
', unsafe_allow_html=True) st.markdown('
Min Difference
', unsafe_allow_html=True) st.markdown('
', unsafe_allow_html=True) # Add calculation settings with st.expander("Calculation Details"): st.markdown(f""" - **Matrix Dimensions**: {n} × {p} - **Parameter a**: {a} - **Parameter y (p/n)**: {y:.4f} - **Beta points**: {fineness} - **Theoretical grid points**: {theory_grid_points} - **Theoretical tolerance**: {theory_tolerance:.1e} """) except Exception as e: st.error(f"An error occurred: {str(e)}") else: # Try to load existing data if available data_file = os.path.join(output_dir, "eigenvalue_data.json") if os.path.exists(data_file): try: with open(data_file, 'r') as f: data = json.load(f) # Extract data beta_values = np.array(data['beta_values']) max_eigenvalues = np.array(data['max_eigenvalues']) min_eigenvalues = np.array(data['min_eigenvalues']) theoretical_max = np.array(data['theoretical_max']) theoretical_min = np.array(data['theoretical_min']) # Create an interactive plot using Plotly fig = go.Figure() # Add traces for each line fig.add_trace(go.Scatter( x=beta_values, y=max_eigenvalues, mode='lines+markers', name='Empirical Max Eigenvalue', line=dict(color='rgb(220, 60, 60)', width=3), marker=dict( symbol='circle', size=8, color='rgb(220, 60, 60)', line=dict(color='white', width=1) ), hovertemplate='β: %{x:.3f}
Value: %{y:.6f}Empirical Max' )) fig.add_trace(go.Scatter( x=beta_values, y=min_eigenvalues, mode='lines+markers', name='Empirical Min Eigenvalue', line=dict(color='rgb(60, 60, 220)', width=3), marker=dict( symbol='circle', size=8, color='rgb(60, 60, 220)', line=dict(color='white', width=1) ), hovertemplate='β: %{x:.3f}
Value: %{y:.6f}Empirical Min' )) fig.add_trace(go.Scatter( x=beta_values, y=theoretical_max, mode='lines+markers', name='Theoretical Max Function', line=dict(color='rgb(30, 180, 30)', width=3), marker=dict( symbol='diamond', size=8, color='rgb(30, 180, 30)', line=dict(color='white', width=1) ), hovertemplate='β: %{x:.3f}
Value: %{y:.6f}Theoretical Max' )) fig.add_trace(go.Scatter( x=beta_values, y=theoretical_min, mode='lines+markers', name='Theoretical Min Function', line=dict(color='rgb(180, 30, 180)', width=3), marker=dict( symbol='diamond', size=8, color='rgb(180, 30, 180)', line=dict(color='white', width=1) ), hovertemplate='β: %{x:.3f}
Value: %{y:.6f}Theoretical Min' )) # Configure layout for better appearance fig.update_layout( title={ 'text': f'Eigenvalue Analysis (Previous Result)', 'font': {'size': 24, 'color': '#1E88E5'}, 'y': 0.95, 'x': 0.5, 'xanchor': 'center', 'yanchor': 'top' }, xaxis={ 'title': 'β Parameter', 'titlefont': {'size': 18, 'color': '#424242'}, 'tickfont': {'size': 14}, 'gridcolor': 'rgba(220, 220, 220, 0.5)', 'showgrid': True }, yaxis={ 'title': 'Eigenvalues', 'titlefont': {'size': 18, 'color': '#424242'}, 'tickfont': {'size': 14}, 'gridcolor': 'rgba(220, 220, 220, 0.5)', 'showgrid': True }, plot_bgcolor='rgba(240, 240, 240, 0.8)', paper_bgcolor='rgba(249, 249, 249, 0.8)', hovermode='closest', legend={ 'font': {'size': 14}, 'bgcolor': 'rgba(255, 255, 255, 0.9)', 'bordercolor': 'rgba(200, 200, 200, 0.5)', 'borderwidth': 1 }, margin={'l': 60, 'r': 30, 't': 100, 'b': 60}, height=600 ) # Display the interactive plot in Streamlit st.plotly_chart(fig, use_container_width=True) st.info("This is the previous analysis result. Adjust parameters and click 'Generate Analysis' to create a new visualization.") except Exception as e: st.info("👈 Set parameters and click 'Generate Analysis' to create a visualization.") else: # Show placeholder st.info("👈 Set parameters and click 'Generate Analysis' to create a visualization.") st.markdown('
', unsafe_allow_html=True)