diff --git "a/app.py" "b/app.py"
--- "a/app.py"
+++ "b/app.py"
@@ -1,2389 +1,2493 @@
-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
-import sympy as sp
-from PIL import Image
-import time
-import io
-import sys
-import tempfile
-import platform
-from sympy import symbols, solve, I, re, im, Poly, simplify, N
-import mpmath
-from scipy.stats import gaussian_kde
-
-# Set page config with wider layout
-st.set_page_config(
- page_title="Matrix Analysis Dashboard",
- page_icon="📈",
- layout="wide",
- initial_sidebar_state="expanded"
-)
-
-# Apply custom CSS for a modern, clean dashboard layout
-st.markdown("""
-
-""", unsafe_allow_html=True)
-
-# Dashboard Header
-st.markdown('
', unsafe_allow_html=True)
- st.markdown('', unsafe_allow_html=True)
-
- # Parameter inputs with defaults and validation
- st.markdown('
', unsafe_allow_html=True)
- st.markdown("### Matrix Parameters")
- n = st.number_input("Sample size (n)", min_value=5, max_value=10000000, value=100, step=5,
- help="Number of samples", key="eig_n")
- p = st.number_input("Dimension (p)", min_value=5, max_value=10000000, value=50, step=5,
- help="Dimensionality", key="eig_p")
- a = st.number_input("Value for a", min_value=1.1, max_value=10000.0, value=2.0, step=0.1,
- help="Parameter a > 1", key="eig_a")
-
- # Automatically calculate y = p/n (as requested)
- y = p/n
- st.info(f"Value for y = p/n: {y:.4f}")
- st.markdown('
', unsafe_allow_html=True)
-
- st.markdown('
', unsafe_allow_html=True)
- 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)",
- key="eig_fineness"
- )
- st.markdown('
', unsafe_allow_html=True)
-
- 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",
- key="eig_grid_points"
- )
-
- 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",
- key="eig_tolerance"
- )
-
- # Debug mode
- debug_mode = st.checkbox("Debug Mode", value=False, key="eig_debug")
-
- # Timeout setting
- timeout_seconds = st.number_input(
- "Computation timeout (seconds)",
- min_value=30,
- max_value=3600,
- value=300,
- help="Maximum time allowed for computation before timeout",
- key="eig_timeout"
- )
-
- # Generate button
- eig_generate_button = st.button("Generate Varying β Analysis",
- type="primary",
- use_container_width=True,
- key="eig_generate")
- st.markdown('
', unsafe_allow_html=True)
-
- with right_column:
- # Main visualization area
- st.markdown('', unsafe_allow_html=True)
- st.markdown('', unsafe_allow_html=True)
-
- # Container for the analysis results
- eig_results_container = st.container()
-
- # Process when generate button is clicked (existing logic)
- if eig_generate_button:
- with eig_results_container:
- # Show progress
- progress_container = st.container()
- with progress_container:
- progress_bar = st.progress(0)
- status_text = st.empty()
-
- try:
- # Create data file path
- 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)
-
- # Build command for eigenvalue analysis with the proper arguments
- cmd = [
- executable,
- "eigenvalues", # Mode argument
- str(n),
- str(p),
- str(a),
- str(y),
- str(fineness),
- str(theory_grid_points),
- str(theory_tolerance),
- data_file
- ]
-
- # Run the command
- status_text.text("Running eigenvalue analysis...")
-
- if debug_mode:
- success, stdout, stderr = run_command(cmd, True, timeout=timeout_seconds)
- # Process stdout for progress updates
- if success:
- progress_bar.progress(1.0)
- else:
- # Start the process with pipe for stdout to read progress
- process = subprocess.Popen(
- cmd,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- text=True,
- bufsize=1,
- universal_newlines=True
- )
-
- # Track progress from stdout
- success = True
- stdout_lines = []
-
- start_time = time.time()
- while True:
- # Check for timeout
- if time.time() - start_time > timeout_seconds:
- process.kill()
- status_text.error(f"Computation timed out after {timeout_seconds} seconds")
- success = False
- break
-
- # Try to read a line (non-blocking)
- line = process.stdout.readline()
- if not line and process.poll() is not None:
- break
-
- if line:
- stdout_lines.append(line)
- if line.startswith("PROGRESS:"):
- try:
- # Update progress bar
- progress_value = float(line.split(":")[1].strip())
- progress_bar.progress(progress_value)
- status_text.text(f"Calculating... {int(progress_value * 100)}% complete")
- except:
- pass
- elif line:
- status_text.text(line.strip())
-
- # Get the return code and stderr
- returncode = process.poll()
- stderr = process.stderr.read()
-
- if returncode != 0:
- success = False
- st.error(f"Error executing the analysis: {stderr}")
- with st.expander("Error Details"):
- st.code(stderr)
-
- if success:
- progress_bar.progress(1.0)
- status_text.text("Analysis complete! Generating visualization...")
-
- # Check if the output file was created
- if not os.path.exists(data_file):
- st.error(f"Output file not created: {data_file}")
- st.stop()
-
- try:
- # Load the results from the JSON file
- with open(data_file, 'r') as f:
- data = json.load(f)
-
- # Process data - convert string values to numeric
- beta_values = np.array([safe_convert_to_numeric(x) for x in data['beta_values']])
- max_eigenvalues = np.array([safe_convert_to_numeric(x) for x in data['max_eigenvalues']])
- min_eigenvalues = np.array([safe_convert_to_numeric(x) for x in data['min_eigenvalues']])
- theoretical_max = np.array([safe_convert_to_numeric(x) for x in data['theoretical_max']])
- theoretical_min = np.array([safe_convert_to_numeric(x) for x in 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=color_max, width=3),
- marker=dict(
- symbol='circle',
- size=8,
- color=color_max,
- 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=color_min, width=3),
- marker=dict(
- symbol='circle',
- size=8,
- color=color_min,
- 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',
- line=dict(color=color_theory_max, width=3),
- marker=dict(
- symbol='diamond',
- size=8,
- color=color_theory_max,
- 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',
- line=dict(color=color_theory_min, width=3),
- marker=dict(
- symbol='diamond',
- size=8,
- color=color_theory_min,
- 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'Varying β Analysis: n={n}, p={p}, a={a}, y={y:.4f}',
- 'font': {'size': 24, 'color': '#0e1117'},
- 'y': 0.95,
- 'x': 0.5,
- 'xanchor': 'center',
- 'yanchor': 'top'
- },
- xaxis={
- 'title': {'text': 'β Parameter', 'font': {'size': 18, 'color': '#424242'}},
- 'tickfont': {'size': 14},
- 'gridcolor': 'rgba(220, 220, 220, 0.5)',
- 'showgrid': True
- },
- yaxis={
- 'title': {'text': 'Eigenvalues', 'font': {'size': 18, 'color': '#424242'}},
- 'tickfont': {'size': 14},
- 'gridcolor': 'rgba(220, 220, 220, 0.5)',
- 'showgrid': True
- },
- plot_bgcolor='rgba(250, 250, 250, 0.8)',
- paper_bgcolor='rgba(255, 255, 255, 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,
- )
-
- # 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)
-
- # Display statistics in a cleaner way
- st.markdown('
', unsafe_allow_html=True)
- col1, col2, col3, col4 = st.columns(4)
- with col1:
- st.metric("Max Empirical", f"{max_eigenvalues.max():.4f}")
- with col2:
- st.metric("Min Empirical", f"{min_eigenvalues.min():.4f}")
- with col3:
- st.metric("Max Theoretical", f"{theoretical_max.max():.4f}")
- with col4:
- st.metric("Min Theoretical", f"{theoretical_min.min():.4f}")
- st.markdown('
', unsafe_allow_html=True)
-
- except json.JSONDecodeError as e:
- st.error(f"Error parsing JSON results: {str(e)}")
- if os.path.exists(data_file):
- with open(data_file, 'r') as f:
- content = f.read()
- st.code(content[:1000] + "..." if len(content) > 1000 else content)
-
- except Exception as e:
- st.error(f"An error occurred: {str(e)}")
- if debug_mode:
- st.exception(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)
-
- # Process data - convert string values to numeric
- beta_values = np.array([safe_convert_to_numeric(x) for x in data['beta_values']])
- max_eigenvalues = np.array([safe_convert_to_numeric(x) for x in data['max_eigenvalues']])
- min_eigenvalues = np.array([safe_convert_to_numeric(x) for x in data['min_eigenvalues']])
- theoretical_max = np.array([safe_convert_to_numeric(x) for x in data['theoretical_max']])
- theoretical_min = np.array([safe_convert_to_numeric(x) for x in 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=color_max, width=3),
- marker=dict(
- symbol='circle',
- size=8,
- color=color_max,
- 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=color_min, width=3),
- marker=dict(
- symbol='circle',
- size=8,
- color=color_min,
- 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',
- line=dict(color=color_theory_max, width=3),
- marker=dict(
- symbol='diamond',
- size=8,
- color=color_theory_max,
- 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',
- line=dict(color=color_theory_min, width=3),
- marker=dict(
- symbol='diamond',
- size=8,
- color=color_theory_min,
- 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'Varying β Analysis (Previous Result)',
- 'font': {'size': 24, 'color': '#0e1117'},
- 'y': 0.95,
- 'x': 0.5,
- 'xanchor': 'center',
- 'yanchor': 'top'
- },
- xaxis={
- 'title': {'text': 'β Parameter', 'font': {'size': 18, 'color': '#424242'}},
- 'tickfont': {'size': 14},
- 'gridcolor': 'rgba(220, 220, 220, 0.5)',
- 'showgrid': True
- },
- yaxis={
- 'title': {'text': 'Eigenvalues', 'font': {'size': 18, 'color': '#424242'}},
- 'tickfont': {'size': 14},
- 'gridcolor': 'rgba(220, 220, 220, 0.5)',
- 'showgrid': True
- },
- plot_bgcolor='rgba(250, 250, 250, 0.8)',
- paper_bgcolor='rgba(255, 255, 255, 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 Varying β Analysis' to create a visualization.")
- else:
- # Show placeholder
- st.info("Set parameters and click 'Generate Varying β Analysis' to create a visualization.")
-
- st.markdown('
', unsafe_allow_html=True)
-
- # Sub-tab 2: Fixed Beta Analysis (new functionality)
- with eig_subtabs[1]:
- # Two-column layout for the dashboard
- left_column_fixed, right_column_fixed = st.columns([1, 3])
-
- with left_column_fixed:
- st.markdown('', unsafe_allow_html=True)
- st.markdown('', unsafe_allow_html=True)
-
- # Parameter inputs with defaults and validation
- st.markdown('
', unsafe_allow_html=True)
- st.markdown("### Matrix Parameters")
- n_fixed = st.number_input("Sample size (n)", min_value=5, max_value=10000000, value=100, step=5,
- help="Number of samples", key="eig_n_fixed")
- p_fixed = st.number_input("Dimension (p)", min_value=5, max_value=10000000, value=50, step=5,
- help="Dimensionality", key="eig_p_fixed")
-
- # Automatically calculate y = p/n
- y_fixed = p_fixed/n_fixed
- st.info(f"Value for y = p/n: {y_fixed:.4f}")
-
- # Fixed beta parameter
- beta_fixed = st.slider("Fixed β value", min_value=0.0, max_value=1.0, value=0.5, step=0.01,
- help="Fixed value of β parameter", key="eig_beta_fixed")
- st.markdown('
', unsafe_allow_html=True)
-
- st.markdown('
', unsafe_allow_html=True)
- st.markdown("### Parameter Range")
- a_min = st.number_input("Minimum a value", min_value=1.1, max_value=100.0, value=1.1, step=0.1,
- help="Minimum value for parameter a", key="eig_a_min")
- a_max = st.number_input("Maximum a value", min_value=1.1, max_value=100.0, value=5.0, step=0.1,
- help="Maximum value for parameter a", key="eig_a_max")
-
- if a_min >= a_max:
- st.error("Minimum a value must be less than maximum a value")
-
- fineness_fixed = st.slider(
- "Parameter points",
- min_value=20,
- max_value=500,
- value=100,
- step=10,
- help="Number of points to calculate along the a axis",
- key="eig_fineness_fixed"
- )
- st.markdown('
', unsafe_allow_html=True)
-
- with st.expander("Advanced Settings"):
- # Add controls for theoretical calculation precision
- theory_grid_points_fixed = 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",
- key="eig_grid_points_fixed"
- )
-
- theory_tolerance_fixed = 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",
- key="eig_tolerance_fixed"
- )
-
- # Debug mode
- debug_mode_fixed = st.checkbox("Debug Mode", value=False, key="eig_debug_fixed")
-
- # Timeout setting
- timeout_seconds_fixed = st.number_input(
- "Computation timeout (seconds)",
- min_value=30,
- max_value=3600,
- value=300,
- help="Maximum time allowed for computation before timeout",
- key="eig_timeout_fixed"
- )
-
- # Generate button
- eig_generate_button_fixed = st.button("Generate Fixed β Analysis",
- type="primary",
- use_container_width=True,
- key="eig_generate_fixed")
- st.markdown('
', unsafe_allow_html=True)
-
- with right_column_fixed:
- # Main visualization area
- st.markdown('', unsafe_allow_html=True)
- st.markdown('', unsafe_allow_html=True)
-
- # Container for the analysis results
- eig_results_container_fixed = st.container()
-
- # Process when generate button is clicked
- if eig_generate_button_fixed:
- if a_min >= a_max:
- st.error("Please ensure minimum a value is less than maximum a value")
- else:
- with eig_results_container_fixed:
- # Show progress
- progress_container_fixed = st.container()
- with progress_container_fixed:
- progress_bar_fixed = st.progress(0)
- status_text_fixed = st.empty()
-
- try:
- # Create data file path
- data_file_fixed = os.path.join(output_dir, "eigenvalue_fixed_beta_data.json")
-
- # Delete previous output if exists
- if os.path.exists(data_file_fixed):
- os.remove(data_file_fixed)
-
- # Build command for fixed beta eigenvalue analysis
- cmd_fixed = [
- executable,
- "eigenvalues_fixed_beta", # Mode argument
- str(n_fixed),
- str(p_fixed),
- str(y_fixed),
- str(beta_fixed),
- str(a_min),
- str(a_max),
- str(fineness_fixed),
- str(theory_grid_points_fixed),
- str(theory_tolerance_fixed),
- data_file_fixed
- ]
-
- # Run the command
- status_text_fixed.text("Running fixed β eigenvalue analysis...")
-
- if debug_mode_fixed:
- success, stdout, stderr = run_command(cmd_fixed, True, timeout=timeout_seconds_fixed)
- if success:
- progress_bar_fixed.progress(1.0)
- else:
- # Start the process with pipe for stdout to read progress
- process = subprocess.Popen(
- cmd_fixed,
- stdout=subprocess.PIPE,
- stderr=subprocess.PIPE,
- text=True,
- bufsize=1,
- universal_newlines=True
- )
-
- # Track progress from stdout
- success = True
- stdout_lines = []
-
- start_time = time.time()
- while True:
- # Check for timeout
- if time.time() - start_time > timeout_seconds_fixed:
- process.kill()
- status_text_fixed.error(f"Computation timed out after {timeout_seconds_fixed} seconds")
- success = False
- break
-
- # Try to read a line (non-blocking)
- line = process.stdout.readline()
- if not line and process.poll() is not None:
- break
-
- if line:
- stdout_lines.append(line)
- if line.startswith("PROGRESS:"):
- try:
- # Update progress bar
- progress_value = float(line.split(":")[1].strip())
- progress_bar_fixed.progress(progress_value)
- status_text_fixed.text(f"Calculating... {int(progress_value * 100)}% complete")
- except:
- pass
- elif line:
- status_text_fixed.text(line.strip())
-
- # Get the return code and stderr
- returncode = process.poll()
- stderr = process.stderr.read()
-
- if returncode != 0:
- success = False
- st.error(f"Error executing the analysis: {stderr}")
- with st.expander("Error Details"):
- st.code(stderr)
-
- if success:
- progress_bar_fixed.progress(1.0)
- status_text_fixed.text("Analysis complete! Generating visualization...")
-
- # Check if the output file was created
- if not os.path.exists(data_file_fixed):
- st.error(f"Output file not created: {data_file_fixed}")
- st.stop()
-
- try:
- # Load the results from the JSON file
- with open(data_file_fixed, 'r') as f:
- data = json.load(f)
-
- # Process data - convert string values to numeric
- a_values = np.array([safe_convert_to_numeric(x) for x in data['a_values']])
- max_eigenvalues = np.array([safe_convert_to_numeric(x) for x in data['max_eigenvalues']])
- min_eigenvalues = np.array([safe_convert_to_numeric(x) for x in data['min_eigenvalues']])
- theoretical_max = np.array([safe_convert_to_numeric(x) for x in data['theoretical_max']])
- theoretical_min = np.array([safe_convert_to_numeric(x) for x in data['theoretical_min']])
-
- # Create an interactive plot using Plotly
- fig = go.Figure()
-
- # Add traces for each line
- fig.add_trace(go.Scatter(
- x=a_values,
- y=max_eigenvalues,
- mode='lines+markers',
- name='Empirical Max Eigenvalue',
- line=dict(color=color_max, width=3),
- marker=dict(
- symbol='circle',
- size=8,
- color=color_max,
- line=dict(color='white', width=1)
- ),
- hovertemplate='a: %{x:.3f}
Value: %{y:.6f}
Empirical Max'
- ))
-
- fig.add_trace(go.Scatter(
- x=a_values,
- y=min_eigenvalues,
- mode='lines+markers',
- name='Empirical Min Eigenvalue',
- line=dict(color=color_min, width=3),
- marker=dict(
- symbol='circle',
- size=8,
- color=color_min,
- line=dict(color='white', width=1)
- ),
- hovertemplate='a: %{x:.3f}
Value: %{y:.6f}
Empirical Min'
- ))
-
- fig.add_trace(go.Scatter(
- x=a_values,
- y=theoretical_max,
- mode='lines+markers',
- name='Theoretical Max',
- line=dict(color=color_theory_max, width=3),
- marker=dict(
- symbol='diamond',
- size=8,
- color=color_theory_max,
- line=dict(color='white', width=1)
- ),
- hovertemplate='a: %{x:.3f}
Value: %{y:.6f}
Theoretical Max'
- ))
-
- fig.add_trace(go.Scatter(
- x=a_values,
- y=theoretical_min,
- mode='lines+markers',
- name='Theoretical Min',
- line=dict(color=color_theory_min, width=3),
- marker=dict(
- symbol='diamond',
- size=8,
- color=color_theory_min,
- line=dict(color='white', width=1)
- ),
- hovertemplate='a: %{x:.3f}
Value: %{y:.6f}
Theoretical Min'
- ))
-
- # Configure layout for better appearance
- fig.update_layout(
- title={
- 'text': f'Fixed β Analysis: n={n_fixed}, p={p_fixed}, β={beta_fixed:.3f}, y={y_fixed:.4f}',
- 'font': {'size': 24, 'color': '#0e1117'},
- 'y': 0.95,
- 'x': 0.5,
- 'xanchor': 'center',
- 'yanchor': 'top'
- },
- xaxis={
- 'title': {'text': 'a Parameter', 'font': {'size': 18, 'color': '#424242'}},
- 'tickfont': {'size': 14},
- 'gridcolor': 'rgba(220, 220, 220, 0.5)',
- 'showgrid': True
- },
- yaxis={
- 'title': {'text': 'Eigenvalues', 'font': {'size': 18, 'color': '#424242'}},
- 'tickfont': {'size': 14},
- 'gridcolor': 'rgba(220, 220, 220, 0.5)',
- 'showgrid': True
- },
- plot_bgcolor='rgba(250, 250, 250, 0.8)',
- paper_bgcolor='rgba(255, 255, 255, 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,
- )
-
- # 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_fixed.empty()
-
- # Display the interactive plot in Streamlit
- st.plotly_chart(fig, use_container_width=True)
-
- # Display statistics in a cleaner way
- st.markdown('
', unsafe_allow_html=True)
- col1, col2, col3, col4 = st.columns(4)
- with col1:
- st.metric("Max Empirical", f"{max_eigenvalues.max():.4f}")
- with col2:
- st.metric("Min Empirical", f"{min_eigenvalues.min():.4f}")
- with col3:
- st.metric("Max Theoretical", f"{theoretical_max.max():.4f}")
- with col4:
- st.metric("Min Theoretical", f"{theoretical_min.min():.4f}")
- st.markdown('
', unsafe_allow_html=True)
-
- # Add interpretation section
- with st.expander("Fixed β Analysis Explanation", expanded=False):
- st.markdown(f"""
- ### Understanding Fixed β Analysis
-
- In this analysis, β is fixed at **{beta_fixed:.3f}** while parameter **a** varies from **{a_min:.2f}** to **{a_max:.2f}**.
-
- **What this shows:**
- - How eigenvalue bounds change with parameter **a** when the mixture proportion β is constant
- - The relationship between the spike eigenvalue parameter **a** and the empirical eigenvalue distribution
- - Validation of theoretical predictions for varying **a** at fixed β
-
- **Key insights:**
- - As **a** increases, the maximum eigenvalue generally increases
- - The minimum eigenvalue behavior depends on the specific value of β
- - The gap between theoretical and empirical bounds shows finite-sample effects
- """)
-
- except json.JSONDecodeError as e:
- st.error(f"Error parsing JSON results: {str(e)}")
- if os.path.exists(data_file_fixed):
- with open(data_file_fixed, 'r') as f:
- content = f.read()
- st.code(content[:1000] + "..." if len(content) > 1000 else content)
-
- except Exception as e:
- st.error(f"An error occurred: {str(e)}")
- if debug_mode_fixed:
- st.exception(e)
-
- else:
- # Try to load existing data if available
- data_file_fixed = os.path.join(output_dir, "eigenvalue_fixed_beta_data.json")
- if os.path.exists(data_file_fixed):
- try:
- with open(data_file_fixed, 'r') as f:
- data = json.load(f)
-
- # Process data - convert string values to numeric
- a_values = np.array([safe_convert_to_numeric(x) for x in data['a_values']])
- max_eigenvalues = np.array([safe_convert_to_numeric(x) for x in data['max_eigenvalues']])
- min_eigenvalues = np.array([safe_convert_to_numeric(x) for x in data['min_eigenvalues']])
- theoretical_max = np.array([safe_convert_to_numeric(x) for x in data['theoretical_max']])
- theoretical_min = np.array([safe_convert_to_numeric(x) for x in data['theoretical_min']])
-
- # Create an interactive plot using Plotly
- fig = go.Figure()
-
- # Add traces for each line
- fig.add_trace(go.Scatter(
- x=a_values,
- y=max_eigenvalues,
- mode='lines+markers',
- name='Empirical Max Eigenvalue',
- line=dict(color=color_max, width=3),
- marker=dict(
- symbol='circle',
- size=8,
- color=color_max,
- line=dict(color='white', width=1)
- ),
- hovertemplate='a: %{x:.3f}
Value: %{y:.6f}
Empirical Max'
- ))
-
- fig.add_trace(go.Scatter(
- x=a_values,
- y=min_eigenvalues,
- mode='lines+markers',
- name='Empirical Min Eigenvalue',
- line=dict(color=color_min, width=3),
- marker=dict(
- symbol='circle',
- size=8,
- color=color_min,
- line=dict(color='white', width=1)
- ),
- hovertemplate='a: %{x:.3f}
Value: %{y:.6f}
Empirical Min'
- ))
-
- fig.add_trace(go.Scatter(
- x=a_values,
- y=theoretical_max,
- mode='lines+markers',
- name='Theoretical Max',
- line=dict(color=color_theory_max, width=3),
- marker=dict(
- symbol='diamond',
- size=8,
- color=color_theory_max,
- line=dict(color='white', width=1)
- ),
- hovertemplate='a: %{x:.3f}
Value: %{y:.6f}
Theoretical Max'
- ))
-
- fig.add_trace(go.Scatter(
- x=a_values,
- y=theoretical_min,
- mode='lines+markers',
- name='Theoretical Min',
- line=dict(color=color_theory_min, width=3),
- marker=dict(
- symbol='diamond',
- size=8,
- color=color_theory_min,
- line=dict(color='white', width=1)
- ),
- hovertemplate='a: %{x:.3f}
Value: %{y:.6f}
Theoretical Min'
- ))
-
- # Configure layout for better appearance
- fig.update_layout(
- title={
- 'text': f'Fixed β Analysis (Previous Result)',
- 'font': {'size': 24, 'color': '#0e1117'},
- 'y': 0.95,
- 'x': 0.5,
- 'xanchor': 'center',
- 'yanchor': 'top'
- },
- xaxis={
- 'title': {'text': 'a Parameter', 'font': {'size': 18, 'color': '#424242'}},
- 'tickfont': {'size': 14},
- 'gridcolor': 'rgba(220, 220, 220, 0.5)',
- 'showgrid': True
- },
- yaxis={
- 'title': {'text': 'Eigenvalues', 'font': {'size': 18, 'color': '#424242'}},
- 'tickfont': {'size': 14},
- 'gridcolor': 'rgba(220, 220, 220, 0.5)',
- 'showgrid': True
- },
- plot_bgcolor='rgba(250, 250, 250, 0.8)',
- paper_bgcolor='rgba(255, 255, 255, 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 Fixed β Analysis' to create a new visualization.")
-
- except Exception as e:
- st.info("Set parameters and click 'Generate Fixed β Analysis' to create a visualization.")
- else:
- # Show placeholder
- st.info("Set parameters and click 'Generate Fixed β Analysis' to create a visualization.")
-
- st.markdown('
', unsafe_allow_html=True)
-
-
-# ----- Tab 2: Complex Root Analysis -----
-with tab2:
- st.header("Complex Root Analysis")
- plot_tabs = st.tabs(["Im{s} vs. z", "Im{s} vs. β", "Phase Diagram", "Eigenvalue Distribution"])
-
- with plot_tabs[0]:
- col1, col2 = st.columns([1, 2])
- with col1:
- beta_z = st.number_input("β", value=0.5, min_value=0.0, max_value=1.0, key="beta_tab2_z")
- y_z = st.number_input("y", value=1.0, key="y_tab2_z")
- z_a_z = st.number_input("z_a", value=1.0, key="z_a_tab2_z")
- z_min_z = st.number_input("z_min", value=-10.0, key="z_min_tab2_z")
- z_max_z = st.number_input("z_max", value=10.0, key="z_max_tab2_z")
- with st.expander("Resolution Settings", expanded=False):
- z_points = st.slider("z grid points", min_value=100, max_value=2000, value=500, step=100, key="z_points_z")
- if st.button("Compute Complex Roots vs. z", key="tab2_button_z"):
- with col2:
- fig_im, fig_re, fig_disc = generate_root_plots(beta_z, y_z, z_a_z, z_min_z, z_max_z, z_points)
- if fig_im is not None and fig_re is not None and fig_disc is not None:
- st.plotly_chart(fig_im, use_container_width=True)
- st.plotly_chart(fig_re, use_container_width=True)
- st.plotly_chart(fig_disc, use_container_width=True)
- with st.expander("Root Structure Analysis", expanded=False):
- st.markdown("""
- ### Root Structure Explanation
-
- The red dashed vertical lines mark the points where the cubic discriminant equals zero.
- At these points, the cubic equation's root structure changes:
-
- - When the discriminant is positive, the cubic has three distinct real roots.
- - When the discriminant is negative, the cubic has one real root and two complex conjugate roots.
- - When the discriminant is exactly zero, the cubic has at least two equal roots.
-
- These transition points align perfectly with the z*(β) boundary curves from the first tab,
- which represent exactly these transitions in the (β,z) plane.
- """)
-
- with plot_tabs[1]:
- col1, col2 = st.columns([1, 2])
- with col1:
- z_beta = st.number_input("z", value=1.0, key="z_tab2_beta")
- y_beta = st.number_input("y", value=1.0, key="y_tab2_beta")
- z_a_beta = st.number_input("z_a", value=1.0, key="z_a_tab2_beta")
- beta_min = st.number_input("β_min", value=0.0, min_value=0.0, max_value=1.0, key="beta_min_tab2")
- beta_max = st.number_input("β_max", value=1.0, min_value=0.0, max_value=1.0, key="beta_max_tab2")
- with st.expander("Resolution Settings", expanded=False):
- beta_points = st.slider("β grid points", min_value=100, max_value=1000, value=500, step=100, key="beta_points")
- if st.button("Compute Complex Roots vs. β", key="tab2_button_beta"):
- with col2:
- fig_im_beta, fig_re_beta, fig_disc = generate_roots_vs_beta_plots(z_beta, y_beta, z_a_beta, beta_min, beta_max, beta_points)
- if fig_im_beta is not None and fig_re_beta is not None and fig_disc is not None:
- st.plotly_chart(fig_im_beta, use_container_width=True)
- st.plotly_chart(fig_re_beta, use_container_width=True)
- st.plotly_chart(fig_disc, use_container_width=True)
- transition_points, structure_types = analyze_complex_root_structure(np.linspace(beta_min, beta_max, beta_points), z_beta, z_a_beta, y_beta)
- if transition_points:
- st.subheader("Root Structure Transition Points")
- for i, beta in enumerate(transition_points):
- prev_type = structure_types[i]
- next_type = structure_types[i+1] if i+1 < len(structure_types) else "unknown"
- st.markdown(f"- At β = {beta:.6f}: Transition from {prev_type} roots to {next_type} roots")
- else:
- st.info("No transitions detected in root structure across this β range.")
- with st.expander("Analysis Explanation", expanded=False):
- st.markdown("""
- ### Interpreting the Plots
-
- - **Im{s} vs. β**: Shows how the imaginary parts of the roots change with β. When all curves are at Im{s}=0, all roots are real.
- - **Re{s} vs. β**: Shows how the real parts of the roots change with β.
- - **Discriminant Plot**: The cubic discriminant changes sign at points where the root structure changes.
- - When discriminant < 0: The cubic has one real root and two complex conjugate roots.
- - When discriminant > 0: The cubic has three distinct real roots.
- - When discriminant = 0: The cubic has multiple roots (at least two roots are equal).
-
- The vertical red dashed lines mark the transition points where the root structure changes.
- """)
-
- with plot_tabs[2]:
- col1, col2 = st.columns([1, 2])
- with col1:
- z_a_phase = st.number_input("z_a", value=1.0, key="z_a_phase")
- y_phase = st.number_input("y", value=1.0, key="y_phase")
- beta_min_phase = st.number_input("β_min", value=0.0, min_value=0.0, max_value=1.0, key="beta_min_phase")
- beta_max_phase = st.number_input("β_max", value=1.0, min_value=0.0, max_value=1.0, key="beta_max_phase")
- z_min_phase = st.number_input("z_min", value=-10.0, key="z_min_phase")
- z_max_phase = st.number_input("z_max", value=10.0, key="z_max_phase")
- with st.expander("Resolution Settings", expanded=False):
- beta_steps_phase = st.slider("β grid points", min_value=20, max_value=200, value=100, step=20, key="beta_steps_phase")
- z_steps_phase = st.slider("z grid points", min_value=20, max_value=200, value=100, step=20, key="z_steps_phase")
- if st.button("Generate Phase Diagram", key="tab2_button_phase"):
- with col2:
- st.info("Generating phase diagram. This may take a while depending on resolution...")
- fig_phase = generate_phase_diagram(z_a_phase, y_phase, beta_min_phase, beta_max_phase, z_min_phase, z_max_phase, beta_steps_phase, z_steps_phase)
- if fig_phase is not None:
- st.plotly_chart(fig_phase, use_container_width=True)
- with st.expander("Phase Diagram Explanation", expanded=False):
- st.markdown("""
- ### Understanding the Phase Diagram
-
- This heatmap shows the regions in the (β, z) plane where:
-
- - **Red Regions**: The cubic equation has all real roots
- - **Blue Regions**: The cubic equation has one real root and two complex conjugate roots
-
- The boundaries between these regions represent values where the discriminant is zero,
- which are the exact same curves as the z*(β) boundaries in the first tab. This phase
- diagram provides a comprehensive view of the eigenvalue support structure.
- """)
-
- with plot_tabs[3]:
- st.subheader("Eigenvalue Distribution for B_n = S_n T_n")
- with st.expander("Simulation Information", expanded=False):
- st.markdown("""
- This simulation generates the eigenvalue distribution of B_n as n→∞, where:
- - B_n = (1/n)XX^T with X being a p×n matrix
- - p/n → y as n→∞
- - The diagonal entries of T_n follow distribution β·δ(z_a) + (1-β)·δ(1)
- """)
- col_eigen1, col_eigen2 = st.columns([1, 2])
- with col_eigen1:
- beta_eigen = st.number_input("β", value=0.5, min_value=0.0, max_value=1.0, key="beta_eigen")
- y_eigen = st.number_input("y", value=1.0, key="y_eigen")
- z_a_eigen = st.number_input("z_a", value=1.0, key="z_a_eigen")
- n_samples = st.slider("Number of samples (n)", min_value=100, max_value=2000, value=1000, step=100)
- sim_seed = st.number_input("Random seed", min_value=1, max_value=1000, value=42, step=1)
- show_theoretical = st.checkbox("Show theoretical boundaries", value=True)
- show_empirical_stats = st.checkbox("Show empirical statistics", value=True)
- if st.button("Generate Eigenvalue Distribution", key="tab2_eigen_button"):
- with col_eigen2:
- fig_eigen, eigenvalues = generate_eigenvalue_distribution(beta_eigen, y_eigen, z_a_eigen, n=n_samples, seed=sim_seed)
- if show_theoretical:
- betas = np.array([beta_eigen])
- min_eig, max_eig = compute_eigenvalue_support_boundaries(z_a_eigen, y_eigen, betas, n_samples=n_samples, seeds=5)
- fig_eigen.add_vline(x=min_eig[0], line=dict(color="red", width=2, dash="dash"), annotation_text="Min theoretical", annotation_position="top right")
- fig_eigen.add_vline(x=max_eig[0], line=dict(color="red", width=2, dash="dash"), annotation_text="Max theoretical", annotation_position="top left")
- st.plotly_chart(fig_eigen, use_container_width=True)
- if show_theoretical and show_empirical_stats:
- empirical_min = eigenvalues.min()
- empirical_max = eigenvalues.max()
- st.markdown("### Comparison of Empirical vs Theoretical Bounds")
- col1, col2, col3 = st.columns(3)
- with col1:
- st.metric("Theoretical Min", f"{min_eig[0]:.4f}")
- st.metric("Theoretical Max", f"{max_eig[0]:.4f}")
- st.metric("Theoretical Width", f"{max_eig[0] - min_eig[0]:.4f}")
- with col2:
- st.metric("Empirical Min", f"{empirical_min:.4f}")
- st.metric("Empirical Max", f"{empirical_max:.4f}")
- st.metric("Empirical Width", f"{empirical_max - empirical_min:.4f}")
- with col3:
- st.metric("Min Difference", f"{empirical_min - min_eig[0]:.4f}")
- st.metric("Max Difference", f"{empirical_max - max_eig[0]:.4f}")
- st.metric("Width Difference", f"{(empirical_max - empirical_min) - (max_eig[0] - min_eig[0]):.4f}")
- if show_empirical_stats:
- st.markdown("### Eigenvalue Statistics")
- col1, col2 = st.columns(2)
- with col1:
- st.metric("Mean", f"{np.mean(eigenvalues):.4f}")
- st.metric("Median", f"{np.median(eigenvalues):.4f}")
- with col2:
- st.metric("Standard Deviation", f"{np.std(eigenvalues):.4f}")
- st.metric("Interquartile Range", f"{np.percentile(eigenvalues, 75) - np.percentile(eigenvalues, 25):.4f}")
-
-# Add footer with instructions
-st.markdown("""
-
+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
+import sympy as sp
+from PIL import Image
+import time
+import io
+import sys
+import tempfile
+import platform
+from sympy import symbols, solve, I, re, im, Poly, simplify, N
+import mpmath
+from scipy.stats import gaussian_kde
+
+# Set page config with wider layout
+st.set_page_config(
+ page_title="Matrix Analysis Dashboard",
+ page_icon="chart",
+ layout="wide",
+ initial_sidebar_state="expanded"
+)
+
+# Apply custom CSS for a modern, clean dashboard layout
+st.markdown("""
+
+""", unsafe_allow_html=True)
+
+# Dashboard Header
+st.markdown('