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('
', unsafe_allow_html=True)
st.markdown('', 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('', 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)