euler314 commited on
Commit
8e6bfb8
·
verified ·
1 Parent(s): 891dadc

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +878 -106
app.py CHANGED
@@ -67,6 +67,19 @@ st.markdown("""
67
  padding: 10px;
68
  margin: 10px 0;
69
  }
 
 
 
 
 
 
 
 
 
 
 
 
 
70
  .stWarning {
71
  background-color: #fff3cd;
72
  padding: 10px;
@@ -79,6 +92,14 @@ st.markdown("""
79
  border-left: 3px solid #28a745;
80
  margin: 10px 0;
81
  }
 
 
 
 
 
 
 
 
82
  </style>
83
  """, unsafe_allow_html=True)
84
 
@@ -138,10 +159,668 @@ def run_command(cmd, show_output=True, timeout=None):
138
 
139
  # Check if C++ source file exists
140
  if not os.path.exists(cpp_file):
 
141
  with open(cpp_file, "w") as f:
142
- st.warning(f"C++ source file not found at: {cpp_file}")
143
- st.info("Creating an empty file. Please paste the C++ code into this file and recompile.")
144
- f.write("// Paste the C++ code here and recompile\n")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
145
 
146
  # Compile the C++ code with the right OpenCV libraries
147
  st.sidebar.title("Compiler Settings")
@@ -207,6 +886,7 @@ with tab1:
207
  st.markdown('<div class="panel-header">Eigenvalue Analysis Controls</div>', unsafe_allow_html=True)
208
 
209
  # Parameter inputs with defaults and validation
 
210
  st.markdown("### Matrix Parameters")
211
  n = st.number_input("Sample size (n)", min_value=5, max_value=1000, value=100, step=5,
212
  help="Number of samples", key="eig_n")
@@ -218,7 +898,9 @@ with tab1:
218
  # Automatically calculate y = p/n (as requested)
219
  y = p/n
220
  st.info(f"Value for y = p/n: {y:.4f}")
 
221
 
 
222
  st.markdown("### Calculation Controls")
223
  fineness = st.slider(
224
  "Beta points",
@@ -229,6 +911,7 @@ with tab1:
229
  help="Number of points to calculate along the β axis (0 to 1)",
230
  key="eig_fineness"
231
  )
 
232
 
233
  with st.expander("Advanced Settings"):
234
  # Add controls for theoretical calculation precision
@@ -691,6 +1374,7 @@ with tab2:
691
  st.markdown('<div class="panel-header">Im(s) vs z Analysis Controls</div>', unsafe_allow_html=True)
692
 
693
  # Parameter inputs with defaults and validation
 
694
  st.markdown("### Cubic Equation Parameters")
695
  cubic_a = st.number_input("Value for a", min_value=1.1, max_value=10.0, value=2.0, step=0.1,
696
  help="Parameter a > 1", key="cubic_a")
@@ -698,7 +1382,9 @@ with tab2:
698
  help="Parameter y > 0", key="cubic_y")
699
  cubic_beta = st.number_input("Value for β", min_value=0.0, max_value=1.0, value=0.5, step=0.05,
700
  help="Value between 0 and 1", key="cubic_beta")
 
701
 
 
702
  st.markdown("### Calculation Controls")
703
  cubic_points = st.slider(
704
  "Number of z points",
@@ -722,6 +1408,7 @@ with tab2:
722
  help="Maximum time allowed for computation before timeout",
723
  key="cubic_timeout"
724
  )
 
725
 
726
  # Show cubic equation
727
  st.markdown('<div class="math-box">', unsafe_allow_html=True)
@@ -802,120 +1489,195 @@ with tab2:
802
  ims_values2 = np.array(data['ims_values2'])
803
  ims_values3 = np.array(data['ims_values3'])
804
 
805
- # Create an interactive plot using Plotly
806
- fig = go.Figure()
 
 
807
 
808
- # Add traces for each root's imaginary part
809
- fig.add_trace(go.Scatter(
810
- x=z_values,
811
- y=ims_values1,
812
- mode='lines',
813
- name='Im(s₁)',
814
- line=dict(color='rgb(220, 60, 60)', width=3),
815
- hovertemplate='z: %{x:.3f}<br>Im(s₁): %{y:.6f}<extra>Root 1</extra>'
816
- ))
817
 
818
- fig.add_trace(go.Scatter(
819
- x=z_values,
820
- y=ims_values2,
821
- mode='lines',
822
- name='Im(s₂)',
823
- line=dict(color='rgb(60, 60, 220)', width=3),
824
- hovertemplate='z: %{x:.3f}<br>Im(s₂): %{y:.6f}<extra>Root 2</extra>'
825
- ))
826
-
827
- fig.add_trace(go.Scatter(
828
- x=z_values,
829
- y=ims_values3,
830
- mode='lines',
831
- name='Im(s₃)',
832
- line=dict(color='rgb(30, 180, 30)', width=3),
833
- hovertemplate='z: %{x:.3f}<br>Im(s₃): %{y:.6f}<extra>Root 3</extra>'
834
- ))
835
-
836
- # Configure layout for better appearance
837
- fig.update_layout(
838
- title={
839
- 'text': f'Im(s) vs z Analysis: a={cubic_a}, y={cubic_y}, β={cubic_beta}',
840
- 'font': {'size': 24, 'color': '#1E88E5'},
841
- 'y': 0.95,
842
- 'x': 0.5,
843
- 'xanchor': 'center',
844
- 'yanchor': 'top'
845
- },
846
- xaxis={
847
- 'title': {'text': 'z (logarithmic scale)', 'font': {'size': 18, 'color': '#424242'}},
848
- 'tickfont': {'size': 14},
849
- 'gridcolor': 'rgba(220, 220, 220, 0.5)',
850
- 'showgrid': True,
851
- 'type': 'log' # Use logarithmic scale for better visualization
852
- },
853
- yaxis={
854
- 'title': {'text': 'Im(s)', 'font': {'size': 18, 'color': '#424242'}},
855
- 'tickfont': {'size': 14},
856
- 'gridcolor': 'rgba(220, 220, 220, 0.5)',
857
- 'showgrid': True
858
- },
859
- plot_bgcolor='rgba(240, 240, 240, 0.8)',
860
- paper_bgcolor='rgba(249, 249, 249, 0.8)',
861
- hovermode='closest',
862
- legend={
863
- 'font': {'size': 14},
864
- 'bgcolor': 'rgba(255, 255, 255, 0.9)',
865
- 'bordercolor': 'rgba(200, 200, 200, 0.5)',
866
- 'borderwidth': 1
867
- },
868
- margin={'l': 60, 'r': 30, 't': 100, 'b': 60},
869
- height=600,
870
- annotations=[
871
- {
872
- 'text': f"Cubic Equation: {cubic_a}zs³ + [{cubic_a+1}z+{cubic_a}(1-{cubic_y})]s² + [z+{cubic_a+1}-{cubic_y}-{cubic_y*cubic_beta}({cubic_a-1})]s + 1 = 0",
873
- 'xref': 'paper', 'yref': 'paper',
874
- 'x': 0.5, 'y': 0.02,
875
- 'showarrow': False,
876
- 'font': {'size': 12, 'color': 'black'},
 
 
877
  'bgcolor': 'rgba(255, 255, 255, 0.9)',
878
- 'bordercolor': 'rgba(0, 0, 0, 0.5)',
879
- 'borderwidth': 1,
880
- 'borderpad': 4,
881
- 'align': 'center'
882
- }
883
- ]
884
- )
885
-
886
- # Add custom modebar buttons
887
- fig.update_layout(
888
- modebar_add=[
889
- 'drawline', 'drawopenpath', 'drawclosedpath',
890
- 'drawcircle', 'drawrect', 'eraseshape'
891
- ],
892
- modebar_remove=['lasso2d', 'select2d'],
893
- dragmode='zoom'
894
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
895
 
896
  # Clear progress container
897
  progress_container.empty()
898
 
899
- # Display the interactive plot in Streamlit
900
- st.plotly_chart(fig, use_container_width=True)
901
-
902
  # Add explanation text
 
903
  st.markdown("""
