euler314 commited on
Commit
da34afc
·
verified ·
1 Parent(s): f159645

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +109 -178
app.py CHANGED
@@ -8,9 +8,26 @@ from scipy.optimize import fsolve
8
  st.set_page_config(
9
  page_title="Cubic Root Analysis",
10
  layout="wide",
11
- initial_sidebar_state="collapsed"
12
  )
13
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
14
  #############################
15
  # 1) Define the discriminant
16
  #############################
@@ -73,7 +90,6 @@ def find_z_at_discriminant_zero(z_a, y, beta, z_min, z_max, steps):
73
  roots_found.append(root_approx)
74
 
75
  return np.array(roots_found)
76
-
77
  @st.cache_data
78
  def sweep_beta_and_find_z_bounds(z_a, y, z_min, z_max, beta_steps, z_steps):
79
  """
@@ -127,6 +143,23 @@ def compute_high_y_curve(betas, z_a, y):
127
  numerator = -4*a*(a-1)*y*betas - 2*a*y - 2*a*(2*a-1)
128
  return numerator/denominator
129
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
130
  def compute_custom_expression(betas, z_a, y, num_expr_str, denom_expr_str):
131
  """
132
  Compute a custom curve given numerator and denominator expressions
@@ -186,8 +219,7 @@ def generate_z_vs_beta_plot(z_a, y, z_min, z_max, beta_steps, z_steps,
186
  line=dict(color='lightblue'),
187
  )
188
  )
