use crate::common::IntegrateFloat;
use crate::dae::types::{DAEIndex, DAEOptions, DAEResult, DAEType};
use crate::error::{IntegrateError, IntegrateResult};
use crate::ode::ODEMethod;
use scirs2_core::ndarray::{Array1, Array2, ArrayView1};
#[inline(always)]
fn const_f64<F: IntegrateFloat>(value: f64) -> F {
F::from_f64(value).expect("Failed to convert constant to target float type")
}
type GMRESResult<F> = (Array2<F>, Vec<Array1<F>>, F, usize);
#[allow(dead_code)]
const MAX_GMRES_ITER: usize = 100;
const GMRES_RESTART: usize = 30;
const GMRES_TOL: f64 = 1e-8;
#[allow(dead_code)]
pub fn krylov_bdf_semi_explicit_dae<F, FFunc, GFunc>(
f: FFunc,
g: GFunc,
t_span: [F; 2],
x0: Array1<F>,
y0: Array1<F>,
options: DAEOptions<F>,
) -> IntegrateResult<DAEResult<F>>
where
F: IntegrateFloat,
FFunc: Fn(F, ArrayView1<F>, ArrayView1<F>) -> Array1<F>,
GFunc: Fn(F, ArrayView1<F>, ArrayView1<F>) -> Array1<F>,
{
let n_x = x0.len();
let n_y = y0.len();
let n_total = n_x + n_y;
let mut t_values = vec![t_span[0]];
let mut x_values = vec![x0.clone()];
let mut y_values = vec![y0.clone()];
let mut t_current = t_span[0];
let mut _x_current = x0.clone();
let mut _y_current = y0.clone();
let mut h = options.h0.unwrap_or_else(|| {
let _span = t_span[1] - t_span[0];
_span * const_f64::<F>(0.01) });
let min_step = options.min_step.unwrap_or_else(|| {
let _span = t_span[1] - t_span[0];
_span * const_f64::<F>(1e-6) });
let max_step = options.max_step.unwrap_or_else(|| {
let _span = t_span[1] - t_span[0];
_span * const_f64::<F>(0.1) });
let max_order = options.max_order.unwrap_or(5).min(5);
let rtol = options.rtol;
let atol = options.atol;
let gmres_tol = F::from_f64(GMRES_TOL).expect("Failed to convert to float");
let max_gmres_iter = options.max_newton_iterations * 3;
let mut n_steps = 0;
let mut n_accepted = 0;
let mut n_rejected = 0;
let mut n_f_evals = 0;
let mut n_g_evals = 0;
let mut n_jac_evals = 0;
let mut n_krylov_iters = 0;
let alpha_coeffs = [
vec![const_f64::<F>(-1.0)],
vec![
F::from_f64(-4.0 / 3.0).expect("Failed to convert to float"),
F::from_f64(1.0 / 3.0).expect("Failed to convert to float"),
],
vec![
F::from_f64(-18.0 / 11.0).expect("Failed to convert to float"),
F::from_f64(9.0 / 11.0).expect("Failed to convert to float"),
F::from_f64(-2.0 / 11.0).expect("Failed to convert to float"),
],
vec![
F::from_f64(-48.0 / 25.0).expect("Failed to convert to float"),
F::from_f64(36.0 / 25.0).expect("Failed to convert to float"),
F::from_f64(-16.0 / 25.0).expect("Failed to convert to float"),
F::from_f64(3.0 / 25.0).expect("Failed to convert to float"),
],
vec![
F::from_f64(-300.0 / 137.0).expect("Failed to convert to float"),
F::from_f64(300.0 / 137.0).expect("Failed to convert to float"),
F::from_f64(-200.0 / 137.0).expect("Failed to convert to float"),
F::from_f64(75.0 / 137.0).expect("Failed to convert to float"),
F::from_f64(-12.0 / 137.0).expect("Failed to convert to float"),
],
];
let beta_coeffs = [
const_f64::<F>(1.0), F::from_f64(2.0 / 3.0).expect("Failed to convert to float"), F::from_f64(6.0 / 11.0).expect("Failed to convert to float"), F::from_f64(12.0 / 25.0).expect("Failed to convert to float"), F::from_f64(60.0 / 137.0).expect("Failed to convert to float"), ];
let mut order = 1;
while t_current < t_span[1] && n_steps < options.max_steps {
if t_current + h > t_span[1] {
h = t_span[1] - t_current;
}
let t_new = t_current + h;
let (x_pred, y_pred) = predict_step(&x_values, &y_values, order, h);
let x_pred_orig = x_pred.clone();
let y_pred_orig = y_pred.clone();
let _g_pred = g(t_new, x_pred.view(), y_pred.view());
n_g_evals += 1;
let alpha = alpha_coeffs[order - 1].clone();
let beta = beta_coeffs[order - 1];
let mut x_history = Vec::with_capacity(order);
for i in 0..order {
let idx = x_values.len() - 1 - i;
x_history.push(x_values[idx].clone());
}
let mut x_corr = x_pred.clone();
let mut y_corr = y_pred.clone();
let max_newton_iter = options.max_newton_iterations;
let mut converged = false;
for _iter in 0..max_newton_iter {
let f_val = f(t_new, x_corr.view(), y_corr.view());
n_f_evals += 1;
let g_val = g(t_new, x_corr.view(), y_corr.view());
n_g_evals += 1;
let mut residual_x = Array1::zeros(n_x);
for i in 0..n_x {
residual_x[i] = x_corr[i];
for j in 0..order {
residual_x[i] += alpha[j] * x_history[j][i];
}
residual_x[i] -= h * beta * f_val[i];
}
let residual_g = g_val;
let res_x_norm = residual_x
.iter()
.fold(F::zero(), |acc, &val| acc + val * val)
.sqrt();
let res_g_norm = residual_g
.iter()
.fold(F::zero(), |acc, &val| acc + val * val)
.sqrt();
let residual_norm = (res_x_norm * res_x_norm + res_g_norm * res_g_norm).sqrt();
if residual_norm < options.newton_tol {
converged = true;
break;
}
let f_x = compute_jacobian_x(
&f,
t_new,
x_corr.view(),
y_corr.view(),
const_f64::<F>(1e-8),
);
let f_y = compute_jacobian_y(
&f,
t_new,
x_corr.view(),
y_corr.view(),
const_f64::<F>(1e-8),
);
n_jac_evals += 2;
let g_x = compute_jacobian_x(
&g,
t_new,
x_corr.view(),
y_corr.view(),
const_f64::<F>(1e-8),
);
let g_y = compute_jacobian_y(
&g,
t_new,
x_corr.view(),
y_corr.view(),
const_f64::<F>(1e-8),
);
n_jac_evals += 2;
let mut full_residual = Array1::<F>::zeros(n_total);
for i in 0..n_x {
full_residual[i] = residual_x[i];
}
for i in 0..n_y {
full_residual[n_x + i] = residual_g[i];
}
let neg_residual = full_residual.mapv(|x| -x);
let jacobian_matvec = |v: &Array1<F>| -> Array1<F> {
let mut result = Array1::<F>::zeros(n_total);
let v_x = v.slice(scirs2_core::ndarray::s![0..n_x]).to_owned();
let v_y = v.slice(scirs2_core::ndarray::s![n_x..]).to_owned();
for i in 0..n_x {
result[i] = v_x[i]; for j in 0..n_x {
result[i] -= h * beta * f_x[[i, j]] * v_x[j];
}
}
for i in 0..n_x {
for j in 0..n_y {
result[i] -= h * beta * f_y[[i, j]] * v_y[j];
}
}
for i in 0..n_y {
for j in 0..n_x {
result[n_x + i] += g_x[[i, j]] * v_x[j];
}
}
for i in 0..n_y {
for j in 0..n_y {
result[n_x + i] += g_y[[i, j]] * v_y[j];
}
}
result
};
let diag_preconditioner = |v: &Array1<F>| -> Array1<F> {
let mut diag = Array1::<F>::ones(n_total);
for i in 0..n_x {
diag[i] = F::one() - h * beta * f_x[[i, i]];
if diag[i].abs() < const_f64::<F>(1e-14) {
diag[i] = const_f64::<F>(1e-14) * diag[i].signum();
}
diag[i] = F::one() / diag[i];
}
for i in 0..n_y {
diag[n_x + i] = g_y[[i, i]];
if diag[n_x + i].abs() < const_f64::<F>(1e-14) {
diag[n_x + i] = const_f64::<F>(1e-14) * diag[n_x + i].signum();
}
diag[n_x + i] = F::one() / diag[n_x + i];
}
v.iter()
.zip(diag.iter())
.map(|(&vi, &di)| vi * di)
.collect()
};
let (delta_z, gmres_iterations) = match gmres_solver(
jacobian_matvec,
&neg_residual,
Some(diag_preconditioner),
gmres_tol,
max_gmres_iter,
GMRES_RESTART,
) {
Ok((dz, iters)) => (dz, iters),
Err(_e) => {
h *= const_f64::<F>(0.5);
break;
}
};
n_krylov_iters += gmres_iterations;
let delta_x = delta_z.slice(scirs2_core::ndarray::s![0..n_x]).to_owned();
let delta_y = delta_z.slice(scirs2_core::ndarray::s![n_x..]).to_owned();
let mut alpha_damp = F::one();
let min_alpha = const_f64::<F>(0.1);
while alpha_damp >= min_alpha {
let x_new = &x_corr + &(&delta_x * alpha_damp);
let y_new = &y_corr + &(&delta_y * alpha_damp);
let f_new = f(t_new, x_new.view(), y_new.view());
let g_new = g(t_new, x_new.view(), y_new.view());
n_f_evals += 1;
n_g_evals += 1;
let mut residual_x_new = Array1::zeros(n_x);
for i in 0..n_x {
residual_x_new[i] = x_new[i];
for j in 0..order {
residual_x_new[i] += alpha[j] * x_history[j][i];
}
residual_x_new[i] -= h * beta * f_new[i];
}
let res_x_new_norm = residual_x_new
.iter()
.fold(F::zero(), |acc, &val| acc + val * val)
.sqrt();
let res_g_new_norm = g_new
.iter()
.fold(F::zero(), |acc, &val| acc + val * val)
.sqrt();
let residual_new_norm =
(res_x_new_norm * res_x_new_norm + res_g_new_norm * res_g_new_norm).sqrt();
if residual_new_norm < residual_norm {
x_corr = x_new;
y_corr = y_new;
break;
}
alpha_damp *= const_f64::<F>(0.5);
}
if alpha_damp < min_alpha {
h *= const_f64::<F>(0.5);
break;
}
}
if !converged {
h *= const_f64::<F>(0.5);
if h < min_step {
return Err(IntegrateError::ComputationError(format!(
"Failed to converge at t = {t_current}. Step size too small."
)));
}
n_rejected += 1;
continue;
}
n_steps += 1;
n_accepted += 1;
let error_x = (&x_corr - &x_pred_orig).mapv(|x| x.abs());
let error_y = (&y_corr - &y_pred_orig).mapv(|x| x.abs());
let mut error_norm_x = F::zero();
for i in 0..n_x {
let scale = atol + rtol * x_corr[i].abs();
error_norm_x += (error_x[i] / scale).powi(2);
}
error_norm_x = (error_norm_x / F::from_usize(n_x).expect("Operation failed")).sqrt();
let mut error_norm_y = F::zero();
for i in 0..n_y {
let scale = atol + rtol * y_corr[i].abs();
error_norm_y += (error_y[i] / scale).powi(2);
}
error_norm_y = (error_norm_y / F::from_usize(n_y).expect("Operation failed")).sqrt();
let error_norm = error_norm_x.max(error_norm_y);
let error_order = order as i32;
let safety = const_f64::<F>(0.9);
let h_new = if error_norm > F::zero() {
h * safety
* (F::one() / error_norm)
.powf(F::one() / F::from_i32(error_order).expect("Operation failed"))
} else {
h * const_f64::<F>(2.0) };
let max_increase = const_f64::<F>(2.0);
let max_decrease = const_f64::<F>(0.1);
let h_new = (h_new / h).max(max_decrease).min(max_increase) * h;
let h_new = h_new.min(max_step).max(min_step);
h = h_new;
t_values.push(t_new);
x_values.push(x_corr.clone());
y_values.push(y_corr.clone());
t_current = t_new;
_x_current = x_corr;
_y_current = y_corr;
if n_steps >= 5 {
if order < max_order {
order = (order + 1).min(max_order);
}
}
}
let success = t_current >= t_span[1];
let result = DAEResult {
t: t_values,
x: x_values,
y: y_values,
success,
message: if success {
Some(format!(
"Successful integration. {n_steps} steps taken, {n_krylov_iters} GMRES iterations."
))
} else {
Some(format!(
"Integration did not reach end time. {n_steps} steps taken, {n_krylov_iters} GMRES iterations."
))
},
n_eval: n_f_evals,
n_constraint_eval: n_g_evals,
n_steps,
n_accepted,
n_rejected,
n_lu: 0, n_jac: n_jac_evals,
method: ODEMethod::Bdf,
dae_type: DAEType::SemiExplicit,
index: DAEIndex::Index1,
};
Ok(result)
}
#[allow(dead_code)]
pub fn krylov_bdf_implicit_dae<F, FFunc>(
f: FFunc,
t_span: [F; 2],
y0: Array1<F>,
y_prime0: Array1<F>,
options: DAEOptions<F>,
) -> IntegrateResult<DAEResult<F>>
where
F: IntegrateFloat,
FFunc: Fn(F, ArrayView1<F>, ArrayView1<F>) -> Array1<F>,
{
let n = y0.len();
let mut t_values = vec![t_span[0]];
let mut y_values = vec![y0.clone()];
let mut y_prime_values = vec![y_prime0.clone()];
let mut t_current = t_span[0];
let mut y_current = y0.clone();
let mut _y_prime_current = y_prime0.clone();
let mut h = options.h0.unwrap_or_else(|| {
let _span = t_span[1] - t_span[0];
_span * const_f64::<F>(0.01) });
let min_step = options.min_step.unwrap_or_else(|| {
let _span = t_span[1] - t_span[0];
_span * const_f64::<F>(1e-6) });
let max_step = options.max_step.unwrap_or_else(|| {
let _span = t_span[1] - t_span[0];
_span * const_f64::<F>(0.1) });
let max_order = options.max_order.unwrap_or(5).min(5);
let rtol = options.rtol;
let atol = options.atol;
let gmres_tol = F::from_f64(GMRES_TOL).expect("Failed to convert to float");
let max_gmres_iter = options.max_newton_iterations * 3;
let mut n_steps = 0;
let mut n_accepted = 0;
let mut n_rejected = 0;
let mut n_f_evals = 0;
let mut n_jac_evals = 0;
let mut n_krylov_iters = 0;
let alpha_coeffs = [
vec![const_f64::<F>(-1.0)],
vec![
F::from_f64(-4.0 / 3.0).expect("Failed to convert to float"),
F::from_f64(1.0 / 3.0).expect("Failed to convert to float"),
],
vec![
F::from_f64(-18.0 / 11.0).expect("Failed to convert to float"),
F::from_f64(9.0 / 11.0).expect("Failed to convert to float"),
F::from_f64(-2.0 / 11.0).expect("Failed to convert to float"),
],
vec![
F::from_f64(-48.0 / 25.0).expect("Failed to convert to float"),
F::from_f64(36.0 / 25.0).expect("Failed to convert to float"),
F::from_f64(-16.0 / 25.0).expect("Failed to convert to float"),
F::from_f64(3.0 / 25.0).expect("Failed to convert to float"),
],
vec![
F::from_f64(-300.0 / 137.0).expect("Failed to convert to float"),
F::from_f64(300.0 / 137.0).expect("Failed to convert to float"),
F::from_f64(-200.0 / 137.0).expect("Failed to convert to float"),
F::from_f64(75.0 / 137.0).expect("Failed to convert to float"),
F::from_f64(-12.0 / 137.0).expect("Failed to convert to float"),
],
];
let beta_coeffs = [
const_f64::<F>(1.0), F::from_f64(2.0 / 3.0).expect("Failed to convert to float"), F::from_f64(6.0 / 11.0).expect("Failed to convert to float"), F::from_f64(12.0 / 25.0).expect("Failed to convert to float"), F::from_f64(60.0 / 137.0).expect("Failed to convert to float"), ];
let mut order = 1;
while t_current < t_span[1] && n_steps < options.max_steps {
if t_current + h > t_span[1] {
h = t_span[1] - t_current;
}
let t_new = t_current + h;
let history_start = if y_values.len() >= order {
y_values.len() - order
} else {
0
};
let _yhistory = &y_values[history_start..];
let y_pred = predict_fully_implicit(_yhistory, order);
let y_prime_pred = if order == 1 {
(&y_pred - &y_current) / h
} else {
let mut y_prime = Array1::zeros(n);
let alpha = &alpha_coeffs[order - 1];
let beta = beta_coeffs[order - 1];
for i in 0..n {
y_prime[i] = y_pred[i];
for (j, &alpha_j) in alpha.iter().enumerate().take(order) {
let idx = y_values.len() - 1 - j;
if idx < y_values.len() {
y_prime[i] += alpha_j * y_values[idx][i];
}
}
y_prime[i] /= h * beta;
}
y_prime
};
let y_pred_orig = y_pred.clone();
let _y_prime_pred_orig = y_prime_pred.clone();
let mut y_corr = y_pred;
let mut y_prime_corr = y_prime_pred;
let max_newton_iter = options.max_newton_iterations;
let mut converged = false;
for _iter in 0..max_newton_iter {
let residual = f(t_new, y_corr.view(), y_prime_corr.view());
n_f_evals += 1;
let residual_norm = residual
.iter()
.fold(F::zero(), |acc, &val| acc + val * val)
.sqrt();
if residual_norm < options.newton_tol {
converged = true;
break;
}
let jac_y = compute_jacobian_y_implicit(
&f,
t_new,
y_corr.view(),
y_prime_corr.view(),
const_f64::<F>(1e-8),
);
let jac_yprime = compute_jacobian_yprime_implicit(
&f,
t_new,
y_corr.view(),
y_prime_corr.view(),
const_f64::<F>(1e-8),
);
n_jac_evals += 2;
let beta = beta_coeffs[order - 1];
let scale = F::one() / (h * beta);
let jacobian_matvec = |v: &Array1<F>| -> Array1<F> {
let mut result = Array1::<F>::zeros(n);
for i in 0..n {
for j in 0..n {
result[i] += jac_y[[i, j]] * v[j];
}
}
for i in 0..n {
for j in 0..n {
result[i] += jac_yprime[[i, j]] * scale * v[j];
}
}
result
};
let diag_preconditioner = |v: &Array1<F>| -> Array1<F> {
let mut diag = Array1::<F>::zeros(n);
for i in 0..n {
diag[i] = jac_y[[i, i]] + jac_yprime[[i, i]] * scale;
if diag[i].abs() < const_f64::<F>(1e-14) {
diag[i] = const_f64::<F>(1e-14) * diag[i].signum();
}
diag[i] = F::one() / diag[i];
}
v.iter()
.zip(diag.iter())
.map(|(&vi, &di)| vi * di)
.collect()
};
let neg_residual = residual.mapv(|x| -x);
let (delta_y, gmres_iterations) = match gmres_solver(
jacobian_matvec,
&neg_residual,
Some(diag_preconditioner),
gmres_tol,
max_gmres_iter,
GMRES_RESTART,
) {
Ok((dy, iters)) => (dy, iters),
Err(_e) => {
h *= const_f64::<F>(0.5);
break;
}
};
n_krylov_iters += gmres_iterations;
let mut alpha_damp = F::one();
let min_alpha = const_f64::<F>(0.1);
while alpha_damp >= min_alpha {
let y_new = &y_corr + &(&delta_y * alpha_damp);
let mut y_prime_new = Array1::zeros(n);
let alpha = &alpha_coeffs[order - 1];
let beta = beta_coeffs[order - 1];
for i in 0..n {
y_prime_new[i] = y_new[i];
for j in 0..order {
if j < _yhistory.len() {
y_prime_new[i] += alpha[j] * _yhistory[_yhistory.len() - 1 - j][i];
}
}
y_prime_new[i] /= h * beta;
}
let residual_new = f(t_new, y_new.view(), y_prime_new.view());
n_f_evals += 1;
let residual_new_norm = residual_new
.iter()
.fold(F::zero(), |acc, &val| acc + val * val)
.sqrt();
if residual_new_norm < residual_norm {
y_corr = y_new;
y_prime_corr = y_prime_new;
break;
}
alpha_damp *= const_f64::<F>(0.5);
}
if alpha_damp < min_alpha {
h *= const_f64::<F>(0.5);
break;
}
}
if !converged {
h *= const_f64::<F>(0.5);
if h < min_step {
return Err(IntegrateError::ComputationError(format!(
"Failed to converge at t = {t_current}. Step size too small."
)));
}
n_rejected += 1;
continue;
}
n_steps += 1;
n_accepted += 1;
let error = (&y_corr - &y_pred_orig).mapv(|x| x.abs());
let mut error_norm = F::zero();
for i in 0..n {
let scale = atol + rtol * y_corr[i].abs();
error_norm += (error[i] / scale).powi(2);
}
error_norm = (error_norm / F::from_usize(n).expect("Operation failed")).sqrt();
let error_order = order as i32;
let safety = const_f64::<F>(0.9);
let h_new = if error_norm > F::zero() {
h * safety
* (F::one() / error_norm)
.powf(F::one() / F::from_i32(error_order).expect("Operation failed"))
} else {
h * const_f64::<F>(2.0) };
let max_increase = const_f64::<F>(2.0);
let max_decrease = const_f64::<F>(0.1);
let h_new = (h_new / h).max(max_decrease).min(max_increase) * h;
let h_new = h_new.min(max_step).max(min_step);
h = h_new;
t_values.push(t_new);
y_values.push(y_corr.clone());
y_prime_values.push(y_prime_corr.clone());
t_current = t_new;
y_current = y_corr;
_y_prime_current = y_prime_corr;
if n_steps >= 5 {
if order < max_order {
order = (order + 1).min(max_order);
}
}
}
let success = t_current >= t_span[1];
let empty_array = Array1::<F>::zeros(0);
let empty_y = vec![empty_array; t_values.len()];
let result = DAEResult {
t: t_values,
x: y_values,
y: empty_y,
success,
message: if success {
Some(format!(
"Successful integration. {n_steps} steps taken, {n_krylov_iters} GMRES iterations."
))
} else {
Some(format!(
"Integration did not reach end time. {n_steps} steps taken, {n_krylov_iters} GMRES iterations."
))
},
n_eval: n_f_evals,
n_constraint_eval: 0, n_steps,
n_accepted,
n_rejected,
n_lu: 0, n_jac: n_jac_evals,
method: ODEMethod::Bdf,
dae_type: DAEType::FullyImplicit,
index: DAEIndex::Index1,
};
Ok(result)
}
#[allow(dead_code)]
fn gmres_solver<F>(
matvec: impl Fn(&Array1<F>) -> Array1<F>,
b: &Array1<F>,
preconditioner: Option<impl Fn(&Array1<F>) -> Array1<F>>,
tol: F,
max_iter: usize,
restart: usize,
) -> IntegrateResult<(Array1<F>, usize)>
where
F: IntegrateFloat,
{
let n = b.len();
let mut x = Array1::<F>::zeros(n);
let mut total_iters = 0;
let b_precond = match &preconditioner {
Some(precond) => precond(b),
None => b.clone(),
};
let r0 = if x.iter().all(|&v| v == F::zero()) {
b_precond.clone()
} else {
let ax = matvec(&x);
let residual = b - &ax;
match &preconditioner {
Some(precond) => precond(&residual),
None => residual,
}
};
let r0_norm = r0.iter().fold(F::zero(), |acc, &v| acc + v * v).sqrt();
if r0_norm <= tol {
return Ok((x, 0)); }
let scaled_tol = tol * r0_norm;
let mut cycles = 0;
let max_cycles = max_iter.div_ceil(restart);
while cycles < max_cycles {
let (h, q, residual_norm, iters) =
arnoldi_process(&matvec, &preconditioner, &r0, r0_norm, scaled_tol, restart)?;
total_iters += iters;
let y = solve_upper_triangular(&h, &residual_norm, iters);
for i in 0..iters {
for j in 0..n {
x[j] += q[i][j] * y[i];
}
}
if residual_norm <= scaled_tol {
break;
}
let ax = matvec(&x);
let residual = b - &ax;
let _r0 = match &preconditioner {
Some(precond) => precond(&residual),
None => residual,
};
cycles += 1;
}
Ok((x, total_iters))
}
#[allow(dead_code)]
fn arnoldi_process<F>(
matvec: &impl Fn(&Array1<F>) -> Array1<F>,
preconditioner: &Option<impl Fn(&Array1<F>) -> Array1<F>>,
r0: &Array1<F>,
r0_norm: F,
tol: F,
max_iter: usize,
) -> IntegrateResult<GMRESResult<F>>
where
F: IntegrateFloat,
{
let n = r0.len();
let m = max_iter.min(n);
let mut q = Vec::with_capacity(m + 1);
q.push(r0 / r0_norm);
let mut h = Array2::<F>::zeros((m + 1, m));
let mut cs = Array1::<F>::zeros(m);
let mut sn = Array1::<F>::zeros(m);
let mut g = Array1::<F>::zeros(m + 1);
g[0] = r0_norm;
for j in 0..m {
let aq = matvec(&q[j]);
let mut w = match preconditioner {
Some(precond) => precond(&aq),
None => aq,
};
for i in 0..=j {
h[[i, j]] = dot(&q[i], &w);
for k in 0..n {
w[k] -= h[[i, j]] * q[i][k];
}
}
let w_norm = w.iter().fold(F::zero(), |acc, &val| acc + val * val).sqrt();
h[[j + 1, j]] = w_norm;
if w_norm < const_f64::<F>(1e-14) {
return Ok((h, q, g[j + 1], j + 1));
}
q.push(w / w_norm);
for i in 0..j {
let temp = h[[i, j]];
h[[i, j]] = cs[i] * temp + sn[i] * h[[i + 1, j]];
h[[i + 1, j]] = -sn[i] * temp + cs[i] * h[[i + 1, j]];
}
let (c, s) = givens_rotation(h[[j, j]], h[[j + 1, j]]);
cs[j] = c;
sn[j] = s;
let temp = h[[j, j]];
h[[j, j]] = c * temp + s * h[[j + 1, j]];
h[[j + 1, j]] = F::zero();
let temp = g[j];
g[j] = c * temp;
g[j + 1] = -s * temp;
if g[j + 1].abs() <= tol {
return Ok((h, q, g[j + 1].abs(), j + 1));
}
}
Ok((h, q, g[m].abs(), m))
}
#[allow(dead_code)]
fn dot<F>(a: &Array1<F>, b: &Array1<F>) -> F
where
F: IntegrateFloat,
{
a.iter()
.zip(b.iter())
.fold(F::zero(), |acc, (&ai, &bi)| acc + ai * bi)
}
#[allow(dead_code)]
fn givens_rotation<F>(a: F, b: F) -> (F, F)
where
F: IntegrateFloat,
{
if b == F::zero() {
(F::one(), F::zero())
} else if a.abs() < b.abs() {
let t = -a / b;
let s = F::one() / (F::one() + t * t).sqrt();
let c = s * t;
(-c, s)
} else {
let t = -b / a;
let c = F::one() / (F::one() + t * t).sqrt();
let s = c * t;
(c, s)
}
}
#[allow(dead_code)]
fn solve_upper_triangular<F>(r: &Array2<F>, g: &F, n: usize) -> Array1<F>
where
F: IntegrateFloat,
{
let mut x = Array1::<F>::zeros(n);
let mut g_vec = Array1::<F>::zeros(n);
g_vec[0] = *g;
for i in (0..n).rev() {
let mut sum = F::zero();
for j in (i + 1)..n {
sum += r[[i, j]] * x[j];
}
x[i] = (g_vec[i] - sum) / r[[i, i]];
}
x
}
#[allow(dead_code)]
fn predict_step<F>(
x_history: &[Array1<F>],
_yhistory: &[Array1<F>],
order: usize,
h: F,
) -> (Array1<F>, Array1<F>)
where
F: IntegrateFloat,
{
let n_x = x_history[0].len();
let n_y = _yhistory[0].len();
let history_len = x_history.len();
if history_len < 2 || order == 1 {
return (
x_history[history_len - 1].clone(),
_yhistory[history_len - 1].clone(),
);
}
let order_to_use = order.min(history_len - 1);
let mut x_pred = x_history[history_len - 1].clone();
let mut y_pred = _yhistory[history_len - 1].clone();
if order_to_use == 1 {
for i in 0..n_x {
x_pred[i] = x_history[history_len - 1][i]
+ (x_history[history_len - 1][i] - x_history[history_len - 2][i]);
}
for i in 0..n_y {
y_pred[i] = _yhistory[history_len - 1][i]
+ (_yhistory[history_len - 1][i] - _yhistory[history_len - 2][i]);
}
return (x_pred, y_pred);
}
let scaling = F::from_f64(1.0 + 0.3 * order_to_use as f64).expect("Failed to convert to float");
for i in 0..n_x {
x_pred[i] = x_history[history_len - 1][i]
+ (x_history[history_len - 1][i] - x_history[history_len - 2][i]) * scaling;
}
for i in 0..n_y {
y_pred[i] = _yhistory[history_len - 1][i]
+ (_yhistory[history_len - 1][i] - _yhistory[history_len - 2][i]) * scaling;
}
(x_pred, y_pred)
}
#[allow(dead_code)]
fn predict_fully_implicit<F>(_yhistory: &[Array1<F>], order: usize) -> Array1<F>
where
F: IntegrateFloat,
{
let n = _yhistory[0].len();
let history_len = _yhistory.len();
if history_len < 2 || order == 1 {
return _yhistory[history_len - 1].clone();
}
let mut y_pred = _yhistory[history_len - 1].clone();
for i in 0..n {
y_pred[i] = _yhistory[history_len - 1][i]
+ (_yhistory[history_len - 1][i] - _yhistory[history_len - 2][i]);
}
y_pred
}
#[allow(dead_code)]
fn compute_jacobian_x<F, Func>(
f: &Func,
t: F,
x: ArrayView1<F>,
y: ArrayView1<F>,
epsilon: F,
) -> Array2<F>
where
F: IntegrateFloat,
Func: Fn(F, ArrayView1<F>, ArrayView1<F>) -> Array1<F>,
{
let n_x = x.len();
let n_f = f(t, x, y).len();
let mut jacobian = Array2::<F>::zeros((n_f, n_x));
let f_base = f(t, x, y);
let mut x_perturbed = x.to_owned();
for j in 0..n_x {
let h = epsilon.max(x[j].abs() * epsilon);
x_perturbed[j] = x[j] + h;
let f_perturbed = f(t, x_perturbed.view(), y);
x_perturbed[j] = x[j];
let col_j = (f_perturbed - &f_base) / h;
for i in 0..n_f {
jacobian[[i, j]] = col_j[i];
}
}
jacobian
}
#[allow(dead_code)]
fn compute_jacobian_y<F, Func>(
f: &Func,
t: F,
x: ArrayView1<F>,
y: ArrayView1<F>,
epsilon: F,
) -> Array2<F>
where
F: IntegrateFloat,
Func: Fn(F, ArrayView1<F>, ArrayView1<F>) -> Array1<F>,
{
let n_y = y.len();
let n_f = f(t, x, y).len();
let mut jacobian = Array2::<F>::zeros((n_f, n_y));
let f_base = f(t, x, y);
let mut y_perturbed = y.to_owned();
for j in 0..n_y {
let h = epsilon.max(y[j].abs() * epsilon);
y_perturbed[j] = y[j] + h;
let f_perturbed = f(t, x, y_perturbed.view());
y_perturbed[j] = y[j];
let col_j = (f_perturbed - &f_base) / h;
for i in 0..n_f {
jacobian[[i, j]] = col_j[i];
}
}
jacobian
}
#[allow(dead_code)]
fn compute_jacobian_y_implicit<F, Func>(
f: &Func,
t: F,
y: ArrayView1<F>,
y_prime: ArrayView1<F>,
epsilon: F,
) -> Array2<F>
where
F: IntegrateFloat,
Func: Fn(F, ArrayView1<F>, ArrayView1<F>) -> Array1<F>,
{
let n = y.len();
let n_f = f(t, y, y_prime).len();
let mut jacobian = Array2::<F>::zeros((n_f, n));
let f_base = f(t, y, y_prime);
let mut y_perturbed = y.to_owned();
for j in 0..n {
let h = epsilon.max(y[j].abs() * epsilon);
y_perturbed[j] = y[j] + h;
let f_perturbed = f(t, y_perturbed.view(), y_prime);
y_perturbed[j] = y[j];
let col_j = (f_perturbed - &f_base) / h;
for i in 0..n_f {
jacobian[[i, j]] = col_j[i];
}
}
jacobian
}
#[allow(dead_code)]
fn compute_jacobian_yprime_implicit<F, Func>(
f: &Func,
t: F,
y: ArrayView1<F>,
y_prime: ArrayView1<F>,
epsilon: F,
) -> Array2<F>
where
F: IntegrateFloat,
Func: Fn(F, ArrayView1<F>, ArrayView1<F>) -> Array1<F>,
{
let n = y_prime.len();
let n_f = f(t, y, y_prime).len();
let mut jacobian = Array2::<F>::zeros((n_f, n));
let f_base = f(t, y, y_prime);
let mut y_prime_perturbed = y_prime.to_owned();
for j in 0..n {
let h = epsilon.max(y_prime[j].abs() * epsilon);
y_prime_perturbed[j] = y_prime[j] + h;
let f_perturbed = f(t, y, y_prime_perturbed.view());
y_prime_perturbed[j] = y_prime[j];
let col_j = (f_perturbed - &f_base) / h;
for i in 0..n_f {
jacobian[[i, j]] = col_j[i];
}
}
jacobian
}