904
- ### Explanation of the Analysis
905
 
906
- This plot shows the imaginary parts of the three roots (s₁, s₂, s₃) of the cubic equation as a function of z.
907
- The cubic equation being solved is:
908
 
909
- ```
910
- zas³ + [z(a+1)+a(1-y)]s² + [z+(a+1)-y-yβ(a-1)]s + 1 = 0
911
- ```
912
 
913
- Where a, y, and β are parameters you can adjust in the control panel. The imaginary parts of the roots represent
914
- oscillatory behavior in the system.
 
 
915
 
916
- - When Im(s) = 0, the root is purely real
917
- - When Im(s) 0, the root has an oscillatory component
918
  """)
 
919
 
920
  except json.JSONDecodeError as e:
921
  st.error(f"Error parsing JSON results: {str(e)}")
@@ -943,7 +1705,12 @@ with tab2:
943
  ims_values2 = np.array(data['ims_values2'])
944
  ims_values3 = np.array(data['ims_values3'])
945
 
946
- # Create an interactive plot using Plotly
 
 
 
 
 
947
  fig = go.Figure()
948
 
949
  # Add traces for each root's imaginary part
@@ -1031,7 +1798,12 @@ st.markdown("""
1031
  2. **Adjust parameters** in the left panel to configure your analysis
1032
  3. **Click the Generate button** to run the analysis with the selected parameters
1033
  4. **Explore the results** in the interactive plot
1034
- 5. For advanced users, you can enable **Debug Mode** to see detailed output
1035
 
1036
  If you encounter any issues with compilation, try clicking the "Recompile C++ Code" button in the sidebar.
1037
- """)
 
 
 
 
 
 
67
  padding: 10px;
68
  margin: 10px 0;
69
  }
70
+ .explanation-box {
71
+ background-color: #e8f4f8;
72
+ padding: 15px;
73
+ border-radius: 5px;
74
+ margin-top: 20px;
75
+ border-left: 3px solid #1E88E5;
76
+ }
77
+ .parameter-container {
78
+ background-color: #f0f7fa;
79
+ padding: 15px;
80
+ border-radius: 5px;
81
+ margin-bottom: 15px;
82
+ }
83
  .stWarning {
84
  background-color: #fff3cd;
85
  padding: 10px;
 
92
  border-left: 3px solid #28a745;
93
  margin: 10px 0;
94
  }
95
+ .plot-container {
96
+ margin-top: 1.5rem;
97
+ }
98
+ .footnote {
99
+ font-size: 0.8rem;
100
+ color: #6c757d;
101
+ margin-top: 2rem;
102
+ }
103
  </style>
104
  """, unsafe_allow_html=True)
105
 
 
159
 
160
  # Check if C++ source file exists
161
  if not os.path.exists(cpp_file):
162
+ # Create the C++ file with our improved cubic solver
163
  with open(cpp_file, "w") as f:
