peroxide/numerical/
utils.rs

1use crate::structure::ad::*;
2use crate::structure::matrix::*;
3use crate::traits::matrix::MatrixTrait;
4use crate::util::non_macro::{cat, zeros};
5
6/// Jacobian Matrix
7///
8/// # Description
9/// : Exact jacobian matrix using Automatic Differenitation
10///
11/// # Type
12/// `(F, &Vec<f64>) -> Matrix where F: Fn(&Vec<AD>) -> Vec<AD>`
13///
14/// # Examples
15/// ```
16/// #[macro_use]
17/// extern crate peroxide;
18/// use peroxide::fuga::*;
19///
20/// fn main() {
21///     let x = c!(1, 1);
22///     let j = jacobian(f, &x);
23///     j.print();
24///
25///     //      c[0] c[1]
26///     // r[0]    1   -1
27///     // r[1]    1    2
28/// }
29///
30/// fn f(xs: &Vec<AD>) -> Vec<AD> {
31///     let x = xs[0];
32///     let y = xs[1];
33///
34///     vec![
35///        x - y,
36///        x + 2.*y,
37///    ]
38/// }
39/// ```
40#[allow(non_snake_case)]
41pub fn jacobian<F: Fn(&Vec<AD>) -> Vec<AD>>(f: F, x: &Vec<f64>) -> Matrix {
42    let l = x.len();
43    let mut x_ad: Vec<AD> = x.iter().map(|&x| AD1(x, 0f64)).collect();
44    let l2 = f(&x_ad).len();
45
46    let mut J = zeros(l2, l);
47
48    for i in 0..l {
49        x_ad[i][1] = 1f64;
50        let slopes: Vec<f64> = f(&x_ad).iter().map(|ad| ad.dx()).collect();
51        J.subs_col(i, &slopes);
52        x_ad[i][1] = 0f64;
53    }
54    J
55}
56
57///// Hessian Matrix
58//#[allow(non_snake_case)]
59//pub fn hessian<F: Fn(&Vec<AD>) -> AD>(f: F, x: &Vec<f64>) -> Matrix {
60//    let l = x.len();
61//    let mut x_ad: Vec<AD> = x.iter().map(|&x| AD2(x, 0f64, 0f64)).collect();
62//
63//    let mut H = zeros(l, l);
64//
65//    for i in 0 .. l {
66//        for j in 0 .. l {
67//        }
68//    }
69//
70//    unimplemented!()
71//}
72
73//#[allow(non_snake_case)]
74//pub fn jacobian_ad<F: Fn(&Vec<AD>) -> Vec<AD>>(f: F, x: &Vec<AD>) -> Vec<Vec<AD>> {
75//    let l = x.len();
76//    let mut x_ad: Vec<AD> = x.clone().into_iter().map(|mut t| {
77//        t.iter_mut().skip(1).for_each(|k| *k = 0f64);
78//        t
79//    }).collect();
80//    let l2 = f(&x_ad).len();
81//
82//    let mut JT: Vec<Vec<AD>> = vec![vec![AD0(0f64); l2]; l];
83//
84//    for i in 0 .. l {
85//        x_ad[i][1] = 1f64;
86//        let ads = f(&x_ad);
87//        JT[i] = ads;
88//        x_ad[i][1] = 0f64;
89//    }
90//    JT
91//}
92
93/// TriDiagonal Matrix Algorithm (TDMA)
94///
95/// # Description
96///
97/// Solve tri-diagonal matrix system efficiently (O(n))
98/// ```bash
99/// |b0 c0         | |x0|   |y0|
100/// |a1 b1 c1      | |x1|   |y1|
101/// |   a2 b2 c2   | |x2| = |y2|
102/// |      ...     | |..|   |..|
103/// |         am bm| |xm|   |ym|
104/// ```
105///
106/// # Caution
107///
108/// You should apply boundary condition yourself
109pub fn tdma(a_input: Vec<f64>, b_input: Vec<f64>, c_input: Vec<f64>, y_input: Vec<f64>) -> Matrix {
110    let n = b_input.len();
111    assert_eq!(a_input.len(), n - 1);
112    assert_eq!(c_input.len(), n - 1);
113    assert_eq!(y_input.len(), n);
114
115    let a = cat(0f64, &a_input);
116    let mut b = b_input.clone();
117    let c = {
118        let mut c_temp = c_input.clone();
119        c_temp.push(0f64);
120        c_temp.clone()
121    };
122    let mut y = y_input.clone();
123
124    // Forward substitution
125    let mut w = vec![0f64; n];
126    for i in 1..n {
127        w[i] = a[i] / b[i - 1];
128        b[i] = b[i] - w[i] * c[i - 1];
129        y[i] = y[i] - w[i] * y[i - 1];
130    }
131
132    // Backward substitution
133    let mut x = vec![0f64; n];
134    x[n - 1] = y[n - 1] / b[n - 1];
135    for i in (0..n - 1).rev() {
136        x[i] = (y[i] - c[i] * x[i + 1]) / b[i];
137    }
138    x.into()
139}