import streamlit as st import subprocess import os import json import numpy as np import plotly.graph_objects as go from PIL import Image import time import io import sys import tempfile import platform # Set page config with wider layout st.set_page_config( page_title="Matrix Analysis Dashboard", page_icon="πŸ“Š", layout="wide", initial_sidebar_state="expanded" ) # Apply custom CSS for a dashboard-like appearance st.markdown(""" """, unsafe_allow_html=True) # Dashboard Header st.markdown('

Matrix Analysis Dashboard

', unsafe_allow_html=True) # Create output directory in the current working directory current_dir = os.getcwd() output_dir = os.path.join(current_dir, "output") os.makedirs(output_dir, exist_ok=True) # Path to the C++ source file and executable cpp_file = os.path.join(current_dir, "app.cpp") executable = os.path.join(current_dir, "eigen_analysis") if platform.system() == "Windows": executable += ".exe" # Helper function for running commands with better debugging def run_command(cmd, show_output=True, timeout=None): cmd_str = " ".join(cmd) if show_output: st.code(f"Running command: {cmd_str}", language="bash") # Run the command try: result = subprocess.run( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, check=False, timeout=timeout ) if result.returncode == 0: if show_output: st.success("Command completed successfully.") if result.stdout and show_output: with st.expander("Command Output"): st.code(result.stdout) return True, result.stdout, result.stderr else: if show_output: st.error(f"Command failed with return code {result.returncode}") st.error(f"Command: {cmd_str}") st.error(f"Error output: {result.stderr}") return False, result.stdout, result.stderr except subprocess.TimeoutExpired: if show_output: st.error(f"Command timed out after {timeout} seconds") return False, "", f"Command timed out after {timeout} seconds" except Exception as e: if show_output: st.error(f"Error executing command: {str(e)}") return False, "", str(e) # Check if C++ source file exists if not os.path.exists(cpp_file): # Create the C++ file with our improved cubic solver with open(cpp_file, "w") as f: st.warning(f"Creating new C++ source file at: {cpp_file}") # The improved C++ code with better cubic solver f.write(''' // app.cpp - Modified version for command line arguments with improved cubic solver #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Struct to hold cubic equation roots struct CubicRoots { std::complex root1; std::complex root2; std::complex root3; }; // Function to solve cubic equation: az^3 + bz^2 + cz + d = 0 // Improved to properly handle zero roots and classification of positive/negative CubicRoots solveCubic(double a, double b, double c, double d) { // Constants for numerical stability const double epsilon = 1e-14; const double zero_threshold = 1e-10; // Threshold for considering a value as zero // Handle special case for a == 0 (quadratic) if (std::abs(a) < epsilon) { CubicRoots roots; // For a quadratic equation: bz^2 + cz + d = 0 if (std::abs(b) < epsilon) { // Linear equation or constant if (std::abs(c) < epsilon) { // Constant - no finite roots roots.root1 = std::complex(std::numeric_limits::quiet_NaN(), 0.0); roots.root2 = std::complex(std::numeric_limits::quiet_NaN(), 0.0); roots.root3 = std::complex(std::numeric_limits::quiet_NaN(), 0.0); } else { // Linear equation roots.root1 = std::complex(-d / c, 0.0); roots.root2 = std::complex(std::numeric_limits::infinity(), 0.0); roots.root3 = std::complex(std::numeric_limits::infinity(), 0.0); } return roots; } double discriminant = c * c - 4.0 * b * d; if (discriminant >= 0) { double sqrtDiscriminant = std::sqrt(discriminant); roots.root1 = std::complex((-c + sqrtDiscriminant) / (2.0 * b), 0.0); roots.root2 = std::complex((-c - sqrtDiscriminant) / (2.0 * b), 0.0); roots.root3 = std::complex(std::numeric_limits::infinity(), 0.0); } else { double real = -c / (2.0 * b); double imag = std::sqrt(-discriminant) / (2.0 * b); roots.root1 = std::complex(real, imag); roots.root2 = std::complex(real, -imag); roots.root3 = std::complex(std::numeric_limits::infinity(), 0.0); } return roots; } // Handle special case when d is zero - one root is zero if (std::abs(d) < epsilon) { // Factor out z: z(az^2 + bz + c) = 0 CubicRoots roots; roots.root1 = std::complex(0.0, 0.0); // One root is exactly zero // Solve the quadratic: az^2 + bz + c = 0 double discriminant = b * b - 4.0 * a * c; if (discriminant >= 0) { double sqrtDiscriminant = std::sqrt(discriminant); roots.root2 = std::complex((-b + sqrtDiscriminant) / (2.0 * a), 0.0); roots.root3 = std::complex((-b - sqrtDiscriminant) / (2.0 * a), 0.0); } else { double real = -b / (2.0 * a); double imag = std::sqrt(-discriminant) / (2.0 * a); roots.root2 = std::complex(real, imag); roots.root3 = std::complex(real, -imag); } return roots; } // Normalize equation: z^3 + (b/a)z^2 + (c/a)z + (d/a) = 0 double p = b / a; double q = c / a; double r = d / a; // Substitute z = t - p/3 to get t^3 + pt^2 + qt + r = 0 double p1 = q - p * p / 3.0; double q1 = r - p * q / 3.0 + 2.0 * p * p * p / 27.0; // Calculate discriminant double D = q1 * q1 / 4.0 + p1 * p1 * p1 / 27.0; // Precompute values const double two_pi = 2.0 * M_PI; const double third = 1.0 / 3.0; const double p_over_3 = p / 3.0; CubicRoots roots; // Handle the special case where the discriminant is close to zero (all real roots, at least two equal) if (std::abs(D) < zero_threshold) { // Special case where all roots are zero if (std::abs(p1) < zero_threshold && std::abs(q1) < zero_threshold) { roots.root1 = std::complex(-p_over_3, 0.0); roots.root2 = std::complex(-p_over_3, 0.0); roots.root3 = std::complex(-p_over_3, 0.0); return roots; } // General case for D β‰ˆ 0 double u = std::cbrt(-q1 / 2.0); // Real cube root roots.root1 = std::complex(2.0 * u - p_over_3, 0.0); roots.root2 = std::complex(-u - p_over_3, 0.0); roots.root3 = roots.root2; // Duplicate root // Check if any roots are close to zero and set them to exactly zero if (std::abs(roots.root1.real()) < zero_threshold) roots.root1 = std::complex(0.0, 0.0); if (std::abs(roots.root2.real()) < zero_threshold) { roots.root2 = std::complex(0.0, 0.0); roots.root3 = std::complex(0.0, 0.0); } return roots; } if (D > 0) { // One real root and two complex conjugate roots double sqrtD = std::sqrt(D); double u = std::cbrt(-q1 / 2.0 + sqrtD); double v = std::cbrt(-q1 / 2.0 - sqrtD); // Real root roots.root1 = std::complex(u + v - p_over_3, 0.0); // Complex conjugate roots double real_part = -(u + v) / 2.0 - p_over_3; double imag_part = (u - v) * std::sqrt(3.0) / 2.0; roots.root2 = std::complex(real_part, imag_part); roots.root3 = std::complex(real_part, -imag_part); // Check if any roots are close to zero and set them to exactly zero if (std::abs(roots.root1.real()) < zero_threshold) roots.root1 = std::complex(0.0, 0.0); return roots; } else { // Three distinct real roots double angle = std::acos(-q1 / 2.0 * std::sqrt(-27.0 / (p1 * p1 * p1))); double magnitude = 2.0 * std::sqrt(-p1 / 3.0); // Calculate all three real roots roots.root1 = std::complex(magnitude * std::cos(angle / 3.0) - p_over_3, 0.0); roots.root2 = std::complex(magnitude * std::cos((angle + two_pi) / 3.0) - p_over_3, 0.0); roots.root3 = std::complex(magnitude * std::cos((angle + 2.0 * two_pi) / 3.0) - p_over_3, 0.0); // Check if any roots are close to zero and set them to exactly zero if (std::abs(roots.root1.real()) < zero_threshold) roots.root1 = std::complex(0.0, 0.0); if (std::abs(roots.root2.real()) < zero_threshold) roots.root2 = std::complex(0.0, 0.0); if (std::abs(roots.root3.real()) < zero_threshold) roots.root3 = std::complex(0.0, 0.0); return roots; } } // Function to compute the cubic equation for Im(s) vs z std::vector> computeImSVsZ(double a, double y, double beta, int num_points) { std::vector z_values(num_points); std::vector ims_values1(num_points); std::vector ims_values2(num_points); std::vector ims_values3(num_points); std::vector real_values1(num_points); std::vector real_values2(num_points); std::vector real_values3(num_points); // Generate z values from 0.01 to 10 (or adjust range as needed) double z_start = 0.01; // Avoid z=0 to prevent potential division issues double z_end = 10.0; double z_step = (z_end - z_start) / (num_points - 1); for (int i = 0; i < num_points; ++i) { double z = z_start + i * z_step; z_values[i] = z; // Coefficients for the cubic equation: // zasΒ³ + [z(a+1)+a(1-y)]sΒ² + [z+(a+1)-y-yΞ²(a-1)]s + 1 = 0 double coef_a = z * a; double coef_b = z * (a + 1) + a * (1 - y); double coef_c = z + (a + 1) - y - y * beta * (a - 1); double coef_d = 1.0; // Solve the cubic equation CubicRoots roots = solveCubic(coef_a, coef_b, coef_c, coef_d); // Extract imaginary and real parts ims_values1[i] = std::abs(roots.root1.imag()); ims_values2[i] = std::abs(roots.root2.imag()); ims_values3[i] = std::abs(roots.root3.imag()); real_values1[i] = roots.root1.real(); real_values2[i] = roots.root2.real(); real_values3[i] = roots.root3.real(); } // Create output vector, now including real values for better analysis std::vector> result = { z_values, ims_values1, ims_values2, ims_values3, real_values1, real_values2, real_values3 }; return result; } // Function to save Im(s) vs z data as JSON bool saveImSDataAsJSON(const std::string& filename, const std::vector>& data) { std::ofstream outfile(filename); if (!outfile.is_open()) { std::cerr << "Error: Could not open file " << filename << " for writing." << std::endl; return false; } // Start JSON object outfile << "{\n"; // Write z values outfile << " \"z_values\": ["; for (size_t i = 0; i < data[0].size(); ++i) { outfile << data[0][i]; if (i < data[0].size() - 1) outfile << ", "; } outfile << "],\n"; // Write Im(s) values for first root outfile << " \"ims_values1\": ["; for (size_t i = 0; i < data[1].size(); ++i) { outfile << data[1][i]; if (i < data[1].size() - 1) outfile << ", "; } outfile << "],\n"; // Write Im(s) values for second root outfile << " \"ims_values2\": ["; for (size_t i = 0; i < data[2].size(); ++i) { outfile << data[2][i]; if (i < data[2].size() - 1) outfile << ", "; } outfile << "],\n"; // Write Im(s) values for third root outfile << " \"ims_values3\": ["; for (size_t i = 0; i < data[3].size(); ++i) { outfile << data[3][i]; if (i < data[3].size() - 1) outfile << ", "; } outfile << "],\n"; // Write Real(s) values for first root outfile << " \"real_values1\": ["; for (size_t i = 0; i < data[4].size(); ++i) { outfile << data[4][i]; if (i < data[4].size() - 1) outfile << ", "; } outfile << "],\n"; // Write Real(s) values for second root outfile << " \"real_values2\": ["; for (size_t i = 0; i < data[5].size(); ++i) { outfile << data[5][i]; if (i < data[5].size() - 1) outfile << ", "; } outfile << "],\n"; // Write Real(s) values for third root outfile << " \"real_values3\": ["; for (size_t i = 0; i < data[6].size(); ++i) { outfile << data[6][i]; if (i < data[6].size() - 1) outfile << ", "; } outfile << "]\n"; // Close JSON object outfile << "}\n"; outfile.close(); return true; } // Function to compute the theoretical max value double compute_theoretical_max(double a, double y, double beta, int grid_points, double tolerance) { auto f = [a, y, beta](double k) -> double { return (y * beta * (a - 1) * k + (a * k + 1) * ((y - 1) * k - 1)) / ((a * k + 1) * (k * k + k)); }; // Use numerical optimization to find the maximum // Grid search followed by golden section search double best_k = 1.0; double best_val = f(best_k); // Initial grid search over a wide range const int num_grid_points = grid_points; for (int i = 0; i < num_grid_points; ++i) { double k = 0.01 + 100.0 * i / (num_grid_points - 1); // From 0.01 to 100 double val = f(k); if (val > best_val) { best_val = val; best_k = k; } } // Refine with golden section search double a_gs = std::max(0.01, best_k / 10.0); double b_gs = best_k * 10.0; const double golden_ratio = (1.0 + std::sqrt(5.0)) / 2.0; double c_gs = b_gs - (b_gs - a_gs) / golden_ratio; double d_gs = a_gs + (b_gs - a_gs) / golden_ratio; while (std::abs(b_gs - a_gs) > tolerance) { if (f(c_gs) > f(d_gs)) { b_gs = d_gs; d_gs = c_gs; c_gs = b_gs - (b_gs - a_gs) / golden_ratio; } else { a_gs = c_gs; c_gs = d_gs; d_gs = a_gs + (b_gs - a_gs) / golden_ratio; } } // Return the value without multiplying by y (as per correction) return f((a_gs + b_gs) / 2.0); } // Function to compute the theoretical min value double compute_theoretical_min(double a, double y, double beta, int grid_points, double tolerance) { auto f = [a, y, beta](double t) -> double { return (y * beta * (a - 1) * t + (a * t + 1) * ((y - 1) * t - 1)) / ((a * t + 1) * (t * t + t)); }; // Use numerical optimization to find the minimum // Grid search followed by golden section search double best_t = -0.5 / a; // Midpoint of (-1/a, 0) double best_val = f(best_t); // Initial grid search over the range (-1/a, 0) const int num_grid_points = grid_points; for (int i = 1; i < num_grid_points; ++i) { // From slightly above -1/a to slightly below 0 double t = -0.999/a + 0.998/a * i / (num_grid_points - 1); if (t >= 0 || t <= -1.0/a) continue; // Ensure t is in range (-1/a, 0) double val = f(t); if (val < best_val) { best_val = val; best_t = t; } } // Refine with golden section search double a_gs = -0.999/a; // Slightly above -1/a double b_gs = -0.001/a; // Slightly below 0 const double golden_ratio = (1.0 + std::sqrt(5.0)) / 2.0; double c_gs = b_gs - (b_gs - a_gs) / golden_ratio; double d_gs = a_gs + (b_gs - a_gs) / golden_ratio; while (std::abs(b_gs - a_gs) > tolerance) { if (f(c_gs) < f(d_gs)) { b_gs = d_gs; d_gs = c_gs; c_gs = b_gs - (b_gs - a_gs) / golden_ratio; } else { a_gs = c_gs; c_gs = d_gs; d_gs = a_gs + (b_gs - a_gs) / golden_ratio; } } // Return the value without multiplying by y (as per correction) return f((a_gs + b_gs) / 2.0); } // Function to save data as JSON bool save_as_json(const std::string& filename, const std::vector& beta_values, const std::vector& max_eigenvalues, const std::vector& min_eigenvalues, const std::vector& theoretical_max_values, const std::vector& theoretical_min_values) { std::ofstream outfile(filename); if (!outfile.is_open()) { std::cerr << "Error: Could not open file " << filename << " for writing." << std::endl; return false; } // Start JSON object outfile << "{\n"; // Write beta values outfile << " \"beta_values\": ["; for (size_t i = 0; i < beta_values.size(); ++i) { outfile << beta_values[i]; if (i < beta_values.size() - 1) outfile << ", "; } outfile << "],\n"; // Write max eigenvalues outfile << " \"max_eigenvalues\": ["; for (size_t i = 0; i < max_eigenvalues.size(); ++i) { outfile << max_eigenvalues[i]; if (i < max_eigenvalues.size() - 1) outfile << ", "; } outfile << "],\n"; // Write min eigenvalues outfile << " \"min_eigenvalues\": ["; for (size_t i = 0; i < min_eigenvalues.size(); ++i) { outfile << min_eigenvalues[i]; if (i < min_eigenvalues.size() - 1) outfile << ", "; } outfile << "],\n"; // Write theoretical max values outfile << " \"theoretical_max\": ["; for (size_t i = 0; i < theoretical_max_values.size(); ++i) { outfile << theoretical_max_values[i]; if (i < theoretical_max_values.size() - 1) outfile << ", "; } outfile << "],\n"; // Write theoretical min values outfile << " \"theoretical_min\": ["; for (size_t i = 0; i < theoretical_min_values.size(); ++i) { outfile << theoretical_min_values[i]; if (i < theoretical_min_values.size() - 1) outfile << ", "; } outfile << "]\n"; // Close JSON object outfile << "}\n"; outfile.close(); return true; } // Eigenvalue analysis function bool eigenvalueAnalysis(int n, int p, double a, double y, int fineness, int theory_grid_points, double theory_tolerance, const std::string& output_file) { std::cout << "Running eigenvalue analysis with parameters: n = " << n << ", p = " << p << ", a = " << a << ", y = " << y << ", fineness = " << fineness << ", theory_grid_points = " << theory_grid_points << ", theory_tolerance = " << theory_tolerance << std::endl; std::cout << "Output will be saved to: " << output_file << std::endl; // ─── Beta range parameters ──────────────────────────────────────── const int num_beta_points = fineness; // Controlled by fineness parameter std::vector beta_values(num_beta_points); for (int i = 0; i < num_beta_points; ++i) { beta_values[i] = static_cast(i) / (num_beta_points - 1); } // ─── Storage for results ──────────────────────────────────────── std::vector max_eigenvalues(num_beta_points); std::vector min_eigenvalues(num_beta_points); std::vector theoretical_max_values(num_beta_points); std::vector theoretical_min_values(num_beta_points); try { // ─── Random‐Gaussian X and S_n ──────────────────────────────── std::random_device rd; std::mt19937_64 rng{rd()}; std::normal_distribution norm(0.0, 1.0); cv::Mat X(p, n, CV_64F); for(int i = 0; i < p; ++i) for(int j = 0; j < n; ++j) X.at(i,j) = norm(rng); // ─── Process each beta value ───────────────────────────────── for (int beta_idx = 0; beta_idx < num_beta_points; ++beta_idx) { double beta = beta_values[beta_idx]; // Compute theoretical values with customizable precision theoretical_max_values[beta_idx] = compute_theoretical_max(a, y, beta, theory_grid_points, theory_tolerance); theoretical_min_values[beta_idx] = compute_theoretical_min(a, y, beta, theory_grid_points, theory_tolerance); // ─── Build T_n matrix ────────────────────────────────── int k = static_cast(std::floor(beta * p)); std::vector diags(p, 1.0); std::fill_n(diags.begin(), k, a); std::shuffle(diags.begin(), diags.end(), rng); cv::Mat T_n = cv::Mat::zeros(p, p, CV_64F); for(int i = 0; i < p; ++i){ T_n.at(i,i) = diags[i]; } // ─── Form B_n = (1/n) * X * T_n * X^T ──────────── cv::Mat B = (X.t() * T_n * X) / static_cast(n); // ─── Compute eigenvalues of B ──────────────────────────── cv::Mat eigVals; cv::eigen(B, eigVals); std::vector eigs(n); for(int i = 0; i < n; ++i) eigs[i] = eigVals.at(i, 0); max_eigenvalues[beta_idx] = *std::max_element(eigs.begin(), eigs.end()); min_eigenvalues[beta_idx] = *std::min_element(eigs.begin(), eigs.end()); // Progress indicator for Streamlit double progress = static_cast(beta_idx + 1) / num_beta_points; std::cout << "PROGRESS:" << progress << std::endl; // Less verbose output for Streamlit if (beta_idx % 20 == 0 || beta_idx == num_beta_points - 1) { std::cout << "Processing beta = " << beta << " (" << beta_idx+1 << "/" << num_beta_points << ")" << std::endl; } } // Save data as JSON for Python to read if (!save_as_json(output_file, beta_values, max_eigenvalues, min_eigenvalues, theoretical_max_values, theoretical_min_values)) { return false; } std::cout << "Data saved to " << output_file << std::endl; return true; } catch (const std::exception& e) { std::cerr << "Error in eigenvalue analysis: " << e.what() << std::endl; return false; } catch (...) { std::cerr << "Unknown error in eigenvalue analysis" << std::endl; return false; } } // Cubic equation analysis function bool cubicAnalysis(double a, double y, double beta, int num_points, const std::string& output_file) { std::cout << "Running cubic equation analysis with parameters: a = " << a << ", y = " << y << ", beta = " << beta << ", num_points = " << num_points << std::endl; std::cout << "Output will be saved to: " << output_file << std::endl; try { // Compute Im(s) vs z data std::vector> ims_data = computeImSVsZ(a, y, beta, num_points); // Save to JSON if (!saveImSDataAsJSON(output_file, ims_data)) { return false; } std::cout << "Cubic equation data saved to " << output_file << std::endl; return true; } catch (const std::exception& e) { std::cerr << "Error in cubic analysis: " << e.what() << std::endl; return false; } catch (...) { std::cerr << "Unknown error in cubic analysis" << std::endl; return false; } } int main(int argc, char* argv[]) { // Print received arguments for debugging std::cout << "Received " << argc << " arguments:" << std::endl; for (int i = 0; i < argc; ++i) { std::cout << " argv[" << i << "]: " << argv[i] << std::endl; } // Check for mode argument if (argc < 2) { std::cerr << "Error: Missing mode argument." << std::endl; std::cerr << "Usage: " << argv[0] << " eigenvalues