164
+ st.warning(f"Creating new C++ source file at: {cpp_file}")
165
+
166
+ # The improved C++ code with better cubic solver
167
+ f.write('''
168
+ // app.cpp - Modified version for command line arguments with improved cubic solver
169
+ #include <opencv2/opencv.hpp>
170
+ #include <algorithm>
171
+ #include <cmath>
172
+ #include <iostream>
173
+ #include <iomanip>
174
+ #include <numeric>
175
+ #include <random>
176
+ #include <vector>
177
+ #include <limits>
178
+ #include <sstream>
179
+ #include <string>
180
+ #include <fstream>
181
+ #include <complex>
182
+ #include <stdexcept>
183
+
184
+ // Struct to hold cubic equation roots
185
+ struct CubicRoots {
186
+ std::complex<double> root1;
187
+ std::complex<double> root2;
188
+ std::complex<double> root3;
189
+ };
190
+
191
+ // Function to solve cubic equation: az^3 + bz^2 + cz + d = 0
192
+ // Improved to properly handle zero roots and classification of positive/negative
193
+ CubicRoots solveCubic(double a, double b, double c, double d) {
194
+ // Constants for numerical stability
195
+ const double epsilon = 1e-14;
196
+ const double zero_threshold = 1e-10; // Threshold for considering a value as zero
197
+
198
+ // Handle special case for a == 0 (quadratic)
199
+ if (std::abs(a) < epsilon) {
200
+ CubicRoots roots;
201
+ // For a quadratic equation: bz^2 + cz + d = 0
202
+ if (std::abs(b) < epsilon) { // Linear equation or constant
203
+ if (std::abs(c) < epsilon) { // Constant - no finite roots
204
+ roots.root1 = std::complex<double>(std::numeric_limits<double>::quiet_NaN(), 0.0);
205
+ roots.root2 = std::complex<double>(std::numeric_limits<double>::quiet_NaN(), 0.0);
206
+ roots.root3 = std::complex<double>(std::numeric_limits<double>::quiet_NaN(), 0.0);
207
+ } else { // Linear equation
208
+ roots.root1 = std::complex<double>(-d / c, 0.0);
209
+ roots.root2 = std::complex<double>(std::numeric_limits<double>::infinity(), 0.0);
210
+ roots.root3 = std::complex<double>(std::numeric_limits<double>::infinity(), 0.0);
211
+ }
212
+ return roots;
213
+ }
214
+
215
+ double discriminant = c * c - 4.0 * b * d;
216
+ if (discriminant >= 0) {
217
+ double sqrtDiscriminant = std::sqrt(discriminant);
218
+ roots.root1 = std::complex<double>((-c + sqrtDiscriminant) / (2.0 * b), 0.0);
219
+ roots.root2 = std::complex<double>((-c - sqrtDiscriminant) / (2.0 * b), 0.0);
220
+ roots.root3 = std::complex<double>(std::numeric_limits<double>::infinity(), 0.0);
221
+ } else {
222
+ double real = -c / (2.0 * b);
223
+ double imag = std::sqrt(-discriminant) / (2.0 * b);
224
+ roots.root1 = std::complex<double>(real, imag);
225
+ roots.root2 = std::complex<double>(real, -imag);
226
+ roots.root3 = std::complex<double>(std::numeric_limits<double>::infinity(), 0.0);
227
+ }
228
+ return roots;
229
+ }
230
+
231
+ // Handle special case when d is zero - one root is zero
232
+ if (std::abs(d) < epsilon) {
233
+ // Factor out z: z(az^2 + bz + c) = 0
234
+ CubicRoots roots;
235
+ roots.root1 = std::complex<double>(0.0, 0.0); // One root is exactly zero
236
+
237
+ // Solve the quadratic: az^2 + bz + c = 0
238
+ double discriminant = b * b - 4.0 * a * c;
239
+ if (discriminant >= 0) {
240
+ double sqrtDiscriminant = std::sqrt(discriminant);
241
+ roots.root2 = std::complex<double>((-b + sqrtDiscriminant) / (2.0 * a), 0.0);
242
+ roots.root3 = std::complex<double>((-b - sqrtDiscriminant) / (2.0 * a), 0.0);
243
+ } else {
244
+ double real = -b / (2.0 * a);
245
+ double imag = std::sqrt(-discriminant) / (2.0 * a);
246
+ roots.root2 = std::complex<double>(real, imag);
247
+ roots.root3 = std::complex<double>(real, -imag);
248
+ }
249
+ return roots;
250
+ }
251
+
252
+ // Normalize equation: z^3 + (b/a)z^2 + (c/a)z + (d/a) = 0
253
+ double p = b / a;
254
+ double q = c / a;
255
+ double r = d / a;
256
+
257
+ // Substitute z = t - p/3 to get t^3 + pt^2 + qt + r = 0
258
+ double p1 = q - p * p / 3.0;
259
+ double q1 = r - p * q / 3.0 + 2.0 * p * p * p / 27.0;
260
+
261
+ // Calculate discriminant
262
+ double D = q1 * q1 / 4.0 + p1 * p1 * p1 / 27.0;
263
+
264
+ // Precompute values
265
+ const double two_pi = 2.0 * M_PI;
266
+ const double third = 1.0 / 3.0;
267
+ const double p_over_3 = p / 3.0;
268
+
269
+ CubicRoots roots;
270
+
271
+ // Handle the special case where the discriminant is close to zero (all real roots, at least two equal)
272
+ if (std::abs(D) < zero_threshold) {
273
+ // Special case where all roots are zero
274
+ if (std::abs(p1) < zero_threshold && std::abs(q1) < zero_threshold) {
275
+ roots.root1 = std::complex<double>(-p_over_3, 0.0);
276
+ roots.root2 = std::complex<double>(-p_over_3, 0.0);
277
+ roots.root3 = std::complex<double>(-p_over_3, 0.0);
278
+ return roots;
279
+ }
280
+
281
+ // General case for D ≈ 0
282
+ double u = std::cbrt(-q1 / 2.0); // Real cube root
283
+
284
+ roots.root1 = std::complex<double>(2.0 * u - p_over_3, 0.0);
285
+ roots.root2 = std::complex<double>(-u - p_over_3, 0.0);
286
+ roots.root3 = roots.root2; // Duplicate root
287
+
288
+ // Check if any roots are close to zero and set them to exactly zero
289
+ if (std::abs(roots.root1.real()) < zero_threshold)
290
+ roots.root1 = std::complex<double>(0.0, 0.0);
291
+ if (std::abs(roots.root2.real()) < zero_threshold) {
292
+ roots.root2 = std::complex<double>(0.0, 0.0);
293
+ roots.root3 = std::complex<double>(0.0, 0.0);
294
+ }
295
+
296
+ return roots;
297
+ }
298
+
299
+ if (D > 0) { // One real root and two complex conjugate roots
300
+ double sqrtD = std::sqrt(D);
301
+ double u = std::cbrt(-q1 / 2.0 + sqrtD);
302
+ double v = std::cbrt(-q1 / 2.0 - sqrtD);
303
+
304
+ // Real root
305
+ roots.root1 = std::complex<double>(u + v - p_over_3, 0.0);
306
+
307
+ // Complex conjugate roots
308
+ double real_part = -(u + v) / 2.0 - p_over_3;
309
+ double imag_part = (u - v) * std::sqrt(3.0) / 2.0;
310
+ roots.root2 = std::complex<double>(real_part, imag_part);
311
+ roots.root3 = std::complex<double>(real_part, -imag_part);
312
+
313
+ // Check if any roots are close to zero and set them to exactly zero
314
+ if (std::abs(roots.root1.real()) < zero_threshold)
315
+ roots.root1 = std::complex<double>(0.0, 0.0);
316
+
317
+ return roots;
318
+ }
319
+ else { // Three distinct real roots
320
+ double angle = std::acos(-q1 / 2.0 * std::sqrt(-27.0 / (p1 * p1 * p1)));
321
+ double magnitude = 2.0 * std::sqrt(-p1 / 3.0);
322
+
323
+ // Calculate all three real roots
324
+ roots.root1 = std::complex<double>(magnitude * std::cos(angle / 3.0) - p_over_3, 0.0);
325
+ roots.root2 = std::complex<double>(magnitude * std::cos((angle + two_pi) / 3.0) - p_over_3, 0.0);
326
+ roots.root3 = std::complex<double>(magnitude * std::cos((angle + 2.0 * two_pi) / 3.0) - p_over_3, 0.0);
327
+
328
+ // Check if any roots are close to zero and set them to exactly zero
329
+ if (std::abs(roots.root1.real()) < zero_threshold)
330
+ roots.root1 = std::complex<double>(0.0, 0.0);
331
+ if (std::abs(roots.root2.real()) < zero_threshold)
332
+ roots.root2 = std::complex<double>(0.0, 0.0);
333
+ if (std::abs(roots.root3.real()) < zero_threshold)
334
+ roots.root3 = std::complex<double>(0.0, 0.0);
335
+
336
+ return roots;
337
+ }
338
+ }
339
+
340
+ // Function to compute the cubic equation for Im(s) vs z
341
+ std::vector<std::vector<double>> computeImSVsZ(double a, double y, double beta, int num_points) {
342
+ std::vector<double> z_values(num_points);
343
+ std::vector<double> ims_values1(num_points);
344
+ std::vector<double> ims_values2(num_points);
345
+ std::vector<double> ims_values3(num_points);
346
+ std::vector<double> real_values1(num_points);
347
+ std::vector<double> real_values2(num_points);
348
+ std::vector<double> real_values3(num_points);
349
+
350
+ // Generate z values from 0.01 to 10 (or adjust range as needed)
351
+ double z_start = 0.01; // Avoid z=0 to prevent potential division issues
352
+ double z_end = 10.0;
353
+ double z_step = (z_end - z_start) / (num_points - 1);
354
+
355
+ for (int i = 0; i < num_points; ++i) {
356
+ double z = z_start + i * z_step;
357
+ z_values[i] = z;
358
+
359
+ // Coefficients for the cubic equation:
360
+ // zas³ + [z(a+1)+a(1-y)]s² + [z+(a+1)-y-yβ(a-1)]s + 1 = 0
361
+ double coef_a = z * a;
362
+ double coef_b = z * (a + 1) + a * (1 - y);
363
+ double coef_c = z + (a + 1) - y - y * beta * (a - 1);
364
+ double coef_d = 1.0;
365
+
366
+ // Solve the cubic equation
367
+ CubicRoots roots = solveCubic(coef_a, coef_b, coef_c, coef_d);
368
+
369
+ // Extract imaginary and real parts
370
+ ims_values1[i] = std::abs(roots.root1.imag());
371
+ ims_values2[i] = std::abs(roots.root2.imag());
372
+ ims_values3[i] = std::abs(roots.root3.imag());
373
+
374
+ real_values1[i] = roots.root1.real();
375
+ real_values2[i] = roots.root2.real();
376
+ real_values3[i] = roots.root3.real();
377
+ }
378
+
379
+ // Create output vector, now including real values for better analysis
380
+ std::vector<std::vector<double>> result = {
381
+ z_values, ims_values1, ims_values2, ims_values3,
382
+ real_values1, real_values2, real_values3
383
+ };
384
+
385
+ return result;
386
+ }
387
+
388
+ // Function to save Im(s) vs z data as JSON
389
+ bool saveImSDataAsJSON(const std::string& filename,
390
+ const std::vector<std::vector<double>>& data) {
391
+ std::ofstream outfile(filename);
392
+
393
+ if (!outfile.is_open()) {
394
+ std::cerr << "Error: Could not open file " << filename << " for writing." << std::endl;
395
+ return false;
396
+ }
397
+
398
+ // Start JSON object
399
+ outfile << "{\n";
400
+
401
+ // Write z values
402
+ outfile << " \"z_values\": [";
403
+ for (size_t i = 0; i < data[0].size(); ++i) {
404
+ outfile << data[0][i];
405
+ if (i < data[0].size() - 1) outfile << ", ";
406
+ }
407
+ outfile << "],\n";
408
+
409
+ // Write Im(s) values for first root
410
+ outfile << " \"ims_values1\": [";
411
+ for (size_t i = 0; i < data[1].size(); ++i) {
412
+ outfile << data[1][i];
413
+ if (i < data[1].size() - 1) outfile << ", ";
414
+ }
415
+ outfile << "],\n";
416
+
417
+ // Write Im(s) values for second root
418
+ outfile << " \"ims_values2\": [";
419
+ for (size_t i = 0; i < data[2].size(); ++i) {
420
+ outfile << data[2][i];
421
+ if (i < data[2].size() - 1) outfile << ", ";
422
+ }
423
+ outfile << "],\n";
424
+
425
+ // Write Im(s) values for third root
426
+ outfile << " \"ims_values3\": [";
427
+ for (size_t i = 0; i < data[3].size(); ++i) {
428
+ outfile << data[3][i];
429
+ if (i < data[3].size() - 1) outfile << ", ";
430
+ }
431
+ outfile << "],\n";
432
+
433
+ // Write Real(s) values for first root
434
+ outfile << " \"real_values1\": [";
435
+ for (size_t i = 0; i < data[4].size(); ++i) {
436
+ outfile << data[4][i];
437
+ if (i < data[4].size() - 1) outfile << ", ";
438
+ }
439
+ outfile << "],\n";
440
+
441
+ // Write Real(s) values for second root
442
+ outfile << " \"real_values2\": [";
443
+ for (size_t i = 0; i < data[5].size(); ++i) {
444
+ outfile << data[5][i];
445
+ if (i < data[5].size() - 1) outfile << ", ";
446
+ }
447
+ outfile << "],\n";
448
+
449
+ // Write Real(s) values for third root
450
+ outfile << " \"real_values3\": [";
451
+ for (size_t i = 0; i < data[6].size(); ++i) {
452
+ outfile << data[6][i];
453
+ if (i < data[6].size() - 1) outfile << ", ";
454
+ }
455
+ outfile << "]\n";
456
+
457
+ // Close JSON object
458
+ outfile << "}\n";
459
+
460
+ outfile.close();
461
+ return true;
462
+ }
463
+
464
+ // Function to compute the theoretical max value
465
+ double compute_theoretical_max(double a, double y, double beta, int grid_points, double tolerance) {
466
+ auto f = [a, y, beta](double k) -> double {
467
+ return (y * beta * (a - 1) * k + (a * k + 1) * ((y - 1) * k - 1)) /
468
+ ((a * k + 1) * (k * k + k));
469
+ };
470
+
471
+ // Use numerical optimization to find the maximum
472
+ // Grid search followed by golden section search
473
+ double best_k = 1.0;
474
+ double best_val = f(best_k);
475
+
476
+ // Initial grid search over a wide range
477
+ const int num_grid_points = grid_points;
478
+ for (int i = 0; i < num_grid_points; ++i) {
479
+ double k = 0.01 + 100.0 * i / (num_grid_points - 1); // From 0.01 to 100
480
+ double val = f(k);
481
+ if (val > best_val) {
482
+ best_val = val;
483
+ best_k = k;
484
+ }
485
+ }
486
+
487
+ // Refine with golden section search
488
+ double a_gs = std::max(0.01, best_k / 10.0);
489
+ double b_gs = best_k * 10.0;
490
+ const double golden_ratio = (1.0 + std::sqrt(5.0)) / 2.0;
491
+
492
+ double c_gs = b_gs - (b_gs - a_gs) / golden_ratio;
493
+ double d_gs = a_gs + (b_gs - a_gs) / golden_ratio;
494
+
495
+ while (std::abs(b_gs - a_gs) > tolerance) {
496
+ if (f(c_gs) > f(d_gs)) {
497
+ b_gs = d_gs;
498
+ d_gs = c_gs;
499
+ c_gs = b_gs - (b_gs - a_gs) / golden_ratio;
500
+ } else {
501
+ a_gs = c_gs;
502
+ c_gs = d_gs;
503
+ d_gs = a_gs + (b_gs - a_gs) / golden_ratio;
504
+ }
505
+ }
506
+
507
+ // Return the value without multiplying by y (as per correction)
508
+ return f((a_gs + b_gs) / 2.0);
509
+ }
510
+
511
+ // Function to compute the theoretical min value
512
+ double compute_theoretical_min(double a, double y, double beta, int grid_points, double tolerance) {
513
+ auto f = [a, y, beta](double t) -> double {
514
+ return (y * beta * (a - 1) * t + (a * t + 1) * ((y - 1) * t - 1)) /
515
+ ((a * t + 1) * (t * t + t));
516
+ };
517
+
518
+ // Use numerical optimization to find the minimum
519
+ // Grid search followed by golden section search
520
+ double best_t = -0.5 / a; // Midpoint of (-1/a, 0)
521
+ double best_val = f(best_t);
522
+
523
+ // Initial grid search over the range (-1/a, 0)
524
+ const int num_grid_points = grid_points;
525
+ for (int i = 1; i < num_grid_points; ++i) {
526
+ // From slightly above -1/a to slightly below 0
527
+ double t = -0.999/a + 0.998/a * i / (num_grid_points - 1);
528
+ if (t >= 0 || t <= -1.0/a) continue; // Ensure t is in range (-1/a, 0)
529
+
530
+ double val = f(t);
531
+ if (val < best_val) {
532
+ best_val = val;
533
+ best_t = t;
534
+ }
535
+ }
536
+
537
+ // Refine with golden section search
538
+ double a_gs = -0.999/a; // Slightly above -1/a
539
+ double b_gs = -0.001/a; // Slightly below 0
540
+ const double golden_ratio = (1.0 + std::sqrt(5.0)) / 2.0;
541
+
542
+ double c_gs = b_gs - (b_gs - a_gs) / golden_ratio;
543
+ double d_gs = a_gs + (b_gs - a_gs) / golden_ratio;
544
+
545
+ while (std::abs(b_gs - a_gs) > tolerance) {
546
+ if (f(c_gs) < f(d_gs)) {
547
+ b_gs = d_gs;
548
+ d_gs = c_gs;
549
+ c_gs = b_gs - (b_gs - a_gs) / golden_ratio;
550
+ } else {
551
+ a_gs = c_gs;
552
+ c_gs = d_gs;
553
+ d_gs = a_gs + (b_gs - a_gs) / golden_ratio;
554
+ }
555
+ }
556
+
557
+ // Return the value without multiplying by y (as per correction)
558
+ return f((a_gs + b_gs) / 2.0);
559
+ }
560
+
561
+ // Function to save data as JSON
562
+ bool save_as_json(const std::string& filename,
563
+ const std::vector<double>& beta_values,
564
+ const std::vector<double>& max_eigenvalues,
565
+ const std::vector<double>& min_eigenvalues,
566
+ const std::vector<double>& theoretical_max_values,
567
+ const std::vector<double>& theoretical_min_values) {
568
+
569
+ std::ofstream outfile(filename);
570
+
571
+ if (!outfile.is_open()) {
572
+ std::cerr << "Error: Could not open file " << filename << " for writing." << std::endl;
573
+ return false;
574
+ }
575
+
576
+ // Start JSON object
577
+ outfile << "{\n";
578
+
579
+ // Write beta values
580
+ outfile << " \"beta_values\": [";
581
+ for (size_t i = 0; i < beta_values.size(); ++i) {
582
+ outfile << beta_values[i];
583
+ if (i < beta_values.size() - 1) outfile << ", ";
584
+ }
585
+ outfile << "],\n";
586
+
587
+ // Write max eigenvalues
588
+ outfile << " \"max_eigenvalues\": [";
589
+ for (size_t i = 0; i < max_eigenvalues.size(); ++i) {
590
+ outfile << max_eigenvalues[i];
591
+ if (i < max_eigenvalues.size() - 1) outfile << ", ";
592
+ }
593
+ outfile << "],\n";
594
+
595
+ // Write min eigenvalues
596
+ outfile << " \"min_eigenvalues\": [";
597
+ for (size_t i = 0; i < min_eigenvalues.size(); ++i) {
598
+ outfile << min_eigenvalues[i];
599
+ if (i < min_eigenvalues.size() - 1) outfile << ", ";
600
+ }
601
+ outfile << "],\n";
602
+
603
+ // Write theoretical max values
604
+ outfile << " \"theoretical_max\": [";
605
+ for (size_t i = 0; i < theoretical_max_values.size(); ++i) {
606
+ outfile << theoretical_max_values[i];
607
+ if (i < theoretical_max_values.size() - 1) outfile << ", ";
608
+ }
609
+ outfile << "],\n";
610
+
611
+ // Write theoretical min values
612
+ outfile << " \"theoretical_min\": [";
613
+ for (size_t i = 0; i < theoretical_min_values.size(); ++i) {
614
+ outfile << theoretical_min_values[i];
615
+ if (i < theoretical_min_values.size() - 1) outfile << ", ";
616
+ }
617
+ outfile << "]\n";
618
+
619
+ // Close JSON object
620
+ outfile << "}\n";
621
+
622
+ outfile.close();
623
+ return true;
624
+ }
625
+
626
+ // Eigenvalue analysis function
627
+ bool eigenvalueAnalysis(int n, int p, double a, double y, int fineness,
628
+ int theory_grid_points, double theory_tolerance,
629
+ const std::string& output_file) {
630
+
631
+ std::cout << "Running eigenvalue analysis with parameters: n = " << n << ", p = " << p
632
+ << ", a = " << a << ", y = " << y << ", fineness = " << fineness
633
+ << ", theory_grid_points = " << theory_grid_points
634
+ << ", theory_tolerance = " << theory_tolerance << std::endl;
635
+ std::cout << "Output will be saved to: " << output_file << std::endl;
636
+
637
+ // ─── Beta range parameters ────────────────────────────────────────
638
+ const int num_beta_points = fineness; // Controlled by fineness parameter
639
+ std::vector<double> beta_values(num_beta_points);
640
+ for (int i = 0; i < num_beta_points; ++i) {
641
+ beta_values[i] = static_cast<double>(i) / (num_beta_points - 1);
642
+ }
643
+
644
+ // ─── Storage for results ────────────────────────────────────────
645
+ std::vector<double> max_eigenvalues(num_beta_points);
646
+ std::vector<double> min_eigenvalues(num_beta_points);
647
+ std::vector<double> theoretical_max_values(num_beta_points);
648
+ std::vector<double> theoretical_min_values(num_beta_points);
649
+
650
+ try {
651
+ // ─── Random‐Gaussian X and S_n ────────────────────────────────
652
+ std::random_device rd;
653
+ std::mt19937_64 rng{rd()};
654
+ std::normal_distribution<double> norm(0.0, 1.0);
655
+
656
+ cv::Mat X(p, n, CV_64F);
657
+ for(int i = 0; i < p; ++i)
658
+ for(int j = 0; j < n; ++j)
659
+ X.at<double>(i,j) = norm(rng);
660
+
661
+ // ─── Process each beta value ─────────────────────────────────
662
+ for (int beta_idx = 0; beta_idx < num_beta_points; ++beta_idx) {
663
+ double beta = beta_values[beta_idx];
664
+
665
+ // Compute theoretical values with customizable precision
666
+ theoretical_max_values[beta_idx] = compute_theoretical_max(a, y, beta, theory_grid_points, theory_tolerance);
667
+ theoretical_min_values[beta_idx] = compute_theoretical_min(a, y, beta, theory_grid_points, theory_tolerance);
668
+
669
+ // ─── Build T_n matrix ──────────────────────────────────
670
+ int k = static_cast<int>(std::floor(beta * p));
671
+ std::vector<double> diags(p, 1.0);
672
+ std::fill_n(diags.begin(), k, a);
673
+ std::shuffle(diags.begin(), diags.end(), rng);
674
+
675
+ cv::Mat T_n = cv::Mat::zeros(p, p, CV_64F);
676
+ for(int i = 0; i < p; ++i){
677
+ T_n.at<double>(i,i) = diags[i];
678
+ }
679
+
680
+ // ─── Form B_n = (1/n) * X * T_n * X^T ────────────
681
+ cv::Mat B = (X.t() * T_n * X) / static_cast<double>(n);
682
+
683
+ // ─── Compute eigenvalues of B ────────────────────────────
684
+ cv::Mat eigVals;
685
+ cv::eigen(B, eigVals);
686
+ std::vector<double> eigs(n);
687
+ for(int i = 0; i < n; ++i)
688
+ eigs[i] = eigVals.at<double>(i, 0);
689
+
690
+ max_eigenvalues[beta_idx] = *std::max_element(eigs.begin(), eigs.end());
691
+ min_eigenvalues[beta_idx] = *std::min_element(eigs.begin(), eigs.end());
692
+
693
+ // Progress indicator for Streamlit
694
+ double progress = static_cast<double>(beta_idx + 1) / num_beta_points;
695
+ std::cout << "PROGRESS:" << progress << std::endl;
696
+
697
+ // Less verbose output for Streamlit
698
+ if (beta_idx % 20 == 0 || beta_idx == num_beta_points - 1) {
699
+ std::cout << "Processing beta = " << beta
700
+ << " (" << beta_idx+1 << "/" << num_beta_points << ")" << std::endl;
701
+ }
702
+ }
703
+
704
+ // Save data as JSON for Python to read
705
+ if (!save_as_json(output_file, beta_values, max_eigenvalues, min_eigenvalues,
706
+ theoretical_max_values, theoretical_min_values)) {
707
+ return false;
708
+ }
709
+
710
+ std::cout << "Data saved to " << output_file << std::endl;
711
+ return true;
712
+ }
713
+ catch (const std::exception& e) {
714
+ std::cerr << "Error in eigenvalue analysis: " << e.what() << std::endl;
715
+ return false;
716
+ }
717
+ catch (...) {
718
+ std::cerr << "Unknown error in eigenvalue analysis" << std::endl;
719
+ return false;
720
+ }
721
+ }
722
+
723
+ // Cubic equation analysis function
724
+ bool cubicAnalysis(double a, double y, double beta, int num_points, const std::string& output_file) {
725
+ std::cout << "Running cubic equation analysis with parameters: a = " << a
726
+ << ", y = " << y << ", beta = " << beta << ", num_points = " << num_points << std::endl;
727
+ std::cout << "Output will be saved to: " << output_file << std::endl;
728
+
729
+ try {
730
+ // Compute Im(s) vs z data
731
+ std::vector<std::vector<double>> ims_data = computeImSVsZ(a, y, beta, num_points);
732
+
733
+ // Save to JSON
734
+ if (!saveImSDataAsJSON(output_file, ims_data)) {
735
+ return false;
736
+ }
737
+
738
+ std::cout << "Cubic equation data saved to " << output_file << std::endl;
739
+ return true;
740
+ }
741
+ catch (const std::exception& e) {
742
+ std::cerr << "Error in cubic analysis: " << e.what() << std::endl;
743
+ return false;
744
+ }
745
+ catch (...) {
746
+ std::cerr << "Unknown error in cubic analysis" << std::endl;
747
+ return false;
748
+ }
749
+ }
750
+
751
+ int main(int argc, char* argv[]) {
752
+ // Print received arguments for debugging
753
+ std::cout << "Received " << argc << " arguments:" << std::endl;
754
+ for (int i = 0; i < argc; ++i) {
755
+ std::cout << " argv[" << i << "]: " << argv[i] << std::endl;
756
+ }
757
+
758
+ // Check for mode argument
759
+ if (argc < 2) {
760
+ std::cerr << "Error: Missing mode argument." << std::endl;
761
+ std::cerr << "Usage: " << argv[0] << " eigenvalues <n> <p> <a> <y> <fineness> <theory_grid_points> <theory_tolerance> <output_file>" << std::endl;
762
+ std::cerr << " or: " << argv[0] << " cubic <a> <y> <beta> <num_points> <output_file>" << std::endl;
763
+ return 1;
764
+ }
765
+
766
+ std::string mode = argv[1];
767
+
768
+ try {
769
+ if (mode == "eigenvalues") {
770
+ // ─── Eigenvalue analysis mode ───────────────────────────────────────────
771
+ if (argc != 10) {
772
+ std::cerr << "Error: Incorrect number of arguments for eigenvalues mode." << std::endl;
773
+ std::cerr << "Usage: " << argv[0] << " eigenvalues <n> <p> <a> <y> <fineness> <theory_grid_points> <theory_tolerance> <output_file>" << std::endl;
774
+ std::cerr << "Received " << argc << " arguments, expected 10." << std::endl;
775
+ return 1;
776
+ }
777
+
778
+ int n = std::stoi(argv[2]);
779
+ int p = std::stoi(argv[3]);
780
+ double a = std::stod(argv[4]);
781
+ double y = std::stod(argv[5]);
782
+ int fineness = std::stoi(argv[6]);
783
+ int theory_grid_points = std::stoi(argv[7]);
784
+ double theory_tolerance = std::stod(argv[8]);
785
+ std::string output_file = argv[9];
786
+
787
+ if (!eigenvalueAnalysis(n, p, a, y, fineness, theory_grid_points, theory_tolerance, output_file)) {
788
+ return 1;
789
+ }
790
+
791
+ } else if (mode == "cubic") {
792
+ // ─── Cubic equation analysis mode ───────────────────────────────────────────
793
+ if (argc != 7) {
794
+ std::cerr << "Error: Incorrect number of arguments for cubic mode." << std::endl;
795
+ std::cerr << "Usage: " << argv[0] << " cubic <a> <y> <beta> <num_points> <output_file>" << std::endl;
796
+ std::cerr << "Received " << argc << " arguments, expected 7." << std::endl;
797
+ return 1;
798
+ }
799
+
800
+ double a = std::stod(argv[2]);
801
+ double y = std::stod(argv[3]);
802
+ double beta = std::stod(argv[4]);
803
+ int num_points = std::stoi(argv[5]);
804
+ std::string output_file = argv[6];
805
+
806
+ if (!cubicAnalysis(a, y, beta, num_points, output_file)) {
807
+ return 1;
808
+ }
809
+
810
+ } else {
811
+ std::cerr << "Error: Unknown mode: " << mode << std::endl;
812
+ std::cerr << "Use 'eigenvalues' or 'cubic'" << std::endl;
813
+ return 1;
814
+ }
815
+ }
816
+ catch (const std::exception& e) {
817
+ std::cerr << "Error: " << e.what() << std::endl;
818
+ return 1;
819
+ }
820
+
821
+ return 0;
822
+ }
823
+ ''')
824
 
