Unify limiters
This commit is contained in:
parent
75a6da9cae
commit
88bfae9608
|
|
@ -1,13 +1,18 @@
|
|||
use crate::float_fun::FloatExt2;
|
||||
|
||||
pub trait Limiter {
|
||||
fn value(&self, x: f32) -> f32;
|
||||
fn derivative(&self, x: f32) -> f32;
|
||||
}
|
||||
|
||||
pub struct LinearLimiter {
|
||||
pub min: f32,
|
||||
pub max: f32,
|
||||
}
|
||||
|
||||
impl LinearLimiter {
|
||||
pub fn value(&self, x: f32) -> f32 { (self.min, self.max).inverse_lerp(x.abs()).clamp(0.0, 1.0) }
|
||||
pub fn derivative(&self, x: f32) -> f32 { if x.abs() > self.min && x.abs() < self.max { x.signum() / (self.max - self.min) } else { 0.0 } }
|
||||
impl Limiter for LinearLimiter {
|
||||
fn value(&self, x: f32) -> f32 { (self.min, self.max).inverse_lerp(x.abs()).clamp(0.0, 1.0) }
|
||||
fn derivative(&self, x: f32) -> f32 { if x.abs() > self.min && x.abs() < self.max { x.signum() / (self.max - self.min) } else { 0.0 } }
|
||||
}
|
||||
|
||||
pub struct SmoothstepLimiter {
|
||||
|
|
@ -15,12 +20,12 @@ pub struct SmoothstepLimiter {
|
|||
pub max: f32,
|
||||
}
|
||||
|
||||
impl SmoothstepLimiter {
|
||||
pub fn value(&self, x: f32) -> f32 {
|
||||
impl Limiter for SmoothstepLimiter {
|
||||
fn value(&self, x: f32) -> f32 {
|
||||
let y = (self.min, self.max).inverse_lerp(x.abs()).clamp(0.0, 1.0);
|
||||
3.0 * y * y - 2.0 * y * y * y
|
||||
}
|
||||
pub fn derivative(&self, x: f32) -> f32 {
|
||||
fn derivative(&self, x: f32) -> f32 {
|
||||
if x.abs() > self.min && x.abs() < self.max {
|
||||
let t = (self.min, self.max).inverse_lerp(x.abs());
|
||||
6.0 * x.signum() * t * (1.0 - t) / (self.max - self.min)
|
||||
|
|
@ -35,12 +40,12 @@ pub struct SmootherstepLimiter {
|
|||
pub max: f32,
|
||||
}
|
||||
|
||||
impl SmootherstepLimiter {
|
||||
pub fn value(&self, x: f32) -> f32 {
|
||||
impl Limiter for SmootherstepLimiter {
|
||||
fn value(&self, x: f32) -> f32 {
|
||||
let y = (self.min, self.max).inverse_lerp(x.abs()).clamp(0.0, 1.0);
|
||||
6.0 * y.powi(5) - 15.0 * y.powi(4) + 10.0 * y.powi(3)
|
||||
}
|
||||
pub fn derivative(&self, x: f32) -> f32 {
|
||||
fn derivative(&self, x: f32) -> f32 {
|
||||
if x.abs() > self.min && x.abs() < self.max {
|
||||
let t = (self.min, self.max).inverse_lerp(x.abs());
|
||||
30.0 * (t * (1.0 - t)).powi(2) * x.signum() / (self.max - self.min)
|
||||
|
|
@ -79,84 +84,45 @@ impl QuadraticAccelerator {
|
|||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::*;
|
||||
use approx::{abs_diff_eq, AbsDiffEq, assert_abs_diff_eq};
|
||||
|
||||
fn test_limiter(testee: impl Limiter, min: f32, max: f32, δ: f32) {
|
||||
let ε = 1.0e-4f32;
|
||||
let margin = 1.0 / 16.0;
|
||||
let mul = 1.0 + margin;
|
||||
for x in itertools_num::linspace(0., min, 10) {
|
||||
assert_abs_diff_eq!(testee.value(x), 0., epsilon = ε);
|
||||
assert_abs_diff_eq!(testee.value(-x), 0., epsilon = ε);
|
||||
}
|
||||
for x in itertools_num::linspace(max, mul * max, 10) {
|
||||
assert_abs_diff_eq!(testee.value(x), 1., epsilon = ε);
|
||||
assert_abs_diff_eq!(testee.value(-x), 1., epsilon = ε);
|
||||
}
|
||||
for x in itertools_num::linspace(-mul * max, mul * max, 100) {
|
||||
// Currently, the derivative is discontinuous at ±min and ±max... let’s just skip these for now.
|
||||
if x.abs().abs_diff_eq(&min, δ) || x.abs().abs_diff_eq(&max, δ) {
|
||||
continue;
|
||||
}
|
||||
let df_num = (testee.value(x + δ) - testee.value(x - δ)) / (2. * δ);
|
||||
let df_expl = testee.derivative(x);
|
||||
assert!(abs_diff_eq!(df_expl, df_num, epsilon = ε), "At x={x}, df/dx:\nnumerical: {df_num}\nexplicit: {df_expl}\n");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_linear_limiter() {
|
||||
let testee = super::LinearLimiter { min: 20.0, max: 30.0 };
|
||||
let ε = 1.0e-4f32;
|
||||
let δ = 1.0 / 8.0; // Mathematically, you want this to be small. Computationally, you don’t.
|
||||
let margin = 1.0 / 16.0;
|
||||
let mul = 1.0 + margin;
|
||||
for x in itertools_num::linspace(0., testee.min, 10) {
|
||||
assert_abs_diff_eq!(testee.value(x), 0., epsilon = ε);
|
||||
assert_abs_diff_eq!(testee.value(-x), 0., epsilon = ε);
|
||||
}
|
||||
for x in itertools_num::linspace(testee.max, mul * testee.max, 10) {
|
||||
assert_abs_diff_eq!(testee.value(x), 1., epsilon = ε);
|
||||
assert_abs_diff_eq!(testee.value(-x), 1., epsilon = ε);
|
||||
}
|
||||
for x in itertools_num::linspace(-mul * testee.max, mul * testee.max, 100) {
|
||||
// Currently, the derivative is discontinuous at ±min and ±max... let’s just skip these for now.
|
||||
if x.abs().abs_diff_eq(&testee.min, δ) || x.abs().abs_diff_eq(&testee.max, δ) {
|
||||
continue;
|
||||
}
|
||||
let df_num = (testee.value(x + δ) - testee.value(x - δ)) / (2. * δ);
|
||||
let df_expl = testee.derivative(x);
|
||||
assert!(abs_diff_eq!(df_expl, df_num, epsilon = ε), "At x={x}, df/dx:\nnumerical: {df_num}\nexplicit: {df_expl}\n");
|
||||
}
|
||||
test_limiter(LinearLimiter { min: 20.0, max: 30.0 }, 20.0, 30.0, 1.0 / 8.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_smoothsteo_limiter() {
|
||||
let testee = super::SmoothstepLimiter { min: 20.0, max: 30.0 };
|
||||
let ε = 1.0e-4f32;
|
||||
let δ = 1.0 / 8.0; // Mathematically, you want this to be small. Computationally, you don’t.
|
||||
let margin = 1.0 / 16.0;
|
||||
let mul = 1.0 + margin;
|
||||
for x in itertools_num::linspace(0., testee.min, 10) {
|
||||
assert_abs_diff_eq!(testee.value(x), 0., epsilon = ε);
|
||||
assert_abs_diff_eq!(testee.value(-x), 0., epsilon = ε);
|
||||
}
|
||||
for x in itertools_num::linspace(testee.max, mul * testee.max, 10) {
|
||||
assert_abs_diff_eq!(testee.value(x), 1., epsilon = ε);
|
||||
assert_abs_diff_eq!(testee.value(-x), 1., epsilon = ε);
|
||||
}
|
||||
for x in itertools_num::linspace(-mul * testee.max, mul * testee.max, 100) {
|
||||
// Currently, the derivative is discontinuous at ±min and ±max... let’s just skip these for now.
|
||||
if x.abs().abs_diff_eq(&testee.min, δ) || x.abs().abs_diff_eq(&testee.max, δ) {
|
||||
continue;
|
||||
}
|
||||
let df_num = (testee.value(x + δ) - testee.value(x - δ)) / (2. * δ);
|
||||
let df_expl = testee.derivative(x);
|
||||
assert!(abs_diff_eq!(df_expl, df_num, epsilon = ε), "At x={x}, df/dx:\nnumerical: {df_num}\nexplicit: {df_expl}\n");
|
||||
}
|
||||
fn test_smoothstep_limiter() {
|
||||
test_limiter(SmoothstepLimiter { min: 20.0, max: 30.0 }, 20.0, 30.0, 1.0 / 32.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_smootherstep_limiter() {
|
||||
let testee = super::SmootherstepLimiter { min: 20.0, max: 30.0 };
|
||||
let ε = 1.0e-4f32;
|
||||
let δ = 1.0 / 32.0; // Mathematically, you want this to be small. Computationally, you don’t.
|
||||
let margin = 1.0 / 16.0;
|
||||
let mul = 1.0 + margin;
|
||||
for x in itertools_num::linspace(0., testee.min, 10) {
|
||||
assert_abs_diff_eq!(testee.value(x), 0., epsilon = ε);
|
||||
assert_abs_diff_eq!(testee.value(-x), 0., epsilon = ε);
|
||||
}
|
||||
for x in itertools_num::linspace(testee.max, mul * testee.max, 10) {
|
||||
assert_abs_diff_eq!(testee.value(x), 1., epsilon = ε);
|
||||
assert_abs_diff_eq!(testee.value(-x), 1., epsilon = ε);
|
||||
}
|
||||
for x in itertools_num::linspace(-mul * testee.max, mul * testee.max, 100) {
|
||||
// Currently, the derivative is discontinuous at ±min and ±max... let’s just skip these for now.
|
||||
if x.abs().abs_diff_eq(&testee.min, δ) || x.abs().abs_diff_eq(&testee.max, δ) {
|
||||
continue;
|
||||
}
|
||||
let df_num = (testee.value(x + δ) - testee.value(x - δ)) / (2. * δ);
|
||||
let df_expl = testee.derivative(x);
|
||||
assert!(abs_diff_eq!(df_expl, df_num, epsilon = ε), "At x={x}, df/dx:\nnumerical: {df_num}\nexplicit: {df_expl}\n");
|
||||
}
|
||||
test_limiter(SmootherstepLimiter { min: 20.0, max: 30.0 }, 20.0, 30.0, 1.0 / 32.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
use glam::{f32, Mat2, Vec2, vec2};
|
||||
use crate::fns;
|
||||
use crate::fns::{self, Limiter};
|
||||
use crate::riemann::{Decomp2, Metric, Tens2};
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
|
|
@ -11,7 +11,7 @@ pub struct Tube {
|
|||
}
|
||||
|
||||
impl Tube {
|
||||
fn fx(&self) -> fns::SmootherstepLimiter { fns::SmootherstepLimiter { min: self.inner_radius, max: self.outer_radius } }
|
||||
fn fx(&self) -> impl Limiter { fns::SmootherstepLimiter { min: self.inner_radius, max: self.outer_radius } }
|
||||
fn fy(&self) -> fns::QuadraticAccelerator { fns::QuadraticAccelerator { internal: self.internal_halflength, external: self.external_halflength } }
|
||||
|
||||
pub fn y(&self, v: f32) -> f32 { self.fy().x(v) }
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user