27% speedup NB: Regula falsi alone without Illinois adjustment was slower than interval bisection
89 lines
2.7 KiB
Rust
89 lines
2.7 KiB
Rust
// hpstat: High-performance statistics implementations
|
|
// Copyright © 2023 Lee Yingtong Li (RunasSudo)
|
|
//
|
|
// This program is free software: you can redistribute it and/or modify
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
// (at your option) any later version.
|
|
//
|
|
// This program is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU Affero General Public License for more details.
|
|
//
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
pub struct IllinoisRootFinder {
|
|
bound_lower: f64,
|
|
bound_upper: f64,
|
|
value_lower: f64,
|
|
value_upper: f64,
|
|
last_sign: f64 // Sign of the function at last evaluation (1.0 or -1.0) or 0.0 if first iteration
|
|
}
|
|
|
|
impl IllinoisRootFinder {
|
|
pub fn new(bound_lower: f64, bound_upper: f64, value_lower: f64, value_upper: f64) -> IllinoisRootFinder {
|
|
return IllinoisRootFinder {
|
|
bound_lower: bound_lower,
|
|
bound_upper: bound_upper,
|
|
value_lower: value_lower,
|
|
value_upper: value_upper,
|
|
last_sign: 0.0
|
|
}
|
|
}
|
|
|
|
pub fn update(&mut self, guess: f64, mut value: f64) {
|
|
if value > 0.0 {
|
|
if self.value_lower > 0.0 || self.value_upper < 0.0 {
|
|
self.bound_lower = guess;
|
|
self.value_lower = value;
|
|
|
|
if self.last_sign == 1.0 {
|
|
// Illinois adjustment: Halve the y-value of the retained end point (the other end point) when the new y-value has the same sign as the previous one
|
|
self.value_upper *= 0.5;
|
|
}
|
|
} else {
|
|
self.bound_upper = guess;
|
|
self.value_upper = value;
|
|
|
|
if self.last_sign == 1.0 {
|
|
self.value_lower *= 0.5;
|
|
}
|
|
}
|
|
self.last_sign = 1.0;
|
|
} else {
|
|
if self.value_lower < 0.0 || self.value_upper > 0.0 {
|
|
self.bound_lower = guess;
|
|
self.value_lower = value;
|
|
|
|
if self.last_sign == -1.0 {
|
|
self.value_upper *= 0.5;
|
|
}
|
|
} else {
|
|
self.bound_upper = guess;
|
|
self.value_upper = value;
|
|
|
|
if self.last_sign == -1.0 {
|
|
self.value_lower *= 0.5;
|
|
}
|
|
}
|
|
self.last_sign = -1.0;
|
|
}
|
|
}
|
|
|
|
pub fn next_guess(&self) -> f64 {
|
|
if self.value_lower.is_nan() || self.value_upper.is_nan() {
|
|
// Fall back to interval bisection
|
|
return (self.bound_lower + self.bound_upper) / 2.0;
|
|
} else {
|
|
// Regula falsi
|
|
return (self.bound_lower * self.value_upper - self.bound_upper * self.value_lower) / (self.value_upper - self.value_lower);
|
|
}
|
|
}
|
|
|
|
pub fn precision(&self) -> f64 {
|
|
return (self.bound_upper - self.bound_lower).abs();
|
|
}
|
|
}
|