825
  # Compile the C++ code with the right OpenCV libraries
826
  st.sidebar.title("Compiler Settings")
 
886
  st.markdown('<div class="panel-header">Eigenvalue Analysis Controls</div>', unsafe_allow_html=True)
887
 
888
  # Parameter inputs with defaults and validation
889
+ st.markdown('<div class="parameter-container">', unsafe_allow_html=True)
890
  st.markdown("### Matrix Parameters")
891
  n = st.number_input("Sample size (n)", min_value=5, max_value=1000, value=100, step=5,
892
  help="Number of samples", key="eig_n")
 
898
  # Automatically calculate y = p/n (as requested)
899
  y = p/n
900
  st.info(f"Value for y = p/n: {y:.4f}")
901
+ st.markdown('</div>', unsafe_allow_html=True)
902
 
903
+ st.markdown('<div class="parameter-container">', unsafe_allow_html=True)
904
  st.markdown("### Calculation Controls")
905
  fineness = st.slider(
906
  "Beta points",
 
911
  help="Number of points to calculate along the β axis (0 to 1)",
912
  key="eig_fineness"
913
  )
914
+ st.markdown('</div>', unsafe_allow_html=True)
915
 
916
  with st.expander("Advanced Settings"):
917
  # Add controls for theoretical calculation precision
 
