use ndarray::{Array1, ArrayView1, ArrayView2, Zip};
use rayon::prelude::*;
pub fn calculator_field_krige_and_variance(
krig_mat: ArrayView2<'_, f64>,
krig_vecs: ArrayView2<'_, f64>,
cond: ArrayView1<'_, f64>,
num_threads: Option<usize>,
) -> (Array1<f64>, Array1<f64>) {
assert_eq!(krig_mat.dim().0, krig_mat.dim().1);
assert_eq!(krig_mat.dim().0, krig_vecs.dim().0);
assert_eq!(krig_mat.dim().0, cond.dim());
let mut field = Array1::<f64>::zeros(krig_vecs.shape()[1]);
let mut error = Array1::<f64>::zeros(krig_vecs.shape()[1]);
rayon::ThreadPoolBuilder::new()
.num_threads(num_threads.unwrap_or(rayon::current_num_threads()))
.build()
.unwrap()
.install(|| {
Zip::from(field.view_mut())
.and(error.view_mut())
.and(krig_vecs.columns())
.par_for_each(|f, e, v_col| {
let acc = Zip::from(cond)
.and(v_col)
.and(krig_mat.columns())
.into_par_iter()
.fold(
|| (0.0, 0.0),
|mut acc, (c, v, m_row)| {
let krig_fac = m_row.dot(&v_col);
acc.0 += c * krig_fac;
acc.1 += v * krig_fac;
acc
},
)
.reduce(
|| (0.0, 0.0),
|mut lhs, rhs| {
lhs.0 += rhs.0;
lhs.1 += rhs.1;
lhs
},
);
*f = acc.0;
*e = acc.1;
})
});
(field, error)
}
pub fn calculator_field_krige(
krig_mat: ArrayView2<'_, f64>,
krig_vecs: ArrayView2<'_, f64>,
cond: ArrayView1<'_, f64>,
num_threads: Option<usize>,
) -> Array1<f64> {
assert_eq!(krig_mat.dim().0, krig_mat.dim().1);
assert_eq!(krig_mat.dim().0, krig_vecs.dim().0);
assert_eq!(krig_mat.dim().0, cond.dim());
rayon::ThreadPoolBuilder::new()
.num_threads(num_threads.unwrap_or(rayon::current_num_threads()))
.build()
.unwrap()
.install(|| {
Zip::from(krig_vecs.columns()).par_map_collect(|v_col| {
Zip::from(cond)
.and(krig_mat.columns())
.into_par_iter()
.map(|(c, m_row)| {
let krig_fac = m_row.dot(&v_col);
c * krig_fac
})
.sum()
})
})
}
#[cfg(test)]
mod tests {
use super::*;
use approx::assert_ulps_eq;
use ndarray::{arr1, arr2, Array2};
struct Setup {
krig_mat: Array2<f64>,
krig_vecs: Array2<f64>,
cond: Array1<f64>,
}
impl Setup {
fn new() -> Self {
Self {
krig_mat: arr2(&[
[
5.00000000068981e-01,
-5.87287095364834e-06,
7.82325812566282e-12,
],
[
-5.87287095378827e-06,
5.00000000070158e-01,
-7.67370103394336e-07,
],
[
7.82331319334681e-12,
-7.67370103410243e-07,
5.00000000001178e-01,
],
]),
krig_vecs: arr2(&[
[
3.00650970845165e-01,
7.92958674144233e-11,
7.34102993092809e-02,
1.10371060304999e-08,
2.00114256042442e-01,
7.23018134159345e-03,
],
[
5.51416575736629e-09,
4.79656668238205e-09,
3.91247964853073e-03,
3.59846942149471e-11,
2.10720573114332e-10,
4.83625846265317e-04,
],
[
7.08796598544206e-13,
1.09700007286403e-01,
2.46322359027701e-05,
1.75889992745405e-07,
3.05671083940413e-17,
2.38513785599550e-11,
],
]),
cond: arr1(&[
-1.27755407195723e+00,
1.15554040655641e+00,
8.47374235895458e-01,
]),
}
}
}
#[test]
fn test_calculator_field_krige_and_variance() {
let setup = Setup::new();
let (kr_field, kr_error) = calculator_field_krige_and_variance(
setup.krig_mat.view(),
setup.krig_vecs.view(),
setup.cond.view(),
None,
);
assert_ulps_eq!(
kr_field,
arr1(&[
-0.19205097317842723,
0.04647838537175125,
-0.04462233428403452,
0.0000000674926344864219,
-0.12782974926973434,
-0.0043390949462510245
]),
max_ulps = 6,
);
assert_ulps_eq!(
kr_error,
arr1(&[
0.04519550314128594,
0.006017045799331816,
0.0027021867008690937,
0.000000000000015529554261898964,
0.020022857738471924,
0.00002625466702800745
]),
max_ulps = 6,
);
}
#[test]
fn test_calculator_field_krige() {
let setup = Setup::new();
assert_ulps_eq!(
calculator_field_krige(
setup.krig_mat.view(),
setup.krig_vecs.view(),
setup.cond.view(),
None
),
arr1(&[
-0.19205097317842723,
0.04647838537175125,
-0.04462233428403452,
0.0000000674926344864219,
-0.12782974926973434,
-0.0043390949462510245
]),
max_ulps = 6,
);
}
}