use crate::IntegrateFloat;
use scirs2_core::ndarray::{Array1, Array2};
pub struct BlockILUPreconditioner<F: IntegrateFloat> {
n_x: usize,
n_y: usize,
#[allow(dead_code)]
a_block: Array2<F>, #[allow(dead_code)]
b_block: Array2<F>, #[allow(dead_code)]
c_block: Array2<F>, #[allow(dead_code)]
d_block: Array2<F>,
l_factors: Array2<F>,
u_factors: Array2<F>,
d_scaling: Array1<F>,
}
impl<F: IntegrateFloat> BlockILUPreconditioner<F> {
pub fn new(
f_x: &Array2<F>,
f_y: &Array2<F>,
g_x: &Array2<F>,
g_y: &Array2<F>,
h: F,
beta: F,
) -> Self {
let n_x = f_x.shape()[0];
let n_y = g_y.shape()[0];
let mut a_block = Array2::<F>::eye(n_x);
for i in 0..n_x {
for j in 0..n_x {
a_block[[i, j]] -= h * beta * f_x[[i, j]];
}
}
let b_block = f_y.mapv(|x| -h * beta * x);
let c_block = g_x.clone();
let d_block = g_y.clone();
let (l_factors, u_factors, d_scaling) =
Self::compute_block_ilu0(&a_block, &b_block, &c_block, &d_block);
BlockILUPreconditioner {
n_x,
n_y,
a_block,
b_block,
c_block,
d_block,
l_factors,
u_factors,
d_scaling,
}
}
fn compute_block_ilu0(
a: &Array2<F>,
b: &Array2<F>,
c: &Array2<F>,
d: &Array2<F>,
) -> (Array2<F>, Array2<F>, Array1<F>) {
let n_x = a.shape()[0];
let n_y = d.shape()[0];
let n_total = n_x + n_y;
let mut l = Array2::<F>::zeros((n_total, n_total));
let mut u = Array2::<F>::zeros((n_total, n_total));
for i in 0..n_x {
for j in 0..n_x {
match i.cmp(&j) {
std::cmp::Ordering::Greater => {
l[[i, j]] = a[[i, j]];
}
std::cmp::Ordering::Equal => {
u[[i, j]] = a[[i, j]];
}
std::cmp::Ordering::Less => {
u[[i, j]] = a[[i, j]];
}
}
}
}
for i in 0..n_x {
for j in 0..n_y {
u[[i, n_x + j]] = b[[i, j]];
}
}
for i in 0..n_y {
for j in 0..n_x {
l[[n_x + i, j]] = c[[i, j]];
}
}
for i in 0..n_y {
for j in 0..n_y {
match i.cmp(&j) {
std::cmp::Ordering::Greater => {
l[[n_x + i, n_x + j]] = d[[i, j]];
}
std::cmp::Ordering::Equal => {
u[[n_x + i, n_x + j]] = d[[i, j]];
}
std::cmp::Ordering::Less => {
u[[n_x + i, n_x + j]] = d[[i, j]];
}
}
}
}
for i in 0..n_total {
l[[i, i]] = F::one();
}
for k in 0..n_total {
for i in (k + 1)..n_total {
if l[[i, k]].abs() > F::from_f64(1e-14).expect("Operation failed") {
l[[i, k]] /= u[[k, k]];
for j in (k + 1)..n_total {
if u[[k, j]].abs() > F::from_f64(1e-14).expect("Operation failed") {
if l[[i, j]].abs() > F::from_f64(1e-14).expect("Operation failed")
|| u[[i, j]].abs() > F::from_f64(1e-14).expect("Operation failed")
{
l[[i, j]] = l[[i, j]] - l[[i, k]] * u[[k, j]];
}
}
}
}
}
}
let mut d_scaling = Array1::<F>::ones(n_total);
for i in 0..n_total {
if u[[i, i]].abs() < F::from_f64(1e-14).expect("Operation failed") {
u[[i, i]] = F::from_f64(1e-14).expect("Operation failed") * u[[i, i]].signum();
}
d_scaling[i] = F::one() / u[[i, i]];
}
(l, u, d_scaling)
}
pub fn apply(&self, v: &Array1<F>) -> Array1<F> {
let n_total = self.n_x + self.n_y;
let mut result = Array1::<F>::zeros(n_total);
let mut y = Array1::<F>::zeros(n_total);
for i in 0..n_total {
y[i] = v[i];
for j in 0..i {
y[i] = y[i] - self.l_factors[[i, j]] * y[j];
}
}
for i in (0..n_total).rev() {
result[i] = y[i];
for j in (i + 1)..n_total {
result[i] = result[i] - self.u_factors[[i, j]] * result[j];
}
result[i] *= self.d_scaling[i];
}
result
}
}
pub struct BlockJacobiPreconditioner<F: IntegrateFloat> {
n: usize,
block_size: usize,
n_blocks: usize,
block_inverses: Vec<Array2<F>>,
}
impl<F: IntegrateFloat> BlockJacobiPreconditioner<F> {
pub fn new(jacobian: &Array2<F>, blocksize: usize) -> Self {
let n = jacobian.shape()[0];
assert!(
n.is_multiple_of(blocksize),
"Matrix _size must be divisible by block _size"
);
let n_blocks = n / blocksize;
let mut block_inverses = Vec::with_capacity(n_blocks);
for i in 0..n_blocks {
let start = i * blocksize;
let end = start + blocksize;
let block = jacobian
.slice(scirs2_core::ndarray::s![start..end, start..end])
.to_owned();
let block_inv = Self::compute_block_inverse(&block);
block_inverses.push(block_inv);
}
BlockJacobiPreconditioner {
n,
block_size: blocksize,
n_blocks,
block_inverses,
}
}
fn compute_block_inverse(block: &Array2<F>) -> Array2<F> {
let n = block.shape()[0];
let mut result = Array2::<F>::zeros((n, n));
if n == 1 {
let val = block[[0, 0]];
if val.abs() < F::from_f64(1e-14).expect("Operation failed") {
result[[0, 0]] = F::from_f64(1e-14).expect("Operation failed") * val.signum();
} else {
result[[0, 0]] = F::one() / val;
}
return result;
}
if n == 2 {
let a = block[[0, 0]];
let b = block[[0, 1]];
let c = block[[1, 0]];
let d = block[[1, 1]];
let det = a * d - b * c;
if det.abs() < F::from_f64(1e-14).expect("Operation failed") {
let reg = F::from_f64(1e-14).expect("Operation failed") * det.signum();
result[[0, 0]] = d / (det + reg);
result[[0, 1]] = -b / (det + reg);
result[[1, 0]] = -c / (det + reg);
result[[1, 1]] = a / (det + reg);
} else {
result[[0, 0]] = d / det;
result[[0, 1]] = -b / det;
result[[1, 0]] = -c / det;
result[[1, 1]] = a / det;
}
return result;
}
if n == 3 {
let a = block[[0, 0]];
let b = block[[0, 1]];
let c = block[[0, 2]];
let d = block[[1, 0]];
let e = block[[1, 1]];
let f = block[[1, 2]];
let g = block[[2, 0]];
let h = block[[2, 1]];
let i = block[[2, 2]];
let det = a * (e * i - f * h) - b * (d * i - f * g) + c * (d * h - e * g);
if det.abs() < F::from_f64(1e-14).expect("Operation failed") {
let reg = F::from_f64(1e-14).expect("Operation failed") * det.signum();
let a11 = (e * i - f * h) / (det + reg);
let a12 = (c * h - b * i) / (det + reg);
let a13 = (b * f - c * e) / (det + reg);
let a21 = (f * g - d * i) / (det + reg);
let a22 = (a * i - c * g) / (det + reg);
let a23 = (c * d - a * f) / (det + reg);
let a31 = (d * h - e * g) / (det + reg);
let a32 = (b * g - a * h) / (det + reg);
let a33 = (a * e - b * d) / (det + reg);
result[[0, 0]] = a11;
result[[0, 1]] = a12;
result[[0, 2]] = a13;
result[[1, 0]] = a21;
result[[1, 1]] = a22;
result[[1, 2]] = a23;
result[[2, 0]] = a31;
result[[2, 1]] = a32;
result[[2, 2]] = a33;
} else {
let a11 = (e * i - f * h) / det;
let a12 = (c * h - b * i) / det;
let a13 = (b * f - c * e) / det;
let a21 = (f * g - d * i) / det;
let a22 = (a * i - c * g) / det;
let a23 = (c * d - a * f) / det;
let a31 = (d * h - e * g) / det;
let a32 = (b * g - a * h) / det;
let a33 = (a * e - b * d) / det;
result[[0, 0]] = a11;
result[[0, 1]] = a12;
result[[0, 2]] = a13;
result[[1, 0]] = a21;
result[[1, 1]] = a22;
result[[1, 2]] = a23;
result[[2, 0]] = a31;
result[[2, 1]] = a32;
result[[2, 2]] = a33;
}
return result;
}
let mut inv = Array2::<F>::eye(n);
let mut lu = block.clone();
for k in 0..n {
let mut p = k;
let mut max_val = lu[[k, k]].abs();
for i in (k + 1)..n {
if lu[[i, k]].abs() > max_val {
max_val = lu[[i, k]].abs();
p = i;
}
}
if max_val < F::from_f64(1e-14).expect("Operation failed") {
lu[[p, k]] = F::from_f64(1e-14).expect("Operation failed") * lu[[p, k]].signum();
}
if p != k {
for j in 0..n {
let temp = lu[[k, j]];
lu[[k, j]] = lu[[p, j]];
lu[[p, j]] = temp;
let temp = inv[[k, j]];
inv[[k, j]] = inv[[p, j]];
inv[[p, j]] = temp;
}
}
let pivot = lu[[k, k]];
for j in 0..n {
lu[[k, j]] /= pivot;
inv[[k, j]] /= pivot;
}
for i in 0..n {
if i != k {
let factor = lu[[i, k]];
for j in 0..n {
lu[[i, j]] = lu[[i, j]] - factor * lu[[k, j]];
inv[[i, j]] = inv[[i, j]] - factor * inv[[k, j]];
}
}
}
}
inv
}
pub fn apply(&self, v: &Array1<F>) -> Array1<F> {
let mut result = Array1::<F>::zeros(self.n);
for i in 0..self.n_blocks {
let start = i * self.block_size;
let end = start + self.block_size;
let v_segment = v.slice(scirs2_core::ndarray::s![start..end]).to_owned();
let block_result = &self.block_inverses[i].dot(&v_segment);
for j in 0..self.block_size {
result[start + j] = block_result[j];
}
}
result
}
}
#[allow(dead_code)]
pub fn create_block_ilu_preconditioner<F>(
f_x: &Array2<F>,
f_y: &Array2<F>,
g_x: &Array2<F>,
g_y: &Array2<F>,
h: F,
beta: F,
) -> impl Fn(&Array1<F>) -> Array1<F>
where
F: IntegrateFloat,
{
let preconditioner = BlockILUPreconditioner::new(f_x, f_y, g_x, g_y, h, beta);
move |v: &Array1<F>| preconditioner.apply(v)
}
#[allow(dead_code)]
pub fn create_block_jacobi_preconditioner<F>(
jacobian: &Array2<F>,
block_size: usize,
) -> impl Fn(&Array1<F>) -> Array1<F>
where
F: IntegrateFloat,
{
let preconditioner = BlockJacobiPreconditioner::new(jacobian, block_size);
move |v: &Array1<F>| preconditioner.apply(v)
}