1374
  st.markdown('<div class="panel-header">Im(s) vs z Analysis Controls</div>', unsafe_allow_html=True)
1375
 
1376
  # Parameter inputs with defaults and validation
1377
+ st.markdown('<div class="parameter-container">', unsafe_allow_html=True)
1378
  st.markdown("### Cubic Equation Parameters")
1379
  cubic_a = st.number_input("Value for a", min_value=1.1, max_value=10.0, value=2.0, step=0.1,
1380
  help="Parameter a > 1", key="cubic_a")
 
1382
  help="Parameter y > 0", key="cubic_y")
1383
  cubic_beta = st.number_input("Value for β", min_value=0.0, max_value=1.0, value=0.5, step=0.05,
1384
  help="Value between 0 and 1", key="cubic_beta")
1385
+ st.markdown('</div>', unsafe_allow_html=True)
1386
 
1387
+ st.markdown('<div class="parameter-container">', unsafe_allow_html=True)
1388
  st.markdown("### Calculation Controls")
1389
  cubic_points = st.slider(
1390
  "Number of z points",
 
1408
  help="Maximum time allowed for computation before timeout",
1409
  key="cubic_timeout"
1410
  )
1411
+ st.markdown('</div>', unsafe_allow_html=True)
1412
 
1413
  # Show cubic equation