" << std::endl; std::cerr << " or: " << argv[0] << " cubic " << std::endl; return 1; } std::string mode = argv[1]; try { if (mode == "eigenvalues") { // ─── Eigenvalue analysis mode ─────────────────────────────────────────── if (argc != 10) { std::cerr << "Error: Incorrect number of arguments for eigenvalues mode." << std::endl; std::cerr << "Usage: " << argv[0] << " eigenvalues

" << std::endl; std::cerr << "Received " << argc << " arguments, expected 10." << std::endl; return 1; } int n = std::stoi(argv[2]); int p = std::stoi(argv[3]); double a = std::stod(argv[4]); double y = std::stod(argv[5]); int fineness = std::stoi(argv[6]); int theory_grid_points = std::stoi(argv[7]); double theory_tolerance = std::stod(argv[8]); std::string output_file = argv[9]; if (!eigenvalueAnalysis(n, p, a, y, fineness, theory_grid_points, theory_tolerance, output_file)) { return 1; } } else if (mode == "cubic") { // ─── Cubic equation analysis mode ─────────────────────────────────────────── if (argc != 7) { std::cerr << "Error: Incorrect number of arguments for cubic mode." << std::endl; std::cerr << "Usage: " << argv[0] << " cubic " << std::endl; std::cerr << "Received " << argc << " arguments, expected 7." << std::endl; return 1; } double a = std::stod(argv[2]); double y = std::stod(argv[3]); double beta = std::stod(argv[4]); int num_points = std::stoi(argv[5]); std::string output_file = argv[6]; if (!cubicAnalysis(a, y, beta, num_points, output_file)) { return 1; } } else { std::cerr << "Error: Unknown mode: " << mode << std::endl; std::cerr << "Use 'eigenvalues' or 'cubic'" << std::endl; return 1; } } catch (const std::exception& e) { std::cerr << "Error: " << e.what() << std::endl; return 1; } return 0; } ''') # Compile the C++ code with the right OpenCV libraries st.sidebar.title("Compiler Settings") need_compile = not os.path.exists(executable) or st.sidebar.button("Recompile C++ Code") if need_compile: with st.sidebar: with st.spinner("Compiling C++ code..."): # Try to detect the OpenCV installation opencv_detection_cmd = ["pkg-config", "--cflags", "--libs", "opencv4"] opencv_found, opencv_flags, _ = run_command(opencv_detection_cmd, show_output=False) compile_commands = [] if opencv_found: compile_commands.append( f"g++ -o {executable} {cpp_file} {opencv_flags.strip()} -std=c++11" ) else: # Try different OpenCV configurations compile_commands = [ f"g++ -o {executable} {cpp_file} `pkg-config --cflags --libs opencv4` -std=c++11", f"g++ -o {executable} {cpp_file} `pkg-config --cflags --libs opencv` -std=c++11", f"g++ -o {executable} {cpp_file} -I/usr/include/opencv4 -lopencv_core -lopencv_imgproc -std=c++11", f"g++ -o {executable} {cpp_file} -I/usr/local/include/opencv4 -lopencv_core -lopencv_imgproc -std=c++11" ] compiled = False compile_output = "" for cmd in compile_commands: st.text(f"Trying: {cmd}") success, stdout, stderr = run_command(cmd.split(), show_output=False) compile_output += f"Command: {cmd}\nOutput: {stdout}\nError: {stderr}\n\n" if success: compiled = True st.success(f"Successfully compiled with: {cmd}") break if not compiled: st.error("All compilation attempts failed.") with st.expander("Compilation Details"): st.code(compile_output) st.stop() # Make sure the executable is executable if platform.system() != "Windows": os.chmod(executable, 0o755) st.success("C++ code compiled successfully!") # Create tabs for different analyses tab1, tab2 = st.tabs(["Eigenvalue Analysis", "Im(s) vs z Analysis"]) # Tab 1: Eigenvalue Analysis with tab1: # Two-column layout for the dashboard left_column, right_column = st.columns([1, 3]) with left_column: st.markdown('

