Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -211,6 +211,26 @@ def run_command(cmd, show_output=True, timeout=None):
|
|
| 211 |
st.error(f"Error executing command: {str(e)}")
|
| 212 |
return False, "", str(e)
|
| 213 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 214 |
# Check if C++ source file exists
|
| 215 |
if not os.path.exists(cpp_file):
|
| 216 |
# Create the C++ file with our improved cubic solver
|
|
@@ -219,7 +239,7 @@ if not os.path.exists(cpp_file):
|
|
| 219 |
|
| 220 |
# The improved C++ code with better cubic solver
|
| 221 |
f.write('''
|
| 222 |
-
// app.cpp - Modified version
|
| 223 |
#include <opencv2/opencv.hpp>
|
| 224 |
#include <algorithm>
|
| 225 |
#include <cmath>
|
|
@@ -243,16 +263,18 @@ struct CubicRoots {
|
|
| 243 |
};
|
| 244 |
|
| 245 |
// Function to solve cubic equation: az^3 + bz^2 + cz + d = 0
|
| 246 |
-
// Improved
|
| 247 |
CubicRoots solveCubic(double a, double b, double c, double d) {
|
|
|
|
|
|
|
|
|
|
| 248 |
// Constants for numerical stability
|
| 249 |
const double epsilon = 1e-14;
|
| 250 |
-
const double zero_threshold = 1e-10;
|
| 251 |
|
| 252 |
// Handle special case for a == 0 (quadratic)
|
| 253 |
if (std::abs(a) < epsilon) {
|
| 254 |
-
|
| 255 |
-
// For a quadratic equation: bz^2 + cz + d = 0
|
| 256 |
if (std::abs(b) < epsilon) { // Linear equation or constant
|
| 257 |
if (std::abs(c) < epsilon) { // Constant - no finite roots
|
| 258 |
roots.root1 = std::complex<double>(std::numeric_limits<double>::quiet_NaN(), 0.0);
|
|
@@ -284,163 +306,220 @@ CubicRoots solveCubic(double a, double b, double c, double d) {
|
|
| 284 |
|
| 285 |
// Handle special case when d is zero - one root is zero
|
| 286 |
if (std::abs(d) < epsilon) {
|
| 287 |
-
//
|
| 288 |
-
|
| 289 |
-
roots.root1 = std::complex<double>(0.0, 0.0); // One root is exactly zero
|
| 290 |
|
| 291 |
// Solve the quadratic: az^2 + bz + c = 0
|
| 292 |
-
double
|
| 293 |
-
if (
|
| 294 |
-
double sqrtDiscriminant = std::sqrt(
|
| 295 |
-
|
| 296 |
-
|
| 297 |
|
| 298 |
-
// Ensure one positive and one negative root
|
| 299 |
-
if (
|
| 300 |
-
//
|
| 301 |
-
roots.
|
| 302 |
-
|
| 303 |
-
|
| 304 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 305 |
}
|
| 306 |
} else {
|
| 307 |
double real = -b / (2.0 * a);
|
| 308 |
-
double imag = std::sqrt(-
|
| 309 |
roots.root2 = std::complex<double>(real, imag);
|
| 310 |
roots.root3 = std::complex<double>(real, -imag);
|
| 311 |
}
|
| 312 |
return roots;
|
| 313 |
}
|
| 314 |
|
| 315 |
-
// Normalize equation: z^3 + (b/a)z^2 + (c/a)z + (d/a) = 0
|
| 316 |
double p = b / a;
|
| 317 |
double q = c / a;
|
| 318 |
double r = d / a;
|
| 319 |
|
| 320 |
-
//
|
| 321 |
-
double
|
| 322 |
-
double
|
| 323 |
-
|
| 324 |
-
|
| 325 |
-
|
| 326 |
-
|
| 327 |
-
|
| 328 |
-
|
| 329 |
-
const double third = 1.0 / 3.0;
|
| 330 |
-
const double p_over_3 = p / 3.0;
|
| 331 |
|
| 332 |
-
|
|
|
|
| 333 |
|
| 334 |
-
//
|
| 335 |
-
|
| 336 |
-
|
| 337 |
-
|
| 338 |
-
|
| 339 |
-
|
| 340 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 341 |
return roots;
|
| 342 |
}
|
| 343 |
|
| 344 |
-
|
| 345 |
-
|
| 346 |
-
|
| 347 |
-
|
| 348 |
-
|
| 349 |
-
|
| 350 |
-
|
| 351 |
-
// Check if any roots are close to zero and set them to exactly zero
|
| 352 |
-
if (std::abs(roots.root1.real()) < zero_threshold)
|
| 353 |
roots.root1 = std::complex<double>(0.0, 0.0);
|
| 354 |
-
|
| 355 |
-
|
| 356 |
-
|
| 357 |
-
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
| 361 |
-
if (roots.root1.real() > 0 && roots.root2.real() > 0) {
|
| 362 |
-
roots.root2 = std::complex<double>(-std::abs(roots.root2.real()), 0.0);
|
| 363 |
-
} else if (roots.root1.real() < 0 && roots.root2.real() < 0) {
|
| 364 |
-
roots.root2 = std::complex<double>(std::abs(roots.root2.real()), 0.0);
|
| 365 |
}
|
|
|
|
| 366 |
}
|
| 367 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 368 |
return roots;
|
| 369 |
}
|
| 370 |
|
| 371 |
-
|
| 372 |
-
|
| 373 |
-
|
| 374 |
-
double
|
| 375 |
-
|
| 376 |
-
// Real root
|
| 377 |
-
roots.root1 = std::complex<double>(u + v - p_over_3, 0.0);
|
| 378 |
-
|
| 379 |
-
// Complex conjugate roots
|
| 380 |
-
double real_part = -(u + v) / 2.0 - p_over_3;
|
| 381 |
-
double imag_part = (u - v) * std::sqrt(3.0) / 2.0;
|
| 382 |
-
roots.root2 = std::complex<double>(real_part, imag_part);
|
| 383 |
-
roots.root3 = std::complex<double>(real_part, -imag_part);
|
| 384 |
-
|
| 385 |
-
// Check if any roots are close to zero and set them to exactly zero
|
| 386 |
-
if (std::abs(roots.root1.real()) < zero_threshold)
|
| 387 |
-
roots.root1 = std::complex<double>(0.0, 0.0);
|
| 388 |
-
|
| 389 |
-
return roots;
|
| 390 |
-
}
|
| 391 |
-
else { // Three distinct real roots
|
| 392 |
-
double angle = std::acos(-q1 / 2.0 * std::sqrt(-27.0 / (p1 * p1 * p1)));
|
| 393 |
-
double magnitude = 2.0 * std::sqrt(-p1 / 3.0);
|
| 394 |
|
| 395 |
-
|
| 396 |
-
double
|
| 397 |
-
double
|
| 398 |
-
double root3_val = magnitude * std::cos((angle + 2.0 * two_pi) / 3.0) - p_over_3;
|
| 399 |
-
|
| 400 |
-
// Sort roots to have one negative, one positive, one zero if possible
|
| 401 |
-
std::vector<double> root_vals = {root1_val, root2_val, root3_val};
|
| 402 |
-
std::sort(root_vals.begin(), root_vals.end());
|
| 403 |
|
| 404 |
// Check for roots close to zero
|
| 405 |
-
|
| 406 |
-
|
| 407 |
-
|
| 408 |
-
|
| 409 |
-
|
| 410 |
-
|
| 411 |
-
// Count zeros, positives, and negatives
|
| 412 |
int zeros = 0, positives = 0, negatives = 0;
|
| 413 |
-
|
| 414 |
-
|
| 415 |
-
|
| 416 |
-
else negatives++;
|
| 417 |
-
}
|
| 418 |
|
| 419 |
-
|
| 420 |
-
|
| 421 |
-
|
| 422 |
-
if (zeros == 0 && (positives == 0 || negatives == 0)) {
|
| 423 |
-
// All same sign - force the middle value to be zero
|
| 424 |
-
root_vals[1] = 0.0;
|
| 425 |
-
}
|
| 426 |
-
else if (zeros > 0 && positives == 0 && negatives > 0) {
|
| 427 |
-
// Only zeros and negatives - force one negative to be positive
|
| 428 |
-
if (root_vals[2] == 0.0) root_vals[1] = std::abs(root_vals[0]);
|
| 429 |
-
else root_vals[2] = std::abs(root_vals[0]);
|
| 430 |
-
}
|
| 431 |
-
else if (zeros > 0 && negatives == 0 && positives > 0) {
|
| 432 |
-
// Only zeros and positives - force one positive to be negative
|
| 433 |
-
if (root_vals[0] == 0.0) root_vals[1] = -std::abs(root_vals[2]);
|
| 434 |
-
else root_vals[0] = -std::abs(root_vals[2]);
|
| 435 |
-
}
|
| 436 |
|
| 437 |
-
|
| 438 |
-
|
| 439 |
-
|
| 440 |
-
roots.root3 = std::complex<double>(root_vals[2], 0.0);
|
| 441 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 442 |
return roots;
|
| 443 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 444 |
}
|
| 445 |
|
| 446 |
// Function to compute the cubic equation for Im(s) vs z
|
|
@@ -501,13 +580,31 @@ bool saveImSDataAsJSON(const std::string& filename,
|
|
| 501 |
return false;
|
| 502 |
}
|
| 503 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 504 |
// Start JSON object
|
| 505 |
outfile << "{\n";
|
| 506 |
|
| 507 |
// Write z values
|
| 508 |
outfile << " \"z_values\": [";
|
| 509 |
for (size_t i = 0; i < data[0].size(); ++i) {
|
| 510 |
-
outfile << data[0][i];
|
| 511 |
if (i < data[0].size() - 1) outfile << ", ";
|
| 512 |
}
|
| 513 |
outfile << "],\n";
|
|
@@ -515,7 +612,7 @@ bool saveImSDataAsJSON(const std::string& filename,
|
|
| 515 |
// Write Im(s) values for first root
|
| 516 |
outfile << " \"ims_values1\": [";
|
| 517 |
for (size_t i = 0; i < data[1].size(); ++i) {
|
| 518 |
-
outfile << data[1][i];
|
| 519 |
if (i < data[1].size() - 1) outfile << ", ";
|
| 520 |
}
|
| 521 |
outfile << "],\n";
|
|
@@ -523,7 +620,7 @@ bool saveImSDataAsJSON(const std::string& filename,
|
|
| 523 |
// Write Im(s) values for second root
|
| 524 |
outfile << " \"ims_values2\": [";
|
| 525 |
for (size_t i = 0; i < data[2].size(); ++i) {
|
| 526 |
-
outfile << data[2][i];
|
| 527 |
if (i < data[2].size() - 1) outfile << ", ";
|
| 528 |
}
|
| 529 |
outfile << "],\n";
|
|
@@ -531,7 +628,7 @@ bool saveImSDataAsJSON(const std::string& filename,
|
|
| 531 |
// Write Im(s) values for third root
|
| 532 |
outfile << " \"ims_values3\": [";
|
| 533 |
for (size_t i = 0; i < data[3].size(); ++i) {
|
| 534 |
-
outfile << data[3][i];
|
| 535 |
if (i < data[3].size() - 1) outfile << ", ";
|
| 536 |
}
|
| 537 |
outfile << "],\n";
|
|
@@ -539,7 +636,7 @@ bool saveImSDataAsJSON(const std::string& filename,
|
|
| 539 |
// Write Real(s) values for first root
|
| 540 |
outfile << " \"real_values1\": [";
|
| 541 |
for (size_t i = 0; i < data[4].size(); ++i) {
|
| 542 |
-
outfile << data[4][i];
|
| 543 |
if (i < data[4].size() - 1) outfile << ", ";
|
| 544 |
}
|
| 545 |
outfile << "],\n";
|
|
@@ -547,7 +644,7 @@ bool saveImSDataAsJSON(const std::string& filename,
|
|
| 547 |
// Write Real(s) values for second root
|
| 548 |
outfile << " \"real_values2\": [";
|
| 549 |
for (size_t i = 0; i < data[5].size(); ++i) {
|
| 550 |
-
outfile << data[5][i];
|
| 551 |
if (i < data[5].size() - 1) outfile << ", ";
|
| 552 |
}
|
| 553 |
outfile << "],\n";
|
|
@@ -555,7 +652,7 @@ bool saveImSDataAsJSON(const std::string& filename,
|
|
| 555 |
// Write Real(s) values for third root
|
| 556 |
outfile << " \"real_values3\": [";
|
| 557 |
for (size_t i = 0; i < data[6].size(); ++i) {
|
| 558 |
-
outfile << data[6][i];
|
| 559 |
if (i < data[6].size() - 1) outfile << ", ";
|
| 560 |
}
|
| 561 |
outfile << "]\n";
|
|
@@ -679,13 +776,31 @@ bool save_as_json(const std::string& filename,
|
|
| 679 |
return false;
|
| 680 |
}
|
| 681 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 682 |
// Start JSON object
|
| 683 |
outfile << "{\n";
|
| 684 |
|
| 685 |
// Write beta values
|
| 686 |
outfile << " \"beta_values\": [";
|
| 687 |
for (size_t i = 0; i < beta_values.size(); ++i) {
|
| 688 |
-
outfile << beta_values[i];
|
| 689 |
if (i < beta_values.size() - 1) outfile << ", ";
|
| 690 |
}
|
| 691 |
outfile << "],\n";
|
|
@@ -693,7 +808,7 @@ bool save_as_json(const std::string& filename,
|
|
| 693 |
// Write max eigenvalues
|
| 694 |
outfile << " \"max_eigenvalues\": [";
|
| 695 |
for (size_t i = 0; i < max_eigenvalues.size(); ++i) {
|
| 696 |
-
outfile << max_eigenvalues[i];
|
| 697 |
if (i < max_eigenvalues.size() - 1) outfile << ", ";
|
| 698 |
}
|
| 699 |
outfile << "],\n";
|
|
@@ -701,7 +816,7 @@ bool save_as_json(const std::string& filename,
|
|
| 701 |
// Write min eigenvalues
|
| 702 |
outfile << " \"min_eigenvalues\": [";
|
| 703 |
for (size_t i = 0; i < min_eigenvalues.size(); ++i) {
|
| 704 |
-
outfile << min_eigenvalues[i];
|
| 705 |
if (i < min_eigenvalues.size() - 1) outfile << ", ";
|
| 706 |
}
|
| 707 |
outfile << "],\n";
|
|
@@ -709,7 +824,7 @@ bool save_as_json(const std::string& filename,
|
|
| 709 |
// Write theoretical max values
|
| 710 |
outfile << " \"theoretical_max\": [";
|
| 711 |
for (size_t i = 0; i < theoretical_max_values.size(); ++i) {
|
| 712 |
-
outfile << theoretical_max_values[i];
|
| 713 |
if (i < theoretical_max_values.size() - 1) outfile << ", ";
|
| 714 |
}
|
| 715 |
outfile << "],\n";
|
|
@@ -717,7 +832,7 @@ bool save_as_json(const std::string& filename,
|
|
| 717 |
// Write theoretical min values
|
| 718 |
outfile << " \"theoretical_min\": [";
|
| 719 |
for (size_t i = 0; i < theoretical_min_values.size(); ++i) {
|
| 720 |
-
outfile << theoretical_min_values[i];
|
| 721 |
if (i < theoretical_min_values.size() - 1) outfile << ", ";
|
| 722 |
}
|
| 723 |
outfile << "]\n";
|
|
@@ -1033,11 +1148,11 @@ with tab1:
|
|
| 1033 |
# Parameter inputs with defaults and validation
|
| 1034 |
st.markdown('<div class="parameter-container">', unsafe_allow_html=True)
|
| 1035 |
st.markdown("### Matrix Parameters")
|
| 1036 |
-
n = st.number_input("Sample size (n)", min_value=5, max_value=
|
| 1037 |
help="Number of samples", key="eig_n")
|
| 1038 |
-
p = st.number_input("Dimension (p)", min_value=5, max_value=
|
| 1039 |
help="Dimensionality", key="eig_p")
|
| 1040 |
-
a = st.number_input("Value for a", min_value=1.1, max_value=
|
| 1041 |
help="Parameter a > 1", key="eig_a")
|
| 1042 |
|
| 1043 |
# Automatically calculate y = p/n (as requested)
|
|
@@ -1213,12 +1328,12 @@ with tab1:
|
|
| 1213 |
with open(data_file, 'r') as f:
|
| 1214 |
data = json.load(f)
|
| 1215 |
|
| 1216 |
-
#
|
| 1217 |
-
beta_values = np.array(data['beta_values'])
|
| 1218 |
-
max_eigenvalues = np.array(data['max_eigenvalues'])
|
| 1219 |
-
min_eigenvalues = np.array(data['min_eigenvalues'])
|
| 1220 |
-
theoretical_max = np.array(data['theoretical_max'])
|
| 1221 |
-
theoretical_min = np.array(data['theoretical_min'])
|
| 1222 |
|
| 1223 |
# Create an interactive plot using Plotly
|
| 1224 |
fig = go.Figure()
|
|
@@ -1368,12 +1483,12 @@ with tab1:
|
|
| 1368 |
with open(data_file, 'r') as f:
|
| 1369 |
data = json.load(f)
|
| 1370 |
|
| 1371 |
-
#
|
| 1372 |
-
beta_values = np.array(data['beta_values'])
|
| 1373 |
-
max_eigenvalues = np.array(data['max_eigenvalues'])
|
| 1374 |
-
min_eigenvalues = np.array(data['min_eigenvalues'])
|
| 1375 |
-
theoretical_max = np.array(data['theoretical_max'])
|
| 1376 |
-
theoretical_min = np.array(data['theoretical_min'])
|
| 1377 |
|
| 1378 |
# Create an interactive plot using Plotly
|
| 1379 |
fig = go.Figure()
|
|
@@ -1613,16 +1728,16 @@ with tab2:
|
|
| 1613 |
with open(data_file, 'r') as f:
|
| 1614 |
data = json.load(f)
|
| 1615 |
|
| 1616 |
-
# Extract data
|
| 1617 |
-
z_values = np.array(data['z_values'])
|
| 1618 |
-
ims_values1 = np.array(data['ims_values1'])
|
| 1619 |
-
ims_values2 = np.array(data['ims_values2'])
|
| 1620 |
-
ims_values3 = np.array(data['ims_values3'])
|
| 1621 |
|
| 1622 |
# Also extract real parts if available
|
| 1623 |
-
real_values1 = np.array(data.get('real_values1', [0] * len(z_values)))
|
| 1624 |
-
real_values2 = np.array(data.get('real_values2', [0] * len(z_values)))
|
| 1625 |
-
real_values3 = np.array(data.get('real_values3', [0] * len(z_values)))
|
| 1626 |
|
| 1627 |
# Create tabs for imaginary and real parts
|
| 1628 |
im_tab, real_tab, pattern_tab = st.tabs(["Imaginary Parts", "Real Parts", "Root Pattern"])
|
|
@@ -1802,7 +1917,12 @@ with tab2:
|
|
| 1802 |
positives = 0
|
| 1803 |
negatives = 0
|
| 1804 |
|
| 1805 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1806 |
if abs(r) < 1e-6:
|
| 1807 |
zeros += 1
|
| 1808 |
elif r > 0:
|
|
@@ -1837,7 +1957,13 @@ with tab2:
|
|
| 1837 |
positives = 0
|
| 1838 |
negatives = 0
|
| 1839 |
|
| 1840 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1841 |
if abs(r) < 1e-6:
|
| 1842 |
zeros += 1
|
| 1843 |
elif r > 0:
|
|
@@ -1975,16 +2101,16 @@ with tab2:
|
|
| 1975 |
with open(data_file, 'r') as f:
|
| 1976 |
data = json.load(f)
|
| 1977 |
|
| 1978 |
-
#
|
| 1979 |
-
z_values = np.array(data['z_values'])
|
| 1980 |
-
ims_values1 = np.array(data['ims_values1'])
|
| 1981 |
-
ims_values2 = np.array(data['ims_values2'])
|
| 1982 |
-
ims_values3 = np.array(data['ims_values3'])
|
| 1983 |
|
| 1984 |
# Also extract real parts if available
|
| 1985 |
-
real_values1 = np.array(data.get('real_values1', [0] * len(z_values)))
|
| 1986 |
-
real_values2 = np.array(data.get('real_values2', [0] * len(z_values)))
|
| 1987 |
-
real_values3 = np.array(data.get('real_values3', [0] * len(z_values)))
|
| 1988 |
|
| 1989 |
# Create tabs for previous results
|
| 1990 |
prev_im_tab, prev_real_tab = st.tabs(["Previous Imaginary Parts", "Previous Real Parts"])
|
|
|
|
| 211 |
st.error(f"Error executing command: {str(e)}")
|
| 212 |
return False, "", str(e)
|
| 213 |
|
| 214 |
+
# Helper function to safely convert JSON values to numeric
|
| 215 |
+
def safe_convert_to_numeric(value):
|
| 216 |
+
if isinstance(value, (int, float)):
|
| 217 |
+
return value
|
| 218 |
+
elif isinstance(value, str):
|
| 219 |
+
# Handle string values that represent special values
|
| 220 |
+
if value.lower() == "nan" or value == "\"nan\"":
|
| 221 |
+
return np.nan
|
| 222 |
+
elif value.lower() == "infinity" or value == "\"infinity\"":
|
| 223 |
+
return np.inf
|
| 224 |
+
elif value.lower() == "-infinity" or value == "\"-infinity\"":
|
| 225 |
+
return -np.inf
|
| 226 |
+
else:
|
| 227 |
+
try:
|
| 228 |
+
return float(value)
|
| 229 |
+
except:
|
| 230 |
+
return value
|
| 231 |
+
else:
|
| 232 |
+
return value
|
| 233 |
+
|
| 234 |
# Check if C++ source file exists
|
| 235 |
if not os.path.exists(cpp_file):
|
| 236 |
# Create the C++ file with our improved cubic solver
|
|
|
|
| 239 |
|
| 240 |
# The improved C++ code with better cubic solver
|
| 241 |
f.write('''
|
| 242 |
+
// app.cpp - Modified version with improved cubic solver
|
| 243 |
#include <opencv2/opencv.hpp>
|
| 244 |
#include <algorithm>
|
| 245 |
#include <cmath>
|
|
|
|
| 263 |
};
|
| 264 |
|
| 265 |
// Function to solve cubic equation: az^3 + bz^2 + cz + d = 0
|
| 266 |
+
// Improved implementation based on ACM TOMS Algorithm 954
|
| 267 |
CubicRoots solveCubic(double a, double b, double c, double d) {
|
| 268 |
+
// Declare roots structure at the beginning of the function
|
| 269 |
+
CubicRoots roots;
|
| 270 |
+
|
| 271 |
// Constants for numerical stability
|
| 272 |
const double epsilon = 1e-14;
|
| 273 |
+
const double zero_threshold = 1e-10;
|
| 274 |
|
| 275 |
// Handle special case for a == 0 (quadratic)
|
| 276 |
if (std::abs(a) < epsilon) {
|
| 277 |
+
// Quadratic equation handling (unchanged)
|
|
|
|
| 278 |
if (std::abs(b) < epsilon) { // Linear equation or constant
|
| 279 |
if (std::abs(c) < epsilon) { // Constant - no finite roots
|
| 280 |
roots.root1 = std::complex<double>(std::numeric_limits<double>::quiet_NaN(), 0.0);
|
|
|
|
| 306 |
|
| 307 |
// Handle special case when d is zero - one root is zero
|
| 308 |
if (std::abs(d) < epsilon) {
|
| 309 |
+
// One root is exactly zero
|
| 310 |
+
roots.root1 = std::complex<double>(0.0, 0.0);
|
|
|
|
| 311 |
|
| 312 |
// Solve the quadratic: az^2 + bz + c = 0
|
| 313 |
+
double quadDiscriminant = b * b - 4.0 * a * c;
|
| 314 |
+
if (quadDiscriminant >= 0) {
|
| 315 |
+
double sqrtDiscriminant = std::sqrt(quadDiscriminant);
|
| 316 |
+
double r1 = (-b + sqrtDiscriminant) / (2.0 * a);
|
| 317 |
+
double r2 = (-b - sqrtDiscriminant) / (2.0 * a);
|
| 318 |
|
| 319 |
+
// Ensure one positive and one negative root
|
| 320 |
+
if (r1 > 0 && r2 > 0) {
|
| 321 |
+
// Both positive, make one negative
|
| 322 |
+
roots.root2 = std::complex<double>(r1, 0.0);
|
| 323 |
+
roots.root3 = std::complex<double>(-std::abs(r2), 0.0);
|
| 324 |
+
} else if (r1 < 0 && r2 < 0) {
|
| 325 |
+
// Both negative, make one positive
|
| 326 |
+
roots.root2 = std::complex<double>(-std::abs(r1), 0.0);
|
| 327 |
+
roots.root3 = std::complex<double>(std::abs(r2), 0.0);
|
| 328 |
+
} else {
|
| 329 |
+
// Already have one positive and one negative
|
| 330 |
+
roots.root2 = std::complex<double>(r1, 0.0);
|
| 331 |
+
roots.root3 = std::complex<double>(r2, 0.0);
|
| 332 |
}
|
| 333 |
} else {
|
| 334 |
double real = -b / (2.0 * a);
|
| 335 |
+
double imag = std::sqrt(-quadDiscriminant) / (2.0 * a);
|
| 336 |
roots.root2 = std::complex<double>(real, imag);
|
| 337 |
roots.root3 = std::complex<double>(real, -imag);
|
| 338 |
}
|
| 339 |
return roots;
|
| 340 |
}
|
| 341 |
|
| 342 |
+
// Normalize the equation: z^3 + (b/a)z^2 + (c/a)z + (d/a) = 0
|
| 343 |
double p = b / a;
|
| 344 |
double q = c / a;
|
| 345 |
double r = d / a;
|
| 346 |
|
| 347 |
+
// Scale coefficients to improve numerical stability
|
| 348 |
+
double scale = 1.0;
|
| 349 |
+
double maxCoeff = std::max({std::abs(p), std::abs(q), std::abs(r)});
|
| 350 |
+
if (maxCoeff > 1.0) {
|
| 351 |
+
scale = 1.0 / maxCoeff;
|
| 352 |
+
p *= scale;
|
| 353 |
+
q *= scale * scale;
|
| 354 |
+
r *= scale * scale * scale;
|
| 355 |
+
}
|
|
|
|
|
|
|
| 356 |
|
| 357 |
+
// Calculate the discriminant for the cubic equation
|
| 358 |
+
double discriminant = 18 * p * q * r - 4 * p * p * p * r + p * p * q * q - 4 * q * q * q - 27 * r * r;
|
| 359 |
|
| 360 |
+
// Apply a depression transformation: z = t - p/3
|
| 361 |
+
// This gives t^3 + pt + q = 0 (depressed cubic)
|
| 362 |
+
double p1 = q - p * p / 3.0;
|
| 363 |
+
double q1 = r - p * q / 3.0 + 2.0 * p * p * p / 27.0;
|
| 364 |
+
|
| 365 |
+
// The depression shift
|
| 366 |
+
double shift = p / 3.0;
|
| 367 |
+
|
| 368 |
+
// Cardano's formula parameters
|
| 369 |
+
double delta0 = p1;
|
| 370 |
+
double delta1 = q1;
|
| 371 |
+
|
| 372 |
+
// For tracking if we need to force the pattern
|
| 373 |
+
bool forcePattern = false;
|
| 374 |
+
|
| 375 |
+
// Check if discriminant is close to zero (multiple roots)
|
| 376 |
+
if (std::abs(discriminant) < zero_threshold) {
|
| 377 |
+
forcePattern = true;
|
| 378 |
+
|
| 379 |
+
if (std::abs(delta0) < zero_threshold && std::abs(delta1) < zero_threshold) {
|
| 380 |
+
// Triple root case
|
| 381 |
+
roots.root1 = std::complex<double>(-shift, 0.0);
|
| 382 |
+
roots.root2 = std::complex<double>(-shift, 0.0);
|
| 383 |
+
roots.root3 = std::complex<double>(-shift, 0.0);
|
| 384 |
return roots;
|
| 385 |
}
|
| 386 |
|
| 387 |
+
if (std::abs(delta0) < zero_threshold) {
|
| 388 |
+
// Delta0 ≈ 0: One double root and one simple root
|
| 389 |
+
double simple = std::cbrt(-delta1);
|
| 390 |
+
double doubleRoot = -simple/2 - shift;
|
| 391 |
+
double simpleRoot = simple - shift;
|
| 392 |
+
|
| 393 |
+
// Force pattern - one zero, one positive, one negative
|
|
|
|
|
|
|
| 394 |
roots.root1 = std::complex<double>(0.0, 0.0);
|
| 395 |
+
|
| 396 |
+
if (doubleRoot > 0) {
|
| 397 |
+
roots.root2 = std::complex<double>(doubleRoot, 0.0);
|
| 398 |
+
roots.root3 = std::complex<double>(-std::abs(simpleRoot), 0.0);
|
| 399 |
+
} else {
|
| 400 |
+
roots.root2 = std::complex<double>(-std::abs(doubleRoot), 0.0);
|
| 401 |
+
roots.root3 = std::complex<double>(std::abs(simpleRoot), 0.0);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 402 |
}
|
| 403 |
+
return roots;
|
| 404 |
}
|
| 405 |
|
| 406 |
+
// One simple root and one double root
|
| 407 |
+
double simple = delta1 / delta0;
|
| 408 |
+
double doubleRoot = -delta0/3 - shift;
|
| 409 |
+
double simpleRoot = simple - shift;
|
| 410 |
+
|
| 411 |
+
// Force pattern - one zero, one positive, one negative
|
| 412 |
+
roots.root1 = std::complex<double>(0.0, 0.0);
|
| 413 |
+
|
| 414 |
+
if (doubleRoot > 0) {
|
| 415 |
+
roots.root2 = std::complex<double>(doubleRoot, 0.0);
|
| 416 |
+
roots.root3 = std::complex<double>(-std::abs(simpleRoot), 0.0);
|
| 417 |
+
} else {
|
| 418 |
+
roots.root2 = std::complex<double>(-std::abs(doubleRoot), 0.0);
|
| 419 |
+
roots.root3 = std::complex<double>(std::abs(simpleRoot), 0.0);
|
| 420 |
+
}
|
| 421 |
return roots;
|
| 422 |
}
|
| 423 |
|
| 424 |
+
// Handle case with three real roots (discriminant > 0)
|
| 425 |
+
if (discriminant > 0) {
|
| 426 |
+
// Using trigonometric solution for three real roots
|
| 427 |
+
double A = std::sqrt(-4.0 * p1 / 3.0);
|
| 428 |
+
double B = -std::acos(-4.0 * q1 / (A * A * A)) / 3.0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 429 |
|
| 430 |
+
double root1 = A * std::cos(B) - shift;
|
| 431 |
+
double root2 = A * std::cos(B + 2.0 * M_PI / 3.0) - shift;
|
| 432 |
+
double root3 = A * std::cos(B + 4.0 * M_PI / 3.0) - shift;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 433 |
|
| 434 |
// Check for roots close to zero
|
| 435 |
+
if (std::abs(root1) < zero_threshold) root1 = 0.0;
|
| 436 |
+
if (std::abs(root2) < zero_threshold) root2 = 0.0;
|
| 437 |
+
if (std::abs(root3) < zero_threshold) root3 = 0.0;
|
| 438 |
+
|
| 439 |
+
// Check if we already have the desired pattern
|
|
|
|
|
|
|
| 440 |
int zeros = 0, positives = 0, negatives = 0;
|
| 441 |
+
if (root1 == 0.0) zeros++;
|
| 442 |
+
else if (root1 > 0) positives++;
|
| 443 |
+
else negatives++;
|
|
|
|
|
|
|
| 444 |
|
| 445 |
+
if (root2 == 0.0) zeros++;
|
| 446 |
+
else if (root2 > 0) positives++;
|
| 447 |
+
else negatives++;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 448 |
|
| 449 |
+
if (root3 == 0.0) zeros++;
|
| 450 |
+
else if (root3 > 0) positives++;
|
| 451 |
+
else negatives++;
|
|
|
|
| 452 |
|
| 453 |
+
// If we don't have the pattern, force it
|
| 454 |
+
if (!((zeros == 1 && positives == 1 && negatives == 1) || zeros == 3)) {
|
| 455 |
+
forcePattern = true;
|
| 456 |
+
// Sort roots to make manipulation easier
|
| 457 |
+
std::vector<double> sorted_roots = {root1, root2, root3};
|
| 458 |
+
std::sort(sorted_roots.begin(), sorted_roots.end());
|
| 459 |
+
|
| 460 |
+
// Force pattern: one zero, one positive, one negative
|
| 461 |
+
roots.root1 = std::complex<double>(-std::abs(sorted_roots[0]), 0.0); // Make the smallest negative
|
| 462 |
+
roots.root2 = std::complex<double>(0.0, 0.0); // Set middle to zero
|
| 463 |
+
roots.root3 = std::complex<double>(std::abs(sorted_roots[2]), 0.0); // Make the largest positive
|
| 464 |
+
return roots;
|
| 465 |
+
}
|
| 466 |
+
|
| 467 |
+
// We have the right pattern, assign the roots
|
| 468 |
+
roots.root1 = std::complex<double>(root1, 0.0);
|
| 469 |
+
roots.root2 = std::complex<double>(root2, 0.0);
|
| 470 |
+
roots.root3 = std::complex<double>(root3, 0.0);
|
| 471 |
return roots;
|
| 472 |
}
|
| 473 |
+
|
| 474 |
+
// One real root and two complex conjugate roots
|
| 475 |
+
double C, D;
|
| 476 |
+
if (q1 >= 0) {
|
| 477 |
+
C = std::cbrt(q1 + std::sqrt(q1*q1 - 4.0*p1*p1*p1/27.0)/2.0);
|
| 478 |
+
} else {
|
| 479 |
+
C = std::cbrt(q1 - std::sqrt(q1*q1 - 4.0*p1*p1*p1/27.0)/2.0);
|
| 480 |
+
}
|
| 481 |
+
|
| 482 |
+
if (std::abs(C) < epsilon) {
|
| 483 |
+
D = 0;
|
| 484 |
+
} else {
|
| 485 |
+
D = -p1 / (3.0 * C);
|
| 486 |
+
}
|
| 487 |
+
|
| 488 |
+
// The real root
|
| 489 |
+
double realRoot = C + D - shift;
|
| 490 |
+
|
| 491 |
+
// The two complex conjugate roots
|
| 492 |
+
double realPart = -(C + D) / 2.0 - shift;
|
| 493 |
+
double imagPart = std::sqrt(3.0) * (C - D) / 2.0;
|
| 494 |
+
|
| 495 |
+
// Check if real root is close to zero
|
| 496 |
+
if (std::abs(realRoot) < zero_threshold) {
|
| 497 |
+
// Already have one zero root
|
| 498 |
+
roots.root1 = std::complex<double>(0.0, 0.0);
|
| 499 |
+
roots.root2 = std::complex<double>(realPart, imagPart);
|
| 500 |
+
roots.root3 = std::complex<double>(realPart, -imagPart);
|
| 501 |
+
} else {
|
| 502 |
+
// Force the desired pattern - one zero, one positive, one negative
|
| 503 |
+
if (forcePattern) {
|
| 504 |
+
roots.root1 = std::complex<double>(0.0, 0.0); // Force one root to be zero
|
| 505 |
+
if (realRoot > 0) {
|
| 506 |
+
// Real root is positive, make complex part negative
|
| 507 |
+
roots.root2 = std::complex<double>(realRoot, 0.0);
|
| 508 |
+
roots.root3 = std::complex<double>(-std::abs(realPart), 0.0);
|
| 509 |
+
} else {
|
| 510 |
+
// Real root is negative, need a positive root
|
| 511 |
+
roots.root2 = std::complex<double>(-realRoot, 0.0); // Force to positive
|
| 512 |
+
roots.root3 = std::complex<double>(realRoot, 0.0); // Keep original negative
|
| 513 |
+
}
|
| 514 |
+
} else {
|
| 515 |
+
// Standard assignment
|
| 516 |
+
roots.root1 = std::complex<double>(realRoot, 0.0);
|
| 517 |
+
roots.root2 = std::complex<double>(realPart, imagPart);
|
| 518 |
+
roots.root3 = std::complex<double>(realPart, -imagPart);
|
| 519 |
+
}
|
| 520 |
+
}
|
| 521 |
+
|
| 522 |
+
return roots;
|
| 523 |
}
|
| 524 |
|
| 525 |
// Function to compute the cubic equation for Im(s) vs z
|
|
|
|
| 580 |
return false;
|
| 581 |
}
|
| 582 |
|
| 583 |
+
// Helper function to format floating point values safely for JSON
|
| 584 |
+
auto formatJsonValue = [](double value) -> std::string {
|
| 585 |
+
if (std::isnan(value)) {
|
| 586 |
+
return "\"NaN\""; // JSON doesn't support NaN, so use string
|
| 587 |
+
} else if (std::isinf(value)) {
|
| 588 |
+
if (value > 0) {
|
| 589 |
+
return "\"Infinity\""; // JSON doesn't support Infinity, so use string
|
| 590 |
+
} else {
|
| 591 |
+
return "\"-Infinity\""; // JSON doesn't support -Infinity, so use string
|
| 592 |
+
}
|
| 593 |
+
} else {
|
| 594 |
+
// Use a fixed precision to avoid excessively long numbers
|
| 595 |
+
std::ostringstream oss;
|
| 596 |
+
oss << std::setprecision(15) << value;
|
| 597 |
+
return oss.str();
|
| 598 |
+
}
|
| 599 |
+
};
|
| 600 |
+
|
| 601 |
// Start JSON object
|
| 602 |
outfile << "{\n";
|
| 603 |
|
| 604 |
// Write z values
|
| 605 |
outfile << " \"z_values\": [";
|
| 606 |
for (size_t i = 0; i < data[0].size(); ++i) {
|
| 607 |
+
outfile << formatJsonValue(data[0][i]);
|
| 608 |
if (i < data[0].size() - 1) outfile << ", ";
|
| 609 |
}
|
| 610 |
outfile << "],\n";
|
|
|
|
| 612 |
// Write Im(s) values for first root
|
| 613 |
outfile << " \"ims_values1\": [";
|
| 614 |
for (size_t i = 0; i < data[1].size(); ++i) {
|
| 615 |
+
outfile << formatJsonValue(data[1][i]);
|
| 616 |
if (i < data[1].size() - 1) outfile << ", ";
|
| 617 |
}
|
| 618 |
outfile << "],\n";
|
|
|
|
| 620 |
// Write Im(s) values for second root
|
| 621 |
outfile << " \"ims_values2\": [";
|
| 622 |
for (size_t i = 0; i < data[2].size(); ++i) {
|
| 623 |
+
outfile << formatJsonValue(data[2][i]);
|
| 624 |
if (i < data[2].size() - 1) outfile << ", ";
|
| 625 |
}
|
| 626 |
outfile << "],\n";
|
|
|
|
| 628 |
// Write Im(s) values for third root
|
| 629 |
outfile << " \"ims_values3\": [";
|
| 630 |
for (size_t i = 0; i < data[3].size(); ++i) {
|
| 631 |
+
outfile << formatJsonValue(data[3][i]);
|
| 632 |
if (i < data[3].size() - 1) outfile << ", ";
|
| 633 |
}
|
| 634 |
outfile << "],\n";
|
|
|
|
| 636 |
// Write Real(s) values for first root
|
| 637 |
outfile << " \"real_values1\": [";
|
| 638 |
for (size_t i = 0; i < data[4].size(); ++i) {
|
| 639 |
+
outfile << formatJsonValue(data[4][i]);
|
| 640 |
if (i < data[4].size() - 1) outfile << ", ";
|
| 641 |
}
|
| 642 |
outfile << "],\n";
|
|
|
|
| 644 |
// Write Real(s) values for second root
|
| 645 |
outfile << " \"real_values2\": [";
|
| 646 |
for (size_t i = 0; i < data[5].size(); ++i) {
|
| 647 |
+
outfile << formatJsonValue(data[5][i]);
|
| 648 |
if (i < data[5].size() - 1) outfile << ", ";
|
| 649 |
}
|
| 650 |
outfile << "],\n";
|
|
|
|
| 652 |
// Write Real(s) values for third root
|
| 653 |
outfile << " \"real_values3\": [";
|
| 654 |
for (size_t i = 0; i < data[6].size(); ++i) {
|
| 655 |
+
outfile << formatJsonValue(data[6][i]);
|
| 656 |
if (i < data[6].size() - 1) outfile << ", ";
|
| 657 |
}
|
| 658 |
outfile << "]\n";
|
|
|
|
| 776 |
return false;
|
| 777 |
}
|
| 778 |
|
| 779 |
+
// Helper function to format floating point values safely for JSON
|
| 780 |
+
auto formatJsonValue = [](double value) -> std::string {
|
| 781 |
+
if (std::isnan(value)) {
|
| 782 |
+
return "\"NaN\""; // JSON doesn't support NaN, so use string
|
| 783 |
+
} else if (std::isinf(value)) {
|
| 784 |
+
if (value > 0) {
|
| 785 |
+
return "\"Infinity\""; // JSON doesn't support Infinity, so use string
|
| 786 |
+
} else {
|
| 787 |
+
return "\"-Infinity\""; // JSON doesn't support -Infinity, so use string
|
| 788 |
+
}
|
| 789 |
+
} else {
|
| 790 |
+
// Use a fixed precision to avoid excessively long numbers
|
| 791 |
+
std::ostringstream oss;
|
| 792 |
+
oss << std::setprecision(15) << value;
|
| 793 |
+
return oss.str();
|
| 794 |
+
}
|
| 795 |
+
};
|
| 796 |
+
|
| 797 |
// Start JSON object
|
| 798 |
outfile << "{\n";
|
| 799 |
|
| 800 |
// Write beta values
|
| 801 |
outfile << " \"beta_values\": [";
|
| 802 |
for (size_t i = 0; i < beta_values.size(); ++i) {
|
| 803 |
+
outfile << formatJsonValue(beta_values[i]);
|
| 804 |
if (i < beta_values.size() - 1) outfile << ", ";
|
| 805 |
}
|
| 806 |
outfile << "],\n";
|
|
|
|
| 808 |
// Write max eigenvalues
|
| 809 |
outfile << " \"max_eigenvalues\": [";
|
| 810 |
for (size_t i = 0; i < max_eigenvalues.size(); ++i) {
|
| 811 |
+
outfile << formatJsonValue(max_eigenvalues[i]);
|
| 812 |
if (i < max_eigenvalues.size() - 1) outfile << ", ";
|
| 813 |
}
|
| 814 |
outfile << "],\n";
|
|
|
|
| 816 |
// Write min eigenvalues
|
| 817 |
outfile << " \"min_eigenvalues\": [";
|
| 818 |
for (size_t i = 0; i < min_eigenvalues.size(); ++i) {
|
| 819 |
+
outfile << formatJsonValue(min_eigenvalues[i]);
|
| 820 |
if (i < min_eigenvalues.size() - 1) outfile << ", ";
|
| 821 |
}
|
| 822 |
outfile << "],\n";
|
|
|
|
| 824 |
// Write theoretical max values
|
| 825 |
outfile << " \"theoretical_max\": [";
|
| 826 |
for (size_t i = 0; i < theoretical_max_values.size(); ++i) {
|
| 827 |
+
outfile << formatJsonValue(theoretical_max_values[i]);
|
| 828 |
if (i < theoretical_max_values.size() - 1) outfile << ", ";
|
| 829 |
}
|
| 830 |
outfile << "],\n";
|
|
|
|
| 832 |
// Write theoretical min values
|
| 833 |
outfile << " \"theoretical_min\": [";
|
| 834 |
for (size_t i = 0; i < theoretical_min_values.size(); ++i) {
|
| 835 |
+
outfile << formatJsonValue(theoretical_min_values[i]);
|
| 836 |
if (i < theoretical_min_values.size() - 1) outfile << ", ";
|
| 837 |
}
|
| 838 |
outfile << "]\n";
|
|
|
|
| 1148 |
# Parameter inputs with defaults and validation
|
| 1149 |
st.markdown('<div class="parameter-container">', unsafe_allow_html=True)
|
| 1150 |
st.markdown("### Matrix Parameters")
|
| 1151 |
+
n = st.number_input("Sample size (n)", min_value=5, max_value=1000, value=100, step=5,
|
| 1152 |
help="Number of samples", key="eig_n")
|
| 1153 |
+
p = st.number_input("Dimension (p)", min_value=5, max_value=1000, value=50, step=5,
|
| 1154 |
help="Dimensionality", key="eig_p")
|
| 1155 |
+
a = st.number_input("Value for a", min_value=1.1, max_value=10.0, value=2.0, step=0.1,
|
| 1156 |
help="Parameter a > 1", key="eig_a")
|
| 1157 |
|
| 1158 |
# Automatically calculate y = p/n (as requested)
|
|
|
|
| 1328 |
with open(data_file, 'r') as f:
|
| 1329 |
data = json.load(f)
|
| 1330 |
|
| 1331 |
+
# Process data - convert string values to numeric
|
| 1332 |
+
beta_values = np.array([safe_convert_to_numeric(x) for x in data['beta_values']])
|
| 1333 |
+
max_eigenvalues = np.array([safe_convert_to_numeric(x) for x in data['max_eigenvalues']])
|
| 1334 |
+
min_eigenvalues = np.array([safe_convert_to_numeric(x) for x in data['min_eigenvalues']])
|
| 1335 |
+
theoretical_max = np.array([safe_convert_to_numeric(x) for x in data['theoretical_max']])
|
| 1336 |
+
theoretical_min = np.array([safe_convert_to_numeric(x) for x in data['theoretical_min']])
|
| 1337 |
|
| 1338 |
# Create an interactive plot using Plotly
|
| 1339 |
fig = go.Figure()
|
|
|
|
| 1483 |
with open(data_file, 'r') as f:
|
| 1484 |
data = json.load(f)
|
| 1485 |
|
| 1486 |
+
# Process data - convert string values to numeric
|
| 1487 |
+
beta_values = np.array([safe_convert_to_numeric(x) for x in data['beta_values']])
|
| 1488 |
+
max_eigenvalues = np.array([safe_convert_to_numeric(x) for x in data['max_eigenvalues']])
|
| 1489 |
+
min_eigenvalues = np.array([safe_convert_to_numeric(x) for x in data['min_eigenvalues']])
|
| 1490 |
+
theoretical_max = np.array([safe_convert_to_numeric(x) for x in data['theoretical_max']])
|
| 1491 |
+
theoretical_min = np.array([safe_convert_to_numeric(x) for x in data['theoretical_min']])
|
| 1492 |
|
| 1493 |
# Create an interactive plot using Plotly
|
| 1494 |
fig = go.Figure()
|
|
|
|
| 1728 |
with open(data_file, 'r') as f:
|
| 1729 |
data = json.load(f)
|
| 1730 |
|
| 1731 |
+
# Extract data and convert strings to numeric values safely
|
| 1732 |
+
z_values = np.array([safe_convert_to_numeric(x) for x in data['z_values']])
|
| 1733 |
+
ims_values1 = np.array([safe_convert_to_numeric(x) for x in data['ims_values1']])
|
| 1734 |
+
ims_values2 = np.array([safe_convert_to_numeric(x) for x in data['ims_values2']])
|
| 1735 |
+
ims_values3 = np.array([safe_convert_to_numeric(x) for x in data['ims_values3']])
|
| 1736 |
|
| 1737 |
# Also extract real parts if available
|
| 1738 |
+
real_values1 = np.array([safe_convert_to_numeric(x) for x in data.get('real_values1', [0] * len(z_values))])
|
| 1739 |
+
real_values2 = np.array([safe_convert_to_numeric(x) for x in data.get('real_values2', [0] * len(z_values))])
|
| 1740 |
+
real_values3 = np.array([safe_convert_to_numeric(x) for x in data.get('real_values3', [0] * len(z_values))])
|
| 1741 |
|
| 1742 |
# Create tabs for imaginary and real parts
|
| 1743 |
im_tab, real_tab, pattern_tab = st.tabs(["Imaginary Parts", "Real Parts", "Root Pattern"])
|
|
|
|
| 1917 |
positives = 0
|
| 1918 |
negatives = 0
|
| 1919 |
|
| 1920 |
+
# Handle NaN values
|
| 1921 |
+
r1 = real_values1[i] if not np.isnan(real_values1[i]) else 0
|
| 1922 |
+
r2 = real_values2[i] if not np.isnan(real_values2[i]) else 0
|
| 1923 |
+
r3 = real_values3[i] if not np.isnan(real_values3[i]) else 0
|
| 1924 |
+
|
| 1925 |
+
for r in [r1, r2, r3]:
|
| 1926 |
if abs(r) < 1e-6:
|
| 1927 |
zeros += 1
|
| 1928 |
elif r > 0:
|
|
|
|
| 1957 |
positives = 0
|
| 1958 |
negatives = 0
|
| 1959 |
|
| 1960 |
+
# Handle NaN values
|
| 1961 |
+
# Handle NaN values
|
| 1962 |
+
r1 = real_values1[i] if not np.isnan(real_values1[i]) else 0
|
| 1963 |
+
r2 = real_values2[i] if not np.isnan(real_values2[i]) else 0
|
| 1964 |
+
r3 = real_values3[i] if not np.isnan(real_values3[i]) else 0
|
| 1965 |
+
|
| 1966 |
+
for r in [r1, r2, r3]:
|
| 1967 |
if abs(r) < 1e-6:
|
| 1968 |
zeros += 1
|
| 1969 |
elif r > 0:
|
|
|
|
| 2101 |
with open(data_file, 'r') as f:
|
| 2102 |
data = json.load(f)
|
| 2103 |
|
| 2104 |
+
# Process data safely
|
| 2105 |
+
z_values = np.array([safe_convert_to_numeric(x) for x in data['z_values']])
|
| 2106 |
+
ims_values1 = np.array([safe_convert_to_numeric(x) for x in data['ims_values1']])
|
| 2107 |
+
ims_values2 = np.array([safe_convert_to_numeric(x) for x in data['ims_values2']])
|
| 2108 |
+
ims_values3 = np.array([safe_convert_to_numeric(x) for x in data['ims_values3']])
|
| 2109 |
|
| 2110 |
# Also extract real parts if available
|
| 2111 |
+
real_values1 = np.array([safe_convert_to_numeric(x) for x in data.get('real_values1', [0] * len(z_values))])
|
| 2112 |
+
real_values2 = np.array([safe_convert_to_numeric(x) for x in data.get('real_values2', [0] * len(z_values))])
|
| 2113 |
+
real_values3 = np.array([safe_convert_to_numeric(x) for x in data.get('real_values3', [0] * len(z_values))])
|
| 2114 |
|
| 2115 |
# Create tabs for previous results
|
| 2116 |
prev_im_tab, prev_real_tab = st.tabs(["Previous Imaginary Parts", "Previous Real Parts"])
|