1414
  st.markdown('<div class="math-box">', unsafe_allow_html=True)
 
1489
  ims_values2 = np.array(data['ims_values2'])
1490
  ims_values3 = np.array(data['ims_values3'])
1491
 
1492
+ # Also extract real parts if available
1493
+ real_values1 = np.array(data.get('real_values1', [0] * len(z_values)))
1494
+ real_values2 = np.array(data.get('real_values2', [0] * len(z_values)))
1495
+ real_values3 = np.array(data.get('real_values3', [0] * len(z_values)))
1496
 
1497
+ # Create tabs for imaginary and real parts
1498
+ im_tab, real_tab = st.tabs(["Imaginary Parts", "Real Parts"])
 
 
 
 
 
 
 
1499
 
1500
+ # Tab for imaginary parts
1501
+ with im_tab:
1502
+ # Create an interactive plot for imaginary parts
1503
+ im_fig = go.Figure()
1504
+
1505
+ # Add traces for each root's imaginary part
1506
+ im_fig.add_trace(go.Scatter(
1507
+ x=z_values,
1508
+ y=ims_values1,
1509
+ mode='lines',
1510
+ name='Im(s₁)',
1511
+ line=dict(color='rgb(220, 60, 60)', width=3),
1512
+ hovertemplate='z: %{x:.3f}<br>Im(s₁): %{y:.6f}<extra>Root 1</extra>'
1513
+ ))
1514
+
1515
+ im_fig.add_trace(go.Scatter(
1516
+ x=z_values,
1517
+ y=ims_values2,
1518
+ mode='lines',
1519
+ name='Im(s₂)',
1520
+ line=dict(color='rgb(60, 60, 220)', width=3),
1521
+ hovertemplate='z: %{x:.3f}<br>Im(s): %{y:.6f}<extra>Root 2</extra>'
1522
+ ))
1523
+
1524
+ im_fig.add_trace(go.Scatter(
1525
+ x=z_values,
1526
+ y=ims_values3,
1527
+ mode='lines',
1528
+ name='Im(s₃)',
1529
+ line=dict(color='rgb(30, 180, 30)', width=3),
1530
+ hovertemplate='z: %{x:.3f}<br>Im(s₃): %{y:.6f}<extra>Root 3</extra>'
1531
+ ))
1532
+
1533
+ # Configure layout for better appearance
1534
+ im_fig.update_layout(
1535
+ title={
1536
+ 'text': f'Im(s) vs z Analysis: a={cubic_a}, y={cubic_y}, β={cubic_beta}',
1537
+ 'font': {'size': 24, 'color': '#1E88E5'},
1538
+ 'y': 0.95,
1539
+ 'x': 0.5,
1540
+ 'xanchor': 'center',
1541
+ 'yanchor': 'top'
1542
+ },
1543
+ xaxis={
1544
+ 'title': {'text': 'z (logarithmic scale)', 'font': {'size': 18, 'color': '#424242'}},
1545
+ 'tickfont': {'size': 14},
1546
+ 'gridcolor': 'rgba(220, 220, 220, 0.5)',
1547
+ 'showgrid': True,
1548
+ 'type': 'log' # Use logarithmic scale for better visualization
1549
+ },
1550
+ yaxis={
1551
+ 'title': {'text': 'Im(s)', 'font': {'size': 18, 'color': '#424242'}},
1552
+ 'tickfont': {'size': 14},
1553
+ 'gridcolor': 'rgba(220, 220, 220, 0.5)',
1554
+ 'showgrid': True
1555
+ },
1556
+ plot_bgcolor='rgba(240, 240, 240, 0.8)',
1557
+ paper_bgcolor='rgba(249, 249, 249, 0.8)',
1558
+ hovermode='closest',
1559
+ legend={
1560
+ 'font': {'size': 14},
1561
  'bgcolor': 'rgba(255, 255, 255, 0.9)',
1562
+ 'bordercolor': 'rgba(200, 200, 200, 0.5)',
1563
+ 'borderwidth': 1
1564
+ },
1565
+ margin={'l': 60, 'r': 30, 't': 100, 'b': 60},
1566
+ height=500,
1567
+ annotations=[
1568
+ {
1569
+ 'text': f"Cubic Equation: {cubic_a}zs³ + [{cubic_a+1}z+{cubic_a}(1-{cubic_y})]s² + [z+{cubic_a+1}-{cubic_y}-{cubic_y*cubic_beta}({cubic_a-1})]s + 1 = 0",
1570
+ 'xref': 'paper', 'yref': 'paper',
1571
+ 'x': 0.5, 'y': 0.02,
1572
+ 'showarrow': False,
1573
+ 'font': {'size': 12, 'color': 'black'},
1574
+ 'bgcolor': 'rgba(255, 255, 255, 0.9)',
1575
+ 'bordercolor': 'rgba(0, 0, 0, 0.5)',
1576
+ 'borderwidth': 1,
1577
+ 'borderpad': 4,
1578
+ 'align': 'center'
1579
+ }
1580
+ ]
1581
+ )
1582
+
1583
+ # Display the interactive plot in Streamlit
1584
+ st.plotly_chart(im_fig, use_container_width=True)
1585
+
1586
+ # Tab for real parts
1587
+ with real_tab:
1588
+ # Create an interactive plot for real parts
1589
+ real_fig = go.Figure()
1590
+
1591
+ # Add traces for each root's real part
1592
+ real_fig.add_trace(go.Scatter(
1593
+ x=z_values,
1594
+ y=real_values1,
1595
+ mode='lines',
1596
+ name='Re(s₁)',
1597
+ line=dict(color='rgb(220, 60, 60)', width=3),
1598
+ hovertemplate='z: %{x:.3f}<br>Re(s₁): %{y:.6f}<extra>Root 1</extra>'
1599
+ ))
1600
+
1601
+ real_fig.add_trace(go.Scatter(
1602
+ x=z_values,
1603
+ y=real_values2,
1604
+ mode='lines',
1605
+ name='Re(s₂)',
1606
+ line=dict(color='rgb(60, 60, 220)', width=3),
1607
+ hovertemplate='z: %{x:.3f}<br>Re(s₂): %{y:.6f}<extra>Root 2</extra>'
1608
+ ))
1609
+
1610
+ real_fig.add_trace(go.Scatter(
1611
+ x=z_values,
1612
+ y=real_values3,
1613
+ mode='lines',
1614
+ name='Re(s₃)',
1615
+ line=dict(color='rgb(30, 180, 30)', width=3),
1616
+ hovertemplate='z: %{x:.3f}<br>Re(s₃): %{y:.6f}<extra>Root 3</extra>'
1617
+ ))
1618
+
1619
+ # Configure layout for better appearance
1620
+ real_fig.update_layout(
1621
+ title={
1622
+ 'text': f'Re(s) vs z Analysis: a={cubic_a}, y={cubic_y}, β={cubic_beta}',
1623
+ 'font': {'size': 24, 'color': '#1E88E5'},
1624
+ 'y': 0.95,
1625
+ 'x': 0.5,
1626
+ 'xanchor': 'center',
1627
+ 'yanchor': 'top'
1628
+ },
1629
+ xaxis={
1630
+ 'title': {'text': 'z (logarithmic scale)', 'font': {'size': 18, 'color': '#424242'}},
1631
+ 'tickfont': {'size': 14},
1632
+ 'gridcolor': 'rgba(220, 220, 220, 0.5)',
1633
+ 'showgrid': True,
1634
+ 'type': 'log' # Use logarithmic scale for better visualization
1635
+ },
1636
+ yaxis={
1637
+ 'title': {'text': 'Re(s)', 'font': {'size': 18, 'color': '#424242'}},
1638
+ 'tickfont': {'size': 14},
1639
+ 'gridcolor': 'rgba(220, 220, 220, 0.5)',
1640
+ 'showgrid': True
1641
+ },
1642
+ plot_bgcolor='rgba(240, 240, 240, 0.8)',
1643
+ paper_bgcolor='rgba(249, 249, 249, 0.8)',
1644
+ hovermode='closest',
1645
+ legend={
1646
+ 'font': {'size': 14},
1647
+ 'bgcolor': 'rgba(255, 255, 255, 0.9)',
1648
+ 'bordercolor': 'rgba(200, 200, 200, 0.5)',
1649
+ 'borderwidth': 1
1650
+ },
1651
+ margin={'l': 60, 'r': 30, 't': 100, 'b': 60},
1652
+ height=500
1653
+ )
1654
+
1655
+ # Display the interactive plot in Streamlit
1656
+ st.plotly_chart(real_fig, use_container_width=True)
1657
 
