Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -88,6 +88,7 @@ def sweep_beta_and_find_z_bounds(z_a, y, z_min, z_max, beta_steps, z_steps):
|
|
88 |
z_min_values.append(np.min(roots))
|
89 |
z_max_values.append(np.max(roots))
|
90 |
return betas, np.array(z_min_values), np.array(z_max_values)
|
|
|
91 |
@st.cache_data
|
92 |
def compute_low_y_curve(betas, z_a, y):
|
93 |
"""
|
@@ -143,7 +144,8 @@ def compute_all_derivatives(betas, z_mins, z_maxs, low_y_curve, high_y_curve, al
|
|
143 |
derivatives['lower'] = compute_derivatives(z_mins, betas)
|
144 |
|
145 |
# Low y Expression
|
146 |
-
|
|
|
147 |
|
148 |
# High y Expression
|
149 |
derivatives['high_y'] = compute_derivatives(high_y_curve, betas)
|
@@ -198,6 +200,7 @@ def compute_custom_expression(betas, z_a, y, s_num_expr, s_denom_expr, is_s_base
|
|
198 |
if np.isscalar(result):
|
199 |
result = np.full_like(betas, result)
|
200 |
return result
|
|
|
201 |
def generate_z_vs_beta_plot(z_a, y, z_min, z_max, beta_steps, z_steps,
|
202 |
s_num_expr=None, s_denom_expr=None,
|
203 |
z_num_expr=None, z_denom_expr=None,
|
@@ -208,7 +211,10 @@ def generate_z_vs_beta_plot(z_a, y, z_min, z_max, beta_steps, z_steps,
|
|
208 |
|
209 |
betas = np.linspace(0, 1, beta_steps)
|
210 |
betas, z_mins, z_maxs = sweep_beta_and_find_z_bounds(z_a, y, z_min, z_max, beta_steps, z_steps)
|
211 |
-
|
|
|
|
|
|
|
212 |
high_y_curve = compute_high_y_curve(betas, z_a, y)
|
213 |
alt_low_expr = compute_alternate_low_expr(betas, z_a, y)
|
214 |
|
@@ -222,7 +228,7 @@ def generate_z_vs_beta_plot(z_a, y, z_min, z_max, beta_steps, z_steps,
|
|
222 |
|
223 |
# Compute derivatives if needed
|
224 |
if show_derivatives:
|
225 |
-
derivatives = compute_all_derivatives(betas, z_mins, z_maxs,
|
226 |
alt_low_expr, custom_curve1, custom_curve2)
|
227 |
|
228 |
fig = go.Figure()
|
@@ -232,8 +238,11 @@ def generate_z_vs_beta_plot(z_a, y, z_min, z_max, beta_steps, z_steps,
|
|
232 |
name="Upper z*(β)", line=dict(color='blue')))
|
233 |
fig.add_trace(go.Scatter(x=betas, y=z_mins, mode="markers+lines",
|
234 |
name="Lower z*(β)", line=dict(color='lightblue')))
|
235 |
-
|
236 |
-
|
|
|
|
|
|
|
237 |
fig.add_trace(go.Scatter(x=betas, y=high_y_curve, mode="markers+lines",
|
238 |
name="High y Expression", line=dict(color='green')))
|
239 |
fig.add_trace(go.Scatter(x=betas, y=alt_low_expr, mode="markers+lines",
|
@@ -251,7 +260,7 @@ def generate_z_vs_beta_plot(z_a, y, z_min, z_max, beta_steps, z_steps,
|
|
251 |
curve_info = [
|
252 |
('upper', 'Upper z*(β)', 'blue'),
|
253 |
('lower', 'Lower z*(β)', 'lightblue'),
|
254 |
-
('low_y', 'Low y', 'red'),
|
255 |
('high_y', 'High y', 'green'),
|
256 |
('alt_low', 'Alt Low', 'orange')
|
257 |
]
|
@@ -281,6 +290,7 @@ def generate_z_vs_beta_plot(z_a, y, z_min, z_max, beta_steps, z_steps,
|
|
281 |
)
|
282 |
)
|
283 |
return fig
|
|
|
284 |
def compute_cubic_roots(z, beta, z_a, y):
|
285 |
"""
|
286 |
Compute the roots of the cubic equation for given parameters.
|
@@ -326,11 +336,73 @@ def generate_root_plots(beta, y, z_a, z_min, z_max, n_points):
|
|
326 |
xaxis_title="z", yaxis_title="Re{s}", hovermode="x unified")
|
327 |
return fig_im, fig_re
|
328 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
329 |
# ----------------- Streamlit UI -----------------
|
330 |
st.title("Cubic Root Analysis")
|
331 |
|
332 |
-
# Define
|
333 |
-
tab1, tab2, tab3
|
334 |
|
335 |
# ----- Tab 1: z*(β) Curves -----
|
336 |
with tab1:
|
@@ -371,7 +443,6 @@ with tab1:
|
|
371 |
st.markdown("""
|
372 |
- **Upper z*(β)** (Blue): Maximum z value where discriminant is zero
|
373 |
- **Lower z*(β)** (Light Blue): Minimum z value where discriminant is zero
|
374 |
-
- **Low y Expression** (Red): Asymptotic approximation for low y values
|
375 |
- **High y Expression** (Green): Asymptotic approximation for high y values
|
376 |
- **Alternate Low Expression** (Orange): Alternative asymptotic expression
|
377 |
- **Custom Expression 1** (Purple): Result from user-defined s substituted into the main formula
|
@@ -384,9 +455,9 @@ with tab1:
|
|
384 |
- Dotted lines: Second derivatives (d²/dβ²)
|
385 |
""")
|
386 |
|
387 |
-
# ----- Tab 2: Im{s} vs. z -----
|
388 |
with tab2:
|
389 |
-
st.header("Plot Complex Roots vs. z")
|
390 |
col1, col2 = st.columns([1, 2])
|
391 |
with col1:
|
392 |
beta = st.number_input("β", value=0.5, min_value=0.0, max_value=1.0, key="beta_tab2")
|
@@ -396,12 +467,33 @@ with tab2:
|
|
396 |
z_max_2 = st.number_input("z_max", value=10.0, key="z_max_tab2")
|
397 |
with st.expander("Resolution Settings"):
|
398 |
z_points = st.slider("z grid points", min_value=1000, max_value=10000, value=5000, step=500, key="z_points")
|
399 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
400 |
with col2:
|
401 |
fig_im, fig_re = generate_root_plots(beta, y_2, z_a_2, z_min_2, z_max_2, z_points)
|
402 |
if fig_im is not None and fig_re is not None:
|
403 |
st.plotly_chart(fig_im, use_container_width=True)
|
404 |
st.plotly_chart(fig_re, use_container_width=True)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
405 |
|
406 |
# ----- Tab 3: Differential Analysis -----
|
407 |
with tab3:
|
@@ -420,7 +512,6 @@ with tab3:
|
|
420 |
# Add options for curve selection
|
421 |
st.subheader("Curves to Analyze")
|
422 |
analyze_upper_lower = st.checkbox("Upper-Lower Difference", value=True)
|
423 |
-
analyze_low_y = st.checkbox("Low y Expression", value=False)
|
424 |
analyze_high_y = st.checkbox("High y Expression", value=False)
|
425 |
analyze_alt_low = st.checkbox("Alternate Low Expression", value=False)
|
426 |
|
@@ -443,18 +534,6 @@ with tab3:
|
|
443 |
fig_diff.add_trace(go.Scatter(x=betas_diff, y=d2, mode="lines",
|
444 |
name="Upper-Lower d²/dβ²", line=dict(color="magenta", dash='dot')))
|
445 |
|
446 |
-
if analyze_low_y:
|
447 |
-
low_y_curve = compute_low_y_curve(betas_diff, z_a_diff, y_diff)
|
448 |
-
d1 = np.gradient(low_y_curve, betas_diff)
|
449 |
-
d2 = np.gradient(d1, betas_diff)
|
450 |
-
|
451 |
-
fig_diff.add_trace(go.Scatter(x=betas_diff, y=low_y_curve, mode="lines",
|
452 |
-
name="Low y", line=dict(color="red", width=2)))
|
453 |
-
fig_diff.add_trace(go.Scatter(x=betas_diff, y=d1, mode="lines",
|
454 |
-
name="Low y d/dβ", line=dict(color="red", dash='dash')))
|
455 |
-
fig_diff.add_trace(go.Scatter(x=betas_diff, y=d2, mode="lines",
|
456 |
-
name="Low y d²/dβ²", line=dict(color="red", dash='dot')))
|
457 |
-
|
458 |
if analyze_high_y:
|
459 |
high_y_curve = compute_high_y_curve(betas_diff, z_a_diff, y_diff)
|
460 |
d1 = np.gradient(high_y_curve, betas_diff)
|
|
|
88 |
z_min_values.append(np.min(roots))
|
89 |
z_max_values.append(np.max(roots))
|
90 |
return betas, np.array(z_min_values), np.array(z_max_values)
|
91 |
+
|
92 |
@st.cache_data
|
93 |
def compute_low_y_curve(betas, z_a, y):
|
94 |
"""
|
|
|
144 |
derivatives['lower'] = compute_derivatives(z_mins, betas)
|
145 |
|
146 |
# Low y Expression
|
147 |
+
if low_y_curve is not None:
|
148 |
+
derivatives['low_y'] = compute_derivatives(low_y_curve, betas)
|
149 |
|
150 |
# High y Expression
|
151 |
derivatives['high_y'] = compute_derivatives(high_y_curve, betas)
|
|
|
200 |
if np.isscalar(result):
|
201 |
result = np.full_like(betas, result)
|
202 |
return result
|
203 |
+
|
204 |
def generate_z_vs_beta_plot(z_a, y, z_min, z_max, beta_steps, z_steps,
|
205 |
s_num_expr=None, s_denom_expr=None,
|
206 |
z_num_expr=None, z_denom_expr=None,
|
|
|
211 |
|
212 |
betas = np.linspace(0, 1, beta_steps)
|
213 |
betas, z_mins, z_maxs = sweep_beta_and_find_z_bounds(z_a, y, z_min, z_max, beta_steps, z_steps)
|
214 |
+
|
215 |
+
# Remove low_y_curve computation and display as requested
|
216 |
+
# low_y_curve = compute_low_y_curve(betas, z_a, y) # Commented out
|
217 |
+
|
218 |
high_y_curve = compute_high_y_curve(betas, z_a, y)
|
219 |
alt_low_expr = compute_alternate_low_expr(betas, z_a, y)
|
220 |
|
|
|
228 |
|
229 |
# Compute derivatives if needed
|
230 |
if show_derivatives:
|
231 |
+
derivatives = compute_all_derivatives(betas, z_mins, z_maxs, None, high_y_curve,
|
232 |
alt_low_expr, custom_curve1, custom_curve2)
|
233 |
|
234 |
fig = go.Figure()
|
|
|
238 |
name="Upper z*(β)", line=dict(color='blue')))
|
239 |
fig.add_trace(go.Scatter(x=betas, y=z_mins, mode="markers+lines",
|
240 |
name="Lower z*(β)", line=dict(color='lightblue')))
|
241 |
+
|
242 |
+
# Remove low_y_curve trace as requested
|
243 |
+
# fig.add_trace(go.Scatter(x=betas, y=low_y_curve, mode="markers+lines",
|
244 |
+
# name="Low y Expression", line=dict(color='red')))
|
245 |
+
|
246 |
fig.add_trace(go.Scatter(x=betas, y=high_y_curve, mode="markers+lines",
|
247 |
name="High y Expression", line=dict(color='green')))
|
248 |
fig.add_trace(go.Scatter(x=betas, y=alt_low_expr, mode="markers+lines",
|
|
|
260 |
curve_info = [
|
261 |
('upper', 'Upper z*(β)', 'blue'),
|
262 |
('lower', 'Lower z*(β)', 'lightblue'),
|
263 |
+
# ('low_y', 'Low y', 'red'), # Removed as requested
|
264 |
('high_y', 'High y', 'green'),
|
265 |
('alt_low', 'Alt Low', 'orange')
|
266 |
]
|
|
|
290 |
)
|
291 |
)
|
292 |
return fig
|
293 |
+
|
294 |
def compute_cubic_roots(z, beta, z_a, y):
|
295 |
"""
|
296 |
Compute the roots of the cubic equation for given parameters.
|
|
|
336 |
xaxis_title="z", yaxis_title="Re{s}", hovermode="x unified")
|
337 |
return fig_im, fig_re
|
338 |
|
339 |
+
# New function for the eigenvalue distribution
|
340 |
+
@st.cache_data
|
341 |
+
def compute_eigenvalue_distribution(z_a, y, beta, x_min, x_max, num_points, epsilon=1e-6):
|
342 |
+
"""
|
343 |
+
Compute the eigenvalue distribution (ESD) of B_n as n → ∞.
|
344 |
+
|
345 |
+
B_n = (1/n)XX*
|
346 |
+
X is a p×n matrix with p/n → y as n → ∞
|
347 |
+
All elements of X are i.i.d. with distribution β·δ_a + (1-β)·δ_1
|
348 |
+
"""
|
349 |
+
x_values = np.linspace(x_min, x_max, num_points)
|
350 |
+
density_values = np.zeros(num_points)
|
351 |
+
|
352 |
+
second_moment = beta * (z_a**2) + (1-beta) * 1
|
353 |
+
|
354 |
+
for i, x in enumerate(x_values):
|
355 |
+
z = complex(x, epsilon)
|
356 |
+
|
357 |
+
# Define the fixed-point equation for the Stieltjes transform
|
358 |
+
def fixed_point_equation(m_real_imag):
|
359 |
+
m_real, m_imag = m_real_imag
|
360 |
+
m = complex(m_real, m_imag)
|
361 |
+
|
362 |
+
term1 = beta / (z_a - z - y * m * second_moment)
|
363 |
+
term2 = (1-beta) / (1 - z - y * m * second_moment)
|
364 |
+
|
365 |
+
result = term1 + term2 - m
|
366 |
+
|
367 |
+
return [result.real, result.imag]
|
368 |
+
|
369 |
+
# Solve the fixed-point equation
|
370 |
+
initial_guess = [-0.1, -0.1] # Initial guess for the Stieltjes transform
|
371 |
+
m_solution = fsolve(fixed_point_equation, initial_guess)
|
372 |
+
m = complex(m_solution[0], m_solution[1])
|
373 |
+
|
374 |
+
# Compute the density using the Stieltjes inversion formula
|
375 |
+
density_values[i] = -1/np.pi * m.imag
|
376 |
+
|
377 |
+
# Ensure density is non-negative
|
378 |
+
density_values = np.maximum(density_values, 0)
|
379 |
+
|
380 |
+
return x_values, density_values
|
381 |
+
|
382 |
+
def generate_esd_plot(z_a, y, beta, x_min, x_max, num_points=1000):
|
383 |
+
"""
|
384 |
+
Generate a plot of the eigenvalue distribution.
|
385 |
+
"""
|
386 |
+
x_values, density_values = compute_eigenvalue_distribution(z_a, y, beta, x_min, x_max, num_points)
|
387 |
+
|
388 |
+
fig = go.Figure()
|
389 |
+
fig.add_trace(go.Scatter(x=x_values, y=density_values, mode="lines",
|
390 |
+
name="Eigenvalue Density", line=dict(color='blue', width=2)))
|
391 |
+
|
392 |
+
fig.update_layout(
|
393 |
+
title=f"Eigenvalue Distribution (β={beta:.3f}, y={y:.3f}, z_a={z_a:.3f})",
|
394 |
+
xaxis_title="x",
|
395 |
+
yaxis_title="Density",
|
396 |
+
hovermode="x unified"
|
397 |
+
)
|
398 |
+
|
399 |
+
return fig
|
400 |
+
|
401 |
# ----------------- Streamlit UI -----------------
|
402 |
st.title("Cubic Root Analysis")
|
403 |
|
404 |
+
# Define three tabs (removed "Curve Intersections" tab)
|
405 |
+
tab1, tab2, tab3 = st.tabs(["z*(β) Curves", "Im{s} vs. z", "Differential Analysis"])
|
406 |
|
407 |
# ----- Tab 1: z*(β) Curves -----
|
408 |
with tab1:
|
|
|
443 |
st.markdown("""
|
444 |
- **Upper z*(β)** (Blue): Maximum z value where discriminant is zero
|
445 |
- **Lower z*(β)** (Light Blue): Minimum z value where discriminant is zero
|
|
|
446 |
- **High y Expression** (Green): Asymptotic approximation for high y values
|
447 |
- **Alternate Low Expression** (Orange): Alternative asymptotic expression
|
448 |
- **Custom Expression 1** (Purple): Result from user-defined s substituted into the main formula
|
|
|
455 |
- Dotted lines: Second derivatives (d²/dβ²)
|
456 |
""")
|
457 |
|
458 |
+
# ----- Tab 2: Im{s} vs. z and Eigenvalue Distribution -----
|
459 |
with tab2:
|
460 |
+
st.header("Plot Complex Roots vs. z and Eigenvalue Distribution")
|
461 |
col1, col2 = st.columns([1, 2])
|
462 |
with col1:
|
463 |
beta = st.number_input("β", value=0.5, min_value=0.0, max_value=1.0, key="beta_tab2")
|
|
|
467 |
z_max_2 = st.number_input("z_max", value=10.0, key="z_max_tab2")
|
468 |
with st.expander("Resolution Settings"):
|
469 |
z_points = st.slider("z grid points", min_value=1000, max_value=10000, value=5000, step=500, key="z_points")
|
470 |
+
|
471 |
+
# Add new settings for eigenvalue distribution
|
472 |
+
st.subheader("Eigenvalue Distribution Settings")
|
473 |
+
x_min = st.number_input("x_min", value=0.0, key="x_min_esd")
|
474 |
+
x_max = st.number_input("x_max", value=5.0, key="x_max_esd")
|
475 |
+
num_points = st.slider("Number of points", min_value=100, max_value=2000, value=1000, step=100, key="num_points_esd")
|
476 |
+
|
477 |
+
if st.button("Compute", key="tab2_button"):
|
478 |
with col2:
|
479 |
fig_im, fig_re = generate_root_plots(beta, y_2, z_a_2, z_min_2, z_max_2, z_points)
|
480 |
if fig_im is not None and fig_re is not None:
|
481 |
st.plotly_chart(fig_im, use_container_width=True)
|
482 |
st.plotly_chart(fig_re, use_container_width=True)
|
483 |
+
|
484 |
+
# Add eigenvalue distribution plot
|
485 |
+
fig_esd = generate_esd_plot(z_a_2, y_2, beta, x_min, x_max, num_points)
|
486 |
+
st.plotly_chart(fig_esd, use_container_width=True)
|
487 |
+
|
488 |
+
st.markdown("""
|
489 |
+
### Eigenvalue Distribution Explanation
|
490 |
+
This plot shows the limiting eigenvalue distribution of B_n = (1/n)XX* as n → ∞, where:
|
491 |
+
- X is a p×n matrix with p/n → y
|
492 |
+
- Elements of X are i.i.d. following distribution β·δ_a + (1-β)·δ_1
|
493 |
+
- a = z_a, y = y, β = β
|
494 |
+
|
495 |
+
The distribution is calculated using the Stieltjes transform approach.
|
496 |
+
""")
|
497 |
|
498 |
# ----- Tab 3: Differential Analysis -----
|
499 |
with tab3:
|
|
|
512 |
# Add options for curve selection
|
513 |
st.subheader("Curves to Analyze")
|
514 |
analyze_upper_lower = st.checkbox("Upper-Lower Difference", value=True)
|
|
|
515 |
analyze_high_y = st.checkbox("High y Expression", value=False)
|
516 |
analyze_alt_low = st.checkbox("Alternate Low Expression", value=False)
|
517 |
|
|
|
534 |
fig_diff.add_trace(go.Scatter(x=betas_diff, y=d2, mode="lines",
|
535 |
name="Upper-Lower d²/dβ²", line=dict(color="magenta", dash='dot')))
|
536 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
537 |
if analyze_high_y:
|
538 |
high_y_curve = compute_high_y_curve(betas_diff, z_a_diff, y_diff)
|
539 |
d1 = np.gradient(high_y_curve, betas_diff)
|