Finish project 2
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
#include "include/helpers.hpp"
|
||||
#include <iostream>
|
||||
#include <armadillo>
|
||||
|
||||
using namespace std;
|
||||
using namespace arma;
|
||||
int main(int argc, char** argv) {
|
||||
int N = 6;
|
||||
if (argc > 1){ // Allow user to specify matrix size during call
|
||||
N = std::stoi(argv[1]);
|
||||
}
|
||||
|
||||
// Setup of the matrix
|
||||
double h = 1.0 / (N + 1);
|
||||
mat A = create_tridiagonal_matrix(N, h);
|
||||
cout << "Tridiagonal matrix A:" << endl << A << endl;
|
||||
|
||||
// Using Armadillo's built-in solver for comparison
|
||||
vec eigenvalues;
|
||||
mat eigenvectors;
|
||||
// Calling arma
|
||||
eig_sym(eigenvalues, eigenvectors, A);
|
||||
// Sorting and normalizing the results
|
||||
normalize_columns(eigenvectors);
|
||||
sort_eigenpairs(eigenvalues, eigenvectors);
|
||||
cout << "Eigenvalues:" << endl << eigenvalues << endl;
|
||||
cout << "Normalized eigenvectors:" << endl << eigenvectors << endl;
|
||||
|
||||
// Generate analytical solution
|
||||
vec analytical_eigenvalues;
|
||||
mat analytical_eigenvectors;
|
||||
analytical_solution(N, h, analytical_eigenvalues, analytical_eigenvectors);
|
||||
// Again: Sort the pairs of eigenvalues and eigenvectors by eigenvalue magnitude
|
||||
normalize_columns(analytical_eigenvectors);
|
||||
sort_eigenpairs(analytical_eigenvalues, analytical_eigenvectors);
|
||||
cout << "Analytical Eigenvalues:" << endl << analytical_eigenvalues << endl;
|
||||
cout << "Analytical Normalized Eigenvectors:" << endl << analytical_eigenvectors << endl;
|
||||
|
||||
// Using self-made Jacobi solver
|
||||
// Setup variables to capture output
|
||||
vec jacobi_eigenvalues;
|
||||
mat jacobi_eigenvectors;
|
||||
int iterations;
|
||||
bool converged;
|
||||
double eps = 1e-8;
|
||||
int maxiter = 1e5;
|
||||
// Calling the solver
|
||||
jacobi_eigensolver(A, eps, jacobi_eigenvalues, jacobi_eigenvectors, maxiter, iterations, converged);
|
||||
if (converged) {
|
||||
cout << "Jacobi method converged in " << iterations << " iterations." << endl;
|
||||
} else {
|
||||
cout << "Jacobi method did not converge within the maximum number of iterations." << endl;
|
||||
}
|
||||
normalize_columns(jacobi_eigenvectors); // Normalize the eigenvectors
|
||||
sort_eigenpairs(jacobi_eigenvalues, jacobi_eigenvectors);
|
||||
cout << "Jacobi Eigenvalues:" << endl << jacobi_eigenvalues << endl;
|
||||
cout << "Jacobi Normalized Eigenvectors:" << endl << jacobi_eigenvectors << endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
#include <armadillo>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
|
||||
#include "include/helpers.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace arma;
|
||||
|
||||
void write_boundary_conditions(ofstream& outfile, int num_eigenvectors, int decimal_precision){
|
||||
for(int i = 0; i < num_eigenvectors; i++){
|
||||
outfile << setprecision(decimal_precision) << scientific << 0.0 << "," << 0.0; // Boundary condition at x=1
|
||||
if (i < num_eigenvectors - 1) {
|
||||
outfile << ",";
|
||||
}
|
||||
else {
|
||||
outfile << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int n = 10;
|
||||
const int NUM_EIGENVECTORS = 3; // Number of eigenvectors to save
|
||||
const int DECIMAL_PRECISION = 12; // Decimal precision for output
|
||||
if (argc > 1){
|
||||
n = std::stoi(argv[1]);
|
||||
}
|
||||
int N = n - 1; // Number of interior points
|
||||
double h = 1.0 / (n); // Step size
|
||||
mat A = create_tridiagonal_matrix(N, h);
|
||||
|
||||
|
||||
// Generate analytical solution
|
||||
vec analytical_eigenvalues;
|
||||
mat analytical_eigenvectors;
|
||||
analytical_solution(N, h, analytical_eigenvalues, analytical_eigenvectors);
|
||||
normalize_columns(analytical_eigenvectors);
|
||||
sort_eigenpairs(analytical_eigenvalues, analytical_eigenvectors);
|
||||
|
||||
// Using self-made Jacobi solver
|
||||
vec jacobi_eigenvalues;
|
||||
mat jacobi_eigenvectors;
|
||||
int iterations;
|
||||
bool converged;
|
||||
double eps = 1e-8;
|
||||
int maxiter = 1e6;
|
||||
jacobi_eigensolver(A, eps, jacobi_eigenvalues, jacobi_eigenvectors, maxiter, iterations, converged);
|
||||
if (converged) {
|
||||
cout << "Jacobi method converged in " << iterations << " iterations." << endl;
|
||||
} else {
|
||||
cout << "Jacobi method did not converge within the maximum number of iterations." << endl;
|
||||
return 1;
|
||||
}
|
||||
normalize_columns(jacobi_eigenvectors); // Normalize the eigenvectors
|
||||
sort_eigenpairs(jacobi_eigenvalues, jacobi_eigenvectors);
|
||||
|
||||
// Write results to file
|
||||
ofstream outfile;
|
||||
string filename = "eigen_data_" + to_string(n) + ".txt"; // e.g., eigen_data_10.txt
|
||||
outfile.open(filename);
|
||||
for(int i = 0; i < NUM_EIGENVECTORS; i++){
|
||||
outfile << "analytical_solution_" << i+1 << "," << "jacobi_solution_" << i+1; // Column headers
|
||||
if(i < NUM_EIGENVECTORS - 1){
|
||||
outfile << ",";
|
||||
} else {
|
||||
outfile << endl;
|
||||
}
|
||||
}
|
||||
write_boundary_conditions(outfile, NUM_EIGENVECTORS, DECIMAL_PRECISION); // Boundary condition at x=0
|
||||
for(int j = 0; j < N; j++){
|
||||
for(int i = 0; i < NUM_EIGENVECTORS; i++){
|
||||
outfile << setprecision(DECIMAL_PRECISION) << scientific << analytical_eigenvectors(j,i) << "," << jacobi_eigenvectors(j,i);
|
||||
if(i < NUM_EIGENVECTORS - 1){
|
||||
outfile << ",";
|
||||
} else {
|
||||
outfile << endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
write_boundary_conditions(outfile, NUM_EIGENVECTORS, DECIMAL_PRECISION); // Boundary condition at x=1
|
||||
outfile.close();
|
||||
return 0;
|
||||
}
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
#!/bin/bash
|
||||
echo "Cleaning up old results..."
|
||||
rm -f eigen_data_*.txt scaling_data*.txt
|
||||
rm -f arma_eigenproblem_checker buckling_beam_solver off_diagonal_finder scaling_tester
|
||||
rm -f ../../projects/project2/include/*.pdf
|
||||
|
||||
echo "Compiling the C++ code..."
|
||||
g++ -larmadillo src/* arma_eigenproblem_checker.cpp -o arma_eigenproblem_checker
|
||||
g++ -larmadillo src/* buckling_beam_solver.cpp -o buckling_beam_solver
|
||||
g++ -larmadillo src/* off_diagonal_finder.cpp -o off_diagonal_finder
|
||||
g++ -larmadillo src/* scaling_tester.cpp -o scaling_tester
|
||||
echo "Compilation finished."
|
||||
|
||||
echo "Generating numerical results..."
|
||||
./scaling_tester 250
|
||||
./scaling_tester 250 dense
|
||||
uv run python/scaling_plotter.py scaling_data.txt
|
||||
uv run python/scaling_plotter.py scaling_data_dense.txt
|
||||
|
||||
./buckling_beam_solver 10
|
||||
./buckling_beam_solver 100
|
||||
uv run python/buckling_beam_plotter.py eigen_data_10.txt
|
||||
uv run python/buckling_beam_plotter.py eigen_data_100.txt
|
||||
|
||||
echo "All done!"
|
||||
@@ -0,0 +1,18 @@
|
||||
#include <armadillo>
|
||||
#include <cmath>
|
||||
|
||||
arma::mat create_tridiagonal_matrix(int N, double h);
|
||||
|
||||
arma::mat create_dense_matrix(int N, double h);
|
||||
|
||||
void normalize_columns(arma::mat& A);
|
||||
|
||||
void analytical_solution(int N, double h, arma::vec& eigenvalues, arma::mat& eigenvectors);
|
||||
|
||||
double max_offdiag_symmetric(const arma::mat& A, int& k, int &l);
|
||||
|
||||
void jacobi_rotate(arma::mat& A, arma::mat& R, int k, int l);
|
||||
|
||||
void jacobi_eigensolver(const arma::mat& A, double eps, arma::vec& eigenvalues, arma::mat& eigenvectors, const int maxiter, int& iterations, bool& converged);
|
||||
|
||||
void sort_eigenpairs(arma::vec& eigenvalues, arma::mat& eigenvectors);
|
||||
@@ -0,0 +1,23 @@
|
||||
#include "include/helpers.hpp"
|
||||
#include <iostream>
|
||||
#include <armadillo>
|
||||
|
||||
using namespace std;
|
||||
using namespace arma;
|
||||
|
||||
int main(){
|
||||
// Setup of the test matrix
|
||||
mat A = {{1., 0., 0., 0.5},
|
||||
{0., 1., -0.7, 0.},
|
||||
{0., -0.7, 1., 0.},
|
||||
{0.5, 0., 0., 1.}};
|
||||
|
||||
// Call the function
|
||||
int k, l;
|
||||
double max_val = max_offdiag_symmetric(A, k, l);
|
||||
|
||||
// Print the results
|
||||
cout << "Max off-diagonal value: " << max_val << " at (" << k << ", " << l << ")" << endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
import argparse
|
||||
import os
|
||||
|
||||
parser = argparse.ArgumentParser(description='Plot buckling beam data.')
|
||||
parser.add_argument('input_file', type=str, help='Path to the input CSV file')
|
||||
|
||||
args = parser.parse_args()
|
||||
data = pd.read_csv(args.input_file)
|
||||
|
||||
x = np.linspace(0, 1, len(data))
|
||||
fig, axs = plt.subplots(1, 2, figsize=(12, 5), sharex=True)
|
||||
for col in data.columns:
|
||||
c = f"C{int(col[-1])}"
|
||||
ls = "-" if "jacobi" in col else "--"
|
||||
label = col.replace("_", " ").title()
|
||||
axs[0].plot(x, data[col], label=label, color=c, linestyle=ls)
|
||||
if "analytical" in col:
|
||||
jacobi_col = col.replace("analytical", "jacobi")
|
||||
if jacobi_col in data.columns:
|
||||
error = np.abs(data[col] - data[jacobi_col])
|
||||
axs[1].plot(x, error, label=f'Error {label.split(" ")[-1]}', color=c, linestyle=':')
|
||||
axs[1].set_xlabel('x')
|
||||
axs[1].set_ylabel('Error')
|
||||
axs[1].set_title('Buckling Beam Errors')
|
||||
axs[1].legend()
|
||||
axs[1].grid()
|
||||
axs[0].set_xlabel('x')
|
||||
axs[0].set_ylabel('Displacement')
|
||||
axs[0].set_title('Buckling Beam Displacements')
|
||||
axs[0].legend()
|
||||
axs[0].grid()
|
||||
|
||||
dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../projects/project2/include"))
|
||||
filename = args.input_file.split("/")[-1].replace(".txt", "")
|
||||
fig.savefig(f"{dir}/{filename}_plot.pdf")
|
||||
@@ -0,0 +1,37 @@
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
import argparse
|
||||
import os
|
||||
|
||||
parser = argparse.ArgumentParser(description='Plot scaling data.')
|
||||
parser.add_argument('input_file', type=str, help='Path to the input CSV file')
|
||||
parser.add_argument("--times", "-t", action="store_true", help="Plot times instead of iterations")
|
||||
|
||||
args = parser.parse_args()
|
||||
data = pd.read_csv(args.input_file)
|
||||
|
||||
x = data["N"]
|
||||
fig, ax = plt.subplots(figsize=(8, 5))
|
||||
if args.times:
|
||||
ax.plot(x, data["Jacobi_time"], label='Jacobi Time', color='C0', linestyle='-', marker='o')
|
||||
ax.plot(x, data["Arma_time"], label='Armadillo Time', color='C1', linestyle='-', marker='o')
|
||||
ax.set_ylabel('Time (s)')
|
||||
ax.set_title('Scaling of Computation Time')
|
||||
ax.set_yscale('log')
|
||||
ax.legend()
|
||||
ax.grid()
|
||||
else:
|
||||
ax.set_ylabel('Number of Iterations')
|
||||
ax.set_title('Scaling of Jacobi Iterations')
|
||||
ax.set_yscale('log')
|
||||
ax.plot(x, data["Jacobi_iterations"], label='Jacobi Iterations', color='C0', linestyle='-', marker='o')
|
||||
ax.grid()
|
||||
|
||||
ax.set_xlabel('Matrix Size N')
|
||||
dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "../../../projects/project2/include"))
|
||||
filename = args.input_file.split("/")[-1].replace(".txt", "")
|
||||
if args.times:
|
||||
fig.savefig(f"{dir}/{filename}_times.pdf")
|
||||
else:
|
||||
fig.savefig(f"{dir}/{filename}_iterations.pdf")
|
||||
@@ -0,0 +1,73 @@
|
||||
#include <armadillo>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include "include/helpers.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace arma;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
int max_N = 200; // Maximum matrix size
|
||||
if (argc > 1){ // Allow user to specify max matrix size during call
|
||||
max_N = std::stoi(argv[1]);
|
||||
}
|
||||
|
||||
string filename;
|
||||
mat (*matrix_creator_func)(int, double);
|
||||
if (argc > 2 && string(argv[2]) == "dense") { // Use dense matrix if specified
|
||||
filename = "scaling_data_dense.txt";
|
||||
matrix_creator_func = create_dense_matrix;
|
||||
} else { // Default to tridiagonal matrix
|
||||
filename = "scaling_data.txt";
|
||||
matrix_creator_func = create_tridiagonal_matrix;
|
||||
}
|
||||
ofstream outfile;
|
||||
outfile.open(filename);
|
||||
outfile << "N,Arma_time,Analytical_time,Jacobi_time,Jacobi_iterations,Jacobi_converged\n";
|
||||
|
||||
for(int N = 10; N <= max_N; N += 10) { // Iterate over matrix sizes in steps of 10
|
||||
double h = 1.0 / (N + 1);
|
||||
mat A = matrix_creator_func(N, h);
|
||||
|
||||
// Armadillo solver
|
||||
vec arma_eigenvalues;
|
||||
mat arma_eigenvectors;
|
||||
auto start_arma = chrono::high_resolution_clock::now();
|
||||
eig_sym(arma_eigenvalues, arma_eigenvectors, A);
|
||||
auto end_arma = chrono::high_resolution_clock::now();
|
||||
chrono::duration<double> arma_duration = end_arma - start_arma;
|
||||
|
||||
// Analytical solver
|
||||
vec analytical_eigenvalues;
|
||||
mat analytical_eigenvectors;
|
||||
auto start_analytical = chrono::high_resolution_clock::now();
|
||||
analytical_solution(N, h, analytical_eigenvalues, analytical_eigenvectors);
|
||||
auto end_analytical = chrono::high_resolution_clock::now();
|
||||
chrono::duration<double> analytical_duration = end_analytical - start_analytical;
|
||||
|
||||
// Jacobi solver
|
||||
vec jacobi_eigenvalues;
|
||||
mat jacobi_eigenvectors;
|
||||
int iterations;
|
||||
bool converged;
|
||||
double eps = 1e-8;
|
||||
int maxiter = 1e6;
|
||||
auto start_jacobi = chrono::high_resolution_clock::now();
|
||||
jacobi_eigensolver(A, eps, jacobi_eigenvalues, jacobi_eigenvectors, maxiter, iterations, converged);
|
||||
auto end_jacobi = chrono::high_resolution_clock::now();
|
||||
chrono::duration<double> jacobi_duration = end_jacobi - start_jacobi;
|
||||
|
||||
// Write results to file
|
||||
outfile << N << ","
|
||||
<< arma_duration.count() << ","
|
||||
<< analytical_duration.count() << ","
|
||||
<< jacobi_duration.count() << ","
|
||||
<< iterations << ","
|
||||
<< (converged ? "true" : "false") << "\n";
|
||||
|
||||
cout << "Completed N = " << N << endl;
|
||||
}
|
||||
outfile.close();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
#include "../include/helpers.hpp"
|
||||
|
||||
arma::mat create_tridiagonal_matrix(int N, double h) {
|
||||
arma::mat A = arma::mat(N, N, arma::fill::zeros);
|
||||
|
||||
double diag_value = 2.0 / (h * h);
|
||||
double off_diag_value = -1.0 / (h * h);
|
||||
|
||||
for (int i = 0; i < N; ++i) {
|
||||
A(i, i) = diag_value;
|
||||
if (i > 0) { // There is now lower diagnonal element in the first row
|
||||
A(i, i - 1) = off_diag_value;
|
||||
}
|
||||
if (i < N - 1) { // There is no upper diagonal element in the last row
|
||||
A(i, i + 1) = off_diag_value;
|
||||
}
|
||||
}
|
||||
|
||||
return A;
|
||||
}
|
||||
|
||||
arma::mat create_dense_matrix(int N, double h) {
|
||||
arma::mat A = arma::mat(N, N).randn();
|
||||
A = arma::symmatu(A); // Make it symmetric
|
||||
return A;
|
||||
}
|
||||
|
||||
void normalize_columns(arma::mat& A) { // Useful for eigenvector normalization
|
||||
A = arma::normalise(A, 2, 0); // 2-norm along columns (0)
|
||||
|
||||
// Flip sign to ensure first element is positive
|
||||
for (size_t col = 0; col < A.n_cols; ++col) {
|
||||
if (A(0, col) < 0) {
|
||||
A.col(col) = -A.col(col);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void analytical_solution(int N, double h, arma::vec& eigenvalues, arma::mat& eigenvectors) {
|
||||
eigenvalues = arma::vec(N);
|
||||
eigenvectors = arma::mat(N, N);
|
||||
double d = 2.0 / (h * h);
|
||||
double a = -1.0 / (h * h);
|
||||
|
||||
for (int j = 0; j < N; ++j) {
|
||||
eigenvalues(j) = d + 2 * a * std::cos((j + 1) * arma::datum::pi / (N + 1)); // Eigenvalues
|
||||
|
||||
for (int i = 0; i < N; ++i) {
|
||||
eigenvectors(i, j) = std::sin((i + 1) * (j + 1) * arma::datum::pi / (N + 1)); // Eigenvectors
|
||||
}
|
||||
}
|
||||
|
||||
normalize_columns(eigenvectors); // Normalize the eigenvectors
|
||||
}
|
||||
|
||||
double max_offdiag_symmetric(const arma::mat& A, int& k, int &l) {
|
||||
int N = A.n_rows;
|
||||
double max_val = 0.0;
|
||||
|
||||
for (int i = 0; i < N; ++i) {
|
||||
for (int j = i + 1; j < N; ++j) { // Only check upper triangle
|
||||
if (std::abs(A(i, j)) > max_val) {
|
||||
max_val = std::abs(A(i, j));
|
||||
k = i;
|
||||
l = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return max_val;
|
||||
}
|
||||
|
||||
void jacobi_rotate(arma::mat& A, arma::mat& R, int k, int l) {
|
||||
if (A(k, l) != 0.0) {
|
||||
double tau = (A(l, l) - A(k, k)) / (2.0 * A(k, l));
|
||||
double t;
|
||||
if (tau >= 0) {
|
||||
t = 1.0 / (tau + std::sqrt(1.0 + tau * tau));
|
||||
} else {
|
||||
t = -1.0 / (-tau + std::sqrt(1.0 + tau * tau));
|
||||
}
|
||||
double c = 1.0 / std::sqrt(1 + t * t);
|
||||
double s = t * c;
|
||||
double temp;
|
||||
|
||||
// Update matrix A
|
||||
double a_kk = A(k, k);
|
||||
double a_ll = A(l, l);
|
||||
A(k, k) = a_kk * c * c - 2.0 * A(k, l) * c * s + a_ll * s * s;
|
||||
A(l, l) = a_ll * c * c + 2.0 * A(k, l) * c * s + a_kk * s * s;
|
||||
A(k, l) = 0.0; // Hard-coding to zero
|
||||
A(l, k) = 0.0; // Hard-coding to zero
|
||||
|
||||
for (int i = 0; i < A.n_rows; ++i) {
|
||||
if (i != k && i != l) {
|
||||
temp = A(i, k);
|
||||
A(i, k) = temp * c - A(i, l) * s;
|
||||
A(k, i) = A(i, k); // Since A is symmetric
|
||||
A(i, l) = temp * s + A(i, l) * c;
|
||||
A(l, i) = A(i, l); // Since A is symmetric
|
||||
}
|
||||
|
||||
// Update the eigenvector matrix R
|
||||
temp = R(i, k);
|
||||
R(i, k) = temp * c - R(i, l) * s;
|
||||
R(i, l) = temp * s + R(i, l) * c;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void jacobi_eigensolver(const arma::mat& A, double eps, arma::vec& eigenvalues, arma::mat& eigenvectors,
|
||||
const int maxiter, int& iterations, bool& converged) {
|
||||
int N = A.n_rows;
|
||||
arma::mat A_copy = A; // Make a copy to avoid modifying the original matrix
|
||||
eigenvectors = arma::eye<arma::mat>(N, N); // Initialize eigenvector matrix as identity
|
||||
iterations = 0;
|
||||
converged = false;
|
||||
int k, l;
|
||||
double max_offdiag;
|
||||
while (iterations < maxiter) {
|
||||
max_offdiag = max_offdiag_symmetric(A_copy, k, l);
|
||||
if (max_offdiag < eps) {
|
||||
converged = true;
|
||||
break;
|
||||
}
|
||||
jacobi_rotate(A_copy, eigenvectors, k, l);
|
||||
iterations++;
|
||||
}
|
||||
eigenvalues = A_copy.diag(); // Extract eigenvalues from the diagonal of A_copy
|
||||
}
|
||||
|
||||
void sort_eigenpairs(arma::vec& eigenvalues, arma::mat& eigenvectors) {
|
||||
arma::uvec indices = arma::sort_index(eigenvalues, "ascend");
|
||||
eigenvalues = eigenvalues(indices);
|
||||
eigenvectors = eigenvectors.cols(indices);
|
||||
}
|
||||
Reference in New Issue
Block a user