', 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 Eigenvalue 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
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'Eigenvalue 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'Eigenvalue 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 Eigenvalue Analysis' to create a visualization.")
else:
# Show placeholder
st.info("ð Set parameters and click 'Generate Eigenvalue Analysis' to create a visualization.")
st.markdown('
', unsafe_allow_html=True)
# Tab 2: Im(s) vs z Analysis with SymPy
with tab2:
# Two-column layout
left_column, right_column = st.columns([1, 3])
with left_column:
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("### Cubic Equation Parameters")
cubic_a = st.number_input("Value for a", min_value=1.1, max_value=1000.0, value=2.0, step=0.1,
help="Parameter a > 1", key="cubic_a")
cubic_y = st.number_input("Value for y", min_value=0.1, max_value=10.0, value=1.0, step=0.1,
help="Parameter y > 0", key="cubic_y")
cubic_beta = st.number_input("Value for β", min_value=0.0, max_value=1.0, value=0.5, step=0.05,
help="Value between 0 and 1", key="cubic_beta")
st.markdown('
', unsafe_allow_html=True)
st.markdown('
', unsafe_allow_html=True)
st.markdown("### Z-Axis Range")
z_min = st.number_input("Z minimum", min_value=0.01, max_value=1.0, value=0.01, step=0.01,
help="Minimum z value for calculation", key="z_min")
z_max = st.number_input("Z maximum", min_value=1.0, max_value=100.0, value=10.0, step=1.0,
help="Maximum z value for calculation", key="z_max")
cubic_points = st.slider(
"Number of z points",
min_value=50,
max_value=1000,
value=300,
step=50,
help="Number of points to calculate along the z axis",
key="cubic_points"
)
st.markdown('
', unsafe_allow_html=True)
# Show cubic equation
st.markdown('
', unsafe_allow_html=True)
st.markdown("### Cubic Equation")
st.latex(r"zas^3 + [z(a+1)+a(1-y)]\,s^2 + [z+(a+1)-y-y\beta (a-1)]\,s + 1 = 0")
st.markdown('
', unsafe_allow_html=True)
# Generate button
cubic_generate_button = st.button("Generate Im(s) vs z Analysis",
type="primary",
use_container_width=True,
key="cubic_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
cubic_results_container = st.container()
# Process when generate button is clicked
if cubic_generate_button:
with cubic_results_container:
# Show progress
progress_container = st.container()
with progress_container:
progress_bar = st.progress(0)
status_text = st.empty()
status_text.text("Starting cubic equation calculations with SymPy...")
try:
# Create data file path
data_file = os.path.join(output_dir, "cubic_data.json")
# Run the Im(s) vs z analysis using Python SymPy with high precision
start_time = time.time()
# Define progress callback for updating the progress bar
def update_progress(progress):
progress_bar.progress(progress)
status_text.text(f"Calculating with SymPy... {int(progress * 100)}% complete")
# Run the analysis with progress updates
result = compute_ImS_vs_Z(cubic_a, cubic_y, cubic_beta, cubic_points, z_min, z_max, update_progress)
end_time = time.time()
# Format the data for saving
save_data = {
'z_values': result['z_values'],
'ims_values1': result['ims_values1'],
'ims_values2': result['ims_values2'],
'ims_values3': result['ims_values3'],
'real_values1': result['real_values1'],
'real_values2': result['real_values2'],
'real_values3': result['real_values3'],
'parameters': {'a': cubic_a, 'y': cubic_y, 'beta': cubic_beta}
}
# Save results to JSON
save_as_json(save_data, data_file)
status_text.text("SymPy calculations complete! Generating visualization...")
# Clear progress container
progress_container.empty()
# Create Dash-style visualization
dash_fig = create_dash_style_visualization(result, cubic_a, cubic_y, cubic_beta)
st.plotly_chart(dash_fig, use_container_width=True)
# Create sub-tabs for additional visualizations
pattern_tab, complex_tab = st.tabs(["Root Pattern Analysis", "Complex Plane View"])
# Root pattern visualization
with pattern_tab:
pattern_fig = create_root_pattern_visualization(result)
st.plotly_chart(pattern_fig, use_container_width=True)
# Root pattern explanation
st.markdown('
', unsafe_allow_html=True)
st.markdown("""
### Root Pattern Analysis
The cubic equation in this analysis should ideally exhibit roots with the following pattern:
- One root with negative real part
- One root with zero real part
- One root with positive real part
Or, in special cases, all three roots may be zero. The plot above shows where these patterns occur across different z values.
The SymPy implementation with high precision ensures accurate root-finding and pattern maintenance, which is essential for stability analysis.
Blue points indicate where the ideal pattern is achieved, green points show where all roots are zero, and red points indicate other patterns.
""")
st.markdown('
', unsafe_allow_html=True)
# Complex plane visualization
with complex_tab:
# Slider for selecting z value
z_idx = st.slider(
"Select z index",
min_value=0,
max_value=len(result['z_values'])-1,
value=len(result['z_values'])//2,
help="Select a specific z value to visualize its roots in the complex plane"
)
# Create complex plane visualization
complex_fig = create_complex_plane_visualization(result, z_idx)
st.plotly_chart(complex_fig, use_container_width=True)
# Complex plane explanation
st.markdown('
', unsafe_allow_html=True)
st.markdown("""
### Complex Plane Visualization
This visualization shows the three roots of the cubic equation in the complex plane for the selected z value.
The real part is shown on the horizontal axis, and the imaginary part on the vertical axis.
- The dashed circle represents the unit circle |s| = 1
- The roots are colored to match the plots above
- Conjugate pairs of roots (with opposite imaginary parts) often appear in cubic equations
Use the slider to explore how the roots move in the complex plane as z changes.
""")
st.markdown('
', unsafe_allow_html=True)
# Display computation time
st.success(f"SymPy computation completed in {end_time - start_time:.2f} seconds")
except Exception as e:
st.error(f"An error occurred: {str(e)}")
st.exception(e)
else:
# Try to load existing data if available
data_file = os.path.join(output_dir, "cubic_data.json")
if os.path.exists(data_file):
try:
with open(data_file, 'r') as f:
data = json.load(f)
# Process data safely and convert it to the format we need
result = {
'z_values': np.array([safe_convert_to_numeric(x) for x in data['z_values']]),
'ims_values1': np.array([safe_convert_to_numeric(x) for x in data['ims_values1']]),
'ims_values2': np.array([safe_convert_to_numeric(x) for x in data['ims_values2']]),
'ims_values3': np.array([safe_convert_to_numeric(x) for x in data['ims_values3']]),
'real_values1': np.array([safe_convert_to_numeric(x) for x in data.get('real_values1', [0] * len(data['z_values']))]),
'real_values2': np.array([safe_convert_to_numeric(x) for x in data.get('real_values2', [0] * len(data['z_values']))]),
'real_values3': np.array([safe_convert_to_numeric(x) for x in data.get('real_values3', [0] * len(data['z_values']))]),
}
# Extract cubic parameters from data if available (otherwise use defaults)
cubic_params = data.get('parameters', {'a': 2.0, 'y': 1.0, 'beta': 0.5})
cubic_a = cubic_params.get('a', 2.0)
cubic_y = cubic_params.get('y', 1.0)
cubic_beta = cubic_params.get('beta', 0.5)
# Create Dash-style visualization from previous data
st.info("Displaying previous analysis results. Adjust parameters and click 'Generate Analysis' to create a new visualization.")
dash_fig = create_dash_style_visualization(result, cubic_a, cubic_y, cubic_beta)
st.plotly_chart(dash_fig, use_container_width=True)
# Create sub-tabs for additional visualizations
pattern_tab, complex_tab = st.tabs(["Root Pattern Analysis", "Complex Plane View"])
# Root pattern visualization
with pattern_tab:
pattern_fig = create_root_pattern_visualization(result)
st.plotly_chart(pattern_fig, use_container_width=True)
# Complex plane visualization
with complex_tab:
# Slider for selecting z value
z_idx = st.slider(
"Select z index",
min_value=0,
max_value=len(result['z_values'])-1,
value=len(result['z_values'])//2,
help="Select a specific z value to visualize its roots in the complex plane"
)
# Create complex plane visualization
complex_fig = create_complex_plane_visualization(result, z_idx)
st.plotly_chart(complex_fig, use_container_width=True)
except Exception as e:
st.info("ð Set parameters and click 'Generate Im(s) vs z Analysis' to create a visualization.")
st.error(f"Error loading previous data: {str(e)}")
else:
# Show placeholder
st.info("ð Set parameters and click 'Generate Im(s) vs z Analysis' to create a visualization.")
st.markdown('
', unsafe_allow_html=True)
# Add footer with instructions
st.markdown("""
""", unsafe_allow_html=True)