1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
use nalgebra::{Const, DVector};
use nalgebra_sparse::{io::load_coo_from_matrix_market_file, CsrMatrix};

use crate::{
    io, solver,
    utility::{compute_rel_err, init_b},
};

/// Method is used to set the method used by the routine. 
/// 
/// With:
/// * **Method::JA** = Jacobi
/// * **Method::GS** = Gauss-Seidel
/// * **Method::GR** = gradient
/// * **Method::CG** = conjugate gradient
/// * **Method::PG** = gradient with Jacobi preconditioning
#[derive(Debug)]
pub enum Method {
    JA,
    GS,
    GR,
    CG,
    PG,
}

impl Method {
    pub fn copy(&self) -> Method {
        match self {
            Method::JA => Method::JA,
            Method::GS => Method::GS,
            Method::CG => Method::CG,
            Method::GR => Method::GR,
            Method::PG => Method::PG,
        }
    }
}
/// Performace is a struct returned by [compute_precision].
/// 
/// It contains three fields:
/// * **rel_err**: the relative error of the computed result against the correct one
/// * **time**: the time the routine take to solve the system
/// * **iter**: the number of iteration the routine take to solve the system
pub struct Performance {
    pub rel_err: f64,
    pub time: u128,
    pub iter: u32,
}

/// Stat is a struct returned by [solve_linear_system].
/// 
/// It contains three fields:
/// * **solution**: the solution computed by the routine
/// * **time**: the time the routine take to solve the system
/// * **iter**: the number of iteration the routine take to solve the system
#[derive(Debug)]
pub struct Stat {
    solution: DVector<f64>,
    time: u128,
    iter: u32,
}

impl Stat {
    pub fn new(solution: DVector<f64>, time: u128, iter: u32) -> Stat {
        Stat {
            solution,
            time,
            iter,
        }
    }
    pub fn get_solution(&self) -> &DVector<f64> {
        &self.solution
    }

    pub fn get_time(&self) -> u128 {
        self.time
    }

    pub fn get_iterations(&self) -> u32 {
        self.iter
    }

    pub fn to_string(&self) -> String {
        format!(
            "Result:\n{:?}\nMethod converged in \t{} iterations \t({} ms)",
            self.get_solution(),
            self.get_iterations(),
            self.get_time()
        )
    }
}

/// Parse a matrix market file (.mtx) and return the csr matrix representation of it.
/// Panic if matrix is not sparse
pub fn read_matrix_from_matrix_market_file(file_path: &String) -> CsrMatrix<f64> {
    let coo_matrix = load_coo_from_matrix_market_file(file_path).unwrap();
    CsrMatrix::from(&coo_matrix)
}

/// Parse a vector from a matrix market file or a file with each row representing the entry of the vector.
pub fn read_vector_from_file(file_path: &String) -> DVector<f64> {
    io::parse_vector(file_path)
}

/// Initialize a vector with dimension = \[size\] and each entry = value
pub fn init_solution(size: usize, value: f64) -> DVector<f64> {
    DVector::from_element(size, value)
}

/// Initialize a vector with dimension = \[size\] and each entry a random value between 0 and 1
pub fn init_random_vector(size: usize) -> DVector<f64> {
    DVector::new_random_generic(nalgebra::Dyn(size), Const::<1>)
}

/// Solves the linear system ax=b and returns a [Stat] instance:
/// 
/// ### Arguments:
/// * **a**: matrix
/// * **b**: vector of constant terms
/// * **method**: an istance of the enum Method
/// * **tol**: the tolerance required to stop the routine
/// * **max_iter**: the maximum number of iteration after wich the routine stops
/// * **omega**: the relaxation factor, used only if method is either Jacobi or Gauss-Seidel
pub fn solve_linear_system(
    a: &CsrMatrix<f64>,
    b: &DVector<f64>,
    method: Method,
    tol: f64,
    max_iter: i32,
    omega: f64,
) -> Stat {
    solver::exec(a, b, method, tol, max_iter, omega)
}

/// Determines the accuracy of x (solution computed by the routine) against the correct one and returns an istance of [Performance].
/// 
/// Where x is the solution of the system ax = b, with b := a*solution.
/// 
/// ### Arguments:
/// * **a**: matrix 
/// * **solution**: the given solution of the system
/// * **method**: an istance of the enum Method
/// * **tol**: the tolerance required to stop the routine
/// * **max_iter**: the maximum number of iteration after wich the routine stops
/// * **omega**: the relaxation factor, used only if method is either Jacobi or Gauss-Seidel
pub fn compute_precision(
    a: &CsrMatrix<f64>,
    solution: &DVector<f64>,
    method: Method,
    tol: f64,
    max_iter: i32,
    omega: f64,
) -> Performance {
    let b = init_b(solution, a);
    let result = solver::exec(a, &b, method, tol, max_iter, omega);
    let rel_err = compute_rel_err(solution, result.get_solution());
    Performance {
        rel_err,
        time: result.get_time(),
        iter: result.get_iterations(),
    }
}