189
-
190
- fig.add_trace(
191
  go.Scatter(
192
  x=betas,
193
  y=low_y_curve,
@@ -231,8 +263,7 @@ def generate_z_vs_beta_plot(z_a, y, z_min, z_max, beta_steps, z_steps,
231
  hovermode="x unified",
232
  )
233
 
234
- # ----- NEW GRID: Compute Derivatives with Respect to β -----
235
- # Use numpy.gradient assuming betas is evenly spaced.
236
  dzmax_dbeta = np.gradient(z_maxs, betas)
237
  dzmin_dbeta = np.gradient(z_mins, betas)
238
  dlowy_dbeta = np.gradient(low_y_curve, betas)
@@ -251,8 +282,7 @@ def generate_z_vs_beta_plot(z_a, y, z_min, z_max, beta_steps, z_steps,
251
  line=dict(color='blue'),
252
  )
253
  )
254
-
255
- fig_deriv.add_trace(
256
  go.Scatter(
257
  x=betas,
258
  y=dzmin_dbeta,
@@ -318,7 +348,6 @@ def compute_cubic_roots(z, beta, z_a, y):
318
  coeffs = [a, b, c, d]
319
  roots = np.roots(coeffs)
320
  return roots
321
-
322
  def generate_root_plots(beta, y, z_a, z_min, z_max, n_points):
323
  """Generate both Im(s) and Re(s) vs. z plots"""
324
  if z_a <= 0 or y <= 0 or z_min >= z_max:
@@ -377,126 +406,11 @@ def generate_root_plots(beta, y, z_a, z_min, z_max, n_points):
377
  )
378
 
379
  return fig_im, fig_re
380
-
381
- def curve1(s, z, y):
382
- """First curve: z*s^2 + (z-y+1)*s + 1"""
383
- return z*s**2 + (z-y+1)*s + 1
384
-
385
- def curve2(s, y, beta, a):
386
- """Second curve: y*β*((a-1)*s)/(a*s+1)"""
387
- return y*beta*((a-1)*s)/(a*s+1)
388
-
389
- def find_intersections(z, y, beta, a, s_range, n_guesses, tolerance):
390
- """Find intersections between the two curves with improved accuracy"""
391
- def equation(s):
392
- return curve1(s, z, y) - curve2(s, y, beta, a)
393
-
394
- # Create a finer grid of initial guesses
395
- s_guesses = np.linspace(s_range[0], s_range[1], n_guesses)
396
- intersections = []
397
-
398
- # First pass: find all potential intersections
399
- for s_guess in s_guesses:
400
- try:
401
- s_sol = fsolve(equation, s_guess, full_output=True, xtol=tolerance)
402
- if s_sol[2] == 1: # Check if convergence was achieved
403
- s_val = s_sol[0][0]
404
- if (s_range[0] <= s_val <= s_range[1] and
405
- not any(abs(s_val - s_prev) < tolerance for s_prev in intersections)):
406
- if abs(equation(s_val)) < tolerance:
407
- intersections.append(s_val)
408
- except:
409
- continue
410
-
411
- # Sort intersections
412
- intersections = np.sort(np.array(intersections))
413
-
414
- # Ensure even number of intersections by checking for missed ones
415
- if len(intersections) % 2 != 0:
416
- refined_intersections = []
417
- for i in range(len(intersections)-1):
418
- mid_point = (intersections[i] + intersections[i+1])/2
419
- try:
420
- s_sol = fsolve(equation, mid_point, full_output=True, xtol=tolerance)
421
- if s_sol[2] == 1:
422
- s_val = s_sol[0][0]
423
- if (intersections[i] < s_val < intersections[i+1] and
424
- abs(equation(s_val)) < tolerance):
425
- refined_intersections.append(s_val)
426
- except:
427
- continue
428
-
429
- intersections = np.sort(np.append(intersections, refined_intersections))
430
-
431
- return intersections
432
-
433
- def generate_curves_plot(z, y, beta, a, s_range, n_points, n_guesses, tolerance):
434
- s = np.linspace(s_range[0], s_range[1], n_points)
435
-
436
- # Compute curves
437
- y1 = curve1(s, z, y)
438
- y2 = curve2(s, y, beta, a)
439
-
440
- # Find intersections with improved accuracy
441
- intersections = find_intersections(z, y, beta, a, s_range, n_guesses, tolerance)
442
-
443
- fig = go.Figure()
444
-
445
- fig.add_trace(
446
- go.Scatter(
447
- x=s, y=y1,
448
- mode='lines',
449
- name='z*s² + (z-y+1)*s + 1',
450
- line=dict(color='blue', width=2)
451
- )
452
- )
453
-
454
- fig.add_trace(
455
- go.Scatter(
456
- x=s, y=y2,
457
- mode='lines',
458
- name='y*β*((a-1)*s)/(a*s+1)',
459
- line=dict(color='red', width=2)
460
- )
461
- )
462
-
463
- if len(intersections) > 0:
464
- fig.add_trace(
465
- go.Scatter(
466
- x=intersections,
467
- y=curve1(intersections, z, y),
468
- mode='markers',
469
- name='Intersections',
470
- marker=dict(
471
- size=12,
472
- color='green',
473
- symbol='x',
474
- line=dict(width=2)
475
- )
476
- )
477
- )
478
-
479
- fig.update_layout(
480
- title=f"Curve Intersection Analysis (y={y:.4f}, β={beta:.4f}, a={a:.4f})",
481
- xaxis_title="s",
482
- yaxis_title="Value",
483
- hovermode="closest",
484
- showlegend=True,
485
- legend=dict(
486
- yanchor="top",
487
- y=0.99,
488
- xanchor="left",
489
- x=0.01
490
- )
491
- )
492
-
493
- return fig, intersections
494
-
495
  # ------------------- Streamlit UI -------------------
496
 
497
  st.title("Cubic Root Analysis")
498
 
499
- tab1, tab2, tab3 = st.tabs(["z*(β) Curves", "Im{s} vs. z", "Curve Intersections"])
500
 
501
  with tab1:
502
  st.header("Find z Values where Cubic Roots Transition Between Real and Complex")
@@ -513,13 +427,6 @@ with tab1:
513
  beta_steps = st.slider("β steps", min_value=51, max_value=501, value=201, step=50)
514
  z_steps = st.slider("z grid steps", min_value=1000, max_value=100000, value=50000, step=1000)
515
 
516
- st.subheader("Custom Expression")
517
- st.markdown("Enter a **numerator** and a **denominator** expression as functions of `z_a`, `beta`, and `y`.")
518
- default_num = "(y - 2)*((-1 + sqrt(y*beta*(z_a - 1)))/z_a) + y*beta*((z_a-1)/z_a) - 1/z_a - 1"
519
- default_denom = "((-1 + sqrt(y*beta*(z_a - 1)))/z_a)**2 + ((-1 + sqrt(y*beta*(z_a - 1)))/z_a)"
520
- custom_num_expr = st.text_input("Numerator Expression", value=default_num)
521
- custom_denom_expr = st.text_input("Denominator Expression", value=default_denom)
522
-
523
  if st.button("Compute z vs. β Curves"):
524
  with col2:
525
  fig_main, fig_deriv = generate_z_vs_beta_plot(z_a_1, y_1, z_min_1, z_max_1,
@@ -527,24 +434,8 @@ with tab1:
527
  custom_num_expr, custom_denom_expr)
528
  if fig_main is not None and fig_deriv is not None:
529
  st.plotly_chart(fig_main, use_container_width=True)
530
- st.markdown("### Derivative of Each Curve vs. β")
531
  st.plotly_chart(fig_deriv, use_container_width=True)
532
-
533
- st.markdown("### Additional Expressions")
534
- st.markdown("""
535
- **Low y Expression (Red):**
536
- ```
537
- ((y - 2)*(-1 + sqrt(y*β*(z_a-1)))/z_a + y*β*((z_a-1)/z_a) - 1/z_a - 1) /
538
- (((-1 + sqrt(y*β*(z_a-1)))/z_a)**2 + ((-1 + sqrt(y*β*(z_a-1)))/z_a))
539
- ```
540
-
541
- **High y Expression (Green):**
542
- ```
543
- (- 4 z_a*(z_a-1)*y*β - 2z_a*y + 2z_a*(2z_a-1))/(1-2z_a)
544
- ```
545
- where z_a is the input parameter.
546
- """)
547
-
548
  with tab2:
549
  st.header("Plot Complex Roots vs. z")
550
 
@@ -567,40 +458,80 @@ with tab2:
567
  st.plotly_chart(fig_im, use_container_width=True)
568
  st.plotly_chart(fig_re, use_container_width=True)
569
 
570
- with tab3:
571
- st.header("Curve Intersection Analysis")
572
 
573
  col1, col2 = st.columns([1, 2])
574
 
575
  with col1:
576
- z = st.slider("z", min_value=-10.0, max_value=10000.0, value=1.0, step=0.1)
577
- y_3 = st.slider("y", min_value=0.1, max_value=1000.0, value=1.0, step=0.1, key="y_3")
578
- beta_3 = st.slider("β", min_value=0.0, max_value=1.0, value=0.5, step=0.01, key="beta_3")
579
- a = st.slider("a", min_value=0.1, max_value=1000.0, value=1.0, step=0.1)
580
-
581
- st.subheader("s Range")
582
- s_min = st.number_input("s_min", value=-5.0)
583
- s_max = st.number_input("s_max", value=5.0)
584
 
585
  with st.expander("Resolution Settings"):
586
- s_points = st.slider("s grid points", min_value=1000, max_value=10000, value=5000, step=500)
587
- intersection_guesses = st.slider("Intersection search points", min_value=200, max_value=2000, value=1000, step=100)
588
- intersection_tolerance = st.select_slider(
589
- "Intersection tolerance",
590
- options=[1e-6, 1e-8, 1e-10, 1e-12, 1e-14, 1e-16, 1e-18, 1e-20],
591
- value=1e-10
592
- )
593
-
594
- if st.button("Compute Intersections"):
595
  with col2:
596
- s_range = (s_min, s_max)
597
- fig, intersections = generate_curves_plot(z, y_3, beta_3, a, s_range, s_points, intersection_guesses, intersection_tolerance)
598
- st.plotly_chart(fig, use_container_width=True)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
599
 
600
- if len(intersections) > 0:
601
- st.subheader("Intersection Points")
602
- for i, s_val in enumerate(intersections):
603
- y_val = curve1(s_val, z, y_3)
604
- st.write(f"Point {i+1}: s = {s_val:.6f}, y = {y_val:.6f}")
605
- else:
606
- st.write("No intersections found in the given range.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  st.set_page_config(
9
  page_title="Cubic Root Analysis",
10
  layout="wide",
11
+ initial_sidebar_state="expanded" # Changed to expanded
12
  )
13
 
14
+ # Move custom expression inputs to sidebar
15
+ with st.sidebar:
16
+ st.header("Custom Expression Settings")
17
+ expression_type = st.radio(
18
+ "Select Expression Type",
19
+ ["Original Low y", "Alternative Low y"]
20
+ )
21
+
22
+ if expression_type == "Original Low y":
23
+ default_num = "(y - 2)*((-1 + sqrt(y*beta*(z_a - 1)))/z_a) + y*beta*((z_a-1)/z_a) - 1/z_a - 1"
24
+ default_denom = "((-1 + sqrt(y*beta*(z_a - 1)))/z_a)**2 + ((-1 + sqrt(y*beta*(z_a - 1)))/z_a)"
25
+ else:
26
+ default_num = "1*z_a*y*beta*(z_a-1) - 2*z_a*(1 - y) - 2*z_a**2"
27
+ default_denom = "2+2*z_a"
28
+
29
+ custom_num_expr = st.text_input("Numerator Expression", value=default_num)
30
+ custom_denom_expr = st.text_input("Denominator Expression", value=default_denom)
31
  #############################
32
  # 1) Define the discriminant
33
  #############################
 
90
  roots_found.append(root_approx)
91
 
92
  return np.array(roots_found)
 
93
  @st.cache_data
94
  def sweep_beta_and_find_z_bounds(z_a, y, z_min, z_max, beta_steps, z_steps):
95
  """
 
143
  numerator = -4*a*(a-1)*y*betas - 2*a*y - 2*a*(2*a-1)
144
  return numerator/denominator
145
 
146
+ @st.cache_data
147
+ def compute_z_difference_and_derivatives(z_a, y, z_min, z_max, beta_steps, z_steps):
148
+ """
149
+ Compute the difference between upper and lower z*(β) curves and their derivatives
150
+ """
151
+ betas, z_mins, z_maxs = sweep_beta_and_find_z_bounds(z_a, y, z_min, z_max, beta_steps, z_steps)
152
+
153
+ # Compute difference
154
+ z_difference = z_maxs - z_mins
155
+
156
+ # First derivatives
157
+ dz_diff_dbeta = np.gradient(z_difference, betas)
158
+
159
+ # Second derivatives
160
+ d2z_diff_dbeta2 = np.gradient(dz_diff_dbeta, betas)
161
+
162
+ return betas, z_difference, dz_diff_dbeta, d2z_diff_dbeta2
163
  def compute_custom_expression(betas, z_a, y, num_expr_str, denom_expr_str):
164
  """
165
  Compute a custom curve given numerator and denominator expressions
 
219
  line=dict(color='lightblue'),
220
  )
221
  )
