Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -90,21 +90,7 @@ def sweep_beta_and_find_z_bounds(z_a, y, z_min, z_max, beta_steps, z_steps):
|
|
90 |
z_max_values.append(np.max(roots))
|
91 |
return betas, np.array(z_min_values), np.array(z_max_values)
|
92 |
|
93 |
-
|
94 |
-
def compute_low_y_curve(betas, z_a, y):
|
95 |
-
"""
|
96 |
-
Compute the "Low y Expression" curve.
|
97 |
-
"""
|
98 |
-
betas = np.array(betas)
|
99 |
-
with np.errstate(invalid='ignore', divide='ignore'):
|
100 |
-
sqrt_term = y * betas * (z_a - 1)
|
101 |
-
sqrt_term = np.where(sqrt_term < 0, np.nan, np.sqrt(sqrt_term))
|
102 |
-
term = (-1 + sqrt_term) / z_a
|
103 |
-
numerator = (y - 2)*term + y * betas * ((z_a - 1)/z_a) - 1/z_a - 1
|
104 |
-
denominator = term**2 + term
|
105 |
-
mask = (denominator != 0) & ~np.isnan(denominator) & ~np.isnan(numerator)
|
106 |
-
result = np.where(mask, numerator/denominator, np.nan)
|
107 |
-
return result
|
108 |
|
109 |
@st.cache_data
|
110 |
def compute_high_y_curve(betas, z_a, y):
|
@@ -144,7 +130,7 @@ def compute_all_derivatives(betas, z_mins, z_maxs, low_y_curve, high_y_curve, al
|
|
144 |
# Lower z*(β)
|
145 |
derivatives['lower'] = compute_derivatives(z_mins, betas)
|
146 |
|
147 |
-
# Low y Expression
|
148 |
if low_y_curve is not None:
|
149 |
derivatives['low_y'] = compute_derivatives(low_y_curve, betas)
|
150 |
|
@@ -212,10 +198,7 @@ def generate_z_vs_beta_plot(z_a, y, z_min, z_max, beta_steps, z_steps,
|
|
212 |
|
213 |
betas = np.linspace(0, 1, beta_steps)
|
214 |
betas, z_mins, z_maxs = sweep_beta_and_find_z_bounds(z_a, y, z_min, z_max, beta_steps, z_steps)
|
215 |
-
|
216 |
-
# Remove low_y_curve computation and display as requested
|
217 |
-
# low_y_curve = compute_low_y_curve(betas, z_a, y) # Commented out
|
218 |
-
|
219 |
high_y_curve = compute_high_y_curve(betas, z_a, y)
|
220 |
alt_low_expr = compute_alternate_low_expr(betas, z_a, y)
|
221 |
|
@@ -239,11 +222,7 @@ def generate_z_vs_beta_plot(z_a, y, z_min, z_max, beta_steps, z_steps,
|
|
239 |
name="Upper z*(β)", line=dict(color='blue')))
|
240 |
fig.add_trace(go.Scatter(x=betas, y=z_mins, mode="markers+lines",
|
241 |
name="Lower z*(β)", line=dict(color='lightblue')))
|
242 |
-
|
243 |
-
# Remove low_y_curve trace as requested
|
244 |
-
# fig.add_trace(go.Scatter(x=betas, y=low_y_curve, mode="markers+lines",
|
245 |
-
# name="Low y Expression", line=dict(color='red')))
|
246 |
-
|
247 |
fig.add_trace(go.Scatter(x=betas, y=high_y_curve, mode="markers+lines",
|
248 |
name="High y Expression", line=dict(color='green')))
|
249 |
fig.add_trace(go.Scatter(x=betas, y=alt_low_expr, mode="markers+lines",
|
@@ -261,7 +240,7 @@ def generate_z_vs_beta_plot(z_a, y, z_min, z_max, beta_steps, z_steps,
|
|
261 |
curve_info = [
|
262 |
('upper', 'Upper z*(β)', 'blue'),
|
263 |
('lower', 'Lower z*(β)', 'lightblue'),
|
264 |
-
#
|
265 |
('high_y', 'High y', 'green'),
|
266 |
('alt_low', 'Alt Low', 'orange')
|
267 |
]
|
@@ -337,72 +316,68 @@ def generate_root_plots(beta, y, z_a, z_min, z_max, n_points):
|
|
337 |
xaxis_title="z", yaxis_title="Re{s}", hovermode="x unified")
|
338 |
return fig_im, fig_re
|
339 |
|
340 |
-
# New function for computing eigenvalue distribution directly
|
341 |
@st.cache_data
|
342 |
-
def
|
343 |
"""
|
344 |
-
|
345 |
|
346 |
Parameters:
|
347 |
-
|
348 |
-
|
349 |
-
|
350 |
-
|
351 |
-
|
352 |
-
|
353 |
-
|
354 |
-
|
|
|
|
|
|
|
355 |
"""
|
356 |
-
|
357 |
-
|
358 |
|
359 |
-
|
360 |
-
|
361 |
-
# This means each element is 'a' with probability β, and 1 with probability (1-β)
|
362 |
-
random_values = np.random.choice([z_a, 1.0], size=(p, n), p=[beta, 1-beta])
|
363 |
-
|
364 |
-
# Compute B_n = (1/n)XX*
|
365 |
-
X = random_values
|
366 |
-
XX_star = X @ X.T
|
367 |
-
B_n = XX_star / n
|
368 |
-
|
369 |
-
# Compute eigenvalues
|
370 |
-
eigenvalues = np.linalg.eigvalsh(B_n)
|
371 |
-
all_eigenvalues.extend(eigenvalues)
|
372 |
|
373 |
-
|
374 |
-
|
375 |
-
|
376 |
-
"""
|
377 |
-
Generate a plot of the eigenvalue distribution using KDE.
|
378 |
-
"""
|
379 |
-
# Compute eigenvalues
|
380 |
-
eigenvalues = compute_eigenvalue_distribution_direct(z_a, y, beta, n, num_samples)
|
381 |
|
382 |
-
#
|
383 |
-
|
384 |
|
385 |
-
#
|
386 |
-
|
387 |
-
x_max = np.max(eigenvalues) + 0.5
|
388 |
-
x_values = np.linspace(x_min, x_max, 1000)
|
389 |
-
density_values = kde(x_values)
|
390 |
|
391 |
-
#
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
392 |
fig = go.Figure()
|
393 |
-
fig.add_trace(go.Scatter(x=x_values, y=density_values, mode="lines",
|
394 |
-
name="Eigenvalue Density", line=dict(color='blue', width=2)))
|
395 |
|
396 |
-
# Add
|
397 |
-
fig.add_trace(go.
|
398 |
-
|
399 |
-
|
|
|
|
|
|
|
400 |
|
401 |
fig.update_layout(
|
402 |
-
title=f"Eigenvalue Distribution (
|
403 |
xaxis_title="Eigenvalue",
|
404 |
yaxis_title="Density",
|
405 |
-
hovermode="
|
|
|
406 |
)
|
407 |
|
408 |
return fig
|
@@ -410,7 +385,7 @@ def generate_esd_plot_direct(z_a, y, beta, n, num_samples=10, bandwidth=0.1):
|
|
410 |
# ----------------- Streamlit UI -----------------
|
411 |
st.title("Cubic Root Analysis")
|
412 |
|
413 |
-
# Define three tabs (removed "Curve Intersections"
|
414 |
tab1, tab2, tab3 = st.tabs(["z*(β) Curves", "Im{s} vs. z", "Differential Analysis"])
|
415 |
|
416 |
# ----- Tab 1: z*(β) Curves -----
|
@@ -464,9 +439,9 @@ with tab1:
|
|
464 |
- Dotted lines: Second derivatives (d²/dβ²)
|
465 |
""")
|
466 |
|
467 |
-
# ----- Tab 2: Im{s} vs. z
|
468 |
with tab2:
|
469 |
-
st.header("Plot Complex Roots vs. z
|
470 |
col1, col2 = st.columns([1, 2])
|
471 |
with col1:
|
472 |
beta = st.number_input("β", value=0.5, min_value=0.0, max_value=1.0, key="beta_tab2")
|
@@ -476,39 +451,35 @@ with tab2:
|
|
476 |
z_max_2 = st.number_input("z_max", value=10.0, key="z_max_tab2")
|
477 |
with st.expander("Resolution Settings"):
|
478 |
z_points = st.slider("z grid points", min_value=1000, max_value=10000, value=5000, step=500, key="z_points")
|
479 |
-
|
480 |
-
# Add new settings for eigenvalue distribution
|
481 |
-
st.subheader("Eigenvalue Distribution Settings")
|
482 |
-
matrix_size = st.slider("Matrix size (n)", min_value=50, max_value=1000, value=200, step=50, key="matrix_size")
|
483 |
-
num_samples = st.slider("Number of matrix samples", min_value=1, max_value=50, value=10, step=1, key="num_samples")
|
484 |
-
bandwidth = st.slider("KDE bandwidth", min_value=0.01, max_value=0.5, value=0.1, step=0.01, key="kde_bandwidth")
|
485 |
-
|
486 |
-
if st.button("Compute", key="tab2_button"):
|
487 |
with col2:
|
488 |
fig_im, fig_re = generate_root_plots(beta, y_2, z_a_2, z_min_2, z_max_2, z_points)
|
489 |
if fig_im is not None and fig_re is not None:
|
490 |
st.plotly_chart(fig_im, use_container_width=True)
|
491 |
st.plotly_chart(fig_re, use_container_width=True)
|
492 |
-
|
493 |
-
|
494 |
-
|
495 |
-
|
496 |
-
|
497 |
-
|
498 |
-
|
499 |
-
|
500 |
-
|
501 |
-
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
506 |
-
|
507 |
-
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
|
|
|
|
|
|
|
512 |
|
513 |
# ----- Tab 3: Differential Analysis -----
|
514 |
with tab3:
|
|
|
90 |
z_max_values.append(np.max(roots))
|
91 |
return betas, np.array(z_min_values), np.array(z_max_values)
|
92 |
|
93 |
+
# Removed the compute_low_y_curve function
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
94 |
|
95 |
@st.cache_data
|
96 |
def compute_high_y_curve(betas, z_a, y):
|
|
|
130 |
# Lower z*(β)
|
131 |
derivatives['lower'] = compute_derivatives(z_mins, betas)
|
132 |
|
133 |
+
# Low y Expression (only if provided)
|
134 |
if low_y_curve is not None:
|
135 |
derivatives['low_y'] = compute_derivatives(low_y_curve, betas)
|
136 |
|
|
|
198 |
|
199 |
betas = np.linspace(0, 1, beta_steps)
|
200 |
betas, z_mins, z_maxs = sweep_beta_and_find_z_bounds(z_a, y, z_min, z_max, beta_steps, z_steps)
|
201 |
+
# Removed low_y_curve computation
|
|
|
|
|
|
|
202 |
high_y_curve = compute_high_y_curve(betas, z_a, y)
|
203 |
alt_low_expr = compute_alternate_low_expr(betas, z_a, y)
|
204 |
|
|
|
222 |
name="Upper z*(β)", line=dict(color='blue')))
|
223 |
fig.add_trace(go.Scatter(x=betas, y=z_mins, mode="markers+lines",
|
224 |
name="Lower z*(β)", line=dict(color='lightblue')))
|
225 |
+
# Removed the Low y Expression trace
|
|
|
|
|
|
|
|
|
226 |
fig.add_trace(go.Scatter(x=betas, y=high_y_curve, mode="markers+lines",
|
227 |
name="High y Expression", line=dict(color='green')))
|
228 |
fig.add_trace(go.Scatter(x=betas, y=alt_low_expr, mode="markers+lines",
|
|
|
240 |
curve_info = [
|
241 |
('upper', 'Upper z*(β)', 'blue'),
|
242 |
('lower', 'Lower z*(β)', 'lightblue'),
|
243 |
+
# Removed low_y curve
|
244 |
('high_y', 'High y', 'green'),
|
245 |
('alt_low', 'Alt Low', 'orange')
|
246 |
]
|
|
|
316 |
xaxis_title="z", yaxis_title="Re{s}", hovermode="x unified")
|
317 |
return fig_im, fig_re
|
318 |
|
|
|
319 |
@st.cache_data
|
320 |
+
def generate_eigenvalue_distribution(beta, y, z_a, n=1000, seed=42):
|
321 |
"""
|
322 |
+
Generate the eigenvalue distribution of B_n = S_n T_n as n→∞
|
323 |
|
324 |
Parameters:
|
325 |
+
-----------
|
326 |
+
beta : float
|
327 |
+
Fraction of components equal to z_a
|
328 |
+
y : float
|
329 |
+
Aspect ratio p/n
|
330 |
+
z_a : float
|
331 |
+
Value for the delta mass at z_a
|
332 |
+
n : int
|
333 |
+
Number of samples
|
334 |
+
seed : int
|
335 |
+
Random seed for reproducibility
|
336 |
"""
|
337 |
+
# Set random seed
|
338 |
+
np.random.seed(seed)
|
339 |
|
340 |
+
# Compute dimension p based on aspect ratio y
|
341 |
+
p = int(y * n)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
342 |
|
343 |
+
# Constructing T_n (Population / Shape Matrix)
|
344 |
+
T_diag = np.where(np.random.rand(p) < beta, z_a, 1.0)
|
345 |
+
T_n = np.diag(T_diag)
|
|
|
|
|
|
|
|
|
|
|
346 |
|
347 |
+
# Generate the data matrix X with i.i.d. standard normal entries
|
348 |
+
X = np.random.randn(p, n)
|
349 |
|
350 |
+
# Compute the sample covariance matrix S_n = (1/n) * XX^T
|
351 |
+
S_n = (1 / n) * (X @ X.T)
|
|
|
|
|
|
|
352 |
|
353 |
+
# Compute B_n = S_n T_n
|
354 |
+
B_n = S_n @ T_n
|
355 |
+
|
356 |
+
# Compute eigenvalues of B_n
|
357 |
+
eigenvalues = np.linalg.eigvalsh(B_n)
|
358 |
+
|
359 |
+
# Use KDE to compute a smooth density estimate
|
360 |
+
kde = gaussian_kde(eigenvalues)
|
361 |
+
x_vals = np.linspace(min(eigenvalues), max(eigenvalues), 500)
|
362 |
+
kde_vals = kde(x_vals)
|
363 |
+
|
364 |
+
# Create figure
|
365 |
fig = go.Figure()
|
|
|
|
|
366 |
|
367 |
+
# Add histogram trace
|
368 |
+
fig.add_trace(go.Histogram(x=eigenvalues, histnorm='probability density',
|
369 |
+
name="Histogram", marker=dict(color='blue', opacity=0.6)))
|
370 |
+
|
371 |
+
# Add KDE trace
|
372 |
+
fig.add_trace(go.Scatter(x=x_vals, y=kde_vals, mode="lines",
|
373 |
+
name="KDE", line=dict(color='red', width=2)))
|
374 |
|
375 |
fig.update_layout(
|
376 |
+
title=f"Eigenvalue Distribution for B_n = S_n T_n (y={y:.1f}, β={beta:.2f}, a={z_a:.1f})",
|
377 |
xaxis_title="Eigenvalue",
|
378 |
yaxis_title="Density",
|
379 |
+
hovermode="closest",
|
380 |
+
showlegend=True
|
381 |
)
|
382 |
|
383 |
return fig
|
|
|
385 |
# ----------------- Streamlit UI -----------------
|
386 |
st.title("Cubic Root Analysis")
|
387 |
|
388 |
+
# Define three tabs (removed "Curve Intersections")
|
389 |
tab1, tab2, tab3 = st.tabs(["z*(β) Curves", "Im{s} vs. z", "Differential Analysis"])
|
390 |
|
391 |
# ----- Tab 1: z*(β) Curves -----
|
|
|
439 |
- Dotted lines: Second derivatives (d²/dβ²)
|
440 |
""")
|
441 |
|
442 |
+
# ----- Tab 2: Im{s} vs. z -----
|
443 |
with tab2:
|
444 |
+
st.header("Plot Complex Roots vs. z")
|
445 |
col1, col2 = st.columns([1, 2])
|
446 |
with col1:
|
447 |
beta = st.number_input("β", value=0.5, min_value=0.0, max_value=1.0, key="beta_tab2")
|
|
|
451 |
z_max_2 = st.number_input("z_max", value=10.0, key="z_max_tab2")
|
452 |
with st.expander("Resolution Settings"):
|
453 |
z_points = st.slider("z grid points", min_value=1000, max_value=10000, value=5000, step=500, key="z_points")
|
454 |
+
if st.button("Compute Complex Roots vs. z", key="tab2_button"):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
455 |
with col2:
|
456 |
fig_im, fig_re = generate_root_plots(beta, y_2, z_a_2, z_min_2, z_max_2, z_points)
|
457 |
if fig_im is not None and fig_re is not None:
|
458 |
st.plotly_chart(fig_im, use_container_width=True)
|
459 |
st.plotly_chart(fig_re, use_container_width=True)
|
460 |
+
|
461 |
+
# Add a separator
|
462 |
+
st.markdown("---")
|
463 |
+
|
464 |
+
# Add eigenvalue distribution section
|
465 |
+
st.header("Eigenvalue Distribution for B_n = S_n T_n")
|
466 |
+
st.markdown("""
|
467 |
+
This simulation generates the eigenvalue distribution of B_n as n→∞, where:
|
468 |
+
- B_n = (1/n)XX* with X being a p×n matrix
|
469 |
+
- p/n → y as n→∞
|
470 |
+
- All elements of X are i.i.d with distribution β·δ(z_a) + (1-β)·δ(1)
|
471 |
+
""")
|
472 |
+
|
473 |
+
col_eigen1, col_eigen2 = st.columns([1, 2])
|
474 |
+
with col_eigen1:
|
475 |
+
n_samples = st.slider("Number of samples (n)", min_value=100, max_value=2000, value=1000, step=100)
|
476 |
+
sim_seed = st.number_input("Random seed", min_value=1, max_value=1000, value=42, step=1)
|
477 |
+
|
478 |
+
if st.button("Generate Eigenvalue Distribution", key="tab2_eigen_button"):
|
479 |
+
with col_eigen2:
|
480 |
+
fig_eigen = generate_eigenvalue_distribution(beta, y_2, z_a_2, n=n_samples, seed=sim_seed)
|
481 |
+
if fig_eigen is not None:
|
482 |
+
st.plotly_chart(fig_eigen, use_container_width=True)
|
483 |
|
484 |
# ----- Tab 3: Differential Analysis -----
|
485 |
with tab3:
|