', unsafe_allow_html=True) st.markdown('
Eigenvalue Analysis Controls
', unsafe_allow_html=True) # Parameter inputs with defaults and validation st.markdown('
', unsafe_allow_html=True) st.markdown("### Matrix Parameters") n = st.number_input("Sample size (n)", min_value=5, max_value=1000, value=100, step=5, help="Number of samples", key="eig_n") p = st.number_input("Dimension (p)", min_value=5, max_value=1000, value=50, step=5, help="Dimensionality", key="eig_p") a = st.number_input("Value for a", min_value=1.1, max_value=10.0, value=2.0, step=0.1, help="Parameter a > 1", key="eig_a") # Automatically calculate y = p/n (as requested) y = p/n st.info(f"Value for y = p/n: {y:.4f}") st.markdown('
', unsafe_allow_html=True) st.markdown('
', unsafe_allow_html=True) st.markdown("### Calculation Controls") fineness = st.slider( "Beta points", min_value=20, max_value=500, value=100, step=10, help="Number of points to calculate along the Ξ² axis (0 to 1)", key="eig_fineness" ) st.markdown('
', unsafe_allow_html=True) with st.expander("Advanced Settings"): # Add controls for theoretical calculation precision theory_grid_points = st.slider( "Theoretical grid points", min_value=100, max_value=1000, value=200, step=50, help="Number of points in initial grid search for theoretical calculations", key="eig_grid_points" ) theory_tolerance = st.number_input( "Theoretical tolerance", min_value=1e-12, max_value=1e-6, value=1e-10, format="%.1e", help="Convergence tolerance for golden section search", key="eig_tolerance" ) # Debug mode debug_mode = st.checkbox("Debug Mode", value=False, key="eig_debug") # Timeout setting timeout_seconds = st.number_input( "Computation timeout (seconds)", min_value=30, max_value=3600, value=300, help="Maximum time allowed for computation before timeout", key="eig_timeout" ) # Generate button eig_generate_button = st.button("Generate Eigenvalue Analysis", type="primary", use_container_width=True, key="eig_generate") st.markdown('
', unsafe_allow_html=True) with right_column: # Main visualization area st.markdown('
', unsafe_allow_html=True) st.markdown('
Eigenvalue Analysis Results
', unsafe_allow_html=True) # Container for the analysis results eig_results_container = st.container() # Process when generate button is clicked if eig_generate_button: with eig_results_container: # Show progress progress_container = st.container() with progress_container: progress_bar = st.progress(0) status_text = st.empty() try: # Create data file path data_file = os.path.join(output_dir, "eigenvalue_data.json") # Delete previous output if exists if os.path.exists(data_file): os.remove(data_file) # Build command for eigenvalue analysis with the proper arguments cmd = [ executable, "eigenvalues", # Mode argument str(n), str(p), str(a), str(y), str(fineness), str(theory_grid_points), str(theory_tolerance), data_file ] # Run the command status_text.text("Running eigenvalue analysis...") if debug_mode: success, stdout, stderr = run_command(cmd, True, timeout=timeout_seconds) # Process stdout for progress updates if success: progress_bar.progress(1.0) else: # Start the process with pipe for stdout to read progress process = subprocess.Popen( cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True, bufsize=1, universal_newlines=True ) # Track progress from stdout success = True stdout_lines = [] start_time = time.time() while True: # Check for timeout if time.time() - start_time > timeout_seconds: process.kill() status_text.error(f"Computation timed out after {timeout_seconds} seconds") success = False break # Try to read a line (non-blocking) line = process.stdout.readline() if not line and process.poll() is not None: break if line: stdout_lines.append(line) if line.startswith("PROGRESS:"): try: # Update progress bar progress_value = float(line.split(":")[1].strip()) progress_bar.progress(progress_value) status_text.text(f"Calculating... {int(progress_value * 100)}% complete") except: pass elif line: status_text.text(line.strip()) # Get the return code and stderr returncode = process.poll() stderr = process.stderr.read() if returncode != 0: success = False st.error(f"Error executing the analysis: {stderr}") with st.expander("Error Details"): st.code(stderr) if success: progress_bar.progress(1.0) status_text.text("Analysis complete! Generating visualization...") # Check if the output file was created if not os.path.exists(data_file): st.error(f"Output file not created: {data_file}") st.stop() try: # Load the results from the JSON file with open(data_file, 'r') as f: data = json.load(f) # Extract data beta_values = np.array(data['beta_values']) max_eigenvalues = np.array(data['max_eigenvalues']) min_eigenvalues = np.array(data['min_eigenvalues']) theoretical_max = np.array(data['theoretical_max']) theoretical_min = np.array(data['theoretical_min']) # Create an interactive plot using Plotly fig = go.Figure() # Add traces for each line fig.add_trace(go.Scatter( x=beta_values, y=max_eigenvalues, mode='lines+markers', name='Empirical Max Eigenvalue', line=dict(color='rgb(220, 60, 60)', width=3), marker=dict( symbol='circle', size=8, color='rgb(220, 60, 60)', line=dict(color='white', width=1) ), hovertemplate='Ξ²: %{x:.3f}
Value: %{y:.6f}Empirical Max' )) fig.add_trace(go.Scatter( x=beta_values, y=min_eigenvalues, mode='lines+markers', name='Empirical Min Eigenvalue', line=dict(color='rgb(60, 60, 220)', width=3), marker=dict( symbol='circle', size=8, color='rgb(60, 60, 220)', line=dict(color='white', width=1) ), hovertemplate='Ξ²: %{x:.3f}
Value: %{y:.6f}Empirical Min' )) fig.add_trace(go.Scatter( x=beta_values, y=theoretical_max, mode='lines+markers', name='Theoretical Max Function', line=dict(color='rgb(30, 180, 30)', width=3), marker=dict( symbol='diamond', size=8, color='rgb(30, 180, 30)', line=dict(color='white', width=1) ), hovertemplate='Ξ²: %{x:.3f}
Value: %{y:.6f}Theoretical Max' )) fig.add_trace(go.Scatter( x=beta_values, y=theoretical_min, mode='lines+markers', name='Theoretical Min Function', line=dict(color='rgb(180, 30, 180)', width=3), marker=dict( symbol='diamond', size=8, color='rgb(180, 30, 180)', line=dict(color='white', width=1) ), hovertemplate='Ξ²: %{x:.3f}
Value: %{y:.6f}Theoretical Min' )) # Configure layout for better appearance fig.update_layout( title={ 'text': f'Eigenvalue Analysis: n={n}, p={p}, a={a}, y={y:.4f}', 'font': {'size': 24, 'color': '#1E88E5'}, 'y': 0.95, 'x': 0.5, 'xanchor': 'center', 'yanchor': 'top' }, xaxis={ 'title': {'text': 'β Parameter', 'font': {'size': 18, 'color': '#424242'}}, 'tickfont': {'size': 14}, 'gridcolor': 'rgba(220, 220, 220, 0.5)', 'showgrid': True }, yaxis={ 'title': {'text': 'Eigenvalues', 'font': {'size': 18, 'color': '#424242'}}, 'tickfont': {'size': 14}, 'gridcolor': 'rgba(220, 220, 220, 0.5)', 'showgrid': True }, plot_bgcolor='rgba(240, 240, 240, 0.8)', paper_bgcolor='rgba(249, 249, 249, 0.8)', hovermode='closest', legend={ 'font': {'size': 14}, 'bgcolor': 'rgba(255, 255, 255, 0.9)', 'bordercolor': 'rgba(200, 200, 200, 0.5)', 'borderwidth': 1 }, margin={'l': 60, 'r': 30, 't': 100, 'b': 60}, height=600, annotations=[ { 'text': f"Max Function: max{{k ∈ (0,∞)}} [yβ(a-1)k + (ak+1)((y-1)k-1)]/[(ak+1)(k²+k)]", 'xref': 'paper', 'yref': 'paper', 'x': 0.02, 'y': 0.02, 'showarrow': False, 'font': {'size': 12, 'color': 'rgb(30, 180, 30)'}, 'bgcolor': 'rgba(255, 255, 255, 0.9)', 'bordercolor': 'rgb(30, 180, 30)', 'borderwidth': 1, 'borderpad': 4 }, { 'text': f"Min Function: min{{t ∈ (-1/a,0)}} [yβ(a-1)t + (at+1)((y-1)t-1)]/[(at+1)(t²+t)]", 'xref': 'paper', 'yref': 'paper', 'x': 0.55, 'y': 0.02, 'showarrow': False, 'font': {'size': 12, 'color': 'rgb(180, 30, 180)'}, 'bgcolor': 'rgba(255, 255, 255, 0.9)', 'bordercolor': 'rgb(180, 30, 180)', 'borderwidth': 1, 'borderpad': 4 } ] ) # Add custom modebar buttons fig.update_layout( modebar_add=[ 'drawline', 'drawopenpath', 'drawclosedpath', 'drawcircle', 'drawrect', 'eraseshape' ], modebar_remove=['lasso2d', 'select2d'], dragmode='zoom' ) # Clear progress container progress_container.empty() # Display the interactive plot in Streamlit st.plotly_chart(fig, use_container_width=True) # Display statistics with st.expander("Statistics"): col1, col2 = st.columns(2) with col1: st.write("### Eigenvalue Statistics") st.write(f"Max empirical value: {max_eigenvalues.max():.6f}") st.write(f"Min empirical value: {min_eigenvalues.min():.6f}") with col2: st.write("### Theoretical Values") st.write(f"Max theoretical value: {theoretical_max.max():.6f}") st.write(f"Min theoretical value: {theoretical_min.min():.6f}") except json.JSONDecodeError as e: st.error(f"Error parsing JSON results: {str(e)}") if os.path.exists(data_file): with open(data_file, 'r') as f: content = f.read() st.code(content[:1000] + "..." if len(content) > 1000 else content) except Exception as e: st.error(f"An error occurred: {str(e)}") if debug_mode: st.exception(e) else: # Try to load existing data if available data_file = os.path.join(output_dir, "eigenvalue_data.json") if os.path.exists(data_file): try: with open(data_file, 'r') as f: data = json.load(f) # Extract data beta_values = np.array(data['beta_values']) max_eigenvalues = np.array(data['max_eigenvalues']) min_eigenvalues = np.array(data['min_eigenvalues']) theoretical_max = np.array(data['theoretical_max']) theoretical_min = np.array(data['theoretical_min']) # Create an interactive plot using Plotly fig = go.Figure() # Add traces for each line fig.add_trace(go.Scatter( x=beta_values, y=max_eigenvalues, mode='lines+markers', name='Empirical Max Eigenvalue', line=dict(color='rgb(220, 60, 60)', width=3), marker=dict( symbol='circle', size=8, color='rgb(220, 60, 60)', line=dict(color='white', width=1) ), hovertemplate='β: %{x:.3f}
Value: %{y:.6f}Empirical Max' )) fig.add_trace(go.Scatter( x=beta_values, y=min_eigenvalues, mode='lines+markers', name='Empirical Min Eigenvalue', line=dict(color='rgb(60, 60, 220)', width=3), marker=dict( symbol='circle', size=8, color='rgb(60, 60, 220)', line=dict(color='white', width=1) ), hovertemplate='Ξ²: %{x:.3f}
Value: %{y:.6f}Empirical Min' )) fig.add_trace(go.Scatter( x=beta_values, y=theoretical_max, mode='lines+markers', name='Theoretical Max Function', line=dict(color='rgb(30, 180, 30)', width=3), marker=dict( symbol='diamond', size=8, color='rgb(30, 180, 30)', line=dict(color='white', width=1) ), hovertemplate='Ξ²: %{x:.3f}
Value: %{y:.6f}Theoretical Max' )) fig.add_trace(go.Scatter( x=beta_values, y=theoretical_min, mode='lines+markers', name='Theoretical Min Function', line=dict(color='rgb(180, 30, 180)', width=3), marker=dict( symbol='diamond', size=8, color='rgb(180, 30, 180)', line=dict(color='white', width=1) ), hovertemplate='Ξ²: %{x:.3f}
Value: %{y:.6f}Theoretical Min' )) # Configure layout for better appearance fig.update_layout( title={ 'text': f'Eigenvalue Analysis (Previous Result)', 'font': {'size': 24, 'color': '#1E88E5'}, 'y': 0.95, 'x': 0.5, 'xanchor': 'center', 'yanchor': 'top' }, xaxis={ 'title': {'text': 'Ξ² Parameter', 'font': {'size': 18, 'color': '#424242'}}, 'tickfont': {'size': 14}, 'gridcolor': 'rgba(220, 220, 220, 0.5)', 'showgrid': True }, yaxis={ 'title': {'text': 'Eigenvalues', 'font': {'size': 18, 'color': '#424242'}}, 'tickfont': {'size': 14}, 'gridcolor': 'rgba(220, 220, 220, 0.5)', 'showgrid': True }, plot_bgcolor='rgba(240, 240, 240, 0.8)', paper_bgcolor='rgba(249, 249, 249, 0.8)', hovermode='closest', legend={ 'font': {'size': 14}, 'bgcolor': 'rgba(255, 255, 255, 0.9)', 'bordercolor': 'rgba(200, 200, 200, 0.5)', 'borderwidth': 1 }, margin={'l': 60, 'r': 30, 't': 100, 'b': 60}, height=600 ) # Display the interactive plot in Streamlit st.plotly_chart(fig, use_container_width=True) st.info("This is the previous analysis result. Adjust parameters and click 'Generate Analysis' to create a new visualization.") except Exception as e: st.info("πŸ‘ˆ Set parameters and click 'Generate Eigenvalue Analysis' to create a visualization.") else: # Show placeholder st.info("πŸ‘ˆ Set parameters and click 'Generate Eigenvalue Analysis' to create a visualization.") st.markdown('
', unsafe_allow_html=True) # Tab 2: Im(s) vs z Analysis with tab2: # Two-column layout for the dashboard left_column, right_column = st.columns([1, 3]) with left_column: st.markdown('
', unsafe_allow_html=True) st.markdown('
Im(s) vs z Analysis Controls
', unsafe_allow_html=True) # Parameter inputs with defaults and validation st.markdown('
', unsafe_allow_html=True) st.markdown("### Cubic Equation Parameters") cubic_a = st.number_input("Value for a", min_value=1.1, max_value=10.0, value=2.0, step=0.1, help="Parameter a > 1", key="cubic_a") cubic_y = st.number_input("Value for y", min_value=0.1, max_value=10.0, value=1.0, step=0.1, help="Parameter y > 0", key="cubic_y") cubic_beta = st.number_input("Value for Ξ²", min_value=0.0, max_value=1.0, value=0.5, step=0.05, help="Value between 0 and 1", key="cubic_beta") st.markdown('
', unsafe_allow_html=True) st.markdown('
', unsafe_allow_html=True) st.markdown("### Calculation Controls") cubic_points = st.slider( "Number of z points", min_value=50, max_value=1000, value=300, step=50, help="Number of points to calculate along the z axis", key="cubic_points" ) # Debug mode cubic_debug_mode = st.checkbox("Debug Mode", value=False, key="cubic_debug") # Timeout setting cubic_timeout = st.number_input( "Computation timeout (seconds)", min_value=10, max_value=600, value=60, help="Maximum time allowed for computation before timeout", key="cubic_timeout" ) st.markdown('
', unsafe_allow_html=True) # Show cubic equation st.markdown('
', unsafe_allow_html=True) st.markdown("### Cubic Equation") st.latex(r"zas^3 + [z(a+1)+a(1-y)]\,s^2 + [z+(a+1)-y-y\beta (a-1)]\,s + 1 = 0") st.markdown('
', unsafe_allow_html=True) # Generate button cubic_generate_button = st.button("Generate Im(s) vs z Analysis", type="primary", use_container_width=True, key="cubic_generate") st.markdown('
', unsafe_allow_html=True) with right_column: # Main visualization area st.markdown('
', unsafe_allow_html=True) st.markdown('
Im(s) vs z Analysis Results
', unsafe_allow_html=True) # Container for the analysis results cubic_results_container = st.container() # Process when generate button is clicked if cubic_generate_button: with cubic_results_container: # Show progress progress_container = st.container() with progress_container: status_text = st.empty() status_text.text("Starting cubic equation calculations...") try: # Run the C++ executable with the parameters in JSON output mode data_file = os.path.join(output_dir, "cubic_data.json") # Delete previous output if exists if os.path.exists(data_file): os.remove(data_file) # Build command for cubic equation analysis cmd = [ executable, "cubic", # Mode argument str(cubic_a), str(cubic_y), str(cubic_beta), str(cubic_points), data_file ] # Run the command status_text.text("Calculating Im(s) vs z values...") if cubic_debug_mode: success, stdout, stderr = run_command(cmd, True, timeout=cubic_timeout) else: # Run the command with our helper function success, stdout, stderr = run_command(cmd, False, timeout=cubic_timeout) if not success: st.error(f"Error executing cubic analysis: {stderr}") if success: status_text.text("Calculations complete! Generating visualization...") # Check if the output file was created if not os.path.exists(data_file): st.error(f"Output file not created: {data_file}") st.stop() try: # Load the results from the JSON file with open(data_file, 'r') as f: data = json.load(f) # Extract data z_values = np.array(data['z_values']) ims_values1 = np.array(data['ims_values1']) ims_values2 = np.array(data['ims_values2']) ims_values3 = np.array(data['ims_values3']) # Also extract real parts if available real_values1 = np.array(data.get('real_values1', [0] * len(z_values))) real_values2 = np.array(data.get('real_values2', [0] * len(z_values))) real_values3 = np.array(data.get('real_values3', [0] * len(z_values))) # Create tabs for imaginary and real parts im_tab, real_tab = st.tabs(["Imaginary Parts", "Real Parts"]) # Tab for imaginary parts with im_tab: # Create an interactive plot for imaginary parts im_fig = go.Figure() # Add traces for each root's imaginary part im_fig.add_trace(go.Scatter( x=z_values, y=ims_values1, mode='lines', name='Im(s₁)', line=dict(color='rgb(220, 60, 60)', width=3), hovertemplate='z: %{x:.3f}
Im(s₁): %{y:.6f}Root 1' )) im_fig.add_trace(go.Scatter( x=z_values, y=ims_values2, mode='lines', name='Im(sβ‚‚)', line=dict(color='rgb(60, 60, 220)', width=3), hovertemplate='z: %{x:.3f}
Im(sβ‚‚): %{y:.6f}Root 2' )) im_fig.add_trace(go.Scatter( x=z_values, y=ims_values3, mode='lines', name='Im(s₃)', line=dict(color='rgb(30, 180, 30)', width=3), hovertemplate='z: %{x:.3f}
Im(s₃): %{y:.6f}Root 3' )) # Configure layout for better appearance im_fig.update_layout( title={ 'text': f'Im(s) vs z Analysis: a={cubic_a}, y={cubic_y}, Ξ²={cubic_beta}', 'font': {'size': 24, 'color': '#1E88E5'}, 'y': 0.95, 'x': 0.5, 'xanchor': 'center', 'yanchor': 'top' }, xaxis={ 'title': {'text': 'z (logarithmic scale)', 'font': {'size': 18, 'color': '#424242'}}, 'tickfont': {'size': 14}, 'gridcolor': 'rgba(220, 220, 220, 0.5)', 'showgrid': True, 'type': 'log' # Use logarithmic scale for better visualization }, yaxis={ 'title': {'text': 'Im(s)', 'font': {'size': 18, 'color': '#424242'}}, 'tickfont': {'size': 14}, 'gridcolor': 'rgba(220, 220, 220, 0.5)', 'showgrid': True }, plot_bgcolor='rgba(240, 240, 240, 0.8)', paper_bgcolor='rgba(249, 249, 249, 0.8)', hovermode='closest', legend={ 'font': {'size': 14}, 'bgcolor': 'rgba(255, 255, 255, 0.9)', 'bordercolor': 'rgba(200, 200, 200, 0.5)', 'borderwidth': 1 }, margin={'l': 60, 'r': 30, 't': 100, 'b': 60}, height=500, annotations=[ { '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", 'xref': 'paper', 'yref': 'paper', 'x': 0.5, 'y': 0.02, 'showarrow': False, 'font': {'size': 12, 'color': 'black'}, 'bgcolor': 'rgba(255, 255, 255, 0.9)', 'bordercolor': 'rgba(0, 0, 0, 0.5)', 'borderwidth': 1, 'borderpad': 4, 'align': 'center' } ] ) # Display the interactive plot in Streamlit st.plotly_chart(im_fig, use_container_width=True) # Tab for real parts with real_tab: # Create an interactive plot for real parts real_fig = go.Figure() # Add traces for each root's real part real_fig.add_trace(go.Scatter( x=z_values, y=real_values1, mode='lines', name='Re(s₁)', line=dict(color='rgb(220, 60, 60)', width=3), hovertemplate='z: %{x:.3f}
Re(s₁): %{y:.6f}Root 1' )) real_fig.add_trace(go.Scatter( x=z_values, y=real_values2, mode='lines', name='Re(sβ‚‚)', line=dict(color='rgb(60, 60, 220)', width=3), hovertemplate='z: %{x:.3f}
Re(sβ‚‚): %{y:.6f}Root 2' )) real_fig.add_trace(go.Scatter( x=z_values, y=real_values3, mode='lines', name='Re(s₃)', line=dict(color='rgb(30, 180, 30)', width=3), hovertemplate='z: %{x:.3f}
Re(s₃): %{y:.6f}Root 3' )) # Configure layout for better appearance real_fig.update_layout( title={ 'text': f'Re(s) vs z Analysis: a={cubic_a}, y={cubic_y}, Ξ²={cubic_beta}', 'font': {'size': 24, 'color': '#1E88E5'}, 'y': 0.95, 'x': 0.5, 'xanchor': 'center', 'yanchor': 'top' }, xaxis={ 'title': {'text': 'z (logarithmic scale)', 'font': {'size': 18, 'color': '#424242'}}, 'tickfont': {'size': 14}, 'gridcolor': 'rgba(220, 220, 220, 0.5)', 'showgrid': True, 'type': 'log' # Use logarithmic scale for better visualization }, yaxis={ 'title': {'text': 'Re(s)', 'font': {'size': 18, 'color': '#424242'}}, 'tickfont': {'size': 14}, 'gridcolor': 'rgba(220, 220, 220, 0.5)', 'showgrid': True }, plot_bgcolor='rgba(240, 240, 240, 0.8)', paper_bgcolor='rgba(249, 249, 249, 0.8)', hovermode='closest', legend={ 'font': {'size': 14}, 'bgcolor': 'rgba(255, 255, 255, 0.9)', 'bordercolor': 'rgba(200, 200, 200, 0.5)', 'borderwidth': 1 }, margin={'l': 60, 'r': 30, 't': 100, 'b': 60}, height=500 ) # Display the interactive plot in Streamlit st.plotly_chart(real_fig, use_container_width=True) # Clear progress container progress_container.empty() # Add explanation text st.markdown('
', unsafe_allow_html=True) st.markdown(""" ### Root Pattern Analysis For the cubic equation in this analysis, we observe specific patterns in the roots: - One root typically has negative real part - One root typically has positive real part - One root has zero or near-zero real part The imaginary parts show oscillatory behavior, with some z values producing purely real roots (Im(s) = 0) and others producing complex roots with non-zero imaginary parts. This pattern is consistent with the expected behavior of cubic equations and has important implications for system stability analysis. The imaginary parts represent oscillatory behavior in the system, while the real parts represent exponential growth (positive) or decay (negative). """) st.markdown('
', unsafe_allow_html=True) except json.JSONDecodeError as e: st.error(f"Error parsing JSON results: {str(e)}") if os.path.exists(data_file): with open(data_file, 'r') as f: content = f.read() st.code(content[:1000] + "..." if len(content) > 1000 else content) except Exception as e: st.error(f"An error occurred: {str(e)}") if cubic_debug_mode: st.exception(e) else: # Try to load existing data if available data_file = os.path.join(output_dir, "cubic_data.json") if os.path.exists(data_file): try: with open(data_file, 'r') as f: data = json.load(f) # Extract data z_values = np.array(data['z_values']) ims_values1 = np.array(data['ims_values1']) ims_values2 = np.array(data['ims_values2']) ims_values3 = np.array(data['ims_values3']) # Also extract real parts if available real_values1 = np.array(data.get('real_values1', [0] * len(z_values))) real_values2 = np.array(data.get('real_values2', [0] * len(z_values))) real_values3 = np.array(data.get('real_values3', [0] * len(z_values))) # Show previous results with Imaginary parts fig = go.Figure() # Add traces for each root's imaginary part fig.add_trace(go.Scatter( x=z_values, y=ims_values1, mode='lines', name='Im(s₁)', line=dict(color='rgb(220, 60, 60)', width=3), hovertemplate='z: %{x:.3f}
Im(s₁): %{y:.6f}Root 1' )) fig.add_trace(go.Scatter( x=z_values, y=ims_values2, mode='lines', name='Im(sβ‚‚)', line=dict(color='rgb(60, 60, 220)', width=3), hovertemplate='z: %{x:.3f}
Im(sβ‚‚): %{y:.6f}Root 2' )) fig.add_trace(go.Scatter( x=z_values, y=ims_values3, mode='lines', name='Im(s₃)', line=dict(color='rgb(30, 180, 30)', width=3), hovertemplate='z: %{x:.3f}
Im(s₃): %{y:.6f}Root 3' )) # Configure layout for better appearance fig.update_layout( title={ 'text': f'Im(s) vs z Analysis (Previous Result)', 'font': {'size': 24, 'color': '#1E88E5'}, 'y': 0.95, 'x': 0.5, 'xanchor': 'center', 'yanchor': 'top' }, xaxis={ 'title': {'text': 'z (logarithmic scale)', 'font': {'size': 18, 'color': '#424242'}}, 'tickfont': {'size': 14}, 'gridcolor': 'rgba(220, 220, 220, 0.5)', 'showgrid': True, 'type': 'log' # Use logarithmic scale for better visualization }, yaxis={ 'title': {'text': 'Im(s)', 'font': {'size': 18, 'color': '#424242'}}, 'tickfont': {'size': 14}, 'gridcolor': 'rgba(220, 220, 220, 0.5)', 'showgrid': True }, plot_bgcolor='rgba(240, 240, 240, 0.8)', paper_bgcolor='rgba(249, 249, 249, 0.8)', hovermode='closest', legend={ 'font': {'size': 14}, 'bgcolor': 'rgba(255, 255, 255, 0.9)', 'bordercolor': 'rgba(200, 200, 200, 0.5)', 'borderwidth': 1 }, margin={'l': 60, 'r': 30, 't': 100, 'b': 60}, height=600 ) # Display the interactive plot in Streamlit st.plotly_chart(fig, use_container_width=True) st.info("This is the previous analysis result. Adjust parameters and click 'Generate Analysis' to create a new visualization.") except Exception as e: st.info("πŸ‘ˆ Set parameters and click 'Generate Im(s) vs z Analysis' to create a visualization.") else: # Show placeholder st.info("πŸ‘ˆ Set parameters and click 'Generate Im(s) vs z Analysis' to create a visualization.") st.markdown('
', unsafe_allow_html=True) # Add footer with instructions st.markdown(""" --- ### Instructions for Using the Dashboard 1. **Select a tab** at the top to choose between Eigenvalue Analysis and Im(s) vs z Analysis 2. **Adjust parameters** in the left panel to configure your analysis 3. **Click the Generate button** to run the analysis with the selected parameters 4. **Explore the results** in the interactive plot 5. For the Im(s) vs z Analysis, you can toggle between Imaginary and Real parts to see different aspects of the cubic roots If you encounter any issues with compilation, try clicking the "Recompile C++ Code" button in the sidebar.
This dashboard analyzes the properties of cubic equations and eigenvalues for matrix analysis. 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.
""", unsafe_allow_html=True)