1658
  # Clear progress container
1659
  progress_container.empty()
1660
 
 
 
 
1661
  # Add explanation text
1662
+ st.markdown('<div class="explanation-box">', unsafe_allow_html=True)
1663
  st.markdown("""
1664
+ ### Root Pattern Analysis
1665
 
1666
+ For the cubic equation in this analysis, we observe specific patterns in the roots:
 
1667
 
1668
+ - One root typically has negative real part
1669
+ - One root typically has positive real part
1670
+ - One root has zero or near-zero real part
1671
 
1672
+ The imaginary parts show oscillatory behavior, with some z values producing purely real roots
1673
+ (Im(s) = 0) and others producing complex roots with non-zero imaginary parts. This pattern
1674
+ is consistent with the expected behavior of cubic equations and has important implications
1675
+ for system stability analysis.
1676
 
1677
+ The imaginary parts represent oscillatory behavior in the system, while the real parts
1678
+ represent exponential growth (positive) or decay (negative).
1679
  """)
1680
+ st.markdown('</div>', unsafe_allow_html=True)
1681
 
1682
  except json.JSONDecodeError as e:
1683
  st.error(f"Error parsing JSON results: {str(e)}")
 
1705
  ims_values2 = np.array(data['ims_values2'])
1706
  ims_values3 = np.array(data['ims_values3'])
