euler314 commited on
Commit
c02def7
·
verified ·
1 Parent(s): a12ae05

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +204 -214
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
- def compute_custom_expression(betas, z_a, y, s_num_expr, s_denom_expr):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
126
  """
127
- Compute custom curve by:
128
- 1. Computing s = s_num/s_denom
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
- # First calculate s = num/denom
 
 
 
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, show_derivatives=False):
 
 
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
- custom_curve = None
 
 
171
  if s_num_expr and s_denom_expr:
172
- custom_curve = compute_custom_expression(betas, z_a, y, s_num_expr, s_denom_expr)
 
 
173
 
174
- # Compute derivatives
175
- derivatives = compute_all_derivatives(betas, z_mins, z_maxs, low_y_curve, high_y_curve,
176
- alt_low_expr, custom_curve)
 
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 custom_curve is not None:
194
- fig.add_trace(go.Scatter(x=betas, y=custom_curve, mode="markers+lines",
195
- name="Custom Expression", line=dict(color='purple')))
 
 
 
196
 
197
  if show_derivatives:
198
  # First derivatives
199
- fig.add_trace(go.Scatter(x=betas, y=derivatives['upper'][0], mode="lines",
200
- name="Upper z*(β) d/dβ", line=dict(color='blue', dash='dash')))
201
- fig.add_trace(go.Scatter(x=betas, y=derivatives['lower'][0], mode="lines",
202
- name="Lower z*(β) d/dβ", line=dict(color='lightblue', dash='dash')))
203
- fig.add_trace(go.Scatter(x=betas, y=derivatives['low_y'][0], mode="lines",
204
- name="Low y d/dβ", line=dict(color='red', dash='dash')))
205
- fig.add_trace(go.Scatter(x=betas, y=derivatives['high_y'][0], mode="lines",
206
- name="High y d/dβ", line=dict(color='green', dash='dash')))
207
- fig.add_trace(go.Scatter(x=betas, y=derivatives['alt_low'][0], mode="lines",
208
- name="Alt Low d/dβ", line=dict(color='orange', dash='dash')))
209
- if custom_curve is not None:
210
- fig.add_trace(go.Scatter(x=betas, y=derivatives['custom'][0], mode="lines",
211
- name="Custom d/dβ", line=dict(color='purple', dash='dash')))
212
-
213
- # Second derivatives
214
- fig.add_trace(go.Scatter(x=betas, y=derivatives['upper'][1], mode="lines",
215
- name="Upper z*(β) d²/dβ²", line=dict(color='blue', dash='dot')))
216
- fig.add_trace(go.Scatter(x=betas, y=derivatives['lower'][1], mode="lines",
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 Expression")
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
- # Add explanation of the curves
414
- st.markdown("### Curve Explanations")
415
- st.markdown("""
416
- - **Upper z*(β)** (Blue): Maximum z value where discriminant is zero
417
- - **Lower z*(β)** (Light Blue): Minimum z value where discriminant is zero
418
- - **Low y Expression** (Red): Asymptotic approximation for low y values
419
- - **High y Expression** (Green): Asymptotic approximation for high y values
420
- - **Alternate Low Expression** (Orange): Alternative asymptotic expression
421
- - **Custom s Expression** (Purple): Result from user-defined s substituted into:
422
- """)
423
- 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})}")
424
-
425
- # Display the current parameter values
426
- st.markdown("### Current Parameters")
427
- st.markdown(f"""
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: Curve Intersections -----
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
- if st.button("Compute Differentials", key="tab4_button"):
 
 
 
 
 
 
 
 
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
- fig_diff.add_trace(go.Scatter(x=betas_diff, y=diff_curve, mode="lines", name="Difference (Upper - Lower)", line=dict(color="magenta", width=2)))
509
- fig_diff.add_trace(go.Scatter(x=betas_diff, y=d1, mode="lines", name="First Derivative", line=dict(color="brown", width=2)))
510
- fig_diff.add_trace(go.Scatter(x=betas_diff, y=d2, mode="lines", name="Second Derivative", line=dict(color="black", width=2)))
511
- fig_diff.update_layout(title="Differential Analysis vs. β", xaxis_title="β", yaxis_title="Value", hovermode="x unified")
512
- st.plotly_chart(fig_diff, use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
+ """)