Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
@@ -11,6 +11,10 @@ st.set_page_config(
|
|
11 |
initial_sidebar_state="collapsed"
|
12 |
)
|
13 |
|
|
|
|
|
|
|
|
|
14 |
#############################
|
15 |
# 1) Define the discriminant
|
16 |
#############################
|
@@ -84,7 +88,6 @@ def sweep_beta_and_find_z_bounds(z_a, y, z_min, z_max, beta_steps, z_steps):
|
|
84 |
z_min_values.append(np.min(roots))
|
85 |
z_max_values.append(np.max(roots))
|
86 |
return betas, np.array(z_min_values), np.array(z_max_values)
|
87 |
-
|
88 |
@st.cache_data
|
89 |
def compute_low_y_curve(betas, z_a, y):
|
90 |
"""
|
@@ -122,28 +125,69 @@ def compute_alternate_low_expr(betas, z_a, y):
|
|
122 |
betas = np.array(betas)
|
123 |
return (z_a * y * betas * (z_a - 1) - 2*z_a*(1 - y) - 2*z_a**2) / (2 + 2*z_a)
|
124 |
|
125 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
126 |
"""
|
127 |
-
Compute custom curve
|
128 |
-
|
129 |
-
2. Inserting s into the final expression:
|
130 |
-
(y*beta*(z_a-1)*s + (a*s+1)*((y-1)*s-1))/((a*s+1)*(s^2 + s))
|
131 |
"""
|
132 |
beta_sym, z_a_sym, y_sym = sp.symbols("beta z_a y", positive=True)
|
133 |
-
local_dict = {"beta": beta_sym, "z_a": z_a_sym, "y": y_sym}
|
134 |
|
135 |
try:
|
136 |
-
#
|
|
|
|
|
|
|
137 |
num_expr = sp.sympify(s_num_expr, locals=local_dict)
|
138 |
denom_expr = sp.sympify(s_denom_expr, locals=local_dict)
|
139 |
-
s_expr = num_expr / denom_expr
|
140 |
-
|
141 |
-
# Now substitute this s into the main expression
|
142 |
-
a = z_a_sym # a is alias for z_a
|
143 |
-
numerator = y_sym*beta_sym*(z_a_sym-1)*s_expr + (a*s_expr+1)*((y_sym-1)*s_expr-1)
|
144 |
-
denominator = (a*s_expr+1)*(s_expr**2 + s_expr)
|
145 |
-
final_expr = numerator/denominator
|
146 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
147 |
except sp.SympifyError as e:
|
148 |
st.error(f"Error parsing expressions: {e}")
|
149 |
return np.full_like(betas, np.nan)
|
@@ -154,9 +198,10 @@ def compute_custom_expression(betas, z_a, y, s_num_expr, s_denom_expr):
|
|
154 |
if np.isscalar(result):
|
155 |
result = np.full_like(betas, result)
|
156 |
return result
|
157 |
-
|
158 |
def generate_z_vs_beta_plot(z_a, y, z_min, z_max, beta_steps, z_steps,
|
159 |
-
s_num_expr=None, s_denom_expr=None,
|
|
|
|
|
160 |
if z_a <= 0 or y <= 0 or z_min >= z_max:
|
161 |
st.error("Invalid input parameters.")
|
162 |
return None
|
@@ -167,15 +212,19 @@ def generate_z_vs_beta_plot(z_a, y, z_min, z_max, beta_steps, z_steps,
|
|
167 |
high_y_curve = compute_high_y_curve(betas, z_a, y)
|
168 |
alt_low_expr = compute_alternate_low_expr(betas, z_a, y)
|
169 |
|
170 |
-
|
|
|
|
|
171 |
if s_num_expr and s_denom_expr:
|
172 |
-
|
|
|
|
|
173 |
|
174 |
-
# Compute derivatives
|
175 |
-
|
176 |
-
|
|
|
177 |
|
178 |
-
# Create subplots: one for curves, one for first derivatives, one for second derivatives
|
179 |
fig = go.Figure()
|
180 |
|
181 |
# Original curves
|
@@ -190,40 +239,33 @@ def generate_z_vs_beta_plot(z_a, y, z_min, z_max, beta_steps, z_steps,
|
|
190 |
fig.add_trace(go.Scatter(x=betas, y=alt_low_expr, mode="markers+lines",
|
191 |
name="Alternate Low Expression", line=dict(color='orange')))
|
192 |
|
193 |
-
if
|
194 |
-
fig.add_trace(go.Scatter(x=betas, y=
|
195 |
-
name="Custom
|
|
|
|
|
|
|
196 |
|
197 |
if show_derivatives:
|
198 |
# First derivatives
|
199 |
-
|
200 |
-
|
201 |
-
|
202 |
-
|
203 |
-
|
204 |
-
|
205 |
-
|
206 |
-
|
207 |
-
|
208 |
-
|
209 |
-
if
|
210 |
-
|
211 |
-
|
212 |
-
|
213 |
-
|
214 |
-
|
215 |
-
|
216 |
-
|
217 |
-
name="Lower z*(β) d²/dβ²", line=dict(color='lightblue', dash='dot')))
|
218 |
-
fig.add_trace(go.Scatter(x=betas, y=derivatives['low_y'][1], mode="lines",
|
219 |
-
name="Low y d²/dβ²", line=dict(color='red', dash='dot')))
|
220 |
-
fig.add_trace(go.Scatter(x=betas, y=derivatives['high_y'][1], mode="lines",
|
221 |
-
name="High y d²/dβ²", line=dict(color='green', dash='dot')))
|
222 |
-
fig.add_trace(go.Scatter(x=betas, y=derivatives['alt_low'][1], mode="lines",
|
223 |
-
name="Alt Low d²/dβ²", line=dict(color='orange', dash='dot')))
|
224 |
-
if custom_curve is not None:
|
225 |
-
fig.add_trace(go.Scatter(x=betas, y=derivatives['custom'][1], mode="lines",
|
226 |
-
name="Custom d²/dβ²", line=dict(color='purple', dash='dot')))
|
227 |
|
228 |
fig.update_layout(
|
229 |
title="Curves vs β: z*(β) Boundaries and Asymptotic Expressions",
|
@@ -239,8 +281,6 @@ def generate_z_vs_beta_plot(z_a, y, z_min, z_max, beta_steps, z_steps,
|
|
239 |
)
|
240 |
)
|
241 |
return fig
|
242 |
-
|
243 |
-
|
244 |
def compute_cubic_roots(z, beta, z_a, y):
|
245 |
"""
|
246 |
Compute the roots of the cubic equation for given parameters.
|
@@ -286,95 +326,6 @@ def generate_root_plots(beta, y, z_a, z_min, z_max, n_points):
|
|
286 |
xaxis_title="z", yaxis_title="Re{s}", hovermode="x unified")
|
287 |
return fig_im, fig_re
|
288 |
|
289 |
-
def curve1(s, z, y):
|
290 |
-
"""First curve: z*s^2 + (z-y+1)*s + 1"""
|
291 |
-
return z*s**2 + (z-y+1)*s + 1
|
292 |
-
|
293 |
-
def curve2(s, y, beta, a):
|
294 |
-
"""Second curve: y*β*((a-1)*s)/(a*s+1)"""
|
295 |
-
return y*beta*((a-1)*s)/(a*s+1)
|
296 |
-
|
297 |
-
def find_intersections(z, y, beta, a, s_range, n_guesses, tolerance):
|
298 |
-
"""Find intersections between curve1 and curve2."""
|
299 |
-
def equation(s):
|
300 |
-
return curve1(s, z, y) - curve2(s, y, beta, a)
|
301 |
-
s_guesses = np.linspace(s_range[0], s_range[1], n_guesses)
|
302 |
-
intersections = []
|
303 |
-
for s_guess in s_guesses:
|
304 |
-
try:
|
305 |
-
s_sol = fsolve(equation, s_guess, full_output=True, xtol=tolerance)
|
306 |
-
if s_sol[2] == 1:
|
307 |
-
s_val = s_sol[0][0]
|
308 |
-
if (s_range[0] <= s_val <= s_range[1] and
|
309 |
-
not any(abs(s_val - s_prev) < tolerance for s_prev in intersections)):
|
310 |
-
if abs(equation(s_val)) < tolerance:
|
311 |
-
intersections.append(s_val)
|
312 |
-
except:
|
313 |
-
continue
|
314 |
-
intersections = np.sort(np.array(intersections))
|
315 |
-
if len(intersections) % 2 != 0:
|
316 |
-
refined_intersections = []
|
317 |
-
for i in range(len(intersections)-1):
|
318 |
-
mid_point = (intersections[i] + intersections[i+1]) / 2
|
319 |
-
try:
|
320 |
-
s_sol = fsolve(equation, mid_point, full_output=True, xtol=tolerance)
|
321 |
-
if s_sol[2] == 1:
|
322 |
-
s_val = s_sol[0][0]
|
323 |
-
if (intersections[i] < s_val < intersections[i+1] and
|
324 |
-
abs(equation(s_val)) < tolerance):
|
325 |
-
refined_intersections.append(s_val)
|
326 |
-
except:
|
327 |
-
continue
|
328 |
-
intersections = np.sort(np.append(intersections, refined_intersections))
|
329 |
-
return intersections
|
330 |
-
@st.cache_data
|
331 |
-
def compute_derivatives(curve, betas):
|
332 |
-
"""Compute first and second derivatives of a curve"""
|
333 |
-
d1 = np.gradient(curve, betas)
|
334 |
-
d2 = np.gradient(d1, betas)
|
335 |
-
return d1, d2
|
336 |
-
|
337 |
-
def compute_all_derivatives(betas, z_mins, z_maxs, low_y_curve, high_y_curve, alt_low_expr, custom_curve=None):
|
338 |
-
"""Compute derivatives for all curves"""
|
339 |
-
derivatives = {}
|
340 |
-
|
341 |
-
# Upper z*(β)
|
342 |
-
derivatives['upper'] = compute_derivatives(z_maxs, betas)
|
343 |
-
|
344 |
-
# Lower z*(β)
|
345 |
-
derivatives['lower'] = compute_derivatives(z_mins, betas)
|
346 |
-
|
347 |
-
# Low y Expression
|
348 |
-
derivatives['low_y'] = compute_derivatives(low_y_curve, betas)
|
349 |
-
|
350 |
-
# High y Expression
|
351 |
-
derivatives['high_y'] = compute_derivatives(high_y_curve, betas)
|
352 |
-
|
353 |
-
# Alternate Low Expression
|
354 |
-
derivatives['alt_low'] = compute_derivatives(alt_low_expr, betas)
|
355 |
-
|
356 |
-
# Custom Expression (if provided)
|
357 |
-
if custom_curve is not None:
|
358 |
-
derivatives['custom'] = compute_derivatives(custom_curve, betas)
|
359 |
-
|
360 |
-
return derivatives
|
361 |
-
def generate_curves_plot(z, y, beta, a, s_range, n_points, n_guesses, tolerance):
|
362 |
-
s = np.linspace(s_range[0], s_range[1], n_points)
|
363 |
-
y1 = curve1(s, z, y)
|
364 |
-
y2 = curve2(s, y, beta, a)
|
365 |
-
intersections = find_intersections(z, y, beta, a, s_range, n_guesses, tolerance)
|
366 |
-
fig = go.Figure()
|
367 |
-
fig.add_trace(go.Scatter(x=s, y=y1, mode='lines', name='z*s² + (z-y+1)*s + 1', line=dict(color='blue', width=2)))
|
368 |
-
fig.add_trace(go.Scatter(x=s, y=y2, mode='lines', name='y*β*((a-1)*s)/(a*s+1)', line=dict(color='red', width=2)))
|
369 |
-
if len(intersections) > 0:
|
370 |
-
fig.add_trace(go.Scatter(x=intersections, y=curve1(intersections, z, y),
|
371 |
-
mode='markers', name='Intersections',
|
372 |
-
marker=dict(size=12, color='green', symbol='x', line=dict(width=2))))
|
373 |
-
fig.update_layout(title=f"Curve Intersection Analysis (y={y:.4f}, β={beta:.4f}, a={a:.4f})",
|
374 |
-
xaxis_title="s", yaxis_title="Value", hovermode="closest",
|
375 |
-
showlegend=True, legend=dict(yanchor="top", y=0.99, xanchor="left", x=0.01))
|
376 |
-
return fig, intersections
|
377 |
-
|
378 |
# ----------------- Streamlit UI -----------------
|
379 |
st.title("Cubic Root Analysis")
|
380 |
|
@@ -394,42 +345,44 @@ with tab1:
|
|
394 |
beta_steps = st.slider("β steps", min_value=51, max_value=501, value=201, step=50, key="beta_steps")
|
395 |
z_steps = st.slider("z grid steps", min_value=1000, max_value=100000, value=50000, step=1000, key="z_steps")
|
396 |
|
397 |
-
st.subheader("Custom s
|
398 |
st.markdown("""Enter expressions for s = numerator/denominator
|
399 |
-
(using variables `y`, `beta`, `z_a`)""")
|
400 |
st.latex(r"\text{This s will be inserted into:}")
|
401 |
st.latex(r"\frac{y\beta(z_a-1)\underline{s}+(a\underline{s}+1)((y-1)\underline{s}-1)}{(a\underline{s}+1)(\underline{s}^2 + \underline{s})}")
|
402 |
s_num = st.text_input("s numerator", value="y*beta*(z_a-1)", key="s_num")
|
403 |
s_denom = st.text_input("s denominator", value="z_a", key="s_denom")
|
404 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
405 |
if st.button("Compute z vs. β Curves", key="tab1_button"):
|
406 |
with col2:
|
407 |
-
# Compute and plot the z vs. β curves
|
408 |
fig = generate_z_vs_beta_plot(z_a_1, y_1, z_min_1, z_max_1, beta_steps, z_steps,
|
409 |
-
s_num, s_denom)
|
410 |
if fig is not None:
|
411 |
st.plotly_chart(fig, use_container_width=True)
|
412 |
-
|
413 |
-
|
414 |
-
|
415 |
-
|
416 |
-
|
417 |
-
|
418 |
-
|
419 |
-
|
420 |
-
|
421 |
-
|
422 |
-
|
423 |
-
|
424 |
-
|
425 |
-
|
426 |
-
|
427 |
-
|
428 |
-
- z_a = {z_a_1}
|
429 |
-
- y = {y_1}
|
430 |
-
- z range: [{z_min_1}, {z_max_1}]
|
431 |
-
- s = ({s_num})/({s_denom})
|
432 |
-
""")
|
433 |
|
434 |
# ----- Tab 2: Im{s} vs. z -----
|
435 |
with tab2:
|
@@ -450,42 +403,8 @@ with tab2:
|
|
450 |
st.plotly_chart(fig_im, use_container_width=True)
|
451 |
st.plotly_chart(fig_re, use_container_width=True)
|
452 |
|
453 |
-
# ----- Tab 3:
|
454 |
with tab3:
|
455 |
-
st.header("Curve Intersection Analysis")
|
456 |
-
col1, col2 = st.columns([1, 2])
|
457 |
-
with col1:
|
458 |
-
z = st.slider("z", min_value=-10.0, max_value=10000.0, value=1.0, step=0.1, key="z_tab3")
|
459 |
-
y_3 = st.slider("y", min_value=0.1, max_value=1000.0, value=1.0, step=0.1, key="y_tab3")
|
460 |
-
beta_3 = st.slider("β", min_value=0.0, max_value=1.0, value=0.5, step=0.01, key="beta_tab3")
|
461 |
-
a = st.slider("a", min_value=0.1, max_value=1000.0, value=1.0, step=0.1, key="a_tab3")
|
462 |
-
st.subheader("s Range")
|
463 |
-
s_min = st.number_input("s_min", value=-5.0, key="s_min_tab3")
|
464 |
-
s_max = st.number_input("s_max", value=5.0, key="s_max_tab3")
|
465 |
-
with st.expander("Resolution Settings"):
|
466 |
-
s_points = st.slider("s grid points", min_value=1000, max_value=10000, value=5000, step=500, key="s_points_tab3")
|
467 |
-
intersection_guesses = st.slider("Intersection search points", min_value=200, max_value=2000, value=1000, step=100, key="intersect_guesses")
|
468 |
-
intersection_tolerance = st.select_slider(
|
469 |
-
"Intersection tolerance",
|
470 |
-
options=[1e-6, 1e-8, 1e-10, 1e-12, 1e-14, 1e-16, 1e-18, 1e-20],
|
471 |
-
value=1e-10,
|
472 |
-
key="intersect_tol"
|
473 |
-
)
|
474 |
-
if st.button("Compute Intersections", key="tab3_button"):
|
475 |
-
with col2:
|
476 |
-
s_range = (s_min, s_max)
|
477 |
-
fig, intersections = generate_curves_plot(z, y_3, beta_3, a, s_range, s_points, intersection_guesses, intersection_tolerance)
|
478 |
-
st.plotly_chart(fig, use_container_width=True)
|
479 |
-
if len(intersections) > 0:
|
480 |
-
st.subheader("Intersection Points")
|
481 |
-
for i, s_val in enumerate(intersections):
|
482 |
-
y_val = curve1(s_val, z, y_3)
|
483 |
-
st.write(f"Point {i+1}: s = {s_val:.6f}, y = {y_val:.6f}")
|
484 |
-
else:
|
485 |
-
st.write("No intersections found in the given range.")
|
486 |
-
|
487 |
-
# ----- Tab 4: Differential Analysis -----
|
488 |
-
with tab4:
|
489 |
st.header("Differential Analysis vs. β")
|
490 |
st.markdown("This page shows the difference between the Upper (blue) and Lower (lightblue) z*(β) curves, along with their first and second derivatives with respect to β.")
|
491 |
col1, col2 = st.columns([1, 2])
|
@@ -497,16 +416,87 @@ with tab4:
|
|
497 |
with st.expander("Resolution Settings"):
|
498 |
beta_steps_diff = st.slider("β steps", min_value=51, max_value=501, value=201, step=50, key="beta_steps_diff")
|
499 |
z_steps_diff = st.slider("z grid steps", min_value=1000, max_value=100000, value=50000, step=1000, key="z_steps_diff")
|
500 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
501 |
with col2:
|
502 |
betas_diff, lower_vals, upper_vals = sweep_beta_and_find_z_bounds(z_a_diff, y_diff, z_min_diff, z_max_diff, beta_steps_diff, z_steps_diff)
|
503 |
-
diff_curve = upper_vals - lower_vals
|
504 |
-
d1 = np.gradient(diff_curve, betas_diff)
|
505 |
-
d2 = np.gradient(d1, betas_diff)
|
506 |
|
|
|
507 |
fig_diff = go.Figure()
|
508 |
-
|
509 |
-
|
510 |
-
|
511 |
-
|
512 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
11 |
initial_sidebar_state="collapsed"
|
12 |
)
|
13 |
|
14 |
+
def add_sqrt_support(expr_str):
|
15 |
+
"""Replace 'sqrt(' with 'sp.sqrt(' for sympy compatibility"""
|
16 |
+
return expr_str.replace('sqrt(', 'sp.sqrt(')
|
17 |
+
|
18 |
#############################
|
19 |
# 1) Define the discriminant
|
20 |
#############################
|
|
|
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 |
"""
|
|
|
125 |
betas = np.array(betas)
|
126 |
return (z_a * y * betas * (z_a - 1) - 2*z_a*(1 - y) - 2*z_a**2) / (2 + 2*z_a)
|
127 |
|
128 |
+
@st.cache_data
|
129 |
+
def compute_derivatives(curve, betas):
|
130 |
+
"""Compute first and second derivatives of a curve"""
|
131 |
+
d1 = np.gradient(curve, betas)
|
132 |
+
d2 = np.gradient(d1, betas)
|
133 |
+
return d1, d2
|
134 |
+
|
135 |
+
def compute_all_derivatives(betas, z_mins, z_maxs, low_y_curve, high_y_curve, alt_low_expr, custom_curve1=None, custom_curve2=None):
|
136 |
+
"""Compute derivatives for all curves"""
|
137 |
+
derivatives = {}
|
138 |
+
|
139 |
+
# Upper z*(β)
|
140 |
+
derivatives['upper'] = compute_derivatives(z_maxs, betas)
|
141 |
+
|
142 |
+
# Lower z*(β)
|
143 |
+
derivatives['lower'] = compute_derivatives(z_mins, betas)
|
144 |
+
|
145 |
+
# Low y Expression
|
146 |
+
derivatives['low_y'] = compute_derivatives(low_y_curve, betas)
|
147 |
+
|
148 |
+
# High y Expression
|
149 |
+
derivatives['high_y'] = compute_derivatives(high_y_curve, betas)
|
150 |
+
|
151 |
+
# Alternate Low Expression
|
152 |
+
derivatives['alt_low'] = compute_derivatives(alt_low_expr, betas)
|
153 |
+
|
154 |
+
# Custom Expression 1 (if provided)
|
155 |
+
if custom_curve1 is not None:
|
156 |
+
derivatives['custom1'] = compute_derivatives(custom_curve1, betas)
|
157 |
+
|
158 |
+
# Custom Expression 2 (if provided)
|
159 |
+
if custom_curve2 is not None:
|
160 |
+
derivatives['custom2'] = compute_derivatives(custom_curve2, betas)
|
161 |
+
|
162 |
+
return derivatives
|
163 |
+
|
164 |
+
def compute_custom_expression(betas, z_a, y, s_num_expr, s_denom_expr, is_s_based=True):
|
165 |
"""
|
166 |
+
Compute custom curve. If is_s_based=True, compute using s substitution.
|
167 |
+
Otherwise, compute direct z(β) expression.
|
|
|
|
|
168 |
"""
|
169 |
beta_sym, z_a_sym, y_sym = sp.symbols("beta z_a y", positive=True)
|
170 |
+
local_dict = {"beta": beta_sym, "z_a": z_a_sym, "y": y_sym, "sp": sp}
|
171 |
|
172 |
try:
|
173 |
+
# Add sqrt support
|
174 |
+
s_num_expr = add_sqrt_support(s_num_expr)
|
175 |
+
s_denom_expr = add_sqrt_support(s_denom_expr)
|
176 |
+
|
177 |
num_expr = sp.sympify(s_num_expr, locals=local_dict)
|
178 |
denom_expr = sp.sympify(s_denom_expr, locals=local_dict)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
179 |
|
180 |
+
if is_s_based:
|
181 |
+
# Compute s and substitute into main expression
|
182 |
+
s_expr = num_expr / denom_expr
|
183 |
+
a = z_a_sym
|
184 |
+
numerator = y_sym*beta_sym*(z_a_sym-1)*s_expr + (a*s_expr+1)*((y_sym-1)*s_expr-1)
|
185 |
+
denominator = (a*s_expr+1)*(s_expr**2 + s_expr)
|
186 |
+
final_expr = numerator/denominator
|
187 |
+
else:
|
188 |
+
# Direct z(β) expression
|
189 |
+
final_expr = num_expr / denom_expr
|
190 |
+
|
191 |
except sp.SympifyError as e:
|
192 |
st.error(f"Error parsing expressions: {e}")
|
193 |
return np.full_like(betas, np.nan)
|
|
|
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,
|
204 |
+
show_derivatives=False):
|
205 |
if z_a <= 0 or y <= 0 or z_min >= z_max:
|
206 |
st.error("Invalid input parameters.")
|
207 |
return None
|
|
|
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 |
|
215 |
+
# Compute both custom curves
|
216 |
+
custom_curve1 = None
|
217 |
+
custom_curve2 = None
|
218 |
if s_num_expr and s_denom_expr:
|
219 |
+
custom_curve1 = compute_custom_expression(betas, z_a, y, s_num_expr, s_denom_expr, True)
|
220 |
+
if z_num_expr and z_denom_expr:
|
221 |
+
custom_curve2 = compute_custom_expression(betas, z_a, y, z_num_expr, z_denom_expr, False)
|
222 |
|
223 |
+
# Compute derivatives if needed
|
224 |
+
if show_derivatives:
|
225 |
+
derivatives = compute_all_derivatives(betas, z_mins, z_maxs, low_y_curve, high_y_curve,
|
226 |
+
alt_low_expr, custom_curve1, custom_curve2)
|
227 |
|
|
|
228 |
fig = go.Figure()
|
229 |
|
230 |
# Original curves
|
|
|
239 |
fig.add_trace(go.Scatter(x=betas, y=alt_low_expr, mode="markers+lines",
|
240 |
name="Alternate Low Expression", line=dict(color='orange')))
|
241 |
|
242 |
+
if custom_curve1 is not None:
|
243 |
+
fig.add_trace(go.Scatter(x=betas, y=custom_curve1, mode="markers+lines",
|
244 |
+
name="Custom 1 (s-based)", line=dict(color='purple')))
|
245 |
+
if custom_curve2 is not None:
|
246 |
+
fig.add_trace(go.Scatter(x=betas, y=custom_curve2, mode="markers+lines",
|
247 |
+
name="Custom 2 (direct)", line=dict(color='magenta')))
|
248 |
|
249 |
if show_derivatives:
|
250 |
# First derivatives
|
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 |
+
]
|
258 |
+
|
259 |
+
if custom_curve1 is not None:
|
260 |
+
curve_info.append(('custom1', 'Custom 1', 'purple'))
|
261 |
+
if custom_curve2 is not None:
|
262 |
+
curve_info.append(('custom2', 'Custom 2', 'magenta'))
|
263 |
+
|
264 |
+
for key, name, color in curve_info:
|
265 |
+
fig.add_trace(go.Scatter(x=betas, y=derivatives[key][0], mode="lines",
|
266 |
+
name=f"{name} d/dβ", line=dict(color=color, dash='dash')))
|
267 |
+
fig.add_trace(go.Scatter(x=betas, y=derivatives[key][1], mode="lines",
|
268 |
+
name=f"{name} d²/dβ²", line=dict(color=color, dash='dot')))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
269 |
|
270 |
fig.update_layout(
|
271 |
title="Curves vs β: z*(β) Boundaries and Asymptotic Expressions",
|
|
|
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 |
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 |
|
|
|
345 |
beta_steps = st.slider("β steps", min_value=51, max_value=501, value=201, step=50, key="beta_steps")
|
346 |
z_steps = st.slider("z grid steps", min_value=1000, max_value=100000, value=50000, step=1000, key="z_steps")
|
347 |
|
348 |
+
st.subheader("Custom Expression 1 (s-based)")
|
349 |
st.markdown("""Enter expressions for s = numerator/denominator
|
350 |
+
(using variables `y`, `beta`, `z_a`, and `sqrt()`)""")
|
351 |
st.latex(r"\text{This s will be inserted into:}")
|
352 |
st.latex(r"\frac{y\beta(z_a-1)\underline{s}+(a\underline{s}+1)((y-1)\underline{s}-1)}{(a\underline{s}+1)(\underline{s}^2 + \underline{s})}")
|
353 |
s_num = st.text_input("s numerator", value="y*beta*(z_a-1)", key="s_num")
|
354 |
s_denom = st.text_input("s denominator", value="z_a", key="s_denom")
|
355 |
|
356 |
+
st.subheader("Custom Expression 2 (direct z(β))")
|
357 |
+
st.markdown("""Enter direct expression for z(β) = numerator/denominator
|
358 |
+
(using variables `y`, `beta`, `z_a`, and `sqrt()`)""")
|
359 |
+
z_num = st.text_input("z(β) numerator", value="y*beta*(z_a-1)", key="z_num")
|
360 |
+
z_denom = st.text_input("z(β) denominator", value="1", key="z_denom")
|
361 |
+
|
362 |
+
show_derivatives = st.checkbox("Show derivatives", value=False)
|
363 |
+
|
364 |
if st.button("Compute z vs. β Curves", key="tab1_button"):
|
365 |
with col2:
|
|
|
366 |
fig = generate_z_vs_beta_plot(z_a_1, y_1, z_min_1, z_max_1, beta_steps, z_steps,
|
367 |
+
s_num, s_denom, z_num, z_denom, show_derivatives)
|
368 |
if fig is not None:
|
369 |
st.plotly_chart(fig, use_container_width=True)
|
370 |
+
st.markdown("### Curve Explanations")
|
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
|
378 |
+
- **Custom Expression 2** (Magenta): Direct z(β) expression
|
379 |
+
""")
|
380 |
+
if show_derivatives:
|
381 |
+
st.markdown("""
|
382 |
+
Derivatives are shown as:
|
383 |
+
- Dashed lines: First derivatives (d/dβ)
|
384 |
+
- Dotted lines: Second derivatives (d²/dβ²)
|
385 |
+
""")
|
|
|
|
|
|
|
|
|
|
|
386 |
|
387 |
# ----- Tab 2: Im{s} vs. z -----
|
388 |
with tab2:
|
|
|
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:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
408 |
st.header("Differential Analysis vs. β")
|
409 |
st.markdown("This page shows the difference between the Upper (blue) and Lower (lightblue) z*(β) curves, along with their first and second derivatives with respect to β.")
|
410 |
col1, col2 = st.columns([1, 2])
|
|
|
416 |
with st.expander("Resolution Settings"):
|
417 |
beta_steps_diff = st.slider("β steps", min_value=51, max_value=501, value=201, step=50, key="beta_steps_diff")
|
418 |
z_steps_diff = st.slider("z grid steps", min_value=1000, max_value=100000, value=50000, step=1000, key="z_steps_diff")
|
419 |
+
|
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 |
+
|
427 |
+
if st.button("Compute Differentials", key="tab3_button"):
|
428 |
with col2:
|
429 |
betas_diff, lower_vals, upper_vals = sweep_beta_and_find_z_bounds(z_a_diff, y_diff, z_min_diff, z_max_diff, beta_steps_diff, z_steps_diff)
|
|
|
|
|
|
|
430 |
|
431 |
+
# Create figure
|
432 |
fig_diff = go.Figure()
|
433 |
+
|
434 |
+
if analyze_upper_lower:
|
435 |
+
diff_curve = upper_vals - lower_vals
|
436 |
+
d1 = np.gradient(diff_curve, betas_diff)
|
437 |
+
d2 = np.gradient(d1, betas_diff)
|
438 |
+
|
439 |
+
fig_diff.add_trace(go.Scatter(x=betas_diff, y=diff_curve, mode="lines",
|
440 |
+
name="Upper-Lower Difference", line=dict(color="magenta", width=2)))
|
441 |
+
fig_diff.add_trace(go.Scatter(x=betas_diff, y=d1, mode="lines",
|
442 |
+
name="Upper-Lower d/dβ", line=dict(color="magenta", dash='dash')))
|
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)
|
461 |
+
d2 = np.gradient(d1, betas_diff)
|
462 |
+
|
463 |
+
fig_diff.add_trace(go.Scatter(x=betas_diff, y=high_y_curve, mode="lines",
|
464 |
+
name="High y", line=dict(color="green", width=2)))
|
465 |
+
fig_diff.add_trace(go.Scatter(x=betas_diff, y=d1, mode="lines",
|
466 |
+
name="High y d/dβ", line=dict(color="green", dash='dash')))
|
467 |
+
fig_diff.add_trace(go.Scatter(x=betas_diff, y=d2, mode="lines",
|
468 |
+
name="High y d²/dβ²", line=dict(color="green", dash='dot')))
|
469 |
+
|
470 |
+
if analyze_alt_low:
|
471 |
+
alt_low_curve = compute_alternate_low_expr(betas_diff, z_a_diff, y_diff)
|
472 |
+
d1 = np.gradient(alt_low_curve, betas_diff)
|
473 |
+
d2 = np.gradient(d1, betas_diff)
|
474 |
+
|
475 |
+
fig_diff.add_trace(go.Scatter(x=betas_diff, y=alt_low_curve, mode="lines",
|
476 |
+
name="Alt Low", line=dict(color="orange", width=2)))
|
477 |
+
fig_diff.add_trace(go.Scatter(x=betas_diff, y=d1, mode="lines",
|
478 |
+
name="Alt Low d/dβ", line=dict(color="orange", dash='dash')))
|
479 |
+
fig_diff.add_trace(go.Scatter(x=betas_diff, y=d2, mode="lines",
|
480 |
+
name="Alt Low d²/dβ²", line=dict(color="orange", dash='dot')))
|
481 |
+
|
482 |
+
fig_diff.update_layout(
|
483 |
+
title="Differential Analysis vs. β",
|
484 |
+
xaxis_title="β",
|
485 |
+
yaxis_title="Value",
|
486 |
+
hovermode="x unified",
|
487 |
+
showlegend=True,
|
488 |
+
legend=dict(
|
489 |
+
yanchor="top",
|
490 |
+
y=0.99,
|
491 |
+
xanchor="left",
|
492 |
+
x=0.01
|
493 |
+
)
|
494 |
+
)
|
495 |
+
st.plotly_chart(fig_diff, use_container_width=True)
|
496 |
+
|
497 |
+
st.markdown("""
|
498 |
+
### Curve Types
|
499 |
+
- Solid lines: Original curves
|
500 |
+
- Dashed lines: First derivatives (d/dβ)
|
501 |
+
- Dotted lines: Second derivatives (d²/dβ²)
|
502 |
+
""")
|