222
+ fig.add_trace(
 
223
  go.Scatter(
224
  x=betas,
225
  y=low_y_curve,
 
263
  hovermode="x unified",
264
  )
265
 
266
+ # Compute Derivatives with Respect to β
 
267
  dzmax_dbeta = np.gradient(z_maxs, betas)
268
  dzmin_dbeta = np.gradient(z_mins, betas)
269
  dlowy_dbeta = np.gradient(low_y_curve, betas)
 
282
  line=dict(color='blue'),
283
  )
284
  )
285
+ fig_deriv.add_trace(
 
286
  go.Scatter(
287
  x=betas,
288
  y=dzmin_dbeta,
 
348
  coeffs = [a, b, c, d]
349
  roots = np.roots(coeffs)
350
  return roots
 
351
  def generate_root_plots(beta, y, z_a, z_min, z_max, n_points):
352
  """Generate both Im(s) and Re(s) vs. z plots"""
353
  if z_a <= 0 or y <= 0 or z_min >= z_max:
 
406
  )
407
 
408
  return fig_im, fig_re
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
409
  # ------------------- Streamlit UI -------------------
410
 
411
  st.title("Cubic Root Analysis")
412
 
413
+ tab1, tab2, tab3, tab4 = st.tabs(["z*(β) Curves", "Im{s} vs. z", "Curve Intersections", "z*(β) Difference Analysis"])
414
 
415
  with tab1:
416
  st.header("Find z Values where Cubic Roots Transition Between Real and Complex")
 
427
  beta_steps = st.slider("β steps", min_value=51, max_value=501, value=201, step=50)
428
  z_steps = st.slider("z grid steps", min_value=1000, max_value=100000, value=50000, step=1000)
429
 
 
 
 
 
 
 
 
430
  if st.button("Compute z vs. β Curves"):
431
  with col2:
432
  fig_main, fig_deriv = generate_z_vs_beta_plot(z_a_1, y_1, z_min_1, z_max_1,
 
434
  custom_num_expr, custom_denom_expr)
435
  if fig_main is not None and fig_deriv is not None:
436
  st.plotly_chart(fig_main, use_container_width=True)
 
437
  st.plotly_chart(fig_deriv, use_container_width=True)
438
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
439
  with tab2:
440
  st.header("Plot Complex Roots vs. z")
441
 
 
458
  st.plotly_chart(fig_im, use_container_width=True)
459
  st.plotly_chart(fig_re, use_container_width=True)
460
 
461
+ with tab4:
462
+ st.header("z*(β) Difference Analysis")
463
 
464
  col1, col2 = st.columns([1, 2])
465
 
466
  with col1:
467
+ z_a_4 = st.number_input("z_a", value=1.0, key="z_a_4")
468
+ y_4 = st.number_input("y", value=1.0, key="y_4")
469
+ z_min_4 = st.number_input("z_min", value=-10.0, key="z_min_4")
470
+ z_max_4 = st.number_input("z_max", value=10.0, key="z_max_4")
 
 
 
 
471
 
472
  with st.expander("Resolution Settings"):
473
+ beta_steps_4 = st.slider("β steps", min_value=51, max_value=501, value=201, step=50, key="beta_steps_4")
474
+ z_steps_4 = st.slider("z grid steps", min_value=1000, max_value=100000, value=50000, step=1000, key="z_steps_4")
475
+
476
+ if st.button("Compute Difference Analysis"):
 
 
 
 
 
477
  with col2:
478
+ betas, z_diff, dz_diff, d2z_diff = compute_z_difference_and_derivatives(
479
+ z_a_4, y_4, z_min_4, z_max_4, beta_steps_4, z_steps_4
480
+ )
481
+
482
+ # Plot difference
483
+ fig_diff = go.Figure()
484
+ fig_diff.add_trace(
485
+ go.Scatter(
486
+ x=betas,
487
+ y=z_diff,
488
+ mode="lines",
489
+ name="z*(β) Difference",
490
+ line=dict(color='purple', width=2)
491
+ )
492
+ )
493
+ fig_diff.update_layout(
494
+ title="Difference between Upper and Lower z*(β)",
495
+ xaxis_title="β",
496
+ yaxis_title="z_max - z_min",
497
+ hovermode="x unified"
498
+ )
499
+ st.plotly_chart(fig_diff, use_container_width=True)
500
 
501
+ # Plot first derivative
502
+ fig_first_deriv = go.Figure()
503
+ fig_first_deriv.add_trace(
504
+ go.Scatter(
505
+ x=betas,
506
+ y=dz_diff,
507
+ mode="lines",
508
+ name="First Derivative",
509
+ line=dict(color='blue', width=2)
510
+ )
511
+ )
512
+ fig_first_deriv.update_layout(
513
+ title="First Derivative of z*(β) Difference",
514
+ xaxis_title="β",
515
+ yaxis_title="d(z_max - z_min)/dβ",
516
+ hovermode="x unified"
517
+ )
518
+ st.plotly_chart(fig_first_deriv, use_container_width=True)
519
+
520
+ # Plot second derivative
521
+ fig_second_deriv = go.Figure()
522
+ fig_second_deriv.add_trace(
523
+ go.Scatter(
524
+ x=betas,
525
+ y=d2z_diff,
526
+ mode="lines",
527
+ name="Second Derivative",
528
+ line=dict(color='green', width=2)
529
+ )
530
+ )
531
+ fig_second_deriv.update_layout(
532
+ title="Second Derivative of z*(β) Difference",
533
+ xaxis_title="β",
534
+ yaxis_title="d²(z_max - z_min)/dβ²",
535
+ hovermode="x unified"
536
+ )
537
+ st.plotly_chart(fig_second_deriv, use_container_width=True)