1707
 
1708
+ # Also extract real parts if available
1709
+ real_values1 = np.array(data.get('real_values1', [0] * len(z_values)))
1710
+ real_values2 = np.array(data.get('real_values2', [0] * len(z_values)))
1711
+ real_values3 = np.array(data.get('real_values3', [0] * len(z_values)))
1712
+
1713
+ # Show previous results with Imaginary parts
1714
  fig = go.Figure()
1715
 
1716
  # Add traces for each root's imaginary part
 
1798
  2. **Adjust parameters** in the left panel to configure your analysis
1799
  3. **Click the Generate button** to run the analysis with the selected parameters
1800
  4. **Explore the results** in the interactive plot
1801
+ 5. For the Im(s) vs z Analysis, you can toggle between Imaginary and Real parts to see different aspects of the cubic roots
1802
 
1803
  If you encounter any issues with compilation, try clicking the "Recompile C++ Code" button in the sidebar.
1804
+
1805
+ <div class="footnote">
1806
+ This dashboard analyzes the properties of cubic equations and eigenvalues for matrix analysis.
1807
+ The Im(s) vs z Analysis shows the behavior of cubic roots, with specific patterns of one negative, one positive, and one zero or near-zero root.
1808
+ </div>
1809
+ """, unsafe_allow_html=True)