Spaces:
Sleeping
Sleeping
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(""" | |
<style> | |
.main-header { | |
font-size: 2.5rem; | |
color: #1E88E5; | |
text-align: center; | |
margin-bottom: 1rem; | |
padding-bottom: 1rem; | |
border-bottom: 2px solid #f0f0f0; | |
} | |
.dashboard-container { | |
background-color: #f9f9f9; | |
padding: 1.5rem; | |
border-radius: 10px; | |
box-shadow: 0 2px 5px rgba(0,0,0,0.1); | |
margin-bottom: 1.5rem; | |
} | |
.panel-header { | |
font-size: 1.3rem; | |
font-weight: bold; | |
margin-bottom: 1rem; | |
color: #424242; | |
border-left: 4px solid #1E88E5; | |
padding-left: 10px; | |
} | |
.stats-card { | |
background-color: white; | |
padding: 1rem; | |
border-radius: 8px; | |
box-shadow: 0 1px 3px rgba(0,0,0,0.1); | |
text-align: center; | |
} | |
.stats-value { | |
font-size: 1.8rem; | |
font-weight: bold; | |
color: #1E88E5; | |
} | |
.stats-label { | |
font-size: 0.9rem; | |
color: #616161; | |
margin-top: 0.3rem; | |
} | |
</style> | |
""", unsafe_allow_html=True) | |
# Dashboard Header | |
st.markdown('<h1 class="main-header">Eigenvalue Analysis Dashboard</h1>', 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('<div class="dashboard-container">', unsafe_allow_html=True) | |
st.markdown('<div class="panel-header">Control Panel</div>', 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('</div>', 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('<div class="dashboard-container">', unsafe_allow_html=True) | |
st.markdown('<div class="panel-header">Eigenvalue Analysis Visualization</div>', 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}<br>Value: %{y:.6f}<extra>Empirical Max</extra>' | |
)) | |
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}<br>Value: %{y:.6f}<extra>Empirical Min</extra>' | |
)) | |
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}<br>Value: %{y:.6f}<extra>Theoretical Max</extra>' | |
)) | |
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}<br>Value: %{y:.6f}<extra>Theoretical Min</extra>' | |
)) | |
# 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('<div class="stats-card">', unsafe_allow_html=True) | |
st.markdown(f'<div class="stats-value">{emp_max:.4f}</div>', unsafe_allow_html=True) | |
st.markdown('<div class="stats-label">Empirical Maximum</div>', unsafe_allow_html=True) | |
st.markdown('</div>', unsafe_allow_html=True) | |
with col2: | |
st.markdown('<div class="stats-card">', unsafe_allow_html=True) | |
st.markdown(f'<div class="stats-value">{emp_min:.4f}</div>', unsafe_allow_html=True) | |
st.markdown('<div class="stats-label">Empirical Minimum</div>', unsafe_allow_html=True) | |
st.markdown('</div>', unsafe_allow_html=True) | |
with col3: | |
st.markdown('<div class="stats-card">', unsafe_allow_html=True) | |
st.markdown(f'<div class="stats-value">{theo_max:.4f}</div>', unsafe_allow_html=True) | |
st.markdown('<div class="stats-label">Theoretical Maximum</div>', unsafe_allow_html=True) | |
st.markdown('</div>', unsafe_allow_html=True) | |
with col4: | |
st.markdown('<div class="stats-card">', unsafe_allow_html=True) | |
st.markdown(f'<div class="stats-value">{theo_min:.4f}</div>', unsafe_allow_html=True) | |
st.markdown('<div class="stats-label">Theoretical Minimum</div>', unsafe_allow_html=True) | |
st.markdown('</div>', unsafe_allow_html=True) | |
st.markdown("<br>", unsafe_allow_html=True) | |
col1, col2 = st.columns(2) | |
with col1: | |
st.markdown('<div class="stats-card">', unsafe_allow_html=True) | |
st.markdown(f'<div class="stats-value">{max_diff:.4f}</div>', unsafe_allow_html=True) | |
st.markdown('<div class="stats-label">Max Difference</div>', unsafe_allow_html=True) | |
st.markdown('</div>', unsafe_allow_html=True) | |
with col2: | |
st.markdown('<div class="stats-card">', unsafe_allow_html=True) | |
st.markdown(f'<div class="stats-value">{min_diff:.4f}</div>', unsafe_allow_html=True) | |
st.markdown('<div class="stats-label">Min Difference</div>', unsafe_allow_html=True) | |
st.markdown('</div>', 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}<br>Value: %{y:.6f}<extra>Empirical Max</extra>' | |
)) | |
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}<br>Value: %{y:.6f}<extra>Empirical Min</extra>' | |
)) | |
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}<br>Value: %{y:.6f}<extra>Theoretical Max</extra>' | |
)) | |
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}<br>Value: %{y:.6f}<extra>Theoretical Min</extra>' | |
)) | |
# 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('</div>', unsafe_allow_html=True) |