extern crate nalgebra as na;
extern crate num_traits;
extern crate sprs;
use na::{DMatrix, DVector, Dyn, LU};
use log::info;
use std::f64;
use std::ops::AddAssign;
use crate::numerical::BDF::BDF_utils::{OrderEnum, group_columns};
use crate::numerical::BDF::common::{
NumberOrVec, check_arguments, is_sparse, newton_tol, norm, scale_func, select_initial_step,
validate_first_step, validate_max_step, validate_tol,
};
use crate::somelinalg::banded::storage::Banded;
use faer::sparse::Triplet;
use std::fmt::Debug;
use std::fmt::Display;
const MAX_ORDER: usize = 5;
const NEWTON_MAXITER: usize = 4;
const MIN_FACTOR: f64 = 0.2;
const MAX_FACTOR: f64 = 10.0;
const SPARSE: f64 = 0.01;
pub trait BdfLinearFactorization {
fn solve(&self, rhs: &DVector<f64>) -> Option<DVector<f64>>;
}
pub trait BdfLinearBackend {
fn factor(&mut self, matrix: &DMatrix<f64>) -> Option<Box<dyn BdfLinearFactorization>>;
fn factor_shifted_jacobian(
&mut self,
c: f64,
jacobian: &BdfJacobian,
) -> Option<Box<dyn BdfLinearFactorization>> {
let matrix = jacobian.to_shifted_dense(c)?;
self.factor(&matrix)
}
}
#[derive(Clone, Debug)]
pub enum BdfJacobian {
Dense(DMatrix<f64>),
SparseTriplets {
n: usize,
triplets: Vec<Triplet<usize, usize, f64>>,
},
Banded(Banded<f64>),
}
impl BdfJacobian {
pub fn from_dense(matrix: DMatrix<f64>) -> Self {
Self::Dense(matrix)
}
pub fn n(&self) -> usize {
match self {
Self::Dense(matrix) => matrix.nrows(),
Self::SparseTriplets { n, .. } => *n,
Self::Banded(matrix) => matrix.n(),
}
}
pub fn shape(&self) -> (usize, usize) {
let n = self.n();
(n, n)
}
pub fn to_shifted_dense(&self, c: f64) -> Option<DMatrix<f64>> {
let n = self.n();
let mut out = DMatrix::identity(n, n);
match self {
Self::Dense(matrix) => {
if matrix.nrows() != matrix.ncols() {
return None;
}
out -= c * matrix;
}
Self::SparseTriplets { triplets, .. } => {
for triplet in triplets {
if triplet.row >= n || triplet.col >= n {
return None;
}
out[(triplet.row, triplet.col)] -= c * triplet.val;
}
}
Self::Banded(matrix) => {
for j in 0..n {
let i0 = j.saturating_sub(matrix.ku());
let i1 = (j + matrix.kl() + 1).min(n);
for i in i0..i1 {
out[(i, j)] -= c * matrix[(i, j)];
}
}
}
}
Some(out)
}
pub fn to_shifted_sparse_triplets(&self, c: f64) -> Option<Vec<Triplet<usize, usize, f64>>> {
let n = self.n();
let mut triplets = Vec::new();
let mut diagonal = vec![1.0; n];
match self {
Self::Dense(matrix) => {
if matrix.nrows() != matrix.ncols() {
return None;
}
for j in 0..n {
for i in 0..n {
let value = matrix[(i, j)];
if value != 0.0 {
if i == j {
diagonal[i] -= c * value;
} else {
triplets.push(Triplet::new(i, j, -c * value));
}
}
}
}
}
Self::SparseTriplets {
n: sparse_n,
triplets: sparse_triplets,
} => {
if *sparse_n != n {
return None;
}
for triplet in sparse_triplets {
if triplet.row >= n || triplet.col >= n {
return None;
}
if triplet.row == triplet.col {
diagonal[triplet.row] -= c * triplet.val;
} else {
triplets.push(Triplet::new(triplet.row, triplet.col, -c * triplet.val));
}
}
}
Self::Banded(matrix) => {
for j in 0..n {
let i0 = j.saturating_sub(matrix.ku());
let i1 = (j + matrix.kl() + 1).min(n);
for i in i0..i1 {
let value = matrix[(i, j)];
if value != 0.0 {
if i == j {
diagonal[i] -= c * value;
} else {
triplets.push(Triplet::new(i, j, -c * value));
}
}
}
}
}
}
for (i, value) in diagonal.into_iter().enumerate() {
if value != 0.0 {
triplets.push(Triplet::new(i, i, value));
}
}
Some(triplets)
}
pub fn to_shifted_banded(&self, c: f64) -> Option<Banded<f64>> {
match self {
Self::Banded(matrix) => {
let n = matrix.n();
let mut out = Banded::<f64>::zeros(n, matrix.kl(), matrix.ku()).ok()?;
out.fill_from_dense(|i, j| {
let identity = if i == j { 1.0 } else { 0.0 };
identity - c * matrix[(i, j)]
});
Some(out)
}
Self::Dense(matrix) => dense_shifted_to_banded(matrix, c),
Self::SparseTriplets { n, triplets } => {
let mut kl = 0usize;
let mut ku = 0usize;
for triplet in triplets {
if triplet.row >= *n || triplet.col >= *n {
return None;
}
kl = kl.max(triplet.row.saturating_sub(triplet.col));
ku = ku.max(triplet.col.saturating_sub(triplet.row));
}
let mut out = Banded::<f64>::zeros(*n, kl, ku).ok()?;
for i in 0..*n {
out.set(i, i, 1.0).ok()?;
}
for triplet in triplets {
let current = *out.get(triplet.row, triplet.col).unwrap_or(&0.0);
out.set(triplet.row, triplet.col, current - c * triplet.val)
.ok()?;
}
Some(out)
}
}
}
}
fn dense_shifted_to_banded(matrix: &DMatrix<f64>, c: f64) -> Option<Banded<f64>> {
if matrix.nrows() != matrix.ncols() {
return None;
}
let n = matrix.nrows();
let mut kl = 0usize;
let mut ku = 0usize;
for j in 0..n {
for i in 0..n {
let shifted = if i == j { 1.0 } else { 0.0 } - c * matrix[(i, j)];
if shifted != 0.0 {
kl = kl.max(i.saturating_sub(j));
ku = ku.max(j.saturating_sub(i));
}
}
}
let mut out = Banded::<f64>::zeros(n, kl, ku).ok()?;
out.fill_from_dense(|i, j| {
let identity = if i == j { 1.0 } else { 0.0 };
identity - c * matrix[(i, j)]
});
Some(out)
}
struct DenseNalgebraFactorization {
lu: LU<f64, Dyn, Dyn>,
}
impl BdfLinearFactorization for DenseNalgebraFactorization {
fn solve(&self, rhs: &DVector<f64>) -> Option<DVector<f64>> {
self.lu.solve(rhs)
}
}
struct DenseNalgebraLinearBackend;
impl BdfLinearBackend for DenseNalgebraLinearBackend {
fn factor(&mut self, matrix: &DMatrix<f64>) -> Option<Box<dyn BdfLinearFactorization>> {
Some(Box::new(DenseNalgebraFactorization {
lu: LU::new(matrix.clone()),
}))
}
}
fn cumulative_product_along_columns(matrix: &DMatrix<f64>) -> DMatrix<f64> {
let (rows, cols) = matrix.shape();
let mut result = DMatrix::zeros(rows, cols);
for col in 0..cols {
let mut cumprod = 1.0;
for row in 0..rows {
cumprod *= matrix[(row, col)];
result[(row, col)] = cumprod;
}
}
result
}
fn compute_r(order: usize, factor: f64) -> DMatrix<f64> {
let mut m = DMatrix::zeros(order + 1, order + 1);
for i in 1..(order + 1) {
for j in 1..(order + 1) {
m[(i, j)] = (i as f64 - 1.0 - factor * j as f64) / i as f64;
}
}
m.row_mut(0).fill(1.0);
let result = cumulative_product_along_columns(&m);
result
}
fn change_D(D: &mut DMatrix<f64>, order: usize, factor: f64) {
let r = compute_r(order, factor);
let u = compute_r(order, 1.0);
let ru = r * u;
let temp = ru.transpose() * D.rows(0, order + 1);
D.rows_mut(0, order + 1).copy_from(&temp);
}
fn solve_bdf_system<F>(
fun: F,
t_new: f64,
y_predict: &DVector<f64>,
c: f64,
psi: &DVector<f64>,
linear_factorization: &dyn BdfLinearFactorization,
scale: &DVector<f64>, tol: f64,
) -> (bool, usize, DVector<f64>, DVector<f64>)
where
F: Fn(f64, &DVector<f64>) -> DVector<f64>,
{
let mut d = DVector::zeros(y_predict.len()); let mut y = y_predict.clone();
let mut dy_norm_old: Option<f64> = None;
let mut converged = false;
let mut k_: usize = 0;
for k in 0..NEWTON_MAXITER {
let f = fun(t_new, &y);
if !f.iter().all(|&x| x.is_finite()) {
break;
}
let Some(dy) = linear_factorization.solve(&(c * &f - psi - &d)) else {
break;
};
let dy_norm = norm(&(dy.component_div(scale)));
let rate: Option<f64> = if let Some(dy_norm_old) = dy_norm_old {
Some(dy_norm / dy_norm_old)
} else {
None
};
if let Some(rate) = rate {
if rate >= 1.0
|| (rate.powi((NEWTON_MAXITER - k) as i32) / (1.0 - rate)) * dy_norm > tol
{
break;
}
}
y += &dy;
d += &dy;
k_ = k;
if dy_norm == 0.0 {
converged = true;
break;
}
if let Some(rate) = rate {
if rate / (1.0 - rate) * dy_norm < tol {
converged = true;
break;
}
}
dy_norm_old = Some(dy_norm);
}
(converged, k_ + 1, y, d)
}
fn finite_difference_jacobian_rhs(
fun: &dyn Fn(f64, &DVector<f64>) -> DVector<f64>,
t: f64,
y: &DVector<f64>,
) -> DMatrix<f64> {
let n = y.len();
if n == 0 {
return DMatrix::zeros(0, 0);
}
let f0 = fun(t, y);
let mut jac = DMatrix::zeros(n, n);
let eps_base = f64::EPSILON.sqrt();
for col in 0..n {
let mut y_pert = y.clone();
let h = eps_base * (1.0 + y[col].abs());
y_pert[col] += h;
let f1 = fun(t, &y_pert);
for row in 0..n {
jac[(row, col)] = (f1[row] - f0[row]) / h;
}
}
jac
}
pub struct BDF {
fun: Box<dyn Fn(f64, &DVector<f64>) -> DVector<f64>>,
pub t: f64,
pub y: DVector<f64>,
t_bound: f64,
max_step: f64,
rtol: NumberOrVec,
atol: NumberOrVec,
vectorized: bool,
n: usize,
pub t_old: Option<f64>,
h_abs: f64,
h_abs_old: Option<f64>,
error_norm_old: Option<f64>,
newton_tol: f64,
jac_factor: Option<DVector<f64>>,
jac: Option<Box<dyn FnMut(f64, &DVector<f64>) -> BdfJacobian>>,
J: BdfJacobian,
I: DMatrix<f64>,
linear_backend: Box<dyn BdfLinearBackend>,
gamma: DVector<f64>,
alpha: DVector<f64>,
error_const: DVector<f64>,
D: DMatrix<f64>,
order: usize,
max_order_cap: usize,
n_equal_steps: usize,
linear_factorization: Option<Box<dyn BdfLinearFactorization>>,
nlu: usize,
nfev: usize,
njev: usize,
pub direction: f64,
}
impl Display for BDF {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self)
}
}
impl Debug for BDF {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("BDF")
.field("t", &self.t)
.field("y", &self.y)
.field("t_bound", &self.t_bound)
.field("max_step", &self.max_step)
.field("rtol", &self.rtol)
.field("atol", &self.atol)
.field("vectorized", &self.vectorized)
.field("n", &self.n)
.field("t_old", &self.t_old)
.field("h_abs", &self.h_abs)
.field("h_abs_old", &self.h_abs_old)
.field("error_norm_old", &self.error_norm_old)
.field("newton_tol", &self.newton_tol)
.field("jac_factor", &self.jac_factor)
.field("J", &self.J)
.field("I", &self.I)
.field("gamma", &self.gamma)
.field("alpha", &self.alpha)
.field("error_const", &self.error_const)
.field("D", &self.D)
.field("order", &self.order)
.field("max_order_cap", &self.max_order_cap)
.field(
"linear_factorization_cached",
&self.linear_factorization.is_some(),
)
.field("nlu", &self.nlu)
.field("nfev", &self.nfev)
.field("njev", &self.njev)
.field("direction", &self.direction)
.finish()
}
}
impl BDF {
pub fn new() -> Self {
BDF {
fun: Box::new(|_t, y| y.clone()),
t: 0.0,
y: DVector::zeros(1),
t_bound: 1.0,
max_step: 1e-3,
rtol: NumberOrVec::Number(1e-3),
atol: NumberOrVec::Number(1e-4),
vectorized: false,
h_abs: 0.0,
h_abs_old: None,
error_norm_old: None,
newton_tol: 0.0,
jac_factor: None,
jac: None,
J: BdfJacobian::from_dense(DMatrix::zeros(0, 0)),
I: DMatrix::zeros(0, 0),
linear_backend: Box::new(DenseNalgebraLinearBackend),
gamma: DVector::zeros(0),
alpha: DVector::zeros(0),
error_const: DVector::zeros(0),
D: DMatrix::zeros(0, 0),
order: 1,
max_order_cap: MAX_ORDER,
n_equal_steps: 0,
linear_factorization: None,
nlu: 0,
direction: 1.0,
nfev: 0,
njev: 0,
n: 0,
t_old: None,
}
}
pub fn counters(&self) -> (usize, usize, usize) {
(self.nfev, self.njev, self.nlu)
}
pub fn set_max_order_cap(&mut self, max_order_cap: usize) {
assert!(
(1..=MAX_ORDER).contains(&max_order_cap),
"BDF max order cap must be in 1..={MAX_ORDER}, got {max_order_cap}"
);
self.max_order_cap = max_order_cap;
if self.order > self.max_order_cap {
self.order = self.max_order_cap;
self.linear_factorization = None;
self.n_equal_steps = 0;
}
}
pub fn max_order_cap(&self) -> usize {
self.max_order_cap
}
pub fn current_order(&self) -> usize {
self.order
}
pub fn equal_step_count(&self) -> usize {
self.n_equal_steps
}
pub fn set_linear_backend(&mut self, backend: Box<dyn BdfLinearBackend>) {
self.linear_backend = backend;
self.linear_factorization = None;
}
pub fn set_native_jacobian(
&mut self,
mut jacobian: Box<dyn FnMut(f64, &DVector<f64>) -> BdfJacobian>,
) {
let initial = jacobian(self.t, &self.y);
assert_eq!(
initial.shape(),
(self.n, self.n),
"native Jacobian shape is not equal to solver dimension"
);
self.jac = Some(jacobian);
self.J = initial;
self.linear_factorization = None;
self.njev += 1;
}
pub fn set_initial(
&mut self,
fun: Box<dyn Fn(f64, &DVector<f64>) -> DVector<f64>>,
t0: f64,
y0: DVector<f64>,
t_bound: f64,
_max_step: f64,
rtol: NumberOrVec,
atol: NumberOrVec,
jac: Option<Box<dyn Fn(f64, &DVector<f64>) -> DMatrix<f64>>>,
jac_sparsity: Option<DMatrix<f64>>,
vectorized: bool,
first_step: Option<f64>,
) {
self.prelude(fun, t0, y0, t_bound, vectorized);
info!("prelude done");
let max_step = validate_max_step(self.max_step);
self.max_step = max_step.unwrap();
let (rtol, atol) = validate_tol(rtol, atol, self.n).unwrap();
self.rtol = rtol.clone();
self.atol = atol.clone();
info!("tolerance validation: done");
let f = (&self.fun)(self.t, &self.y);
let h_abs = match first_step {
None => select_initial_step(
&self.fun,
self.t,
&self.y,
self.t_bound,
self.max_step,
&f,
self.direction,
1.0,
self.rtol.clone(),
self.atol.clone(),
),
Some(first_step) => {
validate_first_step(first_step, t0, t_bound).expect("first_step must be positive")
}
};
self.h_abs = h_abs;
self.h_abs_old = None;
self.error_norm_old = None;
self.newton_tol = newton_tol(rtol);
self.jac_factor = None;
let kappa = DVector::from_vec(vec![0.0, -0.1850, -1.0 / 9.0, -0.0823, -0.0415, 0.0]);
let gamma = {
let mut g = vec![0.0];
let mut cumsum = 0.0;
for i in 1..=MAX_ORDER {
cumsum += 1.0 / (i as f64);
g.push(cumsum);
}
DVector::from_vec(g)
};
let alpha = (DVector::from(vec![1.0; MAX_ORDER + 1]) - kappa.clone()).component_mul(&gamma);
let error_const = kappa.component_mul(&gamma)
+ DVector::from_iterator(MAX_ORDER + 1, (1..=MAX_ORDER + 1).map(|i| 1.0 / i as f64));
assert_eq!(alpha.len(), MAX_ORDER + 1);
assert_eq!(gamma.len(), MAX_ORDER + 1);
assert_eq!(error_const.len(), MAX_ORDER + 1);
self.alpha = alpha;
self.error_const = error_const;
self.gamma = gamma;
let mut D = DMatrix::zeros(MAX_ORDER + 3, self.y.len());
assert!(
D.nrows() >= MAX_ORDER + 3,
"D matrix must have at least MAX_ORDER + 3 rows"
);
info!("created matrix of size: {:?} x {:?}", D.nrows(), D.ncols());
D.set_row(0, &self.y.transpose());
D.set_row(1, &(f * h_abs * self.direction).transpose());
self.D = D;
self.order = 1;
self.n_equal_steps = 0;
self.linear_factorization = None;
self.create_funct();
self.validate_jac(jac, jac_sparsity);
}
fn create_funct(&mut self) {
self.linear_backend = Box::new(DenseNalgebraLinearBackend);
self.I = DMatrix::identity(self.n, self.n);
info!("linear backend creation: dense nalgebra");
}
fn prelude(
&mut self,
fun: Box<dyn Fn(f64, &DVector<f64>) -> DVector<f64>>,
t0: f64,
y0: DVector<f64>,
t_bound: f64,
vectorized: bool,
) {
let support_complex: bool = false;
self.t_old = None;
self.t = t0;
self.n = y0.len();
let (fun, y) = check_arguments(fun, (&y0).into(), support_complex).unwrap();
self.y = y;
self.t_bound = t_bound;
self.vectorized = vectorized;
self.fun = fun;
self.direction = if t_bound != t0 {
(t_bound - t0).signum()
} else {
1.0
};
self.nfev = 0;
}
fn validate_jac(
&mut self,
jac: Option<Box<dyn Fn(f64, &DVector<f64>) -> DMatrix<f64>>>,
sparsity: Option<DMatrix<f64>>,
) {
let t0 = self.t;
let y0 = self.y.clone();
let _sparsity_ = 0.0;
match jac {
Some(jac) => {
info!("analytical jacobian used");
let J = jac(t0, &y0);
self.njev += 1;
let jac_wrapped: Box<dyn FnMut(f64, &DVector<f64>) -> BdfJacobian> =
if is_sparse(&J, SPARSE) {
Box::new(move |t: f64, y: &DVector<f64>| -> BdfJacobian {
BdfJacobian::from_dense(jac(t, &y))
})
} else {
Box::new(move |t: f64, y: &DVector<f64>| -> BdfJacobian {
BdfJacobian::from_dense(jac(t, &y))
})
};
self.jac = Some(jac_wrapped);
self.J = BdfJacobian::from_dense(J.clone());
}
_ => {
let _new_sparsity: Option<(DMatrix<f64>, Vec<usize>)> =
if let Some(sparsity) = sparsity {
let groups = group_columns(&sparsity, OrderEnum::None);
Some((sparsity.clone(), groups))
} else {
None
};
self.J =
BdfJacobian::from_dense(finite_difference_jacobian_rhs(&*self.fun, t0, &y0));
self.jac = None;
}
};
info!("jac validation: done");
}
pub fn _step_impl(&mut self) -> (bool, Option<&'static str>) {
let t = self.t;
let mut D = self.D.clone();
let max_step = self.max_step;
let min_step = 10.0 * f64::MIN;
let mut h_abs = if self.h_abs > max_step {
change_D(&mut D, self.order, max_step / self.h_abs);
self.n_equal_steps = 0;
max_step
} else if self.h_abs < min_step {
change_D(&mut D, self.order, min_step / self.h_abs);
self.n_equal_steps = 0;
min_step
} else {
self.h_abs
};
let order = self.order;
assert!(
order <= self.max_order_cap,
"Order cannot exceed configured max order cap"
);
assert!(
order < self.alpha.len(),
"Order must be within alpha bounds"
);
let alpha = &self.alpha;
let gamma = &self.gamma;
let error_const = &self.error_const;
let mut J = self.J.clone();
let mut linear_factorization = self.linear_factorization.take();
let mut current_jac = self.jac.is_none();
let mut step_accepted = false;
let mut scale = DVector::zeros(self.n);
let mut n_iter = 0;
let mut y_new = DVector::zeros(self.n);
let _conv = false;
let mut t_new = 0.0;
let mut safety = 0.0;
let mut error_norm = 0.0;
let mut d = DVector::zeros(self.n);
while !step_accepted {
if h_abs < min_step {
return (false, "step size too small".into());
}
let h = h_abs * self.direction;
let t_new_ = t + h;
if self.direction * (t_new - self.t_bound) > 0.0 {
t_new = self.t_bound;
change_D(&mut D, order, (t_new - t).abs() / h_abs);
self.n_equal_steps = 0;
linear_factorization = None;
}
t_new = t_new_;
let h = t_new - t;
h_abs = h.abs();
let y_predict = D.rows(0, order + 1).row_sum().transpose();
let y_predict_abs = y_predict.abs();
let scale_ = scale_func(self.rtol.clone(), self.atol.clone(), &y_predict_abs);
let scale_: DVector<f64> = DVector::from_vec(scale_);
scale = scale_;
let psi = D.rows(1, order).transpose() * gamma.rows(1, order) / alpha[order];
let mut converged = false;
let c = h / alpha[order];
while !converged {
if linear_factorization.is_none() {
assert_eq!(
J.shape(),
(self.n, self.n),
"J shape is not equal to solver dimension"
);
linear_factorization = self.linear_backend.factor_shifted_jacobian(c, &J);
self.nlu += 1;
if linear_factorization.is_none() {
break;
}
}
let (conv, n_iter_, y_new_, d_) = solve_bdf_system(
&self.fun,
t_new,
&y_predict.clone(),
c,
&psi.clone(),
linear_factorization.as_ref().unwrap().as_ref(),
&scale.clone(),
self.newton_tol,
);
n_iter = n_iter_;
y_new = y_new_;
d = d_;
converged = conv;
if !converged {
if current_jac {
break;
}
J = if let Some(jac_fun) = self.jac.as_mut() {
jac_fun(t_new, &y_predict)
} else {
BdfJacobian::from_dense(finite_difference_jacobian_rhs(
&*self.fun, t_new, &y_predict,
))
};
self.njev += 1;
linear_factorization = None;
current_jac = true;
}
}
if !converged {
let factor = 0.5;
h_abs *= factor;
change_D(&mut D, order, factor);
self.n_equal_steps = 0;
linear_factorization = None;
continue;
}
let safety_ = 0.9 * (2.0 * (NEWTON_MAXITER as f64) + 1.0)
/ (2.0 * (NEWTON_MAXITER as f64) + n_iter as f64);
safety = safety_;
let scale = scale_func(self.rtol.clone(), self.atol.clone(), &y_new.abs());
let scale: DVector<f64> = DVector::from_vec(scale);
let error = error_const[order] * d.clone();
let error_norm_ = norm(&(error.component_div(&scale)));
error_norm = error_norm_;
if error_norm > 1.0 {
let factor =
(safety * error_norm.powf(-1.0 / (order as f64 + 1.0))).max(MIN_FACTOR);
h_abs *= factor;
change_D(&mut D, order, factor);
self.n_equal_steps = 0;
} else {
step_accepted = true;
}
}
self.n_equal_steps += 1;
self.t = t_new;
self.y = y_new;
self.h_abs = h_abs;
self.J = J;
self.linear_factorization = linear_factorization;
let D_ = D.clone();
D.set_row(order + 2, &(d.clone().transpose() - D_.row(order + 1)));
D.set_row(order + 1, &d.transpose());
for i in (0..order + 1).rev() {
let D_ = D.clone();
D.row_mut(i).add_assign(D_.row(i + 1));
}
if self.n_equal_steps < order + 1 {
self.D = D;
return (true, None);
}
let error_m_norm = if order > 1 {
let error_m = error_const[order - 1] * D.row(order);
norm(&(error_m.transpose().component_div(&scale)))
} else {
f64::INFINITY
};
let error_p_norm = if order < self.max_order_cap {
let error_p = error_const[order + 1] * D.row(order + 2);
norm(&(error_p.transpose().component_div(&scale)))
} else {
f64::INFINITY
};
let error_norms = DVector::from_vec(vec![error_m_norm, error_norm, error_p_norm]);
let factors: Vec<f64> = error_norms
.iter()
.enumerate()
.map(|(i, x)| x.powf(-1.0 / (order as f64 + i as f64)))
.collect(); let factors: DVector<f64> = factors.into();
let argmax_index = factors.argmax().0;
let delta_order = (argmax_index as i32) - 1; let new_order = ((order as i32) + delta_order)
.max(1)
.min(self.max_order_cap as i32) as usize;
self.order = new_order;
let factor = (safety * factors.max()).min(MAX_FACTOR);
self.h_abs *= factor;
change_D(&mut D, self.order, factor);
self.n_equal_steps = 0;
self.linear_factorization = None;
self.D = D;
